Java的继承与多态/基于良好C++基础光速转行(滑稽)

Java的继承与多态/基于良好C++基础转行

Java类的访问修饰符

鉴于Java核心技术卷中的内容比较繁复,故进行内容整理

public(公共类)

public: 表示该类是公共的,可以在任何地方被访问。我们使用以下类来进行包外访问

package power_control;

public class ExtentOfPower {
    public int one = 1;
    public int two = 2;
    public int three = 3;

    public void getAllValue(){
        System.out.println(one + "  " + two + "  " + three);
    }
}

在此包中进行访问:

package out_vist_class;
import power_control.ExtentOfPower;

public class OutVisitTest {
    public static void main(String[] args) {
        ExtentOfPower obj = new ExtentOfPower();
        obj.getAllValue();
    }
}

default(包私有)

default(包私有): 如果不使用任何修饰符,默认为包私有。只能在同一包中被访问,将上述的类中的public去掉,我们再回到访问包中,IDEA将会提示:

>
JavaTest: 构建 失败 在2023/12/11 13:45,3个错误
OutVisitTest.java src\out_vist_class 3个错误
power_control.ExtentOfPower在power_control中不是公共的;无法从外部程序包中对其进行访问:2
power_control.ExtentOfPower在power_control中不是公共的;无法从外部程序包中对其进行访问:6
power control.ExtentOfPower在power_control中不是公共的;无法从外部程序包中对其进行访问:6
1秒325毫秒

注意:类的访问修饰没有保护(protected)这一说法

Java类字段的访问修饰符

public(公共的)

  • 修饰的成员对所有类可见,没有访问限制。
  • 在当前包和其他包中都可以访问。

示例类的内容:

package power_control;

public class ExtentOfPower {
    public int one = 1;
}

包外访问:

package out_vist_class;
import power_control.ExtentOfPower;

public class OutVisitTest {
    public static void main(String[] args) {
        ExtentOfPower obj = new ExtentOfPower();
        System.out.println(obj.one);
    }
}

private(私有的)

  • 修饰的成员只能在声明它的类中访问,对外部类不可见。外部包自然也不可访问
  • 通常用于封装类的实现细节,实现信息隐藏
class ExtentOfPower {
    private int one = 1;
    public int two = 2;
}

public class MainOfExtentPower{
    public static void main(String[] args){
        ExtentOfPower obj = new ExtentOfPower();
        System.out.println(obj.one +"  " + obj.two);
    }
}

此处IDEA将提示:java: one 在 power_control.ExtentOfPower 中是 private 访问控制

protected(受保护的)

  • 修饰的成员对同一包内的类和所有子类可见,但对其他包的类不可见。
  • 用于继承关系中,允许子类访问父类的受保护成员

同包中的子类与其他类

class ExtentOfPower {
    private int one = 1;
    public int two = 2;
    protected int three = 3;
}

class ExtentFromExtentPower extends ExtentOfPower{
}

public class MainOfExtentPower{
    public static void main(String[] args){
        ExtentFromExtentPower obj = new ExtentFromExtentPower();
        System.out.println(obj.two + "  " + obj.three);
        var object = new ExtentOfPower();
        System.out.println(object.three);
    }
}

其他包中的非子类:

package power_control;

public class ExtentOfPower {
    protected int one = 1;
}
package out_vist_class;
import power_control.ExtentOfPower;

public class OutVisitTest {
    public static void main(String[] args) {
        ExtentOfPower obj = new ExtentOfPower();
        System.out.println(obj.one);
    }
}

IDEA提示:java: one 在 power_control.ExtentOfPower 中是 protected 访问控制

default (包级私有)

  • 修饰的成员只对同一包内的类可见,对其他包的类不可见。
  • 如果没有指定访问控制符,则默认为包级私有。
package power_control;

public class ExtentOfPower {
    int one = 1;
}

去掉访问控制符后,在外部包中类进行访问,IDEA提示:java: one在power_control.ExtentOfPower中不是公共的; 无法从外部程序包中对其进行访问

使用extends继承&方法重写

class SuperClass{
    protected int id;

    public SuperClass(int aId){
        id = aId;
    }
    public void showAllInfo(){
        System.out.println(id);
    }
}

父类书写,没什么特殊的,注意访问控制的关键字就行,重点在于子类:

class SubClass extends SuperClass {
    private String name;

    public SubClass(int aId, String aName){
        super(aId);
        name = aName;
    }

    @Override
    public void showAllInfo() {
        super.showAllInfo();
        System.out.println(name);
    }
}

注解如下:

  • 在父类构造器具有参数时,我们需要使用super关键字来调用父类的构造器,并且提供足够数量的正确类型关键字
  • 在在子类中我们想要调用父类中已进行重写的方法,我们可以使用关键字super来指定父类的重名方法
  • 对于重写的方法我们通常会使用@Override注解
  • 注意Java中没有多重继承这一概念,但是有类似功能,如接口(interface)
  • 其他方面与C++基础语法层面无太大不同,且继承也是is-a关系

@Override 是 Java 编程语言中的一个注解(Annotation)。它的作用是告诉编译器,被注解的方法是一个重写(override)父类或接口中的方法。

在 Java 中,当你声明一个类继承自另一个类,或者实现一个接口时,通常你可以重写父类或接口中的方法。在这种情况下,如果你打算重写一个方法,使用 @Override 注解可以帮助你确保你的方法真的是在父类或接口中存在的,而不是一个新的方法。

使用 @Override 的好处有:

  1. 编译时检查: 编译器会检查被注解的方法是否真的重写了父类或接口中的方法。如果没有正确重写,编译器将给出错误提示。

    @Override
    public void myMethod() {
        // Your implementation
    }
    
  2. 文档说明: @Override 注解也提供了一种文档方式,让其他程序员清楚地知道这个方法是重写的。

    /**
     * This method overrides the corresponding method in the superclass.
     */
    @Override
    public void myMethod() {
        // Your implementation
    }
    

阻止继承final

在 Java 中,final 是一个关键字,可以用于修饰类、方法和变量。当 final 用于修饰一个类时,它阻止这个类被继承。一旦一个类被声明为 final,它不能被其他类继承。

下面是一个简单的例子:

final class FinalClass {
    // 类的内容
}

// 编译错误,无法继承 final 类
// class SubClass extends FinalClass {}

在这个例子中,FinalClass 被声明为 final 类,因此任何尝试继承它的操作都会导致编译错误。

使用 final 类的主要目的是为了防止类的进一步修改和继承,以确保该类的行为和实现不会被子类修改。

abstract抽象类实现

抽象类(Abstract Class)是 Java 中一种特殊的类,它不能被实例化,主要用于作为其他类的基类。抽象类可以包含抽象方法和具体方法。抽象方法是一种没有具体实现的方法,它需要在子类中被具体实现。

类似C++的含纯虚函数类

抽象类的定义

使用 abstract 关键字定义抽象类,同时可以包含抽象方法和具体方法。抽象方法用 abstract 关键字声明,不包含方法体。

abstract class Shape {
    // 抽象方法,没有方法体,需要在子类中实现
    abstract void draw();

    // 具体方法,可以在抽象类中有具体实现
    void resize() {
        System.out.println("Resizing shape");
    }
}

抽象类的示例

class Circle extends Shape {
    // 实现抽象方法
    @Override
    void draw() {
        System.out.println("Drawing a circle");
    }
}

class Square extends Shape {
    // 实现抽象方法
    @Override
    void draw() {
        System.out.println("Drawing a square");
    }
}

抽象类的使用:

public class Main {
    public static void main(String[] args) {
        // 无法实例化抽象类
        // Shape shape = new Shape(); // 编译错误

        // 可以使用抽象类的引用指向其子类的对象
        Shape circle = new Circle();
        circle.draw();  // 输出: Drawing a circle
        circle.resize();  // 输出: Resizing shape

        Shape square = new Square();
        square.draw();  // 输出: Drawing a square
        square.resize();  // 输出: Resizing shape
    }
}

抽象类的使用注意事项:

  1. 无法实例化: 抽象类不能被直接实例化,只能用于被继承。

  2. 子类实现抽象方法: 如果一个类继承了抽象类,它必须实现抽象类中所有的抽象方法,除非该子类也是抽象类。

  3. 抽象类可以包含具体方法: 抽象类中可以包含具体方法(有方法体的方法),子类可以选择性地重写这些方法。

  4. 抽象类可以没有抽象方法: 抽象类可以不包含抽象方法,但包含抽象方法的目的是为了强制子类实现特定的方法。

  5. 抽象类的构造方法: 抽象类可以有构造方法,但不能被直接实例化。子类在实例化时会调用父类的构造方法。

使用抽象类的主要目的是为了定义一个通用的类结构,让子类去具体实现其中的抽象方法。

根类继承Object(所有类的超类)

在Java中,所有类都直接或间接地继承自Object类。Object类是Java类层次结构的根类,它包含了所有对象共有的方法。以下是Object类的一些常用方法:

  1. toString()方法: 该方法返回对象的字符串表示形式。默认的实现返回的是类的名称,后跟 “@” 和对象的哈希码值。
public String toString() {
    return getClass().getName() + "@" + Integer.toHexString(hashCode());
}

通常,为了更好地表示对象,你可以在自定义类中重写toString()方法。

  1. equals(Object obj)方法: 用于比较两个对象是否相等。默认的实现是比较对象的引用是否相等,即是否指向同一内存地址。可以根据实际需要覆盖该方法。
public boolean equals(Object obj) {
    return (this == obj);
}
  1. hashCode()方法: 返回对象的哈希码值。默认的实现是返回对象的内存地址的哈希码。
public int hashCode() {
    return super.hashCode();
}
  1. getClass()方法: 返回对象的运行时类。
public final Class<?> getClass()
  1. clone()方法: 用于创建并返回对象的副本。要使用clone()方法,类必须实现Cloneable接口,否则会抛出CloneNotSupportedException异常。
protected Object clone() throws CloneNotSupportedException
  1. finalize()方法: 在垃圾回收器将对象从内存中清除之前,该方法被调用。可以重写该方法进行资源释放等清理操作。
protected void finalize() throws Throwable

这些是Object类的主要方法。注意事项包括:

  • equals()和hashCode()一致性: 如果两个对象通过equals()方法相等,那么它们的hashCode()值应该相等。这是为了保证在使用散列表等数据结构时能够正确地工作。

  • 谨慎使用clone()方法: clone()方法会创建一个对象的副本,但可能会导致意想不到的结果。最好是实现Cloneable接口并重写clone()方法,确保正确处理对象的复制。

  • toString()可读性: 重写toString()方法时,应该返回一个清晰、简洁且可读性强的字符串,以便在日志、调试和其他情况下更容易理解对象的状态。

Java多态实现与使用

多态(Polymorphism)是面向对象编程中的一个重要概念,它允许使用一个抽象的类型来表示多个具体的类型,并且在运行时可以动态地选择使用哪个具体类型。多态有两种主要形式:编译时多态(静态多态)和运行时多态(动态多态)。

编译时多态(方法重载)

class SuperClass{
    protected int id;

    public SuperClass(int aId){
        id = aId;
    }
    public void showAllInfo(){
        System.out.println(id);
    }

    public void showAllInfo(String greeting){
        System.out.println(greeting + String.valueOf(id));
    }
}

如上所示,使用接收不同的参数的方法来重载方法,其调用不同方法提供不同参数时完成多态,定义对象后就不再改变

运行时多态(继承重写)

编译对象可以与引用对象不一致,在运行时决定其调用的方法

我们回到继承中书写的两个类,我们在main方法中书写以下内容:

public class SubClassMain {
    public static void main(String[] args){
        SuperClass object = new SuperClass(233);
        object.showAllInfo();

        object = new SubClass(2333, "WildPointer");
        object.showAllInfo();
    }
}

main方法运行时,我们编译时建立了一个超类的变量,让超类变量引用子类或超类对象,程序开始运行时,会依据我们实际引用的对象来调用我们对应的方法,从而实现多态

向上转型与向下转型

考虑以下继承状态:

class SuperClass{
    protected int id;

    public SuperClass(int aId){
        id = aId;
    }
    
    public void showAllInfo(){
        System.out.println(id);
    }
}

class SubClass extends SuperClass {
    private String name;

    public SubClass(int aId, String aName){
        super(aId);
        name = aName;
    }

    @Override
    public void showAllInfo() {  //
        super.showAllInfo();
        System.out.println(name);
    }
    
    public void greetingSubClassObject(String greeting){
        System.out.println(greeting + String.valueOf(id) + name);
    }
}
向上转型(父类变量引用子类对象)
public class SubClassMain {
    public static void main(String[] args){
        SuperClass object = new SubClass(233, "WildPointer");
        object.showAllInfo();
        object.greetingSubClassObject("Hello");
    }
}

运行上述代码,我们会收到致命错误一个:

D:\CodeProjects\IntelljIDEAprojects\JavaTest\src\javaclass\SubClassMain.java:37:15
java: 找不到符号
  符号:   方法 greetingSubClassObject(java.lang.String)
  位置: 类型为javaclass.SuperClass的变量 object

原因是父类引用无法调用子类的特有方法,只能够根据访问控制来调用父类中存在的方法

向下转型(父类引用转为子类引用)

向下转型是将一个父类类型的引用变量转换为其子类类型的引用变量的过程。这种类型的转换在 Java 中需要显式地进行,因为在编译时无法确定对象的真实类型。在进行向下转型之前,通常需要使用 instanceof 运算符来检查对象的类型,以避免类型转换异常(ClassCastException)。

  • 语法结构
子类名 子类变量名 = (子类名)父类引用变量名

向下转型的注意事项:只能强制转换父类的引用,不能强转父类的对象,并且要求父类的引用必须指向的是当前目标类型的对象,即父类引用的对象必须是该子类的对象,在向上转型中的问题,我们便可以使用向下转型来解决:

if(object instanceof SubClass){
    SubClass temp = (SubClass) object;
    temp.greetingSubClassObject("Hello");
}

instanceof 是 Java 中的一个关键字,用于测试对象是否是特定类的实例,或者是否实现了特定的接口。它的语法如下:

object instanceof Class/Interface

其中,object 是要测试的对象,而 Class/Interface 是要测试的类或接口。使用 instanceof 可以进行类型检查,以便在运行时确定对象的类型,从而采取相应的操作。它返回一个布尔值,如果对象是指定类的实例或实现了指定接口,则返回 true,否则返回 false

你可能感兴趣的:(java)