每个Java初学者都应该搞懂的问题

    |     2015年4月27日   |   Java面试题   |     0 条评论   |    1181

下面这组问题,每个学 Java 的人都应该搞懂。如果只是玩玩可以跳过;若自以为过了入门、却答不利落,不妨把自己重新算作初学者。

核心其实就几件事:引用指向对象、== 比的是变量里的值、String 不可变、final 锁的是引用而不是对象状态。

一、我声明了什么?

String s = "Hello world!";

很多人会说:声明了一个内容是 Hello world! 的 String。更准确的说法是:声明了一个名叫 s、只能指向 String 的引用,当前指向那个字符串对象。并没有“声明一个 String 对象”,只是声明了引用变量。

若再写 String string = s;,只是多了一个引用 string,不会产生第二个对象,两个引用指向同一份内容。

二、== 和 equals 有什么区别?

== 专门比较变量里存的值是否相等。基本类型好理解:

int a = 10;
int b = 10;
// a == b 为 true

对象就容易晕:

String a = new String("foo");
        String b = new String("foo");
        // a == b 为 false

对象变量是引用,值是内存地址。两次 new 得到两块不同地址,所以 == 是 false。内容都是 foo,那是 equals 该管的事。

Object 默认的 equals 其实就是 ==:

boolean equals(Object o) {
    return this == o;
}

自己的类若不覆盖 equals,结果就和 == 一样。极端实现甚至可以永远返回 true:

class Monster {
    private String content;
    boolean equals(Object another) {
        return true;
    }
}

用 equals 或 HashSet / HashMap / Hashtable 之前,先看这个类的 equals 契约,别想当然。

比较 比什么 典型结果
== 变量里的值(基本类型是数值,对象是地址) new String("foo") 两次,为 false
equals 由类作者定义的“内容是否相等” String 比字符序列;未覆盖则等同 ==

三、String 到底变了没有?

没有。String 被设计成不可变(immutable),所有对象都不可改。

String s = "Hello";
s = s + " world!";

s 原先指向内容为 Hello 的对象;做完 + 之后,s 改去指向新的 Hello world! 对象,旧对象还在内存里,只是没人再指向它。

因此频繁、不可预见地改字符串时,用 String 会制造大量对象,应考虑 StringBuffer。内容相同的字符串不必每次 new:

public class Demo {
    private String s;
    public Demo() {
        s = "Initial Value";
    }
}

不要写成 s = new String("Initial Value");。后者每次都 new,又慢又费。对字符串常量,内容相同则 Java 视为同一个 String 对象;new 则总是新对象。不可变类只读、并发安全,但每个状态都要一个对象;标准库同时提供可变的 StringBuffer。

四、final 到底修饰了什么?

final 让变量“不变”,但对象变量本质是引用,于是有两层意思:引用本身不变,和引用指向的对象不变。

final StringBuffer a = new StringBuffer("immutable");
final StringBuffer b = new StringBuffer("not immutable");
a = b; // 编译期错误

final StringBuffer a = new StringBuffer("immutable");
a.append(" broken!"); // 编译通过

final 只锁引用存的地址,不保证对象内部状态不变。多线程里把共享对象标成 final,并不能阻止别人改它的内容。

五、到底要怎样初始化?

  • 类的属性(域):JVM 会自动初始化;没写初值就用类型默认值。int 为 0,float 为 0.0f,double 为 0.0,boolean 为 false,char 为 0,long 为 0,引用为 null(数组引用也是)。
  • static 属性在类加载时初始化一次;实例属性在创建实例时初始化。
  • 方法里的局部变量必须先赋值再使用;只写在 if / try 里、另一分支没有赋值,编译器会拒绝。
  • 方法参数作为局部变量,传入值就是初值,不必再初始化。

六、instanceof 是什么?

instanceof 是二元运算符,也是关键字:测左边对象是不是右边类的实例,返回 boolean。

String s = "I AM an Object!";
boolean isObject = s instanceof Object; // true

账单例子里用它区分 PhoneBill / GasBill 往往被看成没利用好多态。更面向对象的做法是重载两个 calculate:

public double calculate(PhoneBill bill) {
    // 计算电话账单
}
public double calculate(GasBill bill) {
    // 计算燃气账单
}

绝大多数情况不推荐靠 instanceof 分支,应优先用多态。

一句话总结:声明的是引用不是对象;== 比地址、equals 比内容;String 不可变;final 锁引用不锁状态。

转载请注明来源:每个Java初学者都应该搞懂的问题
本文链接地址:https://ai.zhousir.top/?p=589

上一篇:

下一篇:

回复 取消