01 反射机制
什么是反射?
学过Java的人估计都知道反射,反射可以说是Java中一种非常强大的技术,它可以做的事情太多太多。
有句话说的很好:反射是框架的灵魂 。
没有反射就没有那么多优秀的框架,理解反射对以后学习框架底层源码会很有帮,很多优秀的开源框架都是通过反射完成的。
JAVA反射机制是在运行状态 中,对于任意一个类,都能够知道这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法和属性,这种动态获取的信息 以及动态调用对象的 方法的功能称为java语言的反射机制。
上面就是对Java的反射机制的描述,读完感觉怎么样,是不是有点懵,那你接着向下看!!!
举例
何为运行状态中动态获取的信息 以及动态调用对象的方法 ,我们看两段代码。
-
一般情况下,我们使用某个类时必定知道它是什么类,是用来做什么的,于是我们直接对这个类进行实例化,之后使用这个类对象进行操作。
Cat cat=new Cat();
cat.catchMouse();
-
而反射则是一开始并不知道 我要初始化的类对象是什么,自然也无法使用 new 关键字来创建对象了,这时候,我们使用 JDK 提供的反射 API 进行反射调用
String str="com.aismall.reflection.Cat"
Class clz = Class.forName(str);
Object object = cls.newInstance();
Method method = clz.getMethod("catchMouse");
method.invoke(object);
上面两段代码的执行结果是完全一样的,但是其思路完全不一样:
- 第一段代码在
未运行时 就已经确定了要运行的类(Cat)。 - 第二段代码则是在
运行时 通过字符串值 才得知要运行的类(com.aismall.reflection.Cat),然后调用该类的方法,这就是所谓的在运行时动态获取的信息 以及动态调用对象的方法 。
有的人该问了,为什么要这么麻烦,直接new不是更好吗,高搞得花里胡哨的是害怕我们学会吗????
下面我们就来谈谈反射机制的好处!!!!
反射机制的优点
前面提到反射是框架的灵魂 ,学过框架的小伙伴应该知道框架的开闭原则 ,就是对扩展开放,对修改关闭,这就体现出反射的好处了。
刚才我们那个Cat类中只有一个方法catchMouse ,现在我们想给这个Cat增加一个功能sayMiao 。
如果我之前代码中通过new的方式创建了Cat的实例,然后调用了这个catchMouse 方法,如下:
Cat cat=new Cat();
cat.catchMouse();
因为添加了新功能,我们不想使用哪个功能了,我们现在只想让它sayMiao ,不想让它catchMouse 了
可能是这只猫进城了,被当成宠物喵了,每天只要喵喵叫的卖萌就可以了!!!
这时候是不是要修改源码,如下:
Cat cat=new Cat();
cat.sayMiao();
这样做是违反开闭原则的,拓展可以,修改源码就不可以
如果引入反射机制,我们就可以通过修改配置文件,不动源码,来实现功能的修改,如下:
-
配置文件内容如下: classPath=com.aismall.reflection.Cat
//methodName=catchMouse
//我们把methodName的值修改一下,不动下面的源码,就可以实现功能的修改
methodName=sayMiao
Properties properties= new Properties ();
properties.load(new FileInputStream("application.properties"))
String classPath=properties.get("classPath").toString();
String methodName=properties.get("methodName").toString();
Class clz = Class.forName(classPath);
Object object = cls.newInstance();
Method method = cls.getMethod(methodName);
method.invoke(object);
注意:修改配置文件,不是修改源码 ,重申一下 ,开闭原则是,对扩展开放(也就是增加功能是可以修改源码的),对修改关闭(其他情况下的修改是不允许的)。
反射机制的缺点
使用反射基本是解释执行,对执行速度有影响
下面让我们一点一点的来深入的学习反射机制吧!!!!!
02 反射机制的功能
- 在运行时判断任意一个对象所属的类
- 在运行时构造任意一个类的对象
- 在运行时得到任意一个类所具有的成员变量和方法
- 在运行时调用任意一个对象的成员变量和方法
- 生成动态代理
03 反射相关的类
java.lang.Class :代表一个类,Class对象表示某个类加载后在堆中的对象。java.lang.reflect.Constructor :代表类的构造方法,Constructor对象代表某个类的构造方法。java.lang.reflect.Method :代表类的方法,Method对象代表某个类的方法。java.lang.reflect.Field :代表类的成员变量,Field对象代表某个类的成员变量。
说一下几个注意的点:
- Method类的对象一般是无法获取类的私有属性
- 通过Constructor对象的方法不仅可以获取空参构造函数,也可以获取带参数的构造函数。
04 Class类
概述
-
Class也是类,因此也继承Object类,如下图: -
Class类的对象不是new出来的,而是系统创建的。 -
对于某个类对应的Class类对象,在内存中只有一份,因此类只加载一次(想要详细了解的可以去查:双亲委派机制 )
Class cls1=Class.forName("com.aismall.reflection.Cat");
Class cls2=Class.forName("com.aismall.reflection.Cat");
System.out.println(cls1.hashCode());
System.out.println(cls2.hashCode());
-
每个类的实例都会记得自己是由那个Class实例所生成 -
通过Class类可以完整得到一个类的完整结构,通过一系列API -
Class对象是存放在堆区的 -
类的字节码二进制数据是存放在方法区的,有的地方也把方法区称为元数据去,用来存放类的元数据(包括方法代码,类名,方法名,访问权限等)
代码演示
Cat.class
public class Cat {
public String name;
public int age;
public Cat() {
}
public void catchMouse(){
System.out.println("猫爪老鼠");
}
}
testCat.class
public class testCat {
public static void main(String[] args) {
Class cls=Class.forName("com.aismall.reflection.Cat");
System.out.println(cls);
System.out.println(cls.getClass());
System.out.println(cls.getPackage());
System.out.println(cls.getName());
Cat cat=(Cat)cls.newInstance();
Field age=cls.getField("age");
System.out.println(age.get(cat));
age.set(cat,666);
System.out.println(age.get(cat));
Field[] fields=cls.getFields();
for (Field field:fields) {
System.out.println(field.getName());
}
Constructor constructor=cls.getConstructor();
Cat cat1=(Cat) constructor.newInstance();
cat1.catchMouse();
}
}
获取Class类对象的六种方式
方式一:
- 前提:已知一个类的全类名,且该类在类路径下,可通过Class类的静态方法
forName() 获取,可能抛出ClassNotFoundException - 实例:
Class cls1=Class.forName(classPath); - 应用场景:多用于配置文件读取类全路径加载类。
方式二:
- 前提:若已知具体的类,通过类的Class获取,该方式最为安全可靠,程序性能最高
- 实例:
Class cls2=Cat.class; - 应用场景:多用于参数传递,比如通过反射得到对应构造器对象。
方式三:
- 前提:已知某个类的实例,调用该实例的
getClass() 方法获取Class对象。 - 实例:
Class clazz=对象.getClass(); - 应用场景:通过创建好的对象获取Class对象
方式四:通过类加载器来获取类的对象
方式五:基本数据类型按照如下方式获得Class对象
方式六:基本数据类型对应的包装类,可以通过包装类.TYPE来获取对应的Class对象
public class testCat {
public static void main(String[] args) throws ClassNotFoundException {
String classPath="com.aismall.reflection.Cat";
Class cls1=Class.forName(classPath);
System.out.println(cls1);
Class cls2=Cat.class;
System.out.println(cls2);
Cat cat=new Cat();
Class cls3=cat.getClass();
System.out.println(cls3);
ClassLoader classLoader=cat.getClass().getClassLoader();
Class cls4=classLoader.loadClass(classPath);
System.out.println(cls4);
System.out.println(cls1.hashCode());
System.out.println(cls2.hashCode());
System.out.println(cls3.hashCode());
System.out.println(cls4.hashCode());
Class<Integer> cls5=int.class;
System.out.println(cls5);
Class cls6=Integer.TYPE;
System.out.println(cls6);
System.out.println(cls5.hashCode());
System.out.println(cls6.hashCode());
}
}
|