Java 中文官方教程 2022 版(三)(3)

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

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

局部类和匿名类

有两种额外的内部类。您可以在方法体内声明一个内部类。这些类被称为局部类。您还可以在方法体内声明一个没有命名的内部类。这些类被称为匿名类。

修饰符

您可以为内部类使用与外部类的其他成员相同的修饰符。例如,您可以使用访问修饰符privatepublicprotected来限制对内部类的访问,就像您用它们来限制对其他类成员的访问一样。

本地类

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

本地类是在中定义的类,块是在平衡大括号之间的零个或多个语句组成的组。通常在方法体中定义本地类。

本节涵盖以下主题:

  • 声明本地类
  • 访问封闭类的成员
  • 遮蔽和本地类
  • 本地类类似于内部类

声明本地类

您可以在任何块中定义本地类(请参阅表达式、语句和块了解更多信息)。例如,您可以在方法体、for循环或if子句中定义本地类。

以下示例,LocalClassExample,验证两个电话号码。它在方法validatePhoneNumber中定义了本地类PhoneNumber

public class LocalClassExample {
    static String regularExpression = "[⁰-9]";
    public static void validatePhoneNumber(
        String phoneNumber1, String phoneNumber2) {
        final int numberLength = 10;
        // Valid in JDK 8 and later:
        // int numberLength = 10;
        class PhoneNumber {
            String formattedPhoneNumber = null;
            PhoneNumber(String phoneNumber){
                // numberLength = 7;
                String currentNumber = phoneNumber.replaceAll(
                  regularExpression, "");
                if (currentNumber.length() == numberLength)
                    formattedPhoneNumber = currentNumber;
                else
                    formattedPhoneNumber = null;
            }
            public String getNumber() {
                return formattedPhoneNumber;
            }
            // Valid in JDK 8 and later:
//            public void printOriginalNumbers() {
//                System.out.println("Original numbers are " + phoneNumber1 +
//                    " and " + phoneNumber2);
//            }
        }
        PhoneNumber myNumber1 = new PhoneNumber(phoneNumber1);
        PhoneNumber myNumber2 = new PhoneNumber(phoneNumber2);
        // Valid in JDK 8 and later:
//        myNumber1.printOriginalNumbers();
        if (myNumber1.getNumber() == null) 
            System.out.println("First number is invalid");
        else
            System.out.println("First number is " + myNumber1.getNumber());
        if (myNumber2.getNumber() == null)
            System.out.println("Second number is invalid");
        else
            System.out.println("Second number is " + myNumber2.getNumber());
    }
    public static void main(String... args) {
        validatePhoneNumber("123-456-7890", "456-7890");
    }
}

该示例通过首先从电话号码中删除除数字 0 到 9 之外的所有字符来验证电话号码。然后,它检查电话号码是否恰好包含十个数字(北美电话号码的长度)。该示例打印如下内容:

First number is 1234567890
Second number is invalid

访问封闭类的成员

本地类可以访问其封闭类的成员。在前面的示例中,PhoneNumber构造函数访问成员LocalClassExample.regularExpression

此外,本地类可以访问局部变量。但是,本地类只能访问声明为 final 的局部变量。当本地类访问封闭块的局部变量或参数时,它会捕获该变量或参数。例如,PhoneNumber构造函数可以访问局部变量numberLength,因为它声明为 final;numberLength是一个捕获的变量

然而,从 Java SE 8 开始,本地类可以访问封闭块中的局部变量和参数,这些变量是 final 或有效地 final。一旦初始化后值不会改变的变量或参数是有效地 final。例如,假设变量numberLength没有声明为 final,并且您在PhoneNumber构造函数中添加了突出显示的赋值语句以将有效电话号码的长度更改为 7 位:

PhoneNumber(String phoneNumber) {
    numberLength = 7;
    String currentNumber = phoneNumber.replaceAll(
        regularExpression, "");
    if (currentNumber.length() == numberLength)
        formattedPhoneNumber = currentNumber;
    else
        formattedPhoneNumber = null;
}

由于这个赋值语句,变量numberLength不再是有效地 final。因此,当内部类PhoneNumber尝试访问numberLength变量时,Java 编译器生成类似于"从内部类引用的局部变量必须是 final 或有效地 final"的错误消息:

if (currentNumber.length() == numberLength)

从 Java SE 8 开始,如果你在方法中声明局部类,它可以访问方法的参数。例如,你可以在PhoneNumber局部类中定义以下方法:

public void printOriginalNumbers() {
    System.out.println("Original numbers are " + phoneNumber1 +
        " and " + phoneNumber2);
}

方法printOriginalNumbers访问方法validatePhoneNumber的参数phoneNumber1phoneNumber2

遮蔽和局部类

在局部类中声明的类型(如变量)会遮蔽在外部作用域中具有相同名称的声明。更多信息请参见 Shadowing。

局部类类似于内部类

局部类类似于内部类,因为它们不能定义或声明任何静态成员。在静态方法中的局部类,比如在静态方法validatePhoneNumber中定义的PhoneNumber类,只能引用封闭类的静态成员。例如,如果你没有将成员变量regularExpression定义为静态的,那么 Java 编译器会生成类似“非静态变量regularExpression无法从静态上下文中引用”的错误。

局部类是非静态的,因为它们可以访问封闭块的实例成员。因此,它们不能包含大多数类型的静态声明。

你不能在块内部声明接口;接口本质上是静态的。例如,以下代码摘录不会编译,因为接口HelloThere是在方法greetInEnglish的主体内定义的:

public void greetInEnglish() {
        interface HelloThere {
           public void greet();
        }
        class EnglishHelloThere implements HelloThere {
            public void greet() {
                System.out.println("Hello " + name);
            }
        }
        HelloThere myGreeting = new EnglishHelloThere();
        myGreeting.greet();
    }

你不能在局部类中声明静态初始化程序或成员接口。以下代码摘录不会编译,因为方法EnglishGoodbye.sayGoodbye被声明为static。当编译器遇到这个方法定义时,会生成类似“修饰符’static’仅允许在常量变量声明中使用”的错误:

public void sayGoodbyeInEnglish() {
        class EnglishGoodbye {
            public static void sayGoodbye() {
                System.out.println("Bye bye");
            }
        }
        EnglishGoodbye.sayGoodbye();
    }

局部类可以拥有静态成员,前提是它们是常量变量。(常量变量是指声明为 final 并用编译时常量表达式初始化的原始类型或String类型的变量。编译时常量表达式通常是一个可以在编译时评估的字符串或算术表达式。更多信息请参见理解类成员。)以下代码摘录可以编译,因为静态成员EnglishGoodbye.farewell是一个常量变量:

public void sayGoodbyeInEnglish() {
        class EnglishGoodbye {
            public static final String farewell = "Bye bye";
            public void sayGoodbye() {
                System.out.println(farewell);
            }
        }
        EnglishGoodbye myEnglishGoodbye = new EnglishGoodbye();
        myEnglishGoodbye.sayGoodbye();
    }

匿名类

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

匿名类使您的代码更加简洁。它们使您能够同时声明和实例化一个类。它们类似于本地类,只是没有名称。如果您只需要使用本地类一次,请使用它们。

本节涵盖以下主题:

  • 声明匿名类
  • 匿名类的语法
  • 访问封闭范围的本地变量,并声明和访问匿名类的成员
  • 匿名类示例

声明匿名类

虽然本地类是类声明,匿名类是表达式,这意味着你在另一个表达式中定义类。以下示例,HelloWorldAnonymousClasses,在本地变量frenchGreetingspanishGreeting的初始化语句中使用了匿名类,但在变量englishGreeting的初始化中使用了本地类:

public class HelloWorldAnonymousClasses {
    interface HelloWorld {
        public void greet();
        public void greetSomeone(String someone);
    }
    public void sayHello() {
        class EnglishGreeting implements HelloWorld {
            String name = "world";
            public void greet() {
                greetSomeone("world");
            }
            public void greetSomeone(String someone) {
                name = someone;
                System.out.println("Hello " + name);
            }
        }
        HelloWorld englishGreeting = new EnglishGreeting();
        HelloWorld frenchGreeting = new HelloWorld() {
            String name = "tout le monde";
            public void greet() {
                greetSomeone("tout le monde");
            }
            public void greetSomeone(String someone) {
                name = someone;
                System.out.println("Salut " + name);
            }
        };
        HelloWorld spanishGreeting = new HelloWorld() {
            String name = "mundo";
            public void greet() {
                greetSomeone("mundo");
            }
            public void greetSomeone(String someone) {
                name = someone;
                System.out.println("Hola, " + name);
            }
        };
        englishGreeting.greet();
        frenchGreeting.greetSomeone("Fred");
        spanishGreeting.greet();
    }
    public static void main(String... args) {
        HelloWorldAnonymousClasses myApp =
            new HelloWorldAnonymousClasses();
        myApp.sayHello();
    }            
}

匿名类的语法

如前所述,匿名类是一个表达式。匿名类表达式的语法类似于构造函数的调用,只是其中包含一个代码块中的类定义。

考虑frenchGreeting对象的实例化:

HelloWorld frenchGreeting = new HelloWorld() {
            String name = "tout le monde";
            public void greet() {
                greetSomeone("tout le monde");
            }
            public void greetSomeone(String someone) {
                name = someone;
                System.out.println("Salut " + name);
            }
        };

匿名类表达式包括以下内容:

  • new运算符
  • 要实现的接口名称或要扩展的类名称。在此示例中,匿名类正在实现接口HelloWorld
  • 包含传递给构造函数的参数的括号,就像普通的类实例创建表达式一样。注意:当您实现一个接口时,没有构造函数,所以您使用一个空的括号对,就像这个例子中一样。
  • 一个类声明体。更具体地说,在类体中,允许方法声明,但不允许语句。

因为匿名类定义是一个表达式,所以它必须是语句的一部分。在此示例中,匿名类表达式是实例化frenchGreeting对象的语句的一部分。(这就解释了为什么在右括号后有一个分号。)

访问封闭范围的本地变量,并声明和访问匿名类的成员

像本地类一样,匿名类可以捕获变量;它们对封闭范围的本地变量具有相同的访问权限:

  • 匿名类可以访问其封闭类的成员。
  • 匿名类无法访问其封闭范围中未声明为final或有效final的本地变量。
  • 像嵌套类一样,在匿名类中声明类型(如变量)会遮蔽封闭范围中具有相同名称的任何其他声明。有关更多信息,请参阅遮蔽。

匿名类在成员方面与局部类具有相同的限制:

  • 您不能在匿名类中声明静态初始化程序或成员接口。
  • 一个匿名类可以有静态成员,只要它们是常量变量。

请注意,您可以在匿名类中声明以下内容:

  • 字段
  • 额外的方法(即使它们不实现超类型的任何方法)
  • 实例初始化程序
  • 局部类

然而,你不能在匿名类中声明构造函数。

匿名类示例

匿名类经常用于图形用户界面(GUI)应用程序。

考虑 JavaFX 示例HelloWorld.java(来自Hello World, JavaFX Style部分,取自Getting Started with JavaFX)。此示例创建一个包含**Say ‘Hello World’**按钮的框架。匿名类表达式被突出显示:

import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class HelloWorld extends Application {
    public static void main(String[] args) {
        launch(args);
    }
    @Override
    public void start(Stage primaryStage) {
        primaryStage.setTitle("Hello World!");
        Button btn = new Button();
        btn.setText("Say 'Hello World'");
        btn.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent event) {
                System.out.println("Hello World!");
            }
        });
        StackPane root = new StackPane();
        root.getChildren().add(btn);
        primaryStage.setScene(new Scene(root, 300, 250));
        primaryStage.show();
    }
}

在此示例中,方法调用btn.setOnAction指定了当您选择**Say ‘Hello World’**按钮时会发生什么。此方法需要一个EventHandler类型的对象。EventHandler接口只包含一个方法,即 handle。该示例使用匿名类表达式而不是使用新类来实现此方法。请注意,此表达式是传递给btn.setOnAction方法的参数。

因为EventHandler接口只包含一个方法,所以您可以使用 lambda 表达式代替匿名类表达式。有关更多信息,请参阅 Lambda 表达式部分。

匿名类非常适合实现包含两个或更多方法的接口。以下 JavaFX 示例来自自定义 UI 控件部分。突出显示的代码创建一个仅接受数字值的文本字段。它通过覆盖从TextInputControl类继承的replaceTextreplaceSelection方法,使用匿名类重新定义了TextField类的默认实现。

import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
public class CustomTextFieldSample extends Application {
    final static Label label = new Label();
    @Override
    public void start(Stage stage) {
        Group root = new Group();
        Scene scene = new Scene(root, 300, 150);
        stage.setScene(scene);
        stage.setTitle("Text Field Sample");
        GridPane grid = new GridPane();
        grid.setPadding(new Insets(10, 10, 10, 10));
        grid.setVgap(5);
        grid.setHgap(5);
        scene.setRoot(grid);
        final Label dollar = new Label("$");
        GridPane.setConstraints(dollar, 0, 0);
        grid.getChildren().add(dollar);
        final TextField sum = new TextField() {
            @Override
            public void replaceText(int start, int end, String text) {
                if (!text.matches("[a-z, A-Z]")) {
                    super.replaceText(start, end, text); 
                }
                label.setText("Enter a numeric value");
            }
            @Override
            public void replaceSelection(String text) {
                if (!text.matches("[a-z, A-Z]")) {
                    super.replaceSelection(text);
                }
            }
        };
        sum.setPromptText("Enter the total");
        sum.setPrefColumnCount(10);
        GridPane.setConstraints(sum, 1, 0);
        grid.getChildren().add(sum);
        Button submit = new Button("Submit");
        GridPane.setConstraints(submit, 2, 0);
        grid.getChildren().add(submit);
        submit.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent e) {
                label.setText(null);
            }
        });
        GridPane.setConstraints(label, 0, 1);
        GridPane.setColumnSpan(label, 3);
        grid.getChildren().add(label);
        scene.setRoot(grid);
        stage.show();
    }
    public static void main(String[] args) {
        launch(args);
    }
}

Lambda 表达式

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

匿名类的一个问题是,如果您的匿名类的实现非常简单,例如只包含一个方法的接口,那么匿名类的语法可能显得笨拙和不清晰。在这些情况下,通常您试图将功能作为参数传递给另一个方法,例如当某人单击按钮时应执行什么操作。Lambda 表达式使您能够做到这一点,将功能视为方法参数,或将代码视为数据。

前一节,匿名类,向您展示了如何实现一个没有名称的基类。尽管这通常比具有名称的类更简洁,但对于只有一个方法的类来说,即使是匿名类似乎也有点过多和繁琐。Lambda 表达式让您更简洁地表达单方法类的实例。

本节涵盖以下主题:

  • Lambda 表达式的理想使用情况
  • 方法 1:创建搜索符合一个特征的成员的方法
  • 方法 2:创建更通用的搜索方法
  • 方法 3:在本地类中指定搜索条件代码
  • 方法 4:在匿名类中指定搜索条件代码
  • 方法 5:使用 Lambda 表达式指定搜索条件代码
  • 方法 6:使用 Lambda 表达式与标准功能接口
  • 方法 7:在整个应用程序中使用 Lambda 表达式
  • 方法 8:更广泛地使用泛型
  • 方法 9:使用接受 Lambda 表达式作为参数的聚合操作
  • GUI 应用程序中的 Lambda 表达式
  • Lambda 表达式的语法
  • 访问封闭范围的局部变量
  • 目标类型
  • 目标类型和方法参数
  • 序列化

Lambda 表达式的理想使用情况

假设您正在创建一个社交网络应用程序。您希望创建一个功能,使管理员能够对满足特定条件的社交网络应用程序成员执行任何类型的操作,例如发送消息。以下表格详细描述了这种用例:

字段 描述
名称 对所选成员执行操作
主要执行者 管理员
前提条件 管理员已登录到系统。
后置条件 操作仅在符合指定条件的成员上执行。
主要成功场景
  1. 管理员指定要执行某个操作的成员的条件。
  2. 管理员指定对所选成员执行的操作。
  3. 管理员选择提交按钮。
  4. 系统找到所有符合指定条件的成员。
  5. 系统对所有匹配成员执行指定操作。

|

扩展 1a. 管理员在指定执行操作或选择提交按钮之前有选项预览符合指定条件的成员。
出现频率 一天中多次。

假设这个社交网络应用程序的成员由以下Person类表示:

public class Person {
    public enum Sex {
        MALE, FEMALE
    }
    String name;
    LocalDate birthday;
    Sex gender;
    String emailAddress;
    public int getAge() {
        // ...
    }
    public void printPerson() {
        // ...
    }
}

假设您的社交网络应用程序的成员存储在List实例中。

本节从一个简单的方法开始处理这种用例。它通过本地和匿名类改进了这种方法,然后以使用 lambda 表达式的高效简洁方法结束。在示例RosterTest中找到本节描述的代码摘录。

方法 1:创建搜索符合一个特征的成员的方法

一个简单的方法是创建几种方法;每种方法搜索符合一个特征的成员,例如性别或年龄。以下方法打印比指定年龄更老的成员:

public static void printPersonsOlderThan(List<Person> roster, int age) {
    for (Person p : roster) {
        if (p.getAge() >= age) {
            p.printPerson();
        }
    }
}

注意List是一个有序的Collection集合是将多个元素组合成单个单元的对象。集合用于存储、检索、操作和传递聚合数据。有关集合的更多信息,请参阅 Collections 教程。

这种方法可能会使您的应用程序变得脆弱,这是应用程序由于引入更新(如新数据类型)而无法工作的可能性。假设您升级了应用程序并更改了Person类的结构,使其包含不同的成员变量;也许该类使用不同的数据类型或算法记录和测量年龄。您将不得不重写大量 API 以适应这种变化。此外,这种方法是不必要地限制性的;例如,如果您想打印比某个年龄更年轻的成员会怎样?

Java 中文官方教程 2022 版(三)(4)https://developer.aliyun.com/article/1486284

相关文章
|
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 共存(一)

热门文章

最新文章