首页 > 编程语言 >详解Java之继承与多态

详解Java之继承与多态

时间:2024-09-30 12:19:22浏览次数:8  
标签:Java name 多态 System 详解 Animal println 方法 public

目录

继承

派生类和基类各部分执行顺序

protected

访问权限总结

final关键字

组合

多态

向上转型

向下转型

动态绑定

静态绑定

方法重载

方法重写 

super关键字

super和this的对比

在构造方法中调用重写方法


继承

继承是为了解决多个类具有一些相同的属性和方法而造成的代码冗余问题,将这些具有相同属性和方法分离出来设置为一个类,然后让有这些属性和方法的类来继承它。

被继承的类成为父类,基类或超类,而继承的类成为子类或派生类。

语法规则

class 子类 extends 父类 { }

使用 extends 指定父类.
Java 中一个子类只能继承一个父类 (而C++/Python等语言支持多继承).
子类会继承父类的所有 public 的字段和方法.
对于父类的 private 的字段和方法, 子类中是无法访问的.
子类的实例中, 也包含着父类的实例. 可以使用 super 关键字得到父类实例的引用

super的使用

访问父类的成员属性:super.data

访问父类的成员方法:super.func()

派生类和基类各部分执行顺序

举例

class Animal{
    public String name;

    public Animal(String name) {
        this.name = name;
        System.out.println("Animal的构造方法");
    }

    {
        System.out.println("Animal的实例代码块");
    }

    static{
        System.out.println("Animal的静态代码块");
    }
}

class Dog extends Animal{
    public Dog(String name){
        super(name);
        System.out.println("Dog的构造方法");
    }

    {
        System.out.println("Dog的实例代码块");
    }

    static{
        System.out.println("Dog的静态代码块");
    }
}

public class Main {
    public static void main(String[] args) {
        Dog dog=new Dog("黑子");
    }
}

输出结果: 

 

protected

对于类的调用者来说, protected 修饰的字段和方法是不能访问的;
对于类的 子类 和 同一个包的其他类 来说, protected 修饰的字段和方法是可以访问的。

访问权限总结
Java 中对于字段和方法共有四种访问权限
private: 类内部能访问, 类外部不能访问
默认(也叫包访问权限): 类内部能访问, 同一个包中的类可以访问, 其他类不能访问.
protected: 类内部能访问, 子类和同一个包中的类可以访问, 其他类不能访问.
public : 类内部和类的调用者都能访问

final关键字

当final修饰一个变量或者字段的时候,表示不可被修改(常量)。

final关键字也能修饰类,此时表示被修饰的类不能被继承。

我们平时使用的String字符串类,就是用final修饰的,不能被继承。

此时的array变量保存的是地址,指向一段内存,因为array被final修饰,因此不能修改的是array的指向,但是array保存的地址指向的空间的内容可以被修改!!!

组合

和继承类似, 组合也是一种表达类之间关系的方式, 也是能够达到代码重用的效果.
例如表示一个学校:

public class Student { 
 ...
}
public class Teacher { 
 ...
}
public class School {
    public Student[] students;
    public Teacher[] teachers;
}

组合表示has-a语义;

继承表示is-a语义。

多态
向上转型

向上转型发生的时机:

直接赋值;方法传参;方法返回。

向下转型

举例

class Animal {
    protected String name;
    public Animal(String name) {
        this.name = name;
    }
    public void eat(String food) {
        System.out.println("我是一只小动物");
        System.out.println(this.name + "正在吃" + food);
    }
}
// Bird.java 
class Bird extends Animal {
    public Bird(String name) {
        super(name);
    }

    public void eat(String food) {
        System.out.println("我是一只小鸟");
        System.out.println(this.name + "正在吃" + food);
    }

    public void fly() {
        System.out.println(this.name + "正在飞");
    }
}

如果我们将Bird向上转型为Animal,可以调用eat方法,但是不能调用fly方法了,会编译出错

编译过程中, animal 的类型是 Animal, 此时编译器只知道这个类中有一个 eat 方法, 没有 fly 方法.
虽然 animal 实际引用的是一个 Bird 对象, 但是编译器是以 animal 的类型来查看有哪些方法的.
对于 Animal animal = new Bird("小鸟") 这样的代码,编译器检查有哪些方法存在, 看的是 Animal 这个类型执行时究竟执行父类的方法还是子类的方法, 看的是 Bird 这个类型。

解决方法:向下转型

输出结果:

向下转型是不靠谱的,比如

Animal animal = new Cat(" 小猫 "); Bird bird = (Bird)animal; bird.fly();

因为Cat类中没有fly方法,因此这样会执行出错,抛异常。 

动态绑定

举例

class Animal{
    String name;

    public Animal(String name) {
        this.name = name;
    }

    public void eat(String food){
        System.out.println("我是动物");
        System.out.println(this.name+"正在吃"+food);
    }
}

class Bird extends Animal{
    public Bird(String name){
        super(name);
    }

    public void eat(String food){
        System.out.println("我是小鸟");
        System.out.println(this.name+"正在吃"+food);
    }
}

public class Test {
    public static void main(String[] args) {
        Animal animal=new Animal("动物");
        animal.eat("小麦");
        Animal animal1=new Bird("小鸟");
        animal1.eat("谷物");
    }
}

运行结果:

我们惊奇的发现,虽然animal和animal1都是Animal类型的引用,但是animal1的eat方法调用结果与animal的eat方法调用结果不同,即animal指向Animal类型的实例,animal.eat()调用了父类的实例,animal1指向Bird类型的实例,animal1.eat()调用了子类的实例。

因此,在Java中,调用某个类的方法,究竟执行了哪段代码(是父类方法的代码还是子类方法的代码),要看这个引用指向的是父类对象还是子类对象,这个过程是程序运行时决定的(而不是编译期间),因此称之为 动态绑定。

静态绑定

静态绑定发生在编译时期,也就是说,在编译时就已经确定了要调用的方法。这通常适用于那些不需要在运行时根据对象的实际类型来确定方法的情况。静态绑定主要发生在以下几种情况:

private方法:因为private方法是不可见的,所以它们不能被继承,调用它们的引用在编译时就已知,因此是静态绑定的。
final方法:final方法不能被子类覆盖,所以编译器在编译时就可以确定要调用的方法。
static方法:静态方法属于类,不属于任何对象实例,且可以通过类名直接调用。因此,编译器在编译时就能确定调用哪个方法。
构造方法:虽然构造方法看起来像是方法,但实际上它们是在对象创建时调用的特殊方法。因为构造方法不是通过对象实例调用的,所以也是静态绑定的。

方法重载

重载是指在同一个类中,允许多个同名方法同时存在,但要求它们的参数列表不同(参数的个数、类型或顺序至少有一项不同)。重载与方法的返回类型无关,即两个方法可以有相同的名称和不同的返回类型,但如果它们的参数列表相同,则不能构成重载。重载是编译时多态的体现。

举例

class Test {  
    void show(int a) {  
        System.out.println("整数:" + a);  
    }  
  
    void show(String s) {  
        System.out.println("字符串:" + s);  
    }  
  
    // 下面这个方法是合法的重载,因为参数列表不同(参数类型不同)  
    void show(double a) {  
        System.out.println("双精度数:" + a);  
    }  
  
    // 如果下面这个方法被添加到类中,它将导致编译错误,因为与第一个方法参数列表相同  
    // void show(int a) { ... }  
}

方法重写 

针对刚才的 eat 方法来说:
子类实现父类的同名方法, 并且参数的类型和个数完全相同, 这种情况称为 覆写/重写/覆盖(Override).


关于重写的注意事项
1. 重写和重载完全不一样,不要混淆.
2. 普通方法可以重写, static 修饰的静态方法不能重写.

3. 重写中子类的方法的访问权限不能低于父类的方法访问权限.
4. 重写的方法返回值类型不一定和父类的方法相同(但是建议最好写成相同, 特殊情况除外).

5.被private修饰的方法不能进行重写。

6.被final修饰的方法不能进行重写,此时这个方法叫做密封方法。

7.访问修饰限定符权限大小:private < 默认权限 < protected < public。

8.方法的返回值可以不同,但是必须是父子关系,或者称为协变。

9.构造方法不能进行重写。

举例

class Animal {  
    void eat() {  
        System.out.println("动物吃东西");  
    }  
}  
  
class Dog extends Animal {  
    // Dog类重写了Animal类的eat方法  
    @Override  
    void eat() {  
        System.out.println("狗吃骨头");  
    }  
}  
  
// 使用  
Animal myDog = new Dog();  
myDog.eat(); // 输出:狗吃骨头(尽管引用类型是Animal,但实际对象类型是Dog,体现了多态性)
super关键字

前面的代码中由于使用了重写机制, 调用到的是子类的方法. 如果需要在子类内部调用父类方法怎么办? 可以使用super 关键字.
super 表示获取到父类实例的引用. 涉及到两种常见用法:

1) 使用了 super 来调用父类的构造器
public Bird(String name) {
    super(name);
}
2) 使用 super 来调用父类的普通方法
public class Bird extends Animal {
    public Bird(String name) {
        super(name);
    }
    @Override
    public void eat(String food) {
        // 修改代码, 让子调用父类的接口. 
        super.eat(food);
        System.out.println("我是一只小鸟");
        System.out.println(this.name + "正在吃" + food);
    }
}
在这个代码中 , 如果在子类的 eat 方法中直接调用 eat ( 不加 super), 那么此时就认为是调用子类自己的 eat ( 也就是递归了). 而加上 super 关键字 , 才是调用父类的方法.
super和this的对比
在构造方法中调用重写方法
class B {
    public B() {
        // do nothing 
        func();
    }
    public void func() {
        System.out.println("B.func()");
    }
}
class D extends B {
    private int num = 1;
    @Override
    public void func() {
        System.out.println("D.func() " + num);
    }
}
public class Test {
    public static void main(String[] args) {
        D d = new D();
    }
}

输出结果:

解释:

构造 D 对象的同时, 会调用 B 的构造方法.
B 的构造方法中调用了 func 方法, 此时会触发动态绑定, 会调用到 D 中的 func。
此时 D 对象自身还没有构造, 此时 num 处在未初始化的状态, 值为 0。

标签:Java,name,多态,System,详解,Animal,println,方法,public
From: https://blog.csdn.net/wmh_1234567/article/details/140738138

相关文章

  • H.264编解码 - I/P/B帧详解
    一、概述在H.264编解码中,I/P/B帧是一种常见的帧类型。以下是它们的解释:I帧(关键帧):也称为关键帧,它是视频序列中的第一个帧或每个关键时刻的第一个帧。I帧是完整的、自包含的图像帧,不依赖于其他帧进行解码。它存储了关键时刻的完整图像信息。P帧(预测帧):P帧是依赖于之前的关......
  • 大数据-155 Apache Druid 架构与原理详解 数据存储 索引服务 压缩机制
    点一下关注吧!!!非常感谢!!持续更新!!!目前已经更新到了:Hadoop(已更完)HDFS(已更完)MapReduce(已更完)Hive(已更完)Flume(已更完)Sqoop(已更完)Zookeeper(已更完)HBase(已更完)Redis(已更完)Kafka(已更完)Spark(已更完)Flink(已更完)ClickHouse(已更完)Kudu(已更完)Druid(正在更新…)章节内容上节我们完成了如......
  • java:详解java编译命令和启动命令
    编译命令在Java开发过程中,编译Java源文件(通常以.java为扩展名)是不可或缺的一步。这一步骤是通过javac命令完成的,该命令是Java编译器(JavaCompiler)的命令行工具。编译后的代码会生成字节码文件,这些文件以.class为扩展名,并可在Java虚拟机(JVM)上运行。基本语法......
  • Vue.js组件开发详解
    Vue.js组件开发详解Vue.js是一个用于构建用户界面的渐进式框架,其核心思想是通过数据驱动视图的变化,同时提供了一系列强大的工具来帮助开发者高效地开发复杂的单页应用。在Vue.js中,组件是构建复杂应用的基本单元,通过组件化开发,我们可以将应用拆分成可复用的、独立的模块,......
  • 2024年华为OD机试真题-找终点-Java-OD统一考试(E卷)
    最新华为OD机试考点合集:华为OD机试2024年真题题库(E卷+D卷+C卷)_华为od机试题库-CSDN博客     每一题都含有详细的解题思路和代码注释,精选c++、JAVA、Python三种语言解法。帮助每一位考生轻松、高效刷题。订阅后永久可看,发现新题及时跟新。题目描述给定一个正整数数组,设......
  • 在java后端发送HTTPClient请求
    简介HttpClient遵循http协议的客户端编程工具包支持最新的http协议部分依赖自动传递依赖了HttpClient的jar包明明项目中没有引入HttpClient的Maven坐标,但是却可以直接使用HttpClient原因是:阿里云的sdk依赖中传递依赖了HttpClient的jar包发送get请求......
  • Java内部类详解
    Java内部类详解1.内部类基础在Java中,可以将一个类定义在另一个类里面或者一个方法里面,这样的类称为内部类。广泛意义上的内部类一般来说包括这四种:成员内部类、局部内部类、匿名内部类和静态内部类。下面就先来了解一下这四种内部类的用法。1.1.成员内部类成员内部类是......
  • 【JavaScript】搭建一个具有记忆的简洁个人待办网页
    1.HTML结构文档类型声明:<!DOCTYPEhtml>这告诉浏览器这是一个HTML5文档。HTML标签:<htmllang="zh-CN">表示整个页面的内容,lang="zh-CN"表示内容使用简体中文。头部信息:<head><!--...--></head>包含页面的元数据,如字符集、视图窗口设置、标题和样式。样......
  • 「Java开发指南」如何用MyEclipse为iPhone搭建Spring应用程序?
    本教程将引导您完成在iPhone上使用的软件组件生成,这就产生了一个完全实现的可运行iPhone应用程序,可以立即用于验证和测试生成的iPhone组件。在本教程中,您将学习如何:从数据库表搭建到现有项目部署应用程序MyEclipsev2024.1离线版下载MyEclipse技术交流群:742336981欢迎一起......
  • Spring Boot项目 application.yml 配置赋值到 Java配置类中
    SpringBoot项目 application.yml 配置赋值到Java配置类中application.ymlsdkConfig:#是否为测试环境(true=是;false=否)isDevEnv:falseSdkConfig.javaimportorg.springframework.beans.factory.annotation.Value;importorg.springframework.stereotype.Comp......