Java泛型常见面试题(面试必问)

泛型在java中有很重要的地位,在面向对象编程及各种设计模式中有非常广泛的应用。java泛型知识点也是Java开发岗位必问的一个话题,今天小编就给大家普及下Java泛型常见面试题,感兴趣的朋友一起看看吧

1、泛型的基础概念

1.1 为什么需要泛型

 List list = new ArrayList();//默认类型是Object list.add("A123"); list.add("B234"); list.add("C345"); System.out.println(list); for(int i=0;i

如代码中所示,当我们定义了一个List,list默认的类型是所有对象的基类Object,那么我们取出数据的时候需要经过一次类型转换才能进行对象的实际类型的相关操作。因为List中的类型是Object,那么我们先添加了String类型的数据,然后再添加Integer或者其他类型的数据也是允许的,因为编译时List中是Object类型的数据,然而运行的时候却是它本身的类型,所以当我们将List中的数据当作String处理时会抛出java.lang.ClassCastException

那么有没有什么办法可以使集合能够记住集合内元素各类型,且能够达到只要编译时不出现问题,运行时就不会出现java.lang.ClassCastException异常呢?答案就是使用泛型。

1.2 什么是泛型

Java泛型设计原则是:只要在编译时期没有出现警告,那么运行时期就不会出现ClassCastException异常。

泛型,即“参数化类型”,把类型明确的工作推迟到创建对象或调用方法的时候才去明确的特殊类型,把<数据类型>当作是参数一样传递。

什么是泛型?为什么要使用泛型?

泛型,即“参数化类型”。一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。那么参数化类型怎么理解呢?

顾名思义,就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),

然后在使用/调用时传入具体的类型(类型实参)。

泛型的本质是为了参数化类型(在不创建新的类型的情况下,通过泛型指定的不同类型来控制形参具体限制的类型)。也就是说在泛型使用过程中,

操作的数据类型被指定为一个参数,这种参数类型可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法。

相关术语:

  • ArrayList中的E称为类型参数变量
  • ArrayList中的Integer称为实际类型参数
  • 整个称为ArrayList泛型类型
  • 整个ArrayList称为参数化的类型ParameterizedType

泛型的作用:

代码更加简洁【不用强制转换】

程序更加健壮【只要编译时期没有警告,那么运行时就不会抛出ClassCastException的异常】

可读性和稳定性【在编写集合的时候,就限定了类型】

 List strlist = new ArrayList(); List intlist = new ArrayList(); ​ strlist.add("A"); strlist.add("B"); //strlist.add(123);//编译时报错 ​ for(String str:strlist){ System.out.println(str); //A //B } ​//加入Java开发交流君样:756584822一起吹水聊天 System.out.println(strlist.getClass());//class java.util.ArrayList System.out.println(intlist.getClass());//class java.util.ArrayList System.out.println(strlist.getClass()==intlist.getClass());//true

泛型擦除

泛型是提供给javac编译器使用的,它用于限定集合的输入类型,让编译器在源代码级别上,即挡住向集合中插入非法数据。但编译器编译完带有泛型的java程序后生成的class文件中将不再带有泛型信息,以此使程序的运行效率不受到影响,这个过程称之为“擦除”。

泛型这个概念是JDK5提出的,JDK5以前的版本是没有泛型的,需要建通JDK5以下的集合。当把带有泛型特性的集合赋值给老版本的集合的时候,会把泛型给擦除了,它保留的是类型参数的上限,即Object。而当我们将没有类型参数的集合赋给带类型参数的集合,也不会报错,仅仅是会提示“未经检查的转换(Unchecked assignment)”,甚至也可以将它赋给其他不同类型的带有泛型特性的集合,只是依旧会抛出ClassCastException异常。

 //类型被擦除了,保留的是类型的上限,String的上限就是Object List list = strlist; ​ List stringList2 = list; List intList2 = list;//你也可以把它赋给Integer类型的集合,但是当你把这个集合当成Integer的集合操作的时候,依旧会抛出ClassCastException异常 ​ for (Integer i:intList2){//java.lang.ClassCastException System.out.println(i); }

2、泛型的定义和使用

2.1 泛型类\泛型接口

泛型类、泛型接口就是把泛型定义在类或者接口上,在用户使用该类的时候才把类型明确下来。我们常用的集合,List,Map,Stack……就是泛型类。在类上定义的泛型,在泛型类的方法、变量中都可以使用。

由于类型参数变量T在java泛型中仅仅是一个占位符,在传递参数之后才能使用,即在完成实例创建之后才能使用,所以在泛型类中,不能定义包含泛型类型的静态变量和静态方法,会报错cannot be referenced from a static context。泛型类中包含泛型类型的变量和方法必须在创建了实例明确了传递的类型参数后才可以使用。

 class Myset{ private T value; //public static T sval;//cannot be referenced from a static context public static int sval2; ​//加入Java开发交流君样:756584822一起吹水聊天 public Myset(){ ​ } ​ public Myset(T val){ this.value = val; } ​ public void setValue(T value) { this.value = value; } ​ public T getValue() { return value; } ​ /* public static T getSval(){//cannot be referenced from a static context return sval; }*/ } Myset myset = new Myset<>(); myset.setValue("12345"); System.out.println(myset.getValue());//12345 ​ myset = new Myset<>("23"); ​//加入Java开发交流君样:756584822一起吹水聊天 System.out.println(myset.getClass());//class liwx.learning.Myset

2.2 泛型方法

 public static   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,]

2.3 泛型类的继承

泛型类的子类有两种继承方式

  • 子类不明确泛型类的参数变量,子类也是泛型类
  • 子类明确泛型类的参数变量,子类不是泛型类
 //子类不明确泛型类的参数变量,子类也是泛型类 class MyChiSet1 extends Myset{ public MyChiSet1(){ ​ } public MyChiSet1(T val){ super(val); } ​//加入Java开发交流君样:756584822一起吹水聊天 } //子类明确泛型类的参数变量,子类不是泛型类 class MyChiSet2 extends Myset{ public MyChiSet2(){ ​ } public MyChiSet2(String val){ super(val); } }

2.4 类型通配符?及其上下限

,List,List……的父类,可以传递所有List类型的数据,但是不能在List类型的数据进行于具体类型相关的操作,如add public static void showList2(List list){ System.out.println(""); System.out.println(list.size()); } //设置通配符上限,可以传入List public static void showNumList(List list){ System.out.println(list.size()); } //设置通配符上限,List只可以传入List public static boolean Compare(List list1,List list2){ return list1.size()>list2.size(); }

  List Intgetlist = new ArrayList<>(); List Numberlist = new ArrayList<>(); //虽然Number是Integet的父类,但是传入List,它们逻辑上没有了继承关系 System.out.println(Intgetlist.getClass()==Numberlist.getClass());//true ​//加入Java开发交流君样:756584822一起吹水聊天 //showList(java.util.List) //List和List逻辑上无继承关系,所以无法调用 //showList(Intgetlist);//showList(java.util.List)in FXtest cannot be applied to(java.util.List) showList(Numberlist); ​ //public static void showList2(List list) //通配符List在逻辑上是所有List<具体参数类型>的父类,方法可以传入其子类类型的数据 showList2(Intgetlist); showList2(Numberlist); ​ // public static void showNumList(List list) //当设定了通配符上限,只能传入List的数据 List Stringlist = new ArrayList<>(); showNumList(Intgetlist); showNumList(Numberlist);//加入Java开发交流君样:756584822一起吹水聊天 //showNumList(Stringlist);//showNumList(java.util.List)in FXtest cannot be applied to(java.util.List) ​ ​ //public static boolean Compare(List list1,List list2) //当设定了通配符下限,List只能传入List的数据,不能传入子类Integer的List, // 而List则可以传入其父类Number的List //Compare(Intgetlist,Numberlist); Compare(Numberlist,Intgetlist);

通配符的使用在逻辑上还原了泛型类传入数据类型的参数父类子类的继承关系,同时也可以按照需求设定通配符的上限了下限。

  • List在逻辑上是所有List<具体参数类型>的父类,可对所有List数据进行操作
  • List设定了通配符的上限,可对所有Type及Type的子类进行操作
  • List设定了通配符的下限,可对所有Type及Type的父类进行操作

以上就是Java泛型常见面试题(面试必问)的详细内容,更多请关注0133技术站其它相关文章!

赞(0) 打赏
未经允许不得转载:0133技术站首页 » Java