
变量声明必须带类型,不能省略int、String这些
Java 是静态类型语言,声明变量时类型是强制的,不像 Python 或 JavaScript 那样靠赋值推断。漏写类型会直接编译失败,报错信息通常是 error: cannot find symbol 或 error: illegal start of expression。
常见错误现象:
- 写成
age = 25;(没声明类型)→ 编译不通过 - 在方法外写
int x = 10;但没加static→ 如果是类字段又不在 static 上下文中,可能引发初始化顺序问题
实操建议:
- 局部变量必须在使用前声明并初始化,否则读取会报
error: variable x might not have been initialized - 类字段(成员变量)有默认值(如
int默认是 0,Object默认是null),但别依赖它,显式初始化更安全 - 避免用
var(Java 10+)在复杂表达式中,比如var list = new ArrayList();推导出的是ArrayList而非List,影响后续多态使用
final 是常量的唯一可靠标识,不是大写命名就代表常量
Java 没有 const 关键字,final 才是让变量“不可变”的语法手段。只靠命名规范(比如 MAX_SIZE)不会阻止修改,运行时照样能改——除非加了 final。
立即学习“Java免费学习笔记(深入)”;
使用场景:
- 基本类型 +
final→ 真正不可变(值锁死) - 引用类型 +
final→ 引用不可变(不能指向新对象),但对象内部状态仍可变(比如final List<string> names = new ArrayList(); names.add("a");</string>合法) - 想让整个对象不可变,得配合不可变类(如
ImmutableList)或手动封装
容易踩的坑:
-
static final常量在类加载时初始化,如果初始化过程抛异常,会导致类加载失败,后续所有对该类的引用都会触发NoClassDefFoundError - 非
static的final字段必须在构造器结束前赋值,否则编译报error: variable x might not have been initialized
字符串字面量是常量,但 new String("x") 不是
Java 中字符串常量池的存在,让字面量(如 "hello")天然具备常量属性:相同字面量共享同一对象,且不可修改内容(String 本身不可变)。但用 new String("x") 显式创建的,哪怕内容一样,也是堆上新对象,不进常量池。
性能与兼容性影响:
-
"a" == "a"是true;new String("a") == "a"是false(引用比较) - 用
==比较字符串是否相等?几乎总是错的,该用.equals() - 大量
new String(x)会绕过常量池,浪费内存,尤其在循环里拼接或解析时
实操建议:
- 除非明确需要新对象(比如反射中绕过 intern 限制),否则直接用字面量
- 从外部读入的字符串(如
Scanner.nextLine())一定不是常量池对象,需调用.intern()才能进池——但注意 JDK 7+ 后常量池在堆里,intern()成本已降低,仍需权衡
局部变量和成员变量同名时,不加 this. 就会覆盖
当构造器或方法参数名、局部变量名和成员变量名一致时,Java 默认绑定到最近作用域的变量。这意味着你可能以为在给成员变量赋值,其实只是改了参数或局部变量。
常见错误现象:
- 构造器里写
name = name;→ 实际是参数给自己赋值,成员变量仍是null或默认值 - IDE 通常会警告
Assignment to itself,但不是所有环境都开这个检查
实操建议:
- 一律用
this.name = name;明确区分,这是最无歧义的写法 - 不推荐靠命名差异(比如参数叫
nameArg)来规避,增加认知负担,也破坏通用约定 - Lombok 的
@Data或@Setter会自动生成带this.的赋值逻辑,但手写时千万别省
复杂点在于:这种覆盖行为在嵌套作用域(比如 lambda 内部)也会发生,而 lambda 里访问外部局部变量还要求其为 final 或“事实上 final”,稍不注意就编译不过。











