我丢,去面试初级 Java 开发岗位,被问到泛型,mysql 索引原理面试题
泛型擦除
泛型是提供给 javac 编译器使用的,它用于限定集合的输入类型,让编译器在源代码级别上,即挡住向集合中插入非法数据。但编译器编译完带有泛型的 java 程序后生成的 class 文件中将不再带有泛型信息,以此使程序的运行效率不受到影响,这个过程称之为“擦除”。
泛型这个概念是 JDK5 提出的,JDK5 以前的版本是没有泛型的,需要建通 JDK5 以下的集合。当把带有泛型特性的集合赋值给老版本的集合的时候,会把泛型给擦除了,它保留的是类型参数的上限,即 Object。而当我们将没有类型参数的集合赋给带类型参数的集合,也不会报错,仅仅是会提示“未经检查的转换(Unchecked assignment)”,甚至也可以将它赋给其他不同类型的带有泛型特性的集合,只是依旧会抛出 ClassCastException 异常。
//类型被擦除了,保留的是类型的上限,String 的上限就是 Object
List list = strlist;
?
List<String> stringList2 = list;
List<Integer> intList2 = list;//你也可以把它赋给 Integer 类型的集合,但是当你把这个集合当成 Integer 的集合操作的时候,依旧会抛出 ClassCastException 异常
?
for (Integer i:intList2){//java.lang.ClassCastException
System.out.println(i);
}
[](
)3、泛型的定义和使用
=========================================================================
[](
)3.1 泛型类\泛型接口
泛型类、泛型接口就是把泛型定义在类或者接口上,在用户使用该类的时候才把类型明确下来。我们常用的集合,List,Map<K,V>,Stack……就是泛型类。在类上定义的泛型,在泛型类的方法、变量中都可以使用。
由于类型参数变量 T 在 java 泛型中仅仅是一个占位符,在传递参数之后才能使用,即在完成实例创建之后才能使用,所以在泛型类中,不能定义包含泛型类型的静态变量和静态方法,会报错 cannot be referenced from a static context。泛型类中包含泛型类型的变量和方法必须在创建了实例明确了传递的类型参数后才可以使用。
class Myset<T>{
private T value;
//public static T sval;//cannot be referenced from a static context
public static int sval2;
?//加入 Java 开发交流君样:1025684353 一起吹水聊天
public Myset(){
?
}
?
public Myset(T val){
this.value = val;
}
?
public void setValue(T value) {
this.value = value;
}
?
public T getValue() {
retur
n value;
}
?
/* public static T getSval(){//cannot be referenced from a static context
return sval;
}*/
}
Myset<String> myset = new Myset<>();
myset.setValue("12345");
System.out.println(myset.getValue());//12345
?
myset = new Myset<>("23");
?//加入 Java 开发交流君样:1025684353 一起吹水聊天
System.out.println(myset.getClass());//class liwx.learning.Myset
[](
)3.2 泛型方法
public static <T> void PrintArray(T [] arr){
System.out.print("[");
for(T t:arr){
System.out.print(t+",");
}
System.out.println("]");
}
Integer[] a = {1,2,3,4,5,6,7};
PrintArray(a);//[1,2,3,4,5,6,7,]
[](
)3.3 泛型类的继承
泛型类的子类有两种继承方式
子类不明确泛型类的参数变量,子类也是泛型类
子类明确泛型类的参数变量,子类不是泛型类
//子类不明确泛型类的参数变量,子类也是泛型类
class MyChiSet1<T> extends Myset<T>{
public MyChiSet1(){
?
}
public MyChiSet1(T val){
super(val);
}
?//加入 Java 开发交流君样:1025684353 一起吹水聊天
}
//子类明确泛型类的参数变量,子类不是泛型类
class MyChiSet2 extends Myset<String>{
public MyChiSet2(){
?
}
public MyChiSet2(String val){
super(val);
}
}
[](
)3.4 类型通配符?及其上下限
通配符<?>和类型参数变量的区别是什么?通配符<?>是实参而不是类型形参,而且 List<?>在逻辑上是 List,List 等所有 List<具体类型实参>的父类,它的使用比类型形参 T 更加灵活,但传入的通配符通常进行的是许多于具体类型无关的操作,如果涉及到具体类型相关的操作,以及返回值,还是需要使用泛型方法 T。
当我们使用?号通配符的时候,只能调用与对象无关的方法,不能调用对象与类型有关的方法。因为直到外界使用才知道具体的类型是什么。
//虽然 Object 是所有类的基类,但是 List<Object>在逻辑上与 List<Integer>等并没有继承关系,这个方法只能传入 List<Object>类型的数据
public static void showOList(List<Object> list){
System.out.println(list.size());
}
//同理,这个方法只能传入 List<Number>类型的数据,并不能传入 List<Integer>
public static void showList(List<Number> list){
System.out.println(list.size());
}//加入 Java 开发交流君样:1025684353 一起吹水聊天
//使用通配符,List<?>在逻辑上是所有 List<Number>,List<Integer>,List<String>……的父类,可以传递所有 List 类型的数据,但是不能在 List<?>类型的数据进行于具体类型相关的操作,如 add
public static void showList2(List<?> list){
System.out.println("<?>");
System.out.println(list.size());
}
//设置通配符上限,可以传入 List<Number 及 Number 的子类>
public static void showNumList(List<? extends Number> list){
System.out.println(list.size());
}
//设置通配符上限,List<? super Number>只可以传入 List<Number 及其父类>
public static boolean Compare(List<? super Number> list1,List<? super Integer> list2){
return list1.size()>list2.size();
}
List<Integer> Intgetlist = new ArrayList<>();
List<Number> Numberlist = new ArrayList<>();
//虽然 Number 是 Integet 的父类,但是传入 List,它们逻辑上没有了继承关系
System.out.println(Intgetlist.getClass()==Numberlist.getClass());//true
?//加入 Java 开发交流君样:1025684353 一起吹水聊天
//showList(java.util.List<java.lang.Number>)
//List<Integer>和 List<Number>逻辑上无继承关系,所以无法调用
//showList(Intgetlist);//showList(java.util.List<java.lang.Number>)in FXtest cannot be applied to(java.util.List<java.lang.Integer>)
showList(Numberlist);
?
//public static void showList2(List<?> list)
//通配符 List<?>在逻辑上是所有 List<具体参数类型>的父类,方法可以传入其子类类型的数据
showList2(Intgetlist);
showList2(Numberlist);
?
评论