13、反射
13、Java反射机制
Java Reflaction概述
Reflection(反射)是被视为动态语言的关键,
反射机制允许程序在执行期间借助于Reflection API 获得并操作 任何类的内部信息。Java反射机制提供的功能:
在运行时判断任意一个对象所属的类
在运行时构造任意一个类的对象
在运行时判断任意一个类所具有的成员变量和方法
在运行时获取泛型信息
在运行时调用任意一个对象的成员变量和方法
在运行时处理注解
生成动态代理反射相关的主要API:
java.lang.Class: 代表一个 类
java.lang.reflect.Method: 代表类的 方法
java.lang.reflect.Field: 代表类的 成员变量
java.lang.reflect.Constructor: 代表类的构造器
动态语言与静态语言:
PS:反射机制 与面向对象中的封装性是不是矛盾的?如何看待这两个技术?
不矛盾,
封装,是将用户不会用到的部分封装起来,只把用户会用到的提供给外界访问。就算通过反射拿到了类的私有属性,方法,但这些是没有意义的,用户用不到这些。
对于程序而言,当我们需要动态获取某个对象时,可以通过反射的方式动态获取到还保留在内存中的目标对象;
Class类*
类加载的过程(Load): (狭义)
程序经过javac.exe命令后,会生成一个或多个字节码文件(.class结尾);
接着使用 java.exe命令 对某个字节码文件进行解释运行。即将该字节码文件加载到内存中(类的加载)
加载到内存中的类,称为 运行时类,此运行时类作为Class的实例(所有(运行时)类 都是 Class类的对象) Class的实例对应着一个运行时类。
加载到内存中的运行时类,会在内存中缓存一定的时间,可以通过反射获取。
获取Class的实例的方式:
调用运行时类的属性: 类.class
通过运行时类的对象: 对象.getClass( )
调用Class的静态方法:forName(String classPath) //类的绝对路径
Class cla1 = Person.class; Class cla2 = p.getClass(); Class cla3 = Class.forName("com.wang.Person"); //三个实例都是同一个对象--加载到内存的类
使用类的加载器:ClassLoader (了解)
//1.获取ClassLoader对象 (有三种ClassLoader对象) ClassLoader classloader = test.class.getClassLoader(); //2.调用loadClass()方法 Class cla4 = classloader.loadClass("com.wang.Person");
哪些类型有class对象?
Class c1 = Object.class; //类
Class c2 = Comparable.class; //接口
Class c3 = String[].class; //一维数组
Class c4 = int[][].class; //二维数组
Class c5 = Override.class; //注解
Class c6 = ElementType.class; //枚举
Class c7 = Integer.class; //基本数据类型
Class c8 = void.class; //void
Class c9 = Class.class; //Class
//只要 元素类型与维度一样,就是同一个Class
int[] a = new int[10];
int[] b = new int[100];
Class类常用方法:
static Class forName(String name) : 返回指定类名 name 的 Class 对象
Object newInstance() :调用缺省构造函数,返回该Class对象的一个实例
getName() :返回此Class对象所表示的实体(类、接口、数组类、基本类型或void)名称
Class getSuperClass() :返回当前Class对象的父类的Class对象
Class [] getInterfaces() :获取当前Class对象的接口
ClassLoader getClassLoader() :返回该类的类加载器
Class getSuperclass() : 返回表示此Class所表示的实体的超类的Class
Constructor[] getConstructors() :返回一个包含某些Constructor对象的数组 --构造器
Field[] getDeclaredFields() :返回Field对象的一个数组 --属性
Method[] getMethods() :返回一个Methods 数组 --方法
Method getMethod(方法名, 参数类型) :获取指定方法
类的加载
类的加载过程:(了解)
![]()
- 加载 : 把类的class文件读取到内存,生成Class对象
- 链接: 保证代码没有错误,为static变量 分配内存(默认初始化)
- 初始化:给静态变量,静态代码块赋值;初始化子类前 先初始化父类
类加载器 (ClassLoader) :
- 引导类 (根)加载器 java核心库 rt.jar 无法直接获取
- 拓展类加载器 jre/lib/ext下的jar
ExtClassloader
- 系统类加载器 项目jar,最常用
AppClassloader
- 自定义加载器
使用ClassLoader读取Properties 配置文件:
方式一:使用fis文件流读取配置文件 (默认位置为当前module)
方式二:使用ClassLoader读取配置文件 (默认位置为当前module的src目录下)
Properties properties = new Properties(); //fis获取配置文件的流 FileInputStream fis = new FileInputStream("反射\\db.properties"); properties.load(fis); //classLoader 获取配置文件的流 .getResouurceAsStream() ClassLoader classLoader = classLoader.class.getClassLoader(); //当前类.class.getCL InputStream is = classLoader.getResourceAsStream("db.properties"); properties.load(is); String name = properties.getProperty("name");//读取配置文件 key-value String pwd = properties.getProperty("pwd");
创建运行时类的对象*
通过反射创建对应的运行时类的实例:
1、通过Class对象 .newInstance() 创建对象实例
要求:1.类必须有一个空参构造器
2.类的构造器访问权限必须足够 (一般为public)
Class<Person> cla1 = Person.class; Person p = claz.newInstance(); //newInstance()内部调用运行时类的空参构造器
2、通过Class对象的 构造器 创建对象实例
有参构造器,用的比较少
Class<Person> cla1 = Person.class; //获取运行时类 Constructor constructor = cla1.getDeclaredConstructor(String.class); Person p = constructor.newInstance("李华");
反射的动态性:通过结合 Class静态方法 .forName( ) 动态创建 运行时类对象实例
可以根据 .forName(String classPath) 形参classPath的不同,动态创建不同的对象
public Object getInstance(String classPath){ Class clazz = Class.forName(classPath); return clazz.newInstance(); }
对于正在运行的web服务器而言,反射的逻辑代码已经写好了;
根据前端传来的不同请求,通过反射动态的创建相应的对象; -- 反射 + 注解 +设计模式
操作运行时类的指定 属性和方法
操作属性:
.set(对象,value)
操作方法:
.invoke(对象,value)
关闭安全检测:
.setAccessible(true)
Method, Field , Constructor 都有 setAccessible 方法
取消java语言访问检查;提高反射效率;得以访问修改 私有成员 ;
//创建运行时类的对象实例 Class<Person> c1 = Class.forName("com.wang.Person"); //获取运行时类 Person p = c1.newInstance(); //创建对象实例 //1.操作属性 Field name = c1.getDeclaredFile("name"); //获取运行时类的指定属性 name.setAccessible(true); name.set(p,"刘波"); //修改 指定对象的指定属性的值 //2.操作方法 Method f1 = c1.getDeclaredMethod("f1",String.class); //方法名,形参列表.class f1.setAccessible(true); Object o = f1.invoke(p,"传参"); //指向 指定对象的指定方法 //调用静态方法 .invoke(类.class) 或 .invoke(null);
获取运行时类的属性结构 (了解)
通过反射,获取运行时类的 Class对象,从而获取 该类的内部信息
Name, Field, Method, Constructor, SuperClass, Interface, Annotation
Class cl = Class.forName("com.wang.User"); //获取类的名字 c1.getName(); //类名+包名 c1.getSimpleName(); //获取类的属性 Field[] fields = c1.getFields(); //当前及父类中所有pubilc属性 Field[] fields = c1.getDeclaredFields(); //获得本类全部属性 Field field = c1.getFiled(String fileName); //获取类的方法 c1.getMethods(); c1.getDeclaredMethods(); //获得本类的所有方法 c1.getMethod(String name,String.class); //方法名,参数类型.class //获取构造器 c1.getConstructors(); c1.getConstructor(String.class,int.class); //参数列表.class c1.getDeclaredConstructosr(); c1.getDeclaredConstructor(null); //获取运行时类的父类 Class superclass = c1.getSuperclass(); Type genericSuperclass = c1.getGenericSuperclass(); //带泛型的父类 //获取 注解 c1.getAnnotations(); //获取接口 c1.getInterfaces();
PS:
javabean中要求提供一个空参构造器,原因:
1.便于通过反射,创建运行时类的对象
2.便于 子类继承此运行时类时,默认调用super( ) 保证父类有此构造器;
创建类的对象的方式?
new + 构造器
静态方法 ,XxxFactory,XxxBuilder,Xxx
通过反射
动态代理
代理模式:
接口对象 + 原始对象 + 代理对象
静态代理:原始对象实现接口,代理对象代替原始对象。在实现接口的基础上做一些其他操作。
一个接口 对应着一个代理类
代理类和被代理类在编译期间就被确定了 -- 静态代理动态代理:一个代理类 代理所有的(被代理类) 接口,实现不同的接口的功能。
AOP面向切面编程思想:前后代码不变(通用),中间代码动态改变。![]()
静态代理
//静态代理模式
interface ClothFactory{
public void produceCloth();
}
//被代理类
class NikeCloths implements ClothFactory{
public void produceCloth(){
System.out.println("生成一件Nick的衣服啦");
}
}
//代理类
class ProxyCloths implements ClothFactory{
//注入 被代理类对象实例
private ClothFactory factory;
public ProxyCloths(ClothFactory factory){
this.factory = factory;
}
public void produceCloth() {
System.out.println("代理工厂 做一些准备工作~\n");
factory.produceCloth(); //调用原始对象 实现接口
System.out.println("\n代理工厂 做一些后续的工作~");
}
}
动态代理
- 问题一:如何根据加载到内存中的被代理类,动态的创建代理类对象
- 问题二:当通过代理类对象调用方法时,如何动态调用 被代理类的 同名方法
ProxyFactory.getProxyInstance(new 被代理类对象);
//动态代理 工厂
/* 问题一:**如何**根据加载到内存中的被代理类,动态的创建代理类对象
问题二:当通过代理类对象调用方法时,如何动态调用被代理类的同名方法*/
class ProxyFactory{
//1. 创建静态方法,返回代理类对象
public static Object getProxyInstance(Object obj){ //obj被代理对象
//a.获取类的 加载器
ClassLoader classLoader = obj.getClass().getClassLoader();
//b.获取类的接口
Class<?>[] interfaces = obj.getClass().getInterfaces();
//c.获取 被代理类中的同名方法-- 实现InvocationHandler接口获取实例
MyInvocationHandler handler = new MyInvocationHandler();
handler.bind(obj); //绑定代理类对象
return Proxy.newProxyInstance(classLoader,interfaces,handler); //三个参数
}
}
//2.实现InvocationHandler接口,注入代理类对象,重写invoke实现调用同名方法
class MyInvocationHandler implements InvocationHandler{
private Object obj;
public void bind(Object obj){ //绑定代理类对象
this.obj = obj;
}
//将 被代理类要指向的方法,声明在invoke()中
//实现 代理方法, AOP切面编程
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("实现被代理接口 之前的 重复 操作");
Object returnValue = method.invoke(obj, args);
System.out.println("实现被代理接口 之后的 重复 操作");
return returnValue;
}
}
PS
框架 = 注解 + 反射 + 设计模式
- 理解好反射(动态代理),是学习Spring框架的重要部分!!
反射进一步学习:学会反射后,我被录取了!(干货) (qq.com)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET10 - 预览版1新功能体验(一)