//因为类型是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
异常呢?答案就是使用泛型。
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);
}
=======================================================================
泛型类、泛型接口就是把泛型定义在类或者接口上,在用户使用该类的时候才把类型明确下来。我们常用的集合,List,Map<K,V>,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;
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”);
System.out.println(myset.getClass());//class liwx.learning.Myset
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,]
泛型类的子类有两种继承方式
-
子类不明确泛型类的参数变量,子类也是泛型类
-
子类明确泛型类的参数变量,子类不是泛型类
//子类不明确泛型类的参数变量,子类也是泛型类
class MyChiSet1 extends Myset{
public MyChiSet1(){
}
public MyChiSet1(T val){
super(val);
}
}
//子类明确泛型类的参数变量,子类不是泛型类
class MyChiSet2 extends Myset{
public MyChiSet2(){
}
public MyChiSet2(String val){
super(val);
}
}
通配符<?>和类型参数变量的区别是什么?通配符<?>是实参而不是类型形参,而且List<?>在逻辑上是List,List等所有List<具体类型实参>的父类,它的使用比类型形参T更加灵活,但传入的通配符通常进行的是许多于具体类型无关的操作,如果涉及到具体类型相关的操作,以及返回值,还是需要使用泛型方法T。
当我们使用?号通配符的时候,只能调用与对象无关的方法,不能调用对象与类型有关的方法。因为直到外界使用才知道具体的类型是什么。
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)
最后
ActiveMQ消息中间件面试专题
- 什么是ActiveMQ?
- ActiveMQ服务器宕机怎么办?
- 丢消息怎么办?
- 持久化消息非常慢怎么办?
- 消息的不均匀消费怎么办?
- 死信队列怎么办?
- ActiveMQ中的消息重发时间间隔和重发次数吗?
ActiveMQ消息中间件面试专题解析拓展:
redis面试专题及答案
- 支持一致性哈希的客户端有哪些?
- Redis与其他key-value存储有什么不同?
- Redis的内存占用情况怎么样?
- 都有哪些办法可以降低Redis的内存使用情况呢?
- 查看Redis使用情况及状态信息用什么命令?
- Redis的内存用完了会发生什么?
- Redis是单线程的,如何提高多核CPU的利用率?
Spring面试专题及答案
- 谈谈你对 Spring 的理解
- Spring 有哪些优点?
- Spring 中的设计模式
- 怎样开启注解装配以及常用注解
- 简单介绍下 Spring bean 的生命周期
Spring面试答案解析拓展
高并发多线程面试专题
- 现在有线程 T1、T2 和 T3。你如何确保 T2 线程在 T1 之后执行,并且 T3 线程在 T2 之后执行?
- Java 中新的 Lock 接口相对于同步代码块(synchronized block)有什么优势?如果让你实现一个高性能缓存,支持并发读取和单一写入,你如何保证数据完整性。
- Java 中 wait 和 sleep 方法有什么区别?
- 如何在 Java 中实现一个阻塞队列?
- 如何在 Java 中编写代码解决生产者消费者问题?
- 写一段死锁代码。你在 Java 中如何解决死锁?
高并发多线程面试解析与拓展
jvm面试专题与解析
- JVM 由哪些部分组成?
- JVM 内存划分?
- Java 的内存模型?
- 引用的分类?
- GC什么时候开始?
JVM面试专题解析与拓展!
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
sleep 方法有什么区别?
- 如何在 Java 中实现一个阻塞队列?
- 如何在 Java 中编写代码解决生产者消费者问题?
- 写一段死锁代码。你在 Java 中如何解决死锁?
高并发多线程面试解析与拓展
[外链图片转存中…(img-Nox5hEuG-1713688923190)]
jvm面试专题与解析
- JVM 由哪些部分组成?
- JVM 内存划分?
- Java 的内存模型?
- 引用的分类?
- GC什么时候开始?
JVM面试专题解析与拓展!
[外链图片转存中…(img-tjjhNLQA-1713688923191)]
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!