天天看点

java学习笔记(10)类加载器和反射一、类加载器二、反射

文章目录

  • 一、类加载器
    • 1.三种类加载器
    • 2.类加载器的内容
    • 3.类加载器的双亲委派模型
  • 二、反射
    • 1.反射的定义
    • 2.反射的基本应用
      • (1)获取Class类的对象(即得到相应类的字节码)
      • (2)关于构造方法的反射
      • (3)关于成员变量的反射
      • (4)关于成员方法的反射

一、类加载器

两个类是同一个类 = 两个类用的类加载器相同 + 类本身的全限定名相同

1.三种类加载器

  • Bootstrap ClassLoader 根类加载器(引导类加载器)

    引导类加载器(由C++语言编写)由操作系统的本地代码来实现,这个类加载器没有父类加载器。之所以会出现引导类加载器,是因为其他两个类加载器也需要类加载器来加载。它负责Java核心类的加载,比如System,String等。

  • Extension ClassLoader 扩展类加载器

    扩展类加载器(由Java语言编写)主要负责扩展路径下的代码,负责JRE的扩展目录中jar包的加载。它的父类加载器是根类加载器。

  • System ClassLoader 系统类加载器

    系统类加载器(由Java语言编写)也称为应用类加载器(APPClassLoader),这两个是等价的。负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径。它的父类加载器是扩展类加载器。

  • 用户自定义类加载器

三种类加载器的关系图如下:

java学习笔记(10)类加载器和反射一、类加载器二、反射

2.类加载器的内容

  • 除了根类加载器以外,其他的类加载器都有且只有一个父类加载器。
  • 当生成一个自定义的类加载器实例时,如果没有指定它的父加载器,那么系统类加载器将成为该类加载器的父类加载器。
  • 类加载器只负载它的“祖先”无法加载的类。(双亲委派模型)

    比如,若系统类加载器需要加载一个类,它首先委托它的父类加载器(扩展类加载器),然后扩展类加载器再委托它的父类加载器(根类加载器),如果父类加载器不能加载此类,那么子类加载器就会在自己的库中查找这个类,这样也使得安全性大大提升。

3.类加载器的双亲委派模型

(1)定义

在类加载的时候,系统会首先判断当前类是否被加载过。已经被加载的类会直接返回,否则才会尝试加载。加载的时候,首先会把该请求委派该父类加载器的 loadClass() 处理,因此所有的请求最终都应该传送到顶层的启动类加载器 BootstrapClassLoader 中。当父类加载器无法处理时,才由自己来处理。当父类加载器为null时,会使用启动类加载器 BootstrapClassLoader 作为父类加载器。

java学习笔记(10)类加载器和反射一、类加载器二、反射

(2)双亲委派模型的优点

  1. 双亲委派模型保证了Java程序的稳定运行,可以避免类的重复加载(JVM 区分不同类的方式不仅仅根据类名,相同的类文件被不同的类加载器加载产生的是两个不同的类)。
  2. 保证了 Java 的核心 API 不被篡改。

二、反射

1.反射的定义

  • 反射就是把Java类中的各种成分(成员变量,成员方法,构造器等)映射成相应的Java类。
  • JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
  • 反射一般会涉及如下类:Class(表示一个类的类)、Field(表示属性的类)、Method(表示方法的类)和Constructor(表示类的构造器的类)。

2.反射的基本应用

写在前面,对于IDEA使用反射时,要在File->Settings->Build,Execution,Deployment->Compiler->Java Compiler中的Additional command line parameters: 后面填上 -parameters,点击OK。

java学习笔记(10)类加载器和反射一、类加载器二、反射

(1)获取Class类的对象(即得到相应类的字节码)

  • 方法一:getClass()方法
Person p = new Person();
Class c = p.getClass();
           
  • 方法二:通过 类名.class 获取到字节码文件对象(任意数据类型都具备一个class静态属性)
Class c2 = Person.class;
           
  • 方法三:通过调用Class类中的静态方法forName()方法将类名作为字符串传递给Class类中的静态方法forName即可)
Class c3 = Class.forName("Person");
           

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

首先写好了一个Reflect类,然后利用方法三去获得Class类的对象。

public class Reflect {
    public String name;
    public int age;
    private String address;
    public Reflect(){
        System.out.println("空参构造方法");
    }

    public Reflect(String name){
        this.name = name;
        System.out.println("带有String的构造方法");
    }

    private Reflect(String name,int age) {
        this.name = name;
        this.age = age;
        System.out.println("带有String,int的私有构造方法");
    }

    public Reflect(String name ,int age,String address){
            this.name = name;
            this.age = age;
            this.address = address;
            System.out.println("带有String,int,String的构造方法");
        }

        public void method1(){
            System.out.println("没有返回值没有参数的方法");
    }

    public void method2(String name){
            System.out.println("没有返回值有参数的构造方法"+name);
    }

    public int method3(){
        System.out.println("有返回值,没有参数的构造方法");
        return 1;
    }

    public String method4(String name){
        System.out.println("有返回值,有参数的方法");
        return "参数为:"+name;
    }

    public void method5(){
        System.out.println("私有方法");
    }

    public String toString(){
        return "Reflect [name=" + name + ", age=" + age + ", address=" + address+ "]";
    }
}
           
public class Demo{
    public static void main(String[] args){
        Class c = Class.forName("Reflect");
        System.out.println(c);
    }
}
           

运行结果:

java学习笔记(10)类加载器和反射一、类加载器二、反射

将ClassNotFoundException异常抛出后,运行结果:

java学习笔记(10)类加载器和反射一、类加载器二、反射

(2)关于构造方法的反射

(2)-1通过反射获取构造方法

  • 返回一个构造方法
    • public Constructor getConstructor(Class<?>… parameterTypes) 获取由public修饰, 且指定参数类型对应的构造方法。
    • public Constructor getDeclaredConstructor(Class<?>… parameterTypes) 获取指定参数类型所对应的构造方法(包含私有的)。
  • 返回多个构造方法
    • public Constructor<?>[ ] getConstructors() 获取所有的public 修饰的构造方法。
    • public Constructor<?>[ ] getDeclaredConstructors() 获取所有的构造方法(包含私有的)。

举例如下(需要导包):

import java.lang.reflect.Constructor;
public class Demo{
    public static void main (String[] args) throws ClassNotFoundException, NoSuchMethodException {

        Class c = Class.forName("Reflect");
        System.out.println("----------获取所有的构造方法--------------");
        Constructor[] cons = c.getDeclaredConstructors();
        for(Constructor con : cons){
            System.out.println(con);
        }
        
        System.out.println("-----------获取一个构造方法(无参)-------------");
        Constructor con1 = c.getConstructor(null);
        System.out.println(con1);
        
        System.out.println("-----------获取一个构造方法(有参)-------------");
        Constructor con2 = c.getConstructor(String.class);
        System.out.println(con2);
        
        System.out.println("-----------获取一个全部构造方法(包括私有)(有参)-------------");
        Constructor con3 = c.getDeclaredConstructor(String.class,int.class);
        System.out.println(con3);
    }
}
           

运行结果:

java学习笔记(10)类加载器和反射一、类加载器二、反射

(2)-2通过反射方式,获取构造方法(public权限),创建对象

  • 步骤:
  1. 获取到Class对象(forName())
  2. 获取指定的构造方法(利用(2)-1)
  3. 通过构造方法类Constructor中的方法,创建对象

    public T newInstance(Object… initargs)

  • 举例如下:
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class Demo{
    public static void main (String[] args) throws ClassNotFoundException, 
            NoSuchMethodException, IllegalAccessException, 
            InvocationTargetException, InstantiationException {
        Class c = Class.forName("Reflect");
        Constructor con = c.getConstructor(String.class, int.class, String.class);
       Object obj = con.newInstance("小东",22,"西安");
        System.out.println("obj");
    }
}
           
  • 运行结果:获取的哪个构造方法,就用哪个构造方法创建对象,创建对象时用newInstance()
java学习笔记(10)类加载器和反射一、类加载器二、反射

(2)-3通过反射方式,获取私有构造方法,创建对象

  • 要想获取私有构造方法,并用私有构造方法来创建对象,首先就要取消java的权限修饰符(public、private等)。AccessibleObject 类是 Field、Method 和 Constructor 对象的父类。它提供了将反射的对象标记为在使用时取消默认 Java 语言访问控制检查的能力。
  • 步骤
  1. 获取到Class对象
  2. 获取指定的构造方法
  3. 暴力访问, 通过setAccessible(boolean flag)方法
  4. 通过构造方法类Constructor中的方法,创建对象

    public T newInstance(Object… initargs)

  • 举例如下:
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class Demo{
    public static void main (String[] args) throws ClassNotFoundException,
            NoSuchMethodException, IllegalAccessException,
            InvocationTargetException, InstantiationException {
        Class c = Class.forName("Reflect");
        Constructor con = c.getDeclaredConstructor(String.class, int.class);
        con.setAccessible(true);             //是否取消 Java 语言访问检查:true
        Object obj = con.newInstance("小东",22);
        System.out.println("obj");
    }
}
           
  • 运行结果
    java学习笔记(10)类加载器和反射一、类加载器二、反射

(3)关于成员变量的反射

(3)-1通过反射获取成员变量

在反射机制中,把类中的成员变量使用类Field表示。可通过Class类中提供的方法获取成员变量:

  • 返回一个成员变量
    • public Field getField(String name) 获取指定的 public修饰的变量。
    • public Field getDeclaredField(String name) 获取指定的任意变量。
  • 返回多个成员变量
    • public Field[] getFields() 获取所有public修饰的变量。
    • public Field[] getDeclaredFields() 获取所有的 变量 (包含私有)。

举例如下:

import java.lang.reflect.Field;

public class Demo{
    public static void main (String[] args) throws ClassNotFoundException,
            NoSuchMethodException, NoSuchFieldException {
        Class c = Class.forName("Reflect");
        System.out.println("---------获取多个变量(包括私有)--------");
        Field[] f = c.getDeclaredFields();
        for(Field field : f){
            System.out.println(field);
        }

        System.out.println("---------获取一个变量--------");
        Field ageField = c.getField("age");
        System.out.println(ageField);
        
        System.out.println("---------获取一个变量(私有)--------");
        Field addressField = c.getDeclaredField("address");
        System.out.println(addressField);

    }
}

           

运行结果:

java学习笔记(10)类加载器和反射一、类加载器二、反射

(3)-2通过反射获取成员变量,并进行赋值与获取值操作

  • 获取成员变量,步骤如下:
  1. 获取Class对象
  2. 获取构造方法
  3. 通过构造方法,创建对象
  4. 获取指定的成员变量(私有成员变量,通过setAccessible(boolean flag)方法暴力访问)
  5. 通过方法,给指定对象的指定成员变量赋值或者获取值

    5.1public void set(Object obj, Object value)

    在指定对象obj中,将此 Field 对象表示的成员变量设置为指定的新值

    5.2public Object get(Object obj)

    返回指定对象obj中,此 Field 对象表示的成员变量的值

  • 举例如下:
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;

public class Demo{
    public static void main (String[] args) throws ClassNotFoundException,
            NoSuchMethodException, NoSuchFieldException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Class c = Class.forName("Reflect");
        Constructor con = c.getConstructor(String.class);
        Object obj = con.newInstance("小明");
        Field nameField = c.getField("name");
        Field ageField = c.getField("age");
        Field addressField = c.getDeclaredField("address");
        addressField.setAccessible(true);        //取消私有成员变量addressField的Java 语言访问检查
        //通过方法,获取指定对象的指定成员变量的值
        System.out.println("------------取值--------------");
        System.out.println("name = "+ nameField.get(obj));
        System.out.println("age = "+ ageField.get(obj));
        System.out.println("address = "+ addressField.get(obj));
        //赋值
        System.out.println("------------赋值--------------");
        ageField.set(obj, 23);
        addressField.set(obj, "大雁塔");
        System.out.println("name = "+ nameField.get(obj));
        System.out.println("age = "+ ageField.get(obj));
        System.out.println("address = "+ addressField.get(obj));
    }
}

           
  • 运行结果:
    java学习笔记(10)类加载器和反射一、类加载器二、反射

(4)关于成员方法的反射

(4)-1通过反射获取成员方法

在反射机制中,把类中的成员方法使用类Method表示。可通过Class类中提供的方法获取成员方法:

  • 返回获取一个方法:
    • public Method getMethod(String name, Class<?>… parameterTypes)

      获取public 修饰的方法

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

      获取任意的方法,包含私有的

      (参数1: name 要查找的方法名称; 参数2: parameterTypes 该方法的参数类型)

  • 返回获取多个方法:
    • public Method[] getMethods() 获取本类与父类中所有public 修饰的方法
    • public Method[] getDeclaredMethods() 获取本类中所有的方法(包含私有的)

举例如下:

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

public class Demo{
    public static void main (String[] args) throws ClassNotFoundException,
            NoSuchMethodException, NoSuchFieldException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Class c = Class.forName("Reflect");
        System.out.println("---------获取多个方法--------------");
       Method [] methods = c.getDeclaredMethods();
       for(Method m : methods){
           System.out.println(m);
       }

        System.out.println("---------获取一个方法--------------");
        Method method = c.getMethod("method1", null);
        System.out.println(method);
        method = c.getMethod("method4", String.class);
        System.out.println(method);

        System.out.println("---------获取一个方法(私有)--------------");
        method = c.getDeclaredMethod("method5", null);
        System.out.println(method);
    }
}

           

结果如下:

java学习笔记(10)类加载器和反射一、类加载器二、反射

(4)-2通过反射获取成员方法,使用成员方法

  • 获取成员方法,步骤如下:
  1. 获取Class对象
  2. 获取构造方法
  3. 通过构造方法,创建对象
  4. 获取指定的方法
  5. 执行找到的方法

    5.1public Object invoke(Object obj, Object… args)

    执行指定对象obj中,当前Method对象所代表的方法,方法要传入的参数通过args指定。

  • 举例如下:
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Demo{
    public static void main (String[] args) throws ClassNotFoundException,
            NoSuchMethodException, NoSuchFieldException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Class c = Class.forName("Reflect");
        Constructor con = c.getConstructor(String.class, int.class, String.class);
        Object obj = con.newInstance("小东", 23, "西安");
        
        System.out.println("-----------调用方法------------");
        Method m4 = c.getMethod("method4",String.class);
        Object result = m4.invoke(obj,"你好呀");
        System.out.println("result = " + result);
        
        System.out.println("-----------调用私有方法------------");
        Method m5 = c.getDeclaredMethod("method5",null);
        m5.setAccessible(true);
        m5.invoke(obj,null);
    }
}


           
  • 运行结果:
java学习笔记(10)类加载器和反射一、类加载器二、反射