java面试必备--JAVA基础篇(十二) 之 反射

      相信很多同行小伙伴会因为许多原因想跳槽,不论是干得不开心还是想跳槽涨薪,在如此内卷的行业,我们都面临着“面试造火箭,上班拧螺丝”的局面,鉴于当前形势博主呕心沥血整理的干货满满的造火箭的技巧来了,本博主花费2个月时间,整理归纳java全生态知识体系常见面试题!总字数高达百万! 干货满满,每天更新,关注我,不迷路,用强大的归纳总结,全新全细致的讲解来留住各位猿友的关注,希望能够帮助各位猿友在应付面试笔试上!当然如有归纳总结错误之处请各位指出修正!如有侵权请联系博主QQ1062141499! 
 

目录

1 什么是反射

2 反射的使用场景、作用及优缺点?

3 反射机制主要提供了以下功能

4 反射的原理

5 反射主要实现类有哪些?

6 说说反射在你实际开发中的使用

7 Class类的作用是什么?如何获取Class对象?

​​​​8  动态代理是什么?有哪些应用?

9 怎么实现动态代理?

10 写一个 ArrayList 的动态代理类

11  已有下面两个类,不改变源码,请写一个代理类,在method()执行前打印”before”,执行后打印“after”


1 什么是反射

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

反射就是:在任意一个方法里:

      1.如果我知道一个类的名称/或者它的一个实例对象,我就能把这个类的所有方法和变量的信息找出来(方法名,变量名,方法,修饰符,类型,方法参数等等所有信息)

      2.如果我还明确知道这个类里某个变量的名称,我还能得到这个变量当前的值。

      3.当然,如果我明确知道这个类里的某个方法名+参数个数类型,我还能通过传递参数来运行那个类里的那个方法。

2 反射的使用场景、作用及优缺点?

使用场景

      在编译时无法知道该对象或类可能属于哪些类,程序在运行时获取对象和类的信息

作用

      通过反射可以使程序代码访问装载到 JVM 中的类的内部信息,获取已装载类的属性信息、方法信息

优点

  1. 提高了 Java 程序的灵活性和扩展性,降低耦合性,提高自适应能力。
  2. 允许程序创建和控制任何类的对象,无需提前硬编码目标类
  3. 应用很广,测试工具、框架都用到了反射

缺点

  1. 性能问题:反射是一种解释操作,远慢于直接代码。因此反射机制主要用在对灵活性和扩展性要求很高的系统框架上,普通程序不建议使用
  2. 模糊程序内部逻辑:反射绕过了源代码,无法再源代码中看到程序的逻辑,会带来维护问题
  3. 增大了复杂性:反射代码比同等功能的直接代码更复杂

3 反射机制主要提供了以下功能

       在运行时判断任意一个对象所属的类

      在运行时构造任意一个类的对象

      在运行时判断任意一个类所具有的成员变量和方法

      在运行时调用任意一个对象的方法

生成动态代理。

4 反射的原理

      JAVA语言编译之后会生成一个.class文件,反射就是通过字节码文件找到某一个类、类中的方法以及属性等。

5 反射主要实现类有哪些?

   在JDK中,主要由以下类来实现 Java 反射机制,除了 Class 类,一般位于 java.lang.reflect 包中

  1. java.lang.Class :一个类
  2. java.lang.reflect.Field :类的成员变量(属性)
  3. java.lang.reflect.Method :类的成员方法
  4. java.lang.reflect.Constructor :类的构造方法
  5. java.lang.reflect.Array :提供了静态方法动态创建数组,访问数组的元素

6 说说反射在你实际开发中的使用

      反射使用不好,对性能影响比较,一般项目中很少直接使用。

      反射主要用于底层的框架中,Spring 中就大量使用了反射,比如:

  1. 用 IOC 来注入和组装 bean
  2. 动态代理、面向切面、bean 对象中的方法替换与增强,也使用了反射
  3. 定义的注解,也是通过反射查找

7 Class类的作用是什么?如何获取Class对象?

      Class 类是 Java 反射机制的起源和入口,用于获取与类相关的各种信息,提供了获取类信息的相关方法。

      Class 类存放类的结构信息,能够通过 Class 对象的方法取出相应信息:类的名字、属性、方法、构造方法、父类、接口和注解等信息

  1. 对象名.getClass()   
  2. 对象名.getSuperClass()
  3. Class.forName("oracle.jdbc.driver.OracleDriver");
  4. 类名.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  框架等等都是动态代理的使用例子。

动态代理的应用有

  1. spring AOP
  2. hibernate 数据查询
  3. 测试框架的后端 mock
  4. rpc
  5. Java注解对象获取
  6. 分布式锁

等。

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值