《他强由他强,清风拂山岗;他横由他横,明月照大江》之二

Java基础

1、多态的原理

在Java中,多态是通过使用重写和动态方法分派来实现的。
方法重写(Method Overriding)
在Java中,当子类重写了父类的方法时,根据引用变量所引用对象的实际类型来调用对应的方法。这意味着在编译时,编译器仅检查引用变量的类型,而不会检查对象的实际类型。方法的调用发生在运行时,与对象的实际类型相对应。
动态方法分派(Dynamic Method Dispatch)
运行时多态是通过动态方法分派实现的,这是Java虚拟机(JVM)在运行时决定调用哪个方法版本的机制。动态分派使用Java的虚拟方法调用(invokevirtual)指令来处理对重写方法的调用。
虚方法表(Virtual Method Table,VMT或vtable)
Java在对象布局层面为每个对象存储了一个额外的隐藏引用,这个引用指向所属类的虚方法表(有时叫做vtable)。虚方法表里面包含了所有可被子类重写的方法的实际内存地址。当调用一个重写的方法时,JVM会查找调用对象实际类的虚方法表,找到正确的方法地址,并调用该方法。例如

class Parent {
   
    void show() {
    System.out.println("Parent's show()"); }
}

class Child extends Parent {
   
    void show() {
    System.out.println("Child's show()"); }
}

class Example {
   
    public static void main(String[] args) {
   
        Parent obj1 = new Parent();
        obj1.show(); // 输出 "Parent's show()"
        
        Parent obj2 = new Child();
        obj2.show(); // 输出 "Child's show()",虽然obj2被声明为Parent类型
    }
}

在上面的代码中,尽管obj2的静态类型(编译时类型)是Parent,它实际引用的是一个Child类型的对象。在调用show方法时,虚拟机动态查找并执行的是Child类中的show方法,而不是Parent中的方法。这就是多态的精髓——编码到父类的接口,而不是具体实现,使得程序能够更灵活和可扩展。
类型转换(Type Casting)
尽管多态允许我们通过父类的引用来调用子类对象的重写方法,有时我们可能仍需要访问子类独有的成员。这就需要将父类的引用转换为子类的引用,这是Java的显式类型转换(也称为向下转型)。

2、接口和抽象类

抽象类和接口
抽象类:包含抽象方法的类,即使用abstract修饰的类;抽象类只能被继承,所以不能使用final修饰,抽象类不能被实例化,
接口:接口是一个抽象类型,是抽象方法的集合,接口支持多继承,接口中定义的方法,默认是public abstract修饰的抽象方法
相同点:
​ ① 抽象类和接口都不能被实例化
​ ② 抽象类和接口都可以定义抽象方法,子类/实现类必须覆写这些抽象方法
不同点:
​ ① 抽象类有构造方法,接口没有构造方法
​ ③抽象类可以包含普通方法,接口中只能是public abstract修饰抽象方法(Java8之后可以)
​ ③ 抽象类只能单继承,接口可以多继承
​ ④ 抽象类可以定义各种类型的成员变量,接口中只能是public static final修饰的静态常量
抽象类使用场景
抽象类最合适的使用场景是当你需要定义一个通用的基类,它为子类提供了一个模板,子类需要实现或者扩展该基类定义的一些行为。

共享代码:当多个类有共同的方法实现时,可以将这些公共代码放在抽象类中作为非抽象方法来避免重复。
继承关系:当类之间存在明显的层级关系,并且它们自然地可以进行归类时,使用抽象类可以方便地表达这种层级。
保留拓展点:如果以后可能有新的子类加入,这些子类需要共享一些代码,抽象类就提供了一个很好的拓展点。
控制子类的设计:抽象类可以定义方法,并强制子类按照特定的方式来实现这些方法(通过定义抽象方法),从而对子类的设计和实现施加更多约束。
包含状态:如果你的对象需要包含内部状态或字段,抽象类更适用,因为接口无法持有状态信息。
接口使用场景
接口非常适用于定义不同类共享的行为,但又没有继承关系的情况。通常,它们表征类能够做什么(功能),而不是它是什么(身份)。

定义合约:当你需要定义一组公共的API,且希望不同的类去实现这些API时,接口是最好的选择。
多继承能力:在Java中,由于只能单继承类,接口可以提供一种方式让一个类具备多个类型的能力。
解耦:接口可以帮助你解耦实现和接口,让方法接收接口类型,使你的代码更灵活、更易于扩展。
多实现:当你希望一个类可以从多个来源获取方法实现时,接口提供了这样的机制。
模块化:通过接口,可以将系统分解为多个互相交互的模块,每个模块对其他模块隐藏其具体实现。

3、泛型与泛型擦除

泛型
Java 泛型是在Java 5中引入的特性,它允许类、接口和方法具有类型参数化的能力。类型参数化意味着在编写类或方法时,你可以指定一个或多个类型占位符,这些占位符将在编译时确定为具体的类型。泛型的主要目的是提供类型安全和减少运行时类型转换的需要。
泛型擦除

public class Test {
   
 
    public static void main(String[] args) throws Exception {
   
 
        ArrayList<Integer> list = new ArrayList<Integer>();
 
        list.add(1);  //这样调用 add 方法只能存储整形,因为泛型类型的实例为 Integer
 		// 证明了泛型擦除
        list.getClass().getMethod("add", Object.class).invoke(list, "asd");
 
        for (int i = 0; i < list.size(); i++) {
   
            System.out.println(list.get(i));
        }
    }
 
}
public class Test {
   
 
    public static void main(String[] args) {
   
 
        ArrayList<String> list1 = new ArrayList<String>();
        list1.add("abc");
 
        ArrayList<Integer> list2 = new ArrayList<Integer>();
        list2.add(123);
 		// 结果为true
        System.out.println(list1.getClass() == list2.getClass());
    }
 
}

类型擦除:在编译时,所有的泛型类型参数都会被擦除

  • 31
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值