欢迎来到每日 Java 面试题分享栏目!
订阅专栏,不错过每一天的练习
今日分享 3 道面试题目!
评论区复述一遍印象更深刻噢~
final
、finally
和 finalize
是 Java 中三个容易混淆的概念,它们的作用和使用场景完全不同。以下是详细分析:
final
final
是一个关键字,用于声明类、方法或变量,表示某些特性不可更改。
修饰类:
表示类不能被继承。
示例:
public final class FinalClass {
// 类体
}
// 报错:无法继承 FinalClass
class SubClass extends FinalClass {}
修饰方法:
表示方法不能被子类重写。
示例:
public class Parent {
public final void display() {
System.out.println("This is a final method.");
}
}
class Child extends Parent {
// 报错:不能重写 final 方法
// @Override
// public void display() {}
}
修饰变量:
表示变量值不能被重新赋值(常量)。
示例:
public class Example {
public static final double PI = 3.14159;
public void test() {
final int x = 10;
// 报错:不能修改 final 变量
// x = 20;
}
}
final
用于 限制修改:类不能被继承、方法不能被重写、变量不能重新赋值。
finally
finally
是一个用于 异常处理 的关键字,表示无论是否发生异常,finally
块中的代码都会执行。
确保释放资源:
通常用于关闭文件、释放锁、关闭数据库连接等操作。
示例:
public class Example {
public static void main(String[] args) {
try {
int result = 10 / 0; // 抛出异常
} catch (ArithmeticException e) {
System.out.println("Caught exception: " + e.getMessage());
} finally {
System.out.println("This is the finally block.");
}
}
}
输出:
Caught exception: / by zero
This is the finally block.
注意事项:
即使 try
块中有 return
,finally
块仍然会执行。
示例:
public class Example {
public static void main(String[] args) {
System.out.println(test());
}
public static int test() {
try {
return 10;
} finally {
System.out.println("Finally block executed");
}
}
}
输出:
Finally block executed
10
finally
块是 异常处理机制 的一部分,用于执行清理代码。
finalize
finalize
是 Object
类的一个方法,用于在对象被 垃圾回收(GC)之前执行清理操作。
定义对象的清理逻辑:
可以在 finalize
方法中释放资源,如关闭文件或网络连接。
示例:
public class Example {
@Override
protected void finalize() throws Throwable {
System.out.println("finalize method called");
}
public static void main(String[] args) {
Example obj = new Example();
obj = null; // 使对象变为垃圾
System.gc(); // 显式调用垃圾回收器
}
}
注意事项:
finalize
的执行时间不确定,依赖垃圾回收机制,可能导致性能问题。finalize
方法已被标记为 Deprecated,推荐使用 try-with-resources
或显式清理资源代替。finalize
用于对象被回收前执行清理操作,但已被弃用,不推荐使用。
特性 | final |
finally |
finalize |
---|---|---|---|
类型 | 关键字 | 关键字 | 方法 |
作用 | 限制类、方法、变量的修改或继承 | 异常处理中用于清理操作 | 对象回收前执行清理操作 |
使用场景 | 定义不可更改的类、方法或常量 | 异常处理中释放资源 | 垃圾回收前释放对象持有的资源 |
是否推荐 | 推荐 | 推荐 | 不推荐(已被废弃) |
final
:用于实现不可变类、确保线程安全等。finally
:确保资源释放,如何正确使用异常处理机制。finalize
:了解其作用及替代方案(如 AutoCloseable
)。final
修饰变量时,配合 static
创建常量(例如:public static final
)。try-with-resources
,避免依赖 finally
或 finalize
。finalize
的替代方案(如 PhantomReference
或显式清理)。在 Java 中编写代码时,遇到乱码问题通常是由于 字符编码 处理不当导致的。Java 对字符编码有一套标准机制,但如果开发、存储和传输的编码方式不一致,就可能产生乱码。以下是详细分析:
读取文件时未指定编码:
BufferedReader reader = new BufferedReader(new FileReader("test.txt"));
默认会使用平台编码,可能与文件实际编码不一致。
使用 String
的构造方法或 getBytes()
方法时,未指定编码:
String str = new String(byteArray); // 编码方式默认依赖系统
统一开发环境编码:
File -> Settings -> Editor -> File Encodings
。统一运行时编码:
显式指定 JVM 的默认编码:
java -Dfile.encoding=UTF-8 YourClassName
在代码中指定读取或写入时的编码:
BufferedReader reader = new BufferedReader(new InputStreamReader(
new FileInputStream("test.txt"), "UTF-8"));
在 Windows 系统下,手动将 CMD 控制台编码改为 UTF-8:
chcp 65001
或者直接在代码中设置:
System.setOut(new PrintStream(System.out, true, "UTF-8"));
数据库表和列的编码设置为 UTF-8(如 MySQL 中 CHARACTER SET utf8
)。
确保 JDBC URL 中设置字符编码,例如:
jdbc:mysql://localhost:3306/dbname?useUnicode=true&characterEncoding=UTF-8
在 HTTP 请求或响应时,显式指定内容的编码格式:
response.setCharacterEncoding("UTF-8");
错误示例:
String str = new String(byteArray);
正确示例:
String str = new String(byteArray, "UTF-8");
使用工具(如 Notepad++、VS Code)检查和修改文件编码。
Linux 中使用 file
命令检查:
file -i test.txt
乱码的根本原因是:
例如:
"中"
在 UTF-8 中表示为 0xE4B8AD
。0xE4B8AD
会被解释为乱码字符。为了避免 Java 中的乱码问题,应做到:
通过以上方法,可以有效避免和解决 Java 开发中的乱码问题。
在 JDK 9 中,String
类的底层实现从使用 char[]
数组改为使用 byte[]
数组,这是 Java 性能优化 的一部分。这一改变被称为 Compact Strings,是为了解决内存占用问题并提升性能。下面详细解答这一问题的背景、原因以及优缺点:
JDK 8 及之前的实现:
String
类底层使用 char[]
数组存储字符,每个字符占用 2 字节(基于 UTF-16 编码)。"abc"
,实际只需要 3 字节,但用 char[]
存储需要占用 6 字节。内存浪费问题:
char[]
数组存储所有字符串,导致约 50% 的内存被浪费。在 JDK 9 中,String
类的底层实现改为 byte[]
数组,并添加了一个 coder
字段,表示字符串的编码方式:
byte[]
数组存储实际数据:
ISO-8859-1
)。UTF-16
)。coder
字段:
ISO-8859-1
)还是双字节(UTF-16
)。byte[]
存储只需占用原来一半的内存。"abc"
使用 char[]
需要 6 字节。"abc"
使用 byte[]
只需 3 字节。byte[]
仍使用 UTF-16,但内存占用与之前相同,不会额外浪费。byte[]
占用的内存更少,意味着在运行时,更多的字符串数据可以装入 CPU 的缓存,从而减少缓存未命中的概率。byte[]
实现,但在 API 层面,String
的行为保持不变。ISO-8859-1
和 UTF-16
),需要维护额外的 coder
字段。charAt
、substring
)需要根据编码动态判断字节或字符长度,稍微增加了运算复杂度。以下是 String
在 JDK 8 和 JDK 9 中的底层实现对比(伪代码展示):
public final class String {
private final char[] value; // 使用 char[] 存储字符串,每个字符占 2 字节
private final int hash; // 缓存 hashCode 值
// …
}
public final class String {
private final byte[] value; // 使用 byte[] 存储字符串,节省内存
private final byte coder; // 标记编码类型(0: ISO-8859-1, 1: UTF-16)
private final int hash; // 缓存 hashCode 值
// …
}
JDK 9 将 String
的底层实现从 char[]
改为 byte[]
的主要目的是为了 优化内存使用 和 提升性能,通过支持两种编码(单字节和双字节)显著减少了字符串对象的内存占用。这种改进在不改变 API 的情况下,提升了 Java 应用在内存和运行效率方面的表现,是一项重要的性能优化措施。
今天的 3 道 Java 面试题,您是否掌握了呢?持续关注我们的每日分享,深入学习 Java 面试的各个细节,快速提升技术能力!如果有任何疑问,欢迎在评论区留言,我们会第一时间解答!
明天见!