毕向东Java基础教程-继承【上】

2019-12-17  本文已影响0人  Lois_Huang

概述

特点

super关键字

this和super的用法很相似。

注意:
问题:父类中的私有内容,子类是否具备——实际上子类对于父类中私有的成员变量是继承了的,即定义子类对象时,在堆中存有父类的私有成员变量(上图堆中fu的方框内),只是不能直接访问,所以确切地说,应该是子类中不能直接地访问父类中的私有内容。

Example2

class Person
{
    private String name;
    private int age;

    Person(String name, int age)
    {
        this.name = name;
        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;}
}

class Student extends Person
{
    Student(String name, int age)
    {
        super(name, age);//调用父类的构造函数
    }
}
class Worker extends Person
{
    Worker(String name, int age)
    {
        super(name, age);//调用父类的构造函数
    }
}

函数覆盖(Override)

子类的实例化过程

注意构造函数不会继承更不会覆盖(首先函数名都不一样...
子类的实例化过程:子类中所有的构造函数默认都会访问父类中空参数的构造函数(因为每一个构造函数的第一行都有一条默认的语句super();之所以会有这样一条默认语句,是因为子类继承了父类,获取到了父类中的内容(属性),所以在使用父类内容之前,要先明确父类是如何对这些数据初始化的)。

public class Father 
{
    public Father()
    {
        System.out.println("Father run");
    }
}
class Son extends Father
{
    public Son()
    {
        //super(); //默认会调用父类的空参构造函数
        System.out.println("Son run");
    }
}

当父类中没有空参数的构造函数时,子类的构造函数必须通过this或者super语句指定要访问的构造函数。
注意:super语句必须要定义在子类构造函数的第一行,因为父类的初始化动作要先完成。
如果子类构造函数中使用this调用了本类构造函数,那么就没有super了,因为super和this都只能定义在第一行。但是可以保证的是,子类中肯定会有其他的构造函数访问父类的构造函数,即子类构造函数中至少有一个访问父类构造函数。

public class Father 
{
    public Father(int num)
    {
        System.out.println("Father run");
    }
}
class Son extends Father
{
    public Son()
    {
        this(4);
    }

    public Son(int num)
    {
        super(num);
        System.out.println("Son run");
    }

}

注意,类默认的空参构造函数的修饰符与类保持一致。

class Demo
{
    /*
    Demo()
    {
        super(); //父类为Object
        return;
    }
    */
}

Example1:子类的实例化过程

public class Father {
    Father()
    {
        show();
    }
    public  void show()
    {
        System.out.println("father show");
    }
}
class Son extends Father
{
    int num = 8;
    Son()
    {
        super();
        //分水岭
        System.out.println("Son run..." + num);
    }

    public void show()
    {
        System.out.println("Son show..." + num);
    }
}
class Demo
{
    public static void main(String[] args)
    {
        Son s = new Son();
        s.show();
    }
}

输出结果为:

Son show...0
Son run...8
Son show...8

内存图解:

默认初始化,成员变量num=0;显示初始化,成员变量num=8。
默认初始化后,执行super(),经过父类构造函数初始化,再进行自己的显示初始化,最后是自己的构造函数初始化。

注意,若子类当中有构造代码块,则其初始化的过程为:

一个对象的实例化过程:

Person p = new Person();
1、JVM读取指定路径下的Person.class文件,如果该类有直接父类,先加载Person的父类,再加载Person进内存;
2、在堆内存中开辟空间,分配地址;
3、在堆的对象空间中,对对象中的属性进行默认初始化;
4、调用对应的构造函数进行初始化;
5、在构造函数中,先调用父类的构造函数进行初始化;
6、父类初始化完毕后,再对子类的属性进行显示初始化;
7、构造代码块初始化;
8、进行子类构造函数的特定初始化;
9、初始化完毕后,将地址值赋值给引用变量(不一定是在栈中,如下)。

class Demo
{
    Person p = new Person(); //在堆中
}

final关键字

Example1

class Person
{
    final int x;
}
class FinalDemo
{
    public static void main(String[] args){
        new Person();
    }
}

编译错误:可能尚未初始化变量x(指的显示初始化)。
注意:变量若是有final修饰符,则一般都为静态:static final int x = 7,每个对象的值都一样。

上一篇 下一篇

猜你喜欢

热点阅读