1. 基础知识准备
Java
- 面向对象:Java 是一种面向对象的编程语言,强调封装、继承和多态。
- 跨平台:Java 通过虚拟机(JVM)实现“编写一次,随处运行”的特性。
- 垃圾回收:Java 有自动垃圾回收机制,减少了内存管理的复杂性。
- 丰富的生态系统:拥有大量的库和框架(如 Spring、Hibernate)支持企业级开发。
- 应用场景:大型企业级应用和需要稳定性的场景。
Go
- 并发支持:内置 goroutines 和 channels,易于处理并发编程。
- 高性能:编译为机器码,执行速度较快,适合高性能网络服务和分布式系统。
- 应用场景:适合需要高并发和高性能的网络服务和微服务架构。
2. 面向对象基础
面向过程(POP) 和 面向对象(OOP)的区别
面向过程:把解决问题的过程拆解成一个个方法,通过执行一个个方法解决问题。
面向对象:先抽象对象,利用对象执行方法的方式解决问题。
- 对象(equals)比较内存中存放的内容是否相等。
- 对基本数据类型来说,
==
比较的是值。 - 对引用数据类型来说,
==
比较的是对象的内存地址。
/** 京东笔试选择题的时候遇到过 **/
String str1 = "hello";
String str2 = new String("hello");
String str3 = "hello";
// 使用 == 比较字符串的引用相等
System.out.println(str1 == str2); // false
System.out.println(str1 == str3); // true
// 使用 equals 方法比较字符串的相等
System.out.println(str1.equals(str2)); // true
System.out.println(str1.equals(str3)); // true
封装(对象)、继承(方法)、多态(一个对象具有多种状态,方法重载 / 方法重写)
hashCode 和 equals() 都用于比较两个对象 是否相等。
如果两个对象的hashCode
值相等,那这两个对象不一定相等(哈希碰撞)。
如果两个对象的hashCode
值相等并且equals()
方法也返回 true
,我们才认为这两个对象相等。
如果两个对象的hashCode
值不相等,我们就可以直接认为这两个对象不相等。
方法重载 和 方法重写的区别
方法重载:发生在同一个类中。多个方法名相同但参数(数量、类型、顺序)不同。
方法重写:重写发生在子类和父类之间,方法名和参数都相同,但实现不同使其具有不同的实现。
3.String
- 操作少量的数据: 适用
String
- 单线程操作字符串缓冲区下操作大量数据: 适用
StringBuilder
- 多线程操作字符串缓冲区下操作大量数据: 适用
StringBuffer
字符串拼接:
StringBuilder s = new StringBuilder();
s.append(value);
String str1 = "str";
String str2 = "ing";
String str3 = "str" + "ing";
String str4 = str1 + str2; // 创建新的字符串(堆中)
String str5 = "string"; // 存储在字符串常量池
System.out.println(str3 == str4);//false
System.out.println(str3 == str5);//true
System.out.println(str4 == str5);//false
4. 异常
日常开发中经常会用到的异常
NullPointerException
(空指针错误)
IllegalArgumentException
(参数错误比如方法入参类型错误)
NumberFormatException
(字符串转换为数字格式错误,IllegalArgumentException
的子类)
try {
System.out.println("Try to do something");
throw new RuntimeException("RuntimeException");
log.info("操作XXX");
} catch (Exception e) {
log.error(e);
} finally {
// 不要在 finally 语句块中使用 return!,如果finally写了return,则try中的return不生效
}
如果有必须关闭的资源,优先使用 try- catch-resources,而不是try-finally
// try- catch-resources
try (BufferedReader br = new BufferedReader(new FileReader("file.txt"))) {
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
- 不要把异常定义为静态变量,因为这样会导致异常栈信息错乱。每次手动抛出异常,我们都需要手动 new 一个异常对象抛出。
- 抛出的异常信息一定要有意义,建议抛出更加具体的异常比如字符串转换为数字格式错误的时候应该抛出
NumberFormatException
而不是其父类IllegalArgumentException
。 - 避免重复记录日志:如果在捕获异常的地方已经记录了足够的信息(包括异常类型、错误信息和堆栈跟踪等),那么在业务代码中再次抛出这个异常时,就不应该再次记录相同的错误信息。重复记录日志会使得日志文件膨胀,并且可能会掩盖问题的实际原因,使得问题更难以追踪和解决。
5. 序列化 和 反序列化
- 序列化:将对象转换成二进制字节流存储或传输,也可以是 JSON, XML 等文本格式。
- 反序列化:将在序列化过程中所生成的数据转换为原始数据结构或者对象的过程。
应用场景:
- 对象在进行网络传输(调用 RPC )之前需要先被序列化,接收到序列化的对象之后需要再进行反序列化;
- 将对象存储到文件之前需要进行序列化,将对象从文件中读取出来需要进行反序列化;
- 将对象存储到数据库(如 Redis)之前需要用到序列化,将对象从缓存数据库中读取出来需要反序列化;
- 将对象存储到内存之前需要进行序列化,从内存中读取出来之后需要进行反序列化。
注意:JDK 自带的序列化方式一般不会用 ,因为序列化效率低并且存在安全问题。比较常用的序列化协议有 Hessian、Kryo、Protobuf、ProtoStuff,这些都是基于二进制的序列化协议。像 JSON 和 XML 这种属于文本类序列化方式。虽然可读性比较好,但是性能较差,一般不会选择。
6. 语法糖
常见:Java 中最常用的语法糖主要有泛型、自动拆装箱、变长参数、枚举、内部类、增强 for 循环、try-with-resources 语法、lambda 表达式等。
面试题
1. Java 和C++区别?
Java 不提供指针来直接访问内存,程序内存更加安全
C++同时支持方法重载和操作符重载,但是 Java 只支持方法重载。
Java 的类是单继承的,C++ 支持多重继承;虽然 Java 的类不可以多继承,但是接口可以多继承。
Java 有自动内存管理垃圾回收机制(GC),不需要程序员手动释放无用内存。
2. JVM的组成部分:
- 类加载器:负责加载Java类文件,将其加载到内存中,并将其转换为JVM可以理解的格式。
- 运行时数据区:JVM在执行Java程序时,会在内存中划分不同的区域,包括:
方法区:存储类的结构信息(如字段、方法、常量池等)。
堆:用于存储对象实例,是JVM中最大的一块内存区域。
栈:每个线程都有自己的栈,用于存储局部变量、方法调用和返回值。- 解释器:逐行解释执行字节码。
即时编译器:将热点代码编译为机器码,以提高执行效率。
垃圾收集器:负责自动管理内存,回收不再使用的对象