[toc]
根底面试题-反射
1、什么是反射
次要是指程序能够拜访、检测和批改它自身状态的一种行为,反射就是把 java 类中的各个成分映射为一个个对象,一个类里有:成员变量、办法、构造方法、包等信息。
Java 反射是 Java 被视为动静语言的一个要害性质,这个机制容许程序在运行时透过 Relfection APIs
获得任何一个已知名称 class
的外部信息,包含 modifiers
(诸如 public
、static
等)、superclass
(例如 Object
),实现 interfaces
(例如 Cloneable
)也包含 fiedls
和 methods
的所有信息,并能够扭转 fields
内容或唤起 methods
在动态语言中,应用一个变量,必须晓得他的存在,Java 中,变量的类型信息在编译时都保留到 class 文件中,这样在运行时能力保障准确无误,换句话说,程序在运行时都是固定的,如果这个时候想扭转,就须要反射这个货色了。
实现 Java 发射机制的类都位于 java.lang.relfect 包中
- Class类:代表一个类
- Field类:代表类的成员变量(类的属性)
- Method类:代表类的办法
- Constructor类:代表类的构造方法
- Array类:提供了动态创建数组,以及拜访数组的元素的静态方法
一句话概括就是应用反射能够赋予 jvm
动静编译的能力,否则类的元数据信息只能用明天该编译的形式实现,例如热加载,Tomcat
和 classloader
都无奈反对
快速通道:什么是反射,Java反射是什么
地址:https://baijiahao.baidu.com/s...
### 2、反射机制的优缺点
长处:
- 反射机制极大的进步了程序的灵活性和扩展性,升高模块的耦合性,进步本身的适应能力
- 通过反射机制能够让程序创立和管制任何类的对象,无需提前硬编码指标类
- 应用反射机制可能在运行时结构一个类的对象、判断一个类所具备的成员变量和办法、调用一个对象的办法。
- 反射机制是构建框架技术的根底所在,应用反射能够防止将代码写死在框架中
毛病:
- 性能问题,反射中蕴含很多动静类型,Java虚拟机不能对这些动静内容进行优化,因而反射操作成果要比失常成果低很多
- 平安限度,反射通常须要程序在运行么有平安方面的限度,如果一个程序有平安需要,那就最好不要应用反射
- 程序健壮性,反射容许代码执行一些通常不被运行的操作,应用反射会导致意想不到的结果。反射代码毁坏了Java程序结构的抽象性。
快速通道:Java反射的优缺点
地址:https://blog.csdn.net/hongfen...
3、如何获取反射Class
- 调用某个对象的 getClass 办法
People p = new People();Class cazz = p.getClass();
- 调用某个 class 属性来获取对应的 Class 对象
Class clazz = People.class;
- 应用 Class 类中的静态方法 forName() 获取
Class clazz = Class.forName("xxx.xxx.People");
有了 class 对象,咱们能够通个调用 newInstance() ,默认调用无参结构来返回一个对象
String str = (String)(Class.forName("java.lang.String").newInstance());
也能够通过 Constructor 结构器来获取对象
// 调用无参的公有构造函数Constructor c1 = Class.forName("java.lang.String").getDeclaredConstructor();c1.setAccessible(true);String str1 = (String) c1.newInstance();// 调用有参的公有构造函数Constructor c2 = Class.forName("java.lang.String").getDeclaredConstructor(new Class[] { String.class });c2.setAccessible(true);String str2 = (String) c2.newInstance("hello");
快速通道:简述Java中Class类的作用
地址:https://blog.csdn.net/chongxu...
4、反射的实现办法
举例:
- 通过 Class 对象枚举该类所有办法
- 通过 Method.setAccessible() 绕过 Java 语言的拜访权限,在其余办法中调用该公有办法
- Spring 框架中 IOC 依赖反转基于反射机制
反射如何调用
罕用的办法是调用在 java.lang.reflect.Method.invoke()
后面不钻研,就是做了权限判断, override
是外部的遍历,这一部分是查看 public/private
权限的 ,最初咱们能够看到他交给了 MethodAccessor
来实现
一共是两个
DelegatingMethodAccessorImpl
- 字面上看是委派实现
NativeMethodAccessorImpl
- Native 是本地的意思
public class Demo { public static void main(String[] args) { try { Class T = Demo.class; Method method = T.getDeclaredMethod("test", null); method.invoke(T.newInstance(), null); System.out.println(method); } catch (NoSuchMethodException | SecurityException | IllegalArgumentException | IllegalAccessException | InvocationTargetException | InstantiationException e) { e.printStackTrace(); } } public void test() { int a = 1 / 0; }}
通过这样的
排除除数 0 的异样,我能够看到,调用程序是
Method.invoke -> DelegatingMethodAccessorImpl.invoke (委派)-> NativeMethodAccessorImpl.invoke(本地实现)
为什么用委派而不间接调用本地实现呢,因为反射调用还有另一个机制,就是动静生成字节码的实现,也就是动静实现,间接应用 invoke 指令调用指标办法的,委派实现在这里的作用就是为了能让本地实现和动静实现之间来回切换
能够看看 NativeMethodAccessorImpl 的 invoke 实现
public Object invoke(Object obj, Object[] args) throws IllegalArgumentException, InvocationTargetException{ if (++numInvocations > ReflectionFactory.inflationThreshold()) { MethodAccessorImpl acc = (MethodAccessorImpl) new MethodAccessorGenerator(). generateMethod(method.getDeclaringClass(), method.getName(), method.getParameterTypes(), method.getReturnType(), method.getExceptionTypes(), method.getModifiers()); parent.setDelegate(acc); } return invoke0(method, obj, args);
不然发现,当 numInvocations
> ReflectionFactory.inflationThreshold()
,也就是类收缩阈值时,MethodAccessorGenerator
会生成一个代理类对象,并且委托给 NativeMethodAccessorImpl
的 parent
,也就是 DelegatingMethodAccessorImpl
,设置为这个代理类。转为由 GenerateMethodAccessor1
来实现,也就是动静是实现。
其中代理的实现你能够参考上面这段伪代码
// 这里只列举了要害的调用逻辑,其实它还包含调用者检测、参数检测的字节码。package jdk.internal.reflect; public class GeneratedMethodAccessor1 extends ... { @Overrides public Object invoke(Object obj, Object[] args) throws ... { Demo.test1(); return null; }}
总结
反射在外部有两种实现形式,别离时动静实现和本地实现,本地实现采纳的是 C++ 代码实现的,须要 Java 到 C++ 代码的来回转化,而动静实现因为生成字节码的较长,所以在对反射只是调用一两次的状况下,采纳本地实现会比动静实现快,而在面对大于 15 次以上(能够调整)的状况下,动静实现反而比本地就更高效率了。
咱们还能够把反射的权限的查看敞开setAccessible(true)
可能晋升肯定性能,在 invoke
办法中就体现了。
反射性能的烦扰因素
- 变成参数办法导致的 Object 数组要主动生成
- 根本类型在传入参数时的主动拆装箱子。
- 办法的内联。
快速通道:反射根本实现
地址:https://blog.csdn.net/qq_3739...
5、序列化与反序列化
序列化:将数据对象转换为二进制流的过程称为对象序列化
反序列化:将二进制流复原为数据对象的过程称为反序列化
目标是为了进行数据长久化和网络传输,罕用的场景是 PRC
框架的数据传输
序列化有三种形式实现
- Java原生序列化 实现
Serializable
接口,这个接口十分非凡,没有任何办法,只起标识作用,这个办法的兼容性也最好,然而不反对跨语言,而且性能个别。在实现Seriablizable
时,倡议设置serialVersionUID
字段值,如果不设置,编译器会依据外部实现类包含类名、接口名、办法和属性主动生成serialVersionUID
,如果类的源码有批改,那么从新编译后serialVersionUID
取值可能会产生扭转,因而Serializable
肯定要显示定义serialVersionUID
属性值。 Hessian 序列化,反对动静类型、跨语言、基于对象传输的网络协议,Java对象序列化二进制流能够被其余语言反序列化
特点:
- 自描述序列化类型,不依赖内部形容文件和定义接口,用一个字节示意罕用根底类型,极大缩短二进制流
- 语言无关,反对脚本语言
- 协定简略,比Java原生序列化高效
- JSON 序列化,轻量级数据交换格局,就是将对象转换为 JSON 字符串,在序列化中摈弃了数据类型,所以在反序列化时只有提供类型信息能力精确的反序列化
序列化通常会通过网络传输对象,对象中往往有敏感数据,所以序列化经常会成为黑客攻击点,攻击者奇妙利用反序列过程构建恶意代码,使得程序在反序列地过程中执行任意代码。
Transient
关键字能够防止属性转换为序列化
本文由mdnice多平台公布