1. Java中的泛型是什么 ? 使用泛型的好处是什么?
- 泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。
- 好处:
- 类型安全,提供编译期间的类型检测
- 前后兼容
- 泛化代码,代码可以更多的重复利用
- 性能较高,用GJ(泛型JAVA)编写的代码可以为java编译器和虚拟机带来更多的类型信息,这些信息对java程序做进一步优化提供条件。
2. Java的泛型是如何工作的 ? 什么是类型擦除 ?如何工作?
- 如何工作
- 类型检查:在生成字节码之前提供类型检查
- 类型擦除:所有类型参数都用他们的限定类型替换,包括类、变量和方法(类型擦除)
- 如果类型擦除和多态性发生了冲突时,则在子类中生成桥方法解决
- 如果调用泛型方法的返回类型被擦除,则在调用该方法时插入强制类型转换
- 类型擦除:
泛型是通过类型擦除来实现的,编译器在编译时擦除了所有类型相关的信息,所以在运行时不存在任何类型相关的信息。例如 List< String>在运行时仅用一个List来表示。这样做的目的,是确保能和Java 5之前的版本开发二进制类库进行兼容。你无法在运行时访问到类型参数,因为编译器已经把泛型类型转换成了原始类型。
3. 使用Java泛型要注意什么?
- 不能使用基本数据类型如int,而是使用Integer等包装类型;
- 这种,T是要使用大写的T,而不能使用小写
- 泛型类不可以继承Exception类,即泛型类不可以作为异常被抛出
- 不可以定义泛型数组
- 不可以用泛型构造对象,即:first = new T(); 是错误的
- 在static方法中不可以使用泛型,泛型变量也不可以用static关键字来修饰
- 不要在泛型类中定义equals(T x)这类方法,因为Object类中也有equals方法,当泛型类被擦除后,这两个方法会冲突
4. 什么是泛型中的限定通配符和非限定通配符?
限定通配符对类型进行了限制。有两种限定通配符:
- 一种是<? extends T>它通过确保类型必须是T的子类来设定类型的上界
- 另一种是<? super T>它通过确保类型必须是T的父类来设定类型的下界
- 泛型类型必须用限定内的类型来进行初始化,否则会导致编译错误。另一方面<?>表 示了非限定通配符,因为<?>可以用任意类型来替代
5. List<? extends T>和List <? super T>之间有什么区别?
- 这两个List的声明都是限定通配符的例子
- List<? extends T>可以接受任何继承自T的类型的List
- List<? super T>可以接受任何T的父类构成的List。例如List<? extends Number>可以接受List< Integer>或List< Float>
6. 如何编写一个泛型方法,让它能接受泛型参数并返回泛型类型?
编写一个泛型方法,需要用泛型类型来替代原始类型,比如使用T, E or K,V等被广泛认可的类型占位符。最简单的情况下,一个泛型方法可能会像这样:
public V put(K key, V value) {
return cache.put(key, value);
}
7. 可以把List传递给一个接受List参数的方法吗?
这样做的话会导致编译错误,因为List< Object>可以存储任何类型的对象包括String, Integer等等,而List< String>却只能用来存储Strings。
List<Object> objectList;
List<String> stringList;
objectList = stringList; //compilation error incompatible types
8. Array中可以用泛型吗?
Array事实上并不支持泛型,建议使用List来代替Array,因为List可以提供编译期的类型安全保证,而Array却不能。
9. 如何阻止Java中的类型未检查的警告?
- 如果把泛型和原始类型混合起来使用,例如下列代码,java 5的javac编译器会产生类型未检查的警告,例如
List<String> list = new ArrayList();
注意: xxx.java使用了未检查或称为不安全的操作
2. 这种警告可以使用@SuppressWarnings(“unchecked”)注解来屏蔽
10. Java中List< Object>和原始类型List之间的区别?
- 原始类型和带参数类型< Object>之间的主要区别是,在编译时编译器不会对原始类型进行类型安全检查,却会对带参数的类型进行检查,通过使用Object作为类型,可以告知编译器该方法可以接受任何类型的对象,比如String或Integer
- 它们之间的第二点区别是,你可以把任何带参数的类型传递给原始类型List,但却不能把List< String>传递给接受 List< Object>的方法,因为会产生编译错误
11. Java中List<?>和List之间的区别是什么?
List<?> 是一个未知类型的List,而List 其实是任意类型的List。你可以把List< String>, List< Integer>赋值给List<?>,却不能把List< String>赋值给 List< Object>
List<?> listOfAnyType;
List<Object> listOfObject = new ArrayList<Object>();
List<String> listOfString = new ArrayList<String>();
List<Integer> listOfInteger = new ArrayList<Integer>();
listOfAnyType = listOfString; //legal
listOfAnyType = listOfInteger; //legal
listOfObjectType = (List<Object>) listOfString; //compiler error – in-convertible types
12. List和原始类型List之间的区别?
带参数类型是类型安全的,而且其类型安全是由编译器保证的,但原始类型List却不是类型安全 的。不能把String之外的任何其它类型的Object存入String类型的List中,而可以把任何类型的对象存入原始List中。使用泛型的带参数类型不需要进行类型转换,但是对于原始类型,则需要进行显式的类型转换
List listOfRawTypes = new ArrayList();
listOfRawTypes.add(“abc”);
listOfRawTypes.add(123); //编译器允许这样 – 运行时却会出现异常
String item = (String) listOfRawTypes.get(0); //需要显式的类型转换
item = (String) listOfRawTypes.get(1); //抛ClassCastException,因为Integer不能被转换为String
List<String> listOfString = new ArrayList();
listOfString.add(“abcd”);
listOfString.add(1234); //编译错误,比在运行时抛异常要好
item = listOfString.get(0); //不需要显式的类型转换 – 编译器自动转换
13. 编写一段泛型程序来实现LRU缓存?
提示:LinkedHashMap可以用来实现固定大小的LRU缓存,当LRU缓存已经满 了的时候,它会把最老的键值对移出缓存。LinkedHashMap提供了一个称为removeEldestEntry()的方法,该方法会被put() 和putAll()调用来删除最老的键值对