Java学习笔记——Java语言基础(二十五)(类加载器、反射)

一、类加载器

1.1 类的加载概述
类的加载概述
	当程序要使用某个类时,如果该类还未被加载到内存中,
	则系统会通过加载,连接,初始化三步来实现对这个类进行初始化。
	加载 
		就是指将class文件读入内存,并为之创建一个Class对象。
		任何类被使用时系统都会建立一个Class对象。
	连接
		验证 :	是否有正确的内部结构,并和其他类协调一致
		准备 :	负责为类的静态成员分配内存,并设置默认初始化值
		解析:	把类中的符号引用转换为直接引用
  初始化:就是我们以前讲过的初始化步骤
1.2 类的加载时机
  • 创建类的实例 例如:new Student()
  • 访问类的静态变量,或者为静态变量赋值 例如:Math.PI
  • 调用类的静态方法 例如:Math.abs()
  • 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
  • 初始化某个类的子类
  • 直接使用java.exe命令来运行某个主类
1.3 类加载器的分类以及作用
1.类加载器的概述
	负责将.class文件加载到内在中,并为之生成对应的Class对象。
2.:类加载器的分类
	Bootstrap ClassLoader 根类加载器
	Extension ClassLoader 扩展类加载器
	Sysetm ClassLoader 系统类加载器
3.:类加载器的作用
	Bootstrap ClassLoader 根类加载器
		也被称为引导类加载器,负责Java核心类的加载
		比如System,String等。在JDK中JRE的lib目录下rt.jar文件中
	Extension ClassLoader 扩展类加载器
		负责JRE的扩展目录中jar包的加载。
		在JDK中JRE的lib目录下ext目录
	Sysetm ClassLoader 系统类加载器
		负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径

二、反射

1.反射的概述
2.获取class文件对象的三种方式:

  • Object类的getClass()方法
  • 静态属性class
  • Class类中静态方法forName()
public class Demo01 {
    public static void main(String[] args) throws ClassNotFoundException {
        /*
        反射,剖析这个类的构成,调用类中的属性以及功能
         */
        //获取一个类的字节码文件对象
        //方式1 getClass方法 object类中的方法,对象调用
        Object o = new Object();
        Class aClass = o.getClass();
        System.out.println(aClass);
        //方式2 每个类都会有一个静态的class属性
        Class aClass1 = Object.class;
        Class stringClass = String.class;
        System.out.println(aClass==aClass1);
        System.out.println(stringClass);
        //方式3 使用Class中的方法forName 参数传递全限定名(包名+类名)
        Class aClass2 = Class.forName("fanshe.Method.Demo01");
        System.out.println(aClass2);
    }
}

获取字节码文件对象,然后剖析该类中存在哪些构造方法,成员变量以及成员方法。

2.1 通过反射获取无参构造方法

以Student类为例

public class Student {
    //提供构造方法
    public Student() {
        System.out.println("空参的构造方法调用了");
    }

    public Student(String name) {
        System.out.println("一个参数的构造方法调用了,参数的值");
    }

    public Student(String name,int age,char sex) {
        System.out.println("三个参数的构造方法调用了");
    }

    private Student(String name,int age) {
        System.out.println("两个参数的私有构造方法");
    }
}

获取所有的构造方法:

获取所有的构造方法
public Constructor<?>[] getConstructors() 获取所有的构造方法不包含私有的
public Constructor<?>[] getDeclaredConstructors() 获取所有的构造方法 包括私有的
获取单个的构造方法
public Constructor<T> getConstructor(Class<?>... parameterTypes) 获取单个的构造方法 不包含私有的
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) 获取单个的构造方法包含私有的
public class Test01 {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, ClassNotFoundException {
        Class aClass = Class.forName("num23.Metnod.Student");
        //  1)获得类的一个构造方法:Construactor c = 类的字节码对象.getDeclaredConstructor(参数1.class,参数2.class);
        //构造私有
        Constructor declaredConstructor = aClass.getDeclaredConstructor(String.class, int.class);
        //使用构造方法对象中的方法来创建该类对象。
        //取消权限的语法检测
        declaredConstructor.setAccessible(true);
        Student s1 = (Student) declaredConstructor.newInstance("张三", 20);
        System.out.println(s1);
        //构造公有
        Constructor declaredConstructor1 = aClass.getDeclaredConstructor(String.class, int.class, char.class);
        //不是私有 不需要取消权限的语法检测
        Student s2 = (Student) declaredConstructor1.newInstance("李四", 23, '男');
        System.out.println(s2);
    }
}

注意:当我们需要取空参构造时,不用去获取空参构造对象。

  Class studentClass2 = Class.forName("num23.Metnod.Student");
  Student student2 = (Student) studentClass2.newInstance();
  System.out.println(student2);
2.2 通过反射获取成员变量
获取所有成员变量
public Field[] getFields() 获取所有的成员变量包含从父类继承过来的
public Field[] getDeclaredFields() 获取所有的成员变量 包含私有的 也包含从父类继承过来的成员变量
获取单个成员变量
public Field getField(String name)
public Field getDeclaredField(String name)

提供一个类。创建几个成员变量

public class Teacher {
    //提供成员变量
    public String name;
    int age;
    private char sex;
}

测试类:

public class MyTest {
    public static void main(String[] args) throws Exception{
        Class teacherClass = Class.forName("org.westos.demo4.Teacher");
        //getFields() 获取所有的公共字段
        Field[] fields = teacherClass.getFields();
        for (Field field : fields) {
            System.out.println(field.toString());
        }
        //获取所有字段对象,包括私有
        Field[] declaredFields = teacherClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            System.out.println(declaredField);
        }
        //获取公共的单个字段对象,传入字段名称
        Field name = teacherClass.getField("name");
        System.out.println(name);
        //获取非公共的单个字段对象。
        Field field2 = teacherClass.getDeclaredField("age");
        System.out.println(field2);
        Field field3 = teacherClass.getDeclaredField("sex");
        System.out.println(field3);
    }
}

对成员变量进行赋值:

public class MyTest2 {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, InstantiationException, NoSuchMethodException, ClassNotFoundException {
        //获取出该类的字节码文件对象。
        Class teacherClass =Class.forName("num23.Method2.Teacher") ;
        //获取name字段对象
        Field field = teacherClass.getField("name");
        Object obj = teacherClass.newInstance();
        //调用 Field 类中的方法,给字段赋值
        //参数1,要一个该类对象
        //参数2,字段具体要赋的值
        field.set(obj,"张三");
        //获取字段的值
        Object o = field.get(obj);
        System.out.println(o);
        //采用反射的方式来给私有字段赋值
        //获取私有字段对象
        Field sexFiled = teacherClass.getDeclaredField("sex");
        //调用 Field 类中的set()方法来给私有字段赋值
        //对于私有字段的赋值,我们取消一下权限检测
        sexFiled.setAccessible(true);
        sexFiled.set(obj,'男');
        Object o1 = sexFiled.get(obj);
        System.out.println(o1);
    }
}
2.3 通过反射获取成员方法
获取所有成员方法
	public Method[] getMethods() //获取所有的公共的成员方法不包含私有的 包含从父类继承过来的过来的公共方法
	public Method[] getDeclaredMethods()//获取自己的所有成员方法 包含私有的
获取单个成员方法
	//参数1: 方法名称  参数2:方法行参的class 对象
	public Method getMethod(String name,Class<?>... parameterTypes) //获取单个的方法 不包含私有的
	public Method getDeclaredMethod(String name,Class<?>... parameterTypes) //获取单个方法包括私有的

提供一个Student类,提供一些成员方法

public class Student {
    //提供方法
    public void show1(){
        System.out.println("空参的show1方法");
    }
    public void show2(String name,int age){
        System.out.println("两个参数的方法");
    }

    public int show3(String name, int age,char sex) {
        System.out.println("三个参数的方法");
        return 100;
    }

    private int test(String name, int age, char sex) {
        System.out.println("私有的方法");
        return 100;
    }
}

测试类:

public class MyTest {
    public static void main(String[] args) throws Exception {
        //获取该类的字节码文件对象
        Class studentClass= Class.forName("num23.Method3.Student");
        //获取所有公共的方法对象数组,包括他继承的父类的公共的方法对象
        Method[] methods = studentClass.getMethods();
        for (Method method : methods) {
            System.out.println(method);
        }
        //获取所有的方法对象,包括私有方法,不包括父类的方法对象
        Method[] declaredMethods = studentClass.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            System.out.println(declaredMethod);
        }
        //获取单个的公共方法对象
        //参数1,方法名 参数2:可变参数,他传入形参类型的字节码类型
        Method show = studentClass.getMethod("show1");
        System.out.println(show);
        Method show2 = studentClass.getMethod("show2", String.class, int.class);
        System.out.println(show2);
        Method show3 = studentClass.getMethod("show3", String.class, int.class, double.class);
        System.out.println(show3);
        //获取单个私有方法对象
        Method test = studentClass.getDeclaredMethod("test", String.class, int.class, double.class);
        System.out.println(test);
    }
}

使用反射调用方法

public class MyTest2 {
    public static void main(String[] args) throws Exception {
        //采用反射的方式来调用方法执行。
        //获取该类的字节码文件对象
        Class studentClass = Class.forName("num23.Method3.Student");
        //获取方法对象
        Method showMethod = studentClass.getMethod("show1");
        //获取该类对象
        Object obj = studentClass.newInstance();
        //调用Method类中的 invoke() 方法,让show 调用执行
        //参数1,类的对象,参数2,给方法的形参传递的实际的值
        showMethod.invoke(obj);
        //调用两个参数的方法执行
        Method show2 = studentClass.getMethod("show2", String.class, int.class);
        //参数1,类的对象,参数2,给方法的形参传递的实际的值
        show2.invoke(obj, "张三", 23);
        Method show3 = studentClass.getMethod("show3", String.class, int.class, char.class);
        //invoke()方法调用完成后,返回的值就是调用该方法返回的值
        //当方法有返回值时
        Integer invoke = (Integer) show3.invoke(obj, "李四", 25, '男');
        System.out.println(invoke);
        //通过反射来调用私有方法执行。
        Method test = studentClass.getDeclaredMethod("test", String.class, int.class, char.class);
        //调用私有方法,取消私有的权限校验
        test.setAccessible(true);
        //获取返回值
        Integer invoke1 = (Integer) test.invoke(obj, "赵六", 25,  '男');
        System.out.println(invoke1);
    }
}

三、反射练习

(1)

当ArrayList集合指定泛型为Integer,那么在编译器我们就没有办法往里面添加一个字符串,因为泛型在编译存在语法检查,那么编译期完成不了,我们只能在运行期完成,因为泛型到了运行期以后,泛型会自动擦除,那么要在运行期完成这个需求,那么我们就需要使用反射.

public class MyTest2 {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        //ArrayList<Integer>的一个对象,在这个集合中添加一个字符串数据
        ArrayList<Integer> list = new ArrayList<>();
        list.add(200);
        list.add(200);
        list.add(200);
        //获取字节码文件
        Class listClass = list.getClass();
        //参数1:方法名 参数二:方法形参的class
        Method add = listClass.getDeclaredMethod("add",Object.class);
        //参数1,类的对象,参数2,给方法的形参传递的实际的值
        add.invoke(list,"sd");
        System.out.println(list);
    }
}
(2)通过反射写一个通用的设置某个对象的某个属性为指定的值

设置一个Student类:

public class Student {
    private String name;
    private int age;

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }
}

设置一个工具类:

public class MyUtils {
    public static void setProperte(Object obj,String properteName,Object value) throws NoSuchFieldException, IllegalAccessException {
        //字节码文件 
        Class aClass = obj.getClass();
        //根据参数获取成员变量(字段)
        Field declaredFields = aClass.getDeclaredField(properteName);
        //取消语法检测
        declaredFields.setAccessible(true);
        //给指定对象的指定成员变量设置值
        declaredFields.set(obj,value);
    }
}

测试类:

public class MyTest {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
        Student student = new Student();
        //参数1 对象
        //参数2 成员变量
        //参数3	成员变量赋值
        MyUtils.setProperte(student,"name","张三");
        MyUtils.setProperte(student,"age",52);
        System.out.println(student.getName());
        System.out.println(student.getAge());
    }
}
(3)通过反射运行配置文件内容

设置配置文件:peizhi.properties

className=fanshe.yingyong.Dog
methodName=eat
public class MyTest {
    public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Properties properties = new Properties();
        //加载配置文件
        properties.load(new FileReader("src/fanshe/peizhi.properties"));
        //获取字节码文件
        Class aClass = Class.forName(properties.getProperty("className"));
        //通过反射获取对象 aClass.getDeclaredConstructor()获取构造方法对象
        //newInstance() 获取对象
        Object obj = aClass.getDeclaredConstructor().newInstance();
        //通过反射获取方法 参数方法名
        Method methodName = aClass.getDeclaredMethod(properties.getProperty("methodName"));
        methodName.invoke(obj);
    }
}
class Dog{
    public void eat(){
        System.out.println("狗吃肉");
    }
}
class cat{
    public void eat(int age){
        System.out.println("猫吃鱼"+age);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值