Java Class和反射机制
参考资料:https://blog.****.net/javazejian/article/details/70768369
一. 深入理解Class对象
1. RRTI的概念以及Class对象作用
1.1.RRTI:(Run time type identification) 运行时类型识别
- 传统的RRTI: 在编译期知道所有类型
- 反射机制: 我们在运行时发现和使用类型的信息
1.2.Class类(用来表示运行时的类型信息的类)
Class对象表示的是自己手动编写类的类型信息, 比如创建一个Shapes类,那么,JVM就会创建一个Shapes对应Class类的Class对象,该Class对象保存了Shapes类相关的类型信息,并且保存在同名.class文件里.
当我们new一个新对象或者引用静态成员变量时,Java虚拟机(JVM)中的类加载器子系统会将对应Class对象加载到JVM中,然后JVM再根据这个类型信息相关的Class对象创建我们需要实例对象或者提供静态变量的引用值.
注意,Class对象具有唯一性,即一个类只有一个Class对象.
2.Class对象的加载和获取方式
2.1 Class对象的加载
Class对象是由JVM加载的,那么其加载时机是?实际上所有的类都是在对其第一次使用时动态加载到JVM中的
2.2 Class forName()方法
通过类的名称来获取Class对象
2.3 getClass()方法
通过类的实例来获取Class对象
2.4 Class字面常量
通过字面量的方法获取Class对象的引用不会自动初始化该类,效率更高
类加载过程:
- 加载:类加载过程的一个阶段:通过一个类的完全限定查找此类字节码文件,并利用字节码文件创建一个Class对象
- 链接:验证字节码的安全性和完整性,准备阶段正式为静态域分配存储空间,注意此时只是分配静态成员变量的存储空间,不包含实例成员变量,如果必要的话,解析这个类创建的对其他类的所有引用。
- 初始化:类加载最后阶段,若该类具有超类,则对其进行初始化,执行静态初始化器和静态初始化成员变量。
2.5 必须被类执行初始化的4种场景
-
使用new关键字实例化对象时、读取或者设置一个类的静态字段(不包含编译期常量)以及调用静态方法的时候,必须触发类加载的初始化过程(类加载过程最终阶段)。
-
使用反射包(java.lang.reflect)的方法对类进行反射调用时,如果类还没有被初始化,则需先进行初始化,这点对反射很重要。
-
当初始化一个类的时候,如果其父类还没进行初始化则需先触发其父类的初始化.
-
当Java虚拟机启动时,用户需要指定一个要执行的主类(包含main方法的类),虚拟机会先初始化这个主类
2.6理解泛化的Class对象引用
Class intClass=int.class;
//任意类但没有泛型约束
Class<Integer> integerClass=int.class;
//Integer类
Class<?> intergetClass2=int.class;
//任意类有泛型约束
Class<? extends Number> integerClass3=int.class;
//任意继承了Number的类
intClass=Number.class;
integerClass=Number.class;//编译错误
intergetClass2=Number.class;
integerClass3=Integer.class;
2.7关于类型转换的问题
2.7.1 强制类型转换
Animal animal= new Dog();
//强制转换
Dog dog = (Dog) animal;
在Java中,所有类型转换都是在运行时进行正确性检查的,利用RRTI进行判断类型是否正确从而确保强制转换的完成,如果类型转换失败,将会抛出类型转换异常
2.7.2 通过Class对象进行类型转换(更加麻烦)
Animal animal= new Dog();
//这两句等同于Dog dog = (Dog) animal;
Class<Dog> dogType = Dog.class;
Dog dog = dogType.cast(animal)
2.8 instanceof关键字与isInstance方法
2.8.1 instanceof关键字
instanceof 关键字,它返回一个boolean类型的值,意在告诉我们对象是不是某个 特定的类型实例。如下,在强制转换前利用instanceof检测obj是不是Animal类型的 实例对象,如果返回true再进行类型转换,这样可以避免抛出类型转换的异常.
public void cast2(Object obj){
if(obj instanceof Animal){
Animal animal= (Animal) obj;
}
}
2.8.2 isInstance方法
属于Native方法,也是用于判断obj的类型,效果与instanceof等价
二. 理解反射技术
反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法; 对于任意一个对象,都能够调用它的任意一个方法和属性
1.Constructor类及其用法
反映的是Class 对象所表示的类的构造方法。获取Constructor对象是通过Class 类中的方法获取的
Constructor<T> getConstructor(Class<?>... parameterTypes) 返回指定参数类型、具有public访问权限的构造函数对象
Constructor<?>[] getDeclaredConstructor() 返回所有声明的(包括private)构造函数对象
Class<?> testClass=test.class;
test test1= (test) testClass.newInstance();
//调用默认的构造方法
test1.showstate();
Constructor<?> constructor=testClass.getConstructor(String.class);
test test2= (test) constructor.newInstance("This is Public");
//调用public构造方法
test2.showstate();
constructor=testClass.getDeclaredConstructor(String.class,int.class);
//调用private构造方法
constructor.setAccessible(true);//让方法可访问.
test test3=(test) constructor.newInstance("this is Private",1);
test3.showstate();
2.Field类及其用法
Field 提供有关类或接口的单个字段的信息,以及对它的动态访问权限。反射的字段可能是一个类(静态)字段或实例字段
class Person{
public String name;
public int age;
}
class Student extends Person{
private int score;
public int classNumber;
static int score2;
public void showStatus(){
System.out.println("name:"+name);
System.out.println("age:"+age);
System.out.println("score:"+score);
System.out.println("classNumber:"+classNumber);
}
}
public class FieldTest {
public static void main(String[] args) throws Exception {
Class<?> aClass = Student.class;
Field[] fields = aClass.getFields();
for(Field field:fields){
//输出父类和自己的public属性字段
System.out.println("getFields(): "+field);
}
fields=aClass.getDeclaredFields();
for(Field field:fields){
//输出自己的所有字段
System.out.println("getDeclaredFields():"+field);
}
Student student= (Student) aClass.newInstance();
Field name=aClass.getField("name");//获得对应字段
Field score=aClass.getDeclaredField("score");
score.setAccessible(true);//设置权限可以获取
name.set(student,"123");//修改属性值
score.set(student,100);
student.showStatus();
}
}
3. Method类及其用法(获取函数名具体和Field相同)
Class clazz = Class.forName("reflect.Circle");
//创建对象
Circle circle = (Circle) clazz.newInstance();
//获取指定参数的方法对象Method
Method method = clazz.getMethod("draw",int.class,String.class);
//通过Method对象的invoke(Object obj,Object... args)方法调用
method.invoke(circle,15,"圈圈");
//对私有无参方法的操作
Method method1 = clazz.getDeclaredMethod("drawCircle");
//修改私有方法的访问标识
method1.setAccessible(true);
method1.invoke(circle);
//对有返回值的方法操作
Method method2 =clazz.getDeclaredMethod("getAllCount");
Integer count = (Integer) method2.invoke(circle);
System.out.println("count:"+count);