面渣逆袭Java基础篇V2.1




⾯渣逆袭Java基础篇V2-让天下所有的⾯渣都能逆袭
前⾔ No. 1 / 95
⾯渣逆袭Java基础篇V2-让天下所有的⾯渣都能逆袭
前⾔
2.3 万字 68 张⼿绘图,详解 56 道 Java 基础⾯试⾼频题(让天下没有难背的⼋股),⾯渣背会这些⼋股⽂,这次
吊打⾯试官,我觉得稳了(⼿动 dog)。整理:沉默王⼆,戳转载链接,原作者:星球嘉宾三分恶,戳原⽂链接。
亮⽩版本更适合拿出来打印,这也是很多学⽣党喜欢的⽅式,打印出来背诵的效率会更⾼。
2024 年 12 ⽉ 23 ⽇开始着⼿第⼆版更新。
对于⾼频题,会标注在《Java ⾯试指南(付费)》中出现的位置,哪家公司,原题是什么,并且会加 ,⽬
录⼀⽬了然;如果你想节省时间的话,可以优先背诵这些题⽬,尽快做到知彼知⼰,百战不殆。
区分⼋股精华回答版本和原理底层解释,让⼤家知其然知其所以然,同时⼜能做到⾯试时的⾼效回答。
结合项⽬(技术派、pmhub)来组织语⾔,让⾯试官最⼤程度感受到你的诚意,⽽不是机械化的背诵。
No. 2 / 95
⾯渣逆袭Java基础篇V2-让天下所有的⾯渣都能逆袭
修复第⼀版中出现的问题,包括球友们的私信反馈,⽹站留⾔区的评论,以及 GitHub 仓库中的 issue,让这
份⾯试指南更加完善。
增加⼆哥编程星球的球友们拿到的⼀些 offer,对⾯渣逆袭的感谢,以及对简历修改的⼀些认可,以此来激励
⼤家,给⼤家更多信⼼。
优化排版,增加⼿绘图,重新组织答案,使其更加⼝语化,从⽽更贴近⾯试官的预期。
码,关注⼆哥的公众号,回复【222】即可拉取最新版本。
No. 3 / 95
⾯渣逆袭Java基础篇V2-让天下所有的⾯渣都能逆袭
当然了,请允许我的⼀点点私⼼,那就是星球的 PDF 版本会⽐公众号早⼀个⽉时间,毕竟星球⽤户都付费过了,
我有必要让他们先享受到⼀点点福利。相信⼤家也都能理解,毕竟在线版是免费的,CDN、服务器、域名、OSS
等等都是需要成本的。
更别说我付出的时间和精⼒了,⼤家觉得有帮助还请给个⼝碑,让你身边的同事、同学都能受益到。
我把⼆哥的 Java 进阶之路、JVM 进阶之路、并发编程进阶之路,以及所有⾯渣逆袭的版本都放进来了,涵盖 Java
基础、Java集合、Java并发、JVM、Spring、MyBatis、计算机⽹络、操作系统、MySQL、Redis、RocketMQ、分
布式、微服务、设计模式、Linux 等 16 个⼤的主题,共有 40 多万字,2000+张⼿绘图,可以说是诚意满满。
展示⼀下暗⿊版本的 PDF 吧,排版清晰,字体优雅,更加适合夜服,晚上看会更舒服⼀点。
No. 4 / 95
⾯渣逆袭Java基础篇V2-让天下所有的⾯渣都能逆袭
Java 概述
1. 什么是 Java?
Java 是⼀⻔⾯向对象的编程语⾔,由 Sun 公司的詹姆斯·⾼斯林团队于 1995 年推出。吸收了 C++ 语⾔中⼤量的优
点,但⼜抛弃了 C++ 中容易出错的地⽅,如垃圾回收、指针。
同时,Java ⼜是⼀⻔平台⽆关的编程语⾔,即⼀次编译,处处运⾏。
只需要在对应的平台上安装 JDK,就可以实现跨平台,在 Windows、macOS、Linux 操作系统上运⾏。
No. 5 / 95
⾯渣逆袭Java基础篇V2-让天下所有的⾯渣都能逆袭
多久开始学 Java 的?
我是从⼤⼀下学期开始学习 Java 的,当时已经学完了 C 语⾔,但苦于 C 语⾔没有很好的应⽤⽅向,就开始学习
Java 了,因为我了解到,绝⼤多数的互联⽹公司,包括银⾏、国企,后端服务都是⽤ Java 开发的,另外就是,
Java 的学习资料⾮常丰富,就业岗位和薪资待遇都⽐较理想。
于是就⼀边学,⼀边实战,先做了前后端分离的社区项⽬技术派,接触到了 Spring Boot、MyBatis-Plus、
MySQL、Redis、ElasticSearch、MongoDB、Docker、RabbitMQ 等⼀系列的 Java 技术栈。
后⾯⼜做了微服务项⽬ pmhub,接触到了 Spring Cloud、Nacos、Sentinel、Seata、SkyWalking 等相关技术
栈。
No. 6 / 95
⾯渣逆袭Java基础篇V2-让天下所有的⾯渣都能逆袭
平常⽤什么编程语⾔?
⼤⼀上先学习的 C 语⾔,⼤⼀下半学期开始学习 Java,中间还学过⼀些 Python 和 JavaScript,但整体的感受上来
说还是更喜欢 Java。
因为它可以做的事情太多了,既可以⽤它来写 Web 后端服务,也可以⽤它来造⼀些轮⼦,⽐如 MYDB 这个轮⼦,
就是⽤ Java 完成的,不进加深了我对 MySQL索引、事务、MVCC 的理解,还让我对 Java 的 NIO、多线程、JVM
有了更深的了解。
平时是怎么学 Java 的?
No. 7 / 95
⾯渣逆袭Java基础篇V2-让天下所有的⾯渣都能逆袭
⼀开始,主要是跟着学校的课程⾛,⼊⻔后感觉课程已经满⾜不了我的求知欲了,于是就开始在 B 站和 GitHub 上
找⼀些优质的视频资源和开源知识库来学习。
⽐如说《Java 进阶之路》就很适合我的⼝味,从 Java 的语法、数组&字符串、OOP、集合框架、Java IO、异常处
理、⽹络编程、NIO、并发编程、JVM 等,都有详细的讲解,还有很多⼿绘图和代码实例,我都跟着动⼿⼀步步实
现了,感觉收获很⼤。
后来⼜读了⼀遍《Java 编程思想》、《Effective Java》,周志明⽼师的《深⼊理解 Java 虚拟机》,以及 JDK 的⼀
些源码,⽐如说 String、HashMap,还有字节码⽅⾯的知识。
再后来就开始做实战项⽬ MYDB、技术派、PmHub,算是彻底掌握 Java 项⽬的开发流程了。
Java 语⾔和 C 语⾔有哪些区别?
Java 是⼀种跨平台的编程语⾔,通过在不同操作系统上安装对应版本的 JVM 以实现“⼀次编译,处处运⾏”的⽬的。
⽽ C 语⾔需要在不同的操作系统上重新编译。
Java 实现了内存的⾃动管理,⽽ C 语⾔需要使⽤ malloc 和 free 来⼿动管理内存。
1. Java ⾯试指南(付费)收录的携程⾯经同学 10 Java 暑期实习⼀⾯⾯试原题:多久开始学 java 的
2. Java ⾯试指南(付费)收录的 ⼩公司⾯经合集好未来测开⾯经同学 3 测开⼀⾯⾯试原题:平常⽤什么
编程语⾔
3. Java ⾯试指南(付费)收录的国企零碎⾯经同学 9 ⾯试原题:平时是怎么学 Java 的?
4. Java ⾯试指南(付费)收录的 TP-LINK 联洲同学 5 技术⼀⾯⾯试原题:⽇常⽤的编程语⾔?
5. Java ⾯试指南(付费)收录的荣耀⾯经同学 4 ⾯试原题:接触过那些语⾔?
2.Java 语⾔有哪些特点?
No. 8 / 95
⾯渣逆袭Java基础篇V2-让天下所有的⾯渣都能逆袭
Java 语⾔的特点有:
①、⾯向对象,主要是封装,继承,多态。
②、平台⽆关性,“⼀次编写,到处运⾏”,因此采⽤ Java 语⾔编写的程序具有很好的可移植性。
③、⽀持多线程。C++ 语⾔没有内置的多线程机制,因此必须调⽤操作系统的 API 来完成多线程程序设计,⽽ Java
却提供了封装好多线程⽀持;
④、⽀持 JIT 编译,也就是即时编译器,它可以在程序运⾏时将字节码转换为热点机器码来提⾼程序的运⾏速度。
3.JVM、JDK 和 JRE 有什么区别?
JVM:也就是 Java 虚拟机,是 Java 实现跨平台的关键所在,不同的操作系统有不同的 JVM 实现。JVM 负责将 Java
字节码转换为特定平台的机器码,并执⾏。
JRE:也就是 Java 运⾏时环境,包含了运⾏ Java 程序所必需的库,以及 JVM。
JDK:⼀套完整的 Java SDK,包括 JRE,编译器 javac、Java ⽂档⽣成⼯具 javadoc、Java 字节码⼯具 javap 等。
为开发者提供了开发、编译、调试 Java 程序的⼀整套环境。
简单来说,JDK 包含 JRE,JRE 包含 JVM。
1. Java ⾯试指南(付费)收录的华为⾯经同学 9 Java 通⽤软件开发⼀⾯⾯试原题:JRE 与 JDK 的区别,
JDK 多了哪些东⻄,既安装了 JRE ⼜安装了 JDK,可以利⽤ JDK 做什么事情?
4.说说什么是跨平台?原理是什么
所谓的跨平台,是指 Java 语⾔编写的程序,⼀次编译后,可以在多个操作系统上运⾏。
No. 9 / 95
⾯渣逆袭Java基础篇V2-让天下所有的⾯渣都能逆袭
原理是增加了⼀个中间件 JVM,JVM 负责将 Java 字节码转换为特定平台的机器码,并执⾏。
5.什么是字节码?采⽤字节码的好处是什么?
所谓的字节码,就是 Java 程序经过编译后产⽣的 .class ⽂件。
Java 程序从源代码到运⾏需要经过三步:
编译:将源代码⽂件 .java 编译成 JVM 可以识别的字节码⽂件 .class
解释:JVM 执⾏字节码⽂件,将字节码翻译成操作系统能识别的机器码
执⾏:操作系统执⾏⼆进制的机器码
6.为什么有⼈说 Java 是“编译与解释并存”的语⾔?
编译型语⾔是指编译器针对特定的操作系统,将源代码⼀次性翻译成可被该平台执⾏的机器码。
解释型语⾔是指解释器对源代码进⾏逐⾏解释,解释成特定平台的机器码并执⾏。
举个例⼦,我想读⼀本国外的⼩说,我有两种选择:
找个翻译,等翻译将⼩说全部都翻译成汉语,⼀次性读完。
找个翻译,翻译⼀段我读⼀段,慢慢把书读完。
之所以有⼈说 Java 是“编译与解释并存”的语⾔,是因为 Java 程序需要先将 Java 源代码⽂件编译字节码⽂件,再解
释执⾏。
No. 10 / 95
⾯渣逆袭Java基础篇V2-让天下所有的⾯渣都能逆袭
基础语法
7. Java 有哪些数据类型?
推荐阅读 1:Java 的数据类型
推荐阅读 2:⾯试官竟然问我这么简单的题⽬:Java 中 boolean 占多少字节?
Java 的数据类型可以分为两种:基本数据类型和引⽤数据类型。
No. 11 / 95
⾯渣逆袭Java基础篇V2-让天下所有的⾯渣都能逆袭
基本数据类型有:
①、数值型
整数类型(byte、short、int、long)
浮点类型(float、double)
②、字符型(char)
③、布尔型(boolean)
它们的默认值和占⽤⼤⼩如下所示:
数据类型 默认值 ⼤⼩
boolean false 1 字节或 4 字节
char 'u0000' 2 字节
byte 0 1 字节
short 0 2 字节
int 0 4 字节
long 0L 8 字节
float 0.0f 4 字节
double 0.0 8 字节
No. 12 / 95
⾯渣逆袭Java基础篇V2-让天下所有的⾯渣都能逆袭
引⽤数据类型有:
类(class)
接⼝(interface)
数组( [] )
boolean 类型实际占⽤⼏个字节?
推荐阅读:⼆哥的 Java 进阶之路:基本数据类型篇
这要依据具体的 JVM 实现细节。Java 虚拟机规范中,并没有明确规定 boolean 类型的⼤⼩,只规定了 boolean 类
型的取值 true 或 false。
boolean: The boolean data type has only two possible values: true and false. Use this data type for
simple flags that track true/false conditions. This data type represents one bit of information, but its
"size" isn't something that's precisely defined.
我本机的 64 位 JDK 中,通过 JOL ⼯具查看单独的 boolean 类型,以及 boolean 数组,所占⽤的空间都是 1 个字
节。
给Integer最⼤值+1,是什么结果?
当给 Integer.MAX_VALUE 加 1 时,会发⽣溢出,变成 Integer.MIN_VALUE。
int maxValue = Integer.MAX_VALUE;
System.out.println("Integer.MAX_VALUE = " + maxValue); // Integer.MAX_VALUE = 2147483647
System.out.println("Integer.MAX_VALUE + 1 = " + (maxValue + 1)); // Integer.MAX_VALUE + 1
= -2147483648
// ⽤⼆进制来表示最⼤值和最⼩值
System.out.println("Integer.MAX_VALUE in binary: " + Integer.toBinaryString(maxValue));
// Integer.MAX_VALUE in binary: 1111111111111111111111111111111
System.out.println("Integer.MIN_VALUE in binary: " +
Integer.toBinaryString(Integer.MIN_VALUE)); // Integer.MIN_VALUE in binary:
10000000000000000000000000000000
这是因为 Java 的整数类型采⽤的是⼆进制补码表示法,溢出时值会变成最⼩值。
Integer.MAX_VALUE 的⼆进制表示是 01111111 11111111 11111111 11111111(32 位)。
加 1 后结果变成 10000000 00000000 00000000 00000000,即 -2147483648(Integer.MIN_VALUE)。
1. Java ⾯试指南(付费)收录的⽤友⾦融⼀⾯原题:Java 有哪些基本数据类型?
2. Java ⾯试指南(付费)收录的快⼿⾯经同学 1 部⻔主站技术部⾯试原题:Java 的基础数据类型,分别
占多少字节
3. Java ⾯试指南(付费)收录的 360 ⾯经同学 3 Java 后端技术⼀⾯⾯试原题:java 的基本类型
4. Java ⾯试指南(付费)收录的⽤友⾯试原题:说说 8 ⼤数据类型?
5. Java ⾯试指南(付费)收录的快⼿同学 2 ⼀⾯⾯试原题:给Integer最⼤值+1,是什么结果
8.⾃动类型转换、强制类型转换了解吗?
No. 13 / 95
⾯渣逆袭Java基础篇V2-让天下所有的⾯渣都能逆袭
推荐阅读:聊聊基本数据类型的转换
当把⼀个范围较⼩的数值或变量赋给另外⼀个范围较⼤的变量时,会进⾏⾃动类型转换;反之,需要强制转换。
这就好像,⼩杯⾥的⽔倒进⼤杯没问题,但⼤杯的⽔倒进⼩杯就可能会溢出。
①、 float f=3.4 ,对吗?
不正确。3.4 默认是双精度,将双精度赋值给浮点型属于下转型(down-casting,也称窄化)会造成精度丢失,因
此需要强制类型转换 float f =(float)3.4; 或者写成 float f =3.4F
②、 short s1 = 1; s1 = s1 + 1; 对吗? short s1 = 1; s1 += 1; 对吗?
short s1 = 1; s1 = s1 + 1; 会编译出错,由于 1 是 int 类型,因此 s1+1 运算结果也是 int 型,需要强制转换
类型才能赋值给 short 型。
⽽ short s1 = 1; s1 += 1; 可以正确编译,因为 s1+= 1; 相当于 s1 = (short(s1 + 1); 其中有隐含的强制
类型转换。
9.什么是⾃动拆箱/装箱?
装箱:将基本数据类型转换为包装类型,例如 int 转换为 Integer。
拆箱:将包装类型转换为基本数据类型。
No. 14 / 95
⾯渣逆袭Java基础篇V2-让天下所有的⾯渣都能逆袭
举例:
Integer i = 10; //装箱
int n = i; //拆箱
再换句话说,i 是 Integer 类型,n 是 int 类型;变量 i 是包装器类,变量 n 是基本数据类型。
1. Java ⾯试指南(付费)收录的⽤友⾯试原题:对应有哪些包装器类?
2. Java ⾯试指南(付费)收录的京东⾯经同学 8 ⾯试原题:int和Integer的区别
10.&和&&有什么区别?
& 是 逻辑与 。
&& 是短路与运算。逻辑与跟短路与的差别是⾮常⼤的,虽然⼆者都要求运算符左右两端的布尔值都是 true,整个
表达式的值才是 true。
&& 之所以称为短路运算是因为,如果 && 左边的表达式的值是 false,右边的表达式会直接短路掉,不会进⾏运
算。
例如在验证⽤户登录时判定⽤户名不是 null ⽽且不是空字符串,应当写为 username != null &&
!username.equals("") ,⼆者的顺序不能交换,更不能⽤ & 运算符,因为第⼀个条件如果不成⽴,根本不能进
⾏字符串的 equals ⽐较,会抛出 NullPointerException 异常。
注意:逻辑或运算符( | )和短路或运算符( || )的差别也是类似。
2024 年 12 ⽉ 23 ⽇ 更新到这⾥。
11.switch 语句能否⽤在 byte/long/String 类型上?
Java 5 以前 switch(expr) 中,expr 只能是 byte、short、char、int。
从 Java 5 开始,Java 中引⼊了枚举类型, expr 也可以是 enum 类型。
从 Java 7 开始,expr 还可以是字符串,但是⻓整型在⽬前所有的版本中都是不可以的。
12.break,continue,return 的区别及作⽤?
break 跳出整个循环,不再执⾏循环(结束当前的循环体)
continue 跳出本次循环,继续执⾏下次循环(结束正在执⾏的循环 进⼊下⼀个循环条件)
return 程序返回,不再执⾏下⾯的代码(结束当前的⽅法 直接返回)
No. 15 / 95
⾯渣逆袭Java基础篇V2-让天下所有的⾯渣都能逆袭
13.⽤效率最⾼的⽅法计算 2 乘以 8?
2 << 3 。位运算,数字的⼆进制位左移三位相当于乘以 2 的三次⽅。
14.说说⾃增⾃减运算?
在写代码的过程中,常⻅的⼀种情况是需要某个整数类型变量增加 1 或减少 1,Java 提供了⼀种特殊的运算符,⽤
于这种表达式,叫做⾃增运算符(++)和⾃减运算符(--)。
++和--运算符可以放在变量之前,也可以放在变量之后。
当运算符放在变量之前时(前缀),先⾃增/减,再赋值;当运算符放在变量之后时(后缀),先赋值,再⾃增/减。
例如,当 b = ++a 时,先⾃增(⾃⼰增加 1),再赋值(赋值给 b);当 b = a++ 时,先赋值(赋值给 b),再⾃
增(⾃⼰增加 1)。也就是,++a 输出的是 a+1 的值,a++输出的是 a 值。
⽤⼀句⼝诀就是:“符号在前就先加/减,符号在后就后加/减”。
看⼀下这段代码运⾏结果?
No. 16 / 95
⾯渣逆袭Java基础篇V2-让天下所有的⾯渣都能逆袭
int i = 1;
i = i++;
System.out.println(i);
答案是 1。有点离谱对不对。
对于 JVM ⽽⾔,它对⾃增运算的处理,是会先定义⼀个临时变量来接收 i 的值,然后进⾏⾃增运算,最后⼜将临时
变量赋给了值为 2 的 i,所以最后的结果为 1。
相当于这样的代码:
int i = 1;
int temp = i;
i++;
i = temp;
System.out.println(i);
这段代码会输出什么?
int count = 0;
for(int i = 0;i < 100;i++)
{
count = count++;
}
System.out.println("count = "+count);
答案是 0。
和上⾯的题⽬⼀样的道理,同样是⽤了临时变量,count 实际是等于临时变量的值。
int autoAdd(int count)
{
int temp = count;
count = count + 1;
return temp;
}
15.float 是怎么表示⼩数的?(补充)
2024 年 04 ⽉ 21 ⽇增补
推荐阅读:计算机系统基础(四)浮点数
float 类型的⼩数在计算机中是通过 IEEE 754 标准的单精度浮点数格式来表示的。
S:符号位,0 代表正数,1 代表负数;
M:尾数部分,⽤于表示数值的精度;⽐如说 ${1.25 * 2^2}$;1.25 就是尾数;
R:基数,⼗进制中的基数是 10,⼆进制中的基数是 2;
No. 17 / 95
⾯渣逆袭Java基础篇V2-让天下所有的⾯渣都能逆袭
E:指数部分,例如 $10^{-1}$ 中的 -1 就是指数。
这种表示⽅法可以将⾮常⼤或⾮常⼩的数值⽤有限的位数表示出来,但这也意味着可能会有精度上的损失。
单精度浮点数占⽤ 4 字节(32 位),这 32 位被分为三个部分:符号位、指数部分和尾数部分。
1. 符号位(Sign bit):1 位
2. 指数部分(Exponent):10 位
3. 尾数部分(Mantissa,或 Fraction):21 位
按照这个规则,将⼗进制数 25.125 转换为浮点数,转换过程是这样的:
1. 整数部分:25 转换为⼆进制是 11001;
2. ⼩数部分:0.125 转换为⼆进制是 0.001;
3. ⽤⼆进制科学计数法表示:25.125 = $1.001001 times 2^4$
符号位 S 是 0,表示正数;指数部分 E 是 4,转换为⼆进制是 100;尾数部分 M 是 1.001001。
使⽤浮点数时需要注意,由于精度的限制,进⾏数学运算时可能会遇到舍⼊误差,特别是连续运算累积误差可能会
变得显著。
对于需要⾼精度计算的场景(如⾦融计算),可能需要考虑使⽤ BigDecimal 类来避免这种误差。
1. Java ⾯试指南(付费)收录的帆软同学 3 Java 后端⼀⾯的原题:float 是怎么表示⼩数的
16.讲⼀下数据准确性⾼是怎么保证的?(补充)
2024 年 04 ⽉ 21 ⽇增补
在⾦融计算中,保证数据准确性有两种⽅案,⼀种使⽤ BigDecimal ,⼀种将浮点数转换为整数 int 进⾏计算。
No. 18 / 95
⾯渣逆袭Java基础篇V2-让天下所有的⾯渣都能逆袭
肯定不能使⽤ float 和 double 类型,它们⽆法避免浮点数运算中常⻅的精度问题,因为这些数据类型采⽤⼆进
制浮点数来表示,⽆法准确地表示,例如 0.1 。
BigDecimal num1 = new BigDecimal("0.1");
BigDecimal num2 = new BigDecimal("0.2");
BigDecimal sum = num1.add(num2);
System.out.println("Sum of 0.1 and 0.2 using BigDecimal: " + sum); // 输出 0.3,精确计算
在处理⼩额⽀付或计算时,通过转换为较⼩的货币单位(如分),这样不仅提⾼了运算速度,还保证了计算的准确
性。
int priceInCents = 199; // 商品价格199分
int quantity = 3;
int totalInCents = priceInCents * quantity; // 计算总价
System.out.println("Total price in cents: " + totalInCents); // 输出597分
1. Java ⾯试指南(付费)收录的字节跳动同学 7 Java 后端实习⼀⾯的原题:讲⼀下数据准确性⾼是怎么
保证的?
⾯向对象
17.⾯向对象和⾯向过程的区别?
⾯向过程是以过程为核⼼,通过函数完成任务,程序结构是函数+步骤组成的顺序流程。
⾯向对象是以对象为核⼼,通过对象交互完成任务,程序结构是类和对象组成的模块化结构,代码可以通过继承、
组合、多态等⽅式复⽤。
No. 19 / 95
⾯渣逆袭Java基础篇V2-让天下所有的⾯渣都能逆袭
在技术派实战项⽬中,像 VO、DTO 都是业务抽象后的对象实体类,⽽ Service、Controller 则是业务逻辑的实
现,这其实就是⾯向对象的思想。
1. Java ⾯试指南(付费)收录的快⼿同学 2 ⼀⾯⾯试原题:⾯向对象和⾯向过程的区别?
2. Java ⾯试指南(付费)收录的字节跳动同学 17 后端技术⾯试原题:⾯向对象 项⽬⾥有哪些⾯向对象的
案例
18. ⾯向对象编程有哪些特性?
推荐阅读:深⼊理解 Java 三⼤特性
⾯向对象编程有三⼤特性:封装、继承、多态。
No. 20 / 95
⾯渣逆袭Java基础篇V2-让天下所有的⾯渣都能逆袭
封装是什么?
封装是指将数据(属性,或者叫字段)和操作数据的⽅法(⾏为)捆绑在⼀起,形成⼀个独⽴的对象(类的实
例)。
class Nvshen {
private String name;
private int age;
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setAge(int age) {
this.age = age;
}
}
可以看得出,⼥神类对外没有提供 age 的 getter ⽅法,因为⼥神的年龄要保密。
No. 21 / 95
⾯渣逆袭Java基础篇V2-让天下所有的⾯渣都能逆袭
所以,封装是把⼀个对象的属性私有化,同时提供⼀些可以被外界访问的⽅法。
继承是什么?
继承允许⼀个类(⼦类)继承现有类(⽗类或者基类)的属性和⽅法。以提⾼代码的复⽤性,建⽴类之间的层次关
系。
同时,⼦类还可以重写或者扩展从⽗类继承来的属性和⽅法,从⽽实现多态。
class Person {
protected String name;
protected int age;
public void eat() {
System.out.println("吃饭");
}
}
class Student extends Person {
private String school;
public void study() {
System.out.println("学习");
}
}
Student 类继承了 Person 类的属性(name、age)和⽅法(eat),同时还有⾃⼰的属性(school)和⽅法
(study)。
什么是多态?
多态允许不同类的对象对同⼀消息做出响应,但表现出不同的⾏为(即⽅法的多样性)。
多态其实是⼀种能⼒——同⼀个⾏为具有不同的表现形式;换句话说就是,执⾏⼀段代码,Java 在运⾏时能根据对
象类型的不同产⽣不同的结果。
多态的前置条件有三个:
⼦类继承⽗类
⼦类重写⽗类的⽅法
⽗类引⽤指向⼦类的对象
//⼦类继承⽗类
class Wangxiaoer extends Wanger {
public void write() { // ⼦类重写⽗类⽅法
System.out.println("记住仇恨,表明我们要奋发图强的⼼智");
}
public static void main(String[] args) {
// ⽗类引⽤指向⼦类对象
Wanger wanger = new Wangxiaoer();
No. 22 / 95
⾯渣逆袭Java基础篇V2-让天下所有的⾯渣都能逆袭
wanger.write();
}
}
class Wanger {
public void write() {
System.out.println("沉默王⼆是沙雕");
}
}
为什么Java⾥⾯要多组合少继承?
继承适合描述“is-a”的关系,但继承容易导致类之间的强耦合,⼀旦⽗类发⽣改变,⼦类也要随之改变,违背了开
闭原则(尽量不修改现有代码,⽽是添加新的代码来实现)。
组合适合描述“has-a”或“can-do”的关系,通过在类中组合其他类,能够更灵活地扩展功能。组合避免了复杂的类继
承体系,同时遵循了开闭原则和松耦合的设计原则。
举个例⼦,假设我们采⽤继承,每种形状和样式的组合都会导致类的急剧增加:
// 基类
class Shape {
public void draw() {
System.out.println("Drawing a shape");
}
}
// 圆形
class Circle extends Shape {
@Override
public void draw() {
System.out.println("Drawing a circle");
}
}
// 带红⾊的圆形
class RedCircle extends Circle {
@Override
public void draw() {
System.out.println("Drawing a red circle");
}
}
// 带绿⾊的圆形
class GreenCircle extends Circle {
@Override
public void draw() {
System.out.println("Drawing a green circle");
}
}
// 类似的,对于矩形也要创建多个类
No. 23 / 95
⾯渣逆袭Java基础篇V2-让天下所有的⾯渣都能逆袭
class Rectangle extends Shape {
@Override
public void draw() {
System.out.println("Drawing a rectangle");
}
}
class RedRectangle extends Rectangle {
@Override
public void draw() {
System.out.println("Drawing a red rectangle");
}
}
组合模式更加灵活,可以将形状和颜⾊分开,松耦合。
// 形状接⼝
interface Shape {
void draw();
}
// 颜⾊接⼝
interface Color {
void applyColor();
}
形状⼲形状的事情。
// 圆形的实现
class Circle implements Shape {
private Color color; // 通过组合的⽅式持有颜⾊对象
public Circle(Color color) {
this.color = color;
}
@Override
public void draw() {
System.out.print("Drawing a circle with ");
color.applyColor(); // 调⽤颜⾊的逻辑
}
}
// 矩形的实现
class Rectangle implements Shape {
private Color color;
public Rectangle(Color color) {
this.color = color;
}
No. 24 / 95
⾯渣逆袭Java基础篇V2-让天下所有的⾯渣都能逆袭
@Override
public void draw() {
System.out.print("Drawing a rectangle with ");
color.applyColor();
}
}
颜⾊⼲颜⾊的事情。
// 红⾊的实现
class RedColor implements Color {
@Override
public void applyColor() {
System.out.println("red color");
}
}
// 绿⾊的实现
class GreenColor implements Color {
@Override
public void applyColor() {
System.out.println("green color");
}
}
1. Java ⾯试指南(付费)收录的国企零碎⾯经同学 9 ⾯试原题:Java ⾯向对象的特性,分别怎么理解的
2. Java ⾯试指南(付费)收录的美团⾯经同学 4 ⼀⾯⾯试原题:Java ⾯向对象的特点
3. Java ⾯试指南(付费)收录的字节跳动同学 20 测开⼀⾯的原题:讲⼀下 JAVA 的特性,什么是多态
4. Java ⾯试指南(付费)收录的京东⾯经同学 7 Java 后端技术⼀⾯⾯试原题:⾯向对象三⼤特性
19.多态解决了什么问题?(补充)
2024 年 03 ⽉ 26 ⽇增补
多态指同⼀个接⼝或⽅法在不同的类中有不同的实现,⽐如说动态绑定,⽗类引⽤指向⼦类对象,⽅法的具体调⽤
会延迟到运⾏时决定。
举例,现在有⼀个⽗类 Wanger,⼀个⼦类 Wangxiaoer,都有⼀个 write ⽅法。现在有⼀个⽗类 Wanger 类型的
变量 wanger,它在执⾏ wanger.write() 时,究竟调⽤⽗类 Wanger 的 write() ⽅法,还是⼦类 Wangxiaoer
的 write() ⽅法呢?
//⼦类继承⽗类
class Wangxiaoer extends Wanger {
public void write() { // ⼦类覆盖⽗类⽅法
System.out.println("记住仇恨,表明我们要奋发图强的⼼智");
}
public static void main(String[] args) {
// ⽗类引⽤指向⼦类对象
No. 25 / 95
⾯渣逆袭Java基础篇V2-让天下所有的⾯渣都能逆袭
Wanger[] wangers = { new Wanger(), new Wangxiaoer() };
for (Wanger wanger : wangers) {
// 对象是王⼆的时候输出:勿忘国耻
// 对象是王⼩⼆的时候输出:记住仇恨,表明我们要奋发图强的⼼智
wanger.write();
}
}
}
class Wanger {
public void write() {
System.out.println("勿忘国耻");
}
}
答案是在运⾏时根据对象的类型进⾏后期绑定,编译器在编译阶段并不知道对象的类型,但是 Java 的⽅法调⽤机
制能找到正确的⽅法体,然后执⾏,得到正确的结果,这就是多态的作⽤。
多态的实现原理是什么?
多态通过动态绑定实现,Java 使⽤虚⽅法表存储⽅法指针,⽅法调⽤时根据对象实际类型从虚⽅法表查找具体实
现。
1. Java ⾯试指南(付费)收录的华为⾯经同学 8 技术⼆⾯⾯试原题:多态的⽬的,解决了什么问题?
2. Java ⾯试指南(付费)收录的美团⾯经同学 16 暑期实习⼀⾯⾯试原题:请说说多态、重载和重写
3. Java ⾯试指南(付费)收录的字节跳动⾯经同学19番茄⼩说⼀⾯⾯试原题:多态的⽤法,多态的实现原
理
20.重载和重写的区别?
No. 26 / 95
⾯渣逆袭Java基础篇V2-让天下所有的⾯渣都能逆袭
推荐阅读:⽅法重写 Override 和⽅法重载 Overload 有什么区别?
如果⼀个类有多个名字相同但参数个数不同的⽅法,我们通常称这些⽅法为⽅法重载(overload)。如果⽅法的功
能是⼀样的,但参数不同,使⽤相同的名字可以提⾼程序的可读性。
如果⼦类具有和⽗类⼀样的⽅法(参数相同、返回类型相同、⽅法名相同,但⽅法体可能不同),我们称之为⽅法
重写(override)。⽅法重写⽤于提供⽗类已经声明的⽅法的特殊实现,是实现多态的基础条件。
⽅法重载发⽣在同⼀个类中,同名的⽅法如果有不同的参数(参数类型不同、参数个数不同或者⼆者都不
同)。
⽅法重写发⽣在⼦类与⽗类之间,要求⼦类与⽗类具有相同的返回类型,⽅法名和参数列表,并且不能⽐⽗
类的⽅法声明更多的异常,遵守⾥⽒代换原则。
什么是⾥⽒代换原则?
⾥⽒代换原则也被称为李⽒替换原则(Liskov Substitution Principle, LSP),其规定任何⽗类可以出现的地⽅,
⼦类也⼀定可以出现。
No. 27 / 95
⾯渣逆袭Java基础篇V2-让天下所有的⾯渣都能逆袭
No. 28 / 95
⾯渣逆袭Java基础篇V2-让天下所有的⾯渣都能逆袭
LSP 是继承复⽤的基⽯,只有当⼦类可以替换掉⽗类,并且单位功能不受到影响时,⽗类才能真正被复⽤,⽽⼦类
也能够在⽗类的基础上增加新的⾏为。
这意味着⼦类在扩展⽗类时,不应改变⽗类原有的⾏为。例如,如果有⼀个⽅法接受⼀个⽗类对象作为参数,那么
传⼊该⽅法的任何⼦类对象也应该能正常⼯作。
class Bird {
void fly() {
System.out.println("⻦正在⻜");
}
}
class Duck extends Bird {
@Override
void fly() {
System.out.println("鸭⼦正在⻜");
}
}
class Ostrich extends Bird {
// Ostrich违反了LSP,因为鸵⻦不会⻜,但却继承了会⻜的⻦类
@Override
void fly() {
throw new UnsupportedOperationException("鸵⻦不会⻜");
}
}
在这个例⼦中,Ostrich(鸵⻦)类违反了 LSP 原则,因为它改变了⽗类 Bird 的⾏为(即⻜⾏)。设计时应该更加
谨慎地使⽤继承关系,确保遵守 LSP 原则。
除了李⽒替换原则外,还有其他⼏个重要的⾯向对象设计原则,它们共同构成了 SOLID 原则,分别是:
①、单⼀职责原则(Single Responsibility Principle, SRP),指⼀个类应该只有⼀个引起它变化的原因,即⼀个类
只负责⼀项职责。这样做的⽬的是使类更加清晰,更容易理解和维护。
②、开闭原则