反射基础与原理

一、反射的定义

反射是Java被视为动态语言的关键。
在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;
对于任意一个对象,都能够调用它的任意方法和属性;并且能改变它的属性。

二、三种方式获取Class对象

  1. 通过类名获取 类名.class
Class clazz = Person.class;
  1. 通过对象获取 对象名.getClass()
Person p = new Pserson();
Class clazz = p.getClass();
  1. 通过全类名获取 Class.forName(全类名) classLoader.loadClass(全类名)
 String className = "com.test.refle.Person";
 Class clazz = Class.forName(className);

三、反射基本操作

1.获取构造信息

//获取所有构造方法
Constructor[] constructors = (Constructor[]) clazz.getConstructors();
//获取单个指定参数的构造方法
Constructor constructor = clazz.getConstructor(String.class, int.class);
//使用构造器创建对象
Person p= constructor.newInstance("fanshao", 21);

2.获取方法信息

//获取所有公共和父类方法
Method[] methods = clazz.getMethods();
//获取所有方法,包括私有方法
methods = clazz.getDeclaredMethods();
//获取指定方法
Method method = clazz.getDeclaredMethod("setName", String.class);

//反射调用方法
Object p = clazz.newInstance();
method.invoke(p ,"fanshao");

//反射调用私有方法
method = clazz.getDeclaredMethod("setRealName", String.class);
method.setAccessible(true);
method.invoke(p, "fanshao-real");

3.获取字段信息

//获取所有字段,包括私有字段
Field[] fields = clazz.getDeclaredFields();
//获取指定字段
Field field = clazz.getDeclaredField("name");
//获取字段的值
Person p = new Person("fnashao",21);
Object val = field.get(person);
//设置字段的值
field.setAccessible(true);
field.set(person,"fanshao2");

4.通过反射创建数组

Array.newInstance(String.class, 12);

5.通过反射获取泛型信息

1.TypeVariable

泛型类型变量,封装泛型上下限等信息。

public class TypeVariableTest {

    T t;
    K k;
    public static void main(String[] args) {

        try {
            // 获取字段的类型
            Field field1 = TypeVariableTest.class.getDeclaredField("t");
            Field field2 = TypeVariableTest.class.getDeclaredField("k");
            TypeVariable typeT = (TypeVariable) field1.getGenericType();
            TypeVariable typeK = (TypeVariable) field2.getGenericType();
            //获取字段名称
            System.out.print(typeT.getName() + "\n"); // T
            System.out.print(typeK.getName() + "\n"); // K
            // 获取泛型上界
            /*泛型T的上界:interface java.lang.Cloneable
            泛型T的上界:interface java.io.Closeable*/
            for (Type type : typeT.getBounds()) {
                System.out.print("泛型T的上界:" + type + "\n");
            }

            /*泛型K的上界:class java.lang.Object   默认上界为Object*/
            for (Type type : typeK.getBounds()) {
                System.out.print("泛型K的上界:" + type + "\n");
            }
        } catch (Exception e) {
            System.out.print(e.getMessage());
        }
    }
}
2.ParameterizedType

具体的泛型类型,可以获得元数据中泛型签名类型(泛型真实类型)。

public class ParameterizedTypeTest {

    Map map;
    List[] list;
    public static void main(String[] args)  {

        try{
            Field f = ParameterizedTypeTest.class.getDeclaredField("map");
            
            // java.util.Map
            System.out.println(f.getGenericType()); 
            ParameterizedType pType = (ParameterizedType) f.getGenericType();
            
            //interface java.util.Map
            System.out.println(pType.getRawType());
            
            //class java.lang.String
            //class java.lang.Integer
            for (Type type : pType.getActualTypeArguments()) {
                System.out.println(type);
            }
        }catch (Exception e) {
            System.out.println(e.getMessage());
        }

    }
}

如果是数组,通过GenericArrayType获取

Field f = ParameterizedTypeTest .class.getDeclaredField("list");
GenericArrayType genericType = (GenericArrayType) f.getGenericType();

四、反射的原理

1.类加载机制

要理解反射的原理,需要先搞清楚类加载机制。

Java文件需要经历编译和运行两个过程。
编译就是通过javac命令将.java文件编译成字节码即.class文件;
运行则是把编译生成的.class文件交给Java虚拟机(JVM)执行。
类加载过程就是指JVM虚拟机把.class文件中类信息加载进内存,并进行解析生成对应的class对象的过程。
另外JVM是按需加载,某个类在用到的时候才会加载,且只会加载一次。

类的加载机制有三个阶段:加载、链接、初始化;三个阶段交叉进行。
链接又有三个过程:验证、准备、解析。

2.Java的反射

Java的反射和类加载过程是类似的。
Java的反射就是利用上面第一步加载到jvm中的.class文件来进行操作的。.class文件中包含java类的所有信息,当你不知道某个类具体信息时,可以使用反射获取class,然后进行各种操作。

3.Java的反射和ClassLoader对比

Class.forName 和 ClassLoader 都可以用来装载类,如前面说的包含加载、链接、初始化等操作,但是它们装载类的方式是有区别,简单来说:ClassLoader的loadClass不会执行类的初始化代码。
Class.forName()前者除了将类的.class文件加载到jvm中之外,还会对类进行解释,执行类中的static块。
如果有静态变量,变量的赋值执行了静态方法,那么Class.forName()也会执行静态方法。
并且静态代码块的执行顺序先于静态方法。
Class.forName(name, initialize, loader)带参函数也可控制是否加载static块。
而classLoader只干一件事情,就是将.class文件加载到jvm中,不会执行static中的内容,只有在newInstance才会去执行static块。

你可能感兴趣的:(反射基础与原理)