一、类加载器
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);
}
}