Java学习Day18:基础篇8

多态

基本信息:

应用场景:

可以把子类对象赋值给父类对象,实现多态从而使用同一种方法;

多态中调用成员的特点

1.调用成员变量都看左边

调用成员变量:编译看左边,运行也看左边

编译看左边:javac编译代码的时候,会看左边的父类中有没有这个变量,如果有,编译成功,如果没有编译失败报错。

运行也看左边: java运 行代码的时候,实际获取的就是左边父类中成员变量的值

2.调用成员方法一左一右

调用成员方法:编译看左边,运行看右边

编译看左边: javac编译代码的时候,会看左边的父类中有没有这个方法,如果有,编译成功,如果没有报错

运行看右边: java运 行代码的时候,实际上运行的是子类中的方法。

3.成员变量和成员方法

成员变量:在子类的对象中,会把父类的成员变量也继承下的,所以使用的是弗雷德变量。

成员方法:如果子类对方法进行了重写,那么在虚方法表中是会把父类的方法进行覆盖的,所以调用成员方法输出就是成员重写的方法。

多态的优势

多态的弊端

不能调用子类的特有共能;

解决方案:强转回子类即可;

判断+转换+调用

小总结:

继承多态小案例:

复制代码
package 多态的实例;
public abstract class Animal {
    private String colour;
    private int age;
    public Animal() {
    }
    public Animal(String colour, int age) {
        this.colour = colour;
        this.age = age;
    }
    public void eat(String something){
        System.out.println("吃"+something);
    }
    /**
     * 获取
     * @return name
     */
    public String getColour() {
        return colour;
    }
    /**
     * 设置
     */
    public void setColour(String colour) {
        this.colour = colour;
    }
    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }
    /**
     * 设置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }
    public String toString() {
        return "Animal{colour = " + colour+ ", age = " + age + "}";
    }
    public abstract void eat();
}
复制代码
package 多态的实例;
public class Dog extends Animal{
    @Override
    public void eat(){
        System.out.println(getAge()+"岁"+getColour()+"颜色"+"的狗两只前腿死死的抱住骨头猛吃");
    }
    public void lookHome(){
        System.out.println("lookHome");
    }
}
复制代码
package 多态的实例;
public class Cat extends Animal{
    @Override
    public void eat() {
    }
    public void catchMouse(){
        System.out.println("catchMouse");
    }
    public Cat() {
    }
    public Cat(String colour, int age) {
        super(colour, age);
    }
}
复制代码
package 多态的实例;
public class Polymorphic_demo {
    public static void main(String[] args) {
        Person p =new Person();
        p.setName("老王");
        p.setAgeMan(33);
        Dog d =new Dog();
        d.setAge(3);
        d.setColour("黑色");
        Cat c =new Cat();
        c.setAge(2);
        c.setColour("黑白");
        p.grrenxinxi(d,"骨头");
        p.grrenxinxi(c,"粑粑");
    }
}

抽象类

抽象类(Abstract Class)是一种在编程语言中用于表示不能被实例化的类的概念。它的主要目的是提供一个或多个抽象方法(即没有实现体的方法,只有方法声明),这些方法必须由继承自抽象类的子类来实现。抽象类还可以包含已实现的方法(即具有方法体的方法)以及字段、属性、构造函数等。

抽象类的主要特点包括:

  1. 不能被实例化 :抽象类不能直接创建对象,即不能使用new关键字来实例化抽象类。它的主要目的是被其他类继承,并由这些子类提供具体的实现。

  2. 包含抽象方法 :抽象类可以包含一个或多个抽象方法。抽象方法是没有方法体的方法,只有方法的声明(即方法签名),并且必须以abstract关键字进行修饰。

  3. 允许包含非抽象方法和字段:除了抽象方法外,抽象类还可以包含已经实现的方法(即非抽象方法)以及字段、属性等。

  4. 子类必须实现父类的所有抽象方法:如果子类继承了一个抽象类,并且这个子类不是抽象类(即这个子类需要被实例化),那么子类必须实现父类中所有的抽象方法。否则,子类也必须被声明为抽象类。

  5. 作为类型引用:虽然不能直接实例化抽象类,但可以使用抽象类作为类型引用,指向其子类的实例。

抽象类在软件设计中非常有用,尤其是在实现多态和框架设计时。通过使用抽象类,可以定义一套接口规范,让不同的子类按照这套规范来实现具体功能,同时保证了代码的灵活性和可扩展性。

示例(以Java为例):

|---|-----------------------------------------------|
| | abstract class Animal { |
| | // 抽象方法 |
| | abstract void eat(); |
| | |
| | // 已实现的方法 |
| | void sleep() { |
| | System.out.println("This animal sleeps."); |
| | } |
| | } |
| | |
| | class Dog extends Animal { |
| | // 必须实现父类的抽象方法 |
| | @Override |
| | void eat() { |
| | System.out.println("Dog eats meat."); |
| | } |
| | } |
| | |
| | public class Test { |
| | public static void main(String[] args) { |
| | Animal myDog = new Dog(); // 使用抽象类作为类型引用 |
| | myDog.eat(); // 调用Dog类实现的eat方法 |
| | myDog.sleep(); // 调用Animal类中已实现的sleep方法 |
| | } |
| | } |

抽象类的构造方法、成员方法和成员变量的几问

在Java中,抽象类(Abstract Class)与它的构造方法(Constructor)、成员变量(Member Variables)、以及成员方法(Member Methods,包括抽象方法和非抽象方法)之间有着特定的关系。下面分别解释这些关系:

构造方法(Constructor)

  • 构造方法的存在抽象类可以有构造方法 。这些构造方法主要用于在创建子类对象时初始化父类(即抽象类)的成员变量
  • 构造方法的调用 :当子类被实例化时 ,会首先调用父类(如果父类是抽象类)的构造方法(除非子类也是抽象类且仅被用作其他类的基类)。这是Java中对象初始化的一个基本步骤,确保父类被正确初始化。
  • 限制构造方法本身不能被声明为抽象abstract)。

成员变量(Member Variables)

  • 定义:抽象类可以定义成员变量,这些成员变量可以是任何类型(基本数据类型、对象引用等)。
  • 访问成员变量可以在抽象类的构造方法、成员方法(包括抽象方法和非抽象方法)中被访问和修改。同时,它们也可以被子类继承,并在子类中通过继承的super关键字或直接访问(如果它们不是私有的)来访问和修改

成员方法(Member Methods)

  • 抽象方法 :抽象类中可以定义抽象方法,这些方法只有声明没有实现体 (即方法体为空,并且使用abstract关键字修饰)。子类必须实现这些抽象方法,除非子类也是抽象类
  • 非抽象方法:抽象类中也可以定义非抽象方法,这些方法有具体的实现体,可以直接被调用。这些非抽象方法可以被继承到子类,并在子类中被直接使用或重写。
  • 访问:成员方法(无论是抽象的还是非抽象的)都可以在抽象类的构造方法、其他成员方法中被调用(抽象方法除外,因为抽象方法没有实现体)。同时,这些方法也可以被子类继承,并在子类中通过继承或重写来使用。

小总结

  • 抽象类可以有构造方法,但构造方法本身不能是抽象的。
  • 抽象类可以定义成员变量,这些变量可以在构造方法、成员方法中被访问和修改。
  • 抽象类可以定义抽象方法和非抽象方法。抽象方法必须由子类实现(除非子类也是抽象类),而非抽象方法可以直接在子类中使用或重写。
  • 成员变量和成员方法(非抽象的)都可以被子类继承,并根据需要在子类中进行访问、修改或重写。

接口

在Java中,接口(Interface)是一种引用类型,是一种抽象的类型,用于指定一组方法规范,但不提供这些方法的具体实现 。接口是一种形式上的契约它要求实现了该接口的类(称为接口的实现类或实现接口的类)必须遵循接口中定义的规范

接口的主要特点:

  1. 抽象性接口中的所有方法默认都是抽象的(在Java 8之前),即它们只有声明没有实现体。从Java 8开始,接口中可以包含带有实现体的默认方法和静态方法

  2. 继承性 :接口可以继承另一个或多个接口,使用extends关键字。接口之间的继承是多继承的,即一个接口可以继承多个其他接口

  3. 多态性:接口是支持多态性的重要手段。通过接口,我们可以指向实现了该接口的任何对象的引用。

  4. 实现接口的类必须实现接口中的所有方法 (在Java 8之前),除非该类是抽象类 。从Java 8开始,如果一个类实现了接口,但它不想实现接口中的某个默认方法,它可以选择不覆盖该方法,而是直接使用接口中提供的默认实现

  5. 接口中不能包含实例变量 ,但可以包含常量(即使用public static final修饰的变量,但通常省略这些修饰符)。

  6. 接口中的方法默认是public ,且不允许使用其他访问修饰符(如privateprotected或包级私有)。

  7. 接口可以包含嵌套接口、枚举和静态类(在Java 9之前,接口中只能包含静态内部类;从Java 9开始,接口中可以包含非静态内部类,但内部类不能是抽象的)。

接口的使用场景包括定义一组方法的规范,使不相关的类可以实现这个接口,从而实现多态性。例如,ListSetMap等Java集合框架中的接口定义了集合操作的标准方法,不同的类如ArrayListLinkedListHashMap等实现了这些接口,提供了不同的实现方式。

接口在Java编程中非常重要,它促进了代码的模块化、可重用性和可扩展性。通过定义接口,我们可以让不同的类按照相同的约定进行工作,而不需要知道这些类的具体实现细节。

Java如何表示接口:

public interface Animal {

// 这是一个抽象方法,默认是public abstract的

void eat();

// 接口中的常量,默认是public static final的

int AGE = 10;

// Java 8 引入的默认方法

default void sleep() {

System.out.println("This animal sleeps.");

}

// Java 8 引入的静态方法

static void info() {

System.out.println("This is an interface for animals.");

}

}

public class Dog implements Animal {

// 必须实现接口中的抽象方法

@Override

public void eat() {

System.out.println("Dog is eating.");

}

// 可以选择性地覆盖接口中的默认方法

@Override

public void sleep() {

System.out.println("Dog is sleeping in a doghouse.");

}

// 静态方法和常量可以直接通过接口名访问,无需实现

public static void main(String[] args) {

Animal.info(); // 直接访问接口的静态方法

System.out.println(Animal.AGE); // 直接访问接口的常量

Dog dog = new Dog();

dog.eat();

dog.sleep(); // 调用的是Dog类中覆盖的sleep方法

}

}

接口实例化(虽然不能直接实例化抽象类,但可以使用抽象类作为类型引用,指向其子类的实例。)

// 定义接口

public interface Animal {

void eat();

void sleep();

}

// 实现接口

public class Dog implements Animal {

@Override

public void eat() {

System.out.println("Dog is eating.");

}

@Override

public void sleep() {

System.out.println("Dog is sleeping.");

}

}

// 在另一个类中实例化Dog类

public class Main {

public static void main(String[] args) {

// 实例化Dog类,但可以将实例引用视为Animal类型

Animal myDog = new Dog();

// 调用接口中定义的方法,但实际上是调用Dog类中的实现

myDog.eat();

myDog.sleep();

// 注意:你不能直接实例化接口,下面的代码会编译错误

// Animal anotherAnimal = new Animal(); // 错误:接口不能被实例化

}

}

在这个例子中,Dog类实现了Animal接口,所以我们可以通过new Dog()来创建一个Dog对象。但是,由于Dog实现了Animal接口,我们可以将这个Dog对象的引用视为Animal类型(即Animal myDog = new Dog();)。这允许我们在不直接依赖于具体类(Dog)的情况下编写更灵活和可重用的代码。我们可以将myDog引用传递给期望Animal类型参数的任何方法或函数,只要这些方法或函数不依赖于Animal接口中未由Dog类实现的任何额外方法。

接口的成员方法和成员变量

成员方法

接口中的成员方法默认是public abstract,这意味着它们必须被实现接口的类所实现(除非该类也是抽象的)。然而,从Java 8开始,接口还可以包含默认方法(使用default关键字)和静态方法(使用static关键字)。

  • 抽象方法:没有方法体的方法,必须由实现接口的类提供具体实现。
  • 默认方法 :提供了一种方式,允许接口在不破坏向后兼容性的情况下添加新的方法实现。
  • 静态方法 :可以直接通过接口名来调用,不需要实现接口的类实例。

成员变量

接口中的成员变量实际上是常量,因为它们在接口中默认是public static final的。这意味着你可以直接通过接口名来访问这些常量,而不需要创建接口的实例。

使用示例

|---|-----------------------------------------------------------------------------|
| | // 定义一个接口 |
| | public interface Animal { |
| | // 成员变量(实际上是常量) |
| | int MAX_AGE = 100; |
| | |
| | // 抽象方法 |
| | void eat(); |
| | |
| | // 默认方法 |
| | default void sleep() { |
| | System.out.println("This animal sleeps."); |
| | } |
| | |
| | // 静态方法 |
| | static void info() { |
| | System.out.println("This is an interface for animals."); |
| | } |
| | } |
| | |
| | // 实现接口的类 |
| | public class Dog implements Animal { |
| | @Override |
| | public void eat() { |
| | System.out.println("Dog is eating."); |
| | } |
| | |
| | // 可以选择性地覆盖默认方法 |
| | @Override |
| | public void sleep() { |
| | System.out.println("Dog is sleeping in a doghouse."); |
| | } |
| | |
| | // 使用接口中的常量 |
| | public void printMaxAge() { |
| | System.out.println("The maximum age of an animal is " + Animal.MAX_AGE); |
| | } |
| | } |
| | |
| | // 在另一个类中测试 |
| | public class Main { |
| | public static void main(String[] args) { |
| | // 访问静态方法 |
| | Animal.info(); |
| | |
| | // 实例化Dog类 |
| | Dog myDog = new Dog(); |
| | |
| | // 调用抽象方法的实现 |
| | myDog.eat(); |
| | |
| | // 调用默认方法(可能被覆盖) |
| | myDog.sleep(); |
| | |
| | // 调用打印常量的方法 |
| | myDog.printMaxAge(); |
| | } |
| | } |

在这个例子中,Animal接口定义了一个常量MAX_AGE、一个抽象方法eat()、一个默认方法sleep()和一个静态方法info()Dog类实现了Animal接口,并提供了eat()方法的具体实现,同时覆盖了sleep()默认方法。在Main类中,我们展示了如何通过接口名直接访问静态方法和常量,以及如何通过实现接口的类实例来调用抽象方法和默认方法。

新增接口三兄弟

相关推荐
一颗松鼠2 分钟前
JavaScript 闭包是什么?简单到看完就理解!
开发语言·前端·javascript·ecmascript
有梦想的咸鱼_4 分钟前
go实现并发安全hashtable 拉链法
开发语言·golang·哈希算法
海阔天空_20139 分钟前
Python pyautogui库:自动化操作的强大工具
运维·开发语言·python·青少年编程·自动化
天下皆白_唯我独黑17 分钟前
php 使用qrcode制作二维码图片
开发语言·php
小灰灰__17 分钟前
IDEA加载通义灵码插件及使用指南
java·ide·intellij-idea
夜雨翦春韭20 分钟前
Java中的动态代理
java·开发语言·aop·动态代理
小远yyds22 分钟前
前端Web用户 token 持久化
开发语言·前端·javascript·vue.js
hong16168830 分钟前
跨模态对齐与跨领域学习
学习
何曾参静谧34 分钟前
「C/C++」C/C++ 之 变量作用域详解
c语言·开发语言·c++
程序媛小果41 分钟前
基于java+SpringBoot+Vue的宠物咖啡馆平台设计与实现
java·vue.js·spring boot