Java 基础
热门题目
- 什么是 java ?/ java 的优势?/ 为什么选择 Java?/ 和其他语言的对比?
- jdk、jre、jvm?
- 封装、继承、多态?
- 重载和重写的区别?
- String、StringBuilder、StringBuffer 的区别?
- 字符串常量拼接的过程?
- 接口和抽象类的区别?
- 成员变量、局部变量、静态变量、实例变量?
- final、finally、finalize?
- 基本类型的隐式转换?
- 访问修饰符:private、public、protected、默认?
- this 和 super 的区别?
- == 、equals() 和 hashcode() 的区别?
- 字符串常量池?
- instanceof 的作用?
- 泛型及泛型擦除?
- 深拷贝和浅拷贝的区别?
- 序列化与反序列化?
- 异常机制?
- 装箱和拆箱?
- java 是值传递还是引用传递?
见招拆招
1、什么是 java ?/ java 的优势?/ 为什么选择 Java?/ 和其他语言的对比?
其实在学校的时候,也接触了很多的语言,比如 JavaScript、Python 等,但最终选择 Java 是因为它有几个方面比较吸引我。从语言的角度出发,Java 是纯面向对象的一门语言,类是组织代码结构的基本单位嘛,规范性比较强,不像 JavaScript 或者 Python 之类的,函数式编程占主导;而且 Java 的语言特性也比较吸引我,封装、继承、多态就不用说了,面向对象的三大特征嘛,包括还有泛型、反射、注解等等这些机制。当然,每种语言都有自己擅长的领域,我个人比较倾向于做后端开发嘛,java 在这一块的社区生态是非常完善的,而且国内很多知名的公司都在使用,文档资料也比较齐全。
(这种题就是用来埋钩子的)
2、jdk、jre、jvm?
jvm 中文翻译就是 java 虚拟机,是执行 java 字节码的一个程序,通过它可以实现 java 代码的跨平台运行,就是“一次编写,到处运行”嘛;
jre 指的是 java 的运行环境,包含了 jvm 和运行时所需要的标准类库。
jdk 是 java 的开发工具包,包含jre,同时提供了很多的工具,比如说 javac、javadoc、jconsole 等工具。
(这里,既然提到了这些工具,那么就列举你会的,当然 javac 这种还是不要拿出来,说一些分析工具,面试官追问的概率更大一些,比如调试工具,监控工具)
3、封装、继承、多态
面向对象的三大特性,
封装指的是将字段私有化,向外暴露对应的方法,进行对象状态的获取和修改 => java 的做法就是将字段用 private 修饰,对外提供 public 的 get 方法和 set 方法;
在 java 中,继承的话,就是子类可以拥有父类非 private 的字段和方法,也可以根据需求进行重写嘛;
多态的话,可以分为编译时多态和运行时多态,编译时多态指的是方法的重载,运行时多态指的是变量所引用的具体类型在程序运行的时候才能得到确认,在代码就表现为父类型的变量引用子类型的对象。
4、重载和重写的区别?
它们是两个不同的概念,重载是同一个类中,可以运行方法签名不同的多个方法同时存在。
重写是子类中出现了和父类方法签名一致的方法,返回值可以是协变类型,访问权限不能低于原父类中方法的访问权限,就是覆盖了继承下来的父类方法。
(方法签名:方法名、方法参数个数、位置、类型,与返回值和修饰符无关)
5、String、StringBuilder、StringBuffer 的区别?
它们三个都是用来表示字符串的,
String 的话,它是一个不可变类,所产生的的对象的状态也是不可变的,天然线程安全的。
StringBuilder 的话,底层是一个用于存储字符的数组,jdk 8 是用到 char 数组表示,jdk8 之后是 byte 数组,可用于大量字符串拼接的场景,不是线程安全的。
StringBuffer 其实就是 StringBuilder 的“加锁”版本,它的所有方法都加上了 syncornized ,是线程安全的。
一般设计到字符串拼接的场景都是在单线程中完成的,String 或者 StringBuilder 可以满足日常需求,StringBuffer 的话不常用。
6、字符串常量拼接的过程?
如果只是单纯的字符串常量拼接,然后复制给变量,那么编译器会将这些字符串常量组装成一个字符串;
如果需要运行时才能确定最后的结果,那么编译器会将字符串拼接的操作优化成 StringBuilder,调用它的 append 方法进行字符串的拼接工作,最后调用 toString 方法生成拼接后的字符串。
public static void main(String[] args) {
String s1 = "hello" + " " + "world";
String s2 = "hello world";
System.out.println(s1 == s2); // true
}
解释一下这个例子,s1 被编译优化后,产生字符串 "hello world",放入字符串常量池,s2 在创建的时候,发现字符串常量池中有,直接引用即可,所以最终的结果是 true。
// 源代码
String str1 = "Hello";
String str2 = "World";
String result = str1 + " " + str2;
// 编译优化后
StringBuilder sb = new StringBuilder();
sb.append(str1);
sb.append(" ");
sb.append(str2);
String result = sb.toString();
7、接口和抽象类的区别?
在 java 8 及其以后的 java 版本,这两者很像,要说区别的话,我觉得更多的是在设计理念上:
接口定义了方法,子类去实现,起到一种约束作用;类之间使用继承的方式相关联,父类更多的是对子类公共方法的抽取或者说定义模板以及提供一些公共的状态。
8、成员变量、局部变量、静态变量、实例变量?
成员变量:定义在类中,但是在类方法之外
局部变量:定义在类的方法当中
静态变量:成员变量的一种,使用 static 修饰
实例变量:成员变量的一种,不使用 static 修饰
9、final、finally、finalize?
final:用于声明常量、不可重写的方法和不可继承的类。
finally:用于异常处理,确保某些代码总是会执行。
finalize:对象被垃圾回收之前调用的钩子函数,只会调用一次,jvm 不保证一定调用。
10、基本类型的隐式转换?
这种题,通常出现在笔试之中:
byte、short 、char 参与运算都会转成int(你就记住,int 以下的数据类型参与运算,都会转成int):
除了 " += ",剩下的所有类型在运算的时候都会向上转型(取决于参与运算变量的最大类型,无损的转换):
"+="也是遵循第二条法则的,不过是在向上转型后,再将运算的结果转型回去(可能有损):
11、访问修饰符:private、public、protected、默认?
private:确保所修饰的字段或者方法在其他类中无法访问;
public:对外暴露类中的字段或者方法,其他类可以访问;
protected:包可见,同一个包或者其子包中可以可以访问;
默认(不写):仅在同一个包内可以访问
12、this 和 super 的区别?
它们都是 Java 中的关键字:
this 指向当前对象实例的引用;super 是当前对象父类对象实例的引用。
13、== 、equals() 和 hashcode() 的区别?
== 用于判断两个对象的引用是否相同;
equals() 用于判断两个对象是否相等,其实就是字段值是否相等;
hashcode() 用于计算对象的hash值,可以理解为不精确的 equals(),同样可以用来判断对象是否相等,一般用于像 Set 和 Map 这样的集合中。
(== 就不要说内存地址了,不同的 jvm 实现不太一样,有些是内存地址,有些是对象句柄)
14、字符串常量池?
java 优化字符串存储和管理的一种机制,将符合要求的字符串常量进行缓存,有相同的字符串字面量时,就会直接复用,new 方式产生的字符串并不会进行缓存,但是可以手动调用 intern 方法加入常量池中,该方法会返回一个引用,表示常量池中的字符串对象。
15、instanceof 的作用?
检查对象实例是不是某个类或者派生类对象。
16、泛型及泛型擦除?
泛型是 java 中的一种代码复用的机制,因为 java 是一种强类型的语言,无论是方法的传参还是变量的赋值,都有着比较严格的要求,但许多时候,我们往往需要复用同一套代码逻辑,比如说一个两个数字相加,返回相加的结果,很显然 java 中这么多的数字类型,不可能每一种类型都写一遍,这种情况下,就可以利用一个泛型函数来提高代码的通用性了。
由于 java 中的泛型其实是一种伪泛型的策略,javac 编译源文件时,会根据泛型定义的上下界限进行泛型的擦除工作,最终在 class 文件中保留的就是擦除后的原始类型。
(泛型在平时的开发中比较常见,所以这里留一篇文章,大家可以好好精读一下:https://www.pdai.tech/md/java/basic/java-basic-x-generic.html,然后面试官可能会问你在项目的哪些地方运用到了泛型,你要有所准备)
17、深拷贝和浅拷贝的区别?
最本质的区别就是拷贝出来的对象是否具有和原对象相同的引用,深拷贝产生的数据源与原对象数据源不相同,浅拷贝只会复制数据源的引用,如果是对象的话,和原对象有相同的数据源。
(这种题目没有什么意义)
18、序列化与反序列化?
序列化是将对象转成字节流的形式,方便对外传输和存储;
反序列化则是将字节流转成对象,方便在程序中操作。
(如果不想让某些字段参与序列化,可以添加 transient 关键字;反序列化的时候,为了防止序列化攻击,可以在 readResolve 方法中进行校验)
19、异常机制?
程序运行的过程中,可能会产生各种各样的异常,我们可以对其进行捕获和处理。使用try-catch-finally
可以对异常进行捕获,但需要主要的是,finally语句块中不能写 return,这样做会“吞”掉try
语句中的返回值。JVM 处理异常的话,其实是有一个叫做“异常”表的东西,在编译之后的字节码文件中可以观察到:
表中有四个字段,分别是:from、to、target、type。from 和 to 表示字节码执行的起始点,target表示产生异常之后跳转到的字节码位置,type 字段表示发生异常的类型,如果是 any 的话,表示无论发生什么样的异常,都会跳转到指定的字节码位置。
如果 JVM 实例挂掉了,也就是 java 进程退出了,finally 就不会正常执行。
我自己做过测试,创建一个异常对象是创建普通对象的大于20倍,而抛出、捕获一个异常对象,所花费的时间大约是创建异常对象的4倍。
20、装箱和拆箱?
这描述的是基本类型和对应包装类型进行赋值或者运算时的一种行为,基本类型到包装类型的过程叫做装箱,包装类型到基本类型的过程叫做拆箱。
其实每种包装类型都有自己的缓冲池,存储着一定范围内的基本类型对应的包装类型实例,比如说 Integer ,它的数值缓存范围就是 -128 ~ 127。当然,浮点数没有,因为浮点数不可穷举。
21、java 是值传递还是引用传递?
值传递,java 中,无论是基本类型还是引用类型,在方法传参的时候,都会传递自身的副本,引用类型就直接复制引用嘛。
总结
基本上就这些了,注解、反射、泛型、异常、SPI 这几个机制好好看一下
2025/01/13
writeBy kaiven