Java泛型与反射

💋1、Java 的泛型是如何工作的 ? 什么是类型擦除 ?

泛型是通过类型擦除来实现的,编译器在编译时擦除了所有类型相关的信息,所以在运行时不存在任何类型相关的信息。
类型擦除:泛型信息只存在于代码编译阶段,在进入JVM 之前,与泛型相关的信息会被擦除掉,专业术语叫做类型擦除。
对于泛型,只是允许程序员在编译时检测到非法的类型而已,但是在运行期时,其中的泛型标志会变化为 Object 类型。
在泛型类被类型擦除的时候,之前泛型类中的类型参数部分如果没有指定上限,如 < T > 则会被转译成普通的 Object 类型,如果指定了上限如 < T extends String > 则类型参数就被替换成类型上限。
Snipaste_2021-12-23_10-44-48.png
当我们获取20时,编译器会报错

  1. java.lang.Integer cannot be cast to java.lang.String
  2. at com.zhang.question.Demo.main(Demo.java:25)

补充

  1. List<String> list = new ArrayList<String>();

1、两个 String 其实只有第⼀个起作用,后⾯⼀个没什么卵用,只不过 JDK7 才开始⽀持
Listlist = new ArrayList<> 这种写法。
2、第⼀个 String 就是告诉编译器,List 中存储的是 String 对象,也就是起类型检查的作用,之后编译器会擦除泛型占位符,以保证兼容以前的代码。

2、什么是泛型中的限定通配符和⾮限定通配符 ?

限定通配符对类型进⾏了限制。有两种限定通配符,⼀种是< ? extends T > 它通过确保类型必须是 T 的⼦类来设定类型的上界,另⼀种是< ? super T >它通过确保类型必须是 T 的⽗类来设定类型的下界。泛型类型必须⽤限定内的 类型来进⾏初始化,否则会导致编译错误。另⼀⽅⾯ < ? > 表示了非限定通配符,因为 < ? > 可以⽤任意类型来替代。

💋3、List<? extends T> 和 List <? super T> 之间有什么区别 ?

这两个 List 的声明都是限定通配符的例子,List< ? extends T > 可以接受任何继承自 T 的类型的 List,而List < ? super T > 可以接受任何 T 的父类构成的 List。

List<? extends T>表示类型的上界为T,即参数化的类型可能是T也可能是T的子类。<? extends T>被设计用来读数据的泛型,只能读取类型为T的元素。
List<? super T>表示类型的下界为T,即参数化的类型可能是T也可能是T的父类型。<? super T>被设计用来写数据的泛型,只能写入T或T的子类型,不能用来读。

例如 List< ? extends Number > 可以接受 List 或 List

4、Java 中的反射是什么意思?有哪些应用场景?

每个类都有⼀个 Class 对象,包含了与类有关的信息。当编译⼀个新类时,会产⽣⼀个同名的 .class ⽂件,该文件内容保存着 Class 对象。类加载相当于 Class 对象的加载,类在第⼀次使⽤时才动态加载到 JVM 中。也可以使⽤ Class.forName(“com.mysql.jdbc.Driver”) 这种⽅式来控制类的加载,该⽅法会返回⼀个 Class 对象。
反射可以提供运⾏时的类信息,并且这个类可以在运⾏时才加载进来,甚⾄在编译时期该类的 .class 不存在也可以 加载进来。Class 和 java.lang.reflect ⼀起对反射提供了⽀持,java.lang.reflect 类库主要包含了以下三个类:

  1. Field :可以使⽤ get() 和 set() ⽅法读取和修改 Field 对象关联的字段;
  2. Method :可以使⽤ invoke() ⽅法调⽤与 Method 对象关联的⽅法;
  3. Constructor :可以⽤ Constructor 创建新的对象。

应⽤举例:⼯⼚模式,使⽤反射机制,根据全限定类名获得某个类的 Class 实例。

5、反射的优缺点?

优点:
运行期类型的判断,class.forName() 动态加载类,提⾼代码的灵活度;
缺点:
尽管反射⾮常强⼤,但也不能滥⽤。如果⼀个功能可以不⽤反射完成,那么最好就不⽤。在我们使⽤反射技术时,
下面几条内容应该牢记于心。

  1. 性能开销 :反射涉及了动态类型的解析,所以 JVM ⽆法对这些代码进⾏优化。因此,反射操作的效率要⽐那 些⾮反射操作低得多。我们应该避免在经常被执⾏的代码或对性能要求很⾼的程序中使⽤反射。
  2. 安全限制 :使⽤反射技术要求程序必须在⼀个没有安全限制的环境中运⾏。如果⼀个程序必须在有安全限制 的环境中运⾏,如 Applet,那么这就是个问题了。
  3. 内部暴露:由于反射允许代码执⾏⼀些在正常情况下不被允许的操作(⽐如:访问私有的属性和⽅法),所 以使⽤反射可能会导致意料之外的副作⽤,这可能导致代码功能失调并破坏可移植性。反射代码破坏了抽象性,因此当平台发⽣改变的时候,代码的⾏为就有可能也随着变化。

    6、Java 中的动态代理是什么?有哪些应⽤?

    动态代理:当想要给实现了某个接⼝的类中的⽅法,加⼀些额外的处理。⽐如说加⽇志,加事务等。可以给这个类创建⼀个代理,故名思议就是创建⼀个新的类,这个类不仅包含原来类⽅法的功能,⽽且还在原来的基础上添加了额外处理的新功能。这个代理类并不是定义好的,是动态⽣成的。具有解耦意义,灵活,扩展性强。
    动态代理的应⽤:Spring 的 AOP 、加事务、加权限、加⽇志。

    💋7、怎么实现动态代理?

    首先必须定义⼀个接口,还要有⼀个 InvocationHandler(将实现接口的类的对象传递给它)处理类。再有⼀个工具类 Proxy(习惯性将其称为代理类,因为调用它的 newInstance() 可以产生代理对象,其实它只是⼀个产生代理对象的⼯具类)。利用到 InvocationHandler,拼接代理类源码,将其编译生成代理类的⼆进制码,利用加载器加载,并将其实例化产生代理对象,最后返回。

每⼀个动态代理类都必须要实现 InvocationHandler 这个接口,并且每个代理类的实例都关联到了⼀个 handler,我们通过代理对象调用⼀个方法的时候,这个方法的调用就会被转发为由 InvocationHandler 这个接口的 invoke 方法来进行调用。我们来看看 InvocationHandler 这个接口的唯⼀⼀个方法 invoke 方法:

  1. Object invoke(Object proxy, Method method, Object[] args) throws Throwable

proxy:指代我们所代理的那个真实对象。
method::指代的是我们所要调用真实对象的某个方法的 Method 对象;
args:指代的是调用真实对象某个方法时接受的参数;
Proxy 类的作用是动态创建⼀个代理对象的类。它提供了许多的方法,但是我们用的最多的就是
newProxyInstance 这个方法:

  1. public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,
  2. InvocationHandler handler) throws IllegalArgumentException

loader:⼀个 ClassLoader 对象,定义了由哪个 ClassLoader 对象来对生成的代理对象进行加载;
interfaces:⼀个 Interface 对象的数组,表示的是我将要给我需要代理的对象提供⼀组什么接口,如果我提供了⼀组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了 。
handler:⼀个 InvocationHandler 对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪⼀个 InvocationHandler 对象上。
通过 Proxy.newProxyInstance 创建的代理对象是在 Jvm 运行时动态生成的⼀个对象,它并不是我们的
InvocationHandler 类型,也不是我们定义的那组接口的类型,而是在运行是动态生成的⼀个对象。

具体的demo演示 <-点击链接,看下面这个也行 ↓
动态代理