《面试1v1》java泛型

news/2024/4/20 23:57:34/文章来源:https://blog.csdn.net/qq_40374604/article/details/130340101

我是 javapub,一名 Markdown 程序员从👨‍💻,八股文种子选手。

面试官:小伙子,说实话,泛型这个机制一开始我也是一头雾水,搞不太明白它到底要解决什么问题。你能不能不那么书呆子,给我普普通通地讲一讲泛型?

候选人: 好嘞,我们来聊聊泛型。首先,泛型要解决的最主要的问题就是类型不安全。比如说,你有一个箱子,可以装任何东西:

public class Box {private Object obj;public void set(Object obj) {this.obj = obj;}public Object get() {return obj;}
}

然后你用它装了一个苹果:

Box b = new Box();
b.set(new Apple());

但是当你取出来的时候,是一个水果啊,你不知道是苹果还是香蕉,需要强转类型:

Apple a = (Apple) b.get();  // 强转,可能出现ClassCastException

这就是类型不安全,一旦强转错了类型,程序就GG了。
泛型来了之后,情况就不一样了。我们可以这样定义箱子:

public class Box<T> {  // <T>就是类型参数private T obj;public void set(T obj) {this.obj = obj;}public T get() {return obj;}
}

然后在用的时候,指定T的实际类型,比如:

Box<Apple> b = new Box<Apple>(); 
b.set(new Apple());
Apple a = b.get();  // 不需要强转,类型安全!

所以泛型最大的好处就是让代码类型安全,不再需要强制类型转换,避免ClassCastException异常,让代码更健壮。它把类型检查的工作从运行时提前到了编译时。

面试官:哇,原来如此!讲解的真的很通俗易懂,我都明白了!那泛型中最容易搞混的两个概念是什么?

面试官:最容易搞混的两个概念,应该是类型参数和实际类型参数吧?

候选人: 对的,这两个概念容易混淆。我们再举个例子:

public class Box<T> {   // <T>就是类型参数private T obj;
}Box<Apple> b = new Box<>();   // Apple就是实际类型参数

类型参数T是在定义泛型类Box时使用的,代表一个未知的类型。我们不知道使用者会替换成什么类型,所以用T表示。
而实际类型参数Apple是在实例化Box时实际替换类型参数T的类型。它给T一个明确的类型,用于这次实例化。
所以类型参数是个未知的类型占位符,实际类型参数是替换类型参数的具体类型。理解了这两个概念的区别,泛型的很多地方就不会再混淆了。

面试官:说的太好了,我都不好意思问你其他的了!那最后两点疑问,1)为啥泛型类不能有静态方法?2)类型擦除是干嘛的?

候选人: 好的,两个很好的疑问:
1)泛型类不能有静态方法的原因是因为静态方法在类加载的时候就被创建,而泛型类在实例化的时候才能确定类型参数的实际类型。这时候静态方法已经创建完了,无法使用这个实际类型,所以编译器不允许这么做。
2)类型擦除就是编译器删除所有与类型参数相关的信息,并替换为上限(通常是Object类型)的过程。因为Java在1.5之前并没有泛型的概念,所以编译器会把所有的泛型类型全部擦除掉,在运行时期间不会存在任何泛型类型的参数信息。这也是为什么泛型类不能有基本类型的参数的原因。
类型擦除有利有弊,好处是可以在1.5之前的VM上运行泛型代码,坏处是导致些许运行期间的效率损失,因为擦除后所有的类型参数都被替换为Object类型。不过这点性能损失在大部分情况下可以忽略。

面试官:太棒了,你的解释简直让人眼前一亮!真的学到很多,谢谢你的精彩讲解!

候选人: 谢谢面试官的夸奖,我也在这个过程中对泛型有了更深的理解,非常高兴能与你进行这次交流与探讨。

面试官:在聊了泛型这么多后,还有些细节想问一下:

1. 泛型中<?>和<? extends T>分别代表什么含义?

候选人: <?>代表一个未知类型的通配符,可以用在类型参数的位置,表示接受任何类型。比如:

public void print(Box<?> box) {...
}

这个方法可以传递任何类型的Box进来,因为<?>可以匹配任何类型。
而<? extends T>表示从T类型到其子类型之间的某种类型,它代表的上界类型可能是T,也可能是T的子类型。比如:

public void print(Box<? extends Fruit> box) {...
}

这个方法可以传递Box或者Box进来,因为Apple和Orange都是Fruit的子类。但不能传Box,因为Fruit的子类型不包括Object。
所以<?>表示全类型通配,而<? extends T>表示从T到子类型的范围内的某种类型,具有上界的语义。

2. 泛型方法和泛型类有什么区别?

泛型方法是在普通类中定义带类型参数的方法,而泛型类是在定义类本身时指定类型参数。比如:
泛型方法:

public class Box {public <T> void print(T t) {...}
} 

泛型类:

public class Box<T> {private T t;... 
}

主要区别在于泛型类的类型参数可以用在整个类的方法和属性上,而泛型方法的类型参数只在这个方法内有效。泛型方法更灵活,可以在非泛型类上使用。
除此之外,泛型方法可以有static修饰符,可以在静态方法内使用类型参数。而泛型类不能有静态方法和静态属性,原因和前面说的类型擦除有关。

3. 泛型的上下限是什么?使用场景又是什么?

泛型的上限是<? extends T>,表示从T到子类型的范围;下限是<? super T>,表示从T到父类型的范围。
上限的使用场景是当需要获取T的子类型对象时,比如从集合中取出元素。下限的使用场景是当需要添加T的父类型对象时,比如往集合中添加元素。

Box<? extends Fruit> box1;   // 放入Apple、Orange等
box1.add(new Apple());      // 只能添加Fruit的子类型  Box<? super Fruit> box2;    // 放入Fruit、Food等 
box2.add(new Food());      // 只能添加Fruit的父类型   

所以上下限主要是为了在广泛限制类型的同时,也允许满足某些使用场景的需要,使得泛型更加灵活实用。

面试官:泛型真的有些复杂,但你解释的很通俗易懂,我都差不多明白了。最后两个小问题:

1. 泛型中的边界是干嘛的?

边界是对类型参数指定的约束,目的是限制类型参数能被替换的实际类型。比如,我们可以这样定义一个泛型方法:

public <T extends Number> void print(T t) {System.out.println(t.intValue());
}

这里我们指定T必须是Number或其子类型,如果调用时用String类型替换T,则会编译错误,因为String不符合约束。
边界有两种形式:

  • 类名或者接口名,例如T extends Number,表示T必须是Number类型或其子类
  • 另一个类型参数,例如<T, S extends T>,表示S必须是T或其子类型
    所以边界的作用就是限制类型参数可以替换的实际类型,确保在方法中可以正常使用某些操作,避免因为替换错误类型导致的运行错误。

2. 泛型中通配符<?>和无边界的<?>有什么区别?

无边界的<?>表示任何类型,它没有任何限制,可以理解为,T可以替换为任何类型。 而<?>通配符有些微的区别,它表示“未知类型”,也没有具体的类型边界,但它只能在“读”的场景使用,不能在“写”的场景使用。因为编译器无法确定它到底是哪种类型。
举个例子:

public void print(Box< ?> box) {  // 读操作,ok...
}public void add(Box< ?> box, Object o) { // 写操作,编译错误box.set(o);  
}

所以无边界的<?>可以出现在读和写的操作中,而<?>通配符只能在读操作场景使用,这是两者的主要区别。通常在像泛型方法的定义中,使用无边界的<?>会更灵活,而在一些读操作的泛型方法中,使用<?>通配符可以更广泛的匹配不同的Box类型。

面试官:真是一个很细致的区分,我以前也常常搞不清这两者的差别,你的解释让我受益匪浅!谢谢你将这些泛型的概念讲解的如此清晰和深入,我对泛型也有了更全面的认识。真是一个非常愉快的交流过程!

候选人: 非常高兴能帮到您!我自己在准备和回答的过程中,也对泛型有了更深刻的理解,这种问答的形式确实是学习的好方法。谢谢面试官的精彩问题,让这个过程变得非常有价值。我也非常欣赏这次交流,收获颇丰,祝面试官有一个美好的一天!

在这里插入图片描述

最近我在更新《面试1v1》系列文章,主要以场景化的方式,讲解我们在面试中遇到的问题,致力于让每一位工程师拿到自己心仪的offer,感兴趣可以关注JavaPub追更!

🎁目录合集:

Gitee:https://gitee.com/rodert/JavaPub

GitHub:https://github.com/Rodert/JavaPub

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_102599.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何测试信号源或者发射机的回波损耗

信用源或者发射机的return loss测试过程 1.用网分线缆的第一步就是看线的抖动情况&#xff0c;后面还是要多注意 经过一系列排查后&#xff0c;选用两个抖动比较小的线缆&#xff0c;然后开始测试另外一台仪器。 2.检查测试仪器的输出功率&#xff0c;见图1 打开信号源或者发射…

可以一学的代码优化小技巧:减少if-else冗余

前言 if-else 语句对于程序员来说&#xff0c;是非常非常熟悉的一个判断语句&#xff0c;我们在日常开发和学习中都经常看见它&#xff0c;if-else语句主要用于需要做出选择的地方进行判断&#xff0c;这里就不再赘述if-else语法和特点了。 ​ 我们在写代码&#xff08;如图下…

PC1 - 搭建项目

先看路由&#xff0c;可以查看功能模块划分。熟悉什么看什么 router文件夹下routerConfig.tsx 配置路由&#xff0c;创建模块文件&#xff08;写好内容模块&#xff09;&#xff0c;lazy可懒加载导入。App.tsx配置一级路由&#xff0c;配置二级路由出口 { path:/, element: …

【记录】FFmpeg|超大视频本地有损压缩,500MB变5MB(支持 Windows、Linux、macOS)

参考&#xff1a; 如何将一分钟长的1080p视频压缩至5MB以内&#xff1f;-知乎-滔滔清风近期HEVC扩展备用安装方法-B站-悲剧天下 总共三个步骤&#xff0c;安装FFmpeg、运行指令、打开视频。 亲测 500MB 变 5MB。 1 安装FFmpeg 对于不需要看教程可以自行完成安装的同学们&…

7. 堆的简单学习

7. 堆 7.1 堆的定义 堆是计算机科学中一类特殊的数据结构的统称&#xff0c;堆通常可以被看做是一棵完全二叉树的数组实现。 堆的特性&#xff1a; 它是完全二叉树&#xff0c;除了树的最后一层结点不需要是满的&#xff0c;其它的每一层从左到右都是满的&#xff0c;如果最…

使用python实现自动点击功能

猜你感兴趣 使用Pyqt5玩转ChatGpt内网文件共享服务快速搭建私有pip镜像源python设计模式-创建型模式docker搭建私有git服务器&#xff0c;项目备份和迁移redis持久化方案 被测点击界面 新建counter.html添加下面代码并保存,使用编辑器或浏览器打开 <!DOCTYPE html> &l…

23.4.21总结

正则表达式 正则表达式使用单个字符串来描述、匹配一系列匹配某个句法规则的字符串&#xff0c;通常被用来检索、替换那些符合某个模式&#xff08;规则&#xff09;的文本。 正则表达式是一种对字符串操作的一种逻辑公式&#xff0c;就是用事先定义好的一些特定字符、及这些…

深度学习 - 42.特征交叉与 SetNET、Bilinear Interaction 与 FiBiNet

目录 一.引言 二.摘要 - ABSTRACT 三.介绍 - INTRODUCTION 四.相关工作 - RELATED WORK 1.因式分解机及其变体 - Factorization Machine and Its relevant variants 2. 基于深度学习的点击率模型 - Deep Learning based CTR Models 3.SENET Module 五.FiBiNet Model 1…

【C++】哈希的应用:位图和布隆过滤器

目录 1. 位图1.1 位图的概念1.2 位图的结构1.3 位图的实现 2. 布隆过滤器2.1 概念2.2 结构2.3 布隆过滤器的实现 1. 位图 1.1 位图的概念 &#x1f4ad;位图&#xff08;bitset&#xff09;是一种基于哈希思想设计的数据结构&#xff0c;其功能主要用于判断数据是否已存在。适…

来使用分支语句和循环语句实现一个小游戏吧(猜数字游戏)

猜数字游戏 1.代码展示2.菜单设计3.主函数部分3.随机数设计 所属专栏&#xff1a;C语言 博主首页&#xff1a;初阳785 代码托管&#xff1a;chuyang785 感谢大家的支持&#xff0c;您的点赞和关注是对我最大的支持&#xff01;&#xff01;&#xff01; 博主也会更加的努力&am…

rtthread默认网卡的操作

设置网卡优先级 在 RT-Thread 操作系统中&#xff0c;可以通过修改网卡的优先级来设置默认网卡。优先级越高的网卡会被优先选择为默认网卡。 下面介绍一些设置默认网卡优先级的方法&#xff1a; 在 RT-Thread 的网络配置文件 rtconfig.h 中&#xff0c;可以通过修改 NETIF_P…

Jmeter5.1.1报错:java.net.BindException: Address already in use: connect

Jmeter5.1.1报错&#xff1a;java.net.BindException: Address already in use: connect 原因&#xff1a;从网上找到资料&#xff1a;端口占用 Windows提供给TCP/IP链接的端口为 1024-5000&#xff0c;并且要四分钟来循环回收它们&#xff0c;就导致我们在短时间内跑大量的请…

把ChatGPT训练成你的得力助手

在调教chatgpt时&#xff0c;我们大部分的时候都需要一个好的学术翻译官&#xff0c;但是在他成为学术翻译官之前我们有很多规定要说明&#xff0c;比如不用回答我的问题&#xff0c;不用计算公式等。我将以下命令要求集成&#xff0c;在使用的时候只需要你发给它这段话&#x…

如何评估小程序开发费用:从项目规模到技术需求

作为一种越来越受欢迎的移动应用&#xff0c;小程序的开发费用是许多企业和个人考虑的重要因素之一。但是&#xff0c;要确定小程序开发费用并不是一件容易的事情&#xff0c;因为它涉及到多个因素&#xff0c;从项目规模到技术需求。 项目规模 小程序开发的费用通常与项目规…

Linux部署人大金仓(Kingbase8)

陈老老老板&#x1f9b8; &#x1f468;‍&#x1f4bb;本文专栏&#xff1a;国产数据库-人大金仓&#xff08;kingbase8&#xff09;&#xff08;主要讲一些人大金仓数据库相关的内容&#xff09; &#x1f468;‍&#x1f4bb;本文简述&#xff1a;本文讲一下LInux上部署人大…

Vue+Echarts 项目演练(中)后台数据接口的创建

全局引用Echarts与axios 后台接口创建express路由 api接口数据创建 全局引用Echarts与axios vue3.0的挂载方式&#xff1a;使用Provide/Inject依赖注入&#xff0c;将替代vue2中在原型链上挂载一些属性在app.vue中使用provider来给后代们提供数据 <script> import { p…

经典数据结构之2-3树

2-3树定义 2-3树&#xff0c;是最简单的B-树&#xff0c;其中2、3主要体现在每个非叶子节点都有2个或3个子节点&#xff0c;B-树即是平衡树&#xff0c;平衡树是为了解决不平衡树查询效率问题&#xff0c;常见的二叉平衡书有AVL树&#xff0c;它虽然提高了查询效率&#xff0c…

深入JVM了解Java对象实例化过程

文章目录 一、对象创建方式二、对象产生步骤1、判断对象是否已经加载、链接、初始化2、为对象分配内存空间3、处理并发问题3.1 TLAB 4、初始化零值5、完善对象内存布局的信息6、调用对象的实例化方法 <init>7、总结 三、对象的内存布局1、对象头1.1 运行时元数据&#xf…

详解树与二叉树的概念,结构,及实现(上篇)

目录 一&#xff0c; 树 1.2 树的相关概念 1.3 树的表示 1.4 树在实际中的运用&#xff08;表示文件系统的目录树结构&#xff09; 二&#xff0c; 二叉树 2.1二叉树概念 三&#xff0c;特殊的二叉树 1. 满二叉树 2. 完全二叉树 3. 1 二叉树的性质 3. 2 二叉树的存储…

【速卖通】 AliExpress(速卖通)关键词搜索结果采集

采集场景 在AliExpress(速卖通) 首页中 http://www.aliexpress.com 中输入关键词&#xff0c;采集关键词搜索后得到的商品列表信息。 采集字段 关键词、标题、商品id、商品图片地址、商品详情链接、价格、免费退送货、星级、已出售数量、店铺名 采集结果 采集结果可导出为E…