13、反射

13、Java反射机制

image-20220408161342164

Java Reflaction概述

Reflection(反射)是被视为动态语言的关键,
反射机制允许程序在执行期间借助于Reflection API 获得并操作 任何类的内部信息。

Java反射机制提供的功能
在运行时判断任意一个对象所属的类
在运行时构造任意一个类的对象
在运行时判断任意一个类所具有的成员变量和方法
在运行时获取泛型信息
在运行时调用任意一个对象的成员变量和方法
在运行时处理注解
生成动态代理

反射相关的主要API
 java.lang.Class: 代表一个 类
 java.lang.reflect.Method: 代表类的 方法
 java.lang.reflect.Field: 代表类的 成员变量
 java.lang.reflect.Constructor: 代表类的构造器

动态语言与静态语言:

image-20220408163658947

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(方法名, 参数类型) :获取指定方法

类的加载

类的加载过程:(了解)

image-20220408184947966
  • 加载 : 把类的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面向切面编程思想:前后代码不变(通用),中间代码动态改变。

    image-20220409164537497

静态代理

//静态代理模式
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)

动态代理进一步理解:https://mp.weixin.qq.com/s/CTWVfSm_6wzfeH7DJm2qRA

posted @   simp1e1  阅读(37)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示