相信很多同行小伙伴会因为许多原因想跳槽,不论是干得不开心还是想跳槽涨薪,在如此内卷的行业,我们都面临着“面试造火箭,上班拧螺丝”的局面,鉴于当前形势博主呕心沥血整理的干货满满的造火箭的技巧来了,本博主花费2个月时间,整理归纳java全生态知识体系常见面试题!总字数高达百万! 干货满满,每天更新,关注我,不迷路,用强大的归纳总结,全新全细致的讲解来留住各位猿友的关注,希望能够帮助各位猿友在应付面试笔试上!当然如有归纳总结错误之处请各位指出修正!如有侵权请联系博主QQ1062141499!
目录
11 已有下面两个类,不改变源码,请写一个代理类,在method()执行前打印”before”,执行后打印“after”
1 什么是反射
反射是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为 Java 语言的反射机制。
反射就是:在任意一个方法里:
1.如果我知道一个类的名称/或者它的一个实例对象,我就能把这个类的所有方法和变量的信息找出来(方法名,变量名,方法,修饰符,类型,方法参数等等所有信息)
2.如果我还明确知道这个类里某个变量的名称,我还能得到这个变量当前的值。
3.当然,如果我明确知道这个类里的某个方法名+参数个数类型,我还能通过传递参数来运行那个类里的那个方法。
2 反射的使用场景、作用及优缺点?
使用场景
在编译时无法知道该对象或类可能属于哪些类,程序在运行时获取对象和类的信息
作用
通过反射可以使程序代码访问装载到 JVM 中的类的内部信息,获取已装载类的属性信息、方法信息
优点
- 提高了 Java 程序的灵活性和扩展性,降低耦合性,提高自适应能力。
- 允许程序创建和控制任何类的对象,无需提前硬编码目标类
- 应用很广,测试工具、框架都用到了反射
缺点
- 性能问题:反射是一种解释操作,远慢于直接代码。因此反射机制主要用在对灵活性和扩展性要求很高的系统框架上,普通程序不建议使用
- 模糊程序内部逻辑:反射绕过了源代码,无法再源代码中看到程序的逻辑,会带来维护问题
- 增大了复杂性:反射代码比同等功能的直接代码更复杂
3 反射机制主要提供了以下功能
在运行时判断任意一个对象所属的类。
在运行时构造任意一个类的对象。
在运行时判断任意一个类所具有的成员变量和方法。
在运行时调用任意一个对象的方法。
生成动态代理。
4 反射的原理
JAVA语言编译之后会生成一个.class文件,反射就是通过字节码文件找到某一个类、类中的方法以及属性等。
5 反射主要实现类有哪些?
在JDK中,主要由以下类来实现 Java 反射机制,除了 Class 类,一般位于 java.lang.reflect 包中
- java.lang.Class :一个类
- java.lang.reflect.Field :类的成员变量(属性)
- java.lang.reflect.Method :类的成员方法
- java.lang.reflect.Constructor :类的构造方法
- java.lang.reflect.Array :提供了静态方法动态创建数组,访问数组的元素
6 说说反射在你实际开发中的使用
反射使用不好,对性能影响比较,一般项目中很少直接使用。
反射主要用于底层的框架中,Spring 中就大量使用了反射,比如:
- 用 IOC 来注入和组装 bean
- 动态代理、面向切面、bean 对象中的方法替换与增强,也使用了反射
- 定义的注解,也是通过反射查找
7 Class类的作用是什么?如何获取Class对象?
Class 类是 Java 反射机制的起源和入口,用于获取与类相关的各种信息,提供了获取类信息的相关方法。
Class 类存放类的结构信息,能够通过 Class 对象的方法取出相应信息:类的名字、属性、方法、构造方法、父类、接口和注解等信息
- 对象名.getClass()
- 对象名.getSuperClass()
- Class.forName("oracle.jdbc.driver.OracleDriver");
- 类名.class
Class c2 = Student.class;
Class c2 = int.class
- 包装类.TYPE
Class c2 = Boolean.TYPE;
- Class.getPrimitiveClass()
(Class<Boolean>)Class.getPrimitiveClass("boolean");
8 动态代理是什么?有哪些应用?
静态代理通常只代理一个类,动态代理是代理一个接口下的多个实现类。
静态代理事先知道要代理的是什么,而动态代理不知道要代理什么东西,只有在运行时才知道。
动态代理是实现 JDK 里的 InvocationHandler 接口的 invoke 方法,但注意的是代理的是接口,也就是你的业务类必须要实现接口,通过 Proxy 里的 newProxyInstance 得到代理对象。
还有一种动态代理 CGLIB,代理的是类,不需要业务类继承接口,通过派生的子类来实现代理。通过在运行时,动态修改字节码达到修改类的目的。
AOP 编程就是基于动态代理实现的,比如著名的 Spring 框架、Hibernate 框架等等都是动态代理的使用例子。
动态代理的应用有
- spring AOP
- hibernate 数据查询
- 测试框架的后端 mock
- rpc
- Java注解对象获取
- 分布式锁
等。
9 怎么实现动态代理?
动态代理的根据实现方式的不同可以分为 JDK 动态代理和 CGlib 动态代理。
JDK 动态代理:利用反射机制生成一个实现代理接口的类,在调用具体方法前调用InvokeHandler来处理。
CGlib 动态代理:利用ASM(开源的Java字节码编辑库,操作字节码)开源包,将代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
区别:JDK代理只能对实现接口的类生成代理;CGlib是针对类实现代理,对指定的类生成一个子类,并覆盖其中的方法,这种通过继承类的实现方式,不能代理final修饰的类。
10 写一个 ArrayList 的动态代理类
final List<String> list = new ArrayList<>();
// 利用Java的反射技术(Java Reflection),在运行时创建一个实现某些给定接口的新类(也称“动态代理类”)及其实例(对象),代理的是接口(Interfaces),不是类(Class),也不是抽象类。在运行时才知道具体的实现,spring aop就是此原理。
//newProxyInstance,方法有三个参数:
//loader: 用哪个类加载器去加载代理对象
//interfaces:动态代理类需要实现的接口
//h:动态代理方法在执行时,会调用h里面的invoke方法去执行
List<String> proxyInstance = (List<String>) Proxy.newProxyInstance(
list.getClass().getClassLoader(),
list.getClass().getInterfaces(),
(proxy, method, args) -> method.invoke(list, args));
proxyInstance.add("你好");
11 已有下面两个类,不改变源码,请写一个代理类,在method()执行前打印”before”,执行后打印“after”
public class Source implements Sourceable {
@Override
public void method() {
System.out.println("原方法");
}
}
接口
public interface Sourceable {
void method();
}
动态代理类代码如下:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class SourceProxy implements InvocationHandler {
// 这个就是我们要代理的真实对象
private Object object;
// 构造方法,给我们要代理的真实对象赋初值
public SourceProxy(Object object) {
this.object = object;
}
/**
* @param proxy 指代我们所代理的那个真实对象
* @param method 指代的是我们所要调用真实对象的某个方法的Method对象
* @param args 指代的是调用真实对象某个方法时接受的参数
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before");
System.out.println("Method:" + method);
// 当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用
method.invoke(object, args);
System.out.println("after");
return null;
}
public static void main(String[] args) {
// 我们要代理的真实对象
Sourceable source = new Source();
// 我们要代理哪个真实对象,就将该对象传进去,最后是通过该真实对象来调用其方法的
SourceProxy sourceProxy = new SourceProxy(source);
/*
* 通过Proxy的newProxyInstance方法来创建我们的代理对象,我们来看看其三个参数
* 第一个参数handler.getClass().getClassLoader(),我们这里使用handler这个类的ClassLoader对象来加载我们的代理对象
* 第二个参数realSubject.getClass().getInterfaces(),我们这里为代理对象提供的接口是真实对象所实行的接口,表示我要代理的是该真实对象,这样我就能调用这组接口中的方法了
* 第三个参数handler,我们这里将这个代理对象关联到了上方的 InvocationHandler 这个对象上
*/
Sourceable sourceable = (Sourceable) Proxy.newProxyInstance(sourceProxy.getClass().getClassLoader(), source
.getClass().getInterfaces(), sourceProxy);
sourceable.method();
}