简体   繁体   中英

Java: got an unexpected output

I'am learning Java and I got an unexpected output. That's the code:

public class Point {
    protected final int x,y;
    private final String name;

    public Point(int x,int y){
        this.x = x;
        this.y = y;
        name = makeName();
    }

    protected String makeName() {

        return "["+x+" ,"+y+"]";
    }
    public final String toString(){
        return name;
    }
}
public class ColorPoint extends Point{
    private final String color;

    public ColorPoint(int x,int y, String color){
        super(x,y);
        this.color = color;
    }

    protected String makeName(){
        return super.makeName() + ":" + color;
    }

    public static void main (String[] args){
        System.out.println(
                new ColorPoint(4,2,"viola"));
    }
}

The output is: [4,2]:null .
Why? Shouldn't it be just [4,2] because the variable String name is initialized first in makeName() method of the Point class and then should become unmutable? Am I wrong?

the makeName() method is called before you set this.color = color ;

The makeName() method is called by your super.construtctor when the color variable is still null .

super(x,y)调用ColorPoint类的makeName()方法,直到那时才分配颜色。

Flow is like this,

Point constructor > makeName of ColorPoint > makeName of Point

since, makeName of ColorPoint is called first, till that time color property value was not assigned, so its giving null

makeName() is called in the constructor of the superclass. When it is called color is not yet set so the default value of color is null.

To solve the problem you have to explicitly call makeName() at the end of constructor. Here is the correct code:

public ColorPoint(int x,int y, String color){
    super(x,y);
    this.color = color;
    makeName();
}
 new ColorPoint(4,2,"viola"));

在第22行调用构造函数,然后在第5行调用构造函数,在第5行调用构造函数在第27行调用覆盖的函数make name,该函数将name设置为[4,2]:null,因为尚未初始化颜色。

final fields are initialized with default values (which for references is null ). You can easily check it with this code:

class FinalTest{
    public final String value = test();

    private String test() {
        System.out.println("current state of value is '"+value+"'");
        return "foo";
    }

    public static void main(String[] args) {
        FinalTest ft = new FinalTest();
        System.out.println(ft.value);
    }
}

which produces output:

current state of value is 'null'
foo

So as you see final variable has its default value which can be later modified in constructor once.

But lets get back to your example. When you call

System.out.println(new ColorPoint(4, 2, "viola"));

you are creating invoking constructor of ColorPoint class, and then on this instance toString will be called. But lets take a closer look at ColorPoint constructor:

public ColorPoint(int x, int y, String color) {
    super(x, y);
    this.color = color;
}

As you see before this.color will be set you are invoking super(x,y) which code looks like this:

public Point(int x, int y) {
    this.x = x;
    this.y = y;
    name = makeName();
}

But here in makeName(); thanks to polymorphism ( late binding ) you are using code of makeName from ColorPoint class which looks like

protected String makeName() {
    return super.makeName() + ":" + color;
}

So at start you will get result of super.makeName() which is "[" + x + " ," + y + "]" but then you are trying to access color which as you remember is not initialized with because

super(x, y); //<-- we are still here
this.color = color;//this wasn't invoked (yet)

so color still has its default value ( null ).

This means that name will be set to [4 ,2]:null .

Since ColorPoint inherits toString from Point which looks like

public final String toString() {
    return name;
}

you see as output value stored in name which is [4 ,2]:null .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM