0、工厂模式分析
对于程序的开发模式之前一直强调:尽量减少耦合,而减少耦合的最好做法是使用接口,但是就算使用了接口也逃不出关键字new,所以实际上new是造成耦合的关键元凶。
没有使用反射
package CoreJavaColume.Chapter5.Factory.OldModel;
/**
* @Author Zhou jian
* @Date 2020 ${month} 2020/1/17 0017 20:04
*/
public interface Fruit {
public void eat();
}
package CoreJavaColume.Chapter5.Factory.OldModel;
/**
* @Author Zhou jian
* @Date 2020 ${month} 2020/1/17 0017 20:04
*/
public class Apple implements Fruit {
@Override
public void eat() {
System.out.println("吃苹果");
}
}
/**
* 旧的模式
* @param className
* @return
*/
public static Fruit getInstance(String className){
//这种工厂模式每当 添加一种水果都需要进行添加
//if -- else的判断
if("apple".equals(className)){
return new Apple();//new 对象
}
return null;
}
使用反射
/**
* 使用到反射:Class
* @param className 类的全路径
* 以上为之前所编写最简单的工厂设计模式,但是在这个工厂设计模式之中有一个最大的问题:如果现在接口的子类增加了,那么工厂类肯定需要修改,这是它所面临的最大问题,而这个最大问题造成的关键性的病因是new,
* 那么如果说现在不使用关键字new了,变为了反射机制呢?
*/
public static Fruit getInstanceByReflect(String className){
//使用反射的方法:这个类中的这种方法就不需要改变
//根据传入类的全路径名称:即可构建对象
try {
Class clz = Class.forName(className);
//根据类对象创建 对象
Fruit fruit = (Fruit)clz.newInstance();
System.out.println(fruit);
return fruit;
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
return null;
}
使用反射的好处:
1、反射基础
1.1、什么是反射
反射就是在运行时才知道要操作的类是什么,并且可以在运行时获取类的完整构造,并调用对应的方法
Oracle的官方解释是:
Reflection enables Java code to discover information about the fields, methods and constructors of loaded classes, and to use reflected fields, methods, and constructors to operate on their underlying counterparts, within security restrictions.
The API accommodates applications that need access to either the public members of a target object (based on its runtime class) or the members declared by a given class. It also allows programs to suppress default reflective access control.
通过反射,我们可以在运行是获得程序或程序集中每一个类型的成员的信息。程序中一般的对象的类型都是在编译期就确定下来的,而Java反射机制可以动态的创建对象并调用其属性,这样的对象的类型在编译期是未知的。我们可以通过反射机制直接创建对象,即使这个对象的类型在编译期是未知的
反射的核心是JVM在运行时才动态的加载类或调用方法/访问属性,它不需要事先(写代码的时候或编译期)直到运行对象是谁
Java反射主要
提供以下功能:
- 在
运行时
判断任意一个对象所属的类 - 在
运行时
构造任意一个类的对象 - 在
运行时
判断任意一个类所具有的成员变量和方法(通过反射甚至可以调用private方法) - 在
运行时
调用任意一个对象的方法 - 在
运行时
分析注解
1.2、为什么需要反射
可以思考工厂模式中的if-else判断问题以及直接使用全类路径名称然后再运行时创建对象的这样一种耦合性
1.3、反射作用
解决耦合的问题,动态扩展,解耦
、
在一些框架中大量使用:
Spring的配置:
XML的方式
:在XML中的这种配置方式,框架会解析xml标签中的值,依据依赖关系,然后使用反射构造对象。
<!-- 视图解析器 -->
<bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages/"/>
<property name="suffix" value=".jsp"/>
</bean>
注解的方式:
在大量的代码中使用注解,在程序运行时,分析注解可以获取对象
1.4、反射原理
2、Java中的反射API
2.0、反射基本使用
-
获取类的Class对象
Class clz = Class.forName("com.zhenai.api.Apple");
-
根据 Class 对象实例获取 Constructor 对象
Constructor appleConstructor = clz.getConstructor();
- 使用 Constructor 对象的 newInstance 方法获取反射类对象
Object appleObj = appleConstructor.newInstance();
而如果要调用某一个方法,则需要经过下面的步骤:
- 获取方法的 Method 对象
Method setPriceMethod = clz.getMethod("setPrice", int.class);
- 利用 invoke 方法调用方法
setPriceMethod.invoke(appleObj, 14);
2.1、Class
在程序运行期间,Java运行时系统始终为所有对象维护一个被称为运行时的类型标志。这个信息跟踪着每个对象所属的类。虚拟机利用这些运行时类型信息选择相应额方法执行
可以通过专门的Java类访问这些信息,保存这些信息的类被称为Class
虚拟机为每个类型管理一个Class对象。因此,可以利用==运算符实现两个类对象的比较
2.1.0、class对象的源码
// reflection data that might get invalidated when JVM TI RedefineClasses() is called
private static class ReflectionData<T> {
volatile Field[] declaredFields;
volatile Field[] publicFields;
volatile Method[] declaredMethods;
volatile Method[] publicMethods;
volatile Constructor<T>[] declaredConstructors;
volatile Constructor<T>[] publicConstructors;
// Intermediate results for getFields and getMethods
volatile Field[] declaredPublicFields;
volatile Method[] declaredPublicMethods;
volatile Class<?>[] interfaces;
// Value of classRedefinedCount when we created this ReflectionData instance
final int redefinedCount;
ReflectionData(int redefinedCount) {
this.redefinedCount = redefinedCount;
}
}
//在Class类中定义了一个静态内部类,在其类的内部
//定义了一些成员变量,用来映射加载的类文件对应的一些成员属性:域,构造函数,方法
2.1.1、获取Class对象
三种方法:
- **使用 Class.forName 静态方法。**当你知道该类的全路径名时,你可以使用该方法获取 Class 类对象。
Class clz = Class.forName("java.lang.String");
- 使用 .class 方法。
Class clz = String.class;
-
使用类对象的 getClass() 方法(在Object类中的方法)。
String str = new String(“Hello”);
Class clz = str.getClass();
```
2.1.2、使用Class对象生成新对象
通过反射创建类的对象主要有两种方式:通过Class对象的newUnstance()方法;通过constructor对象的newInstance方法
-
通过Class对象的newInstacne方法:只能使用默认的无参数的构造方法-,若无默认构造器会抛出异常
/** Creates a new instance of the class represented by this {@code Class} * object. The class is instantiated as if by a {@code new} * expression with an empty argument list. The class is initialized if it * has not already been initialized.*/ Class clz = Apple.class; Apple apple = (Apple)clz.newInstance();
-
通过 Constructor 对象的 newInstance() 方法 :可以选择特定的构造方法
Class clz = Apple.class;
Constructor constructor = clz.getConstructor();
Apple apple = (Apple)constructor.newInstance();
///可以选择特定的构造方法
Class clz = Apple.class;
Constructor constructor = clz.getConstructor(String.class, int.class);
Apple apple = (Apple)constructor.newInstance("红富士", 15);
2.2、 Method
2.2.1、获取Method
getDeclaredMethods
方法返回类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。
public Method[] getDeclaredMethods() throws SecurityException
- ·
getMethods
方法返回某个类的所有公用(public)方法,包括其继承类的公用方法。
public Method[] getMethods() throws SecurityException
getMethod
方法返回一个特定的方法,其中第一个参数为方法名称,后面的参数为方法的参数对应Class的对象。
public Method getMethod(String name, Class<?>... parameterTypes)
2.2.2、使用Method调用方法
当我们从类中获取了一个方法后,我们就可以用 invoke()
方法来调用这个方法。invoke
方法的原型为:
ublic Object invoke(Object obj, Object... args)
throws IllegalAccessException, IllegalArgumentException,
InvocationTargetException
2.3、Constructor
获取类构造器的用法与上述获取方法的用法类似。主要是通过Class类的getConstructor方法得到Constructor类的一个实例,而Constructor类有一个newInstance
方法可以创建一个对象实例:
public T newInstance(Object ... initargs)
2.4、Field
-
通过Class对象的
getFields()
方法可以获取Class类的属性,但无法获取私有属性。Class clz = Apple.class; Field[] fields = clz.getFields(); for (Field field : fields) { System.out.println(field.getName()); }
-
如果使用 Class 对象的
getDeclaredFields()
方法则可以获取包括私有属性在内的所有属性
Class clz = Apple.class;
Field[] fields = clz.getDeclaredFields();
for (Field field : fields) {
System.out.println(field.getName());
}