Java笔记17 反射

17.1 反射机制

17.1.1 入门案例

根据配置文件 re.properties 指定信息,创建 Cat 对象并调用 hi() 方法

#re.properties
classfullpath=introduction.Cat
method=hi
public class Cat {
    private String name = "招财猫";
    public void hi() {
        System.out.println("hi, " + name);
    }
    public void cry() {
        System.out.println(name + "喵喵叫...");
    }
}
public class IntroductionToReflect {
    public static void main(String[] args) throws IOException, ReflectiveOperationException {
        //传统方法
        Cat cat = new Cat();
        cat.hi();

        //使用 Properties 类,只能得到类名以及方法名
        //不能进行实例化和调用
        Properties properties = new Properties();
        properties.load(new FileReader("src\\re.properties"));
        String className = properties.getProperty("classfullpath");
        String methodName = properties.getProperty("method");
        System.out.println("类名:"+className);//类名:introduction.Cat
        System.out.println("方法名:"+methodName);//方法名:hi
        //不能直接通过字符串创建对象
        //new className();

        //使用反射机制
        //(1) 加载类, 返回 Class 类型的对象 cls
        Class cls = Class.forName(className);
        //(2) 通过 cls 得到你加载的类 introduction.Cat 的对象实例
        Object o = cls.newInstance();
        System.out.println("o 的运行类型为:"+o.getClass());//o 的运行类型为:class introduction.Cat
        //(3) 通过 cls 得到你加载的类 introduction.Cat 的 名为methodName="hi" 的方法对象
        //    即:在反射中,可以把方法视为对象(万物皆对象)
        Method clsMethod = cls.getMethod(methodName);
        //(4) 通过 clsMethod 调用方法: 即通过方法对象来实现调用方法
        System.out.println("反射机制调用 hi 方法:");
        clsMethod.invoke(o);//hi, 招财猫
        //传统方法:对象.方法(); 反射机制:方法对象.invoke(对象);
        //修改 properties 文件中 method 的值,即可在不修改源码的情况下调用不同的方法
    }
}

17.1.2 Java Reflection

  1. 反射机制允许程序在执行期借助 Reflection API 取得任何类的内部信息(比如成员变量、构造器、成员方法等),并能操作对象的属性及方法。反射在设计模式和框架底层都会用到
  2. 加载完类后,在堆中就产生了一个 Class 类型的对象(一个类只有一个 Class 对象),这个对象包含了类的完整结构信息。通过这个对象的到类的结构。这个 Class 对象就像是一面镜子,透过这面镜子看到类的结构,所以,形象地称之为 反射

17.1.3 Java 反射机制原理示意图

在这里插入图片描述

17.1.4 Java 反射可以完成

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

17.1.5 反射相关的类

  1. java.lang.Class:代表一个类,Class 对象表示某个类加载后在堆中的对象
  2. java.lang.reflect.Method:代表类的方法,Method 对象表示某个类的方法
  3. java.lang.reflect.Field:代表类的成员变量,Field 对象表示某个类的成员变量
  4. java.lang.reflect.Constructor:代表类的构造方法,Constructor 对象表示某个类的构造器
classfullpath=classes.Cat
method=hi
public class Cat {
    private String name = "招财猫";
    public int age = 2;
    public Cat() {
    }
    public Cat(String name) {
        this.name = name;
    }
    public void hi() {
        System.out.println("hi, " + name);
    }
    public void cry() {
        System.out.println(name + "喵喵叫...");
    }
}
public class ReflectionClass {
    public static void main(String[] args) throws IOException, ReflectiveOperationException {
        Properties properties = new Properties();
        properties.load(new FileReader("src\\re.properties"));
        String className = properties.getProperty("classfullpath");
        String methodName = properties.getProperty("method");
        //1.`java.lang.Class`:代表一个类,Class 对象表示某个类加载后在堆中的对象
        Class cls = Class.forName(className);
        Object o = cls.newInstance();
        //2.`java.lang.reflect.Method`:代表类的方法,Method 对象表示某个类的方法
        Method method = cls.getMethod(methodName);
        method.invoke(o);
        //3.`java.lang.reflect.Field`:代表类的成员变量,Field 对象表示某个类的成员变量
        //getField 不能得到私有的属性
        Field ageField = cls.getField("age");
        System.out.println(ageField.get(o));//传统写法:对象.成员变量, 反射:成员变量对象.get(对象)
        //4.`java.lang.reflect.Constructor`:代表类的构造方法,Constructor 对象表示某个类的构造器
        Constructor noParameter = cls.getConstructor(); //()中可以指定构造器参数类型, 返回无参构造器
        System.out.println(noParameter);
        Constructor aParameter = cls.getConstructor(String.class);//有参的构造器
        //这里传入的 String.class 就是 String 类的 Class 对象
        System.out.println(aParameter);
    }
}

17.1.6 反射优缺点

  1. 优点:可以动态的创建和使用对象(也是框架底层核心),使用灵活,没有反射机 制,框架技术就失去底层支撑。
  2. 缺点:使用反射基本是解释执行,对执行速度有影响.

17.1.7 反射调用优化—关闭访问检查

  1. Method 、Field 和 Constructor 对象都有setAccessible()方法
  2. setAccessible 作用是启动或禁止访问安全检查开关
  3. 参数值为 true 表示:反射的对象在使用时取消安全检查,提高反射的效率
  4. 参数值为 false 表示反射的对象执行安全检查

17.2 Class 类

17.2.1 介绍

  1. Class 类也是类,因此也继承 Object 类
  2. Class 类对象不是 new 出来的,而是系统创建的
  3. 对于某个类的 Class 类对象,在内存中只有一份,因此类只加载一次
  4. 每个类的实例都会记得自己是由哪个 Class 实例所生成
  5. 通过 Class 对象可以完整地得到一个类的完整结构,使用一系列API
  6. Class 对象是存放在堆的
  7. 类的字节码二进制数据,是放在方法区的,有的地方称为类的元数据(包括 方法代码、变量名、方法名、访问权限等)java虚拟机Class对象是放在 方法区 还是 堆 中 ?

17.2.2 常用方法

  1. static Class forName(String name):返回指定类名 name 的 Class 对象
  2. Object newInstance():调用缺省构造函数,返回该 Class 对象的一个实例
  3. getName():返回此 Class 对象所表示的实体(类、接口、数组类、基本类型等)名称
  4. Class[] getInterfaces():获取当前 Class 对象的接口
  5. ClassLoader getClassLoader():返回该类的加载器
  6. Constructor[] getConstructors():返回一个包含某些 Constructor 对象的数组
  7. Field[] getDeclaredFields():返回 Field 对象的一个数组
  8. Method getMethod(String name, Class ... paramTypes):返回一个 Method 对象,此对象的形参类型为 paramType
public class ClassMethod {
    public static void main(String[] args) throws ReflectiveOperationException {
        String classFullPath = "classes.Car";
        //1.获取到Car类对应的Class对象
        //<?>表示不确定的Java类型
        Class<?> cls = Class.forName(classFullPath);
        //2.输出cls
        //显示cls对象是哪个类的Class对象,但cls不是Car对象
        System.out.println(cls);//class classes.Car
        //显示 cls 实际的运行类型
        System.out.println(cls.getClass());//class java.lang.Class
        //3.得到包名
        System.out.println(cls.getPackage().getName());//classes
        //4.得到全类名
        System.out.println(cls.getName());//classes.Car
        //5.通过cls创建对象实例
        Car car = (Car)cls.newInstance();
        System.out.println(car);//Car{brand='宝马', price=500000, color='red'}
        //6.通过反射获取属性
        Field brand = cls.getField("brand");//不能访问私有属性
        System.out.println(brand.get(car));//宝马
        //7.通过反射给属性赋值
        brand.set(car,"奔驰");
        System.out.println(brand.get(car));//奔驰
        //8.获取所有属性
        Field[] clsFields = cls.getFields();
        for (Field clsField : clsFields) {
            System.out.print(clsField.get(car) + " ");
        }//奔驰 500000 red
    }
}
public class Car {
    public String brand = "宝马";
    public int price = 500000;
    public String color = "red";

    @Override
    public String toString() {
        return "Car{" +
                "brand='" + brand + '\'' +
                ", price=" + price +
                ", color='" + color + '\'' +
                '}';
    }
}

17.2.3 获取 Class 类对象

在这里插入图片描述

  1. 通过 Class 类的**静态方法forName()**获取

    1. 前提:已知一个类的全类名,且该类在类路径下
    2. 可能抛出ClassNotFoundException
    3. 示例:Class cls = Class.forName("java.lang.Cat");
    4. 应用场景:多用于配置文件,读取类全路径,加载类
  2. 通过**类名.class**获取

    1. 前提:已知具体的类
    2. 该方法最安全可靠,程序性能最高
    3. 示例:Class cls = Cat.class
    4. 应用场景:多用于参数传递,比如通过反射得到对应构造器对象
  3. 通过调用类对象的getClass()方法获取 Class 对象

    1. 前提:已知该类的实例化对象
    2. 示例:Class cls = 对象.getClass(); //运行类型
    3. 应用场景:通过创建好的对象,获取 Class 对象
  4. 通过类加载器 ClassLoader获取

    1. ClassLoader cl = 对象.getClass.getClassLoader();
      Class cls = cl.loadClass("类的全类名")
      
  5. 基本类型获取 Class 对象通过**.class**的方式获取

    1. Class cls = 基本数据类型.class;
  6. 基本数据类型对应的包装类,通过**.TYPE**获取

    1. Class cls = 包装类.TYPE;
//演示得到 Class 对象的各种方式
public class GetClass {
    public static void main(String[] args) throws ReflectiveOperationException {
        //1.通过 Class 类的静态方法`forName()`获取
        String ClassFullName = "classes.Car";//通过读取配置文件得到
        Class<?> cls1 = Class.forName(ClassFullName);
        System.out.println(cls1);//class classes.Car

        //2.通过`类名.class`获取
        //多用于参数传递,比如之前的示例中,将String的Class对象作为参数传递
        //Constructor aParameter = cls.getConstructor(String.class);//有参的构造器
        Class<Car> cls2 = Car.class;
        System.out.println(cls2);//class classes.Car

        //3.通过调用类对象的`getClass()`方法获取 Class 对象
        Car car = new Car();
        Class<? extends Car> cls3 = car.getClass();
        System.out.println(cls3);//class classes.Car

        //4.通过类加载器(4种) ClassLoader获取
        //4.1先得到类加载器
        ClassLoader classLoader = car.getClass().getClassLoader();
        //4.2通过类加载器得到Class对象
        Class<?> cls4 = classLoader.loadClass(ClassFullName);
        System.out.println(cls4);//class classes.Car

        //注意:cls1、cls2、cls3、cls4其实是同一个Class对象
        System.out.println(cls1.hashCode());//460141958
        System.out.println(cls2.hashCode());//460141958
        System.out.println(cls3.hashCode());//460141958
        System.out.println(cls4.hashCode());//460141958

        //5.基本类型获取 Class 对象通过`.clas?s`的方式获取
        Class<Integer> integerClass = int.class;
        Class<Character> characterClass = char.class;
        Class<Boolean> booleanClass = boolean.class;
        System.out.println(integerClass);//int
        System.out.println(characterClass);//char
        System.out.println(booleanClass);//boolean

        //6.基本数据类型对应的包装类,通过`.TYPE`获取
        Class<Integer> integerClass1 = Integer.TYPE;
        Class<Character> characterClass1 = Character.TYPE;
        System.out.println(integerClass1);//int
        System.out.println(characterClass1);//char

        //int 和 Integer 的Class对象是同一个,自动装箱、自动拆箱
        System.out.println(integerClass.hashCode());//1163157884
        System.out.println(integerClass1.hashCode());//1163157884
    }
}

17.2.4 哪些类型由 Class 对象

以下类型有 Class 对象

  1. 外部类、成员内部类、静态内部类、局部内部类、匿名内部类
  2. interface:接口
  3. 数组
  4. enum:枚举
  5. annotation:注解
  6. 基本数据类型
  7. void
public class AllTypeClass {
    public static void main(String[] args) {
        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<Double> cls8 = Double.class;//包装类
        Class<Void> cls9 = void.class;//void
        Class<Class> cls10 = Class.class;//Class
    }
}

17.3 类加载

17.3.1 说明

反射机制是 Java 实现动态语言的关键,也就是通过反射实现类动态加载

  1. 静态加载:编译时加载相关类,如果没有则报错,依赖性太强
  2. 动态加载:运行时加载需要的类,如果运行时不用该类,即使不存在该类,也不报错,降低了依赖性
import java.util.*;
import java.lang.reflect.*;

public class ClassLoad_ {
    public static void main(String[] args) throws ReflectiveOperationException {
        Scanner sc = new Scanner(System.in);
        System.out.println("???key??");
        String key = sc.next();
        switch (key) {
            case "a":
                //静态加载,依赖性很强,没有该类,编译不通过
                //Dog dog = new Dog();
                //dog.cry();
                break;
            case "2":
                //动态加载,执行到再加载,可以通过编译
                Class cls = Class.forName("Person");
                Object o = cls.newInstance();
                Method m = cls.getMethod("hi");
                m.invoke(o);
                System.out.println("OK");
                break;
            default:
                System.out.println("do nothing...");
        }
    }
}

17.3.2 类加载时机

  1. 当创建对象时(new) //静态加载
  2. 当子类被加载,父类也加载 //静态加载
  3. 当调用类中的静态成员 //静态加载
  4. 通过反射 //动态加载

17.3.3 类加载过程图

在这里插入图片描述

17.3.4 类加载各阶段完成任务

在这里插入图片描述

  1. 加载 Loading

    1. JVM在该阶段的主要目的是将字节码从不同的数据源(可能是 class 文件,也可能是 jar 包,甚至网络)转化为二进制字节流加载到内存中,并生成一个代表该类的 java.lang.Class 对象
  2. 连接 Linking

    1. 验证

      1. 目的是为了确保 Class 文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全
      2. 包括:文件格式验证(是否以魔数 oxcafebabe开头)、元数据验证字节码验证符号引用验证
      3. 可以考虑使用 -Xverify:none参数来关闭大部分的类验证措施,缩短虚拟机类加载的时间
    2. 准备

      1. JVM会在该阶段对静态变量分配内存并默认初始化(对应数据类型的默认初始化,如 0、0L、null、false等)。这些变量所使用的内存都将在方法区中进行分配

      2. class A {
            //分析:类加载的连接阶段-准备,属性是如何处理的
            //1.n1是实例属性,不是静态变量,因此在准备阶段,不会分配内存
            //2.n2是静态变量,分配内存,n2的值是默认初始化的0,而不是20
            //3.n3是static final常量,他和静态变量不一样,一旦赋值就不变了,n3直接赋值为30
            public static int n2 = 20;
            public int n1 = 10;
            public static final int n3 = 30;
        }
        
    3. 解析

      1. 虚拟机将常量池内的符号引用替换为直接引用的过程
  3. 初始化 Initialization

    1. 到初始化阶段,才真正开始执行类中定义的 Java 程序代码,此阶段是执行<clinit>()方法的过程

    2. <clinit>()方法是由编译器按语句在源文件中出现的顺序,依次自动收集类中的所有静态变量的赋值动作和静态代码块中的语句,并进行合并

    3. 虚拟机会保证一个类的<clicit>()方法在多线程环境中被正确地加锁、同步,如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的<clinit>()方法,其他线程都需要阻塞等待,直到活动线程执行<clinit>()方法完毕

    4. //演示类加载-初始化阶段
      public class ClassLoad02 {
          public static void main(String[] args) throws ClassNotFoundException {
              //分析:B.num
              //1. 加载B类并生成B类的Class对象
              //2. 连接: num = 0
              //3. 初始化阶段:
              //3.1 依次自动收集类中的所有*静态变量*的赋值动作和*静态代码块*中的语句
              /*
                    clinit() {
                      System.out.println("B 静态代码块被执行");
                      num = 300;
                      num = 100;
                    }
               */
              //3.2 合并为:num = 100;
              //System.out.println(B.num);//100
      
              //加载类的时候有同步机制控制
              /*
                  protected Class<?> loadClass(String name, boolean resolve)
                  throws ClassNotFoundException
                  {
                      //因为这个机制才能保证某个类在内存中,只有一份Class对象
                      synchronized (getClassLoadingLock(name)) {
                      //...
               */
              B b = new B();
          }
      }
      class B {
          static {
              System.out.println("B 静态代码块被执行");
              num = 300;
          }
          static int num = 100;
          public B(){
              System.out.println("B() 构造器被执行");
          }
      }
      

17.4 通过反射获取类的结构信息

17.4.1 第一组:java.lang.Class类

  1. getName:获取全类名
  2. getSimpleName:获取简单类名
  3. getFields:获取所有 public 修饰的属性,包含本类以及父类的
  4. getDeclaredFields:获取本类中所有属性
  5. getMethods:获取所有 public 修饰的方法,包含本类以及父类的
  6. getDeclaredMethods:获取本类中所有方法
  7. getConstructors:获取本类所有 public 修饰的构造器
  8. getDeclaredConstructors:获取本类中所有构造器
  9. getPackage:以 Package 形式返回包信息
  10. getSuperClass:以 Class 形式返回父类信息
  11. getlnterfaces:以 Class[] 形式返回接口信息
  12. getAnnotations:以 Annotation[] 形式返回注解信息

17.4.2 第二组:java.lang.reflect.Field类

  1. getModifiers:以 int 形式返回修饰符
    1. 默认修饰符:0
    2. public:1
    3. private:2
    4. protect:4
    5. static:8
    6. final:16
    7. public static:1 + 8 = 9
  2. getType:以 Class 形式返回类型
  3. getName:返回属性名

17.4.3 第三组:java.lang.reflect.Method类

  1. getModifiers:以 int 形式返回修饰符(返回值规则同上)
  2. getReturnType:以 Class 形式获取返回类型
  3. getName:返回方法名
  4. getParameterTypes:以 Class[] 返回参数类型数组

17.4.4 第四组:java.lang.reflect.Constructor类

  1. getModifiers:以 int 形式返回修饰符(返回值规则同上)
  2. getName:返回构造器名(全类名)
  3. getParameterTypes:以 Class[] 返回参数类型数组

17.4.5 示例

public class ReflectUtils {
    public static void main(String[] args) {}
    //第一组方法API
    @Test
    public void api_01() {
        //得到Class对象
        Class<Person> personClass = Person.class;

        //1. getName:获取全类名
        System.out.println(personClass.getName());//reflect_api.Person

        //2. getSimpleName:获取简单类名
        System.out.println(personClass.getSimpleName());//Person

        //3. getFields:获取所有public修饰的属性,包含本类以及父类的
        Field[] personFields = personClass.getFields();
        System.out.print("本类以及父类共有的属性:");
        for (Field personField : personFields) {
            System.out.print(personField.getName() + " ");
        }//本类以及父类共有的属性:name hobby

        //4. getDeclaredFields:获取本类中所有属性
        Field[] declaredFields = personClass.getDeclaredFields();
        System.out.print("\n本类中所有属性:");
        for (Field field : declaredFields) {
            System.out.print(field.getName()+" ");
        }//本类中所有属性:name age job sal

        //5. getMethods:获取所有public修饰的方法,包含本类以及父类的
        System.out.print("\n本类以及父类共有的方法:");
        Method[] methods = personClass.getMethods();
        for (Method method : methods) {
            System.out.print(method.getName()+" ");
        }//本类以及父类共有的方法:m1 hi wait wait wait equals toString hashCode getClass notify notifyAll

        //6. getDeclaredMethods:获取本类中所有方法
        Method[] declaredMethods = personClass.getDeclaredMethods();
        System.out.print("\n本类中所有方法:");
        for (Method declaredMethod : declaredMethods) {
            System.out.print(declaredMethod.getName()+" ");
        }//本类中所有方法:m1 m2 m4 m3

        //7. getConstructors:获取本类所有public修饰的构造器
        Constructor<?>[] constructors = personClass.getConstructors();
        System.out.print("\n本类所有public修饰的构造器:");
        for (Constructor<?> constructor : constructors) {
            System.out.print(constructor.getName()+" ");
        }//本类所有public修饰的构造器:reflect_api.Person reflect_api.Person

        //8. getDeclaredConstructors:获取本类中所有构造器
        Constructor<?>[] declaredConstructors = personClass.getDeclaredConstructors();
        System.out.print("\n本类中所有构造器:");
        for (Constructor<?> declaredConstructor : declaredConstructors) {
            System.out.print(declaredConstructor.getName()+" ");
        }//本类中所有构造器:reflect_api.Person reflect_api.Person reflect_api.Person

        //9. getPackage:以Package形式返回 包信息
        Package aPackage = personClass.getPackage();
        System.out.println("\n本类包名:"+aPackage.getName());//本类包名:reflect_api

        //10.getSuperClass:以Class形式返回父类信息
        Class<? super Person> superclass = personClass.getSuperclass();
        System.out.println("本类的父类:"+superclass);//本类的父类:class reflect_api.A

        //11.getlnterfaces:以Class[]形式返回接口信息
        Class<?>[] interfaces = personClass.getInterfaces();
        System.out.print("本类接口信息:");
        for (Class<?> anInterface : interfaces) {
            System.out.print(anInterface.getName()+" ");
        }//本类接口信息:reflect_api.IA reflect_api.IB

        //12.getAnnotations:以Annotation[]形式返回注解信息
        Annotation[] annotations = personClass.getAnnotations();
        System.out.print("\n本类的注解:");
        for (Annotation annotation : annotations) {
            System.out.println(annotation+" ");
        }//本类的注解:@java.lang.Deprecated()
    }
    //其他方法API
    @Test
    public void api_m2() throws ClassNotFoundException {
        //得到Class对象
        Class<?> personCls = Class.forName("reflect_api.Person");

        //获取本类所有属性
        Field[] declaredFields = personCls.getDeclaredFields();
        //1. getModifiers:以 int 形式返回修饰符
        //   1. 默认修饰符:0    2. public:1       3. private:2
        //   4. protect:4      5. static:8       6. final:16
        //   7. public static:1 + 8 = 9
        //2. getType:以 Class 形式返回类型
        //3. getName:返回属性名
        for (Field declaredField : declaredFields) {
            System.out.println("属性 "+declaredField.getName()+" 的修饰符为:"+declaredField.getModifiers()
                +" ,属性的类型为:"+declaredField.getType());
        }
        //属性 name 的修饰符为:1 ,属性的类型为:class java.lang.String
        //属性 age 的修饰符为:4 ,属性的类型为:int
        //属性 job 的修饰符为:0 ,属性的类型为:class java.lang.String
        //属性 sal 的修饰符为:2 ,属性的类型为:double

         //获取所有方法
        Method[] methods = personCls.getDeclaredMethods();
        for (Method method : methods) {
            System.out.println("方法 "+method.getName()+" 的修饰符为:"+method.getModifiers()
                +",返回值为:"+method.getReturnType().getName());
            Class<?>[] parameterTypes = method.getParameterTypes();
            System.out.print("该方法的参数有:");
            for (Class<?> parameterType : parameterTypes) {
                System.out.print(parameterType.getSimpleName()+" ");
            }
            System.out.println();
        }
        //方法 m1 的修饰符为:1,返回值为:void
        //该方法的参数有:String int double
        //方法 m2 的修饰符为:4,返回值为:void
        //该方法的参数有:
        //方法 m4 的修饰符为:2,返回值为:void
        //该方法的参数有:
        //方法 m3 的修饰符为:0,返回值为:void
        //该方法的参数有:String

        //获取所有构造器
        Constructor<?>[] constructors = personCls.getDeclaredConstructors();
        for (Constructor<?> constructor : constructors) {
            System.out.println("构造器 "+constructor.getName()+" 修饰符:"+constructor.getModifiers());
            Class<?>[] parameterTypes = constructor.getParameterTypes();
            System.out.print("该构造器的参数有:");
            for (Class<?> parameterType : parameterTypes) {
                System.out.print(parameterType.getSimpleName()+" ");
            }
            System.out.println();
        }
        //构造器 reflect_api.Person 修饰符:2
        //该构造器的参数有:String int
        //构造器 reflect_api.Person 修饰符:1
        //该构造器的参数有:String
        //构造器 reflect_api.Person 修饰符:1
        //该构造器的参数有:
    }

}
class A {
    public String hobby;
    private int height;
    public A() {}
    public void hi() {}
}
interface IA {}
interface IB {}
@Deprecated
class Person extends A implements IA,IB {
    public String name;
    protected int age;
    String job;
    private double sal;
    public Person() {}
    public Person(String name) {}
    private Person(String name,int age) {}
    public void m1(String name,int age,double sal) {}
    protected void m2() {}
    void m3(String job) {}
    private void m4() {}
}

17.5 通过反射创建对象

  1. 方式一:调用类中的 public 修饰的无参构造器
  2. 方式二:调用类中的指定构造器
  3. 相关方法:
    1. Class 类相关方法
      1. newlnstance:调用类中的无参构造器,获取对应类的对象
      2. getConstructor(Class...clazz):根据参数列表,获取对应的 public 构造器对象
      3. getDecalaredConstructor(Class...clazz):根据参数列表,获取对应的所有构造器对象
    2. Constructor 类相关方法
      1. setAccessible:暴破
      2. newlnstance(Object...obj):调用构造器
public class ReflectionCreateInstance {
    public static void main(String[] args) throws ReflectiveOperationException {
        //1.获取User类的Class对象
        Class<?> userClass = Class.forName("reflection.User");

        //2.通过public的无参构造器创建实例
        Object o = userClass.newInstance();
        System.out.println(o);//User [age=10, name=admin]

        //3.通过public的有参构造器创建实例
        //3.1先得到对应的构造器
        Constructor<?> constructor = userClass.getConstructor(String.class);
        //3.2创建实例并传入实参
        Object tom = constructor.newInstance("tom");
        System.out.println(tom);//User [age=10, name=tom]

        //4.通过非public的有参构造器创建实例
        //4.1得到private的构造器对象
        Constructor<?> constructor1 = userClass.getDeclaredConstructor(int.class, String.class);
        //Object jack = constructor1.newInstance(100, "jack");//IllegalAccessException
        //can not access a member of class reflection.User with modifiers "private"
        //创建实例
        //暴破【暴力破解】 , 使用反射可以访问 private 构造器/方法/属性, 反射面前, 都是纸老虎
        constructor1.setAccessible(true);
        Object jack = constructor1.newInstance(100, "jack");//IllegalAccessException
        System.out.println(jack);//User [age=100, name=jack]
    }
}

class User { //User 类
    private int age = 10;
    private String name = "admin";

    public User() {//无参 public
    }

    public User(String name) {//public 的有参构造器
        this.name = name;
    }

    private User(int age, String name) {//private 有参构造器
        this.age = age;
        this.name = name;
    }

    public String toString() {
        return "User [age=" + age + ", name=" + name + "]";
    }
}

17.6 通过反射访问属性

  1. 根据属性名获取Field对象 Field f = Class对象.getDeclaredField(属性名);
  2. 暴破: f.setAccessible(true); // f 是 Field 对象
  3. 访问:
    1. f.set(o,值); // o表示对象
    2. System.out.println(f.get(o)); // o 表示对象
  4. 注意:如果是静态属性,则 set 和 get 中的参数 o ,可以写成 null
public class ReflecAccessProperty {
    public static void main(String[] args) throws ReflectiveOperationException {
        //1. 得到 Student 类对应的 Class 对象
        Class<?> stuClass = Class.forName("reflection.Student");
        //2. 创建对象
        Object o = stuClass.newInstance();//o 的运行类型就是 Student
        System.out.println(o.getClass());//Student
        //3. 使用反射得到 age 属性对象
        Field age = stuClass.getField("age");
        age.set(o, 88);//通过反射来操作属性
        System.out.println(o);//
        System.out.println(age.get(o));//返回 age 属性的值
        //4. 使用反射操作 name 属性
        Field name = stuClass.getDeclaredField("name");
        //对 name 进行暴破, 可以操作 private 属性
        name.setAccessible(true);
        //name.set(o, "tom");
        name.set(null, "tom~");//因为 name 是 static 属性, 因此 o 也可以写出 null
        System.out.println(o);
        System.out.println(name.get(o)); //获取属性值
        System.out.println(name.get(null));//获取属性值, 要求 name 是 static
    }
}

class Student {//类
    public int age;
    private static String name;

    public Student() {//构造器
    }

    public String toString() {
        return "Student [age=" + age + ", name=" + name + "]";
    }
}

17.7 通过反射访问方法

  1. 根据方法名和参数列表获取Method方法对象:
    1. Method m= clazz.getDeclaredMethod(方法名, XX.class);//得到本类的所有方法
  2. 获取对象: Object o=clazz.newlnstance();
  3. 暴破: m.setAccessible(true);
  4. 访问: Object returnValue = m.invoke(o,实参列表);//o就是对象
  5. 注意:如果是静态方法,则invoke的参数o,可以写成null!
public class ReflecAccessMethod {
    public static void main(String[] args) throws ReflectiveOperationException {
        //1. 得到 Boss 类对应的 Class 对象
        Class<?> bossCls = Class.forName("reflection.Boss");
        //2. 创建对象
        Object o = bossCls.newInstance();
        //3. 调用 public 的 hi 方法
        //Method hi = bossCls.getMethod("hi", String.class);//OK
        //3.1 得到 hi 方法对象
        Method hi = bossCls.getDeclaredMethod("hi", String.class);//OK
        //3.2 调用
        hi.invoke(o, "tom");
        //4. 调用 private static 方法
        //4.1 得到 say 方法对象
        Method say = bossCls.getDeclaredMethod("say", int.class, String.class, char.class);
        //4.2 因为 say 方法是 private, 所以需要暴破, 原理和前面讲的构造器和属性一样
        say.setAccessible(true);
        System.out.println(say.invoke(o, 100, "张三", '男'));
        //4.3 因为 say 方法是 static 的, 还可以这样调用 , 可以传入 null
        System.out.println(say.invoke(null, 200, "李四", '女'));
        //5. 在反射中, 如果方法有返回值, 统一返回 Object , 但是他运行类型和方法定义的返回类型一致
        Object reVal = say.invoke(null, 300, "王五", '男');
        System.out.println("reVal 的运行类型=" + reVal.getClass());//String
        //再演示一个返回的案例
        Method m1 = bossCls.getDeclaredMethod("m1");
        Object reVal2 = m1.invoke(o);
        System.out.println("reVal2 的运行类型=" + reVal2.getClass());//Monster
    }
}

class Monster {
}

class Boss {//类
    public int age;
    private static String name;

    public Boss() {//构造器
    }

    public Monster m1() {
        return new Monster();
    }

    private static String say(int n, String s, char c) {//静态方法
        return n + " " + s + " " + c;
    }

    public void hi(String s) {//普通 public 方法
        System.out.println("hi " + s);
    }
}

17.8 练习

  1. 练习1:通过反射修改私有成员变量

    1. 定义PrivateTest类,有私有name属性,并且属性值为hellokitty
    2. 提供getName的公有方法
    3. 创建PrivateTest的类,利用Class类得到私有的name属性,修改私有的name属性值。并调用getName()的方法打印name属性值
    public class Exercise01 {
        public static void main(String[] args) throws ReflectiveOperationException {
            //获取Class对象
            Class<PrivateTest> cls = PrivateTest.class;
            //获取PrivateTest对象
            PrivateTest o = cls.newInstance();
            //获取属性对象
            Field name = cls.getDeclaredField("name");
            //暴破
            name.setAccessible(true);
            //修改属性值
            name.set(o,"tom");//tom
            //调用getName打印属性值
            Method getName = cls.getMethod("getName");
            System.out.println(getName.invoke(o));
        }
    }
    class PrivateTest {
        private String name = "hellokitty";
    
        public String getName(){
            return name;
        }
    }
    
  2. 练习2:利用反射和File完成以下功能

    1. 利用Class类的forName方法得到File类的class对象
    2. 在控制台打印File类的所有构造器
    3. 通过newlnstance的方法创建File对象,并创建 src\aa.txt 文件
    public class Exercise02 {
        public static void main(String[] args) throws ReflectiveOperationException {
            //获得Class对象
            Class<?> fileClass = Class.forName("java.io.File");
            //获得所有构造器
            Constructor<?>[] constructors = fileClass.getDeclaredConstructors();
            System.out.println("File类的构造器有:");
            for (Constructor<?> constructor : constructors) {
                System.out.println(constructor+" ");
            }
            //创建File类构造器对象
            Constructor<?> constructor = fileClass.getConstructor(String.class);
            Object o = constructor.newInstance("src\\aa.txt");
            //获取createNewFile()方法对象
            Method createNewFile = fileClass.getMethod("createNewFile");
            createNewFile.invoke(o);
        }
    }
    
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
JDK 17中,反射仍然是使用java.lang.reflect包中的类来实现的。要反射一个类,你可以按照以下步骤: 1.获取要反射的类的Class对象。可以使用以下方法之一获取Class对象: - 使用类字面常量:例如,String.class。 - 使用对象的getClass()方法:例如,"hello".getClass()。 - 使用Class.forName()方法:例如,Class.forName("java.lang.String")。 2.使用Class对象获取类的构造函数、字段、方法等信息。可以使用以下方法之一获取这些信息: - getConstructor():获取类的公共构造函数。 - getDeclaredConstructor():获取类的所有构造函数,包括私有构造函数。 - getField():获取类的公共字段。 - getDeclaredField():获取类的所有字段,包括私有字段。 - getMethod():获取类的公共方法。 - getDeclaredMethod():获取类的所有方法,包括私有方法。 3.使用获取到的构造函数、字段、方法等信息来创建新对象、修改字段值或调用方法。可以使用以下方法之一来执行这些操作: - newInstance():使用无参构造函数创建新对象。 - setAccessible(true):允许访问私有字段或方法。 - set():设置字段的值。 - get():获取字段的值。 - invoke():调用方法。 下面是一个简单的例子,演示如何使用反射创建一个对象并调用它的方法: ``` // 获取要反射的类的Class对象 Class<?> cls = Class.forName("com.example.MyClass"); // 使用默认构造函数创建一个新对象 Object obj = cls.newInstance(); // 获取MyClass类中名为foo的公共方法 Method method = cls.getMethod("foo"); // 调用foo方法 method.invoke(obj); ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值