最近才开始学Java,这两天接触了框架后对于反射的概念有很大的困惑,在网上检索了很多信息后发现大多都是在说反射怎么使用的。将反射的原理以及为什么需要反射的比较少,即使有讲很多都比较晦涩难懂。
参考: 学习java应该如何理解反射? - 罗大然不写代码的回答 - 知乎)
后来终于找到了一篇知乎回答讲反射的作用的,我觉得讲得很好,加上一些个人的些微见解记录下来。
简单的水果工厂
这位答主用了一个简单的工厂模式举例来说明反射的作用:
// Fruit接口
public interface Fruit {
public void eat();
}
// Apple类
public class Apple implements Fruit{
public void eat() {
System.out.println("eat apple.");
}
}
// Factory 用于生产Fruit
public class Factory {
public static Fruit getInstance(String className){
if(className.equals("Apple")){
return new Apple();
}else{
return null;
}
}
}
// demo
Fruit f = Factory.getInstance("Apple");
f.eat();
这是一个简单的工厂模式范例,工厂通过判断传入的参数来决定生产何种水果。下面我画了一张非常简陋的示意图来说明:
代码和示意图相结合来看:demo程序使用字符串传入了需求,工厂使用if条件来判断需求,使用new对象来生产水果。而判断部分和需求存在严重的耦合:需求的任何改动都需要判断做相应变动。我在需求内每增加一种水果,判断内就得多一条条件分支。
反射的使用
这个代码非常简单,所以修改一下单个类中的代码并不麻烦。如果我们生产水果的不同类型的工厂有一百个、一千个,如果还有和水果相关的其他工厂(生产果汁等)也需要传入需求,那就完全无法解决了。而反射就能够解决这一困难:
// 重写Factory的代码
public class Factory {
public static Fruit getInstance(String className){
// if(className.equals("Apple")){
// return new Apple();
// }else{
// return null;
// }
Fruit f = null;
try {
f = (Fruit) Class.forName(className).newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return f;
}
}
我们通过反射,利用传入的字符串直接获取对应类的class对象,并利用class对象来创建对应的水果实体,这样就不需要在增加需求时反复修改判断部分的代码了。(这应该是称作解耦吧),同样用一张图来解释:
原本的判断部分替换成了反射,也就是说:
- 不再由工厂对需求进行判断
- 需求通过反射直接传递给JVM去查找class
通俗解释
未使用反射之前的代码,我们可以想象成一个工厂有一个专门的订单管理员,这个订单管理员每次接到甲方的需求后都会跟自己手上的生产表单(判断代码)去比对,如果表单里有这一项那么这位管理员就会前往生产线(JVM)上要求开工生产。
生产线接到管理员的指令后,便开始着手准备生产(类加载器相关内容),如果增加了新的需求,那么生产表单也需要同步进行更新,非常麻烦。而且工厂很多,每次新需求都更新表单非常的困难。
于是,这位管理员就直接下岗了(取消判断部分),直接由甲方和生产线对接,需求直接提到了生产线上,生产线根据需求来进行生产:
graph LR
id1{有无生产线}--有-->id2[生产];
id1--无-->id3[查找生产资料];
id3--有资料-->id4[开辟生产线];
id4-->id2;
id3--无资料-->放弃;
这张图涉及到了较浅显的类加载器概念,了解一下类加载器就能将这张图和实际的类加载对应起来了。
原文地址:https://www.cnblogs.com/acct-zcw/p/12299273.html