类加载器与反射

一、类加载器

1、类的加载:

 

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

1.1 加载:

就是指将class文件读入内存,并为之自动创建一个Class对象(字节码对象)。

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

1.2 连接

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

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

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

1.3 初始化

  就是初始化步骤

2、类初始化时机(也就是什么时候类会初始化):

1. 创建类的实例

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

3. 类的静态方法

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

5. 初始化某个类的子类

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

3、类加载器:

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

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

4、类加载器的组成

Bootstrap ClassLoader 根类加载器

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

比如System,String等。在JDK中JRE的lib目录下rt.jar文件中

Extension ClassLoader 扩展类加载器

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

在JDK中JRE的lib目录下ext目录

System ClassLoader 系统类加载器

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

二、反射

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

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

1、class类  字节码文件类

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

 获得字节码文件的三个方法:

方式一: 通过Object类中的getObject()方法

方式二:通过   类名.class获得

方式三:通过Class类中的方法(将类名作为字符串传递给Class类中的静态方法forName即可)

复制代码
package com.oracle.Demo02;

public class Demo01 {
//反射:目的就是不通过new 对象();得到所有属性和方法
    public static void main(String[] args) throws ClassNotFoundException {
        // 获取字节码对象的三个方式
        //1.通过对象获取
        Person p=new Person();
        Class c=p.getClass();
        System.out.println(c);
        //2.通过类名获取(不管是引用类型还是基本数据类型都具备)
        Class c1=Person.class;
        System.out.println(c1);
        //都为true  因为指向的字节码文件都是同一个
        System.out.println(c==c1);   //true
        System.out.println(c.equals(c1)); //true
        //3.通过Class类中的静态方法forName(完整的包名.类名);获取
        Class c2=Class.forName("com.oracle.Demo02.Person");
        System.out.println(c2);
    }

}
复制代码

 

2、通过反射获得构造方法并使用

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

2.1 返回一个构造方法:

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

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

2.2 返回多个构造方法:

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

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

示例代码:

自定义Person类:

 

复制代码
package com.oracle.Demo02;

public class Person {
    public String name;
    private int age;
    static{
        System.out.println("静态代码块");
    }
    public Person(){
        System.out.println("空参构造");
    }
    public Person(String name,int age){
        this.name=name;
        this.age=age;
        System.out.println("有参构造");
    }
    private Person(int age,String name){
        this.name=name;
        this.age=age;
        System.out.println("私有构造");
    }
    public void eat(){
        System.out.println("吃饭");
    }
    public void work(String name){
        System.out.println(name+"工作");
    }
    private void run(String name){
        System.out.println(name+"跑步");
    }
    @Override
    public String toString() {
        return "Person [name=" + name + ", age=" + age + "]";
    }
    
    
}
复制代码

 

 

 

获得构造方法并运行:

复制代码
package com.oracle.Demo02;

import java.lang.reflect.Constructor;

public class Demo02 {

    public static void main(String[] args) throws Exception {
        //反射获取空参构造方法并运行:.getConstructor();
        Class c=Class.forName("com.oracle.Demo02.Person");
        //获取该类中所有的公共构造方法数组
//        Constructor[] con=c.getConstructors();   本方法还需要循环数组,比较麻烦
//        for(Constructor c1:con){
//            System.out.println(c1);
//        }
        Constructor con=c.getConstructor();
        //调用空参构造方法   .newInstance();
//        Object obj=con.newInstance();
//        System.out.println(obj);
        //调用work()方法
//        Person p=(Person)con.newInstance();
//        p.work("张三");
        
    }

}
复制代码

 

通过反射获取有参构造方法并运行:

复制代码
package com.oracle.Demo02;

import java.lang.reflect.Constructor;

public class Demo03 {

    public static void main(String[] args) throws Exception {
        // 通过反射获取有参构造方法并运行
        Class c=Class.forName("com.oracle.Demo02.Person");
        Constructor con=c.getConstructor(String.class,int.class);
        Object obj=con.newInstance("张胜男",16);
        System.out.println(obj);
    }

}
复制代码

快速获取空参并创建对象:

复制代码
package com.oracle.Demo02;

import java.lang.reflect.Constructor;

public class Demo04 {

    public static void main(String[] args) throws Exception {
        // 快速获取空参并创建对象的方式
        //1.类必须有空参构造,2.构造方法必须是public
//        Class c=Class.forName("com.oracle.Demo02.Person");
//        Object obj=c.newInstance();
//        System.out.println(obj);
        //获取私有的构造方法(不推荐)
        //暴力反射,破坏了程序的封装性和安全性
        Class c=Class.forName("com.oracle.Demo02.Person");
        //获取所有的构造方法.getDeclaredConstructors();
//        Constructor[] con=c.getDeclaredConstructors();
//        for(Constructor cc:con){
//            System.out.println(cc);
//        }
        //调用私有化构造方法
        Constructor con=c.getDeclaredConstructor(int.class,String.class);
        con.setAccessible(true);  //暴力反射的开关,为true的时候,可以得到私有的成员   
        Object obj=con.newInstance(18,"张三");
        System.out.println(obj);
                
    }

}
复制代码

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

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

3.1 返回一个成员变量

  public Field getField(String name) 获取指定的 public修饰的变量(不包含私有)

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

3.2 返回多个成员变量

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

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

 

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

 

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 对象表示的成员变量的值

 

复制代码
package com.oracle.Demo02;

import java.lang.reflect.Field;

public class Demo05 {

    public static void main(String[] args) throws Exception {
        // 通过反射获取成员变量并改值
        Class c=Class.forName("com.oracle.Demo02.Person");
        Object obj=c.newInstance();  //反射获得的类对象
        //获取所有的公共的成员变量
        Field[] fields=c.getFields();
        //遍历
//        for(Field f:fields){
//            System.out.println(f);
//        }
        //通过名字获取公共的成员变量
        Field field=c.getField("name");
        //给成员变量赋值
        field.set(obj, "张三");
        System.out.println(obj);
    }

}
复制代码

 

 4、通过反射获得成员方法并使用:

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

4.1 返回获取一个方法:

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

  获取public 修饰的方法

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

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

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

4.2 返回获取多个方法:

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

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

复制代码
package com.oracle.Demo02;

import java.lang.reflect.Method;

public class Demo06 {

    public static void main(String[] args) throws Exception {
        Class c=Class.forName("com.oracle.Demo02.Person");
        Object obj=c.newInstance();
        //获取所有公共的方法(包括从父类继承来的)
//        Method[] method=c.getMethods();
//        for(Method m:method){
//            System.out.println(m);
//        }
        //获取空参成员方法并运行,如果是空参方法,只需要写方法名就可以
        Method method01=c.getMethod("eat");
        //调用方法
        method01.invoke(obj);
        //如果是空参方法,只需要写方法名就可以
        System.out.println(method01);
    }

}
复制代码

获取有参方法并运行:

复制代码
package com.oracle.Demo02;

import java.lang.reflect.Method;

public class Demo07 {
    public static void main(String[] args) throws Exception {
        //获取有参方法并运行
        Class c=Class.forName("com.oracle.Demo02.Person");
        Method method=c.getMethod("work", String.class);
        //添加   .invoke
        method.invoke(c.newInstance(), "韩凯");
    }
}
复制代码

 

5、通过反射,创建对象,调用指定的方法   重点

获取成员方法,步骤如下:

1. 获取Class对象

2. 获取构造方法

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

4. 获取指定的方法

5. 执行找到的方法

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

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

复制代码
package com.oracle.Demo02;

import java.lang.reflect.Method;

public class Demo07 {
    public static void main(String[] args) throws Exception {
        //获取有参方法并运行
        Class c=Class.forName("com.oracle.Demo02.Person");
        Method method=c.getMethod("work", String.class);
        //添加   .invoke
        method.invoke(c.newInstance(), "韩凯");
    }
}
复制代码

 6、通过反射,创建对象,调用指定的private 方法

获取私有成员方法,步骤如下:

1. 获取Class对象

2. 获取构造方法

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

4. 获取指定的方法

5. 开启暴力访问

6. 执行找到的方法

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

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

7、擦除泛型,添加元素

 

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

 

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

 

复制代码
package com.oracle.Demo02;

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

public class Demo08 {
    //有一个ArrayList<String>  list
    //然后往里面添加int类型数据
    public static void main(String[] args) throws Exception {
        ArrayList<String> arr=new ArrayList<String>();
        arr.add("aaa");
        //获取集合的字节码对象
        Class c=arr.getClass();
        //用反射获取 add的方法,Object.calss无关泛型
        Method addd=c.getMethod("add", Object.class);
        //添加int类型的元素
        addd.invoke(arr, 1);
        for(Object obj:arr){
            System.out.println(obj);
        }

    }

}
复制代码

 

8、反射配置文件:

复制代码
package com.oracle.Demo03;

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

public class Demo {
//反射配置文件
    public static void main(String[] args) throws IOException, Exception {
        //类不清楚,方法也不清楚
        //通过配置文件实现,运行的类名和方法名以键值对的形式
        //保存到properrties中,具体运行哪个类里面的方法通过该配置文件去设置
        //步骤:
        //1.准备配置文件,写好键值对
        //2.IO读取配置文件Reader
        //3.文件中的键值对存储到集合中,集合中保存的键值对就是类和方法名
        //4.反射获取指定类的class文件对象
        //5.class文件对象获取指定方法
        //6.运行方法
//        Person p=new Person();
//        p.eat();
        FileReader fr=new FileReader("config.properties");//读取配置好的配置文件
        Properties pro=new Properties();   //创建个集合,用来存键值对的
        pro.load(fr); //将文件中的键值对存到集合中
        fr.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);    //执行方法
    }

}
复制代码

自定义Person类:

复制代码
package com.oracle.Demo03;

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

 

自定义Student类:

复制代码
package com.oracle.Demo03;

public class Student {
    public void study(){
        System.out.println("学生在学习");
    }
}
复制代码

 

自定义Worker类:

复制代码
package com.oracle.Demo03;

public class Worker {
    public void work(){
        System.out.println("工人在工作");
    }
}
复制代码

 

练习题:

自定义类:

Person类

复制代码
package com.oracle.Demo04;

public class Person {
    private String name;
    private int age;
    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 Person(){
        
    }
    public Person(String name,int age){
        this.name=name;
        this.age=age;
    }
}
复制代码

 

Two类

复制代码
package com.oracle.Demo04;

public class Two {
    private String name;
    public Two(){
        System.out.println(name);
    }
    public Two(String name) {
        super();
        this.name = name;
        System.out.println(name);
    }
    public void Work(){
        System.out.println("键盘敲烂,月薪过万");
    }
    @Override
    public String toString() {
        return "Two [name=" + name + "]";
    }
    
}
复制代码

DamoClass自定义类:

复制代码
package com.oracle.Demo04;

public class DemoClass {
    public void run(){
        System.out.println("welcome to oracle");
    }
}
复制代码

 

1、ArrayList<Integer> list = new ArrayList<Integer>(); //这个泛型为Integer的ArrayList中存放一个String类型的对象

复制代码
//1.ArrayList<Integer> list = new ArrayList<Integer>(); 
//这个泛型为Integer的ArrayList中存放一个String类型的对象
public class Demo01 {

    public static void main(String[] args) throws Exception{
        ArrayList<Integer> list=new ArrayList<Integer>();
        list.add(666);
        //获取集合的字节码对象
        Class c=list.getClass();
        //获取集合类中的添加元素方法
        Method addd=c.getDeclaredMethod("add", Object.class);
        //调用方法,并添加元素
        addd.invoke(list, "abc");
        for(Object a:list){
            System.out.println(a);
        }
    }

}
复制代码

 

2、用反射去创建一个对象,有2种方式,尽量用代码去体现

复制代码
package com.oracle.Demo04;

import java.lang.reflect.Constructor;

public class Demo02 {
//2.用反射去创建一个对象,有2种方式,尽量用代码去体现
    public static void main(String[] args) throws Exception {
        //创建一个关于Two自定义类的字节码对象  要有完整的包名
        Class c=Class.forName("com.oracle.Demo04.Two");
        //调用.newInstance();空参构造方法 
        Object obj=c.newInstance();
        System.out.println(obj);
        //调用有参构造方法     Constructor构造器类
        Constructor con=c.getConstructor(String.class);
        Object obj01=con.newInstance("王五");
        System.out.println(obj01);
    }

}
复制代码

 

3、利用反射创建一个类对象,获取指定的方法并执行

复制代码
package com.oracle.Demo04;

import java.lang.reflect.Method;

public class Demo03 {

    public static void main(String[] args) throws Exception {
        // 创建一个字节码对象
        Class c=Class.forName("com.oracle.Demo04.Two");
        //利用反射创建一个类对象
        Object obj=c.newInstance();
        //通过反射获得方法名为“work”的方法
        Method m=c.getMethod("Work");
        //调用obj类中的m方法
        m.invoke(obj);
    }

}
复制代码

 

4、定义一个标准的JavaBean,名叫Person,包含属性name、age。 使用反射的方式创建一个实例、调用构造函数初始化name、age,使用反射方式调用setName方法对名称进行设置, 不使用setAge方法直接使用反射方式对age赋值。

复制代码
package com.oracle.Demo04;

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

public class Demo4 {
//    定义一个标准的JavaBean,名叫Person,包含属性name、age。
//    使用反射的方式创建一个实例、调用构造函数初始化name、age,使用反射方式调用setName方法对名称进行设置,
//    不使用setAge方法直接使用反射方式对age赋值。
    public static void main(String[] args) throws Exception {
        //创建字节码对象
        Class c=Class.forName("com.oracle.Demo04.Person");
        //获取构造方法
        Constructor con=c.getConstructor(String.class,int.class);
        //使用构造方法   创建类对象
        Object obj=con.newInstance("张三",16);
        Method m=c.getMethod("setName", String.class);
        m.invoke(obj, "李四");
        //通过反射与成员变量名称,获得成员变量对象
        Field ageField=c.getDeclaredField("age");
        //因为是私有化的成员变量,所以需要暴力访问
        ageField.setAccessible(true);
        //给年龄赋值
        ageField.set(obj, 60);
        System.out.println(obj);
    }

}
复制代码

 

5、从文件中通过反射读取键值对

复制代码
package com.oracle.Demo04;

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

public class Demo05 {

    public static void main(String[] args) throws Exception {
        //从文件中读取键值对
        FileReader fr=new FileReader("democlass.properties");
        //创建一个集合存放键值对
        Properties pro=new Properties();
        pro.load(fr);
        fr.close();
        //通过键获取值的字符串
        String name=pro.getProperty("name");
        String method=pro.getProperty("method");
        //创建字节码对象
        Class c=Class.forName(name);
        //创建类对象
        Object obj=c.newInstance();
        //获得方法
        Method m=c.getMethod(method);
        //调用方法
        m.invoke(obj);

    }

}
复制代码
posted @ 2019-04-13 16:02  G大调  阅读(127)  评论(0编辑  收藏  举报