反射机制(复习)
创始人
2024-01-29 23:02:48
0

反射机制

  • 反射机制
      • 定义
      • 反射机制的功能
      • 反射机制主要的API
      • 反射机制演示
      • 对 Class 的理解
      • Class实例获取的四种方式
      • Class 对应内存结构说明
      • 加载Properties文件的俩种方式
      • 调用运行时类的结构
        • 调用运行时类的指定属性
        • 调用运行时类指定的方法
        • 调用运行时类指定的构造器
      • 反射的应用:动态代理

定义

动态调用类、对象的信息,方法和属性

反射机制的功能

  • 在运行时判断任意一个对象所属的类
  • 在运行时构造任意一个类的对象
  • 在运行时判断任意一个类所具有的成员变量和方法
  • 在运行时获取泛型信息
  • 在运行时调用任意一个对象的成员变量和方法
  • 在运行时处理注解
  • 生成动态代理

反射机制主要的API

  • java.lang.Class:代表一个类
  • java.lang.reflect.Method:代表类的方法
  • java.lang.reflect.Field:代表类的成员变量
  • java.lang.reflect.Constructor:代表类的构造器

反射机制演示

public class ReflectionTest {@Testpublic void test1() {// p普通方式创建对象,以及调用属性、方法Person p1 = new Person("Tom",21);p1.setName("张三");p1.methods1("aa");// 无法调用私有方法// p1.methods2();System.out.println(p1);}// 通过反射机制创建对象、调用属性、方法@Testpublic void test2() throws Exception{Class aClass = Person.class;// 通过反射调用构造器方法Constructor con1 = aClass.getConstructor(String.class, int.class);Person p1 = con1.newInstance("Mack", 21);System.out.println(p1);// 通过反射机制调用方法Method methods1 = aClass.getDeclaredMethod("methods1", String.class);methods1.invoke(p1,"哈哈哈");// 通过反射机制调用私有方法Method methods2 = aClass.getDeclaredMethod("methods2");methods2.setAccessible(true);methods2.invoke(p1);// 通过反射机制调用普通Field age = aClass.getDeclaredField("age");age.set(p1,22);System.out.println(p1);// 通过反射机制调用私有属性Field name = aClass.getDeclaredField("name");name.setAccessible(true);name.set(p1,"李四");System.out.println(p1);}
}class Person {private String name;public int age ;public Person() {}public void methods1(String s) {System.out.println("我是一个public方法");}private void methods2(){System.out.println("我是一个私有方法");}@Overridepublic String toString() {return "Persion{" +"name='" + name + '\'' +", age=" + age +'}';}private Person(String name) {this.name = name;}public Person(String name, int age) {this.name = name;this.age = age;}public void setName(String name) {this.name = name;}public void setAge(int age) {this.age = age;}public String getName() {return name;}public int getAge() {return age;}
}

疑问1 : 既然可以 new 对象为什么还要有 反射机制 ? 使用哪种方式呢?

平常肯定是使用 new 的方式,但是在某些应用场景下,还是需要使用反射机制。比如:我们在编译期不确定要创建哪个对象,要执行哪个方法。就需要反射机制的动态调用。

比如在 项目中,在项目启动时前端发送请求,这个时候后端就需要根据请求路径动态判断需要执行哪个 Servlet ,这就需要反射机制

以下代码截取为 javaweb 书城的 反射机制代码:

image-20221106151821384

对 Class 的理解

编写完代码后,由前端编译器对源代码文件编译成 .class 字节码文件。

通过 类加载器 ClassLoader 将class文件加载到内存中去。

这个被加载到内存中的类就被称为运行时类,这个类就是Class的一个实例

Person p1 = new Person();

p1 是 Person 的实例,而Person 是 Class的实例。(前提是编译运行之后)

因此也可以说一个 Class实例对应一个运行时类

Class实例获取的四种方式

  • 调用运行时类的属性: .class
  • 通过运行时类的对象,调用getClass()
  • 通过Class的静态方法,Class.forName(String classPath)
  • 通过类加载器中的 loadClass()
  @Testpublic void test1() throws ClassNotFoundException {// 第一种方式: 调用运行时类的属性Class clazz = Person.class;System.out.println(clazz);//第二种方式:通过运行时类的对象,调用 getClassPerson person = new Person();Class clazz1 = person.getClass();System.out.println(clazz1);//第三种方式:调用Class的静态方法 forNameClass clazz2 = Class.forName("Person");System.out.println(clazz2);// 第四种方式:通过ClassLoader的 loadClass方法ClassLoader classLoader = ReflectionTest2.class.getClassLoader();Class clazz3 = classLoader.loadClass("Person");System.out.println(clazz3);}

Class 对应内存结构说明

哪些类型可以有Class对象?

(1)class:外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类
(2)interface:接口
(3)[]:数组
(4)enum:枚举
(5)annotation:注解@interface
(6)primitivetype:基本数据类型
(7)void

    @Testpublic void test4() {Class s1 = Object.class;Class s2 = Comparable.class;Class s3 = String[].class;Class s4 = int[][].class;Class s5 = ElementType.class;Class s6 = Override.class;Class s7 = int.class;Class s8 = void.class;Class s9 = Class.class;int[] a = new int[10];int[] b = new int[100];Class s10 = a.getClass();Class s11 = b.getClass();// 只要数组的元素类型与维度一样,就是同一个ClassSystem.out.println(s10 == s11);}

加载Properties文件的俩种方式

第一种方式:使用 FileInputStream方式

    @Testpublic void test() throws Exception{// 第一种方式: 文件路径默认在module下Properties prop = new Properties();FileInputStream is = new FileInputStream("jdbc.properties");prop.load(is);String name = prop.getProperty("name");String age = prop.getProperty("age");System.out.println(name + "  "+age);}

第二种方式:使用CLassLoader加载

    @Testpublic void test1() throws IOException {// 第二种方式: 默认路径在 src 下Properties prop = new Properties();ClassLoader classLoader = this.getClass().getClassLoader();InputStream asStream = classLoader.getResourceAsStream("jdbc1.properties");prop.load(asStream);String name = prop.getProperty("name");String age = prop.getProperty("age");System.out.println(name + "  " + age);}

俩种方式的区别

第一中方式默认加载的文件路径在 此module 下

第二种方式默认加载文件路径在 src 目录下

第一种方式也可以加载src目录下,增加目录 路径就行了:

FileInputStream is = new FileInputStream("src/jdbc1.properties");

调用运行时类的结构

调用运行时类的指定属性

image-20221106174034649

俩个方法,第一个方法要求类的属性权限至少是 public,第二个要求属性权限至少是private,一般都使用第二种。

    // 获取运行时类的属性@Testpublic void test() throws Exception {Class clazz = Person.class;// 创建运行时类对象Person person = clazz.newInstance();// 获取指定属性Field name = clazz.getDeclaredField("name");// 打破封装name.setAccessible(true);// 设置属性// 第一个参数为属性的对象,第二个参数为属性值name.set(person,"张三");// 获取属性Object name1 =name.get(person);System.out.println(name1);}

调用运行时类指定的方法

image-20221106174952819

使用情况和属性的一样

    @Testpublic void test1() throws Exception {Class clazz = Person.class;Person person = clazz.newInstance();// 获取指定方法: 第一个参数为方法名,第二个参数为方法形参类型Method methods1 = clazz.getDeclaredMethod("methods2",String.class);methods1.setAccessible(true);// invoke:执行方法,invoke方法的返回值是调用方法的返回值methods1.invoke(person,"李四");}

调用运行时类指定的构造器

image-20221106175906863

和上面一样不写了

    // 调用运行时类的指定构造器@Testpublic void test2() throws Exception {Class clazz = Person.class;Person person = clazz.newInstance();Constructor constructor = clazz.getDeclaredConstructor(String.class);constructor.setAccessible(true);Person person1 = constructor.newInstance("李四");System.out.println(person1);}

反射的应用:动态代理

代理模式的原理

使用一个代理将对象包装起来, 然后用该代理对象取代原始对象。任何对原始对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原始对象上。

  • 静态代理:特征是代理类和目标对象的类都是在编译期间确定下来,不利于程序的扩展。同时,每一个代理类只能为一个接口服务,这样一来程序开发中必然产生过多的代理。最好可以通过一个代理类完成全部的代理功能

  • 动态代理:是指客户通过代理类来调用其它对象的方法,并且是在程序运行时根据需要动态创建目标类的代理对象。

  • 动态代理相比于静态代理的优点

    抽象角色中(接口)声明的所有方法都被转移到调用处理器一个集中的方法中处理,这样,我们可以更加灵活和统一的处理众多的方法。

静态代理演示:

public class StaticProxyTest {interface ClothFactory {void createCloths();}// 代理类
static   class ProxyClothFactory implements ClothFactory{private ClothFactory clothFactory;public ProxyClothFactory(ClothFactory clothFactory) {this.clothFactory = clothFactory;}@Overridepublic void createCloths() {System.out.println("代理工厂的一些准备工作");clothFactory.createCloths();System.out.println("代理工厂的一些后序工作");}}// 被代理类static  class NikeClothFactory implements ClothFactory{@Overridepublic void createCloths() {System.out.println("NIKE 生成了一批衣服");}}public static void main(String[] args) {NikeClothFactory nikeClothFactory = new NikeClothFactory();ProxyClothFactory proxyClothFactory = new ProxyClothFactory(nikeClothFactory);proxyClothFactory.createCloths();}
}

输出结果:

代理工厂的一些准备工作
NIKE 生成了一批衣服
代理工厂的一些后序工作

动态代理演示:

动态代理:通过动态生成代理类对象,去调用被代理类中的方法。

一般代理类就是一个接口,只提供方法,不提供具体的服务。在被代理类中提供服务。

  • 如何通过加载到内存中的被代理类,动态生成代理类及其对象?
  • 通过代理对象调用方法时,如何动态的去调用被代理类的方法?
/**** Author: YZG* Date: 2022/11/6 18:25* Description: 动态代理:通过动态生成代理类对象调用被代理类中的方法*/
public class DynamicProxyTest {public static void main(String[] args) {Man man = new Man();// 代理类的对象Human human = (Human) Man.ProxyFactory.getInstance(man);human.eat("麻辣烫");human.sleep(8);}// 代理类interface Human {void eat(String food);void sleep(int hours);}// 被代理类static class Man implements Human {@Overridepublic void eat(String food) {System.out.println("男人吃了 " + food);}@Overridepublic void sleep(int hours) {System.out.println("男人睡了 " + hours + "小时");}// 动态代理类static class ProxyFactory {/*** @description 调用此方法 生成一个代理类的对象* @date 2022/11/6 19:06* @param obj 被代理类的对象* @return java.lang.Object*/public static Object getInstance(Object obj) {return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),/*** @description 动态调用被代理类中的方法。需要实现  InvocationHandler 接口* @date 2022/11/6 19:07* @param obj 被代理类的对象* @return java.lang.Object*/(Object proxy, Method method, Object[] args) -> {// 动态调用被代理类对象中的方法return method.invoke(obj, args);});}}}}

关于 Proxy.newProxyInstance 的三个参数: 被代理类的类加载器,被代理类实现的接口(代理类)、需要实现指定的接口

image-20221106192321497

相关内容

热门资讯

盘点当下最赚钱的无本创业小项目... 创业可以说是现在非常受欢迎的话题,很多人都会想要去创业,去实现自己的梦想,或者说是想改变一下自己的生...
适合小区创业的小本项目有哪些 ... 适合小区创业的小本项目有哪些?创业是每个人都想干的一件事,但是又不知到从何做起,大型的加盟项目投资费...
小区里的小本暴利项目哪些 有哪... 受到疫情影响,今年从年初开始,经济大环境就不算好。大家都在抱怨现在挣钱太难了,市场不景气,都没什么生...
千元小本创业有哪些 2018年... 经济形势的发展下,人民币逐渐贬值。对于创业者来说,想要找到1000元以下创业项目是比较困难的。因为一...
适合小本创业的五个好项目 推荐... 二胎政策开放很久了,儿童行业是具有潜力的市场,现在的家长都很疼爱自己的孩子,很注重孩子的早期教育培训...
2020年最好的创业项目是什么...   还不知道朋友看这里那今天我们就来说几个创业的好项目给大家分享:1、开宠物店,现在的年轻人压力大,...
小本创业代理好项目 小本创业代...  短视频,自媒体,达人种草一站服务最新移动电商创业项目:手机微信制作平台照片书照片书就是把您手机里的...
在四五线小城市有哪些城镇小本创... 如今的生活越来越好,每个人对美好生活的留念越来越重视了,很多人在生活中不管遇到什么样的事情,只要是值...
2019小本创业项目哪个比较好... 现在这些传统行业竞争压力大,投入成本也越来越高了。想要创业但是条件又有限,怎样才能以最小的成本去努力...
投入一千多 有必要理财吗 投入... 摘要:项目:防晒帽子、配饰作为一个赚女人钱的项目,卖防晒帽及配饰还是很不错的。有多少女性朋友,惧怕炎...