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.size();i++){
            //若要将list中的元素赋给String变量,需要进行类型转换,不然会报Incompatible types错误,显示list.get(i)返回的是Object
            String str =  (String) list.get(i);
            System.out.println(str);
        }
​
        list.add(123);//因为类型是Object,我们可以把Integer类型或者其他数据类型的元素也加入list之中
        System.out.println(list.get(3));
​
        for(int i=0;i<list.size();i++){
            //String str =  (String) list.get(i);//但是在这里会报错java.lang.ClassCastException,我们不能直接将Integer类型的数据转换成String
            System.out.println(list.get(i).getClass());
        }

如代码中所示,当我们定义了一个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<String> strlist = new ArrayList<String>();
        List<Integer> intlist = new ArrayList<Integer>();
​
        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<String> stringList2 = list;
        List<Integer> intList2 = list;//你也可以把它赋给Integer类型的集合,但是当你把这个集合当成Integer的集合操作的时候,依旧会抛出ClassCastException异常
​
        for (Integer i:intList2){//java.lang.ClassCastException
            System.out.println(i);
        }

2、泛型的定义和使用

2.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开发交流君样: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<String> 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  <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,]

2.3 泛型类的继承

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

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

2.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开发交流君样:756584822一起吹水聊天
    //使用通配符,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开发交流君样:756584822一起吹水聊天
        //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);
​
        // public static void showNumList(List<? extends Number> list)
        //当设定了通配符上限,只能传入List<Number及其子类>的数据
        List<String> Stringlist = new ArrayList<>();
        showNumList(Intgetlist);
        showNumList(Numberlist);//加入Java开发交流君样:756584822一起吹水聊天
        //showNumList(Stringlist);//showNumList(java.util.List<? extends java.lang.Number>)in FXtest cannot be applied to(java.util.List<java.lang.String>)
​
​
        //public static boolean Compare(List<? super Number> list1,List<? super Integer> list2)
        //当设定了通配符下限,List<? super Number>只能传入List<Number及其父类>的数据,不能传入子类Integer的List,
        // 而List<? super Integer>则可以传入其父类Number的List
        //Compare(Intgetlist,Numberlist);
        Compare(Numberlist,Intgetlist);

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

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

以上就是Java泛型常见面试题(面试必问)的详细内容,更多关于Java泛型面试题的资料请关注我们其它相关文章!

(0)

相关推荐

  • Java中泛型使用的简单方法介绍

    一. 泛型是什么 "泛型",顾名思义,"泛指的类型".我们提供了泛指的概念,但具体执行的时候却可以有具体的规则来约束,比如我们用的非常多的ArrayList就是个泛型类,ArrayList作为集合可以存放各种元素,如Integer, String,自定义的各种类型等,但在我们使用的时候通过具体的规则来约束,如我们可以约束集合中只存放Integer类型的元素,如List<Integer> iniData = new ArrayList<>().

  • Java泛型的用法及T.class的获取过程解析

    这篇文章主要介绍了Java泛型的用法及T.class的获取过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 胡乱总结泛型的四点作用: 第一是泛化,可以拿个T代表任意类型. 但GP是被C++严苛的静态性逼出来的,落到Java.C#这样的花语平原里----所有对象除几个原始类型外都派生于Object,再加上Java的反射功能,Java的Collection库没有范型一样过得好好的. 第二是泛型 + 反射,原本因为Java的泛型拿不到T.cla

  • Java 获取泛型的类型实例详解

    Java 获取泛型的类型实例详解 Java 泛型实际上有很多缺陷,比如不能直接获取泛型的类型,不能获取带泛型类等. 以下方式是不正确的: ①.获取带泛型的类的类型 Class lstUClazz = List<User>.class ②获取局部变量泛型的类型 List<User> listUser = new ArrayList<User>(); Type genType = listUser.getClass().getClass().getGenericSuperc

  • 详谈Java泛型中T和问号(通配符)的区别

    类型本来有:简单类型和复杂类型,引入泛型后把复杂类型分的更细了. 概述 泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数.这种参数类型可以用在类.接口和方法的创建中,分别称为泛型类.泛型接口.泛型方法. Java语言引入泛型的好处是安全简单. 在Java SE 1.5之前,没有泛型的情况的下,通过对类型Object的引用来实现参数的"任意化","任意化"带来的缺点是要做显式的强制类型转换,而这种转换是要求开发者对

  • Java泛型类与泛型方法的定义详解

    本文实例讲述了Java泛型类与泛型方法的定义.分享给大家供大家参考,具体如下: Java泛型类的定义 一 点睛 泛型类定义的语法如下: [访问修饰符] class 类名称 <T> 泛型类的主要作用在于类被实例化后,传入具体的类型参数,对类的成员属性的类型和成员方法的参数类型和返回值类型进行替换. 二 代码 public class Base<T> { T m; Base(T t) { m = t; } public T getM(){ return m; } public void

  • 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.size();i++){ //若要将list中的元素赋给String变量,需要进行类型转换,不然会报Incompati

  • GO必知必会的常见面试题汇总

    目录 引言 值类型和引用类型 值类型有哪些? 引用类型有哪些? 值类型和引用类型的区别? 垃圾回收 一图胜千言 堆和栈 栈 堆 切片 比较 比较的详解 深拷贝和浅拷贝 操作对象 区别如下: new和make new 特点 举个例子: 使用技巧 make make函数的函数签名 特点 使用技巧 小结:new与make的区别 go的map实现排序 解决思路 代码实现: 运行结果 逃逸分析 最后,听我说 引言 今年互联网的就业环境真的好糟糕啊,好多朋友被优化. 我们平常在工作中除了撸好代码,跑通项目之

  • java对象拷贝常见面试题及应答汇总

    为什么要使用克隆? 想对一个对象进行处理,又想保留原有的数据进行接下来的操作,就需要克隆了,Java语言中克隆针对的是类的实例. 如何实现对象克隆? 有两种方式: 实现Cloneable接口并重写Object类中的clone()方法:实现Serializable接口,通过对象的序列化和反序列化实现克隆,可以实现真正的深度克隆,代码如下: import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; impor

  • JAVA中String介绍及常见面试题小结

    字符串广泛应用 在 Java 编程中,在 Java 中字符串属于对象,Java 提供了 String 类来创建和操作字符串. 深刻认识String 1)String为字符串常量:即String对象一旦创建之后该对象是不可更改的.(源码如下) String str1 = "abc"; String str2 = "abc"; String str3 = new String("abc"); System.out.println(str1 == st

  • Java常见面试题之final在java中的作用是什么

    谈到final关键字,想必很多人都不陌生,在使用匿名内部类的时候可能会经常用到final关键字. 另外,Java中的String类就是一个final类. 一.final关键字的基本用法 在Java中,final关键字可以用来修饰类.方法和变量(包括成员变量和局部变量).下面就从这三个方面来了解一下final关键字的基本用法. 1.修饰类 当用final修饰一个类时,表明这个类不能被继承.也就是说,如果一个类你永远不会让他被继承,就可以用final进行修饰.final类中的成员变量可以根据需要设为

  • Java面试必问之ThreadLocal终极篇分享

    目录 前言 ThreadLocal是什么 ThreadLoalMap hash冲突 内存泄露 如何避免内存泄露 总结 前言 在面试环节中,考察"ThreadLocal"也是面试官的家常便饭,所以对它理解透彻,是非常有必要的. 有些面试官会开门见山的提问: "知道ThreadLocal吗?" "讲讲你对ThreadLocal的理解" 当然了,也有面试官会慢慢引导到这个话题上,比如提问"在多线程环境下,如何防止自己的变量被其它线程篡改&qu

  • HashSet如何保证元素不重复(面试必问)

    目录 1.HashSet 基本用法 2.HashSet 无序性 3.HashSet 错误用法 3.1 HashSet 与基本数据类型 3.2 HashSet 与自定义对象类型 4.HashSet 如何保证元素不重复? 总结 本文已收录<Java常见面试题>系列,Git 开源地址:https://gitee.com/mydb/interview HashSet 实现了 Set 接口,由哈希表(实际是 HashMap)提供支持.HashSet 不保证集合的迭代顺序,但允许插入 null 值.也就是

  • 10道springboot常见面试题

    本文为大家分享了10道springboot常见面试题,供大家参考,具体内容如下 1.什么是Spring Boot? 多年来,随着新功能的增加,spring变得越来越复杂.只需访问https://spring.io/projects页面,我们就会看到可以在我们的应用程序中使用的所有Spring项目的不同功能. 如果必须启动一个新的Spring项目,我们必须添加构建路径或添加Maven依赖关系,配置应用程序服务器,添加spring配置. 因此,开始一个新的spring项目需要很多努力,因为我们现在必

  • R语言常见面试题整理

    尊敬的读者,这些R语言面试题是专门设计的,以便您应对在R语言相关面试中可能会被问到的问题. 根据我的经验,良好的面试官几乎不打算在你的面试中问任何特定的问题,通常都是以如下的问题为开端进一步展开后继的问题. 什么是R语言编程? R语言是一种用于统计分析和为此目的创建图形的编程语言.不是数据类型,它具有用于计算的数据对象.它用于数据挖掘,回归分析,概率估计等领域,使用其中可用的许多软件包. R语言中的不同数据对象是什么? 它们是R语言中的6个数据对象.它们是向量,列表,数组,矩阵,数据框和表. 什

  • Vue常见面试题整理【值得收藏】

    看看面试题,只是为了查漏补缺,看看自己那些方面还不懂.切记不要以为背了面试题,就万事大吉了,最好是理解背后的原理,这样面试的时候才能侃侃而谈.不然,稍微有水平的面试官一看就能看出,是否有真才实学还是刚好背中了这道面试题.(都是一些基础的vue面试题,大神不用浪费时间往下看) 一.对于MVVM的理解? MVVM是Model-View-ViewModel的缩写. Model :代表数据模型,也可以在Model中定义数据修改和操作的业务逻辑. View :代表UI组件,它负责将数据模型转化成UI展现出

随机推荐