接口

接口

◆普通类:只有具体实现

◆抽象类:具体实现和规范(抽象方法)都有!

◆接口:只有规范! 自己无法写方法~ 专业的约束 约束和实现分离:面向接口编程

  • 接口就是规范,定义的是一组规则,体现了现实世界中“如果你是...则必须能..”的思想。如果你是天使则必须能飞。如果你是汽车,则必须能跑。如果你好人,则必须干掉坏人;如果你是坏人,则必须欺负好人。

  • 接口的本质是契约,就像我们人间的法律一样。制定好后大家都遵守。

  • oop的精髓,是对对象的抽象,最能体现这一点的就是接口。为什么我们讨论设计 模式都只针对具备了抽象能力的语言(比如c++、java、c#等),就是因为设计模式所研究的,实际上就是如何合理的去抽象。

接口(Interface)是面向对象编程中的一种结构,它定义了一组方法的签名(方法名、参数列表、返回类型),但没有方法体,意味着它仅仅描述了类应该

做什么,而没有描述如何做。接口的主要作用是提供一种统一的规范,强制实现接口的类必须实现接口中定义的所有方法。

接口用于为不同的类提供一个共同的行为约定,即让多个类实现相同的方法,而不关注这些类如何实现这些方法。

image-20250113223228171

接口中的方法

  • 默认修饰符:public abstract
    • 接口中的普通方法(非静态或默认方法)是抽象方法,必须由实现类实现。
    • 显式声明 publicabstract 是多余的,可以直接写方法签名。
    • 从 Java 8 开始,接口可以有默认方法和静态方法。
interface Example {
    void doSomething(); // 等价于 public abstract void doSomething();

    default void defaultMethod() {
        System.out.println("这是一个默认方法!");
    }

    static void staticMethod() {
        System.out.println("这是一个静态方法!");
    }
}

2. 接口中的变量

  • 默认修饰符:public static final
    • 接口中的变量是常量,默认是 public static final,必须初始化。
    • 变量名通常使用大写字母加下划线表示。
interface Example {
    int CONSTANT = 100; // 等价于 public static final int CONSTANT = 100;
}

3. 接口中的构造方法

  • 接口中不能有构造方法

    • 因为接口不能被实例化,所以无法定义构造方法。

4. 接口中的嵌套类型

  • 嵌套类默认是 public static

    • 接口内部可以定义嵌套类、接口或枚举,它们默认是 static 的。
interface Example {
    class NestedClass {
        void print() {
            System.out.println("接口中的嵌套类!");
        }
    }
}

扩展:默认方法与静态方法(Java 8+)

1. 默认方法 (default)

  • 默认方法允许接口提供方法的默认实现。
  • 实现类可以选择性地重写默认方法。
interface Example {
    default void sayHello() {
        System.out.println("这是接口的默认方法!");
    }
}

class Implementation implements Example {}

public class Test {
    public static void main(String[] args) {
        Example e = new Implementation();
        e.sayHello(); // 输出:这是接口的默认方法!
    }
}

2. 静态方法 (static)

  • 静态方法属于接口本身,不能通过实现类调用,必须使用接口名调用。
interface Example {
    static void greet() {
        System.out.println("这是接口的静态方法!");
    }
}

public class Test {
    public static void main(String[] args) {
        Example.greet(); // 输出:这是接口的静态方法!
    }
}

小结:

  • 方法默认是 public abstract:必须实现,或者在 Java 8+ 中使用默认方法提供实现。
  • 变量默认是 public static final:常量,必须初始化。
  • 嵌套类型默认是 public static:适用于接口内部的类、接口和枚举。
  • 没有构造方法:接口不能被实例化,但可以通过实现类或匿名类使用。

通过默认方法和静态方法,接口在 Java 8 之后不仅仅是定义行为的规范,也具备了一定的功能性和扩展能力。

接口具体实现

接口的定义 关键字 interface

package com.oop.demo10;
//测试 interface 接口
/*
* 1.不能包含方法实现(Java 8+ 可包含默认方法和静态方法):方法默认为 public abstract。
* 2.不能定义普通成员变量:只能定义常量(public static final)。
* 3.没有构造方法:因为接口不能实例化。
* 4.支持多继承:一个类可以实现多个接口,一个接口也可以继承多个接口。
* 5.访问修饰符:所有成员默认是 public。
* */
public interface UserService {
    int num=9;   //在接口中,int num = 9; 不会报错,这是因为接口中的所有变量默认被
    // 隐式地 修饰为 public static final。换句话说,int num = 9; 实际上等价于: //public static final int num = 9;
    default void sayHello() {      //java8之后 接口内可以定义默认方法   可以被实现类继承
        System.out.println("hello");
    }
    static void sayHi() {              //java8之后 接口可以定义静态方法  使用是 接口名.静态方法
        System.out.println("hi");
    }
    //public void run(){} //同抽象类一样,接口中的方法没有方法体,写了就报错
    public abstract void run();//接口中定义的方法约束 默认就是 public abstract
    void eat();  //相当于public abstract void eat();
    //public UserService(){} //接口中没有构造方法 接口不能实例化
}

接口中方法的实现 实现类 关键字 implements

package com.oop.demo10;
//这个类  对接口UserService 中抽象方法得具体的实现 重写
//实现接口的类  必须实现接口中的方法!
public class UserServiceImpl implements UserService {
    /*
    * @Override //接口中得默认方法也可以被重写!
    public void sayHello() {
        UserService.super.sayHello();
    }*/

    @Override
    public void run() {
        System.out.println("RUN");
    }

    @Override
    public void eat() {
        System.out.println("EAT");
    }
}

实现类

package com.oop.demo10;
//Service类 将UserService接口中的方法实现后,做一个是实现类多态重建对象 然后调用方法
public class Application {
    public static void main(String[] args) {
        UserService s1 = new UserServiceImpl(); //接口定义指向实现类实例
        s1.sayHello();//默认方法的调用
        UserService.sayHi();//接口静态方法的调用
        System.out.println(s1.num);
        //s1.num=10;   //public static final   常量 不可改变值
        System.out.println(s1.num);
        s1.run();//方法的重写实现接口内 函数的具体实现
        s1.eat();
        UserService s2 = new UserServiceImpl(); //接口定义指向实现类实例
        System.out.println(s2.num);
    }
}

输出结果:

hello
hi
9
9
RUN
EAT
9

1. 接口中的变量

定义:

int num = 9; 
  • 解释
    • 接口中的变量默认是 public static final,即常量。
    • 这意味着 num 是一个类级别的常量,可以通过 UserService.num 访问。
    • 由于是 final,所以无法修改 num 的值,s1.num = 10; 会导致编译错误。

2. 接口中的默认方法

定义:

default void sayHello() {
    System.out.println("hello");
}
    • 默认方法在接口中有方法体,提供了一个默认实现,避免每个实现类都必须实现该方法。
    • 默认方法可以直接被实现类继承,也可以选择重写。
    • 调用时,可以通过接口的引用调用默认方法,例如:s1.sayHello();

3. 接口中的静态方法

定义:

static void sayHi() {
    System.out.println("hi");
}
  • 解释
    • 静态方法属于接口本身,不能被实现类继承。
    • 调用时必须通过接口名调用,例如:UserService.sayHi();
    • 这保证了静态方法不会被子类重写,从而保持接口定义的一致性。

4. 接口中的抽象方法

定义:

public abstract void run();
void eat(); // 等价于 public abstract void eat();
  • 解释
    • 接口中的抽象方法是默认 public abstract 的,可以省略修饰符。
    • 实现类必须提供这些抽象方法的具体实现,否则会报错。

5. 接口没有构造方法

定义:

// public UserService() {} // 不能定义构造方法
  • 解释
    • 接口是用来定义行为规范的,不是用来创建对象的,因此没有构造方法。
    • 接口不能直接实例化,只能通过实现类来创建对象。

6. 实现类中的方法实现

Service 类中,实现了接口中的抽象方法:

@Override
public void run() {
    System.out.println("RUN");
}

@Override
public void eat() {
    System.out.println("EAT");
}
  • 解释
    • Service 类是 UserService 接口的实现类,必须实现接口中的所有抽象方法。
    • 默认方法 sayHello() 也可以选择重写,如果不重写,调用时会使用接口中的默认实现。

7. 测试类的多态性

Application 类中,使用接口的多态性测试了接口和实现类的功能:

UserService s1 = new Service(); // 接口引用指向实现类对象
s1.sayHello();  // 调用接口的默认方法
UserService.sayHi(); // 调用接口的静态方法
System.out.println(s1.num); // 输出常量值
s1.run();  // 调用实现类的重写方法
s1.eat();  // 调用实现类的重写方法
  • 多态性
    • UserService 是接口,s1 是它的引用,实际指向的是 Service 类的对象。
    • 通过多态性,接口的引用可以调用实现类中的具体实现方法(run()eat())。
    • 默认方法和静态方法可以通过接口直接使用,无需依赖实现类。

总结:接口的特点与应用

特性 解释
变量 接口中的变量默认是 public static final,必须初始化,且值不可修改。
抽象方法 默认是 public abstract,实现类必须实现这些方法。
默认方法 使用 default 定义,有方法体,可以直接被实现类继承或选择性重写。
静态方法 使用 static 定义,属于接口本身,不能被实现类继承,必须通过接口名调用。
多态性 接口可以作为引用类型,指向实现类的实例,调用实现类的方法。
没有构造方法 接口不能被实例化,只能通过实现类创建对象。
多继承支持 一个类可以实现多个接口,从而同时继承多个接口的行为规范(类似多继承)。

通过接口的设计,可以规范实现类的行为,同时增加代码的灵活性和扩展性。Java 8 之后的默认方法和静态方法进一步增强了接口的功能,使其更为强大和实用。

dog d1 = new dog(); //通过是实现类的实例化 调用各个接口的 在是实现类中重写的方法 缺点是调用不到 接口中的默认和静态方法 这句注释理解的对吗

ChatGPT

您的注释中对 实现类实例化和接口方法调用 的描述是 部分正确的,但需要稍微调整和补充,特别是关于接口中的 默认方法静态方法 的调用。以下是逐项解析:

1. 关于实现类实例化

Dog d1 = new Dog();
  • 解释
    • Dog 是实现类,通过直接实例化 Dog可以调用 Dog 类中的所有方法,包括从接口继承的方法。
    • 通过实现类的实例化,调用的接口方法实际上是 Dog 类中 实现或重写的版本

2. 关于默认方法的调用

  • 您的描述:“调用不到接口中的默认方法。”
  • 实际情况:不完全正确。

默认方法的继承规则:

  • 默认方法可以被实现类继承,并且可以通过实现类的实例调用。

  • 如果实现类没有重写默认方法,那么默认方法的实现来自接口。

  • 如果实现类重写了默认方法,那么调用的是实现类中的方法。

示例:

interface UserService {
    default void sayHello() {
        System.out.println("接口的默认方法:Hello!");
    }
}

class Dog implements UserService {
    // 继承了接口的默认方法
}

public class Test {
    public static void main(String[] args) {
        Dog d1 = new Dog();
        d1.sayHello(); // 输出:接口的默认方法:Hello!
    }
}

调整后的理解:

  • 默认方法是可以通过实现类的实例调用的。
  • 如果您通过 Dog d1 = new Dog(); 实例化 Dog,默认方法可以被直接调用,除非它在实现类中被重写。

3. 关于静态方法的调用

  • 您的描述:“调用不到接口中的静态方法。”
  • 实际情况:正确。

静态方法的访问规则:

  • 静态方法属于接口本身,不能通过实现类或实现类的实例调用。
  • 必须通过接口名来调用静态方法。

示例:

interface UserService {
    static void sayHi() {
        System.out.println("接口的静态方法:Hi!");
    }
}

class Dog implements UserService {
    // 静态方法不能被继承
}

public class Test {
    public static void main(String[] args) {
        // 不能通过实例调用
        // Dog d1 = new Dog();
        // d1.sayHi(); // 错误

        // 必须通过接口名调用
        UserService.sayHi(); // 输出:接口的静态方法:Hi!
    }
}

调整后的理解:

  • 静态方法不能通过实现类实例调用。
  • 只能通过接口名调用,例如:UserService.sayHi();

总结:接口的默认方法和静态方法的调用规则

方法类型 是否可以通过实现类实例调用? 是否可以通过接口名调用? 说明
默认方法 可以(如果未被重写) 默认方法可以被实现类继承,通过实例调用。
静态方法 可以 静态方法属于接口本身,不能被实现类或其实例调用。

通过实现类实例化时,您可以调用实现类中的方法(包括继承自接口的方法),但静态方法始终只能通过接口名调用。

接口多实现、接口多继承及区别

接口多实现

package com.oop.demo11;
//本例实现 类对多接口的实现

public class Application {
    public static void main(String[] args) {
        dog d1 = new dog(); //通过是实现类的实例化 调用各个接口的 在是实现类中重写的方法
        // 事实上 这种方案也可以调用接口中的默认方法   静态方法(如果有那就)
        /*默认方法可以被实现类继承,并且可以通过实现类的实例调用。
        如果实现类没有重写默认方法,那么默认方法的实现来自接口。
        如果实现类重写了默认方法,那么调用的是实现类中的方法。*/
        d1.eat();
        d1.run();
        d1.sound();
    }
}
interface Animal{
    void sound();
    void eat();
}
interface Runnale{
    void run();
}
class dog implements Animal,Runnable{     //实现类对接口的多实现
    @Override
    public void sound() {
        System.out.println("Bark Bark!");
    }

    @Override
    public void eat() {
        System.out.println("Dog is eating!");
    }

    @Override
    public void run() {
        System.out.println("Dog is running!");
    }
}

接口多继承

package com.oop.demo11;
//本例实现 多接口继承
public class Application02 {
    public static void main(String[] args) {
        landAnimal d1 = new Dog();
        d1.eat();
        d1.run();
        d1.sound();
    }
}
interface Animal{
    void eat();
    void run();
}
interface Runnable{
    void run();
}
interface landAnimal extends Animal, Runnable{   //接口陆地动物实现对Animal 、Runnable两个接口的继承   接口多继承
    void sound();//继承接口自己的方法
}
class Dog implements landAnimal{   //实现类对 继承接口的实现  可以重写所有继承接口的方法   然后利用多态(父类接口的引用指向子类实现类的实例)实现方法调用
    @Override
    public void sound() {

    }
    @Override
    public void eat() {

    }
    @Override
    public void run() {

    }
}

区别对比

在您的代码中,以下两种方式有本质的区别,主要体现在 多态性调用方法的范围 方面。


1 使用实现类作为引用类型:Dog d1 = new Dog();

Dog d1 = new Dog();

特点:

  • 实现类的引用指向自己的实例

    • d1 的编译时类型和运行时类型都是 Dog

    • 可以调用 Dog 类中所有定义的方法,包括接口中定义的方法和 Dog 类独有的方法。

优点:

  1. 访问范围更广

    • 可以调用 Dog 类中所有的方法(包括接口中的方法和 Dog 类独有的方法),功能更全面。
  2. 简单直观

    • 如果只需要使用特定实现类的功能,不涉及接口的多态性,直接使用实现类引用更简单。

缺点:

  1. 缺乏多态性

    • Dog 类型的引用只能指向 Dog 类型的对象,无法动态替换为其他实现类的对象。

    • 如果未来需要用其他实现类替换 Dog,可能需要修改大量代码。

  2. 耦合度高

    • 调用时直接依赖具体实现类,不利于代码扩展和维护。

2. 使用接口作为引用类型:landAnimal d1 = new Dog();**

landAnimal d1 = new Dog();

特点:

  • 父接口的引用指向子类实现类的实例

    • d1 的编译时类型是 landAnimal,而运行时类型是 Dog
    • 可以调用 landAnimal 接口中定义的方法,即:eat()run()sound()
    • 无法直接调用 Dog 类中未在接口中定义的其他方法(如果有)。

优点:

  1. 多态性

    • 父接口引用可以指向不同的实现类对象,从而通过同一类型的引用调用不同实现类的行为。

    • 例如:landAnimal d1 = new Dog();,以后可以替换为 landAnimal d1 = new Cat();(假如 Cat 也实现了 landAnimal 接口),代码的扩展性更强。

  2. 接口约束

    • 强制实现类必须实现接口中所有抽象方法,确保代码满足特定的规范。

  3. 解耦

    • 调用时依赖接口而非具体实现类,代码更灵活,方便未来替换实现类。

缺点:

  • 方法调用受限

    • 只能调用接口中定义的方法(即 landAnimalAnimalRunnable 中的方法)。
    • 无法调用 Dog 类中额外定义的方法(如果 Dog 有独特的方法,无法通过 d1 直接调用)。

对比总结

对比项 landAnimal d1 = new Dog(); Dog d1 = new Dog();
引用类型 父接口类型 实现类类型
多态性 支持多态,可以替换为其他实现类 不支持多态,只能引用 Dog 类型对象
调用范围 只能调用接口中定义的方法 可以调用接口中定义的方法以及 Dog 类中的独特方法
扩展性 扩展性更强,更适合接口驱动的编程方式 扩展性较差,耦合具体实现类
适用场景 当需要解耦、动态切换不同实现类时适用 当明确只需要特定实现类的功能时适用
灵活性 通过接口实现代码解耦,可替换实现类,便于扩展 强依赖具体实现类,灵活性不足

如何选择?

选择接口引用类型(landAnimal d1 = new Dog();):

  • 需要利用 多态性,让代码可以动态替换不同实现类。
  • 编写面向接口的代码,减少对具体实现类的依赖,提高扩展性。
  • 场景:有多个实现类(如 DogCatHorse),且需要动态替换或统一管理它们时。

选择实现类引用类型(Dog d1 = new Dog();):

  • 只需使用特定实现类的功能。
  • 实现类有特定的方法,且这些方法不会在接口中定义。
  • 场景:明确使用 Dog 类的特性,不会更换其他实现类时。

总结

  • 如果您的程序需要动态替换实现类,或者需要定义松耦合、灵活扩展的代码,优先使用接口作为引用类型
  • 如果您的程序只依赖于特定实现类的功能,且不会涉及接口的多态性,使用实现类作为引用类型更简单直接

接口和抽象类的区别比较和总结

抽象类和接口的总结

抽象类

抽象类是面向对象编程中用来为其他类提供通用模板的结构。它既可以包含已实现的方法,也可以包含未实现的抽象方法。

特点
  1. 不能直接实例化:必须通过子类继承后实例化。

  2. 可以包含构造方法:用于初始化子类会继承的属性。

  3. 可以包含已实现的方法和抽象方法:既可以定义行为,也可以强制子类实现特定行为。

  4. 可以定义属性

    • 实例变量:普通成员变量。
    • 静态变量:类级别的共享变量。
  5. 支持继承:只能单继承,即一个子类只能继承一个抽象类。

  6. 访问修饰符:可以定义 publicprotectedprivate 的方法和变量。

适用场景
  • 当多个类之间有共同行为,但这些行为需要在父类中实现或部分实现时使用。
  • 提供代码复用的能力。
  • 希望类具备部分默认行为的场景。

接口

接口是一个更加抽象的结构,用于定义行为的规范(方法的签名),而不提供具体的实现。类可以实现多个接口,从而实现多继承。

特点
  1. 不能包含方法实现(Java 8+ 可包含默认方法和静态方法):方法默认为 public abstract
  2. 不能定义普通成员变量:只能定义常量(public static final)。
  3. 没有构造方法:因为接口不能实例化。
  4. 支持多继承:一个类可以实现多个接口,一个接口也可以继承多个接口。
  5. 访问修饰符:所有成员默认是 public
适用场景
  • 定义行为规范(类似于协议或约定),而不提供实现。
  • 支持类的多继承。
  • 需要解耦合和灵活扩展时使用。

抽象类与接口的比较表

特性 抽象类 接口
实例化 不能直接实例化,需通过子类实现 不能直接实例化,需通过实现类实现
构造方法 可以包含,用于初始化成员 不支持构造方法
方法 既可以包含抽象方法,也可以包含普通方法 方法默认为 public abstract,Java 8+ 支持默认和静态方法
变量 可以包含普通变量、静态变量 只能包含 public static final 常量
继承 只能单继承,一个子类只能继承一个抽象类 支持多继承,一个类可以实现多个接口
访问修饰符 方法和属性可以是 publicprotectedprivate 方法和属性默认为 public
适用场景 共享代码、部分行为实现 定义行为规范,支持多继承

何时使用抽象类与接口?

  • 使用 抽象类
    1. 当类之间存在 "is-a" 的关系时。
    2. 希望在父类中提供部分实现,并强制子类实现某些特定行为。
    3. 需要定义共享的属性和方法时。
  • 使用 接口
    1. 当类之间没有 "is-a" 的关系,而是 "can-do" 的关系。
    2. 希望通过接口定义行为规范,且行为的实现可以灵活分配给不同的类。
    3. 需要实现多继承时。

通过合理使用抽象类和接口,可以提高代码的复用性和扩展性,增强代码的可维护性和灵活性。

复习继承多态对静态、非静态方法的调用

Person p1 =new Student(); 如果在Person Student类中定义了静态方法run() 则 p1.run()是调用哪个

在 Java 中,静态方法是属于类的,而不是属于实例的,所以静态方法的调用不遵循多态性规则。静态方法是根据引用的编译时类型(也就是声明的类型)来决定调用哪个类的静态方法,而不是根据运行时的实际对象类型。

class Person {
    public static void run() {
        System.out.println("Person is running.");
    }
}

class Student extends Person {
    public static void run() {
        System.out.println("Student is running.");
    }
}

public class Test {
    public static void main(String[] args) {
        Person p1 = new Student(); // 父类引用指向子类实例
        p1.run(); // 调用哪个 run() 方法?
    }
}

Person is running.

原因分析

  1. 静态方法的绑定规则
    • 静态方法是 类级别 的方法,在编译时就确定了调用哪个类的静态方法。
    • 因此,p1.run() 调用的是 Person 类的 run 方法,而不是 Student 类的 run 方法。
  2. 编译时类型决定调用的静态方法
    • 变量 p1 的编译时类型是 Person,所以调用的是 Person.run()
  3. 静态方法不支持多态
    • 静态方法不会根据运行时的实际对象类型(这里是 Student)进行动态绑定,静态方法只与变量的编译时类型相关。

扩展:静态方法调用的正确方式

静态方法最好通过类名直接调用,以免造成混淆:

public class Test {
    public static void main(String[] args) {
        Person p1 = new Student();

        // 推荐直接通过类名调用
        Person.run(); // 输出:Person is running.
        Student.run(); // 输出:Student is running.

        // 不推荐通过引用调用静态方法,容易引起误解
        p1.run(); // 输出:Person is running.
    }
}

总结

  • 静态方法是类级别的,不会参与多态。
  • 静态方法的调用依据变量的编译时类型,而不是运行时的实际类型。
  • 建议通过类名调用静态方法,以避免混淆和误解。

p1调用Person静态方法、Person非静态方法(如果没有被Student重写的话)、Student重写的方法

让我们逐一分析在以下场景中,p1 调用静态方法、非静态方法(没有被重写)以及被 Student 重写的方法的行为:


代码结构

class Person {
    // 静态方法
    public static void run() {
        System.out.println("Person is running (static).");
    }

    // 非静态方法(未被子类重写)
    public void walk() {
        System.out.println("Person is walking (non-static).");
    }

    // 非静态方法(将被子类重写)
    public void talk() {
        System.out.println("Person is talking (non-static).");
    }
}

class Student extends Person {
    // 静态方法(隐藏父类静态方法)
    public static void run() {
        System.out.println("Student is running (static).");
    }

    // 重写父类的非静态方法
    @Override
    public void talk() {
        System.out.println("Student is talking (non-static).");
    }
}

public class Test {
    public static void main(String[] args) {
        Person p1 = new Student(); // 父类引用指向子类实例

        // 静态方法调用
        p1.run(); // 调用哪个?  父类静态方法run()

        // 调用父类未被重写的非静态方法 父类没有被重写的方法 walk()
        p1.walk();

        // 调用子类重写的非静态方法  
        p1.talk();
    }
}

运行结果

Person is running (static).
Person is walking (non-static).
Student is talking (non-static).

分析调用行为

1. 调用静态方法:p1.run()

  • 调用结果:Person is running (static).
  • 原因:
    • 静态方法与变量的编译时类型绑定,而 p1 的编译时类型是 Person
    • 静态方法的调用不受运行时对象的影响,因此调用的是 Person 类的静态方法,而不是 Student 的。

2. 调用父类未被重写的非静态方法:p1.walk()

  • 调用结果:Person is walking (non-static).
  • 原因:
    • walk()Person 的非静态方法,Student 没有重写它。
    • 非静态方法调用会根据运行时对象的实际类型决定执行的版本,而 walk() 的唯一版本来自 Person

3. 调用子类重写的非静态方法:p1.talk()

  • 调用结果:Student is talking (non-static).
  • 原因:
    • 非静态方法支持多态。
    • 由于 talk()Student 重写,运行时对象是 Student 类型,因此调用 Studenttalk() 方法。

总结:调用行为一览表

方法类型 方法在 Person 类中 方法在 Student 类中 调用结果
静态方法 run() 定义了静态方法 run() 定义了静态方法 run() 调用编译时类型(Person)的静态方法,结果是 Person is running (static).
非静态方法 walk() 定义了非静态方法 walk() 未重写 调用运行时对象的父类版本,结果是 Person is walking (non-static).
非静态方法 talk() 定义了非静态方法 talk() 重写了非静态方法 talk() 调用运行时对象的子类版本,结果是 Student is talking (non-static).

注意点

  1. 静态方法不支持多态
    • 调用的静态方法取决于 引用变量的编译时类型,与运行时实际对象无关。
  2. 非静态方法支持多态
    • 调用的非静态方法取决于 运行时实际对象的类型
    • 如果子类重写了父类的方法,则调用的是子类版本;否则调用父类版本。
  3. 推荐静态方法的调用方式
    • 静态方法最好通过类名调用,例如:Person.run()Student.run(),避免通过实例变量调用导致混淆。
posted @ 2025-01-14 10:46  panghuhu~  阅读(70)  评论(0)    收藏  举报