继承 ---java

目录

一.为什么需要继承

二.继承的概念

三. 继承的语法

四.父类中的成员访问

4.1子类中访问父类的成员变量

4.2子类中访问父类的成员方法

五. super关键字

1. super.成员变量 ---访问父类中的成员变量

2.super.成员方法 ---访问父类中的成员方法

六. 子类构造方法

super.() ---访问父类构造方法 

七.  super和this

八. 继承关系上的执行顺序

九. protected 关键字

十. 继承方式

十一. final 关键字  

十二. 继承与组合


一.为什么需要继承

定义一只狗:

// Dog.java
public class Dog {
        string name ;
        int age ;
        float weight ;
        public void eat (){
                System . out . println ( name + " 正在吃饭 " );
        }
        public void sleep (){
                System . out . println ( name + " 正在睡觉 " );
        }
        void Bark (){
                System . out . println ( name + " 汪汪汪 ~~~" );
        }
}
定义一只猫:
// Cat.Java
public class Cat {
        string name ;
        int age ;
        float weight ;
        public void eat (){
                System . out . println ( name + " 正在吃饭 " );
        }
        public void sleep () {
                System . out . println ( name + " 正在睡觉 " );
        }
        void mew (){
                System . out . println ( name + " 喵喵喵 ~~~" );
        }
}

通过观察上述代码会发现,猫和狗的类中存在大量重复.那能否将这些共性抽取呢?面向对象思想中提出了继承的概念,专门用来进行共性抽取,实现代码复用 

二.继承的概念

继承 (inheritance) 机制 :是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加新功能 ,这样产生新的类,称 派生类 。继承呈现了面向对象程序设计的层次结构, 体现了由简单到复杂的认知过程。继承主要解决的问题是:共性的抽取,实现代码复用
上述图示中, Dog Cat 都继承了 Animal 类,其中: Animal 类称为父类 / 基类或超类 Dog Cat 可以称为 Animal 子类/ 派生类,继承之后,子类可以复用父类中成员,子类在实现时只需关心自己新增加的成员即可。

三. 继承的语法

Java 中如果要表示类之间的继承关系,需要借助 extends 关键字,具体如下:
修饰符 class 子类 extends 父类 {
// ...
}

上述猫狗用继承思想设计:

// Animal.java
public class Animal {
        String name ;
        int age ;
        public void eat (){
                System . out . println ( name + " 正在吃饭 " );
        }
        public void sleep (){
                System . out . println ( name + " 正在睡觉 " );
        }
}
// Dog.java
public class Dog extends Animal {
        void bark (){
                System . out . println ( name + " 汪汪汪 ~~~" );
        }
}
// Cat.Java
public class Cat extends Animal {
        void mew (){
                System . out . println ( name + " 喵喵喵 ~~~" );
        }
}
注意:
1. 子类会将父类中的成员变量或者成员方法继承到子类中了,子类就不需要再定义父类中的成员变量或者成员方法了. 
2. 子类继承父类之后,必须要新添加自己特有的成员,体现出与基类的不同,否则就没有必要继承了

四.父类中的成员访问

4.1子类中访问父类的成员变量

1. 子类和父类不存在同名成员变量---直接用即可
public class Base {
        int a ;
        int b ;
}
public class Derived extends Base {
        int c ;
        public void method (){
                a = 10 ; // 访问从父类中继承下来的 a
                b = 20 ; // 访问从父类中继承下来的 b
                c = 30 ; // 访问子类自己的 c
        }
}
2. 子类和父类成员变量同名
public class Base {
        int a = 10 ;
        int b = 20 ;
}
/
public class Derived extends Base {
        int a = 1 ; // 与父类中成员 a 同名,且类型相同
        int c =2;
        public void method (){
                   System . out . println(a);
                   System . out . println(b);
                   System . out . println(c);
        }
}
//输出结果:
1     ---输出的是子类当中a的值
20
2
得出结论:
在子类方法中 或者 通过子类对象访问成员时
1. 如果访问的成员变量子类中有,优先访问自己的成员变量。
2. 如果访问的成员变量子类中无,则访问父类继承下来的,如果父类也没有定义,则编译报错。
3. 如果访问的成员变量与父类中成员变量同名,则优先访问自己的。
成员变量访问遵循就近原则,自己有优先自己的,如果没有则向父类中找

4.2子类中访问父类的成员方法

1. 成员方法名字不同
public class Base {
        public void methodA (){
                System . out . println ( "Base 中的 methodA()" );
        }
}
public class Derived extends Base {
        public void methodB (){
                System . out . println ( "Derived 中的 methodB() 方法 " );
        }
        public void methodC (){
                methodB (); // 访问子类自己的 methodB()
                methodA (); // 访问父类继承的 methodA()
        }
}

2. 成员方法名字相同 

public class Base {
        public void methodA (){
                System . out . println ( "Base 中的 methodA()" );
        }
        public void methodB (){
                System . out . println ( "Base 中的 methodB()" );
        }
}
public class Derived extends Base {
        public void methodA ( int a ) {
                System . out . println ( "Derived 中的 method(int) 方法 " );
        }
        public void methodB (){
                System . out . println ( "Derived 中的 methodB() 方法 " );
        }
        public void methodC (){
                methodA (); // 没有传参,访问父类中的 methodA()
                methodA ( 20 ); // 传递 int 参数,访问子类中的 methodA(int)
                methodB (); // 直接访问,则永远访问到的都是子类中的 methodB(), 父类的无法访问
        }
}
//输出结果:
Base 中的 methodA()
Derived 中的 method(int) 方法
Derived 中的 methodB() 方法
得出结论:
1. 通过子类对象访问父类与子类中不同名方法时,优先在子类中找,找到则访问,否则在父类中找,找到则访问,否则编译报错。
2. 通过派生类对象访问父类与子类同名方法时,如果父类和子类同名方法的参数列表不同 ( 重载 ) ,根据调用方法适传递的参数选择合适的方法访问,如果没有则报错;
问题:如果子类中存在与父类中相同的成员变量和成员方法时,那如何在子类中访问父类相同名称的成员呢?

五. super关键字

该关键字主要作用:在子类方法中访问父 类的成员

1. super.成员变量 ---访问父类中的成员变量

上述4.1.2代码,想要访问父类的a,只需要在a前+super.
public class Base {
        int a = 10 ;
        int b = 20 ;
}
/
public class Derived extends Base {
        int a = 1 ; // 与父类中成员 a 同名,且类型相同
        int c =2;
        public void method (){
                   System . out . println( super. a);
                   System . out . println(b);
                   System . out . println(c);
        }
}
//输出结果:
10     ---输出的是父类当中a的值
20
2

2.super.成员方法 ---访问父类中的成员方法

上述4.2.2代码,若想访问父类中的methodB(),只需在前面+super.

public class Base {
        public void methodA (){
                System . out . println ( "Base 中的 methodA()" );
        }
        public void methodB (){
                System . out . println ( "Base 中的 methodB()" );
        }
}
public class Derived extends Base {
        public void methodA ( int a ) {
                System . out . println ( "Derived 中的 method(int) 方法 " );
        }
        public void methodB (){
                System . out . println ( "Derived 中的 methodB() 方法 " );
        }
        public void methodC (){
                methodA (); // 没有传参,访问父类中的 methodA()
                methodA ( 20 ); // 传递 int 参数,访问子类中的 methodA(int)
                super. methodB (); 
        }
}
//输出结果:
Base中的methodA()
Derived中的method(int)方法
Base中的methodB()方法

总结:在子类方法中,如果想要明确访问父类中成员时,借助super关键字即可。 

其实,super的出现,是为了增强代码的可读性,让读者知道哪些是从父类继承过来的. 

注:super关键字不能出现在static方法中.(static方法不依赖对象)

六. 子类构造方法

父子父子,先有父再有子,即: 子类对象构造时,需要先调用基类构造方法,然后执行子类的构造方法。
public class Base {
        public Base (){
                System . out . println ( "Base()" );
        }
}
public class Derived extends Base {
        public Derived (){
                        //super(); (编译器隐含的)
                System . out . println ( "Derived()" );
        }
}
public class Test {
        public static void main ( String [] args ) {
                Derived d = new Derived ();
        }
}
//结果打印:
Base ()
Derived ()

结果可见:

在子类构造方法中,并没有写任何关于基类构造的代码,但是在构造子类对象时,先执行基类的构造方法,然后执行子类的构造方法
因为:
子类对象中成员是有两部分组成的,基类继承下来的以及子类新增加的部分 。父子父子 肯定是先有父再有子,所以在构造子类对象时候 ,先要调用基类的构造方法,将从基类继承下来的成员构造完整 ,然后再调用子类自己的构造方法,将子类自己新增加的成员初始化完整
在子类构造方法创建时 , 默认会调用基类的无参构造方法:super(),
用户没有写时,编译器会自动添加,而且super()必须是子类构造方法中第一条语句,
并且只能出现一次.
上述代码:
class Animal {
        String name ;
        int age ;
}
class Dog extends Animal {
        void bark (){
                System . out . println ( name + " 汪汪汪 ~~~" );
        }
}
若在父类Animal中添加一个带有两个参数的构造方法,即:
发现子类会报错
原因:在子类的默认构造方法中, 默认构造super(), 无参数, 但是如果在父类中构造了两个参数的构造方法, 在父类中将不再默认构造不带参数的构造方法, 则super()无效了. 此时如果想访问父类, 需要传带参数的super, 去访问父类中的构造方法.
改正:构造子类的构造方法,在方法中添加 super(name,age),对父类成员进行初始化

此时super出现了第三个使用方法:

super.() ---访问父类构造方法 

注意:
1. 若父类显式定义无参或者默认的构造方法,在子类构造方法第一行默认有隐含的 super() 调用,即调用基类构造方法
2. 如果父类构造方法是带有参数的,此时需要用户为子类显式定义构造方法,并在子类构造方法中选择合适的父类构造方法调用,否则编译失败。
3. 在子类构造方法中, super(...) 调用父类构造时,必须是子类构造函数中第一条语句。
4. super(...) 只能在子类构造方法中出现一次,并且不能和 this 同时出现

七.  superthis

相同点
1. 都是 Java 中的关键字
2. 只能在类的非静态方法中使用,用来访问非静态成员方法和字段
3. 在构造方法中调用时,必须是构造方法中的第一条语句,并且不能同时存在
不同点
1. this 是当前对象的引用,当前对象即调用实例方法的对象, super 相当于是子类对象中从父类继承下来部分成员的引用
2. 在非静态成员方法中, this 用来访问本类的方法和属性, super 用来访问父类继承下来的方法和属性
3. 在构造方法中: this(...) 用于调用本类构造方法, super(...) 用于调用父类构造方法,两种调用不能同时在构造方法中出现
4. 构造方法中一定会存在 super(...) 的调用,用户没有写编译器也会增加,但是 this(...) 用户不写则没有

八. 继承关系上的执行顺序

在之前没讲继承关系时,我们知道静态代码块,实例代码块,构造方法同时存在时,执行顺序为:

静态代码块->实例代码块->构造方法

那么,当引入继承关系时,执行顺序又会变成怎样呢?

class Animal{
    String name;
    int age;
 static{
     System.out.println("父类静态代码块");
 }
    {
        System.out.println("父类实例代码块");
    }
    public Animal(String name, int age) {
        this.name = name;
        this.age = age;
        System.out.println("父类构造方法");
    }
}
// Dog.java
class Dog extends Animal{
    static{
        System.out.println("子类静态代码块");
    }
    {
        System.out.println("子类实例代码块");
    }
    public Dog(String name, int age) {
        super(name, age);
        System.out.println("子类构造方法");
    }

    void bark(){
        System.out.println(name + "汪汪汪~~~");
    }
}
public class test {
    public static void main(String[] args) {
        Dog dog=new Dog("旺财",10);
    }
}

当父类和子类中都有静态代码块,实例代码块,构造方法时,执行顺序为:

父类静态代码块->子类静态代码块->父类实例代码块->父类构造方法->子类实例代码块->子类构造方法

当第二次实例化子类对象时,父类和子类的静态代码块都将不会再执行.

九. protected 关键字

// extend01 包中
public class B {
        private int a ;
        protected int b ;
        public int c ;
        int d ;
}
// extend01 包中
// 同一个包中的子类
public class D extends B {
        public void method (){
                // super.a = 10; // 编译报错,父类private 成员在相同包子类中不可见
                super . b = 20 ; // 父类中 protected 成员在相同包子类中可以直接访问
                super . c = 30 ; // 父类中 public 成员在相同包子类中可以直接访问
                super . d = 40 ; // 父类中默认访问权限修饰的成员在相同包子类中可以直接访问
        }
}
// extend02 包中
// 不同包中的子类
public class C extends B {
        public void method (){
                // super.a = 10; // 编译报错,父类中private 成员在不同包子类中不可见
                super . b = 20 ; // 父类中 protected 修饰的成员在不同包子类中可以直接访问
                super . c = 30 ; // 父类中 public 修饰的成员在不同包子类中可以直接访问
                //super.d = 40; // 父类中默认访问权限修饰的成员在不同包子类中不能直接访问
        }
}
// extend02 包中
// 不同包中的类
public class TestC {
        public static void main ( String [] args ) {
                C c = new C ();
                c . method ();
                // System.out.println(c.a); // 编译报错, 父类中private 成员在不同包其他类中不可见
                // System.out.println(c.b); // 父类中protected 成员在不同包其他类中不能直接访问
                System . out . println ( c . c ); // 父类中 public 成员在不同包其他类中可以直接访问
                // System.out.println(c.d); // 父类中默认访问权限修饰的成员在不同包其他类中不能直接访问
        }
}

什么时候下用哪一种呢 ?
我们希望类要尽量做到 " 封装 ", 即隐藏内部实现细节 , 只暴露出 必要 的信息给类的调用者 .
因此我们在使用的时候应该尽可能的使用 比较严格 的访问权限 . 例如如果一个方法能用 private, 就尽量不要用 public.
另外 , 还有一种 简单粗暴 的做法 : 将所有的字段设为 private, 将所有的方法设为 public. 不过这种方式属于是对访问权限的滥用, 还是更希望同学们能写代码的时候认真思考 , 该类提供的字段方法到底给 " " 使用 ( 是类内部自己用, 还是类的调用者使用 , 还是子类使用 ).

十. 继承方式

注意: Java 中不支持多继承 。------>引入了接口
时刻牢记 , 我们写的类是现实事物的抽象 . 而我们真正在公司中所遇到的项目往往业务比较复杂 , 可能会涉及到一系列复杂的概念, 都需要我们使用代码来表示 , 所以我们真实项目中所写的类也会有很多 . 类之间的关系也会更加复杂.
但是即使如此 , 我们并不希望类之间的继承层次太复杂 . 一般我们不希望出现超过三层的继承关系 . 如果继承层次太多, 就需要考虑对代码进行重构了 .
如果想从语法上进行限制继承 , 就可以使用 final 关键字

十一. final 关键字  

final 关键可以用来修饰变量、成员方法以及类。
1. 修饰变量或字段,表示常量 ( 即不能修改 )
final int a = 10 ;
a = 20 ; // 编译出错

2. 修饰类:表示此类不能被继承

final public class Animal {
...
}
public class Bird extends Animal {
...
}
// 编译出错
Error :( 3 , 27 ) java : 无法从最终 com . bit . Animal 进行继承

3. 修饰方法:表示该方法不能被重写(后序介绍)

十二. 继承与组合

和继承类似 , 组合也是一种表达类之间关系的方式 , 也是能够达到代码重用的效果。组合并没有涉及到特殊的语法(诸如 extends 这样的关键字 ), 仅仅是 将一个类的实例作为另外一个类的字段
继承表示对象之间是 is-a 的关系 ,比如:狗是动物,猫是动物
组合表示对象之间是 has-a 的关系,比如:汽车和其轮胎、发动机、方向盘、车载系统等的关系就应该是组合,因为汽车是有这些部件组成的。
// 轮胎类
class Tire {
// ...
}
// 发动机类
class Engine {
// ...
}
// 车载系统类
class VehicleSystem {
// ...
}
class Car {
        private Tire tire ; // 可以复用轮胎中的属性和方法
        private Engine engine ; // 可以复用发动机中的属性和方法
        private VehicleSystem vs ; // 可以复用车载系统中的属性和方法
        // ...
}
// 奔驰是汽车
class Benz extend Car {
// 将汽车中包含的:轮胎、发送机、车载系统全部继承下来
}
组合和继承都可以实现代码复用,应该使用继承还是组合,需要根据应用场景来选择,一般建议:能用组合尽量用组合。
  • 15
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 14
    评论
在Java中,类与继承-抽象类有以下几个关键点。首先,继承是指一个类可以继承自另一个类。被继承的类被称为父类/超类/基类,而继承其他类的类被称为子类。继承使得子类可以拥有父类中所有的成员(成员变量和成员方法),从而提高了代码的复用性。 其次,当子类继承自一个抽象类时,子类必须重写父类所有的抽象方法,否则该子类也必须声明为抽象类。最终,必须有子类实现该父类的抽象方法,否则从最初的父类到最终的子类都不能创建对象,失去了继承的意义。 一个示例代码如下: ``` public abstract class Animal { public abstract void run(); } public class Cat extends Animal { public void run() { System.out.println("小猫在墙头走~~~"); } } public class CatTest { public static void main(String[] args) { Cat c = new Cat(); c.run(); } } ``` 在上述代码中,Animal是一个抽象类,其中定义了一个抽象方法run。Cat类继承自Animal类,并且重写了run方法。在CatTest类的main方法中,创建了一个Cat对象并调用了run方法。 更多关于Java类与继承-抽象类的知识,可以参考Java基础教程之接口的继承与抽象类。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [java面向对象基础——继承、抽象类](https://blog.csdn.net/qq_42538127/article/details/115426682)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [【JavaSe】面向对象篇(六) 抽象类](https://blog.csdn.net/qq_41744145/article/details/100023046)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [Java基础教程之接口的继承与抽象类](https://download.csdn.net/download/weixin_38516270/12808613)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值