Java Class和反射机制

参考资料:https://blog.csdn.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字面常量

Java Class和反射机制

通过字面量的方法获取Class对象的引用不会自动初始化该类,效率更高
类加载过程:
Java Class和反射机制

  • 加载:类加载过程的一个阶段:通过一个类的完全限定查找此类字节码文件,并利用字节码文件创建一个Class对象
  • 链接:验证字节码的安全性和完整性,准备阶段正式为静态域分配存储空间,注意此时只是分配静态成员变量的存储空间,不包含实例成员变量,如果必要的话,解析这个类创建的对其他类的所有引用。
  • 初始化:类加载最后阶段,若该类具有超类,则对其进行初始化,执行静态初始化器和静态初始化成员变量。

2.5 必须被类执行初始化的4种场景

  1. 使用new关键字实例化对象时、读取或者设置一个类的静态字段(不包含编译期常量)以及调用静态方法的时候,必须触发类加载的初始化过程(类加载过程最终阶段)。

  2. 使用反射包(java.lang.reflect)的方法对类进行反射调用时,如果类还没有被初始化,则需先进行初始化,这点对反射很重要。

  3. 当初始化一个类的时候,如果其父类还没进行初始化则需先触发其父类的初始化.

  4. 当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);