问题
As far as In understand he getS
method from child class BB
overrides the same method from the parent class AA
. Yet although both classes have initialized the field s
which is returned by getS
it's printed as null
. Why is this happening?
This is the code:
public class AA {
public String getS() {
return s;
}
private String s = "hello1";
public AA() {
System.out.println(service() + getS());
}
public static String service() {
return "A service ";
}
}
public class BB extends AA {
private String s = "hello2";
@Override
public String getS() {
return s;
}
public static String service() {
return "B service ";
}
}
public class CC {
public static void main(String[] args) {
BB b =new BB(); //prints "A service null"
}
}
回答1:
When we call new SomeClass()
new
operator first creates object ofSomeClass
but that object has all its fields set to default values (0, '\0', false, null).After object is created code of constructor is executed to initialize object properly (set its fields to proper values, and possibly do few other things).
But if class has parent class, constructor first (implicitly or explicitly) calls its super()
constructor to ensure that all inherited fields are properly initialized, before we start using inherited methods which may depend on those fields state.
But methods are polymorphic (as long as they are not private
, static
or final
). This means that when in superclass we call method which was overridden, "newest version" of code will be executed (because polymorphism uses actual type of this
instance - returned by new
keyword - to locate class from which it should start searching for code which should be executed - this is called late or dynamic binding).
So since you called getS()
method in AA
superclass but on BB
instance (because that is what new BB
created), overridden code from BB
class was executed. Problem is that this code uses s
declared in BB
class, but that s
wasn't initialized yet. Lets see how default constructor of BB
which was invoked by new BB()
looks like:
BB(){
super(); //calls AA()
s = "hello2"; // by default `s` holds `null` value
// all initialization is moved to constructors
// after superconstructor call
}
So
- before
s = "hello2";
is executeds
(of BB class) holdsnull
- but before
s
is initialized to"hello2"
super()
is called (which internally callsgetS()
) - since
getS()
is polymorphic code fromBB
class for that method will be executed - but that code (from
BB
) usess
of BB which wasn't yet initialized, so it holdsnull
and that is what you see in console.
Because of that it is considered bad practice (in most cases) to put in constructor methods which can be overridden. We should limit ourselves to calling methods which are not polymorphic, which means either private
static
or final
.
回答2:
Here you can see what is the order of initialization when you create an object. That answer is based on Java Specs
Let me copy-paste
Object Initialization
An object is initialized whenever a new object is created, typically by evaluation of a class instance creation expression. This proceeds as follows:
Assign the arguments for the constructor to newly created parameter variables for this constructor invocation.
If this constructor begins with an explicit constructor invocation (§8.8.7.1) of another constructor in the same class (using this), then evaluate the arguments and process that constructor invocation recursively using these same five steps. If that constructor invocation completes abruptly, then this procedure completes abruptly for the same reason; otherwise, continue with step 5.
This constructor does not begin with an explicit constructor invocation of another constructor in the same class (using this). If this constructor is for a class other than Object, then this constructor will begin with an explicit or implicit invocation of a superclass constructor (using super). Evaluate the arguments and process that superclass constructor invocation recursively using these same five steps. If that constructor invocation completes abruptly, then this procedure completes abruptly for the same reason. Otherwise, continue with step 4.
Execute the instance initializers and instance variable initializers for this class, assigning the values of instance variable initializers to the corresponding instance variables, in the left-to-right order in which they appear textually in the source code for the class. If execution of any of these initializers results in an exception, then no further initializers are processed and this procedure completes abruptly with that same exception. Otherwise, continue with step 5.
Execute the rest of the body of this constructor. If that execution completes abruptly, then this procedure completes abruptly for the same reason. Otherwise, this procedure completes normally.
When new BB() is executed, the default constructor is invoked
public BB() {
super();
}
super()
is stated in your code as
public AA() {
System.out.println(service() + getS());
}
service()
is an static method and it can not be overriden, so A.service()
method is invoked and its result is concatenated with the result of invoking the overrided method B.getS()
At this point (see 5), the member B.s
has not been initialized yet, its value is null
.
And these are the reasons of obtaining
A service null
回答3:
It happens because of the class hierarchy. When run the
System.out.println(service() + getS());
It calls the overridden getS() method. In that it prints attribute s which declare in the class BB.(It not access s attribute in class AA because it private).
At that point(When running the constructor in class AA) attributes in class BB are not initialized. Because of that it prints null.
回答4:
The reason is BB doesn't have the constructor itself. new BB() calls to public AA() then the statement below will be execute:
System.out.println(service() + getS());
then it call inside A, so:
service: method of A
getS(): return default value of s (null),
calling constructor means the object wasn't created yet, then the initialize of properties (s) still null.
回答5:
the other answers have pointed out the problem.
what you can is to use an instance initialiser. since it is called before the constructor body. an instance initialiser is defined as a {} block inside the class.
public class AA {
private String s ;
{ //this is an instance initialiser.
//it is called before the constructor body
s = "hello1";
}
public AA() {
//the constructor body is called AFTER the instance initialiser
System.out.println(service() + getS());
}
public String getS() {
return s;
}
public static String service() {
return "A service ";
}
}
来源:https://stackoverflow.com/questions/48922077/why-child-private-field-is-null-when-printing-it-from-parent-class-using-polymor