• JavaSE | 初始Java(十) | 继承和多态


    继承 (inheritance) 机制 :是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加新功能 ,这样产生新的类,称 派生类 。继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。继承主要解决的问题是:共性的抽取,实现代码复用
    Java 中如果要表示类之间的继承关系,需要借助 extends 关键字,具体如下:
    1. 修饰符 class 子类 extends 父类 {
    2. // ...
    3. }
    1. // Animal.java
    2. public class Animal{
    3. String name;
    4. int age;
    5. public void eat(){
    6. System.out.println(name + "正在吃饭");
    7. }
    8. public void sleep(){
    9. System.out.println(name + "正在睡觉");
    10. }
    11. }
    12. // Dog.java
    13. public class Dog extends Animal{
    14. void bark(){
    15. System.out.println(name + "汪汪汪~~~");
    16. }
    17. }
    18. // Cat.Java
    19. public class Cat extends Animal{
    20. void mew(){
    21. System.out.println(name + "喵喵喵~~~");
    22. }
    23. }
    24. // TestExtend.java
    25. public class TestExtend {
    26. public static void main(String[] args) {
    27. Dog dog = new Dog();
    28. // dog类中并没有定义任何成员变量,name和age属性肯定是从父类Animal中继承下来的
    29. System.out.println(dog.name);
    30. System.out.println(dog.age);
    31. // dog访问的eat()和sleep()方法也是从Animal中继承下来的
    32. dog.eat();
    33. dog.sleep();
    34. dog.bark();
    35. }
    36. }
    注意:
    • 子类会将父类中的成员变量或者成员方法继承到子类中了
    • 子类继承父类之后,必须要新添加自己特有的成员,体现出与基类的不同,否则就没有必要继承了
    在继承体系中,子类将父类中的方法和字段继承下来了,那在子类中能否直接访问父类中继承下来的成员呢?
    子类和父类不存在同名成员变量
    1. public class Base {
    2. int a;
    3. int b;
    4. }
    5. public class Derived extends Base{
    6. int c;
    7. public void method(){
    8. a = 10; // 访问从父类中继承下来的a
    9. b = 20; // 访问从父类中继承下来的b
    10. c = 30; // 访问子类自己的c
    11. }
    12. }
    子类和父类成员变量同名
    1. public class Base {
    2. int a;
    3. int b;
    4. int c;
    5. }
    6. /
    7. public class Derived extends Base{
    8. int a; // 与父类中成员a同名,且类型相同
    9. char b; // 与父类中成员b同名,但类型不同
    10. public void method(){
    11. a = 100; // 访问子类自己新增的a
    12. b = 101; // 访问子类自己新增的b
    13. c = 102; // 子类没有c,访问的肯定是从父类继承下来的c
    14. }
    15. }
    在子类方法中 或者 通过子类对象访问成员时
    • 如果访问的成员变量子类中有,优先访问自己的成员变量。
    • 如果访问的成员变量子类中无,则访问父类继承下来的,如果父类也没有定义,则编译报错。
    • 如果访问的成员变量与父类中成员变量同名,则优先访问自己的。
    成员变量访问遵循就近原则,自己有优先自己的,如果没有则向父类中找
    子类中访问父类的成员方法
    1. public class Base {
    2. public void methodA(){
    3. System.out.println("Base中的methodA()");
    4. }
    5. }
    6. public class Derived extends Base{
    7. public void methodB(){
    8. System.out.println("Derived中的methodB()方法");
    9. }
    10. public void methodC(){
    11. methodB(); // 访问子类自己的methodB()
    12. methodA(); // 访问父类继承的methodA()
    13. }
    14. }
    总结:成员方法没有同名时,在子类方法中或者通过子类对象访问方法时,则优先访问自己的,自己没有时再到父类中找,如果父类中也没有则报错。
    成员方法名字相同
    1. public class Base {
    2. public void methodA(){
    3. System.out.println("Base中的methodA()");
    4. }
    5. public void methodB(){
    6. System.out.println("Base中的methodB()");
    7. }
    8. }
    9. public class Derived extends Base{
    10. public void methodA(int a) {
    11. System.out.println("Derived中的method(int)方法");
    12. }
    13. public void methodB(){
    14. System.out.println("Derived中的methodB()方法");
    15. }
    16. public void methodC(){
    17. methodA(); // 没有传参,访问父类中的methodA()
    18. methodA(20); // 传递int参数,访问子类中的methodA(int)
    19. methodB(); // 直接访问,则永远访问到的都是子类中的methodB(),基类的无法访问到
    20. }
    21. }
    【说明】
    • 通过子类对象访问父类与子类中不同名方法时,优先在子类中找,找到则访问,否则在父类中找,找到 则访问,否则编译报错。
    • 通过派生类对象访问父类与子类同名方法时,如果父类和子类同名方法的参数列表不同(重载),根据调用 方法适传递的参数选择合适的方法访问,如果没有则报错;
    问题:如果子类中存在与父类中相同的成员时,那如何在子类中访问父类相同名称的成员呢?
    surper关键字
    由于设计不好,或者因场景需要,子类和父类中可能会存在相同名称的成员,如果要在子类方法中访问父类同名成员时,该如何操作?直接访问是无法做到的,Java 提供了 super 关键字,该关键字主要作用:在子类方法中访问父 类的成员
    1. public class Base {
    2. int a;
    3. int b;
    4. public void methodA(){
    5. System.out.println("Base中的methodA()");
    6. }
    7. public void methodB(){
    8. System.out.println("Base中的methodB()");
    9. }
    10. }
    11. public class Derived extends Base{
    12. int a; // 与父类中成员变量同名且类型相同
    13. char b; // 与父类中成员变量同名但类型不同
    14. // 与父类中methodA()构成重载
    15. public void methodA(int a) {
    16. System.out.println("Derived中的method()方法");
    17. }
    18. // 与基类中methodB()构成重写(即原型一致,重写后序详细介绍)
    19. public void methodB(){
    20. System.out.println("Derived中的methodB()方法");
    21. }
    22. public void methodC(){
    23. // 对于同名的成员变量,直接访问时,访问的都是子类的
    24. a = 100; // 等价于: this.a = 100;
    25. b = 101; // 等价于: this.b = 101;
    26. // 注意:this是当前对象的引用
    27. // 访问父类的成员变量时,需要借助super关键字
    28. // super是获取到子类对象中从基类继承下来的部分
    29. super.a = 200;
    30. super.b = 201;
    31. // 父类和子类中构成重载的方法,直接可以通过参数列表区分清访问父类还是子类方法
    32. methodA(); // 没有传参,访问父类中的methodA()
    33. methodA(20); // 传递int参数,访问子类中的methodA(int)
    34. // 如果在子类中要访问重写的基类方法,则需要借助super关键字
    35. methodB(); // 直接访问,则永远访问到的都是子类中的methodA(),基类的无法访问到
    36. super.methodB(); // 访问基类的methodB()
    37. }
    38. }
    在子类方法中,如果想要明确访问父类中成员时,借助 super 关键字即可。
    注意事项
    • 只能在非静态方法中使用
    • 在子类方法中,访问父类的成员变量和方法
    子类构造方法
    1. public class Base {
    2. public Base(){
    3. System.out.println("Base()");
    4. }
    5. }
    6. public class Derived extends Base{
    7. public Derived(){
    8. // super(); // 注意子类构造方法中默认会调用基类的无参构造方法:super(),
    9. // 用户没有写时,编译器会自动添加,而且super()必须是子类构造方法中第一条语句,
    10. // 并且只能出现一次
    11. System.out.println("Derived()");
    12. }
    13. }
    14. public class Test {
    15. public static void main(String[] args) {
    16. Derived d = new Derived();
    17. }
    18. }
    19. 结果打印:
    20. Base()
    21. Derived()
    在子类构造方法中,并没有写任何关于基类构造的代码,但是在构造子类对象时,先执行基类的构造方法,然后执 行子类的构造方法,因为:子类对象中成员是有两部分组成的,基类继承下来的以及子类新增加的部分 。父子父子 肯定是先有父再有子,所以在构造子类对象时候 ,先要调用基类的构造方法,将从基类继承下来的成员构造完整 ,然后再调用子类自己的构造方法,将子类自己新增加的成员初始化完整
    注意:
    • 若父类显式定义无参或者默认的构造方法,在子类构造方法第一行默认有隐含的super()调用,即调用基类构造方法
    • 如果父类构造方法是带有参数的,此时需要用户为子类显式定义构造方法,并在子类构造方法中选择合适的父类构造方法调用,否则编译失败。
    • 在子类构造方法中,super(...)调用父类构造时,必须是子类构造函数中第一条语句。
    • super(...)只能在子类构造方法中出现一次,并且不能和this同时出现
    super this
    super this 都可以在成员方法中用来访问:成员变量和调用其他的成员函数,都可以作为构造方法的第一条语 句,那他们之间有什么区别呢?
    相同点
    • 1. 都是Java中的关键字
    • 2. 只能在类的非静态方法中使用,用来访问非静态成员方法和字段
    • 3. 在构造方法中调用时,必须是构造方法中的第一条语句,并且不能同时存在
    不同点
    • 1. this是当前对象的引用,当前对象即调用实例方法的对象,super相当于是子类对象中从父类继承下来部分成员的引用
    • 2. 在非静态成员方法中,this用来访问本类的方法和属性,super用来访问父类继承下来的方法和属性
    • 3. 在构造方法中:this(...)用于调用本类构造方法,super(...)用于调用父类构造方法,两种调用不能同时在构造方法中出现
    • 4. 构造方法中一定会存在super(...)的调用,用户没有写编译器也会增加,但是this(...)用户不写则没有

    代码块执行顺序

    1. class Person {
    2. public String name;
    3. public int age;
    4. public Person(String name, int age) {
    5. this.name = name;
    6. this.age = age;
    7. System.out.println("构造方法执行");
    8. }
    9. {
    10. System.out.println("实例代码块执行");
    11. }
    12. static {
    13. System.out.println("静态代码块执行");
    14. }
    15. }
    16. public class TestDemo {
    17. public static void main(String[] args) {
    18. Person person1 = new Person("bit",10);
    19. System.out.println("============================");
    20. Person person2 = new Person("gaobo",20);
    21. }
    22. }
    23. //
    24. 静态代码块执行
    25. 实例代码块执行
    26. 构造方法执行
    27. ============================
    28. 实例代码块执行
    29. 构造方法执行
    1. 静态代码块先执行,并且只执行一次,在类加载阶段执行
    2. 当有对象创建时,才会执行实例代码块,实例代码块执行完成后,最后构造方法执行
    1. class Person {
    2. public String name;
    3. public int age;
    4. public Person(String name, int age) {
    5. this.name = name;
    6. this.age = age;
    7. System.out.println("Person:构造方法执行");
    8. }
    9. {
    10. System.out.println("Person:实例代码块执行");
    11. }
    12. static {
    13. System.out.println("Person:静态代码块执行");
    14. }
    15. }
    16. class Student extends Person{
    17. public Student(String name,int age) {
    18. super(name,age);
    19. System.out.println("Student:构造方法执行");
    20. }
    21. {
    22. System.out.println("Student:实例代码块执行");
    23. }
    24. static {
    25. System.out.println("Student:静态代码块执行");
    26. }
    27. }
    28. public class TestDemo4 {
    29. public static void main(String[] args) {
    30. Student student1 = new Student("张三",19);
    31. System.out.println("===========================");
    32. Student student2 = new Student("gaobo",20);
    33. }
    34. //
    35. Person:静态代码块执行
    36. Student:静态代码块执行
    37. Person:实例代码块执行
    38. Person:构造方法执行
    39. Student:实例代码块执行
    40. Student:构造方法执行
    41. ===========================
    42. Person:实例代码块执行
    43. Person:构造方法执行
    44. Student:实例代码块执行
    45. Student:构造方法执行
    通过分析执行结果,得出以下结论:
    • 父类静态代码块优先于子类静态代码块执行,且是最早执行
    • 父类实例代码块和父类构造方法紧接着执行
    • 子类的实例代码块和子类构造方法紧接着再执行
    • 第二次实例化子类对象时,父类和子类的静态代码块都将不会再执行

    关于不同包中的继承可见性

    1. // 为了掩饰基类中不同访问权限在子类中的可见性,为了简单类B中就不设置成员方法了
    2. // extend01包中
    3. public class B {
    4. private int a;
    5. protected int b;
    6. public int c;
    7. int d;
    8. }
    9. // extend01包中
    10. // 同一个包中的子类
    11. public class D extends B{
    12. public void method(){
    13. // super.a = 10; // 编译报错,父类private成员在相同包子类中不可见
    14. super.b = 20; // 父类中protected成员在相同包子类中可以直接访问
    15. super.c = 30; // 父类中public成员在相同包子类中可以直接访问
    16. super.d = 40; // 父类中默认访问权限修饰的成员在相同包子类中可以直接访问
    17. }
    18. }
    19. // extend02包中
    20. // 不同包中的子类
    21. public class C extends B {
    22. public void method(){
    23. // super.a = 10; // 编译报错,父类中private成员在不同包子类中不可见
    24. super.b = 20; // 父类中protected修饰的成员在不同包子类中可以直接访问
    25. super.c = 30; // 父类中public修饰的成员在不同包子类中可以直接访问
    26. //super.d = 40; // 父类中默认访问权限修饰的成员在不同包子类中不能直接访问
    27. }
    28. }
    29. // extend02包中
    30. // 不同包中的类
    31. public class TestC {
    32. public static void main(String[] args) {
    33. C c = new C();
    34. c.method();
    35. // System.out.println(c.a); // 编译报错,父类中private成员在不同包其他类中不可见
    36. // System.out.println(c.b); // 父类中protected成员在不同包其他类中不能直接访问
    37. System.out.println(c.c); // 父类中public成员在不同包其他类中可以直接访问
    38. // System.out.println(c.d); // 父类中默认访问权限修饰的成员在不同包其他类中不能直接访问
    39. }
    40. }

    Java中的继承

    注意: Java 中不支持多继承
    final 关键字
    final 关键可以用来修饰变量、成员方法以及类。
    1. 修饰变量或字段,表示常量 ( 即不能修改 )
    1. final int a = 10;
    2. a = 20; // 编译出错
    2. 修饰类:表示此类不能被继承
    1. final public class Animal {
    2. ...
    3. }
    4. public class Bird extends Animal {
    5. ...
    6. }
    7. // 编译出错
    8. Error:(3, 27) java: 无法从最终com.bit.Animal进行继承

    我们平时是用的 String 字符串类 , 就是用 final 修饰的 , 不能被继承 .
    3. 修饰方法:表示该方法不能被重写 ( 后序介绍 )
    多态
    多态的概念:通俗来说,就是多种形态, 具体点就是去完成某个行为,当不同的对象去完成时会产生出不同的状 态。
    java 中要实现多态,必须要满足如下几个条件,缺一不可:
    • 1. 必须在继承体系下
    • 2. 子类必须要对父类中方法进行重写
    • 3. 通过父类的引用调用重写的方法
    多态体现:在代码运行时,当传递不同类对象时,会调用对应类中的方法。
    1. public class Animal {
    2. String name;
    3. int age;
    4. public Animal(String name, int age){
    5. this.name = name;
    6. this.age = age;
    7. }
    8. public void eat(){
    9. System.out.println(name + "吃饭");
    10. }
    11. }
    12. public class Cat extends Animal{
    13. public Cat(String name, int age){
    14. super(name, age);
    15. }
    16. @Override
    17. public void eat(){
    18. System.out.println(name+"吃鱼~~~");
    19. }
    20. }
    21. public class Dog extends Animal {
    22. public Dog(String name, int age){
    23. super(name, age);
    24. }
    25. @Override
    26. public void eat(){
    27. System.out.println(name+"吃骨头~~~");
    28. }
    29. }
    30. ///分割线///
    31. public class TestAnimal {
    32. // 编译器在编译代码时,并不知道要调用Dog 还是 Cat 中eat的方法
    33. // 等程序运行起来后,形参a引用的具体对象确定后,才知道调用那个方法
    34. // 注意:此处的形参类型必须时父类类型才可以
    35. public static void eat(Animal a){
    36. a.eat();
    37. }
    38. public static void main(String[] args) {
    39. Cat cat = new Cat("元宝",2);
    40. Dog dog = new Dog("小七", 1);
    41. eat(cat);
    42. eat(dog);
    43. }
    44. }
    45. //
    46. 运行结果:
    47. 元宝吃鱼~~~
    48. 元宝正在睡觉
    49. 小七吃骨头~~~
    50. 小七正在睡觉

  • 相关阅读:
    冰蝎Behinder无法打开,冰蝎Behinder双击无法打开
    RK3568 Android11 默认打开网络ADB
    celery笔记四之在Django中使用celery
    如何启用 Podman 的自动容器更新
    excel功能区(ribbonx)编程笔记 4-combobox和dropdown控件
    什么灯适合学生在暑假使用?照度国AA级的舒适护眼灯
    DG failover TNS description list ,ADG service name 不一样的tnsnames.ora的设置
    性能测试工具——Jmeter的安装【超详细】
    C++的命名空间、缺省参数、函数重载 及引用
    [附源码]SSM计算机毕业设计医院仪器设备管理系统JAVA
  • 原文地址:https://blog.csdn.net/khh1014173041/article/details/133516186