JAVA反射原理(nio java)

JAVA反射原理1JAVA反射原理2JAVA反射原理3

大家好,又见面了,我是你们的朋友全栈君。

What,s 反射

反射是个啥?
为啥要反射?
反射怎么弄?


要弄懂反射,首先需要回答关于反射的这三大问题。

这篇博客的主要目的就是
深入浅出地来回答这三个问题。

RTTI

“卧槽,这是啥,为啥带我到这里,我要知道啥是反射,这四个让人看不懂的字母是个啥。”
骚年,别着急,一步一步来学习,要搞懂反射,先要认识反射他爸(干爸吧),RTTI(Runtime Type Information,运行时类型信息)

并不是所有的Class都能在编译时明确,因此在某些情况下需要在运行时再发现和确定类型信息(比如:基于构建编程,),这就是RTTI(Runtime Type Information,运行时类型信息)。

在java中,有两种RTTI的方式,一种是传统的,即假设在编译时已经知道了所有的类型;还有一种,是利用反射机制,在运行时再尝试确定类型信息。

本文主要讲反射方式实现的RTTI,建议在阅读本文之前,先了解类的加载机制(参考我的博客:JAVA类加载详解)。

传统的RTTI

严格的说,反射也是一种形式的RTTI,不过,一般的文档资料中把RTTI和反射分开,因为一般的,大家认为RTTI指的是传统的RTTI,通过继承和多态来实现,在运行时通过调用超类的方法来实现具体的功能(超类会自动实例化为子类,或使用instance of)。

传统的RTTI有3种实现方式:

  • 向上转型或向下转型(upcasting and downcasting),在java中,向下转型(父类转成子类)需要强制类型转换
  • Class对象(用了Class对象,不代表就是反射,如果只是用Class对象cast成指定的类,那就还是传统的RTTI)
  • instanceof或isInstance()

传统的RTTI与反射最主要的区别,在于RTTI在编译期需要.class文件,而反射不需要。传统的RTTI使用转型或Instance形式实现,但都需要指定要转型的类型。

public void rtti(Object obj){
    Toy toy = Toy(obj);
    // Toy toy = Class.forName("myblog.rtti.Toy")
    // obj instanceof Toy
}

obj虽然是被转型了,但在编译期,就需要知道要转成的类型Toy,也就是需要Toy的.class文件。

相对的,反射完全在运行时在通过Class类来确定类型,不需要提前加载Toy的.class文件。

反射

那到底什么是反射(Reflection)呢?反射有时候也被称为内省(Introspection),事实上,反射,就是一种内省的方式,**Java不允许在运行时改变程序结构或类型变量的结构,但它允许在运行时去探知、加载、调用在编译期完全未知的class,可以在运行时加载该class,生成实例对象(instance object),调用method,或对field赋值。

关于Java的反射API,没必要去记忆,可以在任何JDK API中查询即可:

Class类:http://www.ostools.net/uploads/apidocs/jdk-zh/java/lang/Class.html

reflect包:http://www.ostools.net/uploads/apidocs/jdk-zh/java/lang/reflect/package-summary.html

反射的缺点

这世界上没有什么东西是完美的,口中完美的东西反而死的越快。就像…,能举出来的例子就像省略号一样多。

  • 复杂(本来JAVA敲得好好的,你突然出来个反射)
  • 效率低(18弯的山路肯定要比直直的大马路效率要低)

反射有什么用

灵活,没错,反射的优点就只有这一个,但却是最致命的那一个。它让JAVA变成了一个风骚的绅士,颇具魅力。让其在各大语言的战斗中,深得程序员的深爱。
如果JAVA没有反射,就像哈士奇没有了傻二,就像猫咪没有了可爱,就像我没有了帅气。真的太严重了。

没有了反射首先:
1.所有简单好用的框架和骚代码都消失了

没错,就这一点就足够致命了,所以真的难以想象失去了反射的JAVA还能走多远,就像真的难以想象失去帅气的我还有什么用。(写到这里我有点被自己感动的想哭)

反射的底层原理

反射的底层,这里主要讲解Method的获取与执行

Method获取

调用Class类的getDeclaredMethod可以获取指定方法名和参数的方法对象Method。

getDeclaredMethod
在这里插入图片描述
这里,我们看到privateGetDeclaredMethods和searchMethods两个看起来陌生又重要的方法,privateGetDeclaredMethods方法从缓存或JVM中获取该Class中申明的方法列表,searchMethods方法将从返回的方法列表里找到一个匹配名称和参数的方法对象。

searchMethods
在这里插入图片描述

如果找到一个匹配的Method,则重新copy一份返回,即Method.copy()方法。
所次每次调用getDeclaredMethod方法返回的Method对象其实都是一个新的对象,且新对象的root属性都指向原来的Method对象,如果需要频繁调用,最好把Method对象缓存起来。

privateGetDeclaredMethods
从缓存或JVM中获取该Class中申明的方法列表,实现如下:
在这里插入图片描述

其中reflectionData()方法实现如下:
在这里插入图片描述

这里有个比较重要的数据结构ReflectionData,用来缓存从JVM中读取类的如下属性数据:
在这里插入图片描述

从reflectionData()方法实现可以看出:reflectionData对象是SoftReference类型的,说明在内存紧张时可能会被回收,不过也可以通过-XX:SoftRefLRUPolicyMSPerMB参数控制回收的时机,只要发生GC就会将其回收,如果reflectionData被回收之后,又执行了反射方法,那只能通过newReflectionData方法重新创建一个这样的对象了,newReflectionData方法实现如下:
在这里插入图片描述

通过unsafe.compareAndSwapObject方法重新设置reflectionData字段;

在privateGetDeclaredMethods方法中,如果通过reflectionData()获得的ReflectionData对象不为空,则尝试从ReflectionData对象中获取declaredMethods属性,如果是第一次,或则被GC回收之后,重新初始化后的类属性为空,则需要重新到JVM中获取一次,并赋值给ReflectionData,下次调用就可以使用缓存数据了。

Method调用

获取到指定的方法对象Method之后,就可以调用它的invoke方法了,invoke实现如下:
在这里插入图片描述

应该注意到:这里的MethodAccessor对象是invoke方法实现的关键,一开始methodAccessor为空,需要调用acquireMethodAccessor生成一个新的MethodAccessor对象,MethodAccessor本身就是一个接口,实现如下:
在这里插入图片描述
在acquireMethodAccessor方法中,会通过ReflectionFactory类的newMethodAccessor创建一个实现了MethodAccessor接口的对象,实现如下:
在这里插入图片描述

在ReflectionFactory类中,有2个重要的字段:noInflation(默认false)和inflationThreshold(默认15),在checkInitted方法中可以通过-Dsun.reflect.inflationThreshold=xxx和-Dsun.reflect.noInflation=true对这两个字段重新设置,而且只会设置一次;

如果noInflation为false,方法newMethodAccessor都会返回DelegatingMethodAccessorImpl对象,DelegatingMethodAccessorImpl的类实现
在这里插入图片描述

其实,DelegatingMethodAccessorImpl对象就是一个代理对象,负责调用被代理对象delegate的invoke方法,其中delegate参数目前是NativeMethodAccessorImpl对象,所以最终Method的invoke方法调用的是NativeMethodAccessorImpl对象invoke方法,实现如下:

在这里插入图片描述
这里用到了ReflectionFactory类中的inflationThreshold,当delegate调用了15次invoke方法之后,如果继续调用就通过MethodAccessorGenerator类的generateMethod方法生成MethodAccessorImpl对象,并设置为delegate对象,这样下次执行Method.invoke时,就调用新建的MethodAccessor对象的invoke()方法了。

这里需要注意的是:
generateMethod方法在生成MethodAccessorImpl对象时,会在内存中生成对应的字节码,并调用ClassDefiner.defineClass创建对应的class对象,实现如下:
在这里插入图片描述

在ClassDefiner.defineClass方法实现中,每被调用一次都会生成一个DelegatingClassLoader类加载器对象
在这里插入图片描述

这里每次都生成新的类加载器,是为了性能考虑,在某些情况下可以卸载这些生成的类,因为类的卸载是只有在类加载器可以被回收的情况下才会被回收的,如果用了原来的类加载器,那可能导致这些新创建的类一直无法被卸载,从其设计来看本身就不希望这些类一直存在内存里的,在需要的时候有就行了。

参考链接:
JAVA反射原理0
JAVA反射原理1
JAVA反射原理2
JAVA反射原理3

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/128222.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • 【图像处理】详解 最近邻插值、线性插值、双线性插值、双三次插值「建议收藏」

    【图像处理】详解 最近邻插值、线性插值、双线性插值、双三次插值「建议收藏」插值(Interpolation),通常指内插,既是离散数学名词,也是图像处理术语,二者的联系十分密切。作为图像放缩(Scale)的手段,常见的传统插值方法有:最近邻插值(NearestNeighbourInterpolation)、线性插值(LinearInterpolation)、双线性插值(BilinearInterpolation)、双三次插值(Bicubicinterpolation)等乃至更高阶的线性插值方法。

  • wing是什么_acwing是什么

    wing是什么_acwing是什么原题链接设有 N×N 的方格图,我们在其中的某些方格中填入正整数,而其它的方格中则放入数字0。如下图所示:某人从图中的左上角 A 出发,可以向下行走,也可以向右行走,直到到达右下角的 B 点。在走过的路上,他可以取走方格中的数(取走后的方格中将变为数字0)。此人从 A 点到 B 点共走了两次,试找出两条这样的路径,使得取得的数字和为最大。输入格式第一行为一个整数N,表示 N×N 的方格图。接下来的每行有三个整数,第一个为行号数,第二个为列号数,第三个为在该行、该列上所放的数。行和列编号从

  • Linux学习-vmware虚拟机安装mysql和Navicat连接数据库

    Linux学习-vmware虚拟机安装mysql和Navicat连接数据库

  • 安装AdventureWorks2008[通俗易懂]

    安装AdventureWorks2008[通俗易懂]1、设置sqlserver高权限,并重新启动sqlserver服务。2、安装并启动全文服务(SQLFull-textFilterDaemonLauncher)3、启用sqlserver的文件流 FILESTREAM 的权限4、重启服务再连接即可。

  • 内连接、左外连接与右外连接的区别及作用介绍

    内连接、左外连接与右外连接的区别及作用介绍SQL语句当中比较难的部分就有今天要给朋友们分享的这个,innerjoin,leftjoin和rightjoin他们三个的作用以及区别是什么。顺便也会把交叉连接一起分享了。上面会分享一些基本的语法与使用,下方会详细介绍1)交叉连接,又称笛卡尔积SELECT*FROMtb1CROSSJOINtb2;//简写SELECT*FROMtb1,tb2;2)内连接//语法SELECTsome_columnsFROMtable1INNERJOINta

    2022年10月21日
  • Js二代身份证号码正则验证

    Js二代身份证号码正则验证一代身份证号码是十五位,2013年1月1日开始,咱们中国全面停止使用一代身份证了。二代身份证号码:1-6位:表示行政区划的代码。1、2位,所在省(直辖市,自治区)代码;3、4位,所在地级市(自治州)代码;5、6位,所在区(县,自治县,县级市)的代码;7-14位:表示出生年、月、日15-16位:所在地派出所代码17位:性别。奇数(1、3、5、7、9)男性,偶数(2、4、…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号