泛型 & 反射
参考文章:
反射常见面试题
简单介绍一下反射
概述
获取 Class 字节码对象的方法
- Object类的getClass()方法,判断两个对象是否是同一个字节码文件
- 静态属性class,锁对象
- Class类中静态方法forName(),读取配置文件, 类名必须是完整类名
方法的使用:
反射机制优缺点
使用场景:
- 动态代理
- 我们在使用JDBC连接数据库时使用Class.forName()通过反射加载数据库的驱动程序;
- Spring框架也用到很多反射机制,最经典的就是xml的配置模式。Spring 通过 XML 配置模式装载 Bean 的过程:1) 将程序内所有 XML 或 Properties 配置文件加载入内存中; 2)Java类里面解析xml或properties里面的内容,得到对应实体类的字节码字符串以及相关的属性信息; 3)使用反射机制,根据这个字符串获得某个类的Class实例; 4)动态配置实例的属性。
- 对象的反序列化
静态编译和动态编译
- 静态编译:在编译时确定类型,绑定对象
- 动态编译:运行时确定类型,绑定对象
泛型常见面试题
1、Java中的泛型是什么 ? 使用泛型的好处是什么?
“任意化”
,但是在实际的使用中,会有类型转换异常的问题。所以Java提供了泛型来解决这个安全问题。它提供了编译期的类型安全,确保你只能把正确类型的对象放入集合中,避免了在运行时出现ClassCastException。没有泛型的情况的下,通过对类型Object的引用来实现参数的“任意化”
,存数据的时候因为类型是Object, 所以任何对象都能存进去,但是使用的时候对取出来的元素进行强制类型转换,“任意化”
带来的缺点是要做 显式的强制类型转换,而这种转换是要求开发者对实际参数类型可以预知的情况下进行的。对于强制类型转换错误的情况 ,编译器可能不提示错误,在运行的时候才出现异常,这是一个安全隐患。
2、Java的泛型是如何工作的 ? 什么是类型擦除 ?
ArrayList<String> arrayList1=new ArrayList<String>(); arrayList1.add("abc"); ArrayList<Integer> arrayList2=new ArrayList<Integer>(); arrayList2.add(123); System.out.println(arrayList1.getClass()==arrayList2.getClass());
3、什么是泛型中的限定通配符和非限定通配符 ?
这是另一个非常流行的Java泛型面试题。限定通配符对类型进行了限制。有两种限定通配符,一种是<? extends T>它通过确保类型必须是T的子类来设定类型的上界,另一种是<? super T>它通过确保类型必须是T的父类来设定类型的下界。泛型类型必须用限定内的类型来进行初始化,否则会导致编译错误。另一方面<?>表示了非限定通配符,因为<?>可以用任意类型来替代。更多信息请参阅我的文章泛型中限定通配符和非限定通配符之间的区别。
4、List<? extends T>和List <? super T>之间有什么区别 ?
这和上一个面试题有联系,有时面试官会用这个问题来评估你对泛型的理解,而不是直接问你什么是限定通配符和非限定通配符。这两个List的声明都是限定通配符的例子,List<? extends T>可以接受任何继承自T的类型的List,而List<? super T>可以接受任何T的父类构成的List。例如List<? extends Number>可以接受List<Integer>或List<Float>。在本段出现的连接中可以找到更多信息。
5、如何编写一个泛型方法,让它能接受泛型参数并返回泛型类型?
编写泛型方法并不困难,你需要用泛型类型来替代原始类型,比如使用T, E or K,V等被广泛认可的类型占位符。泛型方法的例子请参阅Java集合类框架。最简单的情况下,一个泛型方法可能会像这样:
public V put(K key, V value) { return cache.put(key, value); }
6、Java中如何使用泛型编写带有参数的类?
这是上一道面试题的延伸。面试官可能会要求你用泛型编写一个类型安全的类,而不是编写一个泛型方法。关键仍然是使用泛型类型来代替原始类型,而且要使用JDK中采用的标准占位符。
7、编写一段泛型程序来实现LRU缓存?
afterNodeAccess()
afterNodeInsertion()
- 设定最大缓存空间 MAX_ENTRIES 为 3;
- 使用 LinkedHashMap 的构造函数将 accessOrder 设置为 true,开启 LRU 顺序;
- 覆盖 removeEldestEntry() 方法实现,在节点多于 MAX_ENTRIES 就会将最近最久未使用的数据移除。
class LRUCache<K, V> extends LinkedHashMap<K, V> { private static final int MAX_ENTRIES = 3; protected boolean removeEldestEntry(Map.Entry eldest) { return size() > MAX_ENTRIES; } LRUCache() { super(MAX_ENTRIES, 0.75f, true); } }
public static void main(String[] args) { LRUCache cache = new LRUCache(); cache.put(1, "a"); System.out.println(cache.keySet()); cache.put(2, "b"); System.out.println(cache.keySet()); cache.put(3, "c"); System.out.println(cache.keySet()); cache.put(4, "d"); System.out.println(cache.keySet()); cache.get(1); System.out.println(cache.keySet()); cache.put(5, "e"); System.out.println(cache.keySet()); }
[1] [1, 2] [1, 2, 3] [1, 2, 3, 4] [2, 3, 4, 1] [3, 4, 1, 5]
8、可以把List<String>传递给一个接受List<Object>参数的方法吗?
List<Object> objectList; List<String> stringList; objectList = stringList; //compilation error incompatible types
9、Array中可以用泛型吗?
10、泛型要注意哪些问题:
instanceof
和 new
。因为在运行时,所有参数的类型信息都丢失了。instanceof
不会有任何效果。因为他们在运行时都会被擦除到上边界上。所以转型的时候用的类型实际上是上边界对应的类型。new T()
的原因有两个,一是因为擦除,不能确定类型;而是无法确定T
是否包含无参构造函数。class UseList{ <W> void f(W v){} <T> void f(T v){}; }
11、 <T extends Comparable<? super T>> 与 <T extends Comparable<T>>的区别
如果用的是 <T extends Comparable<? super T>> 那么只要实参类型或实参类型的父类实现了 Comparable 接口,那 这个实参类型就可以用这个方法,
而如果 用的是 <T extends Comparable<T>> 声明泛型的话,只有当实参类型实现了 Comparable 接口,他才可以使用这个方法,就算它的父类实现这个方法也不行。
<T extends Comparable<? Super T>> 相比于 <T extends Comparable<T>> 有更好的通用性。
// 实参必须是Number的子类,且实参或者实参的父类必须实现了 Comparable接口 private static <T extends Number & Comparable<T>> T min(T[] values){ if(values == null || values.length == 0){ return null; } T min = values[0]; for(int i = 1; i < values.length; i++){ if(min.compareTo(values[i]) > 0){ min = values[i]; } } return min; }
小插曲:
extends关键字声明中,有两个要注意的地方:
- 类必须要写在接口之前;
- 只能设置一个类做边界,其它均为接口。