首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 程式設計入門

達內金牌講師唐亮 Java 語言細節進階(中):深克隆、對象池、浮點數陷阱、奇數判定

     編輯:Java 程式設計入門

Java 基礎語法看似簡單,但細節處往往藏著坑。本文輯錄達內金牌講師唐亮講義精華,聚焦四大高頻考點與實戰陷阱:深克隆三種實現、String/包裝類對象池機制、浮點數比較失真、負數取模奇數判定,附帶標準化代碼模板與最佳實踐。

一、 深克隆:徹底切斷對象圖引用

1.1 淺克隆陷阱

class Teacher { String name; int age; }
class Student implements Cloneable {
    String name; int age; Teacher t;
    // 僅調用 super.clone() → 引用拷貝,修改 s2.t 會影響 s1.t
}

1.2 方案一:手動實現 Cloneable 遞歸克隆(推薦,性能最優)

class Teacher implements Cloneable {
    String name; int age;
    Teacher(String name, int age) { this.name = name; this.age = age; }
    @Override protected Teacher clone() {
        try { return (Teacher) super.clone(); }
        catch (CloneNotSupportedException e) { throw new AssertionError(e); }
    }
}

class Student implements Cloneable {
    String name; int age; Teacher t;
    Student(String name, int age, Teacher t) { this.name = name; this.age = age; this.t = t; }
    @Override protected Student clone() {
        try {
            Student s = (Student) super.clone();
            s.t = t.clone(); // 關鍵:手動克隆引用對象
            return s;
        } catch (CloneNotSupportedException e) { throw new AssertionError(e); }
    }
}

1.3 方案二:序列化反序列化深拷貝(通用,但較慢)

class Teacher implements Serializable { /* ... */ }
class Student implements Serializable {
    String name; int age; Teacher t;
    public Student deepClone() throws IOException, ClassNotFoundException {
        try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
             ObjectOutputStream oos = new ObjectOutputStream(bos)) {
            oos.writeObject(this);
            try (ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
                 ObjectInputStream ois = new ObjectInputStream(bis)) {
                return (Student) ois.readObject();
            }
        }
    }
}

前提:整個對象圖所有類必須實現 Serializable,非序列化字段標 transient。

1.4 方案三:第三方庫(生產首選)

  • SerializationUtils.clone(obj) (Apache Commons Lang)
  • MapperFacade.clone(obj) (Orika/MapStruct)
  • JSON 往返:JSON.parseObject(JSON.toJSONString(obj), Clazz.class) (FastJson/Jackson)

二、 String 與包裝類對象池:== vs equals 的分水嶺

2.1 字符串常量池

String s1 = "hello";        // 常量池獲取/創建
String s2 = "hello";        // 同一對象
String s3 = new String("hello"); // 堆上新建對象
System.out.println(s1 == s2); // true
System.out.println(s1 == s3); // false
System.out.println(s1.equals(s3)); // true

JDK 7+ 常量池移至堆,JDK 8+ 永久代移除,但語義不變:字面量自動進池,new 強制新建。

2.2 基本類型包裝類緩存範圍

包裝類緩存範圍備註
Integer-128 ~ 127可通過 -XX:AutoBoxCacheMax 調大上限
Short / Byte-128 ~ 127全範圍緩存
Long-128 ~ 127
Character0 ~ 127
BooleanTRUE / FALSE只有兩個實例
Float / Double無緩存每次 new
Integer i1 = 5, i2 = 5;        // 自動裝箱 → valueOf(5) → 緩存 → true
Integer i3 = new Integer(5);   // 強制新建 → false
System.out.println(i1 == i2);  // true
System.out.println(i1 == i3);  // false

2.3 自定義對象池模式

class Student {
    private String name; private int age;
    private static final Map<String, Student> POOL = new ConcurrentHashMap<>();

    private Student(String name, int age) { this.name = name; this.age = age; }

    public static Student newInstance(String name, int age) {
        String key = name + "#" + age;
        return POOL.computeIfAbsent(key, k -> new Student(name, age));
    }
    // equals/hashCode 基於 name+age
}

適用於不可變值對象、高頻創建、實例數可控場景(如顏色、坐標、貨幣單位)。

三、 浮點數比較:2.0 - 1.1 != 0.9

3.1 根本原因

IEEE 754 二進制無法精確表示十進制小數,2.0 - 1.1 實際結果為 0.8999999999999999。

3.2 標準比較模板

public static boolean equals(double a, double b, double epsilon) {
    return Math.abs(a - b) < epsilon;
}

// 使用
if (equals(2.0 - 1.1, 0.9, 1e-10)) { /* 相等 */ }

// 金融/精確計算:強制使用 BigDecimal
BigDecimal a = BigDecimal.valueOf(2.0);
BigDecimal b = BigDecimal.valueOf(1.1);
System.out.println(a.subtract(b).compareTo(BigDecimal.valueOf(0.9)) == 0); // true

四、 奇數判定:負數取模陷阱

4.1 錯誤寫法

public boolean isOdd(int n) {
    return n % 2 == 1; // -3 % 2 == -1 → false (錯誤)
}

4.2 正確寫法

public boolean isOdd(int n) {
    return n % 2 != 0; // -3 % 2 == -1 != 0 → true
    // 或位運算(最快)
    // return (n & 1) != 0;
}

Java 取模符號遵循被除數(左操作數),-3 % 2 = -1,3 % -2 = 1。判斷奇偶只需關注「餘數是否為 0」。

五、 總結速查表

場景錯誤姿勢正確姿勢
深克隆super.clone() 不管引用字段手動遞歸 clone / 序列化 / 第三方庫
字符串比較== 比較內容equals() 比較內容,== 僅比較引用
包裝類比較== 比較數值equals() 或拆箱後 ==
浮點數相等a == bMath.abs(a-b) < eps 或 BigDecimal
判斷奇數n % 2 == 1n % 2 != 0 或 (n & 1) != 0

細節決定質量。將這些「反直覺」的語言特性內化為肌肉記憶,配合 SpotBugs、Error Prone、SonarLint 靜態分析工具攔截,能在編碼階段消滅 90% 以上的低級 Bug,讓 Code Review 聚焦於業務邏輯與架構設計。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved