反射应用案例
经过分析之后,可以发现虽然获得了Class类的实例化对象,但是依然觉得这个对象忽悠去的意义不大。所以为了进一步了解反射的核心意义,通过几个案例进行程序的说明(实际开发中一定会使用的)。
反射实例化对象
获取Class对象之后实际上并不是只是一个对象的实例化操作形式,更重要的是Class类提供有一个对象的反射实例化方法(代替关键字new)。
- JDK1.9以前的实例化:
public T newInstance() throws InstantiationException,IllegalAccessException
- JDK1.9之后发生了变化:
clazz.getDeclaredConstructor().newInstance();
通过newInstance()方法实现
package Basic;
public class Person {
//任何情况下如果要实例化类对象则一定要调用类中的构造方法
public Person() {
System.out.println("*****Person Construction******");
}
@Override
public String toString() {
return "i am a person!";
}
}
package Basic;
public class test {
public static void main(String[] args) throws ClassNotFoundException, Exception, IllegalAccessException {
Class<?> cls = Class.forName("Basic.Person");
Object obj = cls.newInstance(); //实例化对象,JDK1.9后被废除
System.out.println(obj); //调用toString
}
}
执行结果:
【cls.newInstance()、关键字new】Person Construction*
【System.out.println(obj)】i am a person!
通过反射实现的对象实例化处理,依然要调用类中的无参构造方法,其本质等价于“类 对象 = new 类”,相当于隐含了字符串new而直接使用字符串进行了替代。
JDK1.9之后newInstance()被替代了,因为默认的Class类中的newInstance方法只能调用无参构造,所以很多开发者认为其描述的不准确,所以将其变换了形式(构造方法讲解)。
package Basic;
public class test {
public static void main(String[] args) throws ClassNotFoundException, Exception, IllegalAccessException {
Class<?> cls = Class.forName("Basic.Person");
Object obj = cls.getDeclaredConstructor().newInstance(); //实例化对象,JDK1.9后被废除
System.out.println(obj); //调用toString
}
}
反射与工厂设计模式
如果要想进行对象的实例化处理除了可以使用关键字new之外,还可以使用反射机制来完成,于是此时一定会思考一个问题:为什么要提供一个反射的实例化?那么到底是使用关键字new还是使用反射呢?
如果要想更好的理解此类问题,最好的解释方法就是通过工厂设计来解决。工厂设计模式最大特点:客户端的一个程序类不直接牵扯到对象的实例化管理,只与接口发生关联,通过工厂类获取指定接口的实例化对象。
传统设计模式
package Basic;
interface IMessage{
public void send(); //消息发送
}
class NetMessage implements IMessage{
@Override
public void send() {
// TODO Auto-generated method stub
System.out.println("[网络消息发送]lyz,冲!");
}
}
public class test {
public static void main(String[] args) throws Exception {
IMessage msg = new NetMessage(); //直接实例化会有耦合问题
}
}
在实际开发中,接口的实际作用是为不同的层提供一个操作的标准。但是此时直接将一个子类设置为接口实例化操作,那么一定会有耦合问题,所以使用了工厂设计模式来解决此问题。
工厂设计模式
package Basic;
interface IMessage{
public void send(); //消息发送
}
class NetMessage implements IMessage{
@Override
public void send() {
// TODO Auto-generated method stub
System.out.println("[网络消息发送]lyz,冲!");
}
}
class Factory{
private Factory() {}; //没有产生实例化对象的意义,所以构造方法私有化
public static IMessage getInstance(String className) {
if("netmessage".equalsIgnoreCase(className)) {
return new NetMessage();
}
return null;
}
}
public class test {
public static void main(String[] args) throws Exception {
IMessage msg = Factory.getInstance("netmessage");
msg.send();
}
}
此种设计模式属于静态工厂设计模式,也就是说如果要追加一个子类,则意味着工厂类一定要作出修改,因为如果不追加这种判断是无法获取指定接口对象的。
为IMessage追加一个子类
package Basic;
interface IMessage{
public void send(); //消息发送
}
class NetMessage implements IMessage{
@Override
public void send() {
System.out.println("[网络消息发送]刘宜柱,冲!");
}
}
class CloudMessage implements IMessage{
@Override
public void send() {
System.out.println("[云消息]发送!");
}
}
class Factory{
private Factory() {}; //没有产生实例化对象的意义,所以构造方法私有化
public static IMessage getInstance(String className) {
if("netmessage".equalsIgnoreCase(className)) {
return new NetMessage();
} else if("cloudmessage".equalsIgnoreCase(className)) {
return new CloudMessage();
}
return null;
}
}
public class test {
public static void main(String[] args) throws Exception {
IMessage msg = Factory.getInstance("cloudmessage");
msg.send();
}
}
工厂设计模式最有效的解决的是子类与客户端的耦合问题,解决的核心思想是在于提供一个工厂类作为过渡端,但是随着项目的进行,IMessage可能会有更多地子类,且随着时间的推移,子类可能越来越多,此时意味着工厂类永远都要修改且永无停止之日。
此时最好的解决方案就是不适用new来完成,因为关键字new因为在使用的时候需要有一个明确的类存在。而newInstance()只需要有一个明确表示类名称的字符串即可。
class Factory{
private Factory() {}; //没有产生实例化对象的意义,所以构造方法私有化
public static IMessage getInstance(String className) {
IMessage instance = null;
try {
instance = (IMessage)Class.forName(className).getDeclaredConstructor().newInstance();
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException
| NoSuchMethodException | SecurityException | ClassNotFoundException e) {
e.printStackTrace();
}
return instance;
}
}
利用反射机制实现的工厂设计模式,最大的优势在于对于接口子类的扩充将不再影响到工厂类的定义。
更进一步实际项目中存再大量接口,且这些接口都可能需要通过工厂类实例化, 所以此时的工厂设计模式不应只为一个IMessage接口服务,而应变为为所有的接口服务。
// 接口、类 略
class Factory{
private Factory() {}; //没有产生实例化对象的意义,所以构造方法私有化
/**
* 获取接口实例化对象
* @param <T>
* @param className 接口的子类
* @param clazz 描述的是一个接口的类型
* @return 若子类存在返回指定接口实例化对象
*/
@SuppressWarnings("unchecked")
public static <T> T getInstance(String className,Class<T> clazz) {
T instance = null;
try {
instance = (T) Class.forName(className).getDeclaredConstructor().newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return instance;
}
}
public class test {
public static void main(String[] args) throws Exception {
IMessage msg = Factory.getInstance("Basic.CloudMessage",IMessage.class);
msg.send();
IService service = Factory.getInstance("Basic.HouseService", IService.class);
service.service();
}
}
此时工厂设计模式将不再受限于指定的接口。
反射与单例设计模式
单例设计模式的核心本质在于:类内部的构造方法私有化,在类的内部产生实例化对象之后通过static方法获取实例化对象进行类中结构调用,单例设计模式一共有两类:懒汉式、饿汉式,主要讨论懒汉式的单例设计。
懒汉式单例的问题
package Basic;
class Singleton{
private static Singleton instance = null;
private Singleton() {
System.out.println("["+Thread.currentThread().getName()+"]实例化Singleton类对象!");
}
public static Singleton getInstance() {
if(instance == null) {
instance = new Singleton();
}
return instance;
}
public void print() {
System.out.println("test!");
}
}
public class test {
public static void main(String[] args) throws Exception {
for(int x=0; x<3; x++) {
new Thread(()->{
Singleton.getInstance().print();
},"单例消费端-"+ x).start();
}
}
}
运行结果:
[单例消费端-2]实例化Singleton类对象!
test!
[单例消费端-0]实例化Singleton类对象!
test!
[单例消费端-1]实例化Singleton类对象!
test!
单例设计模式的最大特点是在整体运行过程中只允许产生一个实例化对象,此时当有若干线程之后,当前的程序就可以产生多个实例化对象,那么此时就不在是单例设计模式了。此时造成问题的关键就是造成了不同步的情况,要解决关键的核心在于进行同步处理,同步自然可以想到synchronized处理。
public static synchronized Singleton getInstance() {
if(instance == null) {
instance = new Singleton();
}
return instance;
}
此时的确进行了同步处理,但是这个同步的代价有些大:效率降低,因为整体代码中只有一块需要进行同步处理:instance对象的实例化处理部分,再这样的情况下同步加的位置有些草率!
标准单例设计模式
package Basic;
class Singleton{
private static volatile Singleton instance = null;
private Singleton() {
System.out.println("["+Thread.currentThread().getName()+"]实例化Singleton类对象!");
}
public static Singleton getInstance() {
if(instance == null) {
synchronized (Singleton.class) {
if(instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
public void print() {
System.out.println("test!");
}
}
public class test {
public static void main(String[] args) throws Exception {
for(int x=0; x<3; x++) {
new Thread(()->{
Singleton.getInstance().print();
},"单例消费端-"+ x).start();
}
}
}
面试: 请编写单例设计模式?
- 【100%】直接编写饿汉式的单例设计模式,并且实现构造方法私有化;
- 【120%】在java中哪里使用单例设计模式?
- Runtime类、Pattern类、Spring框架;
- 【200%!!!】懒汉式单例设计模式的问题?