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

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

方法引用

原文:docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html

你可以使用 lambda 表达式来创建匿名方法。然而,有时候 lambda 表达式仅仅是调用一个已存在的方法。在这种情况下,通过名称引用现有方法通常更清晰。方法引用使你能够做到这一点;它们是紧凑、易读的 lambda 表达式,用于已经有名称的方法。

再次考虑在 lambda 表达式部分讨论的Person类:

public class Person {
    // ...
    LocalDate birthday;
    public int getAge() {
        // ...
    }
    public LocalDate getBirthday() {
        return birthday;
    }   
    public static int compareByAge(Person a, Person b) {
        return a.birthday.compareTo(b.birthday);
    }
    // ...
}

假设你的社交网络应用的成员被包含在一个数组中,并且你想按年龄对数组进行排序。你可以使用以下代码(在示例MethodReferencesTest中找到本节描述的代码片段):

Person[] rosterAsArray = roster.toArray(new Person[roster.size()]);
class PersonAgeComparator implements Comparator<Person> {
    public int compare(Person a, Person b) {
        return a.getBirthday().compareTo(b.getBirthday());
    }
}
Arrays.sort(rosterAsArray, new PersonAgeComparator());

此次调用sort的方法签名如下:

static <T> void sort(T[] a, Comparator<? super T> c)

注意Comparator接口是一个函数式接口。因此,你可以使用 lambda 表达式来代替定义并创建一个实现Comparator的类的新实例:

Arrays.sort(rosterAsArray,
    (Person a, Person b) -> {
        return a.getBirthday().compareTo(b.getBirthday());
    }
);

然而,比较两个Person实例的出生日期的方法Person.compareByAge已经存在。你可以在 lambda 表达式的主体中调用这个方法:

Arrays.sort(rosterAsArray,
    (a, b) -> Person.compareByAge(a, b)
);

因为这个 lambda 表达式调用了一个已存在的方法,你可以使用方法引用代替 lambda 表达式:

Arrays.sort(rosterAsArray, Person::compareByAge);

方法引用Person::compareByAge在语义上与 lambda 表达式(a, b) -> Person.compareByAge(a, b)相同。它们各自具有以下特征:

  • 其形式参数列表是从Comparator.compare复制的,即(Person, Person)
  • 其主体调用方法Person.compareByAge

方法引用的种类

有四种方法引用的种类:

种类 语法 示例
引用静态方法 *ContainingClass*::*staticMethodName* Person::compareByAge MethodReferencesExamples::appendStrings
引用特定对象的实例方法 *containingObject*::*instanceMethodName* myComparisonProvider::compareByName myApp::appendStrings2
引用特定类型的任意对象的实例方法 *ContainingType*::*methodName* String::compareToIgnoreCase String::concat
引用构造函数 *ClassName*::new HashSet::new

以下示例,MethodReferencesExamples,包含了前三种方法引用的示例:

import java.util.function.BiFunction;
public class MethodReferencesExamples {
    public static <T> T mergeThings(T a, T b, BiFunction<T, T, T> merger) {
        return merger.apply(a, b);
    }
    public static String appendStrings(String a, String b) {
        return a + b;
    }
    public String appendStrings2(String a, String b) {
        return a + b;
    }
    public static void main(String[] args) {
        MethodReferencesExamples myApp = new MethodReferencesExamples();
        // Calling the method mergeThings with a lambda expression
        System.out.println(MethodReferencesExamples.
            mergeThings("Hello ", "World!", (a, b) -> a + b));
        // Reference to a static method
        System.out.println(MethodReferencesExamples.
            mergeThings("Hello ", "World!", MethodReferencesExamples::appendStrings));
        // Reference to an instance method of a particular object        
        System.out.println(MethodReferencesExamples.
            mergeThings("Hello ", "World!", myApp::appendStrings2));
        // Reference to an instance method of an arbitrary object of a
        // particular type
        System.out.println(MethodReferencesExamples.
            mergeThings("Hello ", "World!", String::concat));
    }
}

所有的System.out.println()语句都打印相同的内容:Hello World!

BiFunctionjava.util.function包中许多函数接口之一。BiFunction函数接口可以表示接受两个参数并产生结果的 lambda 表达式或方法引用。

静态方法引用

方法引用Person::compareByAgeMethodReferencesExamples::appendStrings是对静态方法的引用。

引用特定对象的实例方法

下面是引用特定对象实例方法的示例:

class ComparisonProvider {
    public int compareByName(Person a, Person b) {
        return a.getName().compareTo(b.getName());
    }
    public int compareByAge(Person a, Person b) {
        return a.getBirthday().compareTo(b.getBirthday());
    }
}
ComparisonProvider myComparisonProvider = new ComparisonProvider();
Arrays.sort(rosterAsArray, myComparisonProvider::compareByName);

方法引用myComparisonProvider::compareByName调用myComparisonProvider对象的compareByName方法。JRE 推断方法类型参数,本例中为(Person, Person)

类似地,方法引用myApp::appendStrings2将调用myApp对象的appendStrings2方法。JRE 推断方法类型参数,本例中为(String, String)

引用特定类型任意对象的实例方法

下面是一个引用特定类型任意对象的实例方法的示例:

String[] stringArray = { "Barbara", "James", "Mary", "John",
    "Patricia", "Robert", "Michael", "Linda" };
Arrays.sort(stringArray, String::compareToIgnoreCase);

方法引用String::compareToIgnoreCase的等效 lambda 表达式将具有形式参数列表(String a, String b),其中ab是用于更好描述此示例的任意名称。方法引用将调用a.compareToIgnoreCase(b)方法。

类似地,方法引用String::concat将调用a.concat(b)方法。

构造函数引用

你可以通过使用名称new来引用构造函数,与引用静态方法的方式相同。以下方法将元素从一个集合复制到另一个集合:

public static <T, SOURCE extends Collection<T>, DEST extends Collection<T>>
    DEST transferElements(
        SOURCE sourceCollection,
        Supplier<DEST> collectionFactory) {
    DEST result = collectionFactory.get();
    for (T t : sourceCollection) {
        result.add(t);
    }
    return result;
}

函数接口Supplier包含一个名为get的方法,不接受参数并返回一个对象。因此,你可以使用 lambda 表达式调用方法transferElements,如下所示:

Set<Person> rosterSetLambda =
    transferElements(roster, () -> { return new HashSet<>(); });

你可以使用构造函数引用来替代 lambda 表达式,如下所示:

Set<Person> rosterSet = transferElements(roster, HashSet::new);

Java 编译器推断你想要创建一个包含类型为Person的元素的HashSet集合。或者,你可以按照以下方式指定:

Set<Person> rosterSet = transferElements(roster, HashSet<Person>::new);

何时使用嵌套类、局部类、匿名类和 Lambda 表达式

原文:docs.oracle.com/javase/tutorial/java/javaOO/whentouse.html

如在嵌套类一节中所述,嵌套类使您能够逻辑地将仅在一个地方使用的类分组,增加封装的使用,并创建更易读和可维护的代码。局部类、匿名类和 Lambda 表达式也具有这些优点;但是,它们旨在用于更具体的情况:

  • 局部类:如果需要创建一个类的多个实例、访问其构造函数或引入一个新的命名类型(例如,因为您需要稍后调用其他方法),请使用它。
  • 匿名类:如果需要声明字段或额外方法,请使用它。
  • Lambda 表达式:
  • 如果您要封装要传递给其他代码的单个行为单元,请使用它。例如,如果您希望对集合的每个元素执行某个操作,当进程完成时,或者当进程遇到错误时,您将使用 Lambda 表达式。
  • 如果需要一个功能接口的简单实例,并且前述条件均不适用(例如,您不需要构造函数、命名类型、字段或额外方法),请使用它。
  • 嵌套类:如果您的需求类似于局部类,并且希望使类型更广泛可用,且不需要访问局部变量或方法参数时,请使用它。
  • 如果需要访问封闭实例的非公共字段和方法,请使用非静态嵌套类(或内部类)。如果不需要此访问权限,请使用静态嵌套类。

问题和练习:嵌套类

原文:docs.oracle.com/javase/tutorial/java/javaOO/QandE/nested-questions.html

问题

  1. 程序Problem.java无法编译。你需要做什么才能使其编译?为什么?
  2. 使用 Java API 文档中Box类(位于javax.swing包中)的文档来帮助回答以下问题。
  1. Box定义了哪个静态嵌套类?
  2. Box定义了哪个内部类?
  3. Box的内部类的超类是什么?
  4. 从任何类中可以使用Box的哪些嵌套类?
  5. 如何创建BoxFiller类的实例?

练习

  1. 获取文件Class1.java。编译并运行Class1。输出是什么?
  2. 以下练习涉及修改类DataStructure.java,该类在内部类示例部分讨论。
  1. 定义一个名为print(DataStructureIterator iterator)的方法。使用EvenIterator类的实例调用此方法,使其执行与printEven方法相同的功能。
  2. 调用方法print(DataStructureIterator iterator),使其打印具有奇数索引值的元素。使用匿名类作为方法的参数,而不是接口DataStructureIterator的实例。
  3. 定义一个名为print(java.util.function.Function iterator)的方法,执行与print(DataStructureIterator iterator)相同的功能。使用 lambda 表达式调用此方法,以打印具有偶数索引值的元素。再次使用 lambda 表达式调用此方法,以打印具有奇数索引值的元素。
  4. 定义两个方法,使得以下两个语句打印具有偶数索引值和具有奇数索引值的元素:
DataStructure ds = new DataStructure()
// ...
ds.print(DataStructure::isEvenIndex);
ds.print(DataStructure::isOddIndex);

检查你的答案。

枚举类型

原文:docs.oracle.com/javase/tutorial/java/javaOO/enum.html

枚举类型是一种特殊的数据类型,允许变量成为一组预定义的常量之一。变量必须等于为其预定义的值之一。常见示例包括罗盘方向(NORTH、SOUTH、EAST 和 WEST 的值)和一周的天数。

由于它们是常量,枚举类型字段的名称必须是大写字母。

在 Java 编程语言中,您可以使用enum关键字定义枚举类型。例如,您可以指定一个星期几的枚举类型如下:

public enum Day {
    SUNDAY, MONDAY, TUESDAY, WEDNESDAY,
    THURSDAY, FRIDAY, SATURDAY 
}

每当需要表示一组固定常量时,都应该使用枚举类型。这包括自然枚举类型,如我们太阳系中的行星和在编译时知道所有可能值的数据集,例如菜单上的选项、命令行标志等。

这里是一些代码,向您展示如何使用上面定义的Day枚举:

public class EnumTest {
    Day day;
    public EnumTest(Day day) {
        this.day = day;
    }
    public void tellItLikeItIs() {
        switch (day) {
            case MONDAY:
                System.out.println("Mondays are bad.");
                break;
            case FRIDAY:
                System.out.println("Fridays are better.");
                break;
            case SATURDAY: case SUNDAY:
                System.out.println("Weekends are best.");
                break;
            default:
                System.out.println("Midweek days are so-so.");
                break;
        }
    }
    public static void main(String[] args) {
        EnumTest firstDay = new EnumTest(Day.MONDAY);
        firstDay.tellItLikeItIs();
        EnumTest thirdDay = new EnumTest(Day.WEDNESDAY);
        thirdDay.tellItLikeItIs();
        EnumTest fifthDay = new EnumTest(Day.FRIDAY);
        fifthDay.tellItLikeItIs();
        EnumTest sixthDay = new EnumTest(Day.SATURDAY);
        sixthDay.tellItLikeItIs();
        EnumTest seventhDay = new EnumTest(Day.SUNDAY);
        seventhDay.tellItLikeItIs();
    }
}

输出为:

Mondays are bad.
Midweek days are so-so.
Fridays are better.
Weekends are best.
Weekends are best.

Java 编程语言的枚举类型比其他语言中的对应类型更强大。enum声明定义了一个(称为枚举类型)。枚举类体可以包括方法和其他字段。编译器在创建枚举时会自动添加一些特殊方法。例如,它们具有一个静态values方法,返回一个包含枚举值的数组,按照它们声明的顺序排列。此方法通常与 for-each 结构结合使用,以遍历枚举类型的值。例如,下面Planet类示例中的代码遍历太阳系中的所有行星。

for (Planet p : Planet.values()) {
    System.out.printf("Your weight on %s is %f%n",
                      p, p.surfaceWeight(mass));
}

注意: 所有枚举隐式扩展java.lang.Enum。因为一个类只能扩展一个父类(参见声明类),Java 语言不支持状态的多重继承(参见状态、实现和类型的多重继承),因此枚举不能扩展其他任何内容。


在下面的示例中,Planet是一个表示太阳系行星的枚举类型。它们定义了常量质量和半径属性。

每个枚举常量都声明了质量和半径参数的值。这些值在创建常量时传递给构造函数。Java 要求常量在任何字段或方法之前定义。此外,当存在字段和方法时,枚举常量列表必须以分号结尾。


注意: 枚举类型的构造函数必须是包私有或私有访问。它会自动创建在枚举体开头定义的常量。您不能自己调用枚举构造函数。


除了其属性和构造函数外,Planet 还有一些方法,可以让你获取每个行星上物体的表面重力和重量。以下是一个示例程序,它接受你在地球上的体重(以任何单位)并计算并打印出你在所有行星上的体重(以相同单位):

public enum Planet {
    MERCURY (3.303e+23, 2.4397e6),
    VENUS   (4.869e+24, 6.0518e6),
    EARTH   (5.976e+24, 6.37814e6),
    MARS    (6.421e+23, 3.3972e6),
    JUPITER (1.9e+27,   7.1492e7),
    SATURN  (5.688e+26, 6.0268e7),
    URANUS  (8.686e+25, 2.5559e7),
    NEPTUNE (1.024e+26, 2.4746e7);
    private final double mass;   // in kilograms
    private final double radius; // in meters
    Planet(double mass, double radius) {
        this.mass = mass;
        this.radius = radius;
    }
    private double mass() { return mass; }
    private double radius() { return radius; }
    // universal gravitational constant  (m3 kg-1 s-2)
    public static final double G = 6.67300E-11;
    double surfaceGravity() {
        return G * mass / (radius * radius);
    }
    double surfaceWeight(double otherMass) {
        return otherMass * surfaceGravity();
    }
    public static void main(String[] args) {
        if (args.length != 1) {
            System.err.println("Usage: java Planet <earth_weight>");
            System.exit(-1);
        }
        double earthWeight = Double.parseDouble(args[0]);
        double mass = earthWeight/EARTH.surfaceGravity();
        for (Planet p : Planet.values())
           System.out.printf("Your weight on %s is %f%n",
                             p, p.surfaceWeight(mass));
    }
}

如果你在命令行中运行 Planet.class 并带上参数 175,你会得到以下输出:

$ java Planet 175
Your weight on MERCURY is 66.107583
Your weight on VENUS is 158.374842
Your weight on EARTH is 175.000000
Your weight on MARS is 66.279007
Your weight on JUPITER is 442.847567
Your weight on SATURN is 186.552719
Your weight on URANUS is 158.397260
Your weight on NEPTUNE is 199.207413

问题和练习:枚举类型

原文:docs.oracle.com/javase/tutorial/java/javaOO/QandE/enum-questions.html

问题

  1. 真或假:Enum类型可以是java.lang.String的子类。

练习

  1. 重写问题和练习:类中的Card类,使其使用枚举类型表示卡牌的等级和花色。
  2. 重写Deck类。

检查你的答案。

课程:注解

原文:docs.oracle.com/javase/tutorial/java/annotations/index.html

注解,一种元数据形式,提供关于程序的数据,这些数据不是程序本身的一部分。注解对其注释的代码的操作没有直接影响。

注解有多种用途,其中包括:

  • 编译器的信息 — 编译器可以使用注解来检测错误或抑制警告。
  • 编译时和部署时处理 — 软件工具可以处理注解信息以生成代码、XML 文件等。
  • 运行时处理 — 一些注解可以在运行时被检查。

本课程解释了注解可以在哪里使用,如何应用注解,在 Java 平台标准版(Java SE API)中有哪些预定义的注解类型可用,如何将类型注解与可插入类型系统结合使用以编写具有更强类型检查的代码,以及如何实现重复注解。

注解基础知识

原文:docs.oracle.com/javase/tutorial/java/annotations/basics.html

注解的格式

在其最简单的形式下,注解看起来像下面这样:

@Entity

符号@告诉编译器接下来是一个注解。在下面的例子中,注解的名称是Override

@Override
void mySuperMethod() { ... }

注解可以包括元素,这些元素可以是命名的或未命名的,并且这些元素有值:

@Author(
   name = "Benjamin Franklin",
   date = "3/27/2003"
)
class MyClass { ... }

@SuppressWarnings(value = "unchecked")
void myMethod() { ... }

如果只有一个名为value的元素,则名称可以省略,如:

@SuppressWarnings("unchecked")
void myMethod() { ... }

如果注解没有元素,则括号可以省略,如前面的@Override示例所示。

也可以在同一声明上使用多个注解:

@Author(name = "Jane Doe")
@EBook
class MyClass { ... }

如果注解具有相同的类型,则称为重复注解:

@Author(name = "Jane Doe")
@Author(name = "John Smith")
class MyClass { ... }

从 Java SE 8 发布开始支持重复注解。更多信息,请参见重复注解。

注解类型可以是 Java SE API 的java.langjava.lang.annotation包中定义的类型之一。在前面的示例中,OverrideSuppressWarnings是预定义的 Java 注解。还可以定义自己的注解类型。前面示例中的AuthorEbook注解是自定义注解类型。

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

相关文章
|
7月前
|
JavaScript NoSQL Java
接替此文【下篇-服务端+后台管理】优雅草蜻蜓z系统JAVA版暗影版为例-【蜻蜓z系列通用】-2025年全新项目整合搭建方式-这是独立吃透代码以后首次改变-独立PC版本vue版搭建教程-优雅草卓伊凡
接替此文【下篇-服务端+后台管理】优雅草蜻蜓z系统JAVA版暗影版为例-【蜻蜓z系列通用】-2025年全新项目整合搭建方式-这是独立吃透代码以后首次改变-独立PC版本vue版搭建教程-优雅草卓伊凡
361 96
接替此文【下篇-服务端+后台管理】优雅草蜻蜓z系统JAVA版暗影版为例-【蜻蜓z系列通用】-2025年全新项目整合搭建方式-这是独立吃透代码以后首次改变-独立PC版本vue版搭建教程-优雅草卓伊凡
|
3月前
|
Oracle Java 关系型数据库
java 编程基础入门级超级完整版教程详解
这份文档是针对Java编程入门学习者的超级完整版教程,涵盖了从环境搭建到实际项目应用的全方位内容。首先介绍了Java的基本概念与开发环境配置方法,随后深入讲解了基础语法、控制流程、面向对象编程的核心思想,并配以具体代码示例。接着探讨了常用类库与API的应用,如字符串操作、集合框架及文件处理等。最后通过一个学生成绩管理系统的实例,帮助读者将理论知识应用于实践。此外,还提供了进阶学习建议,引导学员逐步掌握更复杂的Java技术。适合初学者系统性学习Java编程。资源地址:[点击访问](https://pan.quark.cn/s/14fcf913bae6)。
305 2
|
8月前
|
消息中间件 Java 数据库
自研Java框架 Sunrays-Framework使用教程「博客之星」
### Sunrays-Framework:助力高效开发的Java微服务框架 **Sunrays-Framework** 是一款基于 Spring Boot 构建的高效微服务开发框架,深度融合了 Spring Cloud 生态中的核心技术组件。它旨在简化数据访问、缓存管理、消息队列、文件存储等常见开发任务,帮助开发者快速构建高质量的企业级应用。 #### 核心功能 - **MyBatis-Plus**:简化数据访问层开发,提供强大的 CRUD 操作和分页功能。 - **Redis**:实现高性能缓存和分布式锁,提升系统响应速度。 - **RabbitMQ**:可靠的消息队列支持,适用于异步
自研Java框架 Sunrays-Framework使用教程「博客之星」
|
9月前
|
移动开发 前端开发 Java
Java最新图形化界面开发技术——JavaFx教程(含UI控件用法介绍、属性绑定、事件监听、FXML)
JavaFX是Java的下一代图形用户界面工具包。JavaFX是一组图形和媒体API,我们可以用它们来创建和部署富客户端应用程序。 JavaFX允许开发人员快速构建丰富的跨平台应用程序,允许开发人员在单个编程接口中组合图形,动画和UI控件。本文详细介绍了JavaFx的常见用法,相信读完本教程你一定有所收获!
8410 5
Java最新图形化界面开发技术——JavaFx教程(含UI控件用法介绍、属性绑定、事件监听、FXML)
|
8月前
|
Java 数据库连接 数据处理
探究Java异常处理【保姆级教程】
Java 异常处理是确保程序稳健运行的关键机制。它通过捕获和处理运行时错误,避免程序崩溃。Java 的异常体系以 `Throwable` 为基础,分为 `Error` 和 `Exception`。前者表示严重错误,后者可细分为受检和非受检异常。常见的异常处理方式包括 `try-catch-finally`、`throws` 和 `throw` 关键字。此外,还可以自定义异常类以满足特定需求。最佳实践包括捕获具体异常、合理使用 `finally` 块和谨慎抛出异常。掌握这些技巧能显著提升程序的健壮性和可靠性。
133 4
|
8月前
|
存储 移动开发 算法
【潜意识Java】Java基础教程:从零开始的学习之旅
本文介绍了 Java 编程语言的基础知识,涵盖从简介、程序结构到面向对象编程的核心概念。首先,Java 是一种高级、跨平台的面向对象语言,支持“一次编写,到处运行”。接着,文章详细讲解了 Java 程序的基本结构,包括包声明、导入语句、类声明和 main 方法。随后,深入探讨了基础语法,如数据类型、变量、控制结构、方法和数组。此外,还介绍了面向对象编程的关键概念,例如类与对象、继承和多态。最后,针对常见的编程错误提供了调试技巧,并总结了学习 Java 的重要性和方法。适合初学者逐步掌握 Java 编程。
145 1
|
9月前
|
NoSQL Java 关系型数据库
Liunx部署java项目Tomcat、Redis、Mysql教程
本文详细介绍了如何在 Linux 服务器上安装和配置 Tomcat、MySQL 和 Redis,并部署 Java 项目。通过这些步骤,您可以搭建一个高效稳定的 Java 应用运行环境。希望本文能为您在实际操作中提供有价值的参考。
556 26
|
8月前
|
前端开发 Java 开发工具
Git使用教程-将idea本地Java等文件配置到gitte上【保姆级教程】
本内容详细介绍了使用Git进行版本控制的全过程,涵盖从本地仓库创建到远程仓库配置,以及最终推送代码至远程仓库的步骤。
415 0
|
9月前
|
安全 Java 编译器
Kotlin教程笔记(27) -Kotlin 与 Java 共存(二)
Kotlin教程笔记(27) -Kotlin 与 Java 共存(二)
|
9月前
|
Java 开发工具 Android开发
Kotlin教程笔记(26) -Kotlin 与 Java 共存(一)
Kotlin教程笔记(26) -Kotlin 与 Java 共存(一)

热门文章

最新文章