一、创建继承关系的类的内存原理

        假如有一个父类以及一个继承了这个类的子类为例

原理:

① 首先会加载类的信息,因为父类向上继承了Object类,所以现在方法区中加载Object类的信息,然后加载父类的信息,最后加载子类的信息,它们之间会存在关联关系;

② 在堆空间中开辟一块空间并分配一个地址,首先调用父类的构造器对父类中相应的字段进行初始化并存储,之后是子类进行;

③ 对象实例创建完成后会把地址信息返回给栈中的相应的对象引用。

如以下代码为例:

public class Test {
    public static void main(String[] args) {
        Son son = new Son(); //创建子类对象
    }
}

class Father {  //父类
    String name = "father";
    int age = 40;
}

class Son extends Father {  //子类
    String name = "son";
    String hobby = "sleep";
}

内存原理:

① 方法区中依次加载Object类,Father类,Son类的信息;

② 在堆中开辟一块空间先存储Fahter类中的name的地址和age属性,name存储在方法区内的常量池中,然后存储子类中的属性;

③ 将对象的地址返回给栈中的son这个引用变量。

内存原理图如下:

 

        调用值的时候,先在子类中找,子类中如果有属性,并且可以访问,那么就返回信息;如果子类中没有信息,那么就向上在父类中找,直到找到为止。但是如果需要调用的值被私有修饰符修饰了,那么就会直接编译错误,不会再向上找了。

二、super关键字

        super代表对父类的引用,包括父类的属性,方法和构造器。

如以下例子:

public class Test {
    public static void main(String[] args) {
        System.out.println("=============构造器============");
        Son son = new Son();  //创建子类对象
        System.out.println("=============同名属性============");
        son.showName();
        System.out.println("=============同名方法============");
        son.show();
    }
}

class Father {
    String name = "father";

    Father() {
        System.out.println("父类的空参构造");
    }

    public void show() {
        System.out.println("父类的方法");
    }
}

class Son extends Father {
    String name = "son";

    Son() {
        super();  //调用父类的空参构造
        System.out.println("子类的空参构造");
    }

    public void show() {
        super.show();  //调用父类的方法
        System.out.println("子类的方法");
    }

    public void showName() {
        //使用super关键字调用父类中的同名参数变量
        System.out.println("父类名字:" + super.name + "\n" +
                "子类的名字:" + name);
    }
}

运行结果如下:

 

注意的地方:

  1. super.属性名可以访问父类的属性,但是不能访问父类的私有属性;
  2. super.方法名(参数列表)可以访问父类的方法,但是不能访问父类的私有方法;
  3. 访问父类的构造器,只能在第一句,而且只能出现一次。

        以上访问私有属性需要通过get和set方法来实现,因为private的访问权限只能在同一个类中。如下:

public class Test {
    public static void main(String[] args) {
        Son son = new Son();  //创建子类对象
        son.showName();
    }
}

class Father {
    private String name = "father";

    public String getName() {
        return name;
    }

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

class Son extends Father {
    String name = "son";

    public void showName() {
        System.out.println("父类名字:" + getName() + "\n" +
                "子类名字:" + name);
    }
}

运行结果如下:

this或直接方法名会在本类中找,有就会执行,如果没有就会去父类中找,直到找到为止;

使用super时会直接从父类开始找,如果上级类有重名,符合就近原则。但是如果遇到被private修饰的就会直接报错。

 

三、方法重写

        方法重写:当子类中的方法和父类中的方法参数一致,并且名字也相同的时候,就会触发方法覆盖,当调用方法的时候使用的就是子类中重写的方法,其中子类中重写的方法可以使用@Override注解来修饰,如下:

public class Test {
    public static void main(String[] args) {
        Son son = new Son();  //创建子类对象
        son.show();
    }
}

class Father {
    public void show() {
        System.out.println("父类的方法");
    }
}

class Son extends Father {
    @Override  //也可以不写
    public void show() {
        System.out.println("子类重写的方法");
    }
}

运行结果如下:

 

 关于重写,要注意的是以下两点:

  1. 子类方法的返回类型要与父类返回类型一致或者是父类返回类型的子类;

  2. 子类不能缩小父类方法的访问权限,否则就会无法通过编译。

关于重载和重写的比较,如以下的表格所示:

范围 方法名 形参列表 返回类型 修饰符
重载 本类 相同 不同 无要求 无要求
重写 继承类 相同 相同 与父类相同或子类 子类不小于父类

ps.重写中返回类型为子类指的是发生重写时,子类的返回类型可以是该类的类型,如下:

class Father {
    public Father show() {
        return null;
    }
}

class Son extends Father {
    @Override
    public Son show() {
        return new Son();
    }
}

        在父类中show方法的返回值类型为Father类型,而在子类重写方法的返回类型可以是Father类型,也可以是子类的Son类型。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐