首页 > 编程语言 >java继承与多态

java继承与多态

时间:2024-08-14 08:54:44浏览次数:14  
标签:System java 继承 void 多态 Dog Animal println out

继承与多态

一、概念

  1. 继承
    继承是面向对象编程中的一个基本概念,它允许我们定义一个类(称为子类或派生类)来继承另一个类(称为父类或基类)的属性和方法。通过继承,子类可以复用父类的代码,同时也可以添加自己的特定属性和方法。
    在Java中,继承是通过extends关键字来实现的。一个类只能直接继承一个父类(单继承),但可以通过接口(Interfaces)实现多重继承的效果。
  2. 多态
    多态是面向对象编程中的另一个重要概念,它允许我们以统一的接口处理不同类型的对象。换句话说就是,根据接受对象的不同,进行不同的操作。
    在Java中,多态主要有两种形式:编译时多态(通过方法重载实现)和运行时多态(通过继承和方法重写实现,也称为动态多态)。

二、代码示例解读

继承
package test1;

class Animal {
    public void eat() {
        System.out.println("Animal is eating");
    }
    protected void sleep() {
        System.out.println("Animal is sleeping");
    }
    private void run() {
        System.out.println("Animal is running");
    }
    void play() {
        System.out.println("Animal is playing");
    }
}

class Dog extends Animal {
    public void bark() {
        System.out.println("Dog is barking");
    }
}

public class demo {
    public static void main(String[] args) {
        Animal animal = new Animal();
        animal.eat(); // 执行结果:Animal is eating
        Dog dog = new Dog();
        dog.bark(); // 执行结果:Dog is barking
        dog.eat(); // 执行结果:Animal is eating
        dog.sleep(); // 执行结果:Animal is sleeping
        dog.play(); // 执行结果:Animal is playing
//        dog.run(); // 编译错误:Animal类中没有run()方法

    }
}


在上述代码中,通过extend 关键字Dog继承了Animal 的eat方法。Animal 是Dog的父类,Dog是Animal 的子类。当实例化Dog类后调用eat方法,可以看到执行的就是从父类继承来的方法,sleep、run、paly方法同理。

继承范围:
在Java中,子类可以继承父类的非私有(non-private)成员方法,这包括:

public 方法:公开的方法可以被任何类访问,因此子类可以继承并调用这些方法。

protected 方法:受保护的方法可以被同一个包内的类以及任何子类访问。子类可以继承这些方法,并可以在子类内部或者子类的子类内部调用它们。

默认(包)访问权限的方法:如果没有指定访问修饰符(即没有使用public、protected或private),则该方法具有包访问权限。这意味着这些方法只能在定义它们的同一个包内的类中被访问和继承。

这里要注意:
private 方法:私有方法是不可继承的。它们只能在定义它们的类内部被访问和调用。如果子类试图重写(override)一个父类的私有方法,这实际上是在子类中定义了一个全新的私有方法,它与父类中的那个私有方法没有任何关系。如果运行示例代码可以看到 dog.run();是无法运行的。

重写

在这里将重写和重载的概念对比解释一下。
重写:
重写是发生在子类与父类之间的。当子类中存在一个与父类中具有相同方法签名(方法名和参数列表完全相同)的方法时,子类的方法就重写了父类的方法。
特点:
1、发生在子类与父类之间。
2、 方法名、参数列表必须完全相同。
3、 访问级别不能比被重写的方法更严格(但可以更宽松)。例如,如果父类方法是protected的,那么子类重写的方法不能是private的。
子类方法不能抛出比父类方法更广泛的检查型异常(unchecked exceptions除外)。
4、如果父类方法是static的,那么子类不能重写该方法;如果子类提供了一个与父类静态方法具有相同签名的方法,那么这实际上是隐藏了父类的静态方法,而不是重写。

重载:
它允许在同一个类中定义多个同名的方法,只要这些方法的参数列表不同即可。参数列表的不同可以体现在参数的类型、参数的个数或者参数的顺序上。重载与方法的访问修饰符和返回类型无关。
特点:
1、发生在同一个类中。
2、方法名必须相同。
3、参数列表必须不同(参数类型、个数或顺序至少有一项不同)。
4、与返回类型无关。
5、与访问修饰符无关。

package test1;

class Animal {
    public void eat() {
        System.out.println("Animal is eating");
    }
    private void run() {
        System.out.println("Animal is running");
    }
    void play() {
        System.out.println("Animal is playing");
    }
}

class Dog extends Animal {
    public void bark() {
        System.out.println("Dog is barking");
    }
    @Override
    public void eat() {
        System.out.println("Dog is eating");
    }
}

public class demo {
    public static void main(String[] args) {
        Animal animal = new Animal();
        animal.eat(); 
        Dog dog = new Dog();
        dog.bark(); 
        dog.eat(); // 执行结果:Dog is eating
    }
}

通过上述代码可以看到dog.eat(); 实际上是执行的重写后的方法。

package test1;

class Animal {
    int age = 1;
    public void eat() {
        System.out.println("Animal is eating");
    }
    private void run() {
        System.out.println("Animal is running");
    }
    void play() {
        System.out.println("Animal is playing");
    }
}

class Dog extends Animal {
    int age = 2;
    public void bark() {
        System.out.println("Dog is barking");
    }
    @Override
    public void eat() {
        System.out.println("Dog is eating");
    }
}

public class demo {
    public static void main(String[] args) {

        Dog dog = new Dog();
        System.out.println(dog.age); //输出结果:2
        dog.eat(); //输出结果:Dog is eating
        System.out.println("*******************");
        Animal animal = new Dog();
        System.out.println(animal.age); //输出结果:2
        animal.eat(); //输出结果:Dog is eating
    }
}

执行上述代码,可能会对变量age的打印结果有疑惑。虽然dog和animal都是Dog类的实例化对象,但当你通过 Dog 类型对象访问 age 变量时,你访问的是 Dog 类中定义的 age 变量,因此输出为 2。当你通过 Animal 类型的引用访问 age 变量时,你访问的是 Animal 类中定义的 age 变量,因为引用类型是 Animal,所以输出为 1。

类型转换
package test1;

class Animal {
    int age = 1;
    public void eat() {
        System.out.println("Animal is eating");
    }
    private void run() {
        System.out.println("Animal is running");
    }
    void play() {
        System.out.println("Animal is playing");
    }
}

class Dog extends Animal {
    int age = 2;
    public void bark() {
        System.out.println("Dog is barking");
    }
    @Override
    public void eat() {
        System.out.println("Dog is eating");
    }
    public void getAge() {
        System.out.println("Dog's age is " + this.age); //输出结果:Dog's age is 2
        System.out.println("animal's age is " + super.age); //输出结果:animal's age is 1
        super.play(); //输出结果:Animal is playing
    }
}

public class demo {
    public static void main(String[] args) {
        Animal animal = new Dog();
        System.out.println(animal.age); //输出结果:1
//        animal.bark(); // 无法调用,因为Animal没有bark方法
        Dog animal1 = (Dog)animal; //向下转型
        System.out.println(animal1.age); //输出结果:2
        animal1.bark(); //输出结果:Dog is barking
        System.out.println("*********************");

        //优化后的方案: 判断当前对象是否是Dog类的对象, 如果是, 再调用bark()方法.
        if(animal instanceof Dog) { //判断animal是否是Dog类的对象
            //能走到这里, 说明条件满足
            Dog animal3 = (Dog)animal;
            animal3.bark(); // 输出结果: Dog is barking
        }

    }
}

向上转型:
向上转型是指将子类的引用转换为父类的引用。由于子类继承自父类,因此子类对象可以视为父类的一个特例,所以这种转换是安全的。向上转型是自动进行的,不需要显式地进行类型转换。子类可以直接调用从父类继承来的方法。

向下转型:
向下转型是指将父类的引用转换为子类的引用。由于父类引用可能指向多种子类对象(如果存在多个子类继承自同一个父类),因此这种转换是不安全的,需要进行显式转换,并且可能会抛出ClassCastException。可以在向下转型前使用instanceof 方法进行判断。

执行上述代码,看星号分割线上方的执行结果。在执行 Dog animal1 = (Dog)animal; 向下转型之前,Animal 类型的Dog实例化对象无法调用Dog类中的独有方法dark方法,当执行向下转型之后,可以调用dark方法,并且调用的变量也发生了变化。

this和super

this用于引用当前对象,可以调用当前对象的成员变量和方法,以及构造器。

super用于引用当前对象的直接父类,可以访问父类的成员变量和方法,以及调用父类的构造器

当使用super调用父类的方法时,可以调用父类中定义的任何非私有(non-private)实例方法(即,除了被声明为private的方法以外的所有实例方法)。这是因为私有方法仅在其定义的类内部可见,无法通过继承访问。当然,父类的私有变量也是不可以的。

package test1;

class Animal {
    int age = 1;
    public void eat() {
        System.out.println("Animal is eating");
    }
    private void run() {
        System.out.println("Animal is running");
    }
    void play() {
        System.out.println("Animal is playing");
    }
}

class Dog extends Animal {
    int age = 2;
    public void bark() {
        System.out.println("Dog is barking");
    }
    @Override
    public void eat() {
        System.out.println("Dog is eating");
    }
    public void getAge() {
        System.out.println("Dog's age is " + this.age); //输出结果:Dog's age is 2
        System.out.println("animal's age is " + super.age); //输出结果:animal's age is 1
        super.play(); //输出结果:Animal is playing
    }
}

通过上述代码可以知道,通过this.age调用的是当前类中定义的int age = 2;以及方法,super调用的是父类中的方法或变量。

标签:System,java,继承,void,多态,Dog,Animal,println,out
From: https://blog.csdn.net/kopokliv/article/details/141144436

相关文章

  • Java小白一文视图教废CSDN大佬们局部内部类和匿名内部类
    内部类一个类的内部又完整地嵌套了另一个类结构,被嵌套的类称为内部类,嵌套其他类的类称为外部类,是我们类的第五大成员,内部类最大的特点就是可以直接访问私有属性,并且可以体现类与类之间的包含关系。类的五大成员:属性、方法、构造器、代码块、内部类内部类快速入门//外部其......
  • 日撸Java三百行(day22:二叉树的存储)
    目录前言一、压缩存储二、层次遍历三、代码实现1.顺序表创建及初始化2.方法创建3.数据测试4.完整的程序代码总结前言关于二叉树的存储,昨天我们提到有顺序存储和链式存储这两种方式,不过非完全二叉树顺序存储的话会造成很大的空间浪费,所以我们昨天使用的是链式存储......
  • 【Java手写RPC框架系列-1】—— 基础知识准备:RPC+Netty
    代码随想录知识星球介绍https://articles.zsxq.com/id_m76jd72243bi.html基于Netty手写实现RPChttps://www.cnblogs.com/mic112/p/15565795.html项目背景与介绍RPC:远程过程调用协议:客户端在不知道调用细节的情况下,调用存在于远程计算机上的某个对象,就像调用本地应用程序......
  • Java 运算符(详解)
    文章目录一、简介二、算术运算符三、自增自减运算符四、关系运算符五、逻辑运算符六、位运算符六、赋值运算符七、条件运算符八、字符串连接符九、运算符优先级一、简介在Java中,运算符是用来对数据进行操作和处理的符号,这些符号能使得Java程序进行各种数学计算、......
  • Leetcode JAVA刷刷站(20)有效的括号
    一、题目概述二、思路方向     在Java中,要判断一个仅包含括号('(',')','{','}','[',']')的字符串是否有效,你可以使用栈(Stack)数据结构来实现。栈是一种后进先出(LIFO,LastInFirstOut)的数据结构,非常适合用来处理这类问题。以下是具体的实现步骤和代码示例:创......
  • Java并发类的主要API方法-Semaphore
    一、Semaphoreemaphore是Java并发包(java.util.concurrent)中的一个同步工具类,类Semaphore所提供的功能完全就是synchronized关键字的升级版,但它提供的功能更加的强大与方便,主要的作用就是控制线程并发的数量,而这一点,单纯地使用synchronized是做不到的。emaphore它用......
  • Java数组06:常见排序算法
    1.冒泡排序冒泡排序(BubbleSort),是一种计算机科学领域的较简单的排序算法。它重复地走访过要排序的元素列,依次比较两个相邻的元素,如果他们的顺序(如从大到小、首字母从A到Z)错误就把他们交换过来。走访元素的工作是重复地进行直到没有相邻元素需要交换,也就是说该元素列已经排序完......
  • Java数组07:稀疏数组
    1.线性结构线性结构是最常用的数据结构,其特点是数据元素之间存在一对一的线性关系。线性结构有两种不同存储结构,即顺序存储结构和链式存储结构。顺序存储的线性表称为顺序表,顺序表中的存储元素是连续的,即在内存中是连续的,例如数组。链式存储的线性表称为链表,链表中的存储元......
  • Java数组05:Arrays 类
    数组的工具类java.util.Arrays由于数组对象本身并没有什么方法可以供我们调用,但API中提供了一个工具类Arrays供我们使用,从而可以对数据对象进行一些基本的操作。文档简介:此类包含用来操作数组(比如排序和搜索)的各种方法。此类还包含一个允许将数组作为列表来查看的静态工厂。......
  • 一款Java 性能监控和统计工具——MyPerf4J!【送源码】
    背景随着所在公司的发展,应用服务的规模不断扩大,原有的垂直应用架构已无法满足产品的发展,几十个工程师在一个项目里并行开发不同的功能,开发效率不断降低。于是公司开始全面推进服务化进程,把团队内的大部分工程师主要精力全部都集中到服务化中。服务化可以让每个工程师仅在自己......