反射
一、类加载
1.类加载的过程 //了解
(1)加载类进内存
对每一个class文件创建一个class文件对象,用来描述类的信息
(2)连接类
校验加载进来的东西是否符合语法的要求
(3)类的初始化
先初始化父类,再初始化子类
2.类的初始化时机
(1)创建类的实例 //Student s = new Student();
(2)调用类的类方法 //Student.show();
(3)访问类或者接口的类变量,或者为该类变量赋值
(4)使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
(5)初始化某个类的子类
(6)直接使用java.exe命令来运行某个主类(idea中点击运行按钮)
//什么时候需要用就什么时候加载
二、类加载器
1.类加载器的功能是什么?
负责将.class文件加载到内存中,并为之生成对应的 java.lang.Class 对象(字节码文件对象)
2.特点
针对于不同的类和文件,java会提供不同的类加载器
//比如Student和ArrayList<Student> 它们的类加载器是不同的
三、反射
1.反射的概述
反射:就是利用.class文件对象(而不是java文件对象)来获取类中的构造方法、成员变量、成员方法,并且无视权限修饰符。
2.获取class文件对象的方式
(1)类名.class
举例:Class c1 = Student.class; //最方便
(2)对象名.getClass()方法
举例:Student s1 = new Student();
Class c2 = s1.getClass();
(3)Class.forName(全类名) //开发中最常用
//全类名: 包名+类名
*写法:demo2.Student
*在idea中怎么写全类名?
选中你想要操作的文件 ------------ 右键 ----------- Copy Reference
举例:Class c3 = Class.forName("demo2.Student");
3.反射获取构造方法并使用
(1)Constructor<?>[] getConstructors()返回所有"公共"构造方法对象的数组 //获取所有的public修饰的构造方法对象
(2)Constructor<?>[] getDeclaredConstructors() 返回"所有"构造方法对象的数组 //获取的是所有的构造方法
(3)Constructor getConstructor(Class<?>... parameterTypes) 返回单个"公共"构造方法对象 //获取的是单个的public修饰的构造方法
(4)Constructor getDeclaredConstructor(Class<?>... parameterTypes)返回单个构造方法对象 //获取单个构造方法
创建对象的方法
T newInstance(Object...initargs) 根据指定的构造方法创建对象
//newInstance方法中参数要和构造方法的参数对应起来
//使用反射的第一句代码永远都是获取字节码文件对象
代码实现:
public class Demo2 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//获取字节码文件对象
Class<?> c1 = Class.forName("demo2.Student");
//获取构造方法的对象
Constructor<?> constructor = c1.getConstructor(int.class);
Object o = constructor.newInstance(13);
System.out.println(o);
}
}
4.暴力反射
(1)使用场景
当我们要使用private修饰的构造方法、成员变量、成员方法对象去使用的时候
(2)注意两点
*使用带declared的获取方法
*临时修改权限
constructor.setAccessible(true); //表示暂时允许我访问
代码举例:
public class Demo2 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//获取字节码文件对象
Class<?> c1 = Class.forName("demo2.Student");
//获取构造方法的对象
Constructor<?> constructor = c1.getDeclaredConstructor(int.class);
constructor.setAccessible(true);
Object o = constructor.newInstance(13);
System.out.println(o);
}
}
四、反射获取成员变量并使用
1.回想一下,以前我们怎么给一个对象的成员变量赋值?
Student s1 = new Student();
s1.age = 13;
//也就是说,想要给一个对象的成员变量赋值,需要先有一个对象,第二个是要赋的值
2.相关方法
(1)Field[] getFields() 返回所有"公共"成员变量对象的数组 //获取的是所有的public修饰的成员变量对象
(2)Field[] getDeclaredFields() 返回"所有"成员变量对象的数组 //获取的是所有的修饰符修饰的成员变量对象
(3)Field getField(String name) 返回单个"公共"成员变量对象 //获取的是单个的public修饰的成员变量对象
(4)Field getDeclaredField(String name) 返回单个成员变量对象 //获取的是单个的任意修饰符修饰的成员变量对象
//参数 name指的是 成员变量的名称
反射中给成员变量对象赋值的方法
void set(Object obj,Object value) 给obj对象的成员变量赋值为value
参数:第一个参数obj(相当于上面的s1) 指的是你要给哪个"对象"赋值 第二个参数 value(相当于上面的13) 指的是要给成员变量赋的具体的值
代码实现:
public class Demo3 {
public static void main(String[] args) throws Exception{
Class<?> c1 = Class.forName("demo2.Student");
/*Constructor<?> constructor = c1.getConstructor();
Object o1 = constructor.newInstance();*/
Object o = c1.getConstructor().newInstance();
//获取成员变量的对象
Field namel = c1.getField("namel");
namel.set(o,"王小二");
System.out.println(o);
}
}
五、反射获取成员方法并使用 1.回忆以前我们怎么调用成员方法 Student s1 = new Student(); s1.show(); //前提:调用成员方法需要现有一个对象
2.相关方法
//面试题: 反射中获取getMethods和getDeclaredMethods两个方法的区别?
(1)Method[] getMethods() 返回所有"公共"成员方法对象的数组,包括继承的 // 获取所有public修饰的方法对象 + 包括继承过来的
(2)Method[] getDeclaredMethods() 返回"所有"成员方法对象的数组,不包括继承的 //获取所有任意修饰符修饰的方法对象 (不包括继承的)
(3)Method getMethod(String name, Class<?>... parameterTypes)返回单个公共成员方法对象
(4)Method getDeclaredMethod(String name, Class<?>... parameterTypes)返回单个成员方法对象
//参数: String类型的mame 指的是方法的名称 parameterTypes:指的是方法的参数类型
反射中调用成员方法的方法
Object invoke(Object obj,Object... args) 调用obj对象的成员方法,参数是args,返回值是Object类型
//参数: obj指的是调用方法的对象(相当于上面的s1) args:是你调用方法时需要穿进去的实际参数
//返回值 :指的是方法的返回值
//代码实现:
public class Demo4 {
public static void main(String[] args) throws Exception {
//获取字节码文件对象
Class<?> c1 = Class.forName("demo2.Student");
//创建Student对象
Object o = c1.getConstructor().newInstance();
//获取show方法的对象(暴力反射)
Method show = c1.getDeclaredMethod("show", String.class);
show.setAccessible(true);
Object o1 = show.invoke(null, "比奥利奥二");
Object o1 = show.invoke(o, "比奥利奥二");
System.out.println(o1);
}
}
方法的分类:
*非静态方法
无参无返回值
无参有返回值
有参无返回值
有参有返回值
*静态方法
无参无返回值
无参有返回值
有参无返回值
有参有返回值
六、相关案例
1.越过泛型检查
泛型检查是在"编译期间"用来规范开发人员的书写,等编译结束生成字节码文件之后,泛型就会被擦除掉。
2.反射结合配置文件使用
//使用配置文件就是为了解耦合
模块化 //了解 一、模块化的概述 1.什么是模块 在Idea中我们在开发项目之前,需要新建一个项目(project),在项目下又会创建多个module,这个module就是模块
2.模块的特点
不同的模块之间是不能相互访问的
3.学习模块化的前提
JDK必须大于等于9
二、模块的基本使用 //Maven,也可以实现模块间的相互访问
比如:在A模块中使用B模块的东西,该怎么配置
//A是调用者 B是被调用者
第一步:创建A和B两个模块(创建时一定要注意jdk的版本要大于等于9)
第二步:在A和B的src文件夹下各自创建一个module-info的文件
第三步:在B的module-info文件中配置
exports + 包名
在A的module-info文件中配置
requires + 模块名
三、面向接口编程的思想
调用者只能知道调用的接口名和抽象方法的名称,而看不到具体的实现(为了保证代码的安全性)
|