当前位置:首页 > 科技  > 软件

你真的了解Java的反射机制吗?

来源: 责编: 时间:2023-11-01 17:05:04 212观看
导读书写代码必须符合高质量高性能要求,这也是能够在视觉上和其他程序员拉开差距的技能,同时也是一个优秀程序员的基本要求。何为高质量:代码具备可维护性,可读性,可扩展性,灵活性,简洁性,可复用性, 可测试性。何为高性能:代码能尽

书写代码必须符合高质量高性能要求,这也是能够在视觉上和其他程序员拉开差距的技能,同时也是一个优秀程序员的基本要求。Qs728资讯网——每日最新资讯28at.com

  • 何为高质量:代码具备可维护性,可读性,可扩展性,灵活性,简洁性,可复用性, 可测试性。
  • 何为高性能:代码能尽可能的提高处理效率。

今天我们说一说反射,反射不是设计模式,但是反射机制作为java的基础之一,在众多框架的源码中大量使用,是很多设计模式,框架,组件的重要基础。比如我们知道的spring aop底层是jdk的动态代理,而动态代理依赖的就是反射机制。Qs728资讯网——每日最新资讯28at.com

Qs728资讯网——每日最新资讯28at.com

一、反射机制

1.概念

在运行状态中,对于任意一个类,都能知道这个类的所有属性和方法;对于任意一个对象都能调用它的任意方法和属性,这种动态获取类信息以及动态调用对象方法的功能称为Java的发射机制。Qs728资讯网——每日最新资讯28at.com

先了解下类对象的概念:Qs728资讯网——每日最新资讯28at.com

我们知道类的加载过程为加载 验证 准备 解析 初始化 销毁,在加载阶段,jvm会根据类的全限定名找到二进制字节流,并把这个二进制字节流加载进内存,转为运行时数据区的存储结构,最后创建一个java.lang.Class类型的实例作为方法区这个类型的访问入口,这里所说的Class实例就是类对象,这个类对象创建完成后会存放在堆区,这个动作是在加载阶段完成。Qs728资讯网——每日最新资讯28at.com

方法区中存放的是类的元数据,包括静态变量,有哪些属性,有哪些方法,继承的父类,实现的接口,异常相关的信息等等,而类对象就是这些信息的访问入口,Class类提供了很多api,这些api大多是native方法,也就说明这个类对象只是这个类在堆区的一个接口,由jvm底层来实现,jvm底层会根据每个api的功能去方法区拿类的信息。Qs728资讯网——每日最新资讯28at.com

2.反射的应用

public static void main(String[] args) {    UserService userService = new UserService();    System.out.println("new关键字创建对象:"+userService);        Class<UserService> userClass= UserService.class;      Class userClass1= Class.forName("UserService");          Constructor<?>[] constructors=userClass.getDeclaredConstructors();    Constructor constructor=constructors[0];    Object user=constructor.newInstance("333333","666666");    System.out.println("反射创建对象:"+user;}

通过上面的这个例子,我们可以看到反射是如何应用的:Qs728资讯网——每日最新资讯28at.com

  • 首先通过.class或者Class.forName()方法,获取一个Class实例,这个实例就是类对象,
  • 然后通过调用这个Class实例的方法获取类的构造方法,得到构造方法Constructor实例
  • 然后调用Constructor实例的newInstance方法进行实例化对象。

以上是利用反射机制创建对象,当然除了创建对象,还可以获取类的属性实例Field和方法实例Method,通过方法实例和属性实例的api对对象的方法和属性进行设置或者执行。这便是反射的应用。Qs728资讯网——每日最新资讯28at.com

3.反射的特点

new关键字创建对象是加载类完成后接着走创建对象过程,而反射过程是Class.forName()触发加载类,但是不会创建对象,只有在调用newInstance方法时候才会创建对象,也就是把加载类和创建对象分为两个部分完成,但是调用newInstance方法创建前必须保证类已经加载完成。Qs728资讯网——每日最新资讯28at.com

new关键字创建对象是静态编译,而反射创建对象是动态编译:Qs728资讯网——每日最新资讯28at.com

  • 静态编译:在编译的时候就已经知道要创建什么对象,就会把对应类加载(忽略懒加载)
  • 动态编译:在编译的时候不知道要创建什么对象,等到运行到这段代码的时候才知道要创建什么对象。

比如下面的代码,编译阶段是不知道是否要创建UserService类的对象的,所以UserService不会被加载:Qs728资讯网——每日最新资讯28at.com

public void reflex(String str) {    if("UserService".equals(str)){   Class userClass= Class.forName("UserService");   Constructor<?>[] constructors=userClass.getDeclaredConstructors();   Constructor constructor=constructors[0];   Object UserService=constructor.newInstance("333333","666666");   System.out.println(UserService.toString());  }    }

以java8为讨论基础,网上所说的反射只能通过无参构造方法创建对象是不正确的,事实证明,反射不仅仅可以通过有参构造方式创建对象,而且还可以通过私有构造方法创建对象,而且这种通过私有构造方法创建对象的方式会破坏单例模式,你想一下,单例模式中的构造方法之所以是私有就是为了不允许外部创建单例对象。而通过反射可以创建的话,那不是违背了单例模式的定理吗。Qs728资讯网——每日最新资讯28at.com

例:只是为了说明反射,所有代码中的单例只是一个简单的饿汉式单例:Qs728资讯网——每日最新资讯28at.com

public class IdGenerator {   private String k;   private static final IdGenerator instance = new IdGenerator();   private IdGenerator(String k) {    this.k=k;   }   public static IdGenerator getInstance() {    return instance;   }  }    public class reflex {    public static void main(String[] args){    Class idGeneratorClass= Class.forName("IdGenerator");  Constructor<?>[] constructors=idGeneratorClass.getDeclaredConstructors();  Constructor constructor=constructors[0];    constructor.setAccessible(true);//暴力反射,可以突破私有权限  Object idGenerator=constructor.newInstance("333333");  System.out.println(idGenerator==IdGenerator.getInstance());        }}

这个例子既验证了反射调用有参构造方法创建实例,又验证了反射破坏单例模式。Qs728资讯网——每日最新资讯28at.com

反射会造成泛型擦除:Qs728资讯网——每日最新资讯28at.com

List<UserService> list=new ArrayList<UserService>();  list.add(new UserService());  list.add(new UserService());  list.add(new UserService());  Class<? extends List> listClass=list.getClass();  Method method=listClass.getDeclaredMethod("add",Object.class);  method.invoke(list,"123");  for(int i=0;i<list.size();i++){   System.out.println(list.get(i));  }

上面的例子中通过反射创建的list对象,在调用add方法的时候不会限制类型,导致无法用某个类型去接收list集合中数据,否则会报类型转换异常,这种情况只能直接返回前端。Qs728资讯网——每日最新资讯28at.com

效率问题 ,反射的效率比new字段创建对象的效率低很多,因此在使用的时候要特别注意性能问题,但是即便是这样,我们写出的代码主要的性能影响点很少是反射造成,而大多情况是因为代码结构框架,函数,工具,底层原理的不合理使用造成的。因此发射机制可以用在代码中,但是要用在合适的位置。Qs728资讯网——每日最新资讯28at.com

现在来总结下反射的作用:Qs728资讯网——每日最新资讯28at.com

可以在程序运行过程中去操作字节码文件和类对象进而进行得到类信息,创建对象以及执行对象方法等,不需要重新编译,提高程序的扩展性 复用性 解耦Qs728资讯网——每日最新资讯28at.com

二、反射相关的四个类

反射相关的四个类,这些类的中的方法底层大多是native方法,所以反射其实是jvm底层实现。掌握了这四个类,灵活运用,基本就掌握了反射机制。Qs728资讯网——每日最新资讯28at.com

1.Class类

  • getClassLoader() 返回类加载器
  • getClasses() 返回一个数组,该数组中包含该类中所有公共类和接口类的类对象
  • getDeclaredClasses() 返回一个数组,数组中包含该类中所有类的和接口类的对象
  • forName(String className) 根据类名返回类的类对象
  • newInstance()创建类的实例
  • getPackage()获取类的包
  • getSimpleName() 获取类的名字
  • getSuperclass() 获取当前类继承的类的名字
  • getInterfaces() 获取当前类实现的类或者接口
  • .class 获取当前对象的类对象
  • getField(String str) 获取public的字段对象 只能得到public
  • getFields() 获取所有public字段对象 只能得到public
  • getDeclaredFild(String name) 取某个字段对象
  • getDeclaredFilds() 取所有字段对象
  • getAnnotation(Class) 获取注解
  • getConstructor(String.calss...) 获取该类中对应参数类型的构造方法
  • getConstructors()获取该类的所有公有构造方法
  • getDeclaredConstructor(String.calss...)获取该类中与参数类型匹配的构造方法
  • getDeclaredConstructors()获取所有构造方法
  • getMethods()获取该类所有公有方法
  • getMethod(String name,String.calss...) 获取该类对应名称和参数类型的公有方法
  • getDeclaredMethods() 获取所有方法
  • getDeclaredMethods(String name,String.calss...)获取该类对应名称和参数类型的方法
  • isAnnotation()如果是注解类型返回true
  • isnotationPresent(注解类型)如果是指定类型的注解返回true
  • isArray()如果是数组类型返回true
  • isEnum()如果是枚举类型 返回true
  • isInstance(Object obj)如果传入的参数是该类的实例则返回true
  • isInterface()如果是接口类型返回true

2.Field类

field是类中的成员变量:变量和属性是俩个概念,变量有get和set方法就是属性。Qs728资讯网——每日最新资讯28at.com

  • get(Object obj)获取obj对象中对应的属性值
  • set(Object obj,Object val)设置obj对象中对应属性值
  • setAccessible() 暴力反射,忽略访问权限修饰符

3.Method类

  • invoke(Object obj,object args...) 入对象及参数调用该对象的该方法
  • getName() 取某个方法的名字
  • setAccessible() 暴力反射,忽略访问权限修饰符

4.Constructor类

newInstance(object arg...) 传入参数的时候,会调用有对应参数的构造方法创建对象,不传参数就是使用默认构造方法。Qs728资讯网——每日最新资讯28at.com

setAccessible() 暴力反射,忽略访问权限修饰符:Qs728资讯网——每日最新资讯28at.com

Class userClass= Class.forName("UserService");Constructor<?>[] constructors=userClass.getDeclaredConstructors();Constructor constructor=constructors[0];Object UserService=constructor.newInstance("333333","666666"); 

所谓暴力反射,就是当类中有私有构造方法,私有属性,私有方法的时候,对这些对象进行反射调用的时候会报错,原因是无法突破私有权限,反射调用前先调用对象的setAccessible方法,设置为true,就可以突破私有权限,代码可以看上面破坏单例的例子。Qs728资讯网——每日最新资讯28at.com

本文链接:http://www.28at.com/showinfo-26-16379-0.html你真的了解Java的反射机制吗?

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: Spring Boot 多个环境的配置方式

下一篇: Go 使用环境变量

标签:
  • 热门焦点
  • Automa-通过连接块来自动化你的浏览器

    Automa-通过连接块来自动化你的浏览器

    1、前言通过浏览器插件可实现自动化脚本的录制与编写,具有代表性的工具就是:Selenium IDE、Katalon Recorder,对于简单的业务来说可快速实现自动化的上手工作。Selenium IDEKat
  • 让我们一起聊聊文件的操作

    让我们一起聊聊文件的操作

    文件【1】文件是什么?文件是保存数据的地方,是数据源的一种,比如大家经常使用的word文档、txt文件、excel文件、jpg文件...都是文件。文件最主要的作用就是保存数据,它既可以保
  • 如何通过Python线程池实现异步编程?

    如何通过Python线程池实现异步编程?

    线程池的概念和基本原理线程池是一种并发处理机制,它可以在程序启动时创建一组线程,并将它们置于等待任务的状态。当任务到达时,线程池中的某个线程会被唤醒并执行任务,执行完任
  • 只需五步,使用start.spring.io快速入门Spring编程

    只需五步,使用start.spring.io快速入门Spring编程

    步骤1打开https://start.spring.io/,按照屏幕截图中的内容创建项目,添加 Spring Web 依赖项,并单击“生成”按钮下载 .zip 文件,为下一步做准备。请在进入步骤2之前进行解压。图
  • 19个 JavaScript 单行代码技巧,让你看起来像个专业人士

    19个 JavaScript 单行代码技巧,让你看起来像个专业人士

    今天这篇文章跟大家分享18个JS单行代码,你只需花几分钟时间,即可帮助您了解一些您可能不知道的 JS 知识,如果您已经知道了,就当作复习一下,古人云,温故而知新嘛。现在,我们就开始今
  • 微博大门常打开,迎接海外画师漂洋东渡

    微博大门常打开,迎接海外画师漂洋东渡

    作者:互联网那些事&ldquo;起猛了,我能看得懂日语了&rdquo;。&ldquo;为什么日本人说话我能听懂?&rdquo;&ldquo;中文不像中文,日语不像日语,但是我竟然看懂了&rdquo;&hellip;&hell
  • 华为Mate60标准版细节曝光:经典星环相机模组回归

    华为Mate60标准版细节曝光:经典星环相机模组回归

    这段时间以来,关于华为新旗舰的爆料日渐密集。据此前多方爆料,今年华为将开始恢复一年双旗舰战略,除上半年推出的P60系列外,往年下半年的Mate系列也将
  • 超级标准版旗舰!iQOO 11S全球首发iQOO超算独显芯片

    超级标准版旗舰!iQOO 11S全球首发iQOO超算独显芯片

    上半年已接近尾声,截至目前各大品牌旗下的顶级旗舰都已悉数亮相,而下半年即将推出的顶级旗舰已经成为了数码圈爆料的主流,其中就包括全新的iQOO 11S系
  • OPPO K11搭载长寿版100W超级闪充:26分钟充满100%

    OPPO K11搭载长寿版100W超级闪充:26分钟充满100%

    据此前官方宣布,OPPO将于7月25日也就是今天下午14:30举办新品发布会,届时全新的OPPO K11将正式与大家见面,将主打旗舰影像,和同档位竞品相比,其最大的卖
Top