打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
对象与内存控制
疯狂java读书笔记

当创建任何java对象时,程序总会先调用每个父类非静态初始化块、父类的构造器(总是从Object开始)执行初始化,最后才调用本类的非静态初始化块、构造器执行初始化。

隐式调用和显式调用
当调用某个类的构造器来创建java对象时,系统总会调用父类的非静态初始化块进行初始化,这个调用是隐式执行的。接着会调用父类的一个或多个构造函数执行初始化,可以通过super进行显示调用,也可以是隐式调用。最后调用本类的非静态构造块、构造函数,最后的最后返回本类的实例。

关于调用父类的哪个构造器执行初始化问题:
    1.子类构造器执行体的第一行代码使用super显示调用父类构造器,系统将根据super调用里传入的实参列表来确定调用父类哪个构造器
    2.子类构造器执行体的第一行代码使用this显示调用本类中重载的构造器,系统将根据this调用里传入的实参列表确定本类的另一个构造器
    3.子类构造器中既没有super调用,也没有this调用,系统将会在执行子类构造器之前,隐式调用父类无参数的构造器。

关于静态初始化块:静态初始化块是在类被jvm第一次加载时调用的,只调用一次,并且先调用父类的静态初始化块,然后是子类的,当所有静态初始化块被调用完后,才执行非静态初始化块和构造函数。

super调用用于显示调用父类的构造器,this调用构造函数用于显示调用本类中另一个重载的构造器。super调用和this调用构造器都只能在构造器中使用,而且都必须作为构造器的第一行代码,因此构造器中的super调用和this调用最多只能使用其中之一,而且this调用构造函数在一个类中最多只能使用一次(所以不会产生循环调用构造器的现象)。


访问子类对象的实例变量        
子类的方法可以访问父类的实例变量,但是父类的方法不能访问子类的实例变量,因为父类根本无从知道它将被哪个子类继承,它的子类将会增加怎样的成员变量。
但是,在极端的情况下,可能出现父类访问子类变量的情况。
class Base{
private int i = 2;
public Base(){
this.display();
}
public void display(){
System.out.println(i);
}
}

class Derived extends Base{
private int i = 22;
public Derived(){
i = 222;
}
public void display(){
System.out.println(i);
}
}

public class Test {
public static void main(String[] args){
new Derived();
}
}
这个程序输出是2? 22? 222?。实际上,输出结果为0。
首先澄清一个概念:java对象是由构造器创建的吗?
实际情况是:构造器只负责对java对象实例变量执行初始化(也就是赋初始值),在执行构造器代码之前,该对象所占的内存已经被分配下来。这些内存里的值都是默认值。
上例中,this代表谁
当this在构造器中时,this代表正在初始化的java对象。   此时的情况是:从源代码来看,此时的this位于Base()构造器内,但这些代码实际放在Derived()构造器内执行——是Derived()构造器隐式调用了Base()构造器的代码。由此可见,此时的this应该是Derived对象,而不是Base对象。
有一个问题,修改Base构造函数如下
public Base(){
this.display();
System.out.println(this.i);
System.out.println(this.getClass());
}
此时再执行程序,会发现输出:
0
2
class Derived
为什么直接this.i时会输出2呢?
    因为:这个this虽然代表Derived对象,但它却位于Base构造器中,它的编译时类型是Base,而实际引用一个Derived对象。通过输出this.getClass()可以发现
编译时类型vs运行时类型
当变量的编译时类型和运行时类型不同时,通过该变量访问它引用的对象的实例变量时,该实例变量的值由声明该变量的类型决定。当通过该变量调用它引用的对象的实例方法时,该方法行为将由它实际所引用的对象来决定。

调用被子类重写的方法
在访问权限允许的情况下,子类可以调用父类的方法,但是父类不能调用子类的方法,因为父类根本不知道它将被哪个子类继承,它的子类会增加什么方法。
但是,总有例外,有一种特殊的情况,当子类方法重写父类方法后,父类表面上只调用属于自己的、被子类重写的方法,但随着执行context的改变,将会变成父类实际调用子类的方法。
注意下面这个程序:
class Animal{
private String desc;
public Animal(){
this.desc = getDesc();
}
public String getDesc(){
return "Animal";
}
public String toString(){
return desc;
}
}

public class Wolf extends Animal{
private String name;
private String weight;
public Wolf(String name,String weight){
this.name = name;
this.weight = weight;
}
@Override
public String getDesc(){
return "Wolf [name:"+this.name+" weight:"+this.weight+"]";
}
public static void main(String[] args){
System.out.println(new Wolf("灰太狼","52.2l"));
}
}
这个程序输出的是:Wolf [name:null weight:null]
很明显,父类的构造函数调用了子类的方法,这里父类的构造函数中调用方法时省略了this,原理和前面讲的一样。都是构造函数和this的问题。至于为什么name和weight的值为null的问题,在前面2.1中实例变量和类变量中有介绍,在调用子类的构造器前,会调用父类的构造器。如果要输出name和weight的值不为null,则这个程序该怎么改进呢?
    把父类的toString()方法改成如下就行了:
        public String toString(){
return getDesc();
}
同时,应避免在构造器中调用被子类重写过的方法。
再申明一遍:构造器只负责对java对象实例变量执行初始化,在执行构造器代码之前,该对象所占的内存已经被分配下来,这些内存里的值是默认值。
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
C 之继承中的构造和析构学习总结
难得啊,C语言/C++基础面试知识大集合
Java面向对象中:方法重载和方法重写以及区别、 this关键字和super关键字以及区别
3. 类
精选30道Java笔试题解答
Java中super的几种用法并与this的区别
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服