【java反射】反射机制和Class类

116 阅读2分钟

反射机制

提出一个问题

如何根据外部文件配置,指定对象和方法,在不修改源码的情况下,控制程序(符合开闭原则)

// 使用反射机制解决  
//(1) 加载类, 返回 Class 类型的对象 cls  
Class cls = Class.forName(classfullpath);  
//(2) 通过 cls 得到你加载的类 com.hspedu.Cat 的对象实例  
Object o = cls.newInstance();  
System.out.println("o 的运行类型=" + o.getClass()); //运行类型是Cat
//(3) 通过 cls 得到你加载的类 com.hspedu.Cat 的 methodName"hi" 的方法对象  
//在反射中, 可以把方法视为对象(万物皆对象)  
Method method1 = cls.getMethod(methodName);  
//(4) 通过 method1 调用方法: 即通过方法对象来实现调用方法    
method1.invoke(o); //传统方法 对象.方法() , 反射机制 方法.invoke(对象)

反射机制是什么

反射机制允许程序在执行期间,借助ReflectionAPI获取任何类的内部信息,比如成员变量,构造器,成员方法等,并能操作对象的属性和方法。反射在设计模式和底层框架中都会用到。

反射机制原理

加载完类后,在堆中产生了一个Class类型的对象,一个类对应一个对象。这个对象包含了类的完整结构信息,通过这个对象可以获得类的结构,这个Class对象就像一面镜子,透过镜子可以看到类的完整信息,所以,形象的称为反射。

原理图:

image.png

编译阶段Class类加载阶段运行阶段
javac将代码编译成字节码文件类加载器(ClassLoader)将字节码放在堆-->new Cat()
Cat.java --> Cat.class--> Class对象<-- cat找Class对象

反射机制的作用

  1. 在运行时判断任意一个对象所属的类
  2. 在运行时构造任意一个类的对象
  3. 在运行时得到任意一个类所具有的成员方法
  4. 在运行时调用任意一个对象的成员变量和方法

反射机制相关类

  1. java.lang.Class:Class对象表示类加载后在堆中的对象
  2. java.lang.reflect.Method:Method对象表示类的方法
  3. java.lang.reflect.Field:Field对象表示类的成员变量
Field nameField = cls.getField("age"); // 不能得到私有的成员变量
System.out.println(nameField.get(o)); // 传统写法 对象.成员变量 , 反射 : 成员变量对象.get(对象)
  1. java.lang.reflect.Constructor:Constructor对象表示类的构造器
Constructor constructor = cls.getConstructor(); //()中可以指定构造器参数类型, 返回无参构造器  
System.out.println(constructor);//Cat()
Constructor constructor2 = cls.getConstructor(String.class); 
// String.class是String类的Class 对象  
System.out.println(constructor2);//Cat(String name)

这些类在java.lang.reflection

反射机制的优点和缺点

  1. 优点:可以在运行时动态创建和使用对象,没有反射机制,框架失去底层技术支撑
  2. 缺点:反射基本是解释执行,所以执行速度比较慢

优化

  • 关闭访问检查
  • Method\Field\Constructor都有setAccessible(boolean)方法,调用之前设置为true
    优化前:
Class cls = Class.forName("com.hspedu.Cat");  
Object o = cls.newInstance();  
Method hi = cls.getMethod("hi");  
hi.setAccessible(true);//在反射调用方法时,取消访问检查  
long start = System.currentTimeMillis();  
for (int i = 0; i < 900000000; i++) {  
hi.invoke(o);//反射调用方法  
} lo  
ng end = System.currentTimeMillis();  
System.out.println("m3() 耗时=" + (end - start));

Class类

基本概念

  1. Class类对象不是new出来的,而是系统创建的。对于某个类的Class类对象,在内存中只有一份,因为类只加载一次。
// 反射方式
Class acat = Class.forName("com.hsp.Cat");
// 仍然通过ClassLoader加载对象
// ClassLoader.loadClass(String name)  name:"com.hsp.Cat"

Class cat1 = Class.forName("com.hsp.Cat")Class cat2 = Class.forName("com.hsp.Cat"),cat1和cat2是同一个对象

  1. 每个实例都会有对应的Class类对象的信息,因此可以利用Class对象,得到一个类的完整结构
  2. Class类是存放在堆的,类的二进制字节码是存放在方法区的,也称为类的元数据

常用方法

  • static Class forName(String name)
    返回Class类,输出结果如下图 image.png

直接输出? 会显示是哪个类的Class对象
cls.getClass() Class com.hsp.Car
如何得到运行类型?
cls.getClass() Class java.lang.Class
如何得到包名?
cls.getPackage().getName() com.hsp
如何得到全类名?
cls.getName() com.hsp.Car

  • Object newInstance() 创建对象

如何得到对象属性,并设置属性值?
getField() 如何获取对象的所有属性?

获取Class类对象

代码/编译阶段: forName()
案例:Class cls = Class.forName("全类名") 应用场景:配置文件,读取全路径加载类

类加载阶段: 类.class

应用场景:参数传递,比如通过反射获得对象的构造器对象

运行阶段 对象.getClass()

应用场景:通过创建好的对象,获取Class对象

特殊情况

  1. 通过ClassLoader:
    ClassLoader cl = 对象.getClass().getClassLoader();
    Class cls = cl.loadClass("全类名")
  2. 基本数据类型:Class<Integer> cls = int.class
  3. 基本数据类型对应的包装类:Class<Integer> cls = Integer.TYPE

哪些类型有Class对象

1、 外部类,成员内部类,静态内部类,局部内部类,匿名内部类
2、 接口interface
3、 数组
4、 枚举enum
5、 注解annotation
6、 基本数据类型
7、 void

Class<String> cls1 = String.class;//外部类  
Class<Serializable> cls2 = Serializable.class;//接口  
Class<Integer[]> cls3 = Integer[].class;//数组  
Class<float[][]> cls4 = float[][].class;//二维数组  
Class<Deprecated> cls5 = Deprecated.class;//注解  
//枚举  
Class<Thread.State> cls6 = Thread.State.class;  
Class<Long> cls7 = long.class;//基本数据类型  
Class<Void> cls8 = void.class;//void 数据类型
Class<Class> cls9 = Class.class;
  • 输出:sout(cls)
    image.png