文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

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

2024-11-30 06:15

关注

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

一、反射机制

1.概念

在运行状态中,对于任意一个类,都能知道这个类的所有属性和方法;对于任意一个对象都能调用它的任意方法和属性,这种动态获取类信息以及动态调用对象方法的功能称为Java的发射机制。

先了解下类对象的概念:

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

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

2.反射的应用

public static void main(String[] args) {

    UserService userService = new UserService();
    System.out.println("new关键字创建对象:"+userService);
    
    Class 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;
}

通过上面的这个例子,我们可以看到反射是如何应用的:

以上是利用反射机制创建对象,当然除了创建对象,还可以获取类的属性实例Field和方法实例Method,通过方法实例和属性实例的api对对象的方法和属性进行设置或者执行。这便是反射的应用。

3.反射的特点

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

new关键字创建对象是静态编译,而反射创建对象是动态编译:

比如下面的代码,编译阶段是不知道是否要创建UserService类的对象的,所以UserService不会被加载:

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为讨论基础,网上所说的反射只能通过无参构造方法创建对象是不正确的,事实证明,反射不仅仅可以通过有参构造方式创建对象,而且还可以通过私有构造方法创建对象,而且这种通过私有构造方法创建对象的方式会破坏单例模式,你想一下,单例模式中的构造方法之所以是私有就是为了不允许外部创建单例对象。而通过反射可以创建的话,那不是违背了单例模式的定理吗。

例:只是为了说明反射,所有代码中的单例只是一个简单的饿汉式单例:

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());
    
    }
}

这个例子既验证了反射调用有参构造方法创建实例,又验证了反射破坏单例模式。

反射会造成泛型擦除:

List list=new ArrayList();

  list.add(new UserService());
  list.add(new UserService());
  list.add(new UserService());

  Class listClass=list.getClass();
  Method method=listClass.getDeclaredMethod("add",Object.class);
  method.invoke(list,"123");

  for(int i=0;i

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

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

现在来总结下反射的作用:

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

二、反射相关的四个类

反射相关的四个类,这些类的中的方法底层大多是native方法,所以反射其实是jvm底层实现。掌握了这四个类,灵活运用,基本就掌握了反射机制。

1.Class类

2.Field类

field是类中的成员变量:变量和属性是俩个概念,变量有get和set方法就是属性。

3.Method类

4.Constructor类

newInstance(object arg...) 传入参数的时候,会调用有对应参数的构造方法创建对象,不传参数就是使用默认构造方法。

setAccessible() 暴力反射,忽略访问权限修饰符:


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

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

来源:码农本农内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯