00 21Java高级之反射应用案例


经过一系列分析之后可以发现虽然获得了Class类的实例话都西昂,但是依然觉得这个对象获取的意义不是很大,所以为了进一步的帮助大家理解反射的核心意义所在,下面将通过几个案例进行程序的说明(都是在实际开发之中一定会使用到的)。

1 反射实例化对象

获取Class对象之后最大的意义实际上并不是在于只是一个对象的实例化的操作形式,更重要的是Class类里面提供有一个对象的反射实例化方法(代替了关键字new):
(1)在JDK1.9以前的实例化:@Deprecated(since="9") public T newInstance() throws InstantiationException, IllegalAccessException
(2)在JDK 1.9之后:clazz.getDeclaredConstructor().newInstance()
范例:通过newInstance()方法实例化Student类对象

package org.lks.demo;

public class JavaAPIDemo {
	public static void main(String[] args) {
		Class<?> cls = null;
		try {
			cls = Class.forName("org.lks.model.Student");
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		try {
			Object  obj = cls.newInstance();
			System.out.println(obj);
		} catch (InstantiationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

现在通过反射实现的对象实例化处理,依然要调用类中的无参构造方法,其本质等价于“类.对象 = new 类()”,也就是相当于隐含了关键字new,而直接使用字符串进行类替代。

范例:从JDK 1.9之后newInstance()被替代了

package org.lks.demo;

import java.lang.reflect.InvocationTargetException;

public class JavaAPIDemo {
	public static void main(String[] args) {
		Class<?> cls = null;
		try {
			cls = Class.forName("org.lks.model.Student");
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		Object obj = null;
		try {
			obj = cls.getDeclaredConstructor().newInstance();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println(obj);
	}
}

因为默认的Class类中的newInstance()方法只能够调用无参构造,所以很多开发者会认为其描述的不准确,于是将其变换了形式(构造方法会讲解)

2 反射与工厂设计模式

如果要想进行对象的实例化处理除了可以使用关键字new之外,还可以使用反射机制来完成,于是此时一定会思考一个问题:为什么要提供有一个反射的实例化?那么到底是使用关键子new还是使用反射呢?

如果要想更好的理解此类问题,最好的解释方案就是通过工厂设计模式来解决。工厂设计模式的最大的特点:客户端的一个程序类不直接牵扯到对象的实例化管理,只与接口发生关联,通过工厂类获取指定接口的实例化对象。
范例:传统模式

package org.lks.demo;

public class JavaAPIDemo {
	public static void main(String[] args) {
		IMessage message = new NetMessageImpl();
		message.send();
	}
}

interface IMessage{
	void send();
}

class NetMessageImpl implements IMessage{

	@Override
	public void send() {
		System.out.println("[NetMessage] sending...");
	}
	
}

在实际的开发之中,接口的主要作用是为不同的层提供有一个操作的标准。但是如果此时直接将一个子类设置为接口实例化操作,那么一定会有耦合问题,所以使用了工厂设计模式来解决此问题。
范例:利用工厂设计模式解决问题

package org.lks.demo;

public class JavaAPIDemo {
	public static void main(String[] args) {
		IMessage message = Factory.getInstance("netmessage");
		message.send();
	}
}

class Factory{
	private Factory() {}
	
	public static IMessage getInstance(String classname) {
		if("netmessage".equalsIgnoreCase(classname)) {
			return new NetMessageImpl();
		}
		return null;
	}
}

interface IMessage{
	void send();
}

class NetMessageImpl implements IMessage{

	@Override
	public void send() {
		System.out.println("[NetMessage] sending...");
	}
	
}

此种工厂设计模式属于静态工厂设计模式,也就是说如果现在要追加一个子类,则意味着工厂类一定要做出修改,因为如果不追加这种判断是无法获取指定接口对象的。

范例:为IMessage追加一个子类

package org.lks.demo;

public class JavaAPIDemo {
	public static void main(String[] args) {
		IMessage message = Factory.getInstance("netmessage");
		message.send();
		IMessage messageA = Factory.getInstance("cloudmessage");
		messageA.send();
	}
}

class Factory{
	private Factory() {}
	
	public static IMessage getInstance(String classname) {
		if("netmessage".equalsIgnoreCase(classname)) {
			return new NetMessageImpl();
		}else if("cloudmessage".equalsIgnoreCase(classname)) {
			return new CloudMessageImpl();
		}
		return null;
	}
}

interface IMessage{
	void send();
}

class CloudMessageImpl implements IMessage{

	@Override
	public void send() {
		System.out.println("[CloudMessage] sending...");
	}
	
}

class NetMessageImpl implements IMessage{

	@Override
	public void send() {
		System.out.println("[NetMessage] sending...");
	}
	
}

工厂设计模式最有效解决的是子类与客户端的耦合问题,但是解决的核心思想是在于提供有一个工厂类作为过渡端,但是随着项目的进行,你的IMessage接口可能会有更多的子类,,而且随着时间的推移子类产生的可能越来越多,那么此时就意味着你的工厂类永远都要进行修改,并且永无停止之日。

那么这个时候最好的解决方案就是不使用关键字new来完成,因为关键字new在使用的时候需要有一个明确的类存在。而newInstance()方法只需要有一个明确表示类名称的字符串即可应用。

范例:使用反射实现工厂模式

package org.lks.demo;

import java.lang.reflect.InvocationTargetException;

public class JavaAPIDemo {
	public static void main(String[] args) {
		IMessage message = Factory.getInstance("org.lks.demo.NetMessageImpl");
		message.send();
		IMessage messageA = Factory.getInstance("org.lks.demo.CloudMessageImpl");
		messageA.send();
	}
}

class Factory{
	private Factory() {}
	
	public static IMessage getInstance(String classname) {
		IMessage instance = null;
		try {
			instance = (IMessage) Class.forName(classname).getDeclaredConstructor().newInstance();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return instance;
	}
}

interface IMessage{
	void send();
}

class CloudMessageImpl implements IMessage{

	@Override
	public void send() {
		System.out.println("[CloudMessage] sending...");
	}
	
}

class NetMessageImpl implements IMessage{

	@Override
	public void send() {
		System.out.println("[NetMessage] sending...");
	}
	
}

这个时候可以发现使用反射机制实现的工厂设计模式,最大的优势在于,对于接口子类的扩充将不再影响到工厂类的定义。

但是现在依然需要进一步思考,因为在实际的项目开发过程之中有可能会存在有大量的接口,并且这些接口都可能需要通过工厂类实例化,所以此时的工厂设计模式不应该只为一个IMessage接口服务,而应该变为为所有的接口服务。
范例:泛型和反射实现的工厂模式

package org.lks.demo;

import org.lks.service.IUserService;
import org.lks.service.impl.UserServiceImpl;

public class JavaAPIDemo {
	public static void main(String[] args) {
		IMessage message = Factory.getInstance("org.lks.demo.NetMessageImpl", NetMessageImpl.class);
		message.send();
		IMessage messageA = Factory.getInstance("org.lks.demo.CloudMessageImpl", CloudMessageImpl.class);
		messageA.send();
		IUserService iss = Factory.getInstance("org.lks.service.impl.UserServiceImpl", UserServiceImpl.class);
	}
}

class Factory{
	private Factory() {}
	
	@SuppressWarnings({ "static-access", "unchecked" })
	public static <T> T getInstance(String classname, Class<T> clazz) {
		T instance = null;
		try {
			instance = (T) clazz.forName(classname).getDeclaredConstructor().newInstance();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return instance;
	}
}

interface IMessage{
	void send();
}

class CloudMessageImpl implements IMessage{

	@Override
	public void send() {
		System.out.println("[CloudMessage] sending...");
	}
	
}

class NetMessageImpl implements IMessage{

	@Override
	public void send() {
		System.out.println("[NetMessage] sending...");
	}
	
}

此时的工厂设计模式将不再受限于指定的接口,可以为所有的接口提供实例化服务。

3 反射与单例设计模式

单例设计模式的核心本质在于:类内部的构造方法私有化,在类的内部产生实例化对象之后通过static方法获取实例化对象进行类中的结构调用,单例设计模式一共有两类:懒汉式、饿汉式,饿汉式的单例是不在本次讨论的范围之内的,主要讨论懒汉式的单例设计模式。
范例:观察懒汉式单例设计模式的问题

package org.lks.demo;

public class JavaAPIDemo {
	public static void main(String[] args) {
		for(int i = 0; i < 3; i++) {
			new Thread(()->{
				Singleton.getInstance().print();
			}, "SingletonThread-" + i).start();
		}
	}
}

class Singleton{
	private static Singleton instance = null;
	
	private Singleton() {
		System.out.println("******* " + Thread.currentThread().getName() + "************");
	}
	
	public static Singleton getInstance() {
		if(Singleton.instance == null) {
			Singleton.instance = new Singleton();
		}
		return instance;
	}
	
	public void print() {
		System.out.println(Thread.currentThread().getName() + "Singleton hhhhh!!");
	}
}

/*
******* SingletonThread-2************
SingletonThread-2Singleton hhhhh!!
******* SingletonThread-1************
SingletonThread-1Singleton hhhhh!!
******* SingletonThread-0************
SingletonThread-0Singleton hhhhh!!
*/

单例设计模式的最大特点是在整体的运行过程之中只允许产生一个实例化对象,这个时候会发现当有了若干线程之后实际上当前的程序就可以产生多个实例化对象了,那么此时就不是单例设计模式了。此时问题造成的关键在于代码本身出现了不同步的情况,而要想解决的关键核心就在于需要进行同步处理,同步自然自然可以想到synchronized关键字

范例:修改getInstance()方法进行同步处理

class Singleton{
	private static Singleton instance = null;
	
	private Singleton() {
		System.out.println("******* " + Thread.currentThread().getName() + "************");
	}
	
	public static synchronized Singleton getInstance() {
		if(Singleton.instance == null) {
			Singleton.instance = new Singleton();
		}
		return instance;
	}
	
	public void print() {
		System.out.println(Thread.currentThread().getName() + "Singleton hhhhh!!");
	}
}


这个时候的确是进行了同步处理,但是这个同步的代价有些大,因为效率会低。因为整体代码里面实际上只有一块部分需要进行同步处理,instance对象的实例化处理部分,在这样的情况下会发现同步加的有些草率了。
范例:更加合理的进行同步处理

package org.lks.demo;

public class JavaAPIDemo {
	public static void main(String[] args) {
		for(int i = 0; i < 3; i++) {
			new Thread(()->{
				Singleton.getInstance().print();
			}, "SingletonThread-" + i).start();
		}
	}
}

class Singleton{
	private static Singleton instance = null;
	
	private Singleton() {
		System.out.println("******* " + Thread.currentThread().getName() + "************");
	}
	
	public static  Singleton getInstance() {
		if(Singleton.instance == null) {
			synchronized(Singleton.class) {
				if(instance == null){
					Singleton.instance = new Singleton();
				}
			}
		}
		return instance;
	}
	
	public void print() {
		System.out.println(Thread.currentThread().getName() + "Singleton hhhhh!!");
	}
}

面试题:请编写单例设计模式
(1)直接编写一个饿汉式的单例设计模式,并且实现构造方法私有化;
(2)在Java中哪里使用到了单例设计模式了?Runtime类、Spring框架;
(3)懒汉式单例设计模式的问题?

发布了122 篇原创文章 · 获赞 11 · 访问量 4228

猜你喜欢

转载自blog.csdn.net/weixin_43762330/article/details/104770696
00