【Java】继承和多态

news/2025/2/19 17:51:52/

目录

1.继承

1.1关键字super

1.2关键字protected

1.3关键字final

1.4组合

//小练习:三者乘积

2.多态

2.1重写override 

2.2向上转型与向下转型

2.3运行时绑定 

2.4多态


1.继承

定义:对子类的共性进行抽取并放到父类当中。

优点:达到了代码的复用。

语法:class + 子类/派生类 + extends + 父类/基类/超类

class Animal{ //父类public String name;public int age;public void eat() {System.out.println(this.name+"正在吃饭");}
}
class Dog extends Animal{ //子类public String field;//子类自己的public void bark() {System.out.println(this.name+"汪汪汪");}
}
class Cat extends Animal{ //子类public void bark() {System.out.println(this.name+"喵喵喵");}
}
public class Main {public static void main(String[] args) {Dog dog = new Dog();dog.name = "灰灰";dog.age = 4;dog.bark();Cat cat = new Cat();cat.name = "咪咪";cat.age = 3;cat.bark();}
}

1.1关键字super

1. 当子类和父类成员变量重名时,优先访问子类自己的,若想先访问父类成员,在前面加上关键字super

2.super只能在子类中使用,增加代码的可读性,并不是父类对象的引用;

2. super 和 this 都只能在静态方法中使用;super 与 this 在构造方法中调用时,必须都是第一条语句,所以不能同时存在。

class Base {public int a = 1;public int b = 2;public void method() {System.out.println("父类method");}
}
class Derived extends Base {public int a = 3;//与父类重名了public int c = 4;public void method() {System.out.println(a);//3 —— 重名时优先使用子类自己的System.out.println(super.a);//1 —— 强制优先使用父类System.out.println(b);//2System.out.println(c);//4super.method();//父类method}
}
public class TestDemo {public static void main(String[] args) {Derived derived = new Derived();derived.method();}
}

注意子类在完成构造前,必须先完成父类的构造!(利用super)

若父类中没有写构造方法,编译器会自动在父类和子类中生成不带参数的构造方法,但若父类中写了带参数的构造方法,子类中就必须写代码块帮助父类构造。

class Animal {public String name;public int age;public Animal(String name, int age) { //父类中有构造方法this.name = name;this.age = age;}
}
class Dog extends Animal {String habit;public Dog() { //子类构造方法super("huihui",3);//帮助父类构造}public Dog(String name,int age) { //子类构造方法super(name,age);}public void bark() {System.out.println(this.name+"汪汪汪");}
}

1.2关键字protected

访问权限:同一包中的子类和非子类,不同包中的子类。

同一包中的访问很简单,这里就不赘叙了,下面来看看不同包中如何访问:

package Demo;public class Test1 {protected int a = 10;
}
package Demo2;import Demo.Test1;public class Test2 extends Test1 { //继承public void func() {System.out.println(super.a);//通过super访问}public static void main(String[] args) {Test3 test3 = new Test2();test2.func();//10}
}

1.3关键字final

如果一个类被final修饰了,那么这个类就不能被继承了;

如果一个变量被final修饰了,那么这个变量就不能被修改了(相当于const);

分类:单继承,不同类继承同一个类,多继承(Java不支持)

final class Math { //final修饰后该类不能被继承public int m = 1;public int n = 2;
}
public class Test {public static void main(String[] args) {final int a = 10;//此时a相当于常量//a = 100;//不能被修改System.out.println(a);final int b;//定义的同时可以不初始化b = 20;//b = 200;//但只能初始化一次System.out.println(b);}
}

1.4组合

继承与组合的区别:https://www.hollischuang.com/archives/1319

继承表示对象之间是is-a的关系:狗是动物;

组合表示对象之间是has-a的关系:汽车与零部件,学校与师生。

class Student{
}
class Teacher{
}
class School{private Student[] students;private Teacher[] teachers;
}

//小练习:三者乘积

有父类Base,内部定义了x、y属性。有子类Sub,继承自父类Base。子类新增了一个z属性,并且定义了calculate方法,在此方法内计算了父类和子类中x、y、z属性三者的乘积。

import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner scanner = new Scanner(System.in);while (scanner.hasNextInt()) {int x = scanner.nextInt();int y = scanner.nextInt();int z = scanner.nextInt();Sub sub = new Sub(x, y, z);System.out.println(sub.calculate());}}
}
class Base {private int x;private int y;public Base(int x, int y) {this.x = x;this.y = y;}public int getX() {return x;}public int getY() {return y;}
}
class Sub extends Base {private int z;public Sub(int x, int y, int z) {super(x,y);this.z = z;}public int getZ() {return z;}public int calculate() {return super.getX() * super.getY() * this.getZ();}
}

2.多态

2.1重写override 

条件:方法名称,参数列表(个数,顺序,类型),返回类型均相同。

注意:1> 被 private,static,final修饰的方法 [ 密封方法 ] 以及构造方法不能被重写;

           2> 子类的访问修饰限定符一定要大于等于父类的访问修饰限定符

           3> 重写的返回值类型可以不同,但必须是父子类关系(协变类型);

           4> 可手动加上@Override注解来显式指定,帮我们进行合法性校验。

class Animal {public String name;public int age;public void eat() {System.out.println(this.name+"正在吃饭!");}public Animal(String name, int age) {this.name = name;this.age = age;}
}
class Dog extends Animal {public Dog(String name,int age) {super(name,age);this.name = name;this.age = age;}@Overridepublic void eat() { //与父类的eat方法构成了重写System.out.println(this.name+"正在吃狗粮!");}
}
public class Test {public static void main(String[] args) {Dog dog = new Dog("wangwang",1);dog.eat();}
}

2.2向上转型与向下转型

定义:用父类引用来接受子类对象,从小范围向大范围的转换。

语法:父类类型 对象名 = new 子类类型( );

发生的时机: 直接赋值、传参、返回值。

优点:让代码更简单灵活;缺点:不能调用到子类特有的方法。

public class Test {public static void main(String[] args) {/*Dog dog = new Dog("wangwang",1);Animal animal = dog;*/Animal animal = new Dog("wangwang",1);//等价于上面两行代码,即向上转型}
}

向下转型:不安全,要使用关键字 instanceof 来判断能否转型   

public class Test {public static void main(String[] args) {Animal animal = new Dog("wangwang",1);if (animal instanceof Cat) { //判断能否转型Cat cat = (Cat) animal;//向下转型cat.mimi();//编译通过,运行报错,不安全}if (animal instanceof Dog) {Dog dog = (Dog) animal;//向下转型dog.bark();//编译通过,运行不报错}}
}

2.3运行时绑定 

public class Test {public static void main(String[] args) {Animal animal = new Dog("wangwang",1);//animal.bark();//类型为Animal,animal无法调用子类的bark方法,只能调用父类自己的方法animal.eat();//同上,按理说应当调用父类的eat方法-吃饭,但实际上调用了子类的eat方法-吃狗粮,why??}
}

通过查看汇编代码,我们发现,编译时确实调用了父类Animal的eat方法,但是运行的结果又是子类Dog的。这个过程就叫做 运行时绑定 / 动态绑定

触发条件:1> 发生了重写;2> 通过父类引用调用了重写的方法。

动态绑定:后期绑定(晚绑定),即在编译时不能确定方法的行为,需要等到程序运行时才能确定具体调用哪个方法;

静态绑定:前期绑定(早绑定),即在编译时根据用户所传递的参数类型个数返回值等就确定了具体调用哪个方法,如重载

2.4多态

父类引用子类对象,当引用的子类对象不一样的时候,通过这个父类引用调用父类和子类重写方法时,此时同一个引用呈现出来的不同状态,就是多态。

条件:1> 必须在继承体系下;

           2> 子类必须要对父类中的方法进行重写

           3> 通过父类的引用调用重写方法。

public class Test {public static void testFunc(Animal animal) {animal.eat();}public static void main(String[] args) {Dog dog = new Dog("wangwang",1);testFunc(dog);System.out.println("----------");Cat cat = new Cat("miaomiao",2);testFunc(cat);}
}

优点:降低代码的“圈复杂度”,避免使用大量的if-else语句。 

缺点:代码的运行效率降低,属性没有多态性。

class Shape {public void draw() {System.out.println("画图形!");}
}
class Rect extends Shape{@Overridepublic void draw() {System.out.println("♦");}
}
class Cycle extends Shape{@Overridepublic void draw() {System.out.println("●");}
}
class Flower extends Shape{@Overridepublic void draw() {System.out.println("❀");}
}
public class Demo {public static void drawShapes1() { //不基于多态Rect rect = new Rect();Cycle cycle = new Cycle();Flower flower = new Flower();String[] shapes = {"cycle", "rect", "cycle", "rect", "flower"};for (String shape : shapes) { //有大量的if-else语句if (shape.equals("cycle")) {cycle.draw();} else if (shape.equals("rect")) {rect.draw();} else if (shape.equals("flower")) {flower.draw();}}}public static void drawShapes2() { //基于多态,代码更加简洁Shape[] shapes = {new Cycle(), new Rect(), new Cycle(),new Rect(), new Flower()};for (Shape shape : shapes) {shape.draw();}}public static void main(String[] args) {drawShapes2();}
}

Over!接口见!!


http://www.ppmy.cn/news/56539.html

相关文章

轻松剪辑、合并和添加特效,快速完成视频处理——掌握MoviePy库

🎬MoviePy: Python视频编辑🎥 😍 简介 你喜欢看电影吗?🍿不知道你有没有想过,如果能够自己编辑视频就好了!🤩 没错,现在有了MoviePy,Python视频编辑库&…

【Unity-UGUI控件全面解析】| RawImage 显示纹理组件详解

🎬【Unity-UGUI控件全面解析】| RawImage 显示纹理组件详解一、组件介绍二、组件属性面板三、代码操作组件四、组件常用方法示例4.1 相机映射,可充当小地图4.2 播放视频💯总结🎬 博客主页:https://xiaoy.blog.csdn.net 🎥 本文由 呆呆敲代码的小Y 原创,首发于 CSDN�…

App Store上架流程/苹果app发布流程

第一步:拥有自己的苹果开发者账号: 开发账号分为两类:99美元(发布App Store用的,也就是上架苹果商店用这个);299美元(企业授信证书,不用上架appstore 亦可使用。弊端&am…

论接口的封装能力

刚入职行业不到一年的菜鸟的心得体会,真实感受,不喜勿喷。 为什么想到封装呢?还不是因为踩过坑,最近越来越感觉,直接用一些开源库很不方便,而且用起来总是忘塞。 1.在工程开发过程中,我们不可避…

定位的特殊应用

注意:发生固定定位,绝对定位后,元素都变成了定位元素,默认高宽被内容撑开,则可以设置宽高;以下只针对绝对定位和固定定位的元素,不包括相对定位元素。 1.定位元素块的宽充满包含块 前提&#x…

C#基础 (类型转换_隐式转换)

什么是类型转换 类型转换就是不同变量类型之间的相互转换 隐式转换的基本规则——>不同类型之间自动转换 大范围装小范围 相同大类型之间的转换 有符号 long——>int——>short——>sbyte 可以用大范围装小范围的类型(隐式转换) 不能够用小范…

PostgreSQL16中pg_dump的LZ4和ZSTD压缩

PostgreSQL16中pg_dump的LZ4和ZSTD压缩 pg_dump压缩lz4和zstd LZ4和ZSTD压缩算法合入了PG16。LZ4补丁的作者是Georgios Kokolatos。由Tomas Vondra提交。由Michael Paquier、Rachel Heaton、Justin Pryzby、Shi Yu 和 Tomas Vondra 审阅。提交消息是: Expand pg_dum…

SpringBoot高频面试题

Springboot的优点 内置servlet容器,不需要在服务器部署 tomcat。只需要将项目打成 jar 包,使用 java -jar xxx.jar一键式启动项目SpringBoot提供了starter,把常用库聚合在一起,简化复杂的环境配置,快速搭建spring应用…