反射

  • 类加载器
  • 类加载的描述
  • 类的加载
  • 类的连接
  • 类的初始化
  • 类的初始化步骤
  • JVM的类加载机制
  • Java中的内置类加载器
  • 反射
  • 获取Class类对象的三种方式
  • 使用类的class属性来获取该类对应的Class对象
  • 调用对象的getClass()方法,返回该对象所属类对应的Class对象
  • 使用Class类中的静态方法forName(String className)
  • 反射获取构造方法并使用
  • Constructor类用于创建对象的方法
  • Field类用于给成员变量赋值的方法
  • 反射获取成员方法并使用
  • 反射练习之越过泛型检查
  • 运行配置文件中指定类的指定方法


类加载器

类加载的描述

当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过类的加载,类的连接,类的初始化这三个步骤来对类进行初始化。如果不出现意外情况,JVM将会连续完成这三个步骤,所以有时也把这三个步骤统称为类加载或者类初始化

类的加载

就是指将class文件读入内存,并为之创建一个 java.lang.Class 对象
任何类被使用时,系统都会为之建立一个 java.lang.Class 对象

类的连接

验证阶段:用于检验被加载的类是否有正确的内部结构,并和其他类协调一致准备阶段:负责为类的类变量分配内存,并设置默认初始化值
解析阶段:将类的二进制数据中的符号引用替换为直接引用

类的初始化

在该阶段,主要就是对类变量进行初始化

类的初始化步骤

假如类还未被加载和连接,则程序先加载并连接该类
假如该类的直接父类还未被初始化,则先初始化其直接父类
假如类中有初始化语句,则系统依次执行这些初始化语句
注意:在执行第2个步骤的时候,系统对直接父类的初始化步骤也遵循初始化步骤1-3

JVM的类加载机制

全盘负责:就是当一个类加载器负责加载某个Class时,该Class所依赖的和引用的其他Class也将由该类加载器负责载入,除非显示使用另外一个类加载器来载入
父类委托:就是当一个类加载器负责加载某个Class时,先让父类加载器试图加载该Class,只有在父类加载器无法加载该类时才尝试从自己的类路径中加载该类
缓存机制:保证所有加载过的Class都会被缓存,当程序需要使用某个Class对象时,类加载器先从缓存区中搜索该Class,只有当缓存区中不存在该Class对象时,系统才会读取该类对应的二进制数据,并将其转换成Class对象,存储到缓存区

Java中的内置类加载器

Bootstrap class loader:它是虚拟机的内置类加载器,通常表示为null ,并且没有父null
Platform class loader:平台类加载器可以看到所有平台类 ,平台类包括由平台类加载器或其祖先定义的JavaSE平台API,其实现类和JDK特定的运行时类
System class loader:它也被称为应用程序类加载器 ,与平台类加载器不同。 系统类加载器通常用于定义应用程序类路径,模块路径和JDK特定工具上的类
类加载器的继承关系:System的父加载器为Platform,而Platform的父加载器为Bootstrap

反射

是指在运行时去获取一个类的变量和方法信息。然后通过获取到的信息来创建对象,调用方法的一种机制。由于这种动态性,可以极大的增强程序的灵活性,程序不用在编译期就完成确定,在运行期仍然可以扩展

学生类

import java.io.Serializable;

public class student {
    private  int age;
    private String name;
  

    public student() {
    }

    private student(int age, String name) {
        this.age = age;
        this.name = name;
    }
    
   
    public int getAge() {
        return age;
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
    
     public void study(String subject){
        System.out.println("学习"+subject);
    } 
    
    @Override
    public String toString() {
        return "student{" +
                "age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
}

获取Class类对象的三种方式

使用类的class属性来获取该类对应的Class对象

public class reflect {
    public static void main(String[] args) {
        Class<student> studentClass = student.class;
        System.out.println(studentClass);
    }
}

调用对象的getClass()方法,返回该对象所属类对应的Class对象

public class reflect {
    public static void main(String[] args) {
        student stu=new student();
         Class<? extends student> stuClass = stu.getClass();
        System.out.println(stuClass);
    }
}

使用Class类中的静态方法forName(String className)

public class reflect {
    public static void main(String[] args) throws ClassNotFoundException {
         Class<?> student = Class.forName("student");
        System.out.println(student);
    }
}

反射获取构造方法并使用

说明方法名

Constructor<?>[] getConstructors()

返回所有公共构造方法对象的数组

Constructor<?>[] getDeclaredConstructors()

返回所有构造方法对象的数组

Constructor getConstructor(Class<?>… parameterTypes)

返回单个公共构造方法对象

Constructor getDeclaredConstructor(Class<?>…parameterTypes)

返回单个构造方法对象

import java.lang.reflect.Constructor;

public class reflect {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
         Class<?> stuclass = Class.forName("student");

        //获取无参的构造方法
         Constructor<?> constructor = stuclass.getConstructor();
        System.out.println(constructor);//public student()

        //获取带参构造方法
         Constructor<?> constructor1 = stuclass.getConstructor(int.class, String.class);
        System.out.println(constructor1);//public student(int,java.lang.String)

        //获取所有公共的构造方法
         Constructor<?>[] constructors = stuclass.getConstructors();
          for(Constructor c:constructors){
              System.out.println(c); //public student() public student(int,java.lang.String)
          }

       //获取所有构造方法无论私有还是公共
        Constructor<?>[] constructors1 = stuclass.getDeclaredConstructors();
        for(Constructor c:constructors){
            System.out.println(c); //public student() public student(int,java.lang.String)
        }


    }
}

Constructor类用于创建对象的方法

方法名

说明

T newInstance(Object…initargs)

根据指定的构造方法创建对象

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

public class reflect {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
         Class<?> stuclass = Class.forName("student");

        //获取无参的构造方法
         Constructor<?> constructor = stuclass.getConstructor();
        System.out.println(constructor);//public student()
         Object o = constructor.newInstance();
        System.out.println(o);//student{age=0, name='null'}
        
        //获取带参构造方法
         Constructor<?> constructor1 = stuclass.getConstructor(int.class, String.class);
        System.out.println(constructor1);//public student(int,java.lang.String)
         Object o1 = constructor1.newInstance(18, "dyk");
        System.out.println(o1);//student{age=18, name='dyk'}


    }
}

上面能反射成功是因为student类里面的构造方法都是公共的,现在如果全部改成私有的会怎么样能?

会报错误 : class reflect cannot access a member of class student with modifiers “private”

那该如何解决能?
暴力反射

方法名

说明

public void setAccessible(boolean flag)

值为true,取消访问检查

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

public class reflect {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
         Class<?> stuclass = Class.forName("student");

         Constructor<?> constructor1 = stuclass.getDeclaredConstructor(int.class, String.class);
        System.out.println(constructor1);//private student(int,java.lang.String)
        constructor1.setAccessible(true);
         Object o1 = constructor1.newInstance(18, "dyk");
        System.out.println(o1);//student{age=18, name='dyk'}


    }
}

Field类用于给成员变量赋值的方法

方法名

说明

voidset(Object obj,Object value)

给obj对象的成员变量赋值为value

Field[] getFields()

返回所有公共成员变量对象的数组

Field[] getDeclaredFields()

返回所有成员变量对象的数组

Field getField(String name)

返回单个公共成员变量对象

Field getDeclaredField(String name)

返回单个成员变量对象

由于封装所以所有成员变量全部都由private修饰了,也要暴力反射

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

public class reflect {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
         Class<?> stuclass = Class.forName("student");

         Constructor<?> constructor = stuclass.getDeclaredConstructor();
         Object obj = constructor.newInstance();
        Field[] Fields = stuclass.getDeclaredFields();
         for (Field f:Fields){
             System.out.println(f);
//             private int student.age
//             private java.lang.String student.name
         }
         Field field = stuclass.getDeclaredField("age");
         field.setAccessible(true);
         field.set(obj,18);

         Field field1 = stuclass.getDeclaredField("name");
         field1.setAccessible(true);
         field1.set(obj,"dyk");

        System.out.println(obj);//student{age=18, name='dyk'}

    }
}

反射获取成员方法并使用

方法名

说明

Method[] getMethods()

返回所有公共成员方法对象的数组,包括继承的

Method[] getDeclaredMethods()

返回所有成员方法对象的数组,不包括继承的

Method getMethod(String name, Class<?>…parameterTypes)

返回单个公共成员方法象

Method getDeclaredMethod(String name, Class<?>…parameterTypes)

返回单个成员方法对象

Method类用于执行方法的方法

方法名

说明

Object invoke(Object obj,Object… args)

调用obj对象的成员方法,参数是args,返回值是Object类型

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

public class reflect {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
         Class<?> stuclass = Class.forName("student");

         Constructor<?> constructor = stuclass.getDeclaredConstructor();
         Object obj = constructor.newInstance();
         Method[] methods = stuclass.getDeclaredMethods();
         for (Method m:methods){
             System.out.println(m);
         }
         Method method = stuclass.getDeclaredMethod("study", String.class);
         method.setAccessible(true);
         method.invoke(obj,"java"); //学习java

    }
}

反射练习之越过泛型检查

通过反射技术,向一个泛型为Integer的集合中添加一些字符串数据

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;

public class reflect {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
        ArrayList<Integer> array=new ArrayList<>();
        Class<? extends ArrayList> aClass = array.getClass();
        
        Method method = aClass.getMethod("add", Object.class);
        method.invoke(array,"java");
        method.invoke(array,"python");
        method.invoke(array,"c++");

        System.out.println(array);//[java, python, c++]
    }
}

可以看到反射的强大,我定义的是integer类型的居然可以把string类型放进去

运行配置文件中指定类的指定方法

通过反射运行配置文件中指定类的指定方法

import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;

public class reflect {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException, IOException {
        Properties properties=new Properties();
        FileReader fr=new FileReader("123.txt");
        properties.load(fr);
        fr.close();
        String classname = properties.getProperty("classname");
        String methodname = properties.getProperty("methodname");

       
        Class<?> stuclass = Class.forName(classname);
         Constructor<?> constructor = stuclass.getConstructor();
        Object o = constructor.newInstance();

         Method method = stuclass.getDeclaredMethod(methodname,String.class);

         method.setAccessible(true);
         method.invoke(o,"java");

    }
}