前言
网上的 Java 基础[面试题]文章有非常多,但是大部分都比较老了。
很多题目早已不是当前的热门题目,没有必要在这些题目上花太多时间。
很多答案放现在已经不准确,可能会误导新人。
因此,我花了几天时间整理了一些时下高频的 Java 基础题目,并反复斟酌,给出符合当前版本的解析。
我的最新文章:[全网最硬核 Redis 大厂面试题解析(2022年最新版)]
[面试]系列
我自己前前后后加起来总共应该参加了不下四五十次的面试,拿到过几乎所有一线大厂的 offer:阿里、字节、美团、快手、拼多多等等。
每次面试后我都会将面试的题目进行记录,并整理成自己的题库,最近我将这些题目整理出来,并按大厂的标准给出自己的解析,希望在这金三银四的季节里,能助你一臂之力。
正文
1、面向对象的三个基本特征?
面向对象的三个基本特征是:封装、继承和多态。
继承:让某个类型的对象获得另一个类型的对象的属性的方法。继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为。
封装:隐藏部分对象的属性和实现细节,对数据的访问只能通过外公开的接口。通过这种方式,对象对内部数据提供了不同级别的保护,以防止程序中无关的部分意外的改变或错误的使用了对象的私有部分。
多态:对于同一个行为,不同的子类对象具有不同的表现形式。多态存在的3个条件:1)继承;2)重写;3)父类引用指向子类对象。
举个简单的例子:英雄联盟里面我们按下 Q 键这个动作:
- 对于亚索,就是斩钢闪
- 对于提莫,就是致盲吹箭
- 对于剑圣,就是阿尔法突袭
同一个事件发生在不同的对象上会产生不同的结果。
我再举一个简单的例子帮助大家理解,这个例子可能不是完全准确,但是我认为是有利于理解的。
1. `public class Animal { // 动物` 2. `public void sleep() {` 3. `System.out.println("躺着睡");` 4. `}` 5. `}` 6. `class Horse extends Animal { // 马 是一种动物` 7. `public void sleep() {` 8. `System.out.println("站着睡");` 9. `}` 10. `}` 11. `class Cat extends Animal { // 猫 是一种动物` 12. `private int age;` 13. `public int getAge() {` 14. `return age + 1;` 15. `}` 16. `@Override` 17. `public void sleep() {` 18. `System.out.println("四脚朝天的睡");` 19. `}` 20. `}`
在这个例子中:
House 和 Cat 都是 Animal,所以他们都继承了 Animal,同时也从 Animal 继承了 sleep 这个行为。
但是针对 sleep 这个行为,House 和 Cat 进行了重写,有了不同的表现形式(实现),这个我们称为多态。
在 Cat 里,将 age 属性定义为 private,外界无法直接访问,要获取 Cat 的 age 信息只能通过 getAge 方法,从而对外隐藏了 age 属性,这个就叫做封装。当然,这边 age 只是个例子,实际使用中可能是一个复杂很多的对象。
2、访问修饰符public,private,protected,以及不写时的区别?
3、下面两个代码块能正常编译和执行吗?
1. `// 代码块1` 2. `short s1 = 1; s1 = s1 + 1;` 3. `// 代码块2` 4. `short s1 = 1; s1 += 1;`
代码块1编译报错,错误原因是:不兼容的类型: 从int转换到short可能会有损失”。
代码块2正常编译和执行。
我们将代码块2进行编译,字节码如下:
1. `public class com.joonwhee.open.demo.Convert {` 2. `public com.joonwhee.open.demo.Convert();` 3. `Code:` 4. `0: aload_0` 5. `1: invokespecial #1 // Method [java](https://javajgs.com/archives/tag/java "更多关于 java 的文章")/lang/Object."":()V` 6. `4: return` 8. `public static void main(java.lang.String[]);` 9. `Code:` 10. `0: iconst_1 // 将int类型值1入(操作数)栈` 11. `1: istore_1 // 将栈顶int类型值保存到局部变量1中` 12. `2: iload_1 // 从局部变量1中装载int类型值入栈` 13. `3: iconst_1 // 将int类型值1入栈` 14. `4: iadd // 将栈顶两int类型数相加,结果入栈` 15. `5: i2s // 将栈顶int类型值截断成short类型值,后带符号扩展成int类型值入栈。` 16. `6: istore_1 // 将栈顶int类型值保存到局部变量1中` 17. `7: return` 18. `}`
可以看到字节码中包含了 i2s 指令,该指令用于将 int 转成 short。i2s 是 int to short 的缩写。
其实,s1 += 1 相当于 s1 = (short)(s1 + 1),有兴趣的可以自己编译下这两行代码的字节码,你会发现是一摸一样的。
说好的 Java 基础题,怎么又开始变态起来了???
4、基础考察,指出下题的输出结果
1. `public static void main(String[] args) {` 2. `Integer a = 128, b = 128, c = 127, d = 127;` 3. `System.out.println(a == b);` 4. `System.out.println(c == d);` 5. `}`
答案是:false,true。
执行 Integer a = 128,相当于执行:Integer a = Integer.valueOf(128),基本类型自动转换为包装类的过程称为自动装箱(autoboxing)。
1. `public static Integer valueOf(int i) {` 2. `if (i >= IntegerCache.low && i <= IntegerCache.high)` 3. `return IntegerCache.cache[i + (-IntegerCache.low)];` 4. `return new Integer(i);` 5. `}`
在 Integer 中引入了 IntegerCache 来缓存一定范围的值,IntegerCache 默认情况下范围为:-128~127。
本题中的 127 命中了 IntegerCache,所以 c 和 d 是相同对象,而 128 则没有命中,所以 a 和 b 是不同对象。
但是这个缓存范围时可以修改的,可能有些人不知道。可以通过JVM启动参数:-XX:AutoBoxCacheMax= 来修改上限值,如下图所示: