java继承和多态详解

简介: java继承和多态详解

前言

继承和多态刚开始是比较难理解的,要做到多看,多敲代码。

一、继承

有的时候客观事物之间就存在一些关联关系, 那么在表示成类和对象的时候也会存在一定的关联。

为了方便理解,我们先看下面一段代码。

(1)例如, 设计一个类表示动物

// Animal.java 
public class Animal { 
    public String name; 
    public Animal(String name) { 
this.name = name; 
    } 
    public void eat(String food) { 
        System.out.println(this.name + "正在吃" + food); 
    } 
} 
// Cat.java 
class Cat { 
public String name; 
    public Cat(String name) { 
this.name = name; 
    } 
    public void eat(String food) { 
        System.out.println(this.name + "正在吃" + food); 
    } 
} 
// Bird.java 
class Bird { 
public String name; 
    public Bird(String name) { 
        this.name = name; 
    } 
} 
    public void eat(String food) { 
        System.out.println(this.name + "正在吃" + food); 
    } 
    public void fly() { 
        System.out.println(this.name + "正在飞 ︿( ̄︶ ̄)︿"); 
    }

仔细分析, 我们发现 Animal 和 Cat 以及 Bird 这几个类中存在一定的关联关系:

1)这三个类都具备一个相同的 eat 方法, 而且行为是完全一样的.

2)这三个类都具备一个相同的 name 属性, 而且意义是完全一样的.

(2)语法规则

class 子类 extends 父类 { 
} 

对于上面的代码, 可以使用继承进行改进. 此时我们让 Cat 和 Bird 继承自 Animal 类, 那么 Cat 在定义的时候就不必再写 name 字段和 eat 方法。

class Animal { 
    public String name; 
    public Animal(String name) { 
        this.name = name; 
    } 
    public void eat(String food) { 
        System.out.println(this.name + "正在吃" + food); 
    } 
} 
class Cat extends Animal { 
    public Cat(String name) { 
        // 使用 super 调用父类的构造方法.  
        super(name); 
    } 
} 
class Bird extends Animal { 
    public Bird(String name) { 
      super(name); 
    } 
    public void fly() { 
        System.out.println(this.name + "正在飞 ︿( ̄︶ ̄)︿"); 
    } 
} 
public class Test { 
    public static void main(String[] args) { 
        Cat cat = new Cat("小黑"); 
        cat.eat("猫粮"); 
        Bird bird = new Bird("圆圆"); 
        bird.fly(); 
    } 
} 

extends 英文原意指 “扩展”. 而我们所写的类的继承, 也可以理解成基于父类进行代码上的 “扩展”.

例如我们写的 Bird 类, 就是在 Animal 的基础上扩展出了fly方法.

(3)注意

如果我们把 name 改成 private, 那么此时子类就不能访问了

(4)字段和方法访问权限

private: 类内部能访问, 类外部不能访问

默认(也叫包访问权限): 类内部能访问, 同一个包中的类可以访问, 其他类不能访问.

protected: 类内部能访问, 子类和同一个包中的类可以访问, 其他类不能访问.

public : 类内部和类的调用者都能访问

二、多态

首先需要实现多态需要满足一些条件:

1) 继承关系上满足向上转型

2)子类和父类 有同名的覆盖/重写方法

3)通过父类对象的引用去调用这个重写的方法

完成以上三步就会发生动态绑定,动态绑定是多态的基础.

一、向上转型

(1)在刚才的例子中, 我们写了形如下面的代码

Bird bird = new Bird("圆圆"); 

这个代码也可以写成这个样子

Bird bird = new Bird("圆圆"); 
Animal bird2 = bird; 
// 或者写成下面的方式 
Animal bird2 = new Bird("圆圆"); 

此时 bird2 是一个父类 (Animal) 的引用, 指向一个子类 (Bird) 的实例. 这种写法称为 向上转型.

(2)向上转型方法有三种

1)直接赋值

Animal animal = new Dog("圆圆",7);

2)方法的参数,传参是向上转型

public static void funcl(Animal animal){
}
public static void main(String[] args){
  Dog dog = new Dog("圆圆",6);
  func1(dog);
}

3)返回值向上转型

public static Animal func2(){
  Dog dog = new Dog("圆圆",19);
  return dog;
}

二、重写

针对刚才的 eat 方法来说:

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

(1)关于重写的注意事项

1)最基本的返回值 参数列表 方法名必须是一样的

2)被重写的方法的访问修饰限定符在子类中要大于等于父类的

3)被private修饰的方法不可以被重写的

4)被static修饰的方法是不可以被重写的

5)被final修饰的方法是不可以被重写的

三、动态绑定

子类和父类中出现同名方法的时候, 再去调用会出现什么情况呢?

对前面的代码稍加修改, 给 Bird 类也加上同名的 eat 方法, 并且在两个 eat 中分别加上不同的日志.

// Animal.java 
public 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 
public class Bird extends Animal { 
    public Bird(String name) { 
        super(name); 
    } 
    public void eat(String food) { 
        System.out.println("我是一只小鸟"); 
        System.out.println(this.name + "正在吃" + food); 
    } 
} 
// Test.java 
public class Test { 
    public static void main(String[] args) { 
        Animal animal1 = new Animal("圆圆"); 
        animal1.eat("谷子"); 
        Animal animal2 = new Bird("扁扁"); 
        animal2.eat("谷子"); 
    } 
} 
// 执行结果 
我是一只小动物 
圆圆正在吃谷子 
我是一只小鸟 
扁扁正在吃谷子 

此时, 我们发现:

animal1 和 animal2 虽然都是

Animal 类型的引用, 但是 animal1 指向

Bird 类型的实例.

针对 animal1 和 animal2 分别调用 eat 方法, 发现

animal2.eat() 实际调用了子类的方法

四、多态的好处

类调用者对类的使用成本进一步降低.

封装是让类的调用者不需要知道类的实现细节.

多态能让类的调用者连这个类的类型是什么都不必知道, 只需要知道这个对象具有某个方法即可

相关文章
|
2月前
|
Java 开发者
在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口
【10月更文挑战第20天】在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口。本文揭示了这两种方式的微妙差异和潜在陷阱,帮助你更好地理解和选择适合项目需求的线程创建方式。
20 3
|
2月前
|
Java
在Java多线程编程中,实现Runnable接口通常优于继承Thread类
【10月更文挑战第20天】在Java多线程编程中,实现Runnable接口通常优于继承Thread类。原因包括:1) Java只支持单继承,实现接口不受此限制;2) Runnable接口便于代码复用和线程池管理;3) 分离任务与线程,提高灵活性。因此,实现Runnable接口是更佳选择。
37 2
|
2月前
|
Java
Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口
【10月更文挑战第20天】《JAVA多线程深度解析:线程的创建之路》介绍了Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口。文章详细讲解了每种方式的实现方法、优缺点及适用场景,帮助读者更好地理解和掌握多线程编程技术,为复杂任务的高效处理奠定基础。
31 2
|
2月前
|
Java 开发者
Java多线程初学者指南:介绍通过继承Thread类与实现Runnable接口两种方式创建线程的方法及其优缺点
【10月更文挑战第20天】Java多线程初学者指南:介绍通过继承Thread类与实现Runnable接口两种方式创建线程的方法及其优缺点,重点解析为何实现Runnable接口更具灵活性、资源共享及易于管理的优势。
34 1
|
2月前
|
存储 Java 测试技术
Java零基础-多态详解
【10月更文挑战第10天】Java零基础教学篇,手把手实践教学!
24 4
|
2月前
|
Java 编译器 程序员
Java多态背后的秘密:动态绑定如何工作?
本文介绍了Java中多态的实现原理,通过动态绑定和虚拟方法表,使得父类引用可以调用子类的方法,增强了代码的灵活性和可维护性。文中通过具体示例详细解析了多态的工作机制。
45 4
|
3月前
|
Java 编译器
封装,继承,多态【Java面向对象知识回顾①】
本文回顾了Java面向对象编程的三大特性:封装、继承和多态。封装通过将数据和方法结合在类中并隐藏实现细节来保护对象状态,继承允许新类扩展现有类的功能,而多态则允许对象在不同情况下表现出不同的行为,这些特性共同提高了代码的复用性、扩展性和灵活性。
封装,继承,多态【Java面向对象知识回顾①】
|
2月前
|
Java 测试技术 编译器
Java零基础-继承详解!
【10月更文挑战第4天】Java零基础教学篇,手把手实践教学!
40 2
|
2月前
|
Java 编译器
在Java中,关于final、static关键字与方法的重写和继承【易错点】
在Java中,关于final、static关键字与方法的重写和继承【易错点】
23 5
|
2月前
|
存储 Java 测试技术
Java零基础-多态详解
【10月更文挑战第1天】Java零基础教学篇,手把手实践教学!
29 1