Java开发岗位面试被问到泛型怎么办

目录
  • 1、泛型的基础概念
    • 1.1 为什么需要泛型
    • 1.2 什么是泛型
  • 2、泛型的定义和使用
    • 2.1 泛型类\泛型接口
    • 2.2 泛型方法
    • 2.3 泛型类的继承
    • 2.4 类型通配符?及其上下限
  • 总结

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的父类进行操作

总结

本篇文章就到这里了,希望能给你带来帮助,也希望您能够多多关注我们的更多内容!

(0)

相关推荐

  • java泛型通配符详解

    前言 Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许开发者在编译时检测到非法的类型. 泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数. 泛型带来的好处 在没有泛型的情况的下,通过对类型 Object 的引用来实现参数的"任意化","任意化"带来的缺点是要做显式的强制类型转换,而这种转换是要求开发者对实际参数类型可以预知的情况下进行的.对于强制类型转换错误的情况,编译器可能不提示错

  • Java泛型的类型擦除示例详解

    目录 前言 泛型的类型擦除原则是: 1 擦除类定义中的类型参数 1.1 无限制类型擦除 1.2 有限制类型擦除 2 擦除方法定义中的类型参数 3 桥接方法和泛型的多态 总结 参考资料 前言 Java泛型这个特性是从JDK 1.5才开始加入的,因此为了兼容之前的版本,Java泛型的实现采取了"伪泛型"的策略,即Java在语法上支持泛型,但是在编译阶段会进行所谓的"类型擦除"(Type Erasure),将所有的泛型表示(尖括号中的内容)都替换为具体的类型(其对应的原生

  • 深入了解Java核心类库--泛型类

    目录 1.1 泛型的使用 1.1.1 泛型类 1.1.2 泛型接口 1.1.3 泛型方法 1.1.4 tips 1.2 泛型限制类型 1.3 通配符? 1.3.1 上界限定 1.3.2 下届限定 1.3.3 无限制 1.4 泛型的作用 总结 1.1 泛型的使用 泛型:参数化模型(不确定,后续使用时指定类型) 1.1.1 泛型类 1.1.2 泛型接口 实现接口时,可以选择或不选择指定泛型类型 public interface IntercaceName<T>{ T getData(); } ①指

  • 一篇文章带你入门java泛型

    目录 一.什么是泛型 二.语法 三.示例 1.简单示例 2.返回最大值-支持各种数据类型 3.泛型类 4.类型通配符 总结 一.什么是泛型 Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型. 泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数. 二.语法 你可以写一个泛型方法,该方法在调用时可以接收不同类型的参数.根据传递给泛型方法的参数类型,编译器适当地处理每一个方法调用. 下面是定

  • Java不能真正泛型的原因是什么?

    目录 简单来回顾一下类型擦除,看下面这段代码. 为什么 Java 不能实现真正意义上的泛型呢?背后的原因是什么? 第一,兼容性 第二,不是"实现不了" 总结 简单来回顾一下类型擦除,看下面这段代码. public class Cmower { public static void method(ArrayList<String> list) { System.out.println("Arraylist<String> list"); } p

  • Java开发岗位面试被问到泛型怎么办

    目录 1.泛型的基础概念 1.1 为什么需要泛型 1.2 什么是泛型 2.泛型的定义和使用 2.1 泛型类\泛型接口 2.2 泛型方法 2.3 泛型类的继承 2.4 类型通配符?及其上下限 总结 1.泛型的基础概念 1.1 为什么需要泛型 List list = new ArrayList();//默认类型是Object list.add("A123"); list.add("B234"); list.add("C345"); System.ou

  • Java开发岗位面试被问到嵌套类怎么办

    目录 嵌套类分类 静态内部类 1. 静态内部类中能声明哪些类,变量和方法? 2. 静态内部类能访问外围类的哪些变量和方法? 3. 继承方面 内部类 1. 细分类 2. 内部类中能声明哪些类,变量和方法? 3. 内部类能访问外围类的哪些变量和方法? 4. 内部类是怎样绑定外围对象? 5. 继承方面 6. 本地内部类 嵌套接口 总结 嵌套类分类 静态内部类(静态嵌套类/静态成员类/静态类) 内部类(非静态嵌套类) 内部成员类 本地内部类 匿名内部类 嵌套接口 静态内部类 重要的结论:如果一个类被声明

  • Java开发岗位面试被问到反射怎么办

    目录 到底什么是反射呢??? 2. 类的生命周期 3. Java反射框架主要提供以下功能: 反射的基本用法 1. 获得Class对象 2. 判断是否为某个类的实类 3.创建实例 4. 获取构造器信息 5. 获取方法 6. 获取类的成员变量(字段)信息 7. 利用反射创建数组 反射的注意事项 反射的主要用途 总结 到底什么是反射呢??? 反射的核心就是JVM在运行时才动态加载类或调用方法,访问属性,它不需要事先(写代码的时候或编译期)知道运行对象是谁. 每一个类都会产生一个对应的Class对象,也

  • java锁synchronized面试常问总结

    目录 synchronized都问啥? synchronized是什么? synchronized锁什么? synchronized怎么用? 结语 synchronized都问啥? 如果Java面试有什么是必问的,synchronized必定占据一席之地.初出茅庐时synchronized的用法,成长后synchronized的原理,可谓是Java工程师的“一生之敌”. 按照惯例,先来看synchronized的常见问题(在线Excel同步更新中): 根据统计数据可以总结出synchronize

  • 值得收藏的2017年Java开发岗位面试题

    下面是我自己收集整理的2017年Java岗位的面试题,可以用它来好好准备面试. 一.Java基础 1. String类为什么是final的. 2. HashMap的源码,实现原理,底层结构. 3. 说说你知道的几个Java集合类:list.set.queue.map实现类咯... 4. 描述一下ArrayList和LinkedList各自实现和区别 5. Java中的队列都有哪些,有什么区别. 6. 反射中,Class.forName和classloader的区别 7. Java7.Java8的

  • 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

  • java虚拟机JVM类加载机制原理(面试必问)

    目录 1.类加载的过程. 1)加载 2)验证 3)准备 4)解析 5)初始化 2.Java 虚拟机中有哪些类加载器? 1)启动类加载器(Bootstrap ClassLoader): 2)扩展类加载器(Extension ClassLoader): 3)应用程序类加载器(Application ClassLoader): 3.什么是双亲委派模型? 4.为什么使用双亲委派模式? 5.有哪些场景破坏了双亲委派模型? 1)线程上下文类加载器 2)Tomcat 的多 Web 应用程序 3)OSGI 实现

  • 详解Java中方法重写与重载的区别(面试高频问点)

    Java中方法重写与重载的区别 重 写 重 载 子类方法对父类方法的覆盖 同一个类中同名方法的重载(同一类包括从父类继承的方法) 方法名相同且参数个数类型顺序相同 参数个数或类型顺序至少满足一点不同 只允许访问权限更宽松 访问权限任意 返回值类型若是基本类型则不允许不同:若是复合类型则在子类与父类间必须至少存在继承关系 返回值类型任意 final修饰的父类,子类不能重写,反之可以 final任意 静态方法与实例方法不能互相重写 任意 构造方法不能被重写 构造方法可以重载,任意 一句话描述清楚:

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

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

  • 面试官问如何启动Java 线程

    目录 一.线程启动分析 二.线程启动过程 1. Thread start UML 图 2. Java 层面 Thread 启动 2.1 start() 方法 2.2 start0() 本地方法 3. JVM 创建线程 3.1 JVM_StartThread 3.2 JavaThread 3.3 os::create_thread 3.4 java_start 4. JVM 启动线程 4.1 Thread::start 4.2 os::start_thread(thread) 4.3 pd_sta

随机推荐