原笔记整理了语言特点、面向对象、八种基本类型、命名和 instanceof。大方向可以保留,但几个答案把不同层次混在一起:字段的默认值套到局部变量、字节码的表示套到内存大小、语言风格套到性能高低。
这次按 Java 17 规范核对,并用完整程序和编译反例说明。不测对象布局、不跑性能基准,也不把一台 JVM 的行为扩大成所有实现的内存承诺。
1. 跨平台与面向对象:先说明保证范围
Java 通常先编译成 class 字节码,在兼容的运行环境上执行。但“跨平台”不是没有前提:字节码版本、依赖库、JNI、本机文件路径等都可能影响可运行性。
面向过程更强调步骤与函数,面向对象更强调职责、状态和对象协作,两种思想可以在同一程序中共存。封装、继承、多态是工具,不自动保证低耦合;继承关系设计不当,也会增加耦合。
原文“面向过程性能高,面向对象性能差”不能作为普遍结论。要比较性能,先确定实现、算法、数据分布、优化和测试条件。用没有实测的语言标签替代这些条件,解释不了一个具体程序为什么慢。
2. 八种基本类型:值的位宽不等于对象占用
| 类型 | 值或格式的范围 | 字段/数组元素默认值 | 包装类 |
|---|---|---|---|
| byte | 8位有符号整数 | 0 | Byte |
| short | 16位有符号整数 | 0 | Short |
| int | 32位有符号整数 | 0 | Integer |
| long | 64位有符号整数 | 0L | Long |
| float | binary32浮点格式 | 0.0f | Float |
| double | binary64浮点格式 | 0.0d | Double |
| char | 16位无符号UTF-16代码单元 | 数值为0的字符 | Character |
| boolean | true或false | false | Boolean |
这是语言层面的取值与默认值,不是对象内存布局表。Integer 还涉及对象或缓存、引用等,不能拿 int 的32位直接推出 Integer 对象只占4字节。JLS基本类型。
char 默认值不是引用 null,二者类型不同。一个 char 也不一定表示一个完整 Unicode 字符;某些码点需要两个 UTF-16 代码单元。
3. “int默认是0”必须说明它在哪里
实例字段、静态字段、数组元素有默认初始化;局部变量使用前必须满足明确赋值要求。JLS变量初始化。
文件名 DefaultsProbe.java:
public class DefaultsProbe { int count; Integer boxed; boolean enabled; char letter; public static void main(String[] args) { DefaultsProbe p = new DefaultsProbe(); System.out.println(p.count); System.out.println(p.boxed == null); System.out.println(p.enabled); System.out.println((int) p.letter); int[] numbers = new int[2]; System.out.println(numbers[0]); try { int value = p.boxed; throw new AssertionError("unboxing unexpectedly succeeded"); } catch (NullPointerException expected) { System.out.println("null cannot be unboxed"); } } }输出为 0、true、false、0、0、null cannot be unboxed。未初始化的 Integer 字段为 null;把它拆箱成 int 才触发这里的异常,单纯比较它是否为 null 不会异常。
下面则是故意无法编译的局部变量反例,不要和上面的程序放在同一个文件里:
class LocalBeforeInit { void test() { int count; System.out.println(count); } }编译器会报告可能尚未初始化。不存在“局部 int 没赋值,运行时就读出0”的语言保证。
4. 引用赋值:复制的是引用值,不是整份对象
赋值后:a ----+ +--> 同一个数组 b ----+ clone后:c -------> 另一个数组容器public class ReferenceProbe { public static void main(String[] args) { int[] a = {1, 2}; int[] b = a; b[0] = 9; System.out.println(a[0]); int[] c = a.clone(); c[0] = 7; System.out.println(a[0]); System.out.println(c[0]); Object value = "hello"; System.out.println(value instanceof String); System.out.println(value instanceof CharSequence); value = null; System.out.println(value instanceof String); String supplementary = new String(Character.toChars(0x1F600)); System.out.println(supplementary.length()); System.out.println(supplementary.codePointCount(0, supplementary.length())); } }输出依次为 9、9、7、true、true、false、2、1。
这里 int[] 的 clone 复制了基本类型元素;若改成对象数组,复制数组容器不等于递归复制每个对象。引用也可以指向已经存在的对象,不要求每次赋值都用 new。
instanceof 测试兼容的运行时类型关系,右边也可以是接口;null 的结果为 false。编译期不可转换的类型组合可能直接被拒绝,不能说任意两个类型都能拿来测试后返回 false。JLS instanceof。
5. boolean:不要用字节码槽位推算固定内存大小
JVM 对许多 boolean 操作使用 int 相关指令,boolean 数组有专门的支持;JVMS 也描述了 Oracle JVM 数组元素的编码方式。但规范描述抽象机,不规定所有 JVM 的具体对象布局。
因此不能从这些语句得出“单独 boolean 必定4字节,boolean[]元素在所有实现必定1字节”,更不能由局部变量槽位推算堆中字段大小。实际占用还受实现、布局和对齐等因素影响。JVMS boolean与抽象机说明。
本次没有用内存工具测量,更没有把打印 Boolean.SIZE 当办法:Boolean 没有数值包装类那样的 SIZE 常量。
6. 命名:语法规则与风格不是一回事
Java 标识符不只支持 ASCII 英文字母,还按 Unicode 的标识符规则定义。不能用数字开头,不能使用相应关键字;在 Java 17,单独的下划线也不是合法标识符。美元符号语法上可出现,不代表日常名字就应该随意用它。JLS标识符。
类名大驼峰、方法变量小驼峰属于约定,不是编译器强制所有名字都按该风格。公开顶层类在常规 javac 文件编译中还要注意类名与文件名一致,这和“所有类名都是文件名”不同。
7. 本次核验了什么
本地 JDK 17 编译运行 DefaultsProbe 和 ReferenceProbe,核对默认值、null拆箱异常、共享引用、数组复制、instanceof与UTF-16长度;局部变量未赋值的反例必须编译失败。另用编译检查验证非法标识符,不用“我记得不能这样写”作为唯一依据。
基础面试题往往只差一个限定词:字段还是局部?类型值还是对象占用?复制引用还是复制对象?把这个限定词补齐,答案才能在实际代码里成立。