[Java基礎]

Duancf發表於2024-07-09

資料型別

Java的資料型別包括基本資料型別和引用資料型別:

基本資料型別:

  • 整形:byte, short, int, long

  • 浮點型:float, double

  • 字元型:char

  • 布林型:boolean

引用資料型別:class, interface, array。

引用複製,淺複製,深複製

引用複製
在Java中,物件的引用複製是指將一個物件的引用賦值給另一個變數。透過引用複製,兩個變數將指向同一個物件,它們共享同一塊記憶體空間。當修改其中一個變數指向的物件時,另一個變數也會受到影響。

下面是一個簡單的示例程式碼,演示了物件引用複製的概念:

public class Test {
    public static void main(String[] args) {

        // 建立一個Person物件
        Person person1 = new Person("Alice", 25);

        // 將person1的引用複製給person2
        Person person2 = person1;

        System.out.println("person1: " + person1);
        System.out.println("person2: " + person2);

        // 修改person1的屬性
        person1.setName("Bob");
        person1.setAge(30);

        System.out.println("person1: " + person1);
        System.out.println("person2: " + person2);
		
        // True
        System.out.println(person1 == person2);

    }

}


class Person {
    private String name;
    private int age;
    
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    public void setName(String name) {
        this.name = name;
    }
    
    public void setAge(int age) {
        this.age = age;
    }
    
    @Override
    public String toString() {
        return "Person[name=" + name + ", age=" + age + "]";
    }
}

在上述程式碼中,建立了一個Person物件person1,然後將其引用複製給person2。修改person1的屬性後,列印person1和person2的值,可以發現它們指向同一個物件,因此修改person1的屬性後,person2的屬性也會相應改變。

輸出結果如下:

person1: Person[name=Alice, age=25]
person2: Person[name=Alice, age=25]
person1: Person[name=Bob, age=30]
person2: Person[name=Bob, age=30]
true
可以看到,透過引用複製,person1和person2指向同一個Person物件,修改其中一個物件的屬性會影響另一個物件。

淺複製
在Java中,淺複製(Shallow Copy)是指建立一個新物件,並將原始物件中的所有欄位的值複製到新物件中。淺複製只是簡單地複製物件的欄位值,如果物件中包含引用型別的欄位,則新物件和原始物件會共享這些引用型別的欄位。

淺複製是透過複製物件的引用來實現的,也就是說,新物件和原始物件指向了同一個記憶體地址。因此,如果修改了新物件的引用型別欄位的值,原始物件的引用型別欄位的值也會被修改,因為它們指向同一個物件。

要實現淺複製,可以使用以下幾種方式:

使用Object類的clone()方法:Object類中的clone()方法可以複製一個物件,但它只複製物件的欄位值,不復制引用型別欄位指向的物件。因此,如果物件中包含引用型別欄位,那麼新物件和原始物件會共享這些引用型別的欄位。

使用複製建構函式:透過定義一個建構函式,引數為原始物件,將原始物件中的欄位值複製到新物件中。

需要注意的是,淺複製只是複製物件的欄位值,而不復制物件內部的引用型別欄位的值。因此,如果需要實現深複製,就需要對引用型別欄位進行遞迴複製,確保新物件和原始物件的引用型別欄位指向不同的物件。

public class Address {
    private String address;

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public Address(String address) {
        this.address = address;
    }
}

class Person implements Cloneable {
    private String name;
    private int age;

    private Address address;

    public Person(String name, int age, Address address) {
        this.name = name;
        this.age = age;
        this.address = address;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    public Address getAddress() {
        return address;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return this.name + "-" + this.age + "-" + this.address.getAddress();
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

public class Test {
    public static void main(String[] args) throws CloneNotSupportedException {

        Address address1 = new Address("山東");
        Person person1 = new Person("Alice", 25, address1);
        Person person2 = (Person) person1.clone();

        System.out.println(person1);
        System.out.println(person2);

        person1.setAge(30);
        address1.setAddress("上海");

        System.out.println(person1);
        System.out.println(person2);
        
        //Alice-25-山東
        //Alice-25-山東
        //Alice-30-上海
        //Alice-25-上海

    }

}

深複製
在 Java 中,深複製(Deep Copy)是指建立一個新物件,將原始物件中的所有欄位的值都複製到新物件中。深複製會遞迴複製物件的所有層級,包括物件的引用型別欄位,確保新物件和原始物件完全獨立相對地,淺複製(Shallow Copy)只是簡單地複製物件的欄位值,如果物件中包含引用型別的欄位,那麼新物件和原始物件會共享這些引用型別的欄位。

為了實現深複製,可以使用以下幾種方式:

實現 Cloneable介面和覆寫 clone() 方法:透過實現 Cloneable 介面來指明物件可以進行複製操作,並覆寫 clone() 方法來實現深複製的邏輯。

public class Test {
    public static void main(String[] args) throws CloneNotSupportedException {
        
        Person person1 = new Person("Alice", 25, new Address("山東"));
        Person person2 = (Person) person1.clone();

        System.out.println(person1);
        System.out.println(person2);

        person2.setName("david");
        person2.setAge(30);
        person2.getAddress().setAddress("上海");

        System.out.println(person1);
        System.out.println(person2);
        
        
        //Alice-25-山東
        //Alice-25-山東
        //Alice-25-山東
        //david-30-上海

    }

}

public class Address implements Cloneable{
    private String address;

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public Address(String address) {
        this.address = address;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Address clone = (Address) super.clone();
        clone.setAddress(this.address);
        return clone;
    }
}




class Person implements Cloneable {
    private String name;
    private int age;

    private Address address;

    public Person(String name, int age, Address address) {
        this.name = name;
        this.age = age;
        this.address = address;
    }

    public Person() {

    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    public Address getAddress() {
        return address;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return this.name + "-" + this.age + "-" + this.address.getAddress();
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Person res = (Person) super.clone();
        res.address = (Address) this.address.clone();
        return res;
    }
}


使用序列化和反序列化:透過將物件序列化為字流,再將位元組流反序列化為新物件來實現深複製。

public class MyClass implements Serializable {
    private int value;
    private MyObject obj;

    // 建構函式和其他方法
    
    public MyClass deepCopy() throws IOException, ClassNotFoundException {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(this); // 序列化當前物件
    
        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);
        return (MyClass) ois.readObject(); // 反序列化生成新物件
    }

}

需要注意的是,為了現深複製,不僅僅需要複製物件本身,還需要複製物件內部的引用型別欄位。因此,如果引用型別欄位也需要支援深複製,那麼該引用型別也需要實現 Cloneable 介面或者序列化介面,並在複製方法中進行遞迴拷。

淺複製和深複製的區別
在Java中,淺複製(Shallow Copy)和深複製(Deep Copy)是兩種不同的物件複製方式,它們的主要區別如下:

  • 複製的內容不同:
    • 淺複製只複製物件的欄位值,不復制引用型別欄位指向的物件。
    • 深複製會遞迴複製物件的所有層級,包括物件的引用型別欄位。
  • 物件的獨立性不同:
    • 淺複製複製後的物件與原始物件共享相同的引用型別欄位,即它們指向相同的物件。
    • 深複製複製後的物件與原始物件完全獨立,它們的引用型別欄位指向不同的物件。
  • 物件修改的影響不同:
    • 淺複製中,如果修改新物件的引用型別欄位的值,原始物件的相應欄位也會被修改,因為它們指向同一個物件。
    • 深複製中,修改新物件的引用型別欄位的值不會影響原始物件的相應欄位,因為它們指向不同的物件。
  • 實現方式不同:
    • 淺複製可以透過Object類的clone()方法、複製建構函式等方式實現。
    • 深複製需要對物件的引用型別欄位進行遞迴複製,可以透過實現Cloneable介面和覆蓋clone()方法、使用序列化和反序列化等方式實現。

總結來說,淺複製只複製物件的欄位值,不復制引用型別欄位指向的物件,而深複製會遞迴複製所有層級的物件,確保新物件和原始物件完全獨立。因此,深複製相比淺複製更加安全,但也需要更多的資源和效能開銷。選擇使用哪種複製方式,需要根據具體的場景和需求來決定。