类的加载、反射

1 类加载器

1.1 类的加载

当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化。

  • 加载,就是指将class文件读入内存,并为之创建一个Class对象(字节码对象)
  • 连接,验证是否有正确的内部结构,并和其他类协调一致。准备 负责为类的静态成员分配内存,并设置默认初始化值。解析将类的二进制数据中的符号引用替换为直接引用。
  • 初始化,就是我们以前讲过的初始化步骤。

1.2 类的初始化时机

  1. 创建类的实例
  2. 类的静态变量,或者为静态变量赋值
  3. 类的静态方法
  4. 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
  5. 初始化某个类的子类
  6. 直接使用java.exe命令来运行某个主类

1.3 类的加载器

负责将.class文件加载到内在中,并为之生成对应的Class对象。

1.3.1 类的加载器组成

  1. Bootstrap ClassLoader 根类加载器,也被称为引导类加载器,负责Java核心类的加载。比如System,String等。在JDK中JRE的lib目录下rt.jar文件中。
  2. Extension ClassLoader 扩展类加载器,负责JRE的扩展目录中jar包的加载。在JDK中JRE的lib目录下ext目录。
  3. System ClassLoader 系统类加载器,负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径。

2 反射

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象。

2.1 获取一个类的class文件对象的3中方法

  1. 对象获取;
  2. 类名获取;
  3. Class 类的静态方法获取;
package demo;
public class Person {

    public  String name;
    private int age;

    static {
        System.out.println("静态代码块");
    }

    public Person() {
    }

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

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

    public void eat(){
        System.out.println("吃饭");
    }

    public void sleep(String s,int a,double d){
        System.out.println("在睡觉"+s+"  "+a+"  "+d);
    }

    public void playGame(){
        System.out.println("人在打游戏");
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

    @Override
    public String toString() {
        return "Person{" + "name='" + name + '\'' + ", age=" + age + '}';
    }
}

public static void main(String[] args)throws ClassNotFoundException {
        //1.对象获取
        Person p = new Person();
        Class c = p.getClass();
        System.out.println(c);

        //2.类名获取
        //每个类型,包括基本和引用,都会赋予这个类型一个静态的属性,属性名字class
        Class c1 = Person.class;
        System.out.println(c1);

        System.out.println(c==c1);//T
        System.out.println(c.equals(c1));//T

        //3.Class的静态方法forName(字符串的类名)
        Class c2=Class.forName("demo.Person");
        System.out.println(c2);
    }

前两种你必须明确Person类型.
后面是指定这种类型的字符串就行.这种扩展更强.我不需要知道你的类.我只提供字符串,按照配置文件加载就可以了

2.2 反射获取class文件中的构造方法,运行构造方法(创建对象)

2.2.1 反射获取空参数的构造函数

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

/**
 * 1.获取class文件对象
 * 2.从class文件对象,获取需要的成员
 */
public class ReflactDemo1 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Class c = Class.forName("demo.Person");
        //getConstructors()获取class文件对象中所有的公共的构造方法
       /* Constructor[] cons = c.getConstructors();
        for(Constructor con:cons){
            System.out.println(con);
        }*/

        //获取指定的构造函数,空参数的构造函数
        Constructor con = c.getConstructor();
        //运行空参数的构造方法
        Object obj = con.newInstance();
        System.out.println(obj);

    }
}

2.2.2 反射获取有参数的构造方法

public class ReflactDemo2 {
    public static void main(String[] args)throws Exception {
        Class c=Class.forName("demo.Person");
        //获取带有String 和 int 构造方法
        //Constructor<T> getConstructor(Class<?>...parameterTypes)
        Constructor con= c.getConstructor(String.class,int.class);
        //运行构造方法
        Object obj= con.newInstance("mark",1);
        System.out.println(obj);
    }
}

2.2.3 反射获取构造方法并运行的快速方式

  • 前提:被反射的类,必须具有空参数的构造方法;
  • 构造方法必须 public;
public static void main(String[] args) throws Exception {
        Class c = Class.forName("demo.Person");
        // Class类中定义方法,T newInstance()
        //直接创建被反射类的对象实例
        Object obj = c.newInstance();
        System.out.println(obj);
    }

2.2.4 反射获取私有构造方法并运行

package demo;

import java.lang.reflect.Constructor;
public class ReflactDemo4 {
    public static void main(String[] args) throws Exception {
        Class c = Class.forName("demo.Person");
        // Constructor[] getDeclaredConstructors() 获取所有的构造方法,包括私有
        /*Constructor[] cons = c.getDeclaredConstructors();
        for(Constructor con:cons){
            System.out.println(con);
        }*/

        //Constructor getDeclaredConstructor(Class...c)获取到指定参数列表的构造方法
        Constructor con= c.getDeclaredConstructor(int.class,String.class);

        //Constructor类,父类AccessibleObject,定义方法setAccessible(boolean b)
        con.setAccessible(true);
        Object obj= con.newInstance(18,"John");
        System.out.println(obj);

    }
}

2.2.5 反射获取成员变量并改值


import java.lang.reflect.Field;

public class ReflactDemo5 {
    public static void main(String[] args) throws Exception {
        Class c = Class.forName("demo.Person");
        Object obj = c.newInstance();
        //获取成员变量Class类的方法getFields() class文件中的所有公共的成员变量
        //返回值 Field[],Field类描述成员变量对象的类
        /*Field[] fields = c.getFields();
        for(Field f:fields){
            System.out.println(f);
        }*/

        //获取指定的成员变量
        Field field= c.getField("name");
        System.out.println(field);

        //Field 类的方法 void set(Object obj,Object value)
        //Object obj必须有对象类的支持,Object value 修改后的值
        field.set(obj,"KKKKK");
        System.out.println(obj);
    }
}

2.2.6 反射获取空参数成员方法并运行

package demo;

import java.lang.reflect.Method;

public class ReflactDemo6 {
    public static void main(String[] args)throws Exception {
        Class c= Class.forName("demo.Person");
        Object obj=c.newInstance();
        //Methods[] getMethods 获取的是class 文件中的所有公共成员方法,包括继承的
        //Method 类是描述成员方法的对象
       /* Method[] methods=c.getMethods();
        for(Method m:methods){
            System.out.println(m);
        }*/

       //获取指定的方法eat运行
        //Method getMethod(String methodName,Class...c)
        Method method= c.getMethod("eat");
        //System.out.println(method);

        //运行eat
        method.invoke(obj);

    }
}

2.2.7 反射获取有参数成员方法并运行

public class ReflectDemo7 {
    public static void main(String[] args) throws Exception {
        Class c = Class.forName("demo.Person");
        Object obj = c.newInstance();

        Method method = c.getMethod("sleep", String.class, int.class, double.class);
        //System.out.println(method);
        method.invoke(obj,"休眠",100,889.99);

    }
}

3 反射泛型擦除


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

/**
 * 定义集合类,泛型String
 * 要求向集合添加Integer类型
 * 反射方式,获取出ArrayList类class文件对象
 * 通过class文件对象,调用add方法
 * 伪泛型:编译后的class文件,没有泛型的
 */
public class ReflectTest {
    public static void main(String[] args) throws Exception {
        ArrayList<String> array=new ArrayList<String>();
        array.add("a");
        //反射方式,获取出集合ArrayList类的class文件对象
        Class c= array.getClass();
        Method method= c.getMethod("add",Object.class);
//        System.out.println(method);
        method.invoke(array,150);
        method.invoke(array,1500);
        method.invoke(array,15000);
        System.out.println(array);


    }
}

4 反射通过配置文件

package demo3;

public class Person {
    public void eat(){
        System.out.println("人在吃饭");
    }
}

这里写图片描述

import java.io.FileReader;
import java.lang.reflect.Method;
import java.util.Properties;

/**
 * 调用Person方法,调用Student方法,调用Worker方法
 * 类不清楚,方法也不清楚
 * 通过配置文件实现此功能
 *     运行的类名和方法名字,以键值对的形式写在文本
 * 实现步骤:
 *  1、 准备配置文件
 *  2、 IO流读取配置文件,Reader
 *  3、 文件中的键值对存储到集合中 Properties
 *  4、 反射获取指定类的class 文件对象
 *  5、 class 文件对象,获取指定的方法
 *
 */
public class Test {
    public static void main(String[] args) throws Exception {
        FileReader r=new FileReader("config.properties");
        Properties pro = new Properties();
        pro.load(r);
        r.close();

        String className = pro.getProperty("className");
        String methodName=pro.getProperty("methodName");

        Class c= Class.forName(className);
        Object obj=c.newInstance();
        Method method=c.getMethod(methodName);
        method.invoke(obj);

    }
}

猜你喜欢

转载自blog.csdn.net/u012292754/article/details/80281998