Java继承后访问成员的特点


继承后的特点——成员变量

对象访问成员变量时,会先在子类中查找有没有定义对应的变量,若子类中存在就会就近使用子类中的变量,若子类中没有定义就会沿着继承关系往上找有没有定义相应的变量,若父类中也没有则编译不通过。代码示例:

class Fu {
    // Fu类中的成员变量。
    int num = 5;
    int num2 = 7;
}
class Zi extends Fu {
    // Zi类中的成员变量
    int num = 6;
    public void show() {
        // 访问父类中的num2
        System.out.println("num2=" + num2);
        // 访问子类中的num
        System.out.println("num=" + num);
    }
}
public class ExtendsDemo0 {
    public static void main(String[] args) {
        // 创建子类对象
        Zi z = new Zi();
        // 调用子类中的show方法
        z.show();
    }
}

演示结果:
num2=7
num=6

子父类中出现了同名的成员变量时,在子类中访问父类中非私有成员变量,需要使用 super 关键字,用法类似于 this

使用格式:

super.父类成员变量名

代码如下:

class Zi extends Fu {
    // Zi类中的成员变量
    int num = 6;
    public void show() {
        //访问父类中的num
        System.out.println("Fu num2=" + num2);
        //访问父类中的num
        System.out.println("Fu num=" + super.num);
    }
}
演示结果:
Fu num = 7
Zi num = 5

Fu 类中的私有成员变量,子类是不能直接访问的。编码时,遵循封装的原则,使用private修饰成员变量,可以在父类中提供公共的getXxx方法和setXxx方法来访问。

super和this

父类空间优先于子类对象产生

在每次创建子类对象时,先初始化父类空间,再创建其子类对象本身。目的在于子类对象中包含了其对应的父类空间,便可以包含其父类的成员。代码体现在子类的构造方法调用时,一定先调用父类的构造方法。理解图解如下:

super和this的含义

  • super :代表父类的 存储空间标识 (可以理解为父类的引用)。

  • this :代表 当前对象的引用 (谁调用就代表谁)。

super和this的用法

this.成员变量 -- 本类的
super.成员变量 -- 父类的

this.成员方法名() -- 本类的
super.成员方法名() -- 父类的

代码示例:

class Animal {
    public void eat() {
        System.out.println("animal : eat");
    }
}

class Cat extends Animal {
    public void eat() {
        System.out.println("cat : eat");
    }
    public void eatTest() {
        this.eat();   // this  调用本类的方法
        super.eat();  // super 调用父类的方法
    }
}

public class ExtendsDemo08 {
    public static void main(String[] args) {
        Animal a = new Animal();
        a.eat();
        Cat c = new Cat();
        c.eatTest();
    }
}

输出结果为:
animal : eat
cat : eat
animal : eat

注意:

  1. 通过super引用属性、方法、构造器时,都要求该成员是可见的,即该成员的修饰符不能是private的,跨包的话还不能是缺省的。

  2. super的追溯不仅限于直接父类

  3. 如果某个属性或方法前面使用“this.”,那么先从本类中查找,如果未找到,会沿着继承关系往上找

如果某个属性或方法前面使用“super.”,那么先从直接父类中查找,如果未找到,会沿着继承关系往上找

如果某个属性或方法前面既没有“this.”,也没有“super.”,遵循就近原则,先从本类中查找,如果未找到,会沿着继承关系往上找

继承后的特点——成员方法

成员方法不重名时

如果子、父类中的方法不重名,这时的调用是没有影响的。对象调用方法时,会先在子类中查找有没有定义对应的方法,若子类中存在就执行子类中定义的方法,若子类中没有定义就会沿着继承关系往上找有没有定义相应的方法,若父类中也没有则编译不通过。代码示例:

class Fu{
    public void show(){
        System.out.println("Fu类中的show方法执行");
    }
}
class Zi extends Fu{
    public void show2(){
        System.out.println("Zi类中的show2方法执行");
    }
}
public  class ExtendsDemo04{
    public static void main(String[] args) {
        Zi z = new Zi();
        //子类中没有定义show方法,但是可以找从父类继承的show方法去执行
        z.show(); 
        z.show2();
    }
}

成员方法重名——重写(Override)

如果子、父类中出现 重名 的成员方法,这时的访问是一种特殊情况,叫做 方法重写 (Override)。

  • 方法重写 :子类继承了父类,可以获得父类的成员变量和成员方法;可是当父类的某个方法不适合于子类本身的特征时,在子类中可以根据需要对从父类中继承来的方法进行改造,也称为方法的重写、覆写。在程序执行时,子类的方法将覆盖父类的方法。

代码如下:

class Fu {
    public void show() {
        System.out.println("Fu show");
    }
}
class Zi extends Fu {
    //子类重写了父类的show方法
    public void show() {
        System.out.println("Zi show");
    }
}
public class ExtendsDemo05{
    public static void main(String[] args) {
        Zi z = new Zi();
        // 子类中有show方法,只执行重写后的show方法
        z.show();  // Zi show
    }
}

重写的应用

子类可以根据需要,定义特定于自己的行为。既沿袭了父类的功能名称,又根据子类的需要重新实现父类方法,从而进行扩展增强。比如新的手机增加来电显示头像的功能,代码如下:

class Phone {
    public void sendMessage(){
        System.out.println("发短信");
    }
    public void call(){
        System.out.println("打电话");
    }
    public void showNum(){
        System.out.println("来电显示号码");
    }
}

//智能手机类
class NewPhone extends Phone {

    //重写父类的来电显示号码功能,并增加自己的显示姓名和图片功能
    public void showNum(){
        //调用父类已经存在的功能使用super
        super.showNum();
        //增加自己特有显示姓名和图片功能
        System.out.println("显示来电姓名");
        System.out.println("显示头像");
    }
}

public class ExtendsDemo06 {
    public static void main(String[] args) {
        // 创建子类对象
        NewPhone np = new NewPhone();

        // 调用父类继承而来的方法
        np.call();

        // 调用子类重写的方法
        np.showNum();

    }
}

重写时,用到super.父类成员方法,表示调用父类的成员方法。

方法重写注意事项

  1. 方法名:必须完全一致

  2. 形参列表:必须完全一致

  3. 返回值类型:

如果是基本数据类型和void,必须一致

如果是引用数据类型,重写的方法的返回值类型 < = 被重写方法的返回值类型,Student<Person

  1. 修饰符:重写的方法的修饰符范围 > = 被重写方法的修饰符范围(public > protected > 缺省 > private)

  2. 被重写方法不能被 privatestaticfinal 修饰,如果跨包的话,修饰符缺省的也不能被重写,因为缺省的跨包不可见

继承后的特点——构造方法

首先我们要回忆两个事情,构造方法的定义格式和作用:

  1. 构造方法的名字是与类名一致的。所以子类是无法继承父类构造方法的。
  2. 构造方法的作用是初始化成员变量的。所以子类的初始化过程中,必须先执行父类的初始化动作。
    • 子类不会继承父类的构造器,但是一定会调用父类的构造器
    • 默认情况下,调用的是父类的无参构造,super()写或不写都会调用父类的无参构造,如果要写,必须在子类构造器首行
    • 如果父类没有无参构造,必须在子类的构造器首行使用super(实参列表)显式调用父类的有参构造,否则编译报错
    • super([形参列表]) 和 this([形参列表]) 都必须是在构造方法的第一行,所以不能同时出现。

代码示例:

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

class Son extends Father{
    private String str;
    private int num;

    public Son(){
        //隐含调用了super();  子类的构造器中一定会调用父类的构造器,默认调用父类的无参构造
        System.out.println("子类的无参构造");
    }

    public Son(String str){
        //隐含调用了super()
        this.str = str;
        System.out.println("子类的有参构造1");
    }

    public Son(String str,int num){
        //调用重载的构造器,隐含调用了super()
        this(str);
        this.num = num;
        System.out.println("子类的有参构造2");
    }
}

public class ConstructorTest {
    public static void main(String[] args) {
        Son s1 = new Son();
        /*
            父类的无参构造
            子类的无参构造
         */
        //Son s2 = new Son("java");
        /*
            父类的无参构造
            子类的有参构造1
         */

        Son s3 = new Son("java", 10);
        /*
            父类的无参构造
            子类的有参构造1
            子类的有参构造2
         */
    }
}


原文链接:https://www.cnblogs.com/sun10367/p/13514776.html