第一章:类的加载

1.1 概述

  • 类在内存中的生命周期是加载、使用、卸载。

1.2 类的加载过程

  • 当程序主动使用某个类的时候,如果该类还没有被加载到内存中,系统会通过加载、连接、初始化三个步骤来对该类进行初始化,如果没有意外,JVM 将会连续完成这三个步骤,所以有时也会将这三个步骤称为类加载。
  • 类的加载分为三个阶段:
    • ① 加载(load):就是将 *.class 字节码数据读取到内存中。
    • ② 连接(link):
      • 验证:检验合法性等。
      • 准备:准备对应的内存(方法区)、创建 Class 对象、为类变量赋默认值、为静态常量赋初始化值。
      • 解析:将字节码中的符号引用替换为对应的直接地址引用。
    • ③ 初始化(initialize):类初始化,执行 <clinit> 类初始化方法,大多数情况下,类的加载就完成了类的初始化,有些情况下,会延迟类的初始化。

类的加载过程.png

1.3 类初始化

1.3.1 哪些操作会导致类的初始化?

  • ① 运行主方法所在的类,要先完成类初始化,再执行 main 方法。
  • ② 第一次使用某个类型,即 new 它的对象的时候,此时这个类还没有初始化的话,先完成类初始化再做实例初始化。
  • ③ 调用某个类的静态成员(类变量和类方法),此时这个类没有初始化的话,先完成类初始化。
  • ④ 子类初始化的时候,发现它的父类还没有初始化的话,那么先初始化父类。
  • ⑤ 通过反射操作某个类的时候,如果这个类没有初始化,也会导致该类先初始化。

  • 示例:

  1. package com.github.demo2;
  2. class Father{
  3. static{
  4. System.out.println("main方法所在的类的父类(1)");//初始化子类时,会初始化父类
  5. }
  6. }
  7. public class TestClinit1 extends Father{
  8. static{
  9. System.out.println("main方法所在的类(2)");//主方法所在的类会初始化
  10. }
  11. public static void main(String[] args) throws ClassNotFoundException {
  12. new A();//第一次使用A就是创建它的对象,会初始化A类
  13. B.test();//直接使用B类的静态成员会初始化B类
  14. Class clazz = Class.forName("com.github.demo2.C");//通过反射操作C类,会初始化C类
  15. }
  16. }
  17. class A{
  18. static{
  19. System.out.println("A类初始化");
  20. }
  21. }
  22. class B{
  23. static{
  24. System.out.println("B类初始化");
  25. }
  26. public static void test(){
  27. System.out.println("B类的静态方法");
  28. }
  29. }
  30. class C{
  31. static{
  32. System.out.println("C类初始化");
  33. }
  34. }

1.3.2 哪些使用类的操作,不会导致类的初始化?

  • ① 使用某个类的静态常量(static final)。
  • ② 通过子类调用父类的静态变量、静态方法,只会导致父类初始化,不会导致子类初始化,即只有声明静态成员的类才会初始化。
  • ③ 用某个类型声明数组并创建数组对象时,不会导致这个类初始化。

  • 示例:

package com.github.demo3;

public class TestClinit2 {
   public static void main(String[] args) {
      System.out.println(D.NUM);// D类不会初始化,因为NUM是final的

      System.out.println(F.num);
      F.test();// F类不会初始化,E类会初始化,因为num和test()是在E类中声明的

      // G类不会初始化,此时还没有正式用的G类
      G[] arr = new G[5];// 没有创建G的对象,创建的是准备用来装G对象的数组对象
        // G[]是一种新的类型,是数组类想,动态编译生成的一种新的类型
        // G[].class
   }
}
class D{
   public static final int NUM = 10;
   static{
      System.out.println("D类的初始化");
   }
}
class E{
   static int num = 10;
   static{
      System.out.println("E父类的初始化");
   }
   public static void test(){
      System.out.println("父类的静态方法");
   }
}
class F extends E{
   static{
      System.out.println("F子类的初始化");
   }
}

class G{
   static{
      System.out.println("G类的初始化");
   }
}

1.4 类加载器

1.4.1 概述

  • 很多开发人员都遇到过 java.lang.ClassNotFoundException 或 java.lang.NoClassDefError ,想要更好的解决这类问题,或者在一些特殊的应用场景,比如需要支持类的动态加载或需要对编译后的字节码文件进行加密解密操作,那么需要你自定义类加载器,因此了解类加载器及其类加载机制也就成为每一个 Java 开发人员的必备技能之一。

1.4.2 类加载的分类

  • ① 引导类加载器(Bootstrap Classloader)又称为根类加载器:它负责加载 jre/rt.jar 核心库,它本身不是 Java 代码实现的,也不是 ClassLoader 的子类,获取它的对象时往往返回 null 。
  • ② 扩展类加载器(Extension ClassLoader):它负责加载 jre/lib/ext 扩展库,它是 ClassLoader 的子类。
  • ③ 应用程序类加载器(Application Classloader):它负责加载项目的classpath路径下的类,它是 ClassLoader 的子类。
  • ④ 自定义类加载器:当你的程序需要加载“特定”目录下的类,可以自定义类加载器;当你的程序的字节码文件需要加密时,那么往往会提供一个自定义类加载器对其进行解码。

1.4.3 Java 系统类加载器的双亲委托模式

  • 双亲委托模式:下一级的类加载器,如果接到任务时,会先搜索是否加载过,如果没有,会先把任务往上传,如果都没有加载过,一直到根加载器,如果根加载器在它负责的路径下没有找到,会往回传,如果一路回传到最后一级都没有找到,那么会报 ClassNotFoundException 或 NoClassDefError ,如果在某一级找到了,就直接返回 Class 对象。
  • 应用程序类加载器把扩展类加载器视为父加载器,扩展类加载器把引导类加载器视为父加载器。
  • 不是继承关系,是组合的方式实现的。

第二章:反射

2.1 概述

  • 反射(Reflection)是被视为动态语言的关键,反射机制允许程序在执行期间借助反射的 API 取得任何类的内部信息,并能直接操作任意对象的内部属性和方法。
  • 加载完类之后,在堆内存的方法区中就产生了一个 Class 类型的对象(一个类只有一个 Class 对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像是一面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为:反射

反射概述.png

2.2 动态语言 VS 静态语言

2.2.1 动态语言

  • 是一类在运行时可以改变其结构的语言:例如新的函数、对象、甚至代码可以被引进,已有的函数可以被删除或其他结构上的变化。通俗的说就是在运行时代码可以根据某些条件改变自身结构
  • 主要的动态语言:PHP 、Python 等。

2.2.2 静态语言

  • 和动态语言相对应的,运行时结构不可变的语言就是静态语言。如:Java 、C 、C++ 。
    • Java 不是动态语言,但是 Java 可以称之为“准动态语言”,即 Java 有一定的动态性,我们可以利用反射机制、字节码操作获得类似动态语言的特性。
    • Java 的动态性让编程的时候更加灵活。

2.3 Java反射机制提供的功能

  • ① 在运行时判断任意一个对象所属的类。
  • ② 在运行时构造任意一个类的对象。
  • ③ 在运行时判断任意一个类所具有的成员变量和方法。
  • ④ 在运行时获取泛型新。
  • ⑤ 在运行时调用任意一个对象的成员变量和方法。
  • ⑥ 在运行时处理注解。
  • ⑦ 生成动态代理。

2.4 java.lang.Class 对象

2.4.1 概述

  • 要想剖析一个类,必须先获取该类的 Class 对象,而剖析一个类或用反射解决具体的问题就是使用相关的 API :
    • ① java.lang.Class 。
    • ② java.lang.reflect.* 。
  • 所以,Class对象是反射的根源。

2.4.2 哪些类型可以获取 Class 对象?

  • 所有的 Java 类型。

  • 示例:

package com.github.demo5;

import java.lang.annotation.ElementType;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2021-10-07 07:29
 */
public class Test {
    public static void main(String[] args) {
        // 基本数据类型和void
        Class<Integer> integerClass = int.class;
        System.out.println("integerClass = " + integerClass);
        Class<Void> voidClass = void.class;
        System.out.println("voidClass = " + voidClass);

        // 类和接口
        Class<String> stringClass = String.class;
        System.out.println("stringClass = " + stringClass);
        Class<Comparable> comparableClass = Comparable.class;
        System.out.println("comparableClass = " + comparableClass);

        // 枚举
        Class<ElementType> elementTypeClass = ElementType.class;
        System.out.println("elementTypeClass = " + elementTypeClass);

        // 注解
        Class<Override> overrideClass = Override.class;
        System.out.println("overrideClass = " + overrideClass);

        // 数组
        Class<int[]> aClass = int[].class;
        System.out.println("aClass = " + aClass);
    }
}

2.4.3 获取 Class 对象的四种方式

  • 类型名.class,要求在编译期间已知类型。
  • 对象.getClass(),要求获取对象的运行时类型。
  • Class.forName("全类名"),可以获取编译期间未知的类型。
  • ClassLoader的类加载器对象.loadClass("全类名")

  • 示例:

package com.github.demo4;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2021-10-06 22:16
 */
public class Person {
    private String name;

    private Integer age;

    public Person() {}

    public Person(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return this.age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" + "name='" + this.name + '\'' + ", age=" + this.age + '}';
    }
}
package com.github.demo4;


/**
 * @author 许大仙
 * @version 1.0
 * @since 2021-10-06 22:16
 */
public class Test {
    public static void main(String[] args) throws ClassNotFoundException {
        Class<?> clazz = Person.class;
        System.out.println("clazz = " + clazz);

        Person person = new Person();
        Class<?> aClass = person.getClass();
        System.out.println("aClass = " + aClass);

        Class<?> aClass1 = Class.forName("com.github.demo4.Person");
        System.out.println("aClass1 = " + aClass1);

        Class<?> aClass2 = Test.class.getClassLoader().loadClass("com.github.demo4.Person");
        System.out.println("aClass2 = " + aClass2);
    }
}

2.5 获取类型的详细信息

2.5.1 概述

  • 反射可以获取包、修饰符、类型名、父类(包括泛型父类)、父接口(包括泛型父接口)、成员(属性、构造器、方法)、注解。

2.5.2 获取包信息

  • 获取包信息:
public Package getPackage() {}
  • 示例:
package com.github.demo6;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2021-10-06 22:16
 */
public class Person {
    private String name;

    private Integer age;

    public Person() {}

    public Person(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return this.age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}
package com.github.demo6;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2021-10-07 11:13
 */
public class Test {
    public static void main(String[] args) {
        Class<?> clazz = Person.class;

        // 获取包信息
        Package aPackage = clazz.getPackage();
        String name = aPackage.getName();
        System.out.println("name = " + name); // name = com.github.demo6
    }
}

2.5.3 获取修饰符

  • 获取修饰符(修饰符定义在 Modifier 类中,该类里面有很多常量值,每一个常量对应一种修饰符):
public native int getModifiers();
  • 示例:
package com.github.demo6;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2021-10-06 22:16
 */
public class Person {
    private String name;

    private Integer age;

    public Person() {}

    public Person(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return this.age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}
package com.github.demo6;

import java.lang.reflect.Modifier;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2021-10-07 11:23
 */
public class Test2 {
    public static void main(String[] args) {
        Class<?> clazz = Person.class;

        // 获取修饰符
        int modifiers = clazz.getModifiers();
        String s = Modifier.toString(modifiers);
        System.out.println("s = " + s); // s = public
    }
}

2.5.4 获取类名

  • 获取类名:
public String getName() {}
public String getSimpleName() {}
  • 示例:
package com.github.demo6;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2021-10-06 22:16
 */
public class Person {
    private String name;

    private Integer age;

    public Person() {}

    public Person(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return this.age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}
package com.github.demo6;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2021-10-07 11:33
 */
public class Test3 {
    public static void main(String[] args) {
        Class<?> clazz = Person.class;
        // 获取类名
        String name = clazz.getName();
        System.out.println("name = " + name); // name = com.github.demo6.Person
        String simpleName = clazz.getSimpleName();
        System.out.println("simpleName = " + simpleName); // simpleName = Person
    }
}

2.5.5 获取父类的字节码对象

  • 获取父类的字节码对象:
public native Class<? super T> getSuperclass();
  • 示例:
package com.github.demo6;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2021-10-06 22:16
 */
public class Person {
    private String name;

    private Integer age;

    public Person() {}

    public Person(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return this.age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}
package com.github.demo6;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2021-10-07 11:25
 */
public class Student extends Person{
    private String score;

    public Student() {}

    public Student(String score) {
        this.score = score;
    }

    public Student(String name, Integer age, String score) {
        super(name, age);
        this.score = score;
    }

    public String getScore() {
        return this.score;
    }

    public void setScore(String score) {
        this.score = score;
    }

    @Override
    public String toString() {
        return "Student{" + "score='" + this.score + '\'' + '}';
    }
}
package com.github.demo6;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2021-10-07 13:05
 */
public class Test4 {
    public static void main(String[] args) {
        Class<?> clazz = Student.class;
        Class<?> superclass = clazz.getSuperclass();
        System.out.println("superclass = " + superclass); // superclass = class com.github.demo6.Person
    }
}

2.5.6 获取该类实现的所有接口的字节码对象

  • 获取该类实现的所有接口的字节码对象:
public Class<?>[] getInterfaces() {}
  • 示例:
package com.github.demo6;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2021-10-06 22:16
 */
public class Person {
    private String name;

    private Integer age;

    public Person() {}

    public Person(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return this.age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}
package com.github.demo6;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2021-10-07 11:29
 */
public interface Work {

    void work();
}
package com.github.demo6;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2021-10-07 11:25
 */
public class Student extends Person implements Work {
    private String score;

    public Student() {}

    public Student(String score) {
        this.score = score;
    }

    public Student(String name, Integer age, String score) {
        super(name, age);
        this.score = score;
    }

    public String getScore() {
        return this.score;
    }

    public void setScore(String score) {
        this.score = score;
    }

    @Override
    public String toString() {
        return "Student{" + "score='" + this.score + '\'' + '}';
    }

    @Override
    public void work() {
        System.out.println("学习");
    }
}
package com.github.demo6;

import java.util.Arrays;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2021-10-07 13:07
 */
public class Test5 {
    public static void main(String[] args) {
        Class<?> clazz = Student.class;
        Class<?>[] interfaces = clazz.getInterfaces();
        System.out.println("interfaces = " + Arrays.toString(interfaces)); // interfaces = [interface com.github.demo6.Work]
    }
}

2.5.7 获取类的属性

  • 获取自己的所有成员属性对象数组(能够获取公有和私有的所有成员属性对象数组):
public Field[] getDeclaredFields() throws SecurityException {}
  • 根据属性名称获取自己的成员属性对象(能够获取公有和私有的成员属性对象):
public Field getDeclaredField(String name)
    throws NoSuchFieldException, SecurityException {}
  • 获取所有公有成员属性对象数组(能够获取父类和自己的所有公有属性对象数组):
public Field[] getFields() throws SecurityException {}
  • 根据属性名称获取公有成员属性对象(能够获取父类和自己的公有属性对象):
public Field getField(String name)
    throws NoSuchFieldException, SecurityException {}
  • 示例:
package com.github.demo6;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2021-10-06 22:16
 */
public class Person {
    private String name;

    private Integer age;

    public Person() {}

    public Person(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return this.age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}
package com.github.demo6;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2021-10-07 11:25
 */
public class Student extends Person {
    private String score;

    public Student() {}

    public Student(String score) {
        this.score = score;
    }

    public Student(String name, Integer age, String score) {
        super(name, age);
        this.score = score;
    }

    public String getScore() {
        return this.score;
    }

    public void setScore(String score) {
        this.score = score;
    }

    @Override
    public String toString() {
        return "Student{" + "score='" + this.score + '\'' + '}';
    }

}
package com.github.demo6;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2021-10-07 13:16
 */
public class Test6 {
    public static void main(String[] args) {
        Class<?> clazz = Student.class;
        Field[] declaredFields = clazz.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            int modifiers = declaredField.getModifiers();
            String modifier = Modifier.toString(modifiers);
            System.out.println("修饰符:" + modifier); // 修饰符:private
            Class<?> type = declaredField.getType();
            System.out.println("数据类型:" + type); // 数据类型:class java.lang.String
            String name = declaredField.getName();
            System.out.println("属性名称:" + name); // 属性名称:score
        }
    }
}

2.5.8 获取类的构造函数

  • 获取所有公有的构造函数:
public Constructor<?>[] getConstructors() throws SecurityException {}
  • 根据指定参数类型获取公有的构造函数:
public Constructor<T> getConstructor(Class<?>... parameterTypes)
    throws NoSuchMethodException, SecurityException {}
  • 获取所有的构造函数(包括公有和私有):
public Constructor<?>[] getDeclaredConstructors() throws SecurityException {}
  • 根据指定参数类型获取构造函数(包括公有和私有):
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
    throws NoSuchMethodException, SecurityException {}
  • 示例:
package com.github.demo6;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2021-10-06 22:16
 */
public class Person {
    private String name;

    private Integer age;

    public Person() {}

    public Person(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return this.age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}
package com.github.demo6;

import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
import java.util.Arrays;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2021-10-07 13:44
 */
public class Test7 {
    public static void main(String[] args) throws NoSuchMethodException {
        Class<?> clazz = Person.class;

        Constructor<?> constructor = clazz.getConstructor(String.class, Integer.class);
        // public com.github.demo6.Person(java.lang.String,java.lang.Integer)
        System.out.println(constructor);
        System.out.println("修饰符:" + Modifier.toString(constructor.getModifiers())); // 修饰符:public
        System.out.println("方法名称:" + constructor.getName()); // 方法名称:com.github.demo6.Person
        System.out.println("参数类型:" + Arrays.toString(constructor.getParameterTypes())); // 参数类型:[class java.lang.String, class java.lang.Integer]

        Constructor<?>[] declaredConstructors = clazz.getDeclaredConstructors();
        for (Constructor<?> declaredConstructor : declaredConstructors) {
            // declaredConstructor = public com.github.demo6.Person()
            // declaredConstructor = public com.github.demo6.Person(java.lang.String,java.lang.Integer)
            System.out.println("declaredConstructor = " + declaredConstructor);
            int modifiers = declaredConstructor.getModifiers();
        }

    }
}

2.5.9 获取类的方法

  • 获取自己的所有成员方法对象数组(能够获取公有和私有的所有成员方法对象数组):
public Method[] getDeclaredMethods() throws SecurityException {}
  • 根据方法名称和形参列表获取自己的成员方法对象(能够获取公有和私有的成员方法对象)。
public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
    throws NoSuchMethodException, SecurityException {}
  • 获取父类和自己的所有公有的成员方法对象数组:
public Method[] getMethods() throws SecurityException {}
  • 根据方法名称和形参列表获取父类和自己的成员方法对象:
public Method getMethod(String name, Class<?>... parameterTypes)
    throws NoSuchMethodException, SecurityException {}
  • 示例:
package com.github.demo6;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2021-10-06 22:16
 */
public class Person {
    private String name;

    private Integer age;

    public Person() {}

    public Person(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return this.age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}
package com.github.demo6;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2021-10-07 13:54
 */
public class Test8 {
    public static void main(String[] args) throws NoSuchMethodException {
        Class<?> clazz = Person.class;

        Method[] declaredMethods = clazz.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            // declaredMethod = public java.lang.String com.github.demo6.Person.getName()
            // declaredMethod = public void com.github.demo6.Person.setName(java.lang.String)
            // declaredMethod = public java.lang.Integer com.github.demo6.Person.getAge()
            // declaredMethod = public void com.github.demo6.Person.setAge(java.lang.Integer)
            System.out.println("declaredMethod = " + declaredMethod);
        }

        Method setName = clazz.getDeclaredMethod("setName", String.class);
        // setName = public void com.github.demo6.Person.setName(java.lang.String)
        System.out.println("setName = " + setName);
        System.out.println("返回值类型:" + setName.getReturnType()); // 返回值类型:void
        System.out.println("参数列表:" + Arrays.toString(setName.getParameterTypes())); // 参数列表:[class java.lang.String]
        System.out.println("修饰符:" + Modifier.toString(setName.getModifiers())); // 修饰符:public
        System.out.println("异常类型:" + Arrays.toString(setName.getExceptionTypes())); // 异常类型:[]

        Method[] methods = clazz.getMethods();
        for (Method method : methods) {
            // method = public java.lang.String com.github.demo6.Person.getName()
            // method = public void com.github.demo6.Person.setName(java.lang.String)
            // method = public void com.github.demo6.Person.setAge(java.lang.Integer)
            // method = public java.lang.Integer com.github.demo6.Person.getAge()
            // method = public final void java.lang.Object.wait() throws java.lang.InterruptedException
            // method = public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
            // method = public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
            // method = public boolean java.lang.Object.equals(java.lang.Object)
            // method = public java.lang.String java.lang.Object.toString()
            // method = public native int java.lang.Object.hashCode()
            // method = public final native java.lang.Class java.lang.Object.getClass()
            // method = public final native void java.lang.Object.notify()
            // method = public final native void java.lang.Object.notifyAll()
            System.out.println("method = " + method);
        }
    }
}

2.6 使用反射创建对象

  • 方式一:直接通过 Class 对象来实例化(要求必须有无参构造)。
    • ① 获取该类型的 Class 对象。
    • ② 创建对象。
  • 方式二:通过获取构造器对象来进行实例化。
    • ① 获取该类型的 Class 对象。
    • ② 获取构造器对象。
    • ③ 创建对象。

      注意:如果构造器的权限修饰符修饰的范围不可见,也可以调用 setAccessible(true) 。

  • 示例:
package com.github.demo7;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2021-10-06 22:16
 */
public class Person {
    private String name;

    private Integer age;

    public Person() {}

    public Person(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return this.age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}
package com.github.demo7;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2021-10-07 14:29
 */
public class Test {
    public static void main(String[] args) throws InstantiationException, IllegalAccessException {
        Class<?> clazz = Person.class;
        Object o = clazz.newInstance();
        System.out.println(o instanceof Person); // true
    }
}
  • 示例:
package com.github.demo7;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2021-10-06 22:16
 */
public class Person {
    private String name;

    private Integer age;

    public Person() {}

    private Person(String name) {
        this.name = name;
    }

    public Person(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return this.age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}
package com.github.demo7;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2021-10-07 14:31
 */
public class Test2 {
    public static void main(String[] args)
        throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        Class<?> clazz = Person.class;
        Constructor<?> constructor = clazz.getDeclaredConstructor(String.class);
        constructor.setAccessible(true);
        Object o = constructor.newInstance("许大仙");
        if (o instanceof Person) {
            Person p = (Person)o;
            String name = p.getName();
            System.out.println("name = " + name); // name = 许大仙
        }

    }
}

2.7 使用反射操作类的属性

  • ① 获取该类型的 Class 对象:
Class<?> clazz = Person.class;
  • ② 获取属性对象:
Field field = clazz.getDeclaredField("name");
  • ③ 设置属性可访问:
field.setAccessible(true);
  • ④ 创建实例对象:
Object obj = clazz.newInstance();
  • ⑤ 设置属性值:
field.set(obj,"许大仙");
  • ⑥ 获取属性值:
Object value = field.get(obj);

注意:如果操作的是静态变量,那么实例对象可以省略,使用 null 表示;当然一般情况下,不会使用反射操作静态变量。

  • 示例:
package com.github.demo8;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2021-10-06 22:16
 */
public class Person {
    private String name;

    private Integer age;

    public Person() {}

    private Person(String name) {
        this.name = name;
    }

    public Person(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return this.age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}
package com.github.demo8;

import com.github.demo7.Person;

import java.lang.reflect.Field;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2021-10-07 15:04
 */
public class Test {
    public static void main(String[] args) throws NoSuchFieldException, InstantiationException, IllegalAccessException {
        // ① 获取该类型的Class对象。
        Class<?> clazz = Person.class;
        // ② 获取属性对象
        Field name = clazz.getDeclaredField("name");
        // ③ 设置属性可访问
        name.setAccessible(true);
        // ④ 创建实例对象
        Object obj = clazz.newInstance();
        // ⑤ 设置属性值
        name.set(obj, "许大仙");
        // ⑥ 获取属性值
        Object value = name.get(obj);

        System.out.println("value = " + value); // value = 许大仙
    }
}

2.8 使用反射操作类的方法

  • ① 获取该类型的 Class 对象:
Class<?> clazz = Person.class;
  • ② 获取方法对象:
Method setName = clazz.getDeclaredMethod("setName", String.class);
  • ③ 创建实例对象:
Object obj = clazz.newInstance();
  • ④ 调用方法:
Object result = setName.invoke(obj, "许大仙");

注意:

  • 如果方法的权限修饰符修饰的范围不可见,也可以调用 setAccessible(true) 。
  • 如果方法是静态方法,实例对象也可以省略,用 null 代替。
  • 示例:
package com.github.demo9;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2021-10-07 15:15
 */
public class Test {
    public static void main(String[] args)
        throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
        // ① 获取该类型的Class对象。
        Class<?> clazz = Person.class;
        // ② 获取方法对象。
        Method setName = clazz.getDeclaredMethod("setName", String.class);
        // ③ 创建实例对象。
        Object obj = clazz.newInstance();
        // ④ 调用方法。
        Object result = setName.invoke(obj, "许大仙");
        System.out.println("result = " + result); // result = null

        if (obj instanceof Person) {
            Person person = (Person)obj;
            String name = person.getName();
            System.out.println("name = " + name); // name = 许大仙
        }
    }
}

2.9 Type 接口

2.9.1 概述

  • java.lang.reflect.Type 接口及其相关接口用于描述 Java 中用到的所有类型,是 Java 反射中很重要的组成部分。Type 是 Java 编程语言中所有类型的公共高级接口。它们包括原始类型、参数化类型、数组类型、类型变量和基本类型。

2.9.2 使用发射获取Type

  • ① 当我们拿到一个 Class ,用 Class.getGenericInterfaces() 方法得到 Type[] ,也就是这个类实现接口的 Type 类型列表。
  • ② 当我们拿到一个 Class ,用 Class.getDeclaredFields() 方法得到 Field[] ,也就是类的属性列表,然后用 Field. getGenericType() 方法得到这个属性的 Type 类型。
  • ③ 当我们拿到一个 Method ,用 Method.getGenericParameterTypes() 方法获得T ype[] ,也就是方法的参数类型列表。
  • ④ 当我们拿到一个 Class ,用 clazz.getGenericSuperclass() 这样就可以获取父类的泛型实参列表。

2.9.3 Type 的分类

Type接口的类图.png

  • Type 接口包含了一个实现类(Class)和四个实现接口(TypeVariable、ParameterizedType、GenericArrayType、WildcardType),这四个接口都有自己的实现类,但是在开发中,我们一般都使用这四个接口。
  • Class :当需要描述的类型是普通 Java 类、数组、自定义类、 8 种 java 基本类型 的时候, java 会选择 Class 来作为这个 Type 的实现类,我们甚至可以直接把这个 Type 强行转换类型为 Class 。这些类基本都有一个特点:基本和泛型无关,其他 4 种 Type 的类型,基本都是泛型的各种形态。
  • ParameterizedType :当需要描述的类是 泛型类 时,比如 List , Map 等,不论代码里写没写具体的泛型,java 会选择 ParameterizedType 接口做为Type 的实现。ParameterizedType 接口有 getActualTypeArguments() 方法,用于得到泛型的 Type 类型数组。
  • GenericArrayType :当需要描述的类型是 泛型类的数组 时,比如 List[] , Map[] ,type 用 GenericArrayType 接口作为 Type 的实现。GenericArrayType 接口有 getGenericComponentType() 方法,得到数组的组件类型的Type对象。
  • WildcardType : 当需要描述的类型是泛型类,而且泛型类中的泛型被定义为 (? extends xxx) 或者 (? super xxx) 这种类型,比如 List<? extends TestReflect>,这个类型首先将由 ParameterizedType 实现,当调用 ParameterizedType 的 getActualTypeArguments() 方法后得到的 Type 就由 WildcardType 实现。

2.9.4 获取泛型父类信息

  • 示例:
package com.github.demo13;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2021-10-08 10:38
 */
public class Test {
    public static void main(String[] args) {
        Class<?> clazz = Son.class;
        Type type = clazz.getGenericSuperclass();
        ParameterizedType parameterizedType = (ParameterizedType)type;
        Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
        for (Type actualTypeArgument : actualTypeArguments) {
            // class java.lang.String
            // class java.lang.Integer
            System.out.println(actualTypeArgument);
        }
    }
}
//泛型形参:<T,U>
class Father<T,U>{

}
//泛型实参:<String,Integer>
class Son extends Father <String,Integer>{

}

2.10 使用反射操作数组

  • 在 java.lang.reflect 包下提供了一个 Array 类,Array 对象可以代表所有的数组。程序可以通过 Array 类来动态创建数组,操作数组元素。
  • Array 类提供了如下几个方法:
  • 创建一个具有指定的组件类型和维度的新数组:
public static Object newInstance(Class<?> componentType, int length)
    throws NegativeArraySizeException {}
  • 向数组中存储元素:
public static native void set(Object array, int index, Object value)
    throws IllegalArgumentException, ArrayIndexOutOfBoundsException;
public static native void setBoolean(Object array, int index, boolean z)
    throws IllegalArgumentException, ArrayIndexOutOfBoundsException;
public static native void setByte(Object array, int index, byte b)
    throws IllegalArgumentException, ArrayIndexOutOfBoundsException;
public static native void setChar(Object array, int index, char c)
    throws IllegalArgumentException, ArrayIndexOutOfBoundsException;
public static native void setShort(Object array, int index, short s)
    throws IllegalArgumentException, ArrayIndexOutOfBoundsException;
public static native void setInt(Object array, int index, int i)
    throws IllegalArgumentException, ArrayIndexOutOfBoundsException;
public static native void setLong(Object array, int index, long l)
    throws IllegalArgumentException, ArrayIndexOutOfBoundsException;
public static native void setFloat(Object array, int index, float f)
    throws IllegalArgumentException, ArrayIndexOutOfBoundsException;
public static native void setDouble(Object array, int index, double d)
    throws IllegalArgumentException, ArrayIndexOutOfBoundsException;
  • 获取数组中的元素:
public static native Object get(Object array, int index)
    throws IllegalArgumentException, ArrayIndexOutOfBoundsException;
public static native boolean getBoolean(Object array, int index)
    throws IllegalArgumentException, ArrayIndexOutOfBoundsException;
public static native byte getByte(Object array, int index)
    throws IllegalArgumentException, ArrayIndexOutOfBoundsException;
public static native char getChar(Object array, int index)
    throws IllegalArgumentException, ArrayIndexOutOfBoundsException;
public static native short getShort(Object array, int index)
    throws IllegalArgumentException, ArrayIndexOutOfBoundsException;
public static native int getInt(Object array, int index)
    throws IllegalArgumentException, ArrayIndexOutOfBoundsException;
public static native long getLong(Object array, int index)
    throws IllegalArgumentException, ArrayIndexOutOfBoundsException;
public static native float getFloat(Object array, int index)
    throws IllegalArgumentException, ArrayIndexOutOfBoundsException;
public static native double getDouble(Object array, int index)
    throws IllegalArgumentException, ArrayIndexOutOfBoundsException;
  • 示例:
package com.github.demo10;

import java.lang.reflect.Array;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2021-10-07 16:59
 */
public class Test {
    public static void main(String[] args) {
        // 使用反射创建一个String类型的数组,长度是5
        Object arr = Array.newInstance(String.class, 5);
        // 存储元素
        Array.set(arr, 0, "张三");
        Array.set(arr, 1, "李四");
        Array.set(arr, 2, "王五");
        Array.set(arr, 3, "赵六");
        Array.set(arr, 4, "田七");
        // 获取元素
        for (int i = 0; i < 5; i++) {
            System.out.println(Array.get(arr, i));
        }
    }
}