[JAVA] mutable and immutable objects

I was learning Java Silver SE11, and I didn't understand the meaning of "String object is an immutable object", so I added a memorandum.

Difference between mutable and immutable

What is the difference between mutable (variable) and immutable (immutable) in the first place? Simply put, a mutable object is an object ** that can change the value once set ** later. An immutable object is an object that cannot change the value once set **. It is as it is.

The definition method of immutable object is as follows.

--Qualify all fields with private --Do not provide methods (such as setters) that can change the state inside the object --Declare the class in final to ensure that the method is not overridden (do not change from subclass) --If you have a variable object inside, don't provide that object to the outside (getter, etc.)

From the next section, we will check the details of each by creating an immutable class: Hoge. (Other classes are listed, but these are not immutable.)

Qualify all fields with private

This is easy to understand, isn't it? If you qualify with public, you can change it as much as you want.

Test.java


public class Test {
    public static void main(String args[]){
        Hoge hoge = new Hoge("Daigouji", "Guy" ,18);
    }
}

class Hoge{
    private String name;
    private int age;
    private Foo foo;

    Hoge(){}
    Hoge(String lastName, String firstName, int age){
        foo = new Foo(lastName, firstName);
        this.name = lastName + firstName;
        this.age = age;
    }

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

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

    public Foo getFoo() { return foo; }
    public void setFoo(Foo foo) { this.foo = foo; }

    public void dummy(){}
}

class Foo{
    private String lastName;
    private String firstName;
    Foo(String lastName, String firstName){ this.lastName = lastName; this.firstName = firstName; }

    public void setLastName(String lastName) { this.lastName = lastName; }
    public void setFirstName(String firstName) { this.firstName = firstName; }
    public String getLastName() { return lastName; }
    public String getFirstName() { return firstName; }
}

The access modifiers for the fields, name, age, and foo of class Hoge are private. Yes.

Do not provide methods (such as setters) that can change the state inside the object

This is also easy to understand. Even if you qualify with private, it doesn't mean anything if you can change it via a method.

Test.java


public class Test {
    public static void main(String args[]){
        Hoge hoge = new Hoge("Daigouji", "Guy" ,18);
    }
}

class Hoge{
    private String name;
    private int age;
    private Foo foo;

    Hoge(){}
    Hoge(String lastName, String firstName, int age){
        foo = new Foo(lastName, firstName);
        this.name = lastName + firstName;
        this.age = age;
    }

    public String getName() { return name; }
    public int getAge() { return age; }
    public Foo getFoo() { return foo; }

    public void dummy(){}
}

class Foo{
    private String lastName;
    private String firstName;
    Foo(String lastName, String firstName){ this.lastName = lastName; this.firstName = firstName; }

    public void setLastName(String lastName) { this.lastName = lastName; }
    public void setFirstName(String firstName) { this.firstName = firstName; }
    public String getLastName() { return lastName; }
    public String getFirstName() { return firstName; }
}

You can no longer make changes via the method.

Declare the class in final to ensure that the method is not overridden (do not change from subclass)

Class: Let's create a HogeSub that inherits Hoge.

Test.java


public class Test {
    public static void main(String args[]){
        HogeSub hogesub = new HogeSub("Daigouji", "Guy", 18);
        System.out.println("lastName:" + hogesub.getFoo().getLastName() + " firstName:" + hogesub.getFoo().getFirstName());
        System.out.println("-----------------");
        hogesub.setLastName("Yamada");
        hogesub.setFirstName("Jiro");
        hogesub.dummy();
        System.out.println("lastName:" + hogesub.getFoo().getLastName() + " firstName:" + hogesub.getFoo().getFirstName());
    }
}

class Hoge{
    private String name;
    private int age;
    private Foo foo;

    Hoge(){}
    Hoge(String lastName, String firstName, int age){
        foo = new Foo(lastName, firstName);
        this.name = lastName + firstName;
        this.age = age;
    }

    public String getName() { return name; }
    public int getAge() { return age; }
    public Foo getFoo() { return foo; }
    public void dummy(){}
}

class HogeSub extends Hoge {
    private String lastName;
    private String firstName;

    HogeSub(String lastName, String firstName, int age) {
        super(lastName, firstName, age);
        this.lastName = lastName;
        this.firstName = firstName;
    }

    @Override
    public void dummy() {
        super.getFoo().setFirstName(this.firstName);
        super.getFoo().setLastName(this.lastName);
    }

    public void setLastName(String lastName) { this.lastName = lastName; }
    public void setFirstName(String firstName) { this.firstName = firstName; }
}

class Foo{
    private String lastName;
    private String firstName;
    Foo(String lastName, String firstName){ this.lastName = lastName; this.firstName = firstName; }

    public void setLastName(String lastName) { this.lastName = lastName; }
    public void setFirstName(String firstName) { this.firstName = firstName; }
    public String getLastName() { return lastName; }
    public String getFirstName() { return firstName; }
}

The execution result is as follows.

Execution result


lastName:Daigouji firstName:Guy
-----------------
lastName:Yamada firstName:Jiro

HogeSub overrides Hoge's dummy method and modifies the foo field. To avoid such an implementation, declare Hoge as final.

Test.java


public class Test {
    public static void main(String args[]){
        Hoge hoge = new Hoge("Daigouji", "Guy", 18);
    }
}

final class Hoge{
    private String name;
    private int age;
    private Foo foo;

    Hoge(){}
    Hoge(String lastName, String firstName, int age){
        foo = new Foo(lastName, firstName);
        this.name = lastName + firstName;
        this.age = age;
    }

    public String getName() { return name; }
    public int getAge() { return age; }
    public Foo getFoo() { return foo; }
    public void dummy(){}
}

class Foo{
    private String lastName;
    private String firstName;
    Foo(String lastName, String firstName){ this.lastName = lastName; this.firstName = firstName; }

    public void setLastName(String lastName) { this.lastName = lastName; }
    public void setFirstName(String firstName) { this.firstName = firstName; }
    public String getLastName() { return lastName; }
    public String getFirstName() { return firstName; }
}

You can no longer change fields by overriding.

If you have a mutable object inside, don't provide that object to the outside (getter, etc.)

In the previous stage, the value of the object was changed via the getter of the field: foo. Hoge.getFoo().setXXXName(”xxx”); If you do this kind of operation, you can't say that it's immutable. Also delete the getter.

Test.java


public class Test {
    public static void main(String args[]){
        Hoge hoge = new Hoge("Daigouji", "Guy", 18);
    }
}

final class Hoge{
    private String name;
    private int age;
    private Foo foo;

    Hoge(){}
    Hoge(String lastName, String firstName, int age){
        foo = new Foo(lastName, firstName);
        this.name = lastName + firstName;
        this.age = age;
    }
    public void dummy(){}
}

class Foo{
    private String lastName;
    private String firstName;
    Foo(String lastName, String firstName){ this.lastName = lastName; this.firstName = firstName; }

    public void setLastName(String lastName) { this.lastName = lastName; }
    public void setFirstName(String firstName) { this.firstName = firstName; }
    public String getLastName() { return lastName; }
    public String getFirstName() { return firstName; }
}

You now have an immutable class: Hoge. The finished Hoge can't do anything, but please forgive me as a sample ...

You can change the contents of the String type variable.

** * This section is a guess. Please point out any mistakes. ** **

Test2.java


public class Test2 {
    public static void main(String args[]){
        String str = "Sutoringu 1";
        System.out.println(str);
        System.out.println("-----------------");
        str = "Sutoringu 2";
        System.out.println(str);
    }
}

Execution result


Sutoringu 1
-----------------
Sutoringu 2

Certainly the content of str has changed. It's important to note that ** the value of the variable str has changed (referenced to), but the value of the instance hasn't changed **.

I will follow it with a debugger. Set breakpoints as follows: image.png

First of all, from the contents at the first breakpoint. image.png

Next, the content at the second breakpoint. image.png

You can see that the reference destination of str has changed.

The String class is special, and you can instantiate it with the assignment operator without using the new operator. (str =" Storingu 2 "; and str = new String ("Storingu 2 "); are both the same process) In other words, setting a string in a String type variable with the assignment operator is synonymous with creating an instance each time. (Strictly speaking, it is not always generated every time)

Depending on how you use it, you may end up consuming more and more memory on the JVM. I presume that the RingBuilder class was created to solve these problems.

Finally

After all, I felt that the output would deepen my understanding (although it is speculative). I will continue to do my best to acquire Silver / SE11.

Recommended Posts

mutable and immutable objects
Comparable and Comparator (ordering objects)
Java primitive types, reference types, Immutable, Mutable
About the classification and concept of Immutable / Mutable / Const / Variable of Java and Kotlin.
Convert Java org.w3c.dom.Document objects and XML strings
Difference between final and Immutable in Java
Explanation of Ruby Time and Date objects
Comparison of JavaScript objects and Ruby classes