《面试1v1》java泛型

news2024/12/25 23:42:01

我是 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.coloradmin.cn/o/455942.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

真正的进步,是创业者和员工的共同进步

再伟大的事业&#xff0c;也是由人一点一滴创造出来的。 人&#xff0c;是企业中最基础的存在&#xff0c;下层基础决定上层建筑&#xff0c;管理公司企业&#xff0c;也是人与人之间的交流问题。 创业十余年&#xff0c;与市场打交道&#xff0c;也与人打交道。 对外&#x…

ArduPilot Kakute F7 AIO DIYF450 之GPS配置

ArduPilot Kakute F7 AIO DIYF450 之GPS配置 1. 源由2. 步骤2.1 模块预测试2.2 物理连接2.3 UART配置2.4 Compass使能2.5 GPS使能2.6 校准Compass 3. GPS & Compass配置效果3.1 Mission Planner界面3.2 QGroundControl界面3.3 ArduPilot配置修改 4. 参考资料 1. 源由 之前…

选址-路径问题(Location-Routing Problem, LRP)

今天为大家介绍的是选址-路径问题(Location-Routing Problem, LRP)&#xff0c;首先上目录 目录 问题简介 基础模型、扩展问题及应用 算法 参考文献 1 问题简介 为了更好地了解这个问题&#xff0c;我们不妨当一波老板。 想象一下我们是经营一家口罩生产企业的老板&am…

RocketMQ基本概念

RocketMQ 一 引言 Message Queue&#xff08;消息 队列&#xff09;&#xff0c;从字⾯上理解&#xff1a;⾸先它是⼀个队列。先进先出的数据结构——队列。消息队列就是所谓的存放消息的队列。 消息队列解决的不是存放消息的队列的⽬的&#xff0c;解决的是通信问题&#x…

C语言实现顺序表--数据结构

魔王的介绍&#xff1a;&#x1f636;‍&#x1f32b;️一名双非本科大一小白。魔王的目标&#xff1a;&#x1f92f;努力赶上周围卷王的脚步。魔王的主页&#xff1a;&#x1f525;&#x1f525;&#x1f525;大魔王.&#x1f525;&#x1f525;&#x1f525; ❤️‍&#x1…

项目管理中引入PMO的应用研究——以H研究所为例

摘 要 本文从项目管理办公室&#xff08;PMO&#xff09;的基本内涵出发&#xff0c;探讨了PMO在以“项目”为主要工作组织方式的H研究所应用过程中发挥的作用、具有的优势、取得的成效与存在的不足&#xff0c;从而实现为企业培养专业的项目经理团队&#xff0c;为业务部门定…

Ubuntu20.04安装CUDA和CUDNN

CUDA是GPU深度学习的运行库&#xff0c;那么cuDNN就是训练加速工具&#xff0c;两者要相互配合使用&#xff0c;所以一般机器学习需要训练引擎(tensorflow-gpu) CUDA cuDNN使用。想不安装cuDNN是不可以的&#xff0c;而且cuDNN版本要和CUDA版本相互搭配。 1、前置工作 查看…

最新动态 | 大势智慧参加广东省应急测绘保障与安全生产演练

4月20日&#xff0c;2023年度广东省应急测绘保障与安全生产演练在台山市赤溪镇鱼塘湾举行。本次演练由广东自然资源厅主办&#xff0c;广东省国土资源测绘院、江门市自然资源局和台山市人民政府承办。在省市各指导单位与参演单位的多方协同与指挥下&#xff0c;应急测绘保障与安…

常用PLC学习资料下载地址

常见PLC的资料一般在官网都可以找到&#xff0c;今天整理一下&#xff0c;把西门子、三菱、欧姆龙、汇川四家品牌的官方下载地址直接贴出来供大家直接使用。 1、汇川技术官方网站 汇川技术 - 推进工业文明 共创美好生活 (inovance.com)https://www.inovance.com/2、汇川技术资料…

TCP重传、滑动窗口、流量控制、拥塞控制

目录 重传机制 #超时重传 SACK 方法 Duplicate SACK 滑动窗口 流量控制 窗口关闭 拥塞控制 慢启动 拥塞避免算法 拥塞发生 快速恢复 重传机制 TCP 实现可靠传输的方式之一&#xff0c;是通过序列号与确认应答。 在 TCP 中&#xff0c;当发送端的数据到达接收主机时…

rancher部署flink集群

rancher版本&#xff1a;v2.6.8 k8s版本&#xff1a;v1.22.13rke2r1 flink集群版本&#xff1a;1.15.0 flink安装模式&#xff1a;session cluster 写在前面&#xff1a;因为参照官网的说明安装过程中出现了很多问题&#xff0c;特记录于此&#xff0c;避免后续重复踩坑 目…

FE_CSS 精灵图技术 字体图标 CSS三角

一个网页中往往会应用很多小的背景图像作为修饰&#xff0c;当网页中的图像过多时&#xff0c;服务器就会频繁地接收和发送请求图片&#xff0c;造成服务器请求压力过大&#xff0c;这将大大降低页面的加载速度。 因此&#xff0c;为了有效地减少服务器接收和发送请求的次数&a…

国内申请日本专利有哪些流程?

日本2004年修订的实用新型法已经于2005年4月1日生效&#xff0c;在日本&#xff0c;“专利”这一概念限于发明&#xff0c;实用新型和外观设计均不称为专利。实用新型授权后&#xff0c;就叫实用新型权&#xff0c;并不叫专利权。而且&#xff0c;发明、实用新型和外观设计是“…

掘金Tiktok电商比亚马逊刺激多了

鑫优尚电子商务&#xff1a;万亿跨境电商市场&#xff0c;聚光灯照在了Tiktok&#xff08;抖音海外版&#xff09;身上。 美国、巴西、俄罗斯、越南……2017年年末才正式出海的Tiktok&#xff0c;仅在2年后便覆盖了150个国家和地区&#xff0c;多次登顶App Store下载量首位。 …

使用 Vaex 处理具有 2 亿行的数据集

在这篇文章中,我们生成了 2 亿条时序人工数据,有 4 列,大小接近 12GB。使用 Pandas 库无法读取数据集并对其进行探索和可视化。与 pandas 相比,能够将字符串处理速度提高10-1000 倍。比spark快近十倍。 Pandas是用于数据科学案例研究的最受欢迎的库之一。它是探索性数据分…

rtthread默认网卡的操作

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

SAP CAP篇一:快速创建一个Service,基于Java的实现

这个博客上&#xff0c;还没有写过SAP技术栈的东西&#xff0c;这次开个头&#xff0c;写个最近研究SAP CAP的摸索过程。虽然SAP CAP&#xff08;Cloud Application Model&#xff09;关注在Cloud的开发&#xff0c;我这些文章里面还是偏重本地上的尝试。 文章目录 前置内容现在…

当Kotlin Flow与Channel相逢

Flow之所以用起来香&#xff0c;Flow便捷的操作符功不可没&#xff0c;而想要熟练使用更复杂的操作符&#xff0c;那么需要厘清Flow和Channel的关系。 本篇文章构成&#xff1a; 1. Flow与Channel 对比 1.1 Flow核心原理与使用场景 原理 先看最简单的Demo&#xff1a; fun…

编写 LuCI CBI 模型

编写 LuCI CBI 模型 CBI模型是描述UCI配置文件结构的Lua文件&#xff0c;并且CBI解析器将lua文件转为HTML呈现给用户 。 所有 CBI 模型文件都必须返回类型为luci.cbi.Map的对象。 CBI 模型文件的范围由 luci.cbi 模块的内容和 luci.i18n 的转换函数自动扩展。 CBI控件类型汇总 …

如何使用ESP32-CAM构建一个人脸识别系统

有许多人识别系统使用签名、指纹、语音、手部几何、人脸识别等来识别人&#xff0c;但除了人脸识别系统。 人脸识别系统不仅可以用于安全目的来识别公共场所的人员&#xff0c;还可以用于办公室和学校的考勤目的。 在这个项目中&#xff0c;我们将使用 ESP32-CAM 构建一个人脸识…