每个Java初学者都应该搞懂的问题
下面这组问题,每个学 Java 的人都应该搞懂。如果只是玩玩可以跳过;若自以为过了入门、却答不利落,不妨把自己重新算作初学者。
核心其实就几件事:引用指向对象、== 比的是变量里的值、String 不可变、final 锁的是引用而不是对象状态。
一、我声明了什么?
很多人会说:声明了一个内容是 Hello world! 的 String。更准确的说法是:声明了一个名叫 s、只能指向 String 的引用,当前指向那个字符串对象。并没有“声明一个 String 对象”,只是声明了引用变量。
若再写 String string = s;,只是多了一个引用 string,不会产生第二个对象,两个引用指向同一份内容。
二、== 和 equals 有什么区别?
== 专门比较变量里存的值是否相等。基本类型好理解:
对象就容易晕:
对象变量是引用,值是内存地址。两次 new 得到两块不同地址,所以 == 是 false。内容都是 foo,那是 equals 该管的事。
Object 默认的 equals 其实就是 ==:
自己的类若不覆盖 equals,结果就和 == 一样。极端实现甚至可以永远返回 true:
用 equals 或 HashSet / HashMap / Hashtable 之前,先看这个类的 equals 契约,别想当然。
| 比较 | 比什么 | 典型结果 |
|---|---|---|
== |
变量里的值(基本类型是数值,对象是地址) | new String("foo") 两次,为 false |
equals |
由类作者定义的“内容是否相等” | String 比字符序列;未覆盖则等同 == |
三、String 到底变了没有?
没有。String 被设计成不可变(immutable),所有对象都不可改。
s 原先指向内容为 Hello 的对象;做完 + 之后,s 改去指向新的 Hello world! 对象,旧对象还在内存里,只是没人再指向它。
因此频繁、不可预见地改字符串时,用 String 会制造大量对象,应考虑 StringBuffer。内容相同的字符串不必每次 new:
不要写成 s = new String("Initial Value");。后者每次都 new,又慢又费。对字符串常量,内容相同则 Java 视为同一个 String 对象;new 则总是新对象。不可变类只读、并发安全,但每个状态都要一个对象;标准库同时提供可变的 StringBuffer。
四、final 到底修饰了什么?
final 让变量“不变”,但对象变量本质是引用,于是有两层意思:引用本身不变,和引用指向的对象不变。
final 只锁引用存的地址,不保证对象内部状态不变。多线程里把共享对象标成 final,并不能阻止别人改它的内容。
五、到底要怎样初始化?
-
类的属性(域):JVM 会自动初始化;没写初值就用类型默认值。 int为 0,float为 0.0f,double为 0.0,boolean为 false,char为 0,long为 0,引用为null(数组引用也是)。 -
static属性在类加载时初始化一次;实例属性在创建实例时初始化。 -
方法里的局部变量必须先赋值再使用;只写在 if/try里、另一分支没有赋值,编译器会拒绝。 -
方法参数作为局部变量,传入值就是初值,不必再初始化。
六、instanceof 是什么?
instanceof 是二元运算符,也是关键字:测左边对象是不是右边类的实例,返回 boolean。
账单例子里用它区分 PhoneBill / GasBill 往往被看成没利用好多态。更面向对象的做法是重载两个 calculate:
绝大多数情况不推荐靠 instanceof 分支,应优先用多态。
一句话总结:声明的是引用不是对象;== 比地址、equals 比内容;String 不可变;final 锁引用不锁状态。
转载请注明来源:每个Java初学者都应该搞懂的问题







