黑马程序员全套Java教程_Java基础教程_多线程之生产者消费者(三十七)

  • 1、类加载器
  • 1.1 类加载
  • 1.2 类加载器
  • 2、反射
  • 2.1 反射概述
  • 2.2 获取Class类的对象(字节码文件对象)
  • 2.3 反射获取构造方法并使用
  • 2.4 反射获取构造方法并使用练习1
  • 2.5 反射获取成员变量并使用
  • 2.6 反射获取成员变量并使用练习
  • 2.7 反射获取成员方法并使用
  • 2.7 反射获取成员方法并使用练习
  • 2.9 反射练习


1、类加载器

1.1 类加载

  • 当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过类的加载、类的连接、类的初始化这三个步骤来对类进行初始化。如果不出现意外情况,JVM将会持续完成这三个步骤,所以有时也把这三个步骤统称为类加载或者类初始化。
  • 类的加载:
    (1)就是指将class文件读入内存,并为之创建一个java.lang.Class对象;
    (2)任何类被使用时,系统都会为之建立一个java.lang.Class对象。
  • 类的连接(分三个阶段):
    (1)验证阶段:用于检测被加载的类是否有正确的内部结构,并和其他类协调一致;
    (2)准备阶段:负责为类的类变量分配内存,并设置默认初始值;
    (3)解析阶段:将类的二进制数据中的符号引用替换为直接引用。
  • 类的初始化:在该阶段,主要就是对类变量进行初始化。
  • 类的初始化步骤:
    (1)假如类还未被加载和连接,则程序先加载并连接该类;
    (2)假如该类的直接父类还未被初始化,则先初始化其直接父类;
    (3)假如类中有初始化语句,则系统依次执行这些初始化语句。
    (4)注意:在执行第2个步骤的时候,系统对直接父类的初始化步骤也遵循初始化步骤1~3。
  • 当Java程序首次(要求是首次是因为当一个类被载入Java虚拟机,就不会再次载入)通过以下几种方式来使用某个类或者接口时,系统就会初始化该类和接口(即类的初始化时机)
    (1)创建类的实例;
    (2)调用类的类方法(静态方法);
    (3)访问类或者接口的类变量,或者为该类变量赋值;
    (4)使用反射方式来强制创建某个类或接口对应的java.lang.Class对象;
    (5)初始化某个类的子类(因为初始化子类前要先初始化父类);
    (6)直接使用java.exe命令来运行某个主类。

1.2 类加载器

  • 类加载器的作用:
    (1)负责将.class文件加载到内存中,并为之生成对应的java.lang.Class对象;
    (2)虽然我们不用过分关心类加载机制,但是了解这个机制我们就能更好地理解程序的运行。
  • JVM的类加载机制(三种形式):
    (1)全盘负责:就是当一个类加载器负责加载某个Class时,该CLass所依赖的和引用的其他Class也将由该类加载器负责载入,除非显示使用另一个类加载器来使用;
    (2)父类委托:就是当一个类加载器负责加载某个Class时,先让父类加载器试图加载该Class,只有在父类加载器无法加载该类时才尝试从自己的类路径中加载该类;
    (3)缓存机制:保证所有加载过的Class都会被缓存,当程序需要使用某个Class时,类加载器先从缓存区所有该Class,只有当缓存区中不存在该Class时,系统才会读取该类对应的二进制数据,并将其转换成Class对象,存储到缓存区。
  • ClassLoader:是负责加载类的对象;
  • Java运行时具有以下内置类加载器:
    (1)Bootstrap class loader:它是虚拟机的内置类加载器,通常表示为null,并且没有父null;
    (2)Platform class loader:平台类加载器可以看到所有平台类,平台类中包括由平台类加载器或其祖先定义的Java SE平台API,其实现类的JDK特定的运行时类;
    (3)System class loader:它被称为应用程序类加载器,与平台类加载器不同。系统类加载器通常用于定义应用程序类路径,模块路径和JDK特定工具上的类。
    (4)类加载器的继承关系:System的父类加载器为Platform,而Platform的父类加载器为Bootstrap
  • ClassLoader中的两个方法:
    (1)static ClassLoader getSystemClassLoader():返回用于委托的系统类加载器;
    (2)ClassLoader getParent():返回父类加载器进行委派。
public static void main(String[] args) {
        ClassLoader c = ClassLoader.getSystemClassLoader();
        System.out.println(c);//AppClassLoader

        ClassLoader c2 = c.getParent();
        System.out.println(c2);//PlatformClassLoader

        ClassLoader c3 = c2.getParent();
        System.out.println(c3);//null
    }

2、反射

2.1 反射概述

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

2.2 获取Class类的对象(字节码文件对象)

  • 我们想要通过反射去使用一个类,首先我们要获取该类的字解码文件对象,也就是类型为Class类型的对象,这里提供三种方式获取Class类型的对象:
    (1)使用类的Class属性来获取类对应的Class对象。举例:Student.class将会返回Student类对应的Class对象;
    (2)调用对象的getClass()方法,返回该对象所属类对应的Class对象。该方法是Object类中的方法,所有的Java对象都可以调用该方法。
    (3)使用Class类中的静态方法forName(String className),该方法需要传入字符串参数,该字符串参数的值是某个类的全路径,也就是完整包名的路径。
public static void main(String[] args) throws ClassNotFoundException {
 	   //最方便
        Class<Student> c1 = Student.class;
        System.out.println(c1);//class itheima2.Student
        //一个类在内存中只有一个字节码文件对象
        Class<Student> c2 = Student.class;
        System.out.println(c2==c1);//true


        Student s = new Student();
        Class<? extends Student> c3 = s.getClass();
        System.out.println(c3==c1);//true

		//灵活性更高,其可以把"itheima2.Student"配置到配置文件内,而我们可以修改配置文件中的内容
        Class<?> c4 = Class.forName("itheima2.Student");
        System.out.println(c4==c1);//true
    }

2.3 反射获取构造方法并使用

  • Class类中用于获取构造方法的方法:
    (1)Constructor[] getConstructor():返回所有公共构造方法对象的数组;
    (2)Constructor[] getDeclaredConstructor():返回所有构造方法对象的数组;
    (3)Constructor getConstructor(Class<?>… parameterTypes):返回单个公共构造方法对象;
    (4)Constructor getDeclaredConstructor(Class<?>… parameterTypes):返回单个构造方法对象。
  • Constructor类中用于创建对象的方法:T newInstance(Object…initargs),根据指定的构造方法创建对象。
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        //获取字节码文件
        Class<?> c = Class.forName("itheima2.Student");

        Constructor<?>[] cons = c.getConstructors();
        for (Constructor con : cons){
            System.out.println(con);
                //输出了两个构造方法
                //public itheima2.Student(java.lang.String,int,java.lang.String)
                //public itheima2.Student()
        }
        System.out.println();

        Constructor<?>[] cons2 = c.getDeclaredConstructors();
        for (Constructor con : cons2){
            System.out.println(con);
                //输出所有构造方法
                //public itheima2.Student(java.lang.String,int,java.lang.String)
                //itheima2.Student(java.lang.String,int)
                //private itheima2.Student(java.lang.String)
                //public itheima2.Student()
        }
        System.out.println();

        //public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes):返回一个Constructor对象,该对象反映由此Class对象表示的类或接口的指定构造函数
        //public Constructor<?> getConstructor(Class<?>... parameterTypes):返回一个Constructor对象,该对象反映由此Class对象表示的类或接口的指定构造函数
        //以上两个方法的参数:要获得的构造方法的个数和数据类型对应的字节码文件对象
        //获取无参构造方法
        Constructor<?> con = c.getConstructor();

        //Constructor类提供了一个类的单个构造函数的信息和访问权限
        //public T newInstance(Object ... initargs):使用由此Constructor对象表示的构造函数,使用指定的初始化参数来创建和初始化构造函数的声明类的新实例。
        Object obj = con.newInstance();

        System.out.println(obj);//Student{name='null', age=0, address='null'}
    }

2.4 反射获取构造方法并使用练习1

  • 练习1:通过反射实现如下操作:
    (1)Student s = new Student(“林青霞”,30,“西安”);
    (2)System.out.prinln(s);
    (3)基本数据类型也可以通过.class得到对应的Class类型
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
    	//1、首先得到Class对象c
        Class<?> c = Class.forName("itheima2.Student");

		//2、通过c得到构造方法对象
        //基本数据类型也可以通过.class得到对应的class类型
        Constructor<?> con = c.getConstructor(String.class, int.class, String.class);

		//3、通过构造方法对象调用newInstance()生成一个对象
        Object obj = con.newInstance("林青霞", 30, "西安");

        System.out.println(obj);
    }
  • 练习2:通过反射实现如下操作:
    (1)Student s = new Student(“林青霞”);
    (2)System.out.println(s);
    (3)public void setAccessible(boolean flag):值为true,取消访问检查
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Class<?> c = Class.forName("itheima2.Student");

        //因为要调用的为private修饰的构造方法,所以要用getDeclaredConstructor()
        Constructor<?> con = c.getDeclaredConstructor(String.class);

        //暴力反射
        //public void setAccessible(boolean flag):值为true,取消访问检查
        con.setAccessible(true);

        //IllegalAccessException:不合法访问异常,因为我们虽然获取到了private修饰的方法,但我们不可以直接用私有的方法创建对象。而通过反射,我们可以通过暴力反射来调用私有方法。
        Object obj = con.newInstance("林青霞");

        System.out.println(obj);
    }

2.5 反射获取成员变量并使用

  • Class类中用于获取成员变量的方法:
    (1)Field[] getFileds():返回所有公共成员变量对象的数组;
    (2)Field[] getDeclaredFileds():返回所有成员变量对象的数组;
    (3)Field getFileds(String name):返回单个公共成员变量对象;
    (4)Field getDeclaredFileds(String name):返回单个成员变量对象。
  • Filed类中用于给成员变量赋值的方法:void set(Object obj, Object value),给obj对象的成员变量赋值为value
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        //1、获取Class对象c
        Class<?> c = Class.forName("itheima2.Student");

        Field[] fields = c.getDeclaredFields();
        for (Field field : fields){
            System.out.println(field);
        }
        System.out.println();

        //2、通过c按照指定的成员变量拿到其对应的成员变量对象
        Field addressField = c.getField("address");

        //正常情况:
        //Student s = new Student();
        //s.address = "西安";
        //System.out.println(s);

        //3、获取无参构造方法创建对象
        Constructor<?> con = c.getConstructor();
        Object obj = con.newInstance();

        //4、给obj的成员变量addressField赋值为西安
        addressField.set(obj,"西安");

        System.out.println(obj);
    }

2.6 反射获取成员变量并使用练习

  • 练习:通过反射实现如下操作:
    (1)Student s = new Student();
    (2)s.name = “林青霞”;
    (3)s.age = 30;
    (4)s.addredd = “西安”;
    (5)System.out.println(s);
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
        //1、获取Class对象
        Class<?> c = Class.forName("itheima2.Student");

        //2、通过无参构造方法创建对象
        Constructor<?> con = c.getConstructor();
        Object s = con.newInstance();

        //3、name为private修饰
        Field nameField = c.getDeclaredField("name");
        nameField.setAccessible(true);
        nameField.set(s,"林青霞");

        //age为default
        Field ageField = c.getDeclaredField("age");
        ageField.setAccessible(true);
        ageField.set(s,30);

        //address为public修饰
        Field addressField = c.getField("address");
        addressField.set(s,"西安");

        System.out.println(s);
    }

2.7 反射获取成员方法并使用

  • Class类中用于获取成员方法的方法:
    (1)Method[] getMethods():返回所有公共成员方法对象的数组,包括继承的;
    (2)Method[] getDeclaredMethods():返回所有成员方法对象的数组,不包括继承的;
    (3)Method getMethods():返回单个公共成员方法对象;
    (4)Method getDeclaredMethods():返回所有成员方法对象;
  • Method类中用于调用成员方法的方法:Object invoke(Object obj, Object…args),调用obj对象的成员方法,参数是args,返回值是Object类型。
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Class<?> c = Class.forName("itheima2.Student");

        //Method[] methods = c.getMethods();
        Method[] methods = c.getDeclaredMethods();
        for (Method method : methods){
            System.out.println(method);
        }
        System.out.println();

        Method m = c.getDeclaredMethod("method");
        m.setAccessible(true);

        //获取无参构造方法创建对象
        Constructor<?> con = c.getConstructor();
        Object obj = con.newInstance();

        m.invoke(obj);
    }

2.7 反射获取成员方法并使用练习

  • 练习:通过反射实现如下操作
    (1)Student s = new Student();
    (2)s.method1();
    (3)s.method2(“林青霞”);
    (4)String ss = s.method3(“林青霞”,30);
    (5)System.out.println(ss);
    (6)s.function();
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Class<?> c = Class.forName("itheima2.Student");

        Constructor<?> con = c.getConstructor();
        Object obj = con.newInstance();

        Method m1 = c.getDeclaredMethod("method");
        m1.setAccessible(true);
        m1.invoke(obj);

        Method m2 = c.getDeclaredMethod("method2", String.class);
        m2.setAccessible(true);
        m2.invoke(obj,"林青霞");

        Method m3 = c.getDeclaredMethod("method3", String.class, int.class);
        m3.setAccessible(true);
        Object o = m3.invoke(obj,"林青霞",30);
        System.out.println(o);
        
        Method m4 = c.getDeclaredMethod("function");
        m4.setAccessible(true);
        m4.invoke(obj);
    }

2.9 反射练习

  • 练习1:有一个ArrayList集合,现在想在这个集合中添加一个字符串数据,如何实现?
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        ArrayList<Integer> arrayList = new ArrayList<>();

        Class<? extends ArrayList> c = arrayList.getClass();
        //System.out.println(c);//class java.util.ArrayList

        Method m = c.getMethod("add", Object.class);

        m.invoke(arrayList,"liubei");
        m.invoke(arrayList,"guanyu");
        m.invoke(arrayList,"zhangfei");
        
        System.out.println(arrayList);
    }
  • 练习2:通过配置文件运行类中的方法
public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Properties prop = new Properties();
        BufferedReader br = new BufferedReader(new FileReader("src\\class.txt"));
        prop.load(br);
        br.close();

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

        Class<?> c = Class.forName(className);

        Constructor<?> con = c.getConstructor();
        Object obj = con.newInstance();

        Method method = c.getMethod(methodName);
        method.invoke(obj);
    }