Java 中文官方教程 2022 版(五)(1)

简介: Java 中文官方教程 2022 版(五)

多态性

原文:docs.oracle.com/javase/tutorial/java/IandI/polymorphism.html

多态性的词典定义指的是生物学中的一个原则,即一个生物体或物种可以具有许多不同的形式或阶段。这个原则也可以应用于面向对象编程和像 Java 语言这样的语言中。类的子类可以定义自己独特的行为,同时共享父类的一些功能。

多态性可以通过对Bicycle类进行微小修改来演示。例如,可以向类中添加一个printDescription方法,该方法显示实例中当前存储的所有数据。

public void printDescription(){
    System.out.println("\nBike is " + "in gear " + this.gear
        + " with a cadence of " + this.cadence +
        " and travelling at a speed of " + this.speed + ". ");
}

要在 Java 语言中演示多态特性,请使用MountainBikeRoadBike类扩展Bicycle类。对于MountainBike,添加一个suspension字段,这是一个String值,指示自行车是否有前减震器,Front。或者,自行车有前后减震器,Dual

这是更新后的类:

public class MountainBike extends Bicycle {
    private String suspension;
    public MountainBike(
               int startCadence,
               int startSpeed,
               int startGear,
               String suspensionType){
        super(startCadence,
              startSpeed,
              startGear);
        this.setSuspension(suspensionType);
    }
    public String getSuspension(){
      return this.suspension;
    }
    public void setSuspension(String suspensionType) {
        this.suspension = suspensionType;
    }
    public void printDescription() {
        super.printDescription();
        System.out.println("The " + "MountainBike has a" +
            getSuspension() + " suspension.");
    }
} 

请注意覆盖的printDescription方法。除了之前提供的信息外,输出还包括有关悬架的额外数据。

接下来,创建RoadBike类。因为公路或赛车自行车有细轮胎,所以添加一个属性来跟踪轮胎宽度。这是RoadBike类:

public class RoadBike extends Bicycle{
    // In millimeters (mm)
    private int tireWidth;
    public RoadBike(int startCadence,
                    int startSpeed,
                    int startGear,
                    int newTireWidth){
        super(startCadence,
              startSpeed,
              startGear);
        this.setTireWidth(newTireWidth);
    }
    public int getTireWidth(){
      return this.tireWidth;
    }
    public void setTireWidth(int newTireWidth){
        this.tireWidth = newTireWidth;
    }
    public void printDescription(){
        super.printDescription();
        System.out.println("The RoadBike" + " has " + getTireWidth() +
            " MM tires.");
    }
}

请注意,printDescription方法再次被覆盖。这次,显示了有关轮胎宽度的信息。

总结一下,有三个类:BicycleMountainBikeRoadBike。这两个子类覆盖了printDescription方法并打印了独特的信息。

这是一个创建三个Bicycle变量的测试程序。每个变量分配给三个自行车类中的一个。然后打印每个变量。

public class TestBikes {
  public static void main(String[] args){
    Bicycle bike01, bike02, bike03;
    bike01 = new Bicycle(20, 10, 1);
    bike02 = new MountainBike(20, 10, 5, "Dual");
    bike03 = new RoadBike(40, 20, 8, 23);
    bike01.printDescription();
    bike02.printDescription();
    bike03.printDescription();
  }
}

以下是测试程序的输出:

Bike is in gear 1 with a cadence of 20 and travelling at a speed of 10\. 
Bike is in gear 5 with a cadence of 20 and travelling at a speed of 10\. 
The MountainBike has a Dual suspension.
Bike is in gear 8 with a cadence of 40 and travelling at a speed of 20\. 
The RoadBike has 23 MM tires.

Java 虚拟机(JVM)调用与每个变量引用的对象相对应的方法。它不调用变量类型定义的方法。这种行为称为虚拟方法调用,展示了 Java 语言中重要的多态性特性的一个方面。

隐藏字段

原文:docs.oracle.com/javase/tutorial/java/IandI/hidevariables.html

在一个类中,如果一个字段与超类中的字段同名,即使它们的类型不同,该字段也会隐藏超类的字段。在子类中,无法通过简单名称引用超类中的字段。相反,必须通过super来访问该字段,这将在下一节中介绍。一般来说,我们不建议隐藏字段,因为这会使代码难以阅读。

使用关键字 super

原文:docs.oracle.com/javase/tutorial/java/IandI/super.html

访问超类成员

如果您的方法覆盖了其超类的方法之一,您可以通过关键字super调用被覆盖的方法。您还可以使用super来引用隐藏字段(尽管不建议隐藏字段)。考虑这个类,Superclass

public class Superclass {
    public void printMethod() {
        System.out.println("Printed in Superclass.");
    }
}

这里是一个名为Subclass的子类,覆盖了printMethod()

public class Subclass extends Superclass {
    // overrides printMethod in Superclass
    public void printMethod() {
        super.printMethod();
        System.out.println("Printed in Subclass");
    }
    public static void main(String[] args) {
        Subclass s = new Subclass();
        s.printMethod();    
    }
}

Subclass中,简单名称printMethod()指的是在Subclass中声明的那个,它覆盖了Superclass中的那个。因此,要引用从Superclass继承的printMethod()Subclass必须使用一个限定名称,使用super如所示。编译和执行Subclass将打印以下内容:

Printed in Superclass.
Printed in Subclass

子类构造函数

以下示例说明了如何使用super关键字调用超类的构造函数。回想一下Bicycle示例中MountainBikeBicycle的子类。这是MountainBike(子类)构造函数,它调用超类构造函数,然后添加自己的初始化代码:

public MountainBike(int startHeight, 
                    int startCadence,
                    int startSpeed,
                    int startGear) {
    super(startCadence, startSpeed, startGear);
    seatHeight = startHeight;
} 

调用超类构造函数必须是子类构造函数中的第一行。

调用超类构造函数的语法是

super();  

或者:

super(parameter list);

使用super()时,将调用超类的无参数构造函数。使用super(parameter list)时,将调用具有匹配参数列表的超类构造函数。


**注意:**如果构造函数没有显式调用超类的构造函数,Java 编译器会自动插入对超类的无参数构造函数的调用。如果超类没有无参数构造函数,您将会得到一个编译时错误。Object确实有这样一个构造函数,所以如果Object是唯一的超类,就不会有问题。


如果子类构造函数显式或隐式地调用其超类的构造函数,您可能会认为会有一整个构造函数链被调用,一直回溯到Object的构造函数。事实上,情况确实如此。这被称为构造函数链,当存在长串的类继承时,您需要注意这一点。

作为超类的对象

原文:docs.oracle.com/javase/tutorial/java/IandI/objectclass.html

Object类,位于java.lang包中,位于类层次结构树的顶部。每个类都是Object类的后代,直接或间接的。你使用或编写的每个类都继承了Object的实例方法。你不需要使用这些方法中的任何一个,但是如果选择这样做,可能需要用特定于你的类的代码重写它们。本节讨论的从Object继承的方法有:

  • protected Object clone() throws CloneNotSupportedException
    创建并返回此对象的副本。
  • public boolean equals(Object obj)
    指示某个其他对象是否"等于"这个对象。
  • protected void finalize() throws Throwable
    垃圾回收器在对象上调用的方法
    集合确定没有更多引用指向该对象
  • public final Class getClass()
    返回对象的运行时类。
  • public int hashCode()
    为对象返回一个哈希码值。
  • public String toString()
    返回对象的字符串表示形式。

ObjectnotifynotifyAllwait方法在程序中独立运行的线程的活动同步中起着作用,这将在后面的课程中讨论,这里不会涉及。这些方法有五个:

  • public final void notify()
  • public final void notifyAll()
  • public final void wait()
  • public final void wait(long timeout)
  • public final void wait(long timeout, int nanos)

注意: 这些方法中有一些微妙的方面,特别是clone方法。


clone()方法

如果一个类或其父类实现了Cloneable接口,你可以使用clone()方法从现有对象创建一个副本。要创建一个克隆,你需要编写:

*aCloneableObject*.clone();

Object的这个方法的实现会检查调用clone()的对象是否实现了Cloneable接口。如果对象没有实现,该方法会抛出一个CloneNotSupportedException异常。异常处理将在后面的课程中介绍。目前,你需要知道clone()必须声明为

protected Object clone() throws CloneNotSupportedException

或:

public Object clone() throws CloneNotSupportedException

如果你要编写一个clone()方法来覆盖Object中的方法。

如果调用clone()的对象确实实现了Cloneable接口,Objectclone()方法的实现会创建一个与原始对象相同类的对象,并初始化新对象的成员变量为与原始对象对应的成员变量相同的值。

使你的类可克隆的最简单方法是在类的声明中添加implements Cloneable,然后你的对象可以调用clone()方法。

对于一些类,Objectclone() 方法的默认行为就很好用。然而,如果一个对象包含对外部对象的引用,比如 ObjExternal,你可能需要重写 clone() 来获得正确的行为。否则,一个对象对 ObjExternal 的更改也会在其克隆中可见。这意味着原始对象和其克隆不是独立的—为了解耦它们,你必须重写 clone(),使其克隆对象 ObjExternal。然后原始对象引用 ObjExternal,而克隆引用 ObjExternal 的克隆,这样对象和其克隆就是真正独立的。

equals() 方法

equals() 方法用于比较两个对象是否相等,如果它们相等则返回 trueObject 类中提供的 equals() 方法使用身份运算符 (==) 来确定两个对象是否相等。对于基本数据类型,这会给出正确的结果。然而,对于对象来说,这并不适用。Object 提供的 equals() 方法测试对象的引用是否相等—也就是说,如果比较的对象是完全相同的对象。

要测试两个对象是否在等价性意义上相等(包含相同的信息),你必须重写 equals() 方法。下面是一个重写 equals()Book 类的示例:

public class Book {
    String ISBN;
    public String getISBN() { 
        return ISBN;
    }
    public boolean equals(Object obj) {
        if (obj instanceof Book)
            return ISBN.equals((Book)obj.getISBN()); 
        else
            return false;
    }
}

考虑下面这段代码,用于测试 Book 类的两个实例是否相等:

// Swing Tutorial, 2nd edition
Book firstBook  = new Book("0201914670");
Book secondBook = new Book("0201914670");
if (firstBook.equals(secondBook)) {
    System.out.println("objects are equal");
} else {
    System.out.println("objects are not equal");
}

即使 firstBooksecondBook 引用了两个不同的对象,这个程序会显示 objects are equal。它们被认为是相等的,因为比较的对象包含相同的 ISBN 号码。

如果身份运算符对于你的类不合适,你应该始终重写 equals() 方法。


**注意:**如果你重写了 equals(),你必须同时重写 hashCode()


finalize() 方法

Object 类提供了一个回调方法 finalize(),当对象变为垃圾时可能会被调用。Objectfinalize() 实现什么也不做—你可以重写 finalize() 来进行清理,比如释放资源。

finalize() 方法可能会被系统自动调用,但是何时调用,甚至是否调用都是不确定的。因此,不要依赖这个方法来清理资源。例如,如果在执行 I/O 操作后没有关闭文件描述符,并且期望 finalize() 为您关闭它们,那么可能会耗尽文件描述符。相反,使用 try-with 资源语句来自动关闭应用程序的资源。参见 The try-with-resources Statement 和 Finalization and Weak, Soft, and Phantom References 中的 Java Platform, Standard Edition HotSpot Virtual Machine Garbage Collection Tuning Guide

getClass() 方法

你不能重写 getClass

getClass()方法返回一个Class对象,该对象有一些方法可以获取关于类的信息,比如它的名称(getSimpleName())、它的父类(getSuperclass())和它实现的接口(getInterfaces())。例如,下面的方法获取并显示对象的类名:

void printClassName(Object obj) {
    System.out.println("The object's" + " class is " +
        obj.getClass().getSimpleName());
}

Class类,位于java.lang包中,有大量的方法(超过 50 个)。例如,你可以测试类是否为注解(isAnnotation())、接口(isInterface())或枚举(isEnum())。你可以查看对象的字段(getFields())或方法(getMethods()),等等。

hashCode()方法

hashCode()返回的值是对象的哈希码,是由哈希算法生成的整数值。

根据定义,如果两个对象相等,它们的哈希码也必须相等。如果你重写了equals()方法,改变了两个对象相等的方式,那么ObjecthashCode()实现就不再有效。因此,如果你重写了equals()方法,你也必须重写hashCode()方法。

toString()方法

你应该始终考虑在你的类中重写toString()方法。

ObjecttoString()方法返回对象的String表示,对于调试非常有用。对象的String表示完全取决于对象本身,这就是为什么你需要在你的类中重写toString()

使用toString()System.out.println()可以显示对象的文本表示,比如Book的实例:

System.out.println(firstBook.toString());

对于正确重写的toString()方法,会打印出有用的信息,就像这样:

ISBN: 0201914670; The Swing Tutorial; A Guide to Constructing GUIs, 2nd Edition

编写最终类和方法

原文:docs.oracle.com/javase/tutorial/java/IandI/final.html

你可以将类的一些或所有方法声明为最终。在方法声明中使用final关键字表示该方法不能被子类重写。Object类就是这样做的——它的一些方法是final的。

如果一个方法有一个不应该被更改的实现,并且对对象的一致状态至关重要,你可能希望将其设置为最终方法。例如,你可能想要将ChessAlgorithm类中的getFirstPlayer方法设置为最终方法:

class ChessAlgorithm {
    enum ChessPlayer { WHITE, BLACK }
    ...
    final ChessPlayer getFirstPlayer() {
        return ChessPlayer.WHITE;
    }
    ...
}

从构造函数中调用的方法通常应该声明为最终方法。如果构造函数调用一个非最终方法,子类可能重新定义该方法,导致意想不到或不希望的结果。

请注意,你也可以声明整个类为最终类。声明为最终类的类不能被子类化。例如,当创建像String类这样的不可变类时,这是非常有用的。

抽象方法和类

原文:docs.oracle.com/javase/tutorial/java/IandI/abstract.html

抽象类 是一个声明为 abstract 的类—它可能包含或不包含抽象方法。抽象类不能被实例化,但可以被子类化。

抽象方法 是一种声明但没有实现的方法(没有大括号,后面跟着一个分号),如下所示:

abstract void moveTo(double deltaX, double deltaY);

如果一个类包含抽象方法,那么这个类本身必须声明为 abstract,如下所示:

public abstract class GraphicObject {
   // declare fields
   // declare nonabstract methods
   abstract void draw();
}

当一个抽象类被子类化时,子类通常为其父类中的所有抽象方法提供实现。但是,如果没有提供实现,则子类也必须声明为 abstract


注意: 接口 中的方法(参见 接口 部分)如果没有声明为默认或静态,则隐式是抽象的,因此不需要使用 abstract 修饰符。 (可以使用,但是不必要。)


抽象类与接口的比较

抽象类类似于接口。你不能实例化它们,它们可能包含一些声明有或没有实现的方法。然而,使用抽象类,你可以声明非静态和非最终的字段,并定义公共、受保护和私有的具体方法。使用接口,所有字段都自动是公共的、静态的和最终的,你声明或定义的所有方法(作为默认方法)都是公共的。此外,你只能扩展一个类,无论它是否是抽象的,而你可以实现任意数量的接口。

你应该使用抽象类还是接口?

  • 如果你的情况符合以下任何一种情况,请考虑使用抽象类:
  • 你想在几个密切相关的类之间共享代码。
  • 你期望扩展你的抽象类的类有许多共同的方法或字段,或者需要除了 public 之外的访问修饰符(比如 protected 和 private)。
  • 你想声明非静态或非最终字段。这使你能够定义可以访问和修改它们所属对象状态的方法。
  • 如果你的情况符合以下任何一种情况,请考虑使用接口:
  • 你期望不相关的类会实现你的接口。例如,ComparableCloneable 这些接口被许多不相关的类实现。
  • 你想指定特定数据类型的行为,但不关心谁实现它的行为。
  • 你想利用类型的多重继承。

JDK 中抽象类的一个示例是AbstractMap,它是集合框架的一部分。它的子类(包括 HashMapTreeMapConcurrentHashMap)共享许多方法(包括 getputisEmptycontainsKeycontainsValue),这些方法是由 AbstractMap 定义的。

JDK 中实现多个接口的一个类的示例是HashMap,它实现了接口 SerializableCloneableMap。通过阅读这些接口列表,你可以推断出 HashMap 的实例(无论是哪个开发者或公司实现的类)可以被克隆,是可序列化的(这意味着它可以被转换为字节流;参见可序列化对象部分),并且具有映射功能。此外,Map 接口已经通过许多默认方法(如 mergeforEach)进行了增强,而旧类实现了该接口的类不必定义这些方法。

请注意,许多软件库同时使用抽象类和接口;HashMap 类实现了多个接口,并且还扩展了抽象类 AbstractMap

抽象类示例

在面向对象的绘图应用程序中,你可以绘制圆、矩形、线条、贝塞尔曲线和许多其他图形对象。这些对象都具有某些状态(例如:位置、方向、线条颜色、填充颜色)和行为(例如:moveTo、rotate、resize、draw)是共同的。其中一些状态和行为对所有图形对象都是相同的(例如:位置、填充颜色和 moveTo)。其他需要不同实现(例如,resize 或 draw)。所有 GraphicObject 必须能够自行绘制或调整大小;它们只是在如何执行这些操作上有所不同。这是一个抽象超类的完美情况。你可以利用相似之处,并声明所有图形对象都继承自相同的抽象父对象(例如 GraphicObject),如下图所示。

类 Rectangle、Line、Bezier 和 Circle 继承自 GraphicObject

首先,声明一个抽象类 GraphicObject,以提供所有子类完全共享的成员变量和方法,例如当前位置和 moveTo 方法。GraphicObject 还声明了抽象方法,例如 drawresize,这些方法需要所有子类实现,但必须以不同的方式实现。GraphicObject 类可能如下所示:

abstract class GraphicObject {
    int x, y;
    ...
    void moveTo(int newX, int newY) {
        ...
    }
    abstract void draw();
    abstract void resize();
}

每个非抽象子类 GraphicObject,如 CircleRectangle,必须提供 drawresize 方法的实现:

class Circle extends GraphicObject {
    void draw() {
        ...
    }
    void resize() {
        ...
    }
}
class Rectangle extends GraphicObject {
    void draw() {
        ...
    }
    void resize() {
        ...
    }
}

Java 中文官方教程 2022 版(五)(2)https://developer.aliyun.com/article/1486295

相关文章
|
4天前
|
前端开发 Java Maven
【前端学java】全网最详细的maven安装与IDEA集成教程!
【8月更文挑战第12天】全网最详细的maven安装与IDEA集成教程!
21 2
【前端学java】全网最详细的maven安装与IDEA集成教程!
|
9天前
|
存储 网络协议 Oracle
java教程
java教程【8月更文挑战第11天】
14 5
|
1月前
|
SQL 安全 Java
「滚雪球学Java」教程导航帖(更新2024.07.16)
《滚雪球学Spring Boot》是一个面向初学者的Spring Boot教程,旨在帮助读者快速入门Spring Boot开发。本专通过深入浅出的方式,将Spring Boot开发中的核心概念、基础知识、实战技巧等内容系统地讲解,同时还提供了大量实际的案例,让读者能够快速掌握实用的Spring Boot开发技能。本书的特点在于注重实践,通过实例学习的方式激发读者的学习兴趣和动力,并引导读者逐步掌握Spring Boot开发的实际应用。
42 1
「滚雪球学Java」教程导航帖(更新2024.07.16)
WXM
|
25天前
|
Oracle Java 关系型数据库
Java JDK下载安装及环境配置超详细图文教程
Java JDK下载安装及环境配置超详细图文教程
WXM
129 3
|
1月前
|
测试技术 API Android开发
《手把手教你》系列基础篇(九十七)-java+ selenium自动化测试-框架设计篇-Selenium方法的二次封装和页面基类(详解教程)
【7月更文挑战第15天】这是关于自动化测试框架中Selenium API二次封装的教程总结。教程中介绍了如何设计一个支持不同浏览器测试的页面基类(BasePage),该基类包含了对Selenium方法的二次封装,如元素的输入、点击、清除等常用操作,以减少重复代码。此外,页面基类还提供了获取页面标题和URL的方法。
44 2
|
1月前
|
Web App开发 XML Java
《手把手教你》系列基础篇(九十六)-java+ selenium自动化测试-框架之设计篇-跨浏览器(详解教程)
【7月更文挑战第14天】这篇教程介绍了如何使用Java和Selenium构建一个支持跨浏览器测试的自动化测试框架。设计的核心是通过读取配置文件来切换不同浏览器执行测试用例。配置文件中定义了浏览器类型(如Firefox、Chrome)和测试服务器的URL。代码包括一个`BrowserEngine`类,它初始化配置数据,根据配置启动指定的浏览器,并提供关闭浏览器的方法。测试脚本`TestLaunchBrowser`使用`BrowserEngine`来启动浏览器并执行测试。整个框架允许在不同浏览器上运行相同的测试,以确保兼容性和一致性。
47 3
|
1月前
|
存储 Web App开发 Java
《手把手教你》系列基础篇(九十五)-java+ selenium自动化测试-框架之设计篇-java实现自定义日志输出(详解教程)
【7月更文挑战第13天】这篇文章介绍了如何在Java中创建一个简单的自定义日志系统,以替代Log4j或logback。
136 5
|
1月前
|
Java 数据安全/隐私保护
Java无模版导出Excel 0基础教程
经常写数据导出到EXCEL,没有模板的情况下使用POI技术。以此作为记录,以后方便使用。 2 工具类 样式工具: 处理工具Java接口 水印工具 导出Excel工具类 3 测试代码 与实际复杂业务不同 在此我们只做模拟 Controller Service 4 导出测试 使用Postman进行接口测试,没接触过Postman的小伙伴可以看我这篇博客Postman导出excel文件保存为文件可以看到导出很成功,包括水印 sheet页名称自适应宽度。还有一些高亮……等功能可以直接搜索使用
Java无模版导出Excel 0基础教程
|
1月前
|
设计模式 测试技术 Python
《手把手教你》系列基础篇(九十二)-java+ selenium自动化测试-框架设计基础-POM设计模式简介(详解教程)
【7月更文挑战第10天】Page Object Model (POM)是Selenium自动化测试中的设计模式,用于提高代码的可读性和维护性。POM将每个页面表示为一个类,封装元素定位和交互操作,使得测试脚本与页面元素分离。当页面元素改变时,只需更新对应页面类,减少了脚本的重复工作和维护复杂度,有利于团队协作。POM通过创建页面对象,管理页面元素集合,将业务逻辑与元素定位解耦合,增强了代码的复用性。示例展示了不使用POM时,脚本直接混杂了元素定位和业务逻辑,而POM则能解决这一问题。
43 6
|
1月前
|
设计模式 Java 测试技术
《手把手教你》系列基础篇(九十四)-java+ selenium自动化测试-框架设计基础-POM设计模式实现-下篇(详解教程)
【7月更文挑战第12天】在本文中,作者宏哥介绍了如何在不使用PageFactory的情况下,用Java和Selenium实现Page Object Model (POM)。文章通过一个百度首页登录的实战例子来说明。首先,创建了一个名为`BaiduHomePage1`的页面对象类,其中包含了页面元素的定位和相关操作方法。接着,创建了测试类`TestWithPOM1`,在测试类中初始化WebDriver,设置驱动路径,最大化窗口,并调用页面对象类的方法进行登录操作。这样,测试脚本保持简洁,遵循了POM模式的高可读性和可维护性原则。
27 2