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

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

字符和字符串总结

原文:docs.oracle.com/javase/tutorial/java/data/stringsummary.html

大多数情况下,如果您使用单个字符值,您将使用基本的char类型。然而,有时您需要将 char 用作对象—例如,作为期望对象的方法参数。Java 编程语言为此提供了一个包装类,将char包装在Character对象中。Character类型的对象包含一个类型为char的单个字段。这个Character类还提供了许多有用的类(即静态)方法来操作字符。

字符串是字符序列,在 Java 编程中被广泛使用。在 Java 编程语言中,字符串是对象。String类有 60 多个方法和 13 个构造函数。

最常见的是,您可以使用类似于以下语句创建一个字符串

String s = "Hello world!";

而不是使用其中一个String构造函数。

String类有许多方法可以查找和检索子字符串;然后可以使用+连接运算符将它们轻松重新组装成新的字符串。

String类还包括许多实用方法,其中包括split()toLowerCase()toUpperCase()valueOf()。后者方法在将用户输入的字符串转换为数字时是不可或缺的。Number子类还有将字符串转换为数字以及反之的方法。

除了String类之外,还有一个StringBuilder类。与字符串一起工作相比,使用StringBuilder对象有时可能更有效率。StringBuilder类提供了一些对字符串有用的方法,其中包括reverse()。然而,总的来说,String类具有更广泛的方法。

可以使用StringBuilder构造函数将字符串转换为字符串构建器。可以使用toString()方法将字符串构建器转换为字符串。

自动装箱和拆箱

原文:docs.oracle.com/javase/tutorial/java/data/autoboxing.html

自动装箱是 Java 编译器在原始类型和其对应的对象包装类之间进行的自动转换。例如,将int转换为Integer,将double转换为Double等。如果转换反向进行,则称为拆箱

这是自动装箱的最简单示例:

Character ch = 'a';

本节中的其余示例使用泛型。如果您还不熟悉泛型的语法,请参阅泛型(更新)课程。

考虑以下代码:

List<Integer> li = new ArrayList<>();
for (int i = 1; i < 50; i += 2)
    li.add(i);

尽管您将int值作为原始类型而不是Integer对象添加到li中,但代码仍然可以编译。因为liInteger对象的列表,而不是int值的列表,您可能会想知道为什么 Java 编译器没有发出编译时错误。编译器不会生成错误,因为它从i创建一个Integer对象并将该对象添加到li中。因此,编译器在运行时将前面的代码转换为以下代码:

List<Integer> li = new ArrayList<>();
for (int i = 1; i < 50; i += 2)
    li.add(Integer.valueOf(i));

将原始值(例如int)转换为相应包装类(Integer)的对象称为自动装箱。当原始值是以下情况时,Java 编译器会应用自动装箱:

  • 作为传递给期望相应包装类对象的方法的参数。
  • 赋给相应包装类的变量。

考虑以下方法:

public static int sumEven(List<Integer> li) {
    int sum = 0;
    for (Integer i: li)
        if (i % 2 == 0)
            sum += i;
        return sum;
}

因为余数(%)和一元加号(+=)运算符不适用于Integer对象,您可能会想知道为什么 Java 编译器在不发出任何错误的情况下编译该方法。编译器不会生成错误,因为它在运行时调用intValue方法将Integer转换为int

public static int sumEven(List<Integer> li) {
    int sum = 0;
    for (Integer i : li)
        if (i.intValue() % 2 == 0)
            sum += i.intValue();
        return sum;
}

将包装类型的对象(Integer)转换为其对应的原始类型(int)值称为拆箱。当包装类的对象是以下情况时,Java 编译器会应用拆箱:

  • 作为传递给期望相应原始类型值的方法的参数。
  • 赋给相应原始类型的变量。

Unboxing示例展示了这是如何工作的:

import java.util.ArrayList;
import java.util.List;
public class Unboxing {
    public static void main(String[] args) {
        Integer i = new Integer(-8);
        // 1\. Unboxing through method invocation
        int absVal = absoluteValue(i);
        System.out.println("absolute value of " + i + " = " + absVal);
        List<Double> ld = new ArrayList<>();
        ld.add(3.1416);    // Π is autoboxed through method invocation.
        // 2\. Unboxing through assignment
        double pi = ld.get(0);
        System.out.println("pi = " + pi);
    }
    public static int absoluteValue(int i) {
        return (i < 0) ? -i : i;
    }
}

该程序打印如下内容:

absolute value of -8 = 8
pi = 3.1416

自动装箱和拆箱使开发人员编写更清晰的代码,使其更易于阅读。以下表列出了原始类型及其对应的包装类,这些包装类由 Java 编译器用于自动装箱和拆箱:

Primitive type Wrapper class
boolean Boolean
byte Byte
char Character
float Float
int Integer
long Long
short Short
double Double

问题和练习:字符和字符串

原文:docs.oracle.com/javase/tutorial/java/data/QandE/characters-questions.html

问题

  1. 以下字符串构建器的初始容量是多少?
StringBuilder sb = new StringBuilder("Able was I ere I saw Elba.");
  1. 考虑以下字符串:
String hannah = "Did Hannah see bees? Hannah did.";
  1. 表达式hannah.length()显示的值是多少?
  2. 方法调用hannah.charAt(12)返回的值是多少?
  3. 编写一个表达式,引用hannah所指的字符串中的字母b
  1. 以下表达式返回的字符串有多长?这个字符串是什么?
"Was it a car or a cat I saw?".substring(9, 12)
  1. 在下面的程序中,名为ComputeResult,每个编号行执行后result的值是多少?
public class ComputeResult {
    public static void main(String[] args) {
        String original = "software";
        StringBuilder result = new StringBuilder("hi");
        int index = original.indexOf('a');
/*1*/   result.setCharAt(0, original.charAt(0));
/*2*/   result.setCharAt(1, original.charAt(original.length()-1));
/*3*/   result.insert(1, original.charAt(4));
/*4*/   result.append(original.substring(1,4));
/*5*/   result.insert(3, (original.substring(index, index+2) + " ")); 
        System.out.println(result);
    }
}

练习

  1. 展示两种方法将以下两个字符串连接在一起以得到字符串"Hi, mom."
String hi = "Hi, ";
String mom = "mom.";
  1. 编写一个程序,从你的全名中计算出你的缩写并显示出来。
  2. 一个变位词是由另一个单词或短语的字母重新排列而成的单词或短语;例如,“parliament”是“partial men”的变位词,“software”是“swear oft”的变位词。编写一个程序,判断一个字符串是否是另一个字符串的变位词。该程序应忽略空格和标点符号。

检查你的答案。

Lesson: 泛型(更新)

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

在任何非平凡的软件项目中,错误都是生活中不可避免的事实。仔细的规划、编程和测试可以帮助减少它们的普遍性,但不知何故,它们总会找到一种方式悄悄地潜入你的代码中。随着新功能的引入和代码库规模与复杂性的增长,这一点变得尤为明显。

幸运的是,有些错误比其他错误更容易检测。例如,编译时错误可以在早期被检测出来;你可以利用编译器的错误消息来找出问题所在并立即修复它。然而,运行时错误可能会更加棘手;它们并不总是立即显现,而且当它们出现时,可能是在程序中与问题实际原因相距甚远的地方。

泛型通过在编译时使更多的错误可检测,为你的代码增加了稳定性。完成本课程后,你可能想继续学习 Gilad Bracha 的《泛型》教程。

为什么使用泛型?

原文:docs.oracle.com/javase/tutorial/java/generics/why.html

简而言之,泛型使类型(类和接口)在定义类、接口和方法时成为参数。就像在方法声明中使用的更熟悉的形式参数一样,类型参数提供了一种方式让您可以重复使用相同的代码以不同的输入。不同之处在于,形式参数的输入是值,而类型参数的输入是类型。

使用泛型的代码比不使用泛型的代码有许多好处:

  • 编译时进行更强的类型检查。
    Java 编译器对泛型代码应用强类型检查,如果代码违反类型安全性,则会发出错误。修复编译时错误比修复运行时错误更容易,后者可能很难找到。
  • 消除强制类型转换。
    不使用泛型的以下代码片段需要进行强制类型转换:
List list = new ArrayList();
list.add("hello");
String s = (String) list.get(0);
  • 当重写为使用泛型时,代码不需要进行强制类型转换:
List<String> list = new ArrayList<String>();
list.add("hello");
String s = list.get(0);   // no cast
  • 使程序员能够实现泛型算法。
    通过使用泛型,程序员可以实现适用于不同类型集合的泛型算法,可以进行定制,并且是类型安全且更易阅读的。

泛型类型

原文:docs.oracle.com/javase/tutorial/java/generics/types.html

泛型类型是一个参数化类型的泛型类或接口。下面的 Box 类将被修改以演示这个概念。

一个简单的 Box 类

首先看一下一个操作任意类型对象的非泛型 Box 类。它只需要提供两个方法:set,用于向盒子中添加对象,和 get,用于检索对象:

public class Box {
    private Object object;
    public void set(Object object) { this.object = object; }
    public Object get() { return object; }
}

由于它的方法接受或返回一个 Object,你可以自由传入任何你想要的东西,只要不是原始类型之一。在编译时无法验证类的使用方式。代码的一部分可能将一个 Integer 放入盒子中,并期望从中获取 Integer,而代码的另一部分可能错误地传入一个 String,导致运行时错误。

Box 类的泛型版本

一个泛型类的定义格式如下:

class name<T1, T2, ..., Tn> { /* ... */ }

类型参数部分,由尖括号(<>)界定,跟在类名后面。它指定了类型参数(也称为类型变量T1T2、… 和 Tn

要将 Box 类更新为使用泛型,你需要通过将代码 “public class Box” 更改为 “public class Box” 来创建一个泛型类型声明。这引入了类型变量 T,可以在类内部的任何地方使用。

有了这个改变,Box 类变成了:

/**
 * Generic version of the Box class.
 * @param <T> the type of the value being boxed
 */
public class Box<T> {
    // T stands for "Type"
    private T t;
    public void set(T t) { this.t = t; }
    public T get() { return t; }
}

正如你所看到的,所有 Object 的出现都被 T 替换。类型变量可以是你指定的任何非原始类型:任何类类型、任何接口类型、任何数组类型,甚至是另一个类型变量。

这种技术也可以应用于创建泛型接口。

类型参数命名约定

按照惯例,类型参数的名称是单个大写字母。这与你已经了解的变量命名约定形成鲜明对比,而且有充分的理由:没有这个约定,很难区分类型变量和普通类或接口名称。

最常用的类型参数名称有:

  • E - Element(Java 集合框架广泛使用)
  • K - Key
  • N - Number
  • T - Type
  • V - Value
  • S,U,V 等 - 第二、第三、第四种类型

你将在 Java SE API 和本课程的其余部分中看到这些名称的使用。

调用和实例化泛型类型

要在代码中引用泛型 Box 类,你必须执行一个泛型类型调用,将 T 替换为某个具体值,比如 Integer

Box<Integer> integerBox;

你可以将泛型类型调用看作类似于普通方法调用,但是不是向方法传递参数,而是向 Box 类本身传递一个类型参数 — 在本例中是 Integer


类型参数和类型参数术语: 许多开发人员将“类型参数”和“类型参数”这两个术语互换使用,但这两个术语并不相同。在编码时,为了创建参数化类型,需要提供类型参数。因此,在Foo中,T是类型参数,而在Foo f中的String是类型参数。本课程在使用这些术语时遵守此定义。


与任何其他变量声明一样,此代码实际上并不创建新的Box对象。它只是声明integerBox将保存对“Box of Integer”的引用,这就是Box的含义。

通常将泛型类型的调用称为参数化类型

要实例化此类,像往常一样使用new关键字,但是在类名和括号之间放置

Box<Integer> integerBox = new Box<Integer>();

钻石

在 Java SE 7 及更高版本中,只要编译器可以从上下文中确定或推断出类型参数,就可以用空类型参数集(<>)替换调用泛型类构造函数所需的类型参数。这一对尖括号<>非正式地称为钻石。例如,您可以使用以下语句创建Box的实例:

Box<Integer> integerBox = new Box<>();

有关钻石符号和类型推断的更多信息,请参见类型推断。

多个类型参数

如前所述,泛型类可以具有多个类型参数。例如,实现泛型Pair接口的泛型OrderedPair类:

public interface Pair<K, V> {
    public K getKey();
    public V getValue();
}
public class OrderedPair<K, V> implements Pair<K, V> {
    private K key;
    private V value;
    public OrderedPair(K key, V value) {
  this.key = key;
  this.value = value;
    }
    public K getKey() { return key; }
    public V getValue() { return value; }
}

以下语句创建了OrderedPair类的两个实例:

Pair<String, Integer> p1 = new OrderedPair<String, Integer>("Even", 8);
Pair<String, String>  p2 = new OrderedPair<String, String>("hello", "world");

代码new OrderedPair实例化KStringVInteger。因此,OrderedPair的构造函数的参数类型分别为StringInteger。由于自动装箱,将Stringint传递给类是有效的。

如钻石中所述,因为 Java 编译器可以从声明OrderedPair中推断出KV类型,所以可以使用钻石符号缩短这些语句:

OrderedPair<String, Integer> p1 = new OrderedPair<>("Even", 8);
OrderedPair<String, String>  p2 = new OrderedPair<>("hello", "world");

要创建泛型接口,遵循与创建泛型类相同的约定。

参数化类型

您还可以用参数化类型(即List)替换类型参数(即KV)。例如,使用OrderedPair示例:

OrderedPair<String, Box<Integer>> p = new OrderedPair<>("primes", new Box<Integer>(...));

原始类型

原文:docs.oracle.com/javase/tutorial/java/generics/rawTypes.html

原始类型是没有任何类型参数的泛型类或接口的名称。例如,给定泛型Box类:

public class Box<T> {
    public void set(T t) { /* ... */ }
    // ...
}

要创建Box的参数化类型,您需要为形式类型参数T提供实际类型参数:

Box<Integer> intBox = new Box<>();

如果省略实际类型参数,则创建Box的原始类型:

Box rawBox = new Box();

因此,Box是泛型类型Box的原始类型。但是,非泛型类或接口类型是原始类型。

在旧代码中会出现原始类型,因为在 JDK 5.0 之前,许多 API 类(如Collections类)都不是泛型的。使用原始类型时,实际上获得的是泛型之前的行为 —— Box会给您Object。为了向后兼容,允许将参数化类型分配给其原始类型:

Box<String> stringBox = new Box<>();
Box rawBox = stringBox;               // OK

但是,如果将原始类型赋给参数化类型,则会收到警告:

Box rawBox = new Box();           // rawBox is a raw type of Box<T>
Box<Integer> intBox = rawBox;     // warning: unchecked conversion

如果使用原始类型调用相应泛型类型中定义的泛型方法,也会收到警告:

Box<String> stringBox = new Box<>();
Box rawBox = stringBox;
rawBox.set(8);  // warning: unchecked invocation to set(T)

此警告显示原始类型绕过了泛型类型检查,将不安全代码的捕获推迟到运行时。因此,应避免使用原始类型。

类型擦除部分提供了有关 Java 编译器如何使用原始类型的更多信息。

未经检查的错误消息

如前所述,在将旧代码与泛型代码混合使用时,可能会遇到类似以下的警告消息:

Note: Example.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.

当使用在原始类型上操作的旧 API 时,可能会出现以下示例中所示的情况:

public class WarningDemo {
    public static void main(String[] args){
        Box<Integer> bi;
        bi = createBox();
    }
    static Box createBox(){
        return new Box();
    }
}

“未经检查”一词表示编译器没有足够的类型信息来执行确保类型安全所需的所有类型检查。默认情况下,“未经检查”警告是禁用的,尽管编译器会给出提示。要查看所有“未经检查”警告,请使用-Xlint:unchecked重新编译。

使用-Xlint:unchecked重新编译前面的示例,会显示以下额外信息:

WarningDemo.java:4: warning: [unchecked] unchecked conversion
found   : Box
required: Box<java.lang.Integer>
        bi = createBox();
                      ^
1 warning

要完全禁用未经检查的警告,请使用-Xlint:-unchecked标志。@SuppressWarnings("unchecked")注解可以抑制未经检查的警告。如果您不熟悉@SuppressWarnings语法,请参阅注解。

泛型方法

原文:docs.oracle.com/javase/tutorial/java/generics/methods.html

泛型方法 是引入自己类型参数的方法。这类似于声明一个泛型类型,但类型参数的范围仅限于声明它的方法。允许静态和非静态泛型方法,以及泛型类构造方法。

泛型方法的语法包括一个类型参数列表,在方法返回类型之前出现在尖括号内。对于静态泛型方法,类型参数部分必须出现在方法返回类型之前。

Util 类包含一个泛型方法 compare,用于比较两个 Pair 对象:

public class Util {
    public static <K, V> boolean compare(Pair<K, V> p1, Pair<K, V> p2) {
        return p1.getKey().equals(p2.getKey()) &&
               p1.getValue().equals(p2.getValue());
    }
}
public class Pair<K, V> {
    private K key;
    private V value;
    public Pair(K key, V value) {
        this.key = key;
        this.value = value;
    }
    public void setKey(K key) { this.key = key; }
    public void setValue(V value) { this.value = value; }
    public K getKey()   { return key; }
    public V getValue() { return value; }
}

调用这个方法的完整语法如下:

Pair<Integer, String> p1 = new Pair<>(1, "apple");
Pair<Integer, String> p2 = new Pair<>(2, "pear");
boolean same = Util.<Integer, String>compare(p1, p2);

类型已经明确提供,如粗体所示。通常情况下,这部分可以省略,编译器会推断所需的类型:

Pair<Integer, String> p1 = new Pair<>(1, "apple");
Pair<Integer, String> p2 = new Pair<>(2, "pear");
boolean same = Util.compare(p1, p2);

这个特性被称为类型推断,允许您将泛型方法作为普通方法调用,而无需在尖括号之间指定类型。这个主题在下一节 类型推断 中进一步讨论。

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

相关文章
|
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