Java面试宝典:基础二
🔒 25. final vs abstract 关键字
关键字 | 修饰对象 | 作用 | 规则 |
---|---|---|---|
final | 类 | 禁止被继承 | final class MyClass { ... } |
方法 | 禁止被子类重写 | public final void func() | |
变量 | 变为常量(基本类型值不可变,引用类型地址不可变) | final int MAX = 100; | |
abstract | 类 | 必须被继承 | abstract class Animal { ... } |
方法 | 必须被子类重写 | abstract void eat(); |
关键区别:
final Dog dog = new Dog("欧欧"); dog.name = "美美"; // ✅ 允许修改对象属性 dog = new Dog("亚亚"); // ❌ 禁止修改引用地址
⚖️ 26. final vs finally vs finalize
关键字 | 类别 | 作用 | 示例 |
---|---|---|---|
final | 修饰符 | 定义不可变实体(类/方法/变量) | final class Immutable { ... } |
finally | 异常处理块 | 确保资源释放(无论是否发生异常) | try { ... } finally { conn.close(); } |
finalize | Object类方法 | GC回收对象前调用的清理方法(已废弃) | protected void finalize() { /* 清理 */ } |
注意:
- Java 9+ 废弃
finalize()
,推荐使用Cleaner
或try-with-resources
finally
块始终执行(除非System.exit()
或 JVM 崩溃)
📦 27. java.lang.Object 的六个核心方法
equals(Object obj)
:对象等价性比较(默认比较地址)hashCode()
:返回对象哈希码(需与equals
逻辑一致)toString()
:返回对象字符串表示(默认返回类名@地址)getClass()
:获取对象的运行时类wait()/notify()/notifyAll()
:线程间通信(需在同步块中使用)clone()
:创建并返回对象副本(需实现Cloneable
接口)
重写规范:
@Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; User user = (User) o; return Objects.equals(name, user.name); }
🔑 28. 访问权限修饰符对比
修饰符 | 类内部 | 同包 | 不同包子类 | 全局 |
---|---|---|---|---|
private | ✅ | ❌ | ❌ | ❌ |
默认 | ✅ | ✅ | ❌ | ❌ |
protected | ✅ | ✅ | ✅ | ❌ |
public | ✅ | ✅ | ✅ | ✅ |
类修饰规则:
- 外部类仅支持
public
或默认
- 内部类可使用所有修饰符
🏗️ 29. 继承中的构造方法执行顺序
- 子类构造隐式调用
super()
:class Parent { Parent() { System.out.println("Parent构造"); } } class Child extends Parent { Child() { // 隐含 super(); System.out.println("Child构造"); } } // 输出:Parent构造 → Child构造
- 显式调用父类构造:
Child(int age) { super(age); // 必须位于首行 System.out.println("Child带参构造"); }
- 多级继承顺序:
Object → 爷爷类 → 父类 → 子类
🆚 30. == vs equals()
操作符/方法 | 比较内容 | 示例 |
---|---|---|
== | 基本类型:值 | 10 == 10 → true |
引用类型:内存地址 | new String("A") == "A" → false | |
equals() | 默认同 == | |
重写后按业务逻辑 | "A".equals(new String("A")) → true |
重写原则:
- 自反性:
x.equals(x) = true
- 对称性:
x.equals(y) = y.equals(x)
- 传递性:
x=y, y=z → x=z
🌀 31. Java多态详解
三大条件:
- 继承关系(
extends
或implements
) - 子类重写父类方法
- 父类引用指向子类对象:
Animal dog = new Dog()
转型示例:
// 向上转型(自动)
Animal animal = new Dog();
animal.eat(); // 调用Dog重写的eat() // 向下转型(需显式)
if (animal instanceof Dog) { Dog dog = (Dog) animal; dog.bark();
}
优势:代码扩展性强(新增子类不影响父类逻辑)
🗑️ 32. 垃圾回收机制(GC)
核心特性:
- 自动管理:JRE 后台回收无用对象(
java.lang.Object
未覆盖时) - 回收区域:仅堆内存(不包含栈、方法区)
- 不可预测性:无法精确控制 GC 执行时机
触发时机:
- Eden 区满触发 Minor GC
- 老年代满触发 Full GC
- 手动建议:
System.gc()
(不保证立即执行)
对象复活:
protected void finalize() { this.reference = this; // ❌ 已废弃,禁止使用
}
📦 33. 基本类型与包装类
基本类型 | 包装类 | 自动装箱/拆箱 |
---|---|---|
byte | Byte | Integer i = 10; → 装箱 |
int | Integer | int n = i; → 拆箱 |
double | Double | Double d = 3.14; |
转换场景:
- 集合存储:
List<Integer> list = Arrays.asList(1, 2, 3);
- 类型转换:
int age = Integer.parseInt("25");
- 空值处理:
Integer score = null;
(基本类型不支持null
)
🆚 34. Integer vs int
维度 | int | Integer |
---|---|---|
类型 | 基本类型 | 包装类 |
默认值 | 0 | null |
内存占用 | 栈存储(4字节) | 堆存储(对象头+引用) |
比较 | == 比值 | == 比地址,equals 比值 |
缓存优化 | 无 | -128~127 缓存(IntegerCache ) |
陷阱:
Integer a = 100, b = 100; a == b; // true(缓存范围内) Integer c = 200, d = 200; c == d; // false(超出缓存)
📅 35. java.util.Date vs java.sql.Date
类 | 所属包 | 用途 | 精度 |
---|---|---|---|
java.util.Date | java.util | 通用日期时间 | 毫秒级 |
java.sql.Date | java.sql | 数据库日期(不含时间) | 到日(时间置零) |
转换示例:
// util.Date → sql.Date
java.util.Date utilDate = new java.util.Date();
java.sql.Date sqlDate = new java.sql.Date(utilDate.getTime()); // sql.Date → util.Date(自动向上转型)
java.util.Date date = sqlDate;
📂 36. 递归遍历目录文件
void listFiles(File dir) { File[] files = dir.listFiles(); if (files == null) return; for (File file : files) { if (file.isFile()) { System.out.println("文件: " + file.getPath()); } else { System.out.println("目录: " + file.getPath()); listFiles(file); // 递归遍历子目录 } }
}
// 调用: listFiles(new File("D:/Project"));
优化:使用
Files.walk()
(Java 8 NIO)替代递归
37. Java编译输出结果
题目:关于Java编译,下面哪一个正确?
A. Java程序经编译后产生machine code
B. Java程序经编译后会生产byte code
C. Java程序经编译后会产生DLL
D. 以上都不正确
答案:B
解析:
- Java是解释型语言,编译生成字节码(byte code)(.class文件)而非机器码。
- DLL是C/C++编译的动态链接库,与Java无关。
38. 构造方法(Constructor)规则
题目:下列说法正确的有?
A. class中的construtor不可省略
B. construtor与class同名,但方法不能与class同名
C. construtor在一个对象被new时执行
D. 一个class只能定义一个construtor
答案:C
解析:
- A:类可省略构造方法(系统默认提供无参构造)。
- B:方法名可与类名相同(不推荐,违反规范)。
- D:类可定义多个构造方法(重载)。
- C正确:
new
创建对象时自动执行匹配的构造方法。
39. 接口修饰符
题目:Java中接口的修饰符可以为?
A. private
B. protected
C. final
D. abstract
答案:D
解析:
- 接口默认为
abstract
(可显式声明,通常省略)。 - 禁止使用
private
/protected
(仅支持public
或默认包访问)。 final
会阻止接口被实现,违反接口设计目的。
40. 继承中的构造方法执行顺序
题目:以下代码输出什么?
class A { public A() { System.out.println("A"); }
}
class B extends A { public B() { System.out.println("B"); } public static void main(String[] args) { B b = new B(); }
}
答案:B(输出AB)
解析:
- 子类构造方法隐式调用父类无参构造。
- 执行顺序:父类构造 → 子类构造。
41. 关键字使用错误
题目:下列关于关键字的使用说法错误的是?
A. abstract不能与final并列修饰同一个类
B. abstract类中可以有private成员
C. abstract方法必须在abstract类中
D. static方法能处理非static属性
答案:D
解析:
- D错误:static方法属于类,无法直接访问非static成员(需通过对象实例)。
- A:final类禁止继承,abstract类需被继承,冲突。
- B/C:抽象类可包含private属性和抽象方法。
42. 内存回收机制
题目:下列哪些语句关于内存回收的说法正确?
A. 程序员必须创建一个线程来释放内存
B. 内存回收程序负责释放无用内存
C. 内存回收程序允许程序员直接释放内存
D. 内存回收程序可以在指定时间释放内存
答案:B
解析:
- B正确:GC自动回收无用对象内存。
- A/C/D:程序员无法控制GC执行(仅能建议
System.gc()
)。
43. 合法标识符
题目:选出合理的标识符?
A. _sysl_111
B. 2 mail
C. $change
D. class
答案:A、C
解析:
- 合法规则:
- 可包含字母、数字、
_
、$
- 不能以数字开头(B错误)
- 不能是关键字(D错误,
class
是关键字)
- 可包含字母、数字、
44. 正确说法
题目:下列说法正确的是?
A. java.lang.Cloneable是类
B. java.lang.Runnable是接口
C. Double对象在java.lang包中
D. Double a=1.0是正确的java语句
答案:B、C、D
解析:
- A:
Cloneable
是接口(标记接口)。 - B:
Runnable
是接口(线程相关)。 - C:
Double
包装类在java.lang
。 - D:自动装箱(
double→Double
)。
45. 类声明规则
题目:类名为"MyClass.java"的类可被工程中所有类访问,正确声明为?
A. private class MyClass
B. class MyClass extends Object
C. public class MyClass
D. public class MyClass extends Object
答案:C、D
解析:
- 全局访问需
public
修饰(A/B默认包访问)。 extends Object
可省略(所有类默认继承Object)。
46. 面向对象特征及生活案例
题目:面向对象的特征有哪些?举例说明。
答案:
- 封装
- 隐藏实现细节,暴露接口。
- 例:汽车驾驶(无需了解发动机原理,通过方向盘/油门控制)。
- 继承
- 子类复用父类属性和方法。
- 例:电商系统——
User
为父类,Customer
/Admin
子类继承基础属性。
- 多态
- 同一接口不同实现。
- 例:支付系统——
Payment
接口,Alipay
/WeChatPay
实现不同支付逻辑。
47. 内存泄漏 vs 内存溢出
题目:说明内存泄漏和内存溢出的区别及解决方案。
答案:
类型 | 内存泄漏(Memory Leak) | 内存溢出(OOM) |
---|---|---|
定义 | 对象不再使用但未被GC回收 | 内存不足无法分配新对象 |
原因 | 长生命周期对象持有短生命周期对象引用 | 内存泄漏积累或数据量过大 |
检测工具 | VisualVM、MAT、JProfiler | JVM参数-XX:+HeapDumpOnOutOfMemoryError 生成堆转储 |
解决方案 | - 及时解引用(如List.clear() )- 避免静态集合类滥用 - 使用弱引用( WeakReference ) | - 增加JVM内存 - 优化数据结构 - 分批次处理数据 |
48. Java序列化
题目:什么是Java序列化?如何实现?应用场景?
答案:
- 序列化:将对象状态转换为字节流(便于存储/传输)。
- 实现方式:
public class User implements Serializable { private String name; // 无需重写方法(默认序列化) } // 序列化 try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("user.dat"))) { oos.writeObject(user); }
- 应用场景:
- 分布式系统传输对象(RPC框架如Dubbo)。
- 缓存对象到硬盘(Redis持久化)。
- HTTP Session存储(Tomcat会话保存)。
49. 绕过构造方法创建对象
题目:不通过构造函数也能创建对象吗?
答案:可以,方式包括:
- 反射:
Class.newInstance()
或Constructor.newInstance()
。 - 克隆:实现
Cloneable
接口,调用clone()
。 - 反序列化:
ObjectInputStream.readObject()
。
注意:克隆和反序列化不会调用构造方法。
50. 匿名内部类的继承与实现
题目:匿名内部类可不可以继承或实现接口?
答案:
- 可继承类:
new Thread() { @Override public void run() { /* 重写Thread方法 */ } }.start();
- 可实现接口:
Runnable r = new Runnable() { @Override public void run() { /* 实现Runnable */ } };
限制:
- 匿名内部类不能显式继承和实现同时存在(隐式继承Object)。
- 无法定义构造方法(需通过实例初始化块
{}
模拟)。