java基础——类加载与反射

第1章 类加载器

1.1 类的加载

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

(1)加载

  就是指将class文件读入内存,并为之创建一个Class对象。

  任何类被使用时系统都会建立一个Class对象

(2)连接

  验证 是否有正确的内部结构,并和其他类协调一致

  准备 负责为类的静态成员分配内存,并设置默认初始化值

  解析 将类的二进制数据中的符号引用替换为直接引用

(3)初始化

  就是我们以前讲过的初始化步骤

 

1.2 类初始化时机

1. 创建类的实例

2. 类的静态变量,或者为静态变量赋值

3. 类的静态方法

4. 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象

5. 初始化某个类的子类

6. 直接使用java.exe命令来运行某个主类

 

1.3 类加载器

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

(2)虽然我们不需要关心类加载机制,但是了解这个机制我们就能更好的理解程序的运行

 

1.4 类加载器的组成

(1)Bootstrap ClassLoader 根类加载器

  也被称为引导类加载器,负责Java核心类的加载

  比如System,String等。在JDKJRElib目录下rt.jar文件中

(2)Extension ClassLoader 扩展类加载器

  负责JRE的扩展目录中jar包的加载。

  在JDKJRElib目录下ext目录

(3)System ClassLoader 系统类加载器

  负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径。

 

 

 

 

通过这些描述就可以知道我们常用的类,都是由谁来加载完成的。

到目前为止我们已经知道把class文件加载到内存了,那么,如果我们仅仅站在这些class文件的角度,我们如何来使用这些class文件中的内容呢?

这就是我们反射要研究的内容。

 

第2章 反射

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

2.1 反射的概念

要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象。

2.2 class文件的产生过程

 

2.3 Class

阅读API的Class类得知,Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的

 

/**
 * 获取一个类的class、文件对象的三种方式
 * 1.对象获取
 * 2.类名获取
 * 3.class类的静态方法获取
 * Created by YuKai Fan on 2018/8/21.
 */
public class ReflexDemo {
    public static void main(String[] args) throws ClassNotFoundException {
        //1.对象获取
        Person p = new Person();
        //调用Person类的父类(Object)方法 getClass
        Class c = p.getClass();
        System.out.println(c);

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

        //3.class类的静态方法获取forName(字符串的类名【包名.类名】)
        Class c2 = Class.forName("Demo.Person");
        System.out.println(c2);

        //因为class文件只有一个,class文件对象也只有一个,所以c与c1的内存地址一样
        System.out.println(c==c1);//True
        System.out.println(c.equals(c1));//True
    }
}

 

Person类:

/**
 * Created by YuKai Fan on 2018/8/21.
 */
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 b, double d) {
        System.out.println(b+d+"人睡觉"+s);
    }
    public String getName() {
        return name;
    }

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

    public Integer getAge() {
        return age;
    }

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

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

注意:第三种和前两种的区别

  前两种你必须明确Person类型.

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

 

2.4 通过反射获取构造方法并使用

 

在反射机制中,把类中的成员(构造方法、成员方法、成员变量)都封装成了对应的类进行表示。其中,构造方法使用类Constructor表示。可通过Class类中提供的方法获取构造方法:

 

(1) 返回一个构造方法

 

  l public Constructor<T> getConstructor(Class<?>... parameterTypes) 获取public修饰, 指定参数类型所对应的构造方法

 

  l public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) 获取指定参数类型所对应的构造方法(包含私有的)

 

(2)返回多个构造方法

 

  l public Constructor<?>[] getConstructors() 获取所有的public 修饰的构造方法

 

  l public Constructor<?>[] getDeclaredConstructors() 获取所有的构造方法(包含私有的)

/**
 * 通过反射获取class文件中的构造方法,运行构造方法
 * 运行构造方法,创建对象
 *      获取class文件对象
 *      从class文件对象中,获取需要的成员
 *
 *  Constructor 描述构造方法对象类
 * Created by YuKai Fan on 2018/8/21.
 */
public class ReflexDemo1 {
    public static void main(String[] args) throws Exception {
        Class c = Class.forName("Demo.Person");//会强行的运行,类中的静态代码块
        //使用class文件对象获取类中的构造方法
        //Constructor[]  getConstructors() 获取class文件对象中的所有公共的构造方法
        Constructor[] cons = c.getConstructors();
        for (Constructor con : cons) {
          System.out.println(con);
        }

        //获取指定的构造方法,空参数的构造方法
        Constructor con = c.getConstructor();
        //运行构造方法,Constructor类方法newInstance()运行获取到的构造方法
        Object obj = con.newInstance();
        Person p = (Person)obj;
        p.eat();
        System.out.println(obj);
        System.out.println(con);
    }
}
/**
 * 通过反射,获取有参数的构造方法并运行
 * 方法getConstructor 可以传递构造方法相对于的参数列表即可
 * Created by YuKai Fan on 2018/8/21.
 */
public class ReflexDemo2 {
    public static void main(String[] args) throws Exception {
        Class c = Class.forName("Demo.Person");
        //获取带有int,String参数的构造方法
        //Constructor<T> getConstructor(Class<T>...parameterTypes)
        //Class<T>...parameterTypes传递要获取的构造方法的参数列表
        Constructor con = c.getConstructor(String.class, int.class);
        System.out.println(con);

        //运行构造方法
        //T newInstance(Object...initargs)
        //Object..initargs 运行构造方法后,传递的实际参数
        Object obj = con.newInstance("张三", 20);
        System.out.println(obj);
    }
/**
 * 反射获取构造方法并运行,快捷的方式
 * 前提:
 *      被反射的类,必须具有空参数构造方法
 *      构造方法必须是最大权限public
 * Created by YuKai Fan on 2018/8/21.
 */
public class ReflexDemo3 {
    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.4.1 通过反射方式,获取私有构造方法,创建对象

AccessibleObject 类是 FieldMethod Constructor 对象的类。它提供了将反射的对象标记为在使用时取消默认 Java 语言访问控制检查的能力。

对于公共成员、默认(打包)访问成员、受保护成员和私有成员,在分别使用 FieldMethod Constructor 对象来设置或获取字段、调用方法,或者创建和初始化类的新实例的时候,会执行访问检查。常用方法如下

 public void setAccessible(boolean flag) throws SecurityException 

参数值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。参数值为 false 则指示反射的对象应该实施 Java 语言访问检查。

 

获取私有构造方法,步骤如下:

  1. 获取到Class对象

  2. 获取指定的构造方法

  3. 暴力访问, 通过setAccessible(boolean flag)方法

  4. 通过构造方法类Constructor中的方法,创建对象

  public T newInstance(Object... initargs)

/**
 * 反射获取私有的构造方法运行
 * 不推荐,破坏了程序的封装性,安全性
 * 暴力反射
 * Created by YuKai Fan on 2018/8/21.
 */
public class ReflexDemo4 {
    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);
        System.out.println(con);
        Object obj = con.newInstance(18, "李四");
        System.out.println(obj);
    }

}

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

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

(1) 返回一个成员变量

  public Field getField(String name) 获取指定的 public修饰的变量

  public Field getDeclaredField(String name) 获取指定的任意变量

(2)返回多个成员变量

  public Field[] getFields() 获取所有public 修饰的变量

  public Field[] getDeclaredFields() 获取所有的 变量 (包含私有)

/**
 * 反射获取成员变量,并修改值
 * Person类中的成员String name
 * Created by YuKai Fan on 2018/8/21.
 */
public class ReflexDemo5 {
    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 field : fields) {
            System.out.println(field);
        }

    }
}

2.5.1 通过反射,创建对象,获取指定的成员变量,进行赋值与获取值操作

获取成员变量,步骤如下:

  1. 获取Class对象

  2. 获取构造方法

  3. 通过构造方法,创建对象

  4. 获取指定的成员变量(私有成员变量,通过setAccessible(boolean flag)方法暴力访问)

  5. 通过方法,给指定对象的指定成员变量赋值或者获取值

  public void set(Object obj, Object value)

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

  public Object get(Object obj)

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

 

/**
 * 反射获取成员变量,并修改值
 * Person类中的成员String name
 * Created by YuKai Fan on 2018/8/21.
 */
public class ReflexDemo5 {
    public static void main(String[] args) throws Exception {
        Class c = Class.forName("Demo.Person");
        Object obj = c.newInstance();//获取指定的成员变量String name
        //Class类的方法 getField(传递字符串类型的变量名) 获取指定的成员变量
        Field field = c.getField("name");
        //Field类的方法 void set(Object obj, Object value),修改成员变量的值
        //Object obj 必须有对象的支持,Object value修改后的值
        field.set(obj,"王五");
        System.out.println(obj);
        System.out.println(field);
    }
}

 

2.6 通过反射获取成员方法并使用

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

返回获取一个方法:

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

获取public 修饰的方法

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

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

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

返回获取多个方法:

  public Method[] getMethods() 获取本类与父类中所有public 修饰的方法

  public Method[] getDeclaredMethods() 获取本类中所有的方法(包含私有的)

/**
 * 反射获取成员方法并运行
 * Created by YuKai Fan on 2018/8/21.
 */
public class reflexDemo6 {
    public static void main(String[] args) throws Exception {
        Class c = Class.forName("Demo.Person");
        Object obj = c.newInstance();
        //获取class对象中的成员方法
        //Method[] getMethods()获取class文件中所有公共成员方法,包括继承的
        //Method类时藐视成员方法的对象
        Method[] methods = c.getMethods();
        for (Method method : methods) {
            System.out.println(method);
        }

        //获取指定的方法eat运行
        //Method getMethod(String methodName, Class...c)
        //methodName 获取的方法名,c 方法的参数列表
        Method method = c.getMethod("eat");
        //使用Method类中invoke(Object obj, Object...o)的方法运行获得的方法
        method.invoke(obj);
    }
}
/**
 * 反射获取有参数的成员方法运行
 * public void sleep(String,int double)
 * Created by YuKai Fan on 2018/8/21.
 */
public class ReflexDemo7 {
    public static void main(String[] args) throws Exception {
        Class c = Class.forName("Demo.Person");
        Object obj = c.newInstance();
        //调用Class类的方法getMethod获取指定的方法sleep
        Method method = c.getMethod("sleep", String.class, int.class, double.class);
        method.invoke(obj,"休眠",100,888.99);
    }
}

第3章 反射练习

3.1 泛型擦除

思考,将已存在的ArrayList<Integer>集合中添加一个字符串数据,如何实现呢?

我来告诉大家,其实程序编译后产生的.class文件中是没有泛型约束的,这种现象我们称为泛型的擦除。那么,我们可以通过反射技术,来完成向有泛型约束的集合中,添加任意类型的元素

 

/**
 * 定义集合类,泛型String
 * 要求向集合中添加Integer类型
 *
 * 反射方式,获取出集合ArrayList类的class文件对象
 * 通过class文件对象,调用add方法
 *
 * 该操作主要是对反射调用方法是否理解,在实际应用中并无太大意义
 * Created by YuKai Fan on 2018/8/21.
 */
public class ReflexTest {
    public static void main(String[] args) throws Exception {
        ArrayList<String> list = new ArrayList<>();
        list.add("a");
        //编译失败,数据类型和存储的类型不同
        //list.add(2);
        //伪泛型:编译后的class文件是没有泛型的

        //反射方式,获取出集合ArrayList类的class文件对象
        Class c = list.getClass();
        //获取ArrayList.class文件中的方法add
        Method method = c.getMethod("add", Object.class);
        //使用invoke()运行ArrayList方法add
        method.invoke(list,15);
        method.invoke(list,150);
        method.invoke(list,1500);
        for (Object s : list) {
            System.out.println(s);
        }
        System.out.println(list);
    }
}

 

3.2 反射配置文件

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

读取Peoperties.txt文件中的数据,通过反射技术,来完成Person对象的创建

Peoperties.txt文件内容如下:

 

#className=Demo3.Person
#methodName=eat
className=Demo3.Student
methodName=study
#className=Demo3.Worker
#methodName=job

 

读取配置文件,调用指定类中的对应方法

 

/**
 * 调用Person方法,Student方法,Worker方法
 * 问题:类不清楚,方法也不清楚
 * 解决:通过配置文件实现功能
 *      将运行的类名和方法的名字,以键值对的形式,卸载文本中
 *      运行哪个类,就读取配置文件即可
 *  实现步骤:
 *      1.准备配置文件,键值对
 *      2.IO流读取配置文件Reader
 *      3.文件中的键值对存储到集合中Properties
 *          集合保存的键值对就是类名和方法名
 *      4.反射获取指定类的class文件对象
 *      5.class文件对象,获取指定的方法
 *      6.运行方法
 *
 * Created by YuKai Fan on 2018/8/21.
 */
public class Test {
    public static void main(String[] args) throws Exception{
        //读取配置文件
        FileReader fr = new FileReader("reflexCode/config.properties");
        //创建集合对象
        Properties pro = new Properties();
        //调用集合方法load,传递对象
        pro.load(fr);
        fr.close();
        //通过键,获取值
        String className = pro.getProperty("className");
        String mehtodName = pro.getProperty("methodName");
        //反射获取指定类的class文件对象
        Class c = Class.forName(className);
        Object obj = c.newInstance();
        //获取指定的方法名
        Method method = c.getMethod(mehtodName);
        method.invoke(obj);
    }
}

 

第4章 总结

4.1 知识点总结

如何获取.Class文件对象

  1, 通过ObjectgetClass()方法获取 Class对象

  2, 通过类名.class 方式 获取 Class对象

  3, 通过反射的方式, Class.forName(String classname) 获取Class对象

  public static Class<?> forName(String className)throws ClassNotFoundException

返回与带有给定字符串名的类或接口相关联的 Class 对象

 

通过反射, 获取类中的构造方法,并完成对象的创建

获取指定的构造方法

  public Constructor<T> getConstructor(Class<?>... parameterTypes)

获取指定的public修饰的构造方法

  public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)

获取指定的构造方法,包含私有的

获取所有的构造方法

  public Constructor<?>[] getConstructors() 获取所有的public 修饰的构造方法

       public Constructor<?>[] getDeclaredConstructors() 获取所有的构造方法,包含私有的

 

通过反射, 获取类中的构造方法,并完成对象的创建

   步骤:

     1.获取字节码文件对象

     2,通过字节码文件对象 ,获取到指定的构造方法getConstructor(参数);

     3,通过构造方法,创建对象

     public T newInstance(Object... initargs)

 

私有构造方法,创建对象

  1.获取字节码文件对象

  2,通过字节码文件对象 ,获取到指定的构造方法 getDeclaredConstructor (参数);

       3,暴力访问con.setAccessible(true);

       4,通过构造方法,创建对象

       public T newInstance(Object... initargs)

通过反射,获取Class文件中的方法

获取指定的方法

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

获取指定的public方法

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

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

获取所有的方法

  public Method[] getMethods() 获取本类与父类中所有public 修饰的方法

       public Method[] getDeclaredMethods()获取本类中所有的方法,包含私有的

 

通过反射,调用方法

步骤:

  1.获取Class对象

       2,获取构造方法,创建对象

       3,获取指定的public方法

       4,执行方法

       public Object invoke(Object obj, Object... args)

 

私有方法的调用:

  1.获取Class对象

       2,获取构造方法,创建对象

       3,获取指定的private方法

       4,开启暴力访问m5.setAccessible(true);

       5,执行方法

       public Object invoke(Object obj, Object... args)

 

通过反射,获取成员变量(字段)

获取指定的成员变量

  public Field getField(String name) 获取public修饰的成员变量

       public Field getDeclaredField(String name) 获取任意的成员变量,包含私有

获取所有的成员变量

  public Field[] getFields() 获取所有public修饰的成员变量

       public Field[] getDeclaredFields() 获取司所有的成员变量,包含私有

 

通过反射,获取成员 变量,并赋值使用

步骤:

  1.获取字节码文件对象

       2,获取构造方法,创建对象

       3,获取指定的成员变量

       4,对成员变量赋值\获取值操作

  public void set(Object obj,  Object value) 赋值

       public Object get(Object obj) 获取值

 

私有成员变量的使用

步骤:

  1获取字节码文件对象

       2,获取构造方法,创建对象

       3,获取指定的成员变量

       4,开启暴力访问

       5,对成员变量赋值\获取值操作

       public void set(Object obj,  Object value) 赋值

       public Object get(Object obj) 获取值

 

 

posted @ 2018-08-21 17:34  MichaelKai  阅读(743)  评论(0编辑  收藏  举报