Java基础知识总结之类的继承

和C++不同,Java仅支持单继承。在单继承中,每个类最多只有一个父类。在Java中,要实现多继承需要使用“接口”机制。
继承的三种实现方式:
1.实现继承是指使用基类的属性和方法,而无需额外编码的能力
2.接口继承是指仅使用属性和方法的名称,但是子类必须要 提供实现的能力
3.可视继承指子窗体使用基窗体类的外观和实现代码的能力。

Java继承的内存形态
对于父类中的成员,当它被子类继承后,并非将其复制一份放到子类的空间中,它仍然只在父类空间中存在一份。
如果程序中通过“子类对象名.成员名”的方式使用成员,编译器会首先到子类中查找是否存在此成员,如果没有,则在其父类空间中查找,依次往上推。如果知道Object类(该类为所有类的公共祖先)还没发现此成员,则编译器报错。
由于父类的成员方法没有被复制到子类的空间中,所以子类对象在运行时,必须保证父类的class文件可以访问到。

继承中的隐藏和覆盖
隐藏和覆盖:当子类的成员变量和父类的成员变量同名时,称为父类的成员变量(属性)被隐藏。如果是成员方法同名,称为父类的成员方法覆盖。

属性的隐藏
只要子类中的成员变量与父类的同名,就可以将父类的变量隐藏起来。

1.修饰符完全相同的情况
直接完全覆盖

2.访问权限不相同的情况
子类用于隐藏的变量可以和父类的访问权限不同,如果访问权限被改变,则以子类的权限为准。
可以将父类的变量在子类中修改为public , protected, private.

3.数据类型不同的情况
Java允许子类和父类变量的类型完全不同,以修改后的数据类型为准。

4.常量修饰符不同的情况
Java允许父类的变量被子类的常量隐藏,也允许父类的常量被子类的变量隐藏。
public class ancestor{
    int x = 1;
    final int y = 2;
    static int z =3; 
}

public class hideMember_1 extends ancestor{
    final int x = 100;
    int y = 200 ;
    pblic static void main(String args[]){
        hideMember_1 oa = new hideMember_1();
        oa.x = 300;    //错误,变量x已经被常量的x隐藏了,x现在为常量
        oa.y = 400;    //正确,父类中的常量y已经被隐藏了,y现在为变量。
    }
}




5.静态修饰符不同的情况
Java允许用实例成员来隐藏静态成员变量,也允许使用静态成员变量来隐藏实例成员变量。
public class hideMember_2 extends ancestor{
    static int x = 200 ;
    int z = 300;
    
    public static void main(String args[]){
        hideMember_2 oa = new hideMember_2();
        x = 300;    //正确,x在子类中为静态变量
        z = 400;    //错误,z已经为实例成员了,不允许在静态方法张访问。
    }

}



方法的覆盖(Override)
子类中的方法可以覆盖父类中的方法。在子类中,如果觉得继承下来的方法不能满足自己的需要,可以对继承的原方法进行重写,这被称为覆盖。覆盖需要满足以下两个条件:
1.方法名称必须相同
2.方法的参数必须相同,包括了参数的个数,参数类型,和顺序

如果值满足第一条,不满足第二条,那就不是方法的覆盖,而是方法的重载(Overload)。
方法有权限修饰符,还有返回类型修饰符,关于覆盖,其规则要复杂。
如果覆盖成功,那么使用子类对象时,方法的所有属性都以覆盖后的为准。
1.修饰符完全相同的覆盖
完全覆盖,子类和父类的访问权限相同。
//common.java
public class Common{
    //定义一个保护变量
    protected int x = 200;
    //定义一个带有返回值的普通方法
    public int getX(){
        return x;
    }
    //定义一个最终方法
    public final void setX(int ix){
        x = ix;
    }
    //定义一个具有保护访问权限的方法
    protected void proShowMsg(){
        System.out.println("This is protected proShowMsg() in Common class.")
    }
    
    public void pubShowMsg(){
        System.out.println("This is public ShowMsg() in Common class.");
    }

    static public void stShowMsg(){
        System.out.println("This is static ShowMsg() in Common class.");
    }

}



2.访问权限不同的情况
子类方法的访问权限可以与父类的不相同,但是只允许权限更宽松,而不允许更严格。它遵循了“公开的不再是秘密”这一原则。没有任何办法能改变这一原则。
public class OverrideMember2 extends Common{
//将方法的权限设置为公共的,从父类中的保护方法---->到子类中的公共方法。权限更加宽松了。
    public void proShowMsg(){
        System.out.println("This is public ShowMsg() in derive class.");
    }
//在子类中定义一个保护方法试图覆盖父类中的公共方法,错误。
//Cannot reduce the visibility of the inherited method from Common
    protected void pubShowMsg(){
        System.out.println("This is protected ShowMsg() in derive class.");
    }
}




3.返回值数据类型不相同的情况
当覆盖时,不允许出现返回值数据类型不同的情况。也就是说,覆盖与被覆盖的方法的返回值数据类型必须完全相同。
public class OverrideMember3 extends Common{
//父类中的返回值为int类型,子类中的返回值为double类型。不允许覆盖。
//The return type is incompatible with Common.getX()
    public double getX(){
        return x ;
    }
}



4.final修饰符不同的情况
若方法有final修饰,则表示此方法是一个最终方法,它的子类不能覆盖该方法。但是子类可以用final方法来覆盖父类中的方法。
public class OverrideMember4 extends Common{
    public final int getX(){
        return x ;
    }
//试图覆盖一个最终方法,错误。
//Cannot override the final method from Common
    public final void setX(int ix){
        x = ix*2;
    }
}


5.静态修饰符不同的情况
Java规定,静态方法不允许被实例方法覆盖。同样,实例方法也不允许被静态方法覆盖。就是说,不允许出现父类方法和子类方法覆盖时的static修饰符发生变化。
public class OverrideMember5 extends Common{
     //试图用静态方法来覆盖非静态方法。 错误
//This static method cannot hide the instance method from Common
    public static void pubShowMsg(){
        System.out.println("This is public ShowMsg() in OverrideMember5, static method");
    }
//试图用一个实例方法类覆盖一个静态方法。错误
//This instance method cannot override the static method from Common
    public void stShowMsg(){
        System.out.println("This is static ShowMsg() in OverrideMember5, instance method");   
    }
}

构造方法的继承和调用
构造方法是对类进行初始化的,初始化其中的成员变量的值。构造方法是一类特殊的方法:
1.名字必须与类名完全一致,没有返回值,也不能带void修饰。
2.构造方法的调用时在创建一个对象时,使用new操作符进行的。
3.每个类有0个或者是多个构造方法,如果自己不定义,系统自动提供一个。
4.不能被static, final, synchronized, abstract, native 和private修饰。
构造方法在初始化时,自动执行,一般不能显式的调用。当有多个构造函数形成重载时,调用构造函数按照函数重载的规则进行调用。Java编译系统会自动按照初始化时最后括号的参数个数以及参数类型来自动一一对应。

1.无参数构造方法的继承
无参数的构造方法与普通的继承有本质上的不同,它其实是一种自动调用。
示例:
定义一个父类:FatherClass.java
package com.leo.constructor;

public class FatherClass {
    protected  int x = 100 ;
    public void showMsg(){
        System.out.println("This is a method in ancestor.");
    }

    //定义一个不带参数的构造函数
    FatherClass(){
        System.out.println("This is a Constructor in ancestor without para.");
    }


    //定义一个带参数的构造函数
    FatherClass(int ix){
        x = ix ;
        System.out.println("This is a constructor in ancestor with para ix="+ix);
    }

}


定义一个子类:InheritClass.java
package com.leo.constructor;

public class InheritConstruct1 extends FatherClass{
    public static void main(String[] args) {
        InheritConstruct1 oa = new InheritConstruct1();

    }

}


运行子类后,结果如下图。在创建子类对象时,调用了子类的不带参数的构造方法。而子类这个构造方法又会自动调用父类的无参构造方法。这种调用的实质是通过super关键字来调用的。

子类在执行构造方法时,会先调用父类中的无参构造函数。因此程序员在设计一个类时,最好为父类提供一个无参数的构造函数。

2.带参数的构造方法的继承
Java规定, 带参数的构造方法,不会有子类继承,也不会自动调用。可以通过一定的方法调用。
package com.leo.constructor;
public class InheritConstruct2 extends FatherClass {

    public static void main(String[] args) {
        InheritConstruct1 oa = new InheritConstruct1();
        //The constructor InheritConstruct1(int) is undefined
        InheritConstruct1 ob = new InheritConstruct1(100);
    }
}


super关键字
当子类的变量和父类的变量同名时,父类中的同名变量被屏蔽起来了。但是父类中的变量依然还是存在的。如果想在子类中访问父类中的同名变量,需要使用到关键字super。
用法:super.变量名   或    super.方法名([])

super只能在子类中使用,用来调用父类的成员或构造方法。
1.使用super引用父类的成员
父类:FatherClass.java
package com.leo.constructor;

public class FatherClass {
    protected  int x = 100 ;
    public void showMsg(){
        System.out.println("This is a method in ancestor.");
    }

    //定义一个不带参数的构造函数
    FatherClass(){
        System.out.println("This is a Constructor in ancestor without para.");
    }


    //定义一个带参数的构造函数
    FatherClass(int ix){
        x = ix ;
        System.out.println("This is a constructor in ancestor with para ix="+ix);
    }

}


子类:InheritClass1.java
public class InheritClass1 extends FatherClass{
    protected int x = 200 ;  //隐藏了父类同名成员
    public int getSuperX(){   //获得父类中的成员
        return super.x ;
    }
    public int getX(){
        return x ;
    }
    public void showMsg(){
         System.out.println("This is a method in derive.");
    }
    public void call(){
        showMsg();
        super.showMsg();
    }


    public static void main(String args[]){
        InheritClass1 oa = new InheritClass1();
        System.out.println("oa.getSuperX()"+oa.getSuperX());
        System.out.println("oa.getX()"+oa.getX();
        oa.call();
}


从运行结果表明,通过super关键字的确调用了父类中的成员变量和方法。


2.使用super调用父类的构造方法
在继承关系下,子类的创建对象的时候,会自动的调用父类中的不带参数的构造方法,但是不会自动调用带参数的构造方法。如果子类一定要调用父类中的带参数的构造方法,就必须使用super来是实现。
使用方法:super([参数列表]);
使用super时必须要遵循以下规则:
(1).只能用在子类的构造方法中
(2).只能是第一条语句
(3).一个构造方法中只能有一条super语句。
子类的调用父类带参数的构造函数的代码 ,SuperConstruct.java
package com.leo.constructor;
/**
 * 使用super关键字来调用父类中的带参数的构造方法。
 * 使用this关键字来调用本类中的其他构造方法。
 */
public class SuperConstruct extends FatherClass {
    public SuperConstruct(int ix){
        super(ix);           //调用父类中的带参数的构造方法
        System.out.println(x);   //打印x
        System.out.println("This is a constructor in derive with para");
    }

    public SuperConstruct(){
        super();     //默认调用无参数的构造函数
        System.out.println(x);   //打印x
        System.out.println("This is a constructor in derive without para");
    }

    public SuperConstruct(int ix, int iy){
        this(ix);  //通过this来调用类中的其他构造方法。
    }

    public static void main(String[] args) {
        SuperConstruct oa = new SuperConstruct();
        System.out.println();
        SuperConstruct ob = new SuperConstruct(900);
        System.out.println();
        SuperConstruct oc = new SuperConstruct(1000,0);
    }
}


运行结果图:

从结果上看,通过super关键字实现了对父类带参数的构造函数的调用。一旦显式的调用了父类中的带参数的构造函数,系统就不再调用父类中的无参数的构造方法。
子类构造方法对无父类的 无参数构造方法的自动调用,程序员不用显式的使用super()或者this(),系统会自动在第一行添加一条super()语句,实现对父类无参构造函数的调用。

子类调用父类构造方法的总结:
(1).如果子类没有定义构造方法,在创建子类时,自动调用子类的无参构造方法,同时也会自动调用父类中的无参构造方法。父类的构造方法一定是先于子类的!!!
(2).如果子类定义了构造方法,不论是无参还是带有参数,创建子类时,首先要执行父类的无参构造方法,然后执行自己的构造方法。
(3).如果子类调用父类的构造方法,可以通过super([参数列表])调用所需要的父类的构造方法,该语句 必须是子类构造方法中的第一句,且只能出现在子类的构造方法中。
(4).如果某个构造方法调用类中的其他构造方法,可以使用this([参数列表]),其该语句放在构造方法的第一条语句的位置。





  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值