原文:Java 9 Recipes
协议:CC BY-NC-SA 4.0
一、Java 9 入门
在这一章中,我们提供了一些方法来帮助刚接触 Java 语言的程序员以及那些有其他语言经验的程序员熟悉 Java 9。您将学习安装 Java,还将安装一个集成开发环境(IDE ),从中您将开发应用并试验本书中提供的解决方案。你将学习 Java 的基础知识,比如如何创建一个类,以及如何接受键盘输入。文档经常被忽视,但是在这一章中,你将很快学会如何为你的 Java 代码创建优秀的文档。
注意
Java 9 食谱并不是一个完整的教程。相反,它涵盖了 Java 语言的关键概念。如果你真的是 Java 新手,我们推荐你购买和阅读由 Apress 出版的许多入门 Java 书籍中的一本。
1-1.创建开发环境
问题
您想安装 Java 并尝试使用这种语言。您还希望有一个合理的 IDE 来使用它。
解决办法
安装 Java 开发工具包 9 (JDK。这给了你语言和编译器。然后安装 NetBeans IDE,以提供更高效的工作环境。
Java 标准版(Java SE)对于这本书里的大部分食谱来说已经足够了。要下载该版本,请访问甲骨文技术网(OTN)的以下页面:
图 1-1 显示了下载选项卡,你可以在页面显著位置看到 Java 平台下载链接和图片。该链接旁边是 NetBeans IDE 的图像,它提供了将 JDK 和 NetBeans 一起下载的选项。选择您喜欢的选项,下载适用于您的平台的版本,然后运行安装向导进行安装。出于本书的目的,我使用 NetBeans IDE 8.2。
图 1-1。OTN 上的 Java SE 下载页面
注意
如果您选择只安装 Java 平台(JDK)而不安装 NetBeans,您可以稍后访问 netbeans.org 下载 NetBeans。
它是如何工作的
名称Java是甲骨文公司的商标。语言本身是开源的,它的发展由一个叫做 Java 社区进程 (JCP)的进程控制。你可以在 www.jcp.org 的了解更多关于这个过程的信息。
虽然这种语言本身并不归甲骨文公司所有,但它的核心开发往往是由甲骨文公司主导的。甲骨文公司经营着 jcp.org 的 JCP,并拥有其域名。
Java 有很多版本,比如移动版(ME)和企业版(EE)。Java SE 是标准版,代表了语言的核心。我们在本书中为 Java SE 程序员构建了食谱。那些对为 Raspberry Pi 等设备开发嵌入式应用感兴趣的人可能有兴趣了解更多关于 Java ME 的知识。类似地,那些对开发 web 应用和使用企业解决方案感兴趣的人可能会有兴趣学习更多关于 Java EE 的知识。
注意
企业开发人员可能想要购买并阅读乔希·朱诺号(Apress,2013)的《Java EE 7 食谱》。
有几个不错的网站,您可以访问它们来了解更多关于 Java 的知识,并了解该平台的最新动态。OTN 上的以下页面是开始学习所有 Java 知识的好地方:
该页面提供的丰富资源一开始可能会让人不知所措,但是值得您花时间四处看看,顺便熟悉一下许多可用的链接。
其中一个链接指向 Java SE,它会带你进入图 1-1 中所示的页面。您可以从那里下载 Java SE 和 NetBeans IDE。从那里,您还可以访问官方文档、论坛和时事通讯等社区资源,以及旨在帮助您积累 Java 知识并获得该语言认证的培训资源。
1-2.进入“你好,世界”
问题
您已经安装了 Java SE 9 和 NetBeans IDE。现在您想运行一个简单的 Java 程序来验证您的安装是否正常工作。
解决办法
首先打开 NetBeans IDE。您应该会看到一个类似于图 1-2 中的工作区。如果您已经在 IDE 中处理过项目,您可能会在左侧窗格中看到一些项目。
图 1-2。打开 NetBeans IDE
转到文件菜单并选择新建项目。您将看到图 1-3 中的对话框。选择 Java 类别,然后选择 Java 应用。点击下一步进入如图 1-4 所示的对话框。
图 1-3。创建新的 Java SE 项目
图 1-4。命名项目
为您的项目命名。对于与本书相关的项目,使用名称 Java9Recipes。在图 1-4 对话框顶部的项目名称文本框中输入项目名称。
然后在“创建主类”文本框中指定主类的名称。给出以下名称:
确保您已经输入了项目名和类名,就像我们在这里提供的一样,因为要遵循的代码取决于您这样做。确保“项目名称”文本框指定 Java9Recipes。确保“创建主类”文本框指定 org . Java 9 recipes . chapter 01 . recipe 1 _ 02 . hello world。
小费
注意大小写;Java 是区分大小写的。
按“完成”以完成向导并创建一个框架项目。您现在应该看到一个 Java 源文件。框架代码已经生成,您的 NetBeans IDE 窗口应该类似于图 1-5 中的窗口。
图 1-5。查看 NetBeans 生成的框架代码
将光标放在源代码窗格中的任意位置。按 Ctrl-A 选择所有的骨架代码。然后按 Delete 键删除它。用清单 1-1 中的代码替换删除的代码。
您可以在清单 1-1 中找到代码,作为本书示例下载的一部分。有两个名为 HelloMessage.java 和 HelloWorld.java 的文件,它们位于名为 org . Java 9 recipes . chapter 01 . recipe 1 _ 02 的 Java 包中。请注意,本书中所有配方溶液都在下载的例子中。
第一个类 HelloMessage 是一个容器类,用于保存基于字符串的消息。
清单 1-1。一个“你好,世界”的例子
确保您已经粘贴(或键入)了清单 1-1 中的代码。编译并运行该程序,您应该会看到以下输出:
此输出将出现在一个名为“输出”的新窗格中,该窗格由 NetBeans 在 IDE 窗口的底部打开。
它是如何工作的
您可以使用本菜谱中显示的通用技术运行本章中几乎所有的解决方案。出于这个原因,我们已经煞费苦心地详细说明了这一点,只展示了一步一步的截图。
包装
解决方案示例首先创建一个 Java 包:
包是将相关类组合到一个共享名称空间中的一种方式。这个想法是通过以相反的顺序沿着你的组织的域名前进来实现普遍的唯一性。习惯上也是用小写来写包名。
NetBeans 将创建一个目录结构来模拟您的包路径。在这种情况下,NetBeans 创建了以下目录路径:
以下是关于此路径的一些注意事项:
- 前面部分是 C:Users… netbeans 项目。除非您另外指定,否则 NetBeans 会在 NetbeansProject 目录下创建所有项目,您可以从图 1-4 中的对话框中进行指定。许多开发人员指定较短的路径。
- 接下来是 Java9Recipes 的第一次出现。该事件对应于您在图 1-4 的项目名称文本框中填写的项目名称。
- 您创建的任何源文件都进入 src 目录。NetBeans 在此级别创建其他目录。例如,NetBeans 创建一个构建目录,然后在其下是一个 classes 子目录来保存您编译的类文件。
- 最后是镜像您指定的包路径的目录,在本例中是 org Java 9 recipes chapter 01 recipe 1 _ 02。编译代码时,在 buildclasses 目录下会创建一个相同的结构。请注意,如果使用另一个 IDE,您可能会看到创建的目录有所不同。
您不需要显式创建包。如果您没有创建,Java 编译器会为您创建一个,并给它一个隐藏的名称。我们喜欢直截了当,你也应该这样。在专业环境中,考虑周全并明确 Java 包名是必要的。在开发任何重要的应用时,组织以及明智选择的命名约定都很重要。
JavaBeans 风格的类
接下来,在解决方案示例中,您将看到一个遵循 JavaBeans 模式的类定义。HelloMessage 的定义遵循了您在 Java 编程中经常遇到的模式,因此我们包含了它。这个类很简单,能够保存一个名为 message 的字符串字段。
该类定义了三种方法:
- HelloMessage()。此方法也称为构造函数,与类同名。在这种情况下,它不需要任何参数。每当你创建一个新的类对象时,它都会被自动调用。请注意,这被称为“无参数”构造函数,因为它是在类中类型化的,并且不带任何参数。如果不提供构造函数,JVM 将自动提供一个默认的构造函数(也没有参数)。
- setMessage(字符串)。这个访问器方法以单词 set 开始。它需要一个参数。它指定由相应的 get 方法返回的消息。
- getMessage()。这个访问器方法返回当前定义的消息。在我们的例子中,我们选择大写消息。
注意
JavaBeans 类中使用访问器方法来访问任何私有声明的类成员。在这种情况下,可以使用这些方法访问标识为 message 的私有变量。访问器方法通常被称为“getters”和“setters”
以 set 和 get 开头的方法被称为 setter 和 getter 方法。变量 message 是类私有的,这意味着您不能从类外部直接访问消息。
你会在课上看到关键词 this。它是 Java 中的一个特殊关键字,用来引用当前对象。它的使用在清单 1-1 中是多余的,但是如果任何一个方法碰巧创建了它们自己的变量,并且这些变量也被命名为 message,那么就需要使用它。通常的做法是使用“this”关键字从“getter”和“setter”方法中引用类成员。
在 Java 中,通过 setter 和 getter 方法来协调对类变量的访问是很常见的,就像我们示例中的方法一样。这些方法代表了与其他类和你的主程序的某种契约。它们的好处是您可以随意更改 HelloMessage 的存储实现。只要您保持 setMessage()和 getMessage()的外部行为,依赖于 HelloMessage 的其他代码将继续正常工作。
主程序
咒语公共静态空主(…)在公共类中用来表示 Java 程序的入口点。该声明开始了一个名为 main 的可执行方法。您必须指定一个字符串数组参数,通常该参数被定义为 String[] args。
当您执行当前选定的类时,NetBeans 会将代码编译成一组二进制文件,然后将控制权转移给 main()方法。NetBeans 也可以配置为在保存时重新编译,这将导致控制权转移到 main()方法。该方法依次执行以下操作:
- 执行 HelloMessage 以创建一个名为 hm 的变量,该变量能够保存 HelloMessage 类的实例。变量 hm 此时为空。
- 调用新的 HelloMessage()以该名称创建该类的对象。将执行无参数构造函数,现在“默认消息”被设置为问候文本。新对象现在存储在变量 hm 中。
- 调用 System.out.println()以显示对象的无参数构造函数确实已按预期执行。问候语“默认消息”显示在“输出”窗格中。
- 将消息设置为传统文本“Hello,World”。
- 再次调用 System.out.println()以输出刚刚设置的新消息。现在你可以看到“HELLO,WORLD”这个问候语被添加到了“输出”窗格中。
解决方案中的模式在 Java 编程中很常见。main()方法是执行开始的地方。使用 new 运算符定义变量,创建对象。通常使用 setter 和 getter 方法来设置和检索对象变量。
小费
命令行应用已经过时了。系统管理员和程序员有时将它们作为实用程序来编写,或者批量处理大量数据。但是总的来说,今天的大多数应用都是 GUI 应用。JavaFX 是编写标准桌面应用的前进方向,你可以在第 14 到 16 章中了解到它。Recipe 14-1 以 GUI 形式提供了本质上是“Hello,World”的应用。JavaEE 提供了为 Java 平台开发基于 web 的应用的选项,你可以在第十七章中了解更多。
1-3.配置类路径
问题
您想要执行一个 Java 程序,或者在您正在执行的应用中包含一个外部 Java 库。
解决办法
将 CLASSPATH 变量设置为用户定义的 Java 类或 Java 归档(JAR)文件的目录位置,您需要访问这些文件来执行应用。假设您在 OS 驱动器的根目录下有一个名为 JAVA_DEV 的目录,您的应用需要访问的所有文件都位于这个目录中。如果是这种情况,您应该执行如下命令:
或者在 Unix 和 Linux 系统上:
或者,javac 命令提供了一个选项,用于指定需要为应用加载的资源的位置。在所有平台上,使用这种技术设置类路径可以通过-classpath 选项来完成,如下所示:
当然,在 Microsoft Windows 机器上,文件路径将使用反斜杠()来代替。
注意
可以使用 javac–CP 选项,而不是指定–class path 选项。
它是如何工作的
Java 实现了类路径的概念。这是一个目录搜索路径,可以使用 CLASSPATH 环境变量在系统范围内指定。还可以通过 java 命令的-classpath 选项为 JVM 的特定调用指定类路径。(参见配方 1-4 中的示例。)
注意
对于未来的许多 Java 应用来说,类路径肯定仍然很重要。然而,Java 9 中引入的新模块系统取代了那些利用模块化构建的应用使用脆弱类路径的需要。参见第二十二章了解更多关于 Java 模块化的信息。
当执行 Java 程序时,JVM 使用以下搜索顺序根据需要查找和加载类:
- 这些类是 Java 平台的基础,包含在 Java 安装目录中。
- 位于 JDK 的扩展目录中的任何包或 JAR 文件。
- 在指定的类路径上加载的包、类、JAR 文件和库。
您可能需要为一个应用访问多个目录或 JAR 文件。如果您的依赖项位于多个位置,就会出现这种情况。为此,只需使用操作系统的分隔符(;或者:)作为 CLASSPATH 变量指定的位置之间的分隔符。以下是在 Unix 和 Linux 系统上的 CLASSPATH 环境变量中指定多个 JAR 文件的示例:
或者,您可以通过命令行选项指定类路径:
当加载 Java 应用的资源时,JVM 加载第一个位置指定的所有类和包,然后是第二个位置,依此类推。这一点很重要,因为在某些情况下,加载的顺序可能会有所不同。
注意
JAR 文件用于将应用和 Java 库打包成可分发的格式。如果您没有以那种方式打包您的应用,您可以简单地指定您的。类文件驻留在。
有时,您会希望将所有 JAR 文件包含在一个指定的目录中。为此,请在包含文件的目录后指定通配符(*)。例如:
指定通配符将告诉 JVM 它应该只加载 JAR 文件。它不会加载位于用通配符指定的目录中的类文件。如果还需要这些类文件,您需要为同一个目录指定一个单独的路径条目。例如:
不会搜索类路径中的子目录。为了加载子目录中包含的文件,这些子目录和/或文件必须在类路径中明确列出。但是,相当于子目录结构的 Java 包会被加载。因此,驻留在相当于子目录结构的 Java 包中的任何 Java 类都将被加载。
注意
组织您的代码是一个好主意;组织你在计算机上放置代码的位置也是很好的。一个好的做法是将所有的 Java 项目放在同一个目录中;它可以成为你的工作空间。将 JAR 文件中包含的所有 Java 库放在同一个目录中,以便于管理。
1-4.用包组织代码
问题
您的应用由一组 Java 类、接口和其他类型组成。您希望组织这些源文件,使它们更易于维护,并避免潜在的类命名冲突。
解决办法
创建 Java 包并将源文件放入其中,就像文件系统一样。Java 包可以用来组织应用中源文件的逻辑组。包可以帮助组织代码,减少不同类和其他 Java 类型文件之间的命名冲突,并提供访问控制。要创建一个包,只需在应用源文件夹的根目录下创建一个目录,并将其命名为。包通常相互嵌套,并且符合标准的命名约定。为了这个食谱的目的,假设该组织名为朱诺号,该组织制造小部件。要组织小部件应用的所有代码,请创建一组符合以下目录结构的嵌套包:
放在包中的任何源文件都必须包含 package 语句作为源文件的第一行。package 语句列出包含源文件的包的名称。例如,假设小部件应用的主类名为 JuneauWidgets.java。要将这个类放入名为 org.juneau 的包中,请将源文件移动到名为 juneau 的目录中,该目录位于 org 目录中,而 org 目录又位于应用的源文件夹的根目录中。目录结构应该如下所示:
JuneauWidgets.java 的资料来源如下:
源代码中的第一行包含 package 语句,该语句列出了源文件所在的包的名称。语句中列出了整个包路径,路径中的名称用点分隔。
注意
package 语句必须是 Java 源代码中列出的第一条语句。但是,在 package 语句之前可能会有一个注释或 Javadoc 注释。有关注释或 Javadoc 的更多信息,请参见配方 1-12。
一个应用可以由任意数量的包组成。如果小部件应用包含一些表示小部件对象的类,它们可以放在 org.juneau.widget 包中。该应用可以具有可用于与窗口小部件对象交互的接口。在这种情况下,可能还存在一个名为 org.juneau.interfaces 的包来包含任何这样的接口。
它是如何工作的
Java 包对于组织源文件、控制对不同类的访问以及确保没有命名冲突非常有用。包由文件系统上的一系列物理目录表示,它们可以包含任意数量的 Java 源文件。每个源文件必须在文件中的任何其他语句之前包含一个 package 语句。此 package 语句列出源文件所在的包的名称。在该配方的解决方案中,源代码包括以下程序包语句:
这个 package 语句表明源文件位于一个名为 juneau 的目录中,该目录位于另一个名为 org 的目录中。包命名约定可能因公司或组织而异。然而,重要的是单词全部小写,这样它们就不会与任何 Java 类文件名冲突。许多公司或组织将使用其域名的反义词来命名软件包。但是,如果域名包含连字符,则应使用下划线。
注意
当一个类驻留在 Java 包中时,它不再仅仅由类名引用,而是包名被添加到类名的前面,这就是所谓的完全限定的名。例如,因为驻留在文件 JuneauWidgets.java 中的类包含在 org.juneau 包中,所以使用 org.juneau.JuneauWidgets 而不仅仅是 JuneauWidgets 来引用该类。同名的类可以驻留在不同的包中(例如 org.java9recipes.JuneauWidgets)。
包对于建立安全级别和组织非常有用。默认情况下,位于同一个包中的不同类可以相互访问。如果源文件与它需要使用的另一个文件驻留在不同的包中,则必须在源文件的顶部(包语句下)声明 import 语句,以导入该另一个文件供使用;否则,必须在代码中使用完全限定的 package.class 名称。可以单独导入类,如下面的 import 语句所示:
但是,通常可能需要使用包中的所有类和类型文件。使用通配符(*)的单个 import 语句可以导入命名包中的所有文件,如下所示:
虽然可以导入所有文件,但除非绝对必要,否则不建议这样做。事实上,包含许多使用通配符的 import 语句被认为是一种糟糕的编程实践。相反,类和类型文件应该单独导入。
在包中组织类可以证明是非常有用的。假设这个配方的解决方案中描述的小部件应用为每个不同的小部件对象包含不同的 Java 类。每个小部件类都可以分组到一个名为 org.juneau.widgets 的包中。所有这些接口都可以组织成一个名为 org.juneau.interfaces 的包。
任何实际的 Java 应用都会包含包。您使用的任何 Java 库或应用编程接口(API)都包含包。当您从那些库和 API 中导入类或类型时,您实际上是在导入包。
1-5.声明变量和访问修饰符
问题
你想在你的程序中创建一些变量和操作数据。此外,您希望某些变量只对当前的类可用,而其他的变量应该对所有的类可用,或者只对当前包中的其他类JAVA 9基础语法可用。
解决办法
Java 实现了八种基本数据类型。还有对字符串类类型的特殊支持。清单 1-2 显示了每个声明的示例。从示例中提取,以声明您自己的应用中所需的变量。
清单 1-2。基本类型和字符串类型的声明
注意
如果你对清单 1-2 中的乌克兰字母感到好奇,那就是带上翻的西里尔字母Ghe。你可以在 http://en.wikipedia.org/wiki/Ghe_with_upturn 了解它的历史。你可以在图表中的找到它的码位值。当您需要查找与给定字符对应的代码点时,URL是一个很好的起点。
变量受制于可见性的概念。在清单 1-2 中创建的那些函数在创建后可以从 main()方法中看到,并且在 main()方法结束时被释放。它们在 main()方法之外没有“生命”,并且不能从 main()之外访问。
在类级别创建的变量是一个不同的故事。这样的变量可以被称为类字段或类成员,就像在字段或类成员中一样。成员的使用可以限制在声明它的类的对象、声明它的包中,或者可以从任何包中的任何类访问它。清单 1-3 展示了如何通过私有和公共关键字来控制可见性。
清单 1-3。可见度和场的概念
输出:
成员通常绑定到一个类的对象。类的每个对象都包含该类中每个成员的一个实例。然而,您也可以定义只出现一次的所谓的静态字段,并且给定类的所有实例共享一个值。清单 1-4 说明了区别。
清单 1-4。静态字段
清单 1-4 产生以下输出:
只有名为 sd1 的类实例的 oneValueForAllObjects 字段设置为 true。然而对于 sd2 来说也是如此。这是因为在声明该字段时使用了关键字 static。静态字段对其类中的所有对象只出现一次。
它是如何工作的
清单 1-2 展示了变量声明的基本格式:
在声明变量时初始化它们是很常见的,所以您会经常看到:
字段声明的前面可以有修饰符。例如:
通常将可见性修饰符——public、protected 或 private——放在第一位,但是您可以随意按任何顺序列出这些修饰符。请注意,随着您对这门语言的深入了解,您将会遇到并需要了解更多的修饰语。默认情况下,如果没有指定修饰符,该类或成员将成为包私有的,这意味着只有包中的其他类才能访问该成员。如果一个类成员被指定为 protected,那么它也是包私有的,除了它在另一个包中的子类也有访问权。
字符串类型在 Java 中是特殊的。它实际上是一个类类型,但是在语法上你可以把它当作一个原始类型。每当您用双引号(" … ")将字符串括起来时,Java 都会自动创建一个 String 对象).您不需要调用构造函数,也不需要指定 new 关键字。然而,String 是一个类,在该类中有一些方法可供您使用。清单 1-2 末尾显示的 replace()方法就是这样一种方法。
字符串由字符组成。Java 的 char 类型是一个双字节结构,用于以 Unicode-s UTF-16 编码存储单个字符。有两种方法可以生成 char 类型的文本:
- 如果一个字符很容易输入,那么用单引号将它括起来(例如:G)。
- 否则,指定以弝四位数 UTF-16 码位值(例如:’ Ґ ')。
一些 Unicode 码位需要五位数。这些不能用单个 char 值来表示。如果您需要更多关于 Unicode 和国际化的信息,请参阅第十二章。
避免对货币值使用任何原始类型。为此,尤其要避免使用任何一种浮点类型。请参考第十二章及其使用 Java Money API 计算货币金额的方法(方法 12-10)。BigDecimal 在您需要精确的固定十进制算术的任何时候都很有用。
如果您是 Java 新手,您可能不熟悉 String[]数组表示法,如示例中所示。有关阵列的更多信息,请参见第七章。它涵盖了枚举、数组以及通用数据类型。这一章中还有一些例子,展示了如何编写迭代代码来处理值的集合,比如数组。
1-6.从命令行或终端解释器编译和执行
问题
您无法安装 IDE,或者更喜欢使用标准的文本编辑器进行开发。此外,您希望从命令行或终端编译和执行 Java 程序,以便完全控制环境。
解决办法
使用 javac 命令编译您的程序。然后通过 java 命令执行它们。
首先,确保在执行路径中有 JDK 的 bin 目录。您可能需要执行如下命令之一。
Windows:
x 轴:
然后确保您的 CLASSPATH 环境变量包括包含 Java 代码的目录。以下是在 Windows 下设置环境变量的示例:
现在,将您当前的工作目录更改为与您的项目相对应的目录。菜谱 1-2 让您创建一个名为 Java9Recipes 的项目。在 Windows 系统上更改项目目录,如下所示:
向下一级进入 src 子目录:
从这里,您可以发出 javac 命令来编译项目中的任何类。将适当的包名作为路径的一部分添加到要编译的每个源文件中。一定要包括。文件名后的 java 扩展名。例如,发出以下命令来编译配方 1-2 中的 HelloWorld 类。
Windows:
x 轴:
编译完成后,您将在与相同的目录中拥有一个. class 文件。java 文件。例如,如果您执行目录列表,您应该会看到四个文件:
编译产生两个文件。一个用于 HelloMessage,另一个用于实现 main()方法的名为 HelloWorld 的类。
通过发出 java 命令调用 Java 虚拟机(JVM)来执行 main()方法。将完全限定的类名作为参数传递给命令。通过在包名前面加上前缀来限定类名,但是这次使用与源文件中相同的点符号。例如:
不要指定。在命令的末尾初始化。您现在是以类名而不是文件名引用 HelloWorld。您应该会看到与配方 1-2 相同的输出。
小费
人们必须编译源代码。源代码保存在带有. java 后缀的文件中,因此您的操作系统的文件和目录路径符号是合适的。一个执行一个类。类是语言中的一个抽象概念,所以语言的点符号变得很合适。记住这种区别有助于你记住何时使用哪种符号。
它是如何工作的
前两个解决步骤是内务处理步骤。您的执行路径中必须有 Java 编译器和虚拟机。您的程序所使用的任何类也有必要沿着所谓的类路径找到。指定类路径的一种方法是通过 class path 环境变量。有关类路径的更多信息,请参见方法 1-3。
注意
Java 模块化系统为 javac 编译器增加了几个选项。更多信息请参见第二十二章。
末尾没有 c 的命令 java 是用来执行编译好的代码的。将包含 main 方法的类的限定名作为参数传递。JVM 将解释并执行该类中的字节码,从 main 方法开始。JVM 将沿着类路径搜索任何额外需要的类,比如 HelloMessage。
编译器的默认行为是将每个生成的类文件放入保存相应源文件的目录中。您可以通过-d 选项覆盖该行为。例如:
这个命令中的-d 选项在我们自己的环境中指定一个目录作为保存生成的类文件的目标。该命令还指定源文件的完整路径和文件名。因此,无论当前的工作目录是什么,该命令都可以以相同的结果执行。
小费
配置您的系统,以便您的命令行环境在默认情况下正确设置执行路径和类路径。在基于 Linux 或 Unix 的操作系统中,典型的方法是将适当的命令放入。侧写或者。bash_profile 文件。在 Windows 下,您可以通过单击高级系统设置链接,然后单击环境变量按钮,从名为系统的控制面板窗口中指定环境变量默认值。
有时候,您可能需要为 JVM 的特定执行指定一个定制的类路径。您可以通过-cp 参数来实现,如下所示:
该执行将首先在当前工作目录(类路径中的前导点)中进行搜索,然后在与 NetBeans 放置已编译类的位置相对应的指定包目录下进行搜索。
注意
有关配置类路径的更多信息,请参见方法 1-3。
1-7.在交互式 jShell 中开发
问题
您希望编写 Java 代码并让它立即被解释,这样您就可以快速地测试、原型化和修改您的代码,而不需要等待编译或编写整个 Java 类的仪式来执行琐碎的任务。
解决办法
通过打开命令提示符或终端,并执行 jshell 实用程序,可以利用 Java 9 中新增的交互式 jShell。jshell 位于您的 JDK 主 bin 目录中,就像 java 和 javac 实用程序一样。假设 /bin 目录在类路径中,那么可以使用如下方式调用 jShell:
一旦解释器启动,就可以在 jShell 会话的生命周期内定义声明,可以立即输入和执行表达式和语句,等等。jShell 还允许 Java 开发人员通过删除多余的结构(如分号)来编写该语言的简写版本。清单 1-5 展示了 jShell 提供的一些基本功能。请记住,当您使用交互式 shell 时,如果您在任何时候需要帮助,可以键入/help 命令。
清单 1-5。交互式 jShell
如前所述,拥有交互式 shell 的一个好处是用于原型代码。在许多情况下,开发人员希望原型化类和对象。清单 1-5 展示了如何将类的代码输入到 jShell 中,以及一些在处理类时有用的命令。在构建原型时,从您最喜欢的编辑器中复制代码并粘贴到 jShell 中,然后针对它执行命令,这通常会很有帮助。
它是如何工作的
jShell 为开发人员提供了一个读取评估打印循环(REPL)环境,用于键入或粘贴代码“片段”并立即执行。与 REPL 环境或其他语言(如 Groovy、Python 和 JRuby)非常相似,jShell 为构建代码原型,甚至是动态执行存储的 Java 代码脚本提供了一个极好的环境。
jShell 允许编写缩写的 Java 代码,也称为代码片段。这可能是有益的,因为它允许人们专注于逻辑,而不是语法。最常用的快捷方式之一是能够在行尾省去分号。为了促进快速原型开发,变量可以在类之外声明,表达式和方法可以在类和接口之外动态键入,并且表达式不会留下副作用。除了动态编写代码的能力之外,jShell 还提供了一个便于在活动会话或实例中添加、修改和删除代码片段的系统。
jShell 环境的一个活动会话或实例构成了一个单独的 JShellState 。JShellState 的一个实例包括所有以前定义的变量、方法、类、导入语句等等,这些都是在同一个 jShell 会话中完成的。一旦 jShell 实例被终止,JShellState 也就结束了,因此所有的声明都丢失了。
有许多助手命令可以输入到 jShell 中,以检索关于当前 JShellState 的信息。/classes 命令列出了已经输入到当前 JShellState 中的所有类。/list 命令列出了已经键入到当前 JShellState 中的所有语句、表达式、类、方法、导入等等。/list 命令在每个清单旁边提供了一个行号,这使得用户可以通过键入/后跟您希望重新执行的行号来轻松地重新执行该行代码。因此,如果希望再次执行第 2 行,可以键入/2 再次执行该行。表 1-1 包含了 jShell 中可用命令的完整列表。
表 1-1。jShell 命令
|
命令
|
描述
|
| — | — |
| /l 或/list | 列出在当前会话中键入的源。 |
| /e 或/edit[源的名称或 id] | 打开 JShell 编辑板。可以选择键入要编辑的源条目的名称或 id。 |
| /d 或/drop[源的名称/id] | 删除由名称或 id 引用的源。 |
| /s 或/save [all|history] | 保存在当前会话中键入的源。 |
| /o 或/open | 在 jShell 中打开一个源文件。 |
| /v 或/vars | 列出当前会话中已声明的变量及其当前值。 |
| /m 或/methods | 列出已在当前会话中声明的方法。 |
| /c 或/classes | 列出当前会话中已声明的类。 |
| /x 或/exit | 退出当前 jShell 会话。 |
| /r 或/reset | 重置当前会话的 JShellState。 |
| /f 或/feedback [level] | 启动反馈-选项包括(关闭、简洁、正常、详细、默认或?). |
| /p 或/prompt | 切换提示在 shell 中的显示。 |
| /cp 或/class path[路径] | 将类型化路径添加到当前类路径中。 |
| /h 或/history | 列出活动 JShellState 的历史记录。 |
| /setstart [文件] | 读取和设置启动定义文件。 |
| /savestart[文件] | 将当前会话的定义保存到指定的启动文件。 |
| /! | 重新执行最后一段代码。 |
| / | 重新执行第 n 段代码。 |
| /- | 重新执行第 n 个先前的代码段。 |
如果您键入/e 命令,一个名为“JShell Edit Pad”的便笺式编辑器将会打开,其中包含您为当前 JShellState 输入的源代码,如图 1-6 所示。您可以在这个面板中编辑源代码,然后单击“Accept”按钮在 jShell 中评估这些源代码。
图 1-6。jshell edit pad(jshell 编辑面板)
jShell 的其他有用特性是,您可以通过按键盘上的向上箭头调出之前键入的命令。交互式 shell 还具有 tab 补全功能。如果您开始键入一条语句,然后按 Tab 键,该语句将自动完成,或者显示当前键入字符的选项列表。还可以设置一个预定义的导入列表,以便每次启动 jShell 会话时,这些导入都会自动发生。
jShell 提供了一个交互式环境,允许在输入代码片段时立即得到反馈。这对于原型开发或学习语言是有益的。其他语言,比如 Groovy、Python 和 Scala,也有类似的 REPL 环境。现在,jShell 可用于 Java,它为课堂使用的更具交互性的环境打开了大门,并提高了开发人员的原型开发效率。
小费
要了解 jShell 中可用命令的更多信息,只需在 Shell 打开后键入/help。help 特性显示了 jShell 中可用特性的详细列表。
1-8.与字符串相互转换
问题
您有一个存储在原始数据类型中的值,并且您希望将该值表示为人类可读的字符串。或者,您想从另一个方向出发,将人类可读的字符串转换为原始数据类型。
解决办法
遵循清单 1-6 中的一个模式。该清单展示了从字符串到双精度浮点值的转换,并展示了返回字符串的两种方法。
清单 1-6。字符串转换的一般模式
它是如何工作的
该解决方案展示了一些适用于所有基本类型的转换模式。首先,将浮点数从人类可读的表示形式转换成 Java 语言用于浮点运算的 IEEE 754 格式:
注意模式。您可以用 Float、Long 或任何其他目标数据类型替换 Double。每个基本类型都有一个对应的包装类,名称相同,但首字母大写。这里的基元类型是 double,对应的包装器也是 Double。包装器类实现了 Double.parseDouble()、Long.parseLong()、Boolean.parseBoolean()等辅助方法。这些解析方法将人类可读的表示转换成相应类型的值。
反过来说,调用 String.valueOf()通常是最容易的。String 类实现了这个方法,并且为每个基本数据类型重载了这个方法。或者,包装类也实现 toString()方法,您可以调用该方法将基础类型的值转换为人类可读的形式。至于采取哪种方法,这是你自己的喜好。
以数值类型为目标的转换需要一些异常处理才能实现。您通常需要很好地适应这样一种情况,即字符串值应该是有效的数字表示,但事实并非如此。第九章详细介绍了异常处理,接下来的食谱 1-10 提供了一个简单的例子来帮助你开始。
警告
布尔类型的文字为“真”和“假”。它们区分大小写。当使用 Boolean parseBoolean()转换方法从字符串进行转换时,除这两个值之外的任何值都会被默认解释为 false。
1-9.通过命令行执行传递参数
问题
您希望将值传递给一个 Java 应用,该应用是通过命令行使用 java 实用程序调用的。
解决办法
使用 java 实用程序运行应用,并在应用名称后指定要传递给它的参数。如果你要传递多个参数,每个参数之间应该用空格隔开。例如,假设您想将参数传递给清单 1-7 中创建的类。
清单 1-7。访问命令行参数的示例
首先,确保编译程序,以便有一个. class 文件可以执行。您可以在 NetBeans 中通过右键单击该文件并从上下文菜单中选择“编译文件”选项,或者通过命令行或终端中的 javac 实用程序来完成此操作。
接下来,打开命令提示符或终端窗口,遍历到项目的 buildclasses 目录。(关于从命令行执行的详细讨论,请参见配方 1-6)。例如:
现在发出一个 java 命令来执行该类,并在命令行中类名后面键入一些参数。以下示例传递两个参数:
您应该会看到以下输出:
空格分隔参数。如果要传递包含空格或其他特殊字符的参数,请用双引号将字符串括起来。例如:
输出现在只显示一个参数:
双引号将字符串“Upper Peninsula”转换为单个参数。
它是如何工作的
所有可从命令行或终端执行的 Java 类都包含一个 main()方法。如果查看 main()方法的签名,可以看到它接受 String[]参数。换句话说,您可以将 String 对象的数组传递给 main()方法。命令行解释程序(如 Windows 命令提示符和各种 Linux 和 Unix shells)从命令行参数中构建一个字符串数组,并代表您将该数组传递给 main()方法。
示例中的 main()方法显示传递的每个参数。首先,测试名为 args 的数组的长度,看它是否大于零。如果是,该方法将通过执行 for 循环来遍历数组中的每个参数,同时显示每个参数。如果没有传递任何参数,args 数组的长度将为零,并显示一条消息。否则,您会看到一条不同的消息,后跟一个参数列表。
命令行解释器将空格和其他字符识别为分隔符。一般来说,将数值作为由空格分隔的参数传递是安全的,不需要用引号将每个值括起来。但是,您应该养成用双引号括住字符串参数的习惯,如最终的解决方案示例所示。这样做是为了消除关于每个参数的开始和结束的任何模糊性。
注意
Java 将所有参数视为字符串。如果您将数值作为参数传递,它们将以人类可读的形式作为字符串输入 Java。您可以使用配方 1-8 中所示的转换方法将它们转换成适当的数值类型。
1-10.通过 jShell 执行脚本
问题
您希望编写一个原型或脚本,并通过 jShell 实用程序从命令行或终端执行它。
解决办法
虽然 jShell 并不打算为 Java 开发提供一种新的语言语法,但是可以将在 jShell 中执行的源代码片段保存到一个文件中,然后将该文件传递给 jShell 实用程序来执行。在这个解决方案中,我们将一个简单的代码片段保存到一个名为 myScript.java 的文件中,并使用 jShell 实用程序执行它。
首先,将下面的源代码保存到一个名为 myScript.java 的文件中,并保存到您的文件系统中。
使用以下语法执行脚本:
输出:
它是如何工作的
有时,使用文本编辑器或 jShell 编辑板(见配方 1-7)来保存可以在 JShell 环境中执行的源代码是有益的。这增加了快速构建代码原型的能力,也促进了开发可反复执行的脚本的能力。这对于开发可由 JVM 执行的调度任务或管理任务非常有用。因此,jShell 的源代码可以存储在包含您选择的扩展名的文件中,然后该文件可以传递给 jShell 执行。
在该解决方案中,输出一个简单的字符串,然后退出 jShell 环境。请注意,/x 命令位于文件中源代码之后的单独一行。/x 命令告诉 jShell 环境在完成后退出。如果在完成时退出,一旦源代码运行完成并且 jShell 环境关闭,文件中定义的任何变量、方法、类等都将丢失。
不建议使用 jShell 环境编写应用来执行。事实上,GUI 应用超出了 jShell 的范围,调试器也不受支持。该环境显然是为了教育和原型设计的目的。然而,有些人可能会发现保存代码片段以便以后通过 jShell 执行是很方便的。
1-11.接受来自键盘的输入
问题
您对编写一个接受用户键盘输入的命令行或终端应用感兴趣。
解决办法
利用 java.io.BufferedReader 和 java.io.InputStreamReader 类读取键盘输入并将其存储到局部变量中。清单 1-8 显示了一个程序,它会一直提示输入,直到您输入一些代表 long 类型有效值的字符。
清单 1-8。键盘输入和异常处理
以下是该程序的运行示例:
前两个输入不代表 long 数据类型中的有效值。第三个值有效,运行结束。
它是如何工作的
我们的应用经常需要接受某种类型的用户输入。诚然,现在大多数应用都不是从命令行或终端使用的,但是能够创建从命令行或终端读取输入的应用有助于打下良好的基础,并且在一些应用或脚本中可能是有用的。终端输入在开发您或系统管理员可能使用的管理应用时也很有用。
这个配方的解决方案中使用了两个助手类。它们是 java.io.BufferedReader 和 java.io.InputStreamReader。使用这些类的代码的早期部分理解起来特别重要:
这个语句中最里面的对象是 System.in,它代表键盘。您不需要声明 System.in. Java 的运行时环境会为您创建对象。它简单易用。
System.in 提供对来自输入设备的原始数据字节的访问,在我们的例子中,输入设备是键盘。InputStreamReader 类的工作是获取这些字节并将它们转换为当前字符集中的字符。System.in 被传递给 InputStreamReader()构造函数以创建 InputStreamReader 对象。
InputStreamReaderknows 了解字符,但不了解行。BufferedReader 类的工作是检测输入流中的换行符,并使您能够方便地一次读取一行。BufferedReader 还通过允许从输入设备以不同大小的块进行物理读取来提高效率,这些块的大小不同于应用消耗数据的大小。当输入流是一个大文件而不是键盘时,这一点会有所不同。
下面是清单 1-8 中的程序如何利用 BufferedReader 类的一个实例(名为 readIn)从键盘读取一行输入:
执行此语句会触发以下序列:
- System.in 返回一个字节序列。
- InputStreamReader 将这些字节转换为字符。
- BufferedReader 将字符流分成多行输入。
- readLine()向应用返回一行输入。
I/O 调用必须包装在 try-catch 块中。这些块用于捕捉任何可能发生的异常。如果转换不成功,示例中的 try 部分将会失败。失败会阻止将 numberIsValid 标志设置为 true,这将导致 do 循环进行另一次迭代,以便用户可以再次尝试输入有效值。要了解更多关于捕捉异常的信息,请参见第九章。
清单 1-8 顶部的以下语句值得一提:
该语句使 java.io 包中定义的类和方法可用。其中包括 InputStreamReader 和 BufferedReader。还包括在第一个 try-catch 块中使用的 IOException 类。
1-12.记录您的代码
问题
您希望记录一些 Java 类,以帮助将来的维护。
解决办法
使用 Javadoc 将注释放在要记录的任何类、方法或字段之前。要开始这样的注释,请写下字符/。然后用星号()开始后面的每一行。最后,用字符/结束注释。清单 1-9 显示了用 Javadoc 注释的方法。
清单 1-9。Javadoc 表单中的注释
可以用同样的方法将注释添加到类和字段的开头。这些注释对您和维护代码的其他程序员很有帮助,并且它们的特定格式使您能够轻松地生成代码的 HTML 引用。
通过调用名为 Javadoc 的工具生成 HTML 引用。这是一个命令行工具,它解析命名的 Java 源文件,并根据定义的类元素和 Javadoc 注释制定 HTML 文档。例如:
这个命令将生成几个 HTML 文件,包含类、方法和字段的文档。如果源代码中不存在 Javadoc 注释,仍然会生成一些默认文档。要查看文档,请将以下文件加载到浏览器中:
该文件将与您正在记录的类或包位于同一目录中。还将有一个 index-all.html 文件,严格按照字母顺序列出记录在案的实体。
请记住,使用 Javadoc 工具和使用 javac 时适用相同的规则。您必须驻留在与源文件相同的目录中,或者在文件名前面加上文件所在的路径。
它是如何工作的
从头开始为应用生成文档可能非常繁琐。维护文档可能更麻烦。JDK 附带了一个称为 Javadoc 的广泛的文档系统。在您的代码源代码中放置一些特殊的注释,并运行一个简单的命令行工具,可以很容易地生成有用的文档并使其保持最新。此外,即使应用中的某些类、方法或字段没有专门针对 Javadoc 实用程序进行注释,也会为这些元素生成默认文档。
格式化文档
要创建 Javadoc 注释,请以字符/*开头。虽然从 Java 1.4 开始是可选的,但是通常的做法是在注释中包含一个星号作为每一个后续行的第一个字符。另一个好的做法是缩进注释,使其与被记录的代码一致。最后,用字符/结束注释。
Javadoc 注释应该以对类或方法的简短描述开始。很少使用 Javadoc 对字段进行注释,除非它们被声明为 public static final (constants),在这种情况下,提供注释是个好主意。一个注释可以有几行的长度,甚至可以包含多个段落。如果你想把注释分成几个段落,那么使用
标签来分隔这些段落。注释可以包括几个标记,这些标记指示关于被注释的方法或类的各种细节。Javadoc 标签以&符号(@)开始,一些常见的标签如下:
您还可以在 Javadoc 中包含内联链接来引用 URL。要包含内联链接,请使用标签{@link My link},其中 Link 是您要指向的实际 URL,My Link 是您要显示的文本。在 Javadoc 注释中还可以使用许多其他标记,包括{@literal}、{@code}、{@value org}和许多其他标记。有关完整的列表,请参见 OTN 网站上的 Javadoc 参考资料。
执行工具
Javadoc 工具也可以针对整个包或源代码运行。只需将包名传递给 Javadoc 工具,而不是单个的源文件名。例如,如果一个应用包含一个名为 org.juneau.beans 的包,那么该包中的所有源文件都可以通过运行该工具进行记录,如下所示:
要一次为多个包生成 Javadoc,请用空格分隔包名,如下所示:
另一个选项是使用–source path 标志指定源文件的路径。例如:
默认情况下,Javadoc 工具将生成 HTML,并将其放入与被记录的代码相同的包中。如果您喜欢将源文件从文档中分离出来,那么这个结果可能会变成一个混乱的噩梦。相反,您可以通过将–d 标志传递给 Javadoc 工具来为生成的文档设置一个目的地。
1-13.读取环境变量 s
问题
您正在开发的应用需要利用一些环境变量。您希望从操作系统级别读取已设置的值。
解决办法
利用 Java 系统类来检索任何环境变量值。System 类有一个名为 getenv()的方法,它接受与系统环境变量的名称相对应的字符串参数。然后,该方法将返回给定变量的值。如果不存在匹配的环境变量,将返回空值。清单 1-10 提供了一个例子。ReadOneEnvVariable 类接受环境变量名作为参数,并显示已经在操作系统级别设置的变量值。
清单 1-10。读取环境变量的值
如果您对检索系统上定义的环境变量的完整列表感兴趣,请不要向 System.getenv()方法传递任何参数。您将收到一个 Map 类型的对象,其中包含所有的值。您可以遍历它们,如清单 1-11 所示。
清单 1-11。遍历环境变量的映射
它是如何工作的
System 类包含许多不同的实用程序,可以帮助应用开发。其中之一是 getenv()方法,它将为给定的系统环境变量返回值。
您还可以返回所有变量的值,在这种情况下,这些值存储在一个映射中。映射是名称/值对的集合。第七章提供了更多关于地图的信息,包括一个详细展示如何迭代地图的方法。
清单 1-10 和 1-11 中获取环境变量值的方法是相同的。它被重载来处理解决方案中显示的两种情况。如果您只想获取变量值,请将变量名作为字符串传递。不传递任何参数来获取当前设置的所有变量的名称和值。
摘要
这一章包括了允许你快速开始使用 Java 的方法。它涵盖了 JDK 的安装,以及 NetBeans IDE 的安装和使用。本章还介绍了一些基础知识,如声明变量、编译代码和文档。本书的其余部分深入探讨了 Java 语言的各个不同领域,涵盖了从初学者到专家的各种主题。当您完成本书剩余部分中的示例时,请参考本章了解配置细节。
二、Java 9 增强
JDK 的每个版本都为 Java 平台带来了新的增强和功能。每个版本还具有与以前版本的向后兼容性。这本书包括了许多介绍 Java 9 新特性的方法,这一章展示了几个最重要的增强来吊起你的胃口。本章绝不是所有 Java 9 增强的完整列表。相反,它是让您了解 Java 9 的一些热门新特性的一个飞跃。
2-1.避免接口代码中的冗余
问题
您希望在一个包含非常相似的代码的接口中实现两个或更多的默认方法。与其将代码复制到每个不同的默认方法中并单独维护每个默认方法,不如将相似的代码封装到它自己的方法中以便重用。
解决办法
利用接口中的私有方法来缓解这个问题。Java 9 提供了在接口中包含私有方法的能力。私有方法仅在该接口内可用,并且不能由实现该接口的任何类使用。但是,作为接口一部分的每个默认方法实现都可以利用私有方法。
下面的接口包括两个默认方法和一个私有方法。私有方法封装了可以在每个默认方法实现中使用的功能。
它是如何工作的
在 Java 8 之前,不可能在 Java 接口中包含代码实现。接口是 Java 中的引用类型,类似于类。然而,它的初衷只允许抽象方法、常量、静态方法和嵌套类型。因此,实现接口的类必须实现每个抽象方法。在 Java 8 中,这个限制被取消了,以默认方法的形式包含方法实现成为可能。默认方法可以包含接口中的实现,或者它的实现可以被实现类重写。因此,命名为 default method,意味着如果实现类没有提供默认方法实现,则默认方法实现驻留在接口中。接口中不允许私有方法。
出现了这样的情况:一个接口中的多个默认方法可能包含相似的代码。这段代码现在可以封装在接口的私有方法实现中。私有方法实现不能在接口外部使用。它只能由同一接口中包含的任何默认方法使用。在这个配方的解决方案中,volumeCalc()方法使用标准公式返回方形或矩形游泳池的计算体积。界面中的每个默认方法都能够利用 volumeCalc()方法来查找体积。但是,volumeCalc()方法不能在界面之外使用。
这似乎是一个有争议的话题,因为接口最初只用于字段和方法声明,但也可以说在许多默认方法实现中复制相同的代码是一种不好的做法。尽管如此,这个特性使得在一个接口中重用代码变得更加容易,从而减少了出错的机会,并使维护变得更加容易。
2-2.为简化和代码重用创建模块
问题
您正在编写一个实用程序库或 Java 应用,并且您不希望依赖类路径来管理与其他库的依赖关系。此外,您希望将您的库打包,以便它可以轻松地集成到其他项目中。
解决办法
将您的库或应用开发为一个模块。创建模块非常容易。然而,模块本身可能变得非常复杂。这个例子将涵盖一个非常简单的模块的创建,它不依赖于任何其他模块。其他模块也不会依赖于该模块。首先在文件系统的某个地方创建一个新目录…在这种情况下,将其命名为“recipe2-2”在其中创建一个名为 src 的新文件夹,然后在 src 文件夹中创建一个名为 module-info.java 的文件,这是模块描述符。在该文件中,列出模块名称,如下所示:
接下来,在之前创建的 src 目录中创建一个名为 org.acme.wordcount 的文件夹(图 2-1 )。接下来,在 org.acme.wordcount 文件夹中创建一个名为 org 的文件夹。随后,在 org 文件夹中创建一个 acme 文件夹,然后在 acme 文件夹中创建一个 wordcount 文件夹。
图 2-1。模块文件夹层次结构
现在,通过在 wordcount 文件夹中添加一个名为 WordCount.java 的新文件来创建模块的主体。将以下代码放在 WordCount.java 文件中:
通过使用命令行或终端并遍历前面创建的 src 目录,利用 javac 实用程序来编译模块。发出 javac 命令,指定-d 标志来列出编译后的代码将放入的文件夹。列出每个要编译的源文件,包括 module-info.java 描述符,用空格隔开。以下命令编译在中开发的源代码,并将结果放入名为 mods/org.acme.wordcount 的目录中。
现在代码已经编译好了,是时候执行模块了。使用 java 可执行文件,指定- module-path 选项(这是 Java 9 中的新增功能)来指示模块源的路径。-m 选项用于指定模块的主类。遍历 src 目录并发出以下命令:
此示例将单词“testing”、“one”、“two”、“three”传递给要计数的模块。输出应该如下所示:
它是如何工作的
Jigsaw 项目为 Java 平台带来了模块,最终引入了一种方法来消除旧的类路径,并使用更新的、更可插拔的架构。Java 9 模块系统允许封装自包含的代码模块,并使它们通用,这样一个模块可以依赖于其他模块,或者另一方面,其他模块可以依赖于它。这种模块化的依赖关系取代了旧的类路径系统,尽管类路径仍然可以用来适应向后兼容性,也适用于模块化没有什么意义的情况。
模块的创建由 module-info.java 描述符文件组成。该文件用于指示包含该模块的包,以及该模块与其他模块共享的依赖契约。请参见第二十二章,了解更多关于描述符文件的详细信息。
这个菜谱中的自包含应用驻留在 org.acme.wordcount.WordCount.java 文件中,可以用 javac 编译,用 java 可执行文件执行,这是可以想象的。这两个实用程序提供了新的选项来支持模块化,并且这个方法演示了如何使用这些新选项来编译和执行模块。有关模块编译和执行的更多细节,请参见配方 22-2。
2-3.轻松检索操作系统进程信息
问题
您希望能够找到有关操作系统进程的信息。
解决办法
利用 Java 9 中更新的流程 API。新的 ProcessHandle 接口允许用户轻松获取有关操作系统进程的信息。在下面的代码中,所有操作系统进程都被列出并输出到命令行。
然而,这不是很有帮助,因为它只是简单地列出了每个操作系统进程的进程号…这不是很有用。为了获得流程的更多细节,我们需要获得 ProcessHandle 并调用它的助手方法,这很容易做到。在打印 ProcessHandle 时,下面的代码将打印关于每个进程的更多信息。信息本身。
示例输出可能如下所示:
如果您希望检索与运行该流程的用户相关的信息,这也很容易做到。
使用这种技术的示例输出可能如下所示:
它是如何工作的
在 Java 9 发布之前,获取有关操作系统进程的信息很麻烦。我们必须使用 management factory . getruntimemxbean()方法获取进程 id,然后解析返回的字符串。Java 9 中引入了 ProcessHandle 接口,使得操作系统进程信息的检索成为 JDK 的一等公民。表 2-1 显示了可以在 ProcessInfo 中调用以检索所需信息的方法。
表 2-1。ProcessHandle 接口
|
方法
|
描述
|
| — | — |
| 所有进程() | 对当前进程可见的所有进程的快照。 |
| 儿童() | 当前进程的子进程的快照。 |
| compareTo(ProcessHandle) | 将一个 ProcessHandle 与另一个进行比较。 |
| 当前() | 返回当前进程的 ProcessHandle。 |
| 后代() | 当前进程的所有后代的快照。 |
| 销毁() | 请求终止该进程。返回一个布尔值来表示结果。 |
| destroyForcibly() | 请求强制终止进程。返回一个布尔值来表示结果。 |
| 等于(对象) | 如果传入的对象不为空,则返回 true,并表示相同的系统进程,否则返回 false。 |
| getPid() | 返回进程的进程 ID。 |
| hashCode() | 返回进程的哈希代码值。 |
| 信息() | 返回 ProcessHandle。Info,它是关于当前进程的信息的快照。 |
| isalive() | 返回一个布尔值,以指示进程是否处于活动状态。 |
| (长)的 | 为现有的本地进程返回可选的。 |
| onExit() | 为进程的终止返回 CompletableFuture 。然后可以调用 CompletableFuture 来确定状态。 |
| 父项() | 为当前进程的父进程返回可选的。 |
| 支持 NormalTermination() | 如果 destroy()的实现将正常终止进程,则返回 true。 |
2-4.轻松处理错误
问题
你想容易地管理有效的最终变量的关闭。
解决办法
try-with-resources 构造是在 Java 7 中引入的,它允许轻松管理资源。在 Java 9 中,这变得更加容易,因为不需要为了构造而有效地创建新变量。在下面的代码中,writeFile()方法将 BufferedWriter 作为参数,由于它被传递到方法中并准备使用,因此它实际上是 final。这意味着它可以简单地列在 try-with-resources 中,而不是创建一个新的变量。
在 Java 9 之前,writeFile 应该如下所示:
这段代码将创建一个名为“Easy TryWithResources”的新文件,并将文本“This is easy in Java 9”放入该文件。
它是如何工作的
在 Java 9 中,try-with-resources 构造变得更加容易。try-with-resources 构造是在 Java 8 中引入的,它允许人们非常容易地处理资源的打开和关闭。如果我们有一个资源,比如数据库连接或 BufferedStream,明智地管理是一个好主意。换句话说,打开资源,然后相应地使用它,最后在完成时关闭资源,以确保没有资源泄漏。try-with-resources 构造允许在 try 块中打开一个资源,并在该块完成后自动清除它。
在解决方案中,显示了处理资源的原始方式,随后是 Java 9 中的新方式。现在,如果将资源作为参数传递给一个方法,或者如果它是一个 final 字段,就可以简单地开始使用 try-with-resources 构造中的资源。这意味着不再需要为了在 try-with-resources 中使用而创建占位符变量。虽然这不是一个主要的语言变化,但它肯定会使处理资源变得更容易,而且它肯定会使 try-with-resources 块更容易理解。
2-5.使用流过滤条件前后的数据
问题
您希望利用流来有效地操作您的集合。这样做时,您希望在特定条件发生之前和/或之后过滤这些流。最后,您希望在满足给定的谓词条件之前检索集合中的所有数据。您还希望检索满足给定谓词条件后放置的集合中的所有数据。
解决办法
在您的流中利用新的 Java 9 takeWhile()和 dropWhile()构造。假设我们有以下数据集合,我们希望检索包含单词“Java”的元素之前的所有元素。
要检索包含字符串“Java”的元素之前的所有元素,我们可以使用 takeWhile()构造,如下所示:
假设我们希望检索包含字符串“Java”的元素之后出现的所有元素。我们可以使用 dropWhile()构造,如下所示:
它是如何工作的
流改变了我们在 Java 中开发代码和处理数据集合的方式。可用于流的原始过滤器集相当丰富。然而,在 Java 8 中,增加了更多的选项,使得用流来提炼数据变得更加容易。takeWhile()和 dropWhile()构造允许对流进行解析,返回一个包含第一个不符合指定谓词条件的元素之前的所有元素的新流,或者返回一个包含第一个不符合指定谓词条件的元素之后的所有元素的新流。
在这个菜谱的解决方案中,解析字符串列表,第一次将每个元素打印到命令行。然后,takeWhile()构造被应用于相同的字符串流和流中的元素,然后不满足指定条件的元素将被打印到命令行。takeWhile()接受一个谓词条件,然后将其应用于流中的每个元素,然后只返回那些在谓词条件不匹配之前被迭代的元素。位于流中不满足条件的位置及其之后的所有元素都不会被返回。
使用 dropWhile()构造时会出现相反的结果。在解决方案中,所有流元素都将被忽略,直到返回不再满足指定条件的第一个元素。流中的每个后续元素也将被返回。
takeWhile 和 dropWhile 构造与过滤器非常相似,只是只有一个失败的条件会导致其余的元素分别被忽略或返回。
2-6.开发一个简洁的 HTTP 客户端
问题
您希望在 Java 应用中开发一个 HTTP 客户端。
解决办法
使用 Java 9 的更新的 HTTP/2 客户端。在下面的例子中,通过 HTTP 客户端代码解析并返回一个网站。在下面的例子中,我们的新闻网页以字符串的形式返回。
输出如下所示(为简洁起见,略作缩写):
注意
这是在 Java 9 jShell 实用程序中尝试的一个很好的例子。要启动该实用程序,请打开命令提示符或终端,并键入 jshell–add-modules Java . httpclient 来启动包含 http client 模块的 shell。这假设 jshell 可执行实用程序驻留在路径中。
它是如何工作的
Java 9 中添加了新的 javax.httpclient 模块,它由高级 HTTP 和 WebSocket 客户端 API 组成。API 为 HTTP 客户端提供了同步和异步实现,为 WebSocket 提供了异步实现。API 驻留在 java.net.http 包中。
HttpClient 是 HttpRequests 类型通用的配置信息容器。HttpClient 是通过启动 HttpRequest 生成的。生成器,为请求传递 URI,然后调用 create()方法。create()方法返回一个不可变的 HttpClient。正如在这个配方的解决方案中所看到的,客户端可以用来执行许多活动,包括同步的和异步的。异步请求将返回一个 CompleteableFuture 对象以供使用。有关 HTTP 和 WebSocket 客户端的更多详细信息,请参考配方 21-7 和配方 21-8。
2-7.重定向平台日志
问题
如果日志符合指定的过滤标准,您希望过滤日志并将其重定向到特定文件。
解决办法
利用 Java 9 统一日志 API 来过滤日志并相应地进行路由。在下面的命令行或终端摘录中,调用 java 可执行文件来执行名为 Recipe02_07 的类。指定了-Xlog 选项,传递 gc=debug:file=gc.txt:none。这表示所有使用“调试”级别标记为“gc”的日志消息都应该写入文件 gc.txt,并且不应该使用任何修饰。
这将导致在当前目录中创建一个名为 gc.txt 的文件,所有与指定的–Xlog 选项相关的日志消息都将被写入该文件。
它是如何工作的
JVM 是一个复杂的系统,有时很难确定问题的原因。Java 9 中添加的统一 JVM 日志系统提供了一个更细粒度的解决方案,有助于找到问题的原因。新的命令行选项–Xlog 控制这一功能,提供了许多标签、级别、装饰和输出选项来实现卓越的日志记录。标签可以通过名字来指定,比如 gc、threads、compiler 等等。可能有不同的日志记录级别,包括错误、警告、信息、调试、跟踪和开发。如果指定了“off”级别,则日志记录将被禁用。可以指定装饰者来提供关于消息的详细信息。装饰者可以以定制的顺序来指定,这样期望的结果将被记录。表 2-2 包含了可以使用的不同装饰器的列表。
表 2-2。-Xlog 装饰工
|
装饰者
|
功能
|
| — | — |
| 时间 | 以 ISO-8601 格式提供当前时间和日期。 |
| 正常运行时间 | JVM 启动并运行的时间(秒和毫秒)。 |
| 时间英里 | 从 System.currentTimeInMillis()返回的值; |
| uptimemillis | JVM 的正常运行时间(毫秒)。 |
| 时间纳米 | 从 System.nanoTime()返回的值; |
| 上升趋势 | JVM 的正常运行时间,单位为纳秒。 |
| pid | 进程标识符。 |
| 每日三次 | 线程标识符。 |
| 水平 | 日志消息级别。 |
| 标签 | 与日志消息关联的标记集。 |
支持三种类型的输出,即:stdout、stderr 和文本文件。输出文件的旋转配置是可能的。当所有这些选项一起指定时,日志记录会变得非常详细。有关可能选项的完整列表,请参考 http://openjdk.java.net/jeps/158 的 JEP()。
2-8.利用工厂方法创建不可变集合
问题
您希望生成一个不可变的值集合。
解决办法
利用 Collection.of()构造生成一个不可变的集合。在下面的示例中,创建了两个集合。第一个是不可变列表,第二个是不可变映射。
输出如下所示。请注意,在示例中,我添加了一个 try-catch 块来捕获当我试图修改列表时抛出的 UnsupportedOperationException。
它是如何工作的
历史上,Java 是一种执行小任务的冗长语言。在过去,构建一个填充的数据集合需要几行代码。在第一行,集合必须被初始化,然后是添加到其中的每一项的一行代码。Java 9 增加了方便的 API,可以快速生成不可修改的数据集合,这样就可以在一行代码中初始化和填充构造。
工厂方法已经被添加到 List、Set 和 Map 接口中,用于创建这种不可修改的数据集合。工厂方法由 of()方法组成,该方法接受多达 10 个值,用于快速创建不可变的集合。映射工厂方法最多接受十个键/值对。如果 Map 需要十个以上的对,那么可以调用 Map.ofEntries()方法,传递任意数量的 Map。条目。此外,不能使用空值来填充元素、键或值。
摘要
本章介绍了 Java 9 中添加的一些新特性和增强功能。虽然肯定不是新特性的完整列表,但本章深入研究了一些最值得期待的特性,包括模块化、流程 API 和简单的错误处理。为了更全面地了解新特性,应该通读整本书。然而,这一章让你对将要发生的事情有所了解。
三、字符串
字符串是任何编程语言中最常用的数据类型之一。它们可以用来从键盘上获取文本,将消息打印到命令行,等等。鉴于字符串的使用如此频繁,随着时间的推移,String 对象中增加了许多特性,以使它们更易于使用。毕竟,字符串是 Java 中的一个对象,所以它包含可用于操作字符串内容的方法。在 Java 中,字符串也是不可变的,这意味着它们的状态不能被改变。这使得它们与一些可变的数据类型有所不同。理解如何正确利用不可变对象是很重要的,尤其是当试图改变或给它们赋不同的值时。
本章重点介绍一些最常用的处理字符串对象的方法和技术。我们还将介绍一些有用的技术,这些技术并不是字符串对象所固有的。
压缩字符串:Java 9 字符串增强
自从引入 Java 语言以来,字符串就被存储在 UTF-16 char 类型的数组中。char 数组为每个字符包含两个字节,这最终会产生一个很大的内存堆,因为字符串在我们的应用中经常使用。在 Java 9 中,字符串存储在 byte 类型的数组中,存储的字符编码为 ISO-8859-1/Latin-1(每个字符一个字节)或 UTF-16(每个字符两个字节)。char 数组上还有一个编码标志,用于指示字符串使用的编码类型。这些变化也被称为紧凑字符串。
这些变化不会影响我们使用字符串的方式,也不会以任何方式改变 String 类的 helper 方法。但是,它们可能会显著减少应用使用的内存量。
3-1.获取字符串的子部分
问题
你想检索一个字符串的一部分。
解决办法
使用 substring()方法获取两个不同位置之间的字符串部分。在下面的解决方案中,创建了一个字符串,然后使用 substring()方法打印出字符串的各个部分。
运行此方法将产生以下结果:
它是如何工作的
String 对象包含许多 helper 方法。substring()就是这样一种方法,它可用于返回字符串的一部分。substring()方法有两种变体。其中一个接受单一参数,即起始索引;另一个接受两个参数:startingindex 和 endingindex。substring()方法有两种变体,这使得第二个参数看起来是可选的;如果未指定,则使用调用字符串的长度来代替它。应该注意,索引从零开始,因此字符串中的第一个位置的索引为 0,依此类推。
从这个配方的解决方案中可以看出,第一次使用 substring()打印出了字符串的全部内容。这是因为传递给 substring()方法的第一个参数是 0,传递的第二个参数是原始字符串的长度。在 substring()的第二个示例中,索引 5 用作第一个参数,索引 20 用作第二个参数。这实际上导致只返回字符串的一部分,从字符串中位于第六个位置的字符开始,或索引 5,因为第一个位置的索引为 0;并以字符串中位于第 20 个位置的字符(索引为 19)结束。第三个示例只指定了一个参数;因此,结果将是从该参数指定的位置开始的原始字符串。
注意
substring()方法只接受正整数值。如果试图传递负值,将会引发异常。
3-2.比较字符串
问题
您正在编写的应用需要能够比较两个或多个字符串值。
解决办法
使用内置的 equals()、equalsIgnoreCase()、compareTo()和 compareToIgnoreCase()方法来比较字符串中包含的值。下面是使用不同字符串比较操作的一系列测试。
如您所见,如果比较结果相等,则使用各种 if 语句来打印消息:
产生以下输出:
它是如何工作的
当试图比较两个或更多的值,尤其是字符串值时,使用编程语言会遇到一个更棘手的问题。在 Java 语言中,比较字符串非常简单,记住应该而不是使用进行字符串比较。这是因为比较运算符()用于比较引用,而不是字符串的值。在用 Java 进行字符串编程时,最吸引人的事情之一是使用比较运算符,但您不能这样做,因为结果可能会有所不同。
注意
Java 使用字符串的内部化来提高性能。这意味着 JVM 包含一个包含被拘留字符串的表,每次在字符串上调用 intern()方法时,都会在该表上执行查找以找到匹配。实习返回字符串的规范表示。如果表中没有匹配的字符串,则将该字符串添加到表中并返回一个引用。如果字符串已经存在于表中,则返回引用。Java 会自动填充字符串文字,当使用==比较运算符时,这会导致变化。
在这个配方的解决方案中,您可以看到各种不同的比较字符串值的技术。equals()方法是每个 Java 对象的一部分。Java String equals()方法已被覆盖,因此它将比较字符串中包含的值,而不是对象本身。从下面的例子中可以看出,equals()方法是一种比较字符串的安全方法。
equals()方法将首先使用==操作符检查字符串是否引用同一个对象;如果它们这样做,它将返回 true。如果它们不引用同一个对象,equals()将逐个字符地比较每个字符串,以确定相互比较的字符串是否包含完全相同的值。如果其中一个字符串与另一个字符串的大小写设置不同,该怎么办?他们还用 equals()互相比较相等吗?答案是否定的,这就是创建 equalsIgnoreCase()方法的原因。使用 equalsIgnoreCase()比较两个值会导致比较每个字符时不注意大小写。以下示例摘自该配方的解决方案:
compareTo()和 compareToIgnoreCase()方法执行字符串的字典式比较。这种比较基于字符串中包含的每个字符的 Unicode 值。如果字符串在字典顺序上位于参数字符串之前,则结果将是负整数。如果字符串按字典顺序跟在参数字符串后面,则结果将是正整数。如果两个字符串在字典上彼此相等,结果将为零。以下摘录自该配方的解决方案,演示了 compareTo()方法:
不可避免地,许多应用包含必须在某种程度上比较字符串的代码。下一次当您有一个需要字符串比较的应用时,在编写代码之前,请考虑一下本食谱中讨论的信息。
3-3.修剪空白
问题
您正在处理的一个字符串的两端包含一些空格。你想要去掉那些空白。
解决办法
使用 String trim()方法消除空白。在下面的例子中,打印的句子两边都有空格。然后,使用 trim()方法再次打印同一个句子,删除空白,以便可以看到更改。
输出将如下所示:
它是如何工作的
无论我们多么小心,在处理文本字符串时,空白总是会成为一个问题。将字符串与匹配值进行比较时尤其如此。如果一个字符串包含一个意外的空白字符,那么这对于模式搜索程序来说可能是灾难性的。幸运的是,Java String 对象包含 trim()方法,可以用来自动删除任何给定字符串末尾的空格。
trim()方法非常容易使用。事实上,正如您可以从这个配方的解决方案中看到的,使用 trim()方法所需要的只是对任何给定字符串的调用。因为字符串是对象,所以它们包含许多帮助器方法,这使得它们非常容易使用。毕竟,字符串是任何编程语言中最常用的数据类型之一…所以它们最好易于使用!trim()方法返回原始字符串的副本,去掉了所有的前导和尾随空格。但是,如果没有要删除的空格,trim()方法将返回原始的字符串实例。没有比这更简单的了!
3-4.更改字符串的大小写
问题
应用的一部分包含区分大小写的字符串值。您希望在处理之前将所有字符串都改为大写,以避免以后出现任何区分大小写的问题。
解决办法
使用 toUpperCase()和 toLowerCase()方法。String 对象提供了这两个帮助器方法来帮助对给定字符串中的所有字符执行大小写更改。
例如,给定下面代码中的字符串,将调用这两个方法中的每一个:
将产生以下输出:
它是如何工作的
要确保给定字符串中每个字符的大小写都是大写或小写,请分别使用 toUpperCase()和 toLowerCase()方法。使用这些方法时,有几个事项需要注意。首先,如果给定的字符串包含一个大写字母,并且对其调用 toUpperCase()方法,则大写字母将被忽略。同样的概念也适用于调用 toLowerCase()方法。给定字符串中包含的任何标点符号或数字也会被忽略。
这些方法中的每一种都有两种变体。其中一个变体不接受任何参数,而另一个变体接受与您希望使用的地区相关的参数。不带任何参数调用这些方法将导致使用默认区域设置的大小写转换。如果要使用不同的区域设置,可以使用接受参数的方法的变体,将所需的区域设置作为参数传递。例如,如果您想使用意大利语或法语地区,您可以使用以下代码:
使用这些方法将字符串转换成大写或小写可以使事情变得简单。它们对于比较作为应用输入的字符串也非常有用。考虑这样一种情况,用户被提示输入用户名,结果被保存到字符串中。现在考虑在程序的后面,将该字符串与数据库中存储的所有用户名进行比较,以确保用户名有效。如果输入用户名的人用大写的第一个字符键入它,会发生什么?如果用户名全部以大写形式存储在数据库中,会发生什么情况?这种比较永远不会相等。在这种情况下,开发人员可以使用 toUpperCase()方法来缓解这个问题。对正在比较的字符串调用此方法将导致两个字符串的大小写相同。
3-5.串联字符串
问题
有各种各样的字符串,你想结合成一个。
解决方案 1
如果要将字符串连接到彼此的末尾,请使用 concat()方法。下面的示例演示了 concat()方法的用法:
结果是这样的:
解决方案 2
使用串联运算符以速记方式组合字符串。在以下示例中,在两个字符串之间放置了一个空格字符:
结果是这样的:
解决方案 3
使用 StringBuilder 或 StringBuffer 来组合字符串。下面的示例演示如何使用 StringBuffer 连接两个字符串:
结果是这样的:
它是如何工作的
Java 语言为连接文本字符串提供了几个不同的选项。虽然没有一个比其他的更好,但是你可能会发现在不同的情况下有一个比另一个更好。concat()方法是一个内置的字符串帮助器方法。它提供了将一个字符串附加到另一个字符串的末尾的能力,如该配方的解决方案 1 所示。concat()方法将接受任何字符串值;因此,如果需要,可以显式键入一个字符串值作为参数传递。如解决方案 1 所示,简单地将一个字符串作为参数传递给该方法会将它附加到字符串的末尾,该方法将被调用。但是,如果您想要在两个字符串之间添加一个空格字符,您可以通过传递一个空格字符以及您想要追加的字符串来实现,如下所示:
正如您所看到的,能够向 concat()方法传递任何字符串或字符串组合使它非常有用。因为所有字符串帮助器方法实际上都返回应用了帮助器方法功能的原始字符串的副本,所以您也可以将调用其他帮助器方法的字符串传递给 concat()(或任何其他字符串帮助器方法)。假设您想要显示文本“Hello Java”而不是“Hello Java9”。以下字符串帮助器方法的组合将允许您这样做:
串联运算符(+)可用于组合任意两个字符串。它几乎被认为是 concat()方法的简写形式。本例的解决方案 3 中演示的最后一项技术是使用 StringBuffer,这是一个可变的字符序列,很像字符串,只是它可以通过方法调用来修改。StringBuffer 类包含许多用于构建和操作字符序列的 helper 方法。在解决方案中,append()方法用于追加两个字符串值。append()方法将作为参数传递的字符串放在 StringBuffer 的末尾。有关使用 StringBuffer 的更多信息,请参考位于的在线文档。
3-6.将字符串转换为数值
问题
您希望能够将存储为字符串的任何数值转换为整数。
解决方案 1
使用 Integer.valueOf() helper 方法将字符串转换为 int 数据类型。例如:
如您所见,这两个字符串变量都被转换为整数值。之后,它们被用于执行加法计算,然后存储到一个 int 中。
注意
本例中使用了一种称为自动装箱的技术。自动装箱是 Java 语言的一个特性,它可以自动将原始值转换成相应的包装类。例如,当您将 int 值赋给一个整数时,就会出现这种情况。类似地,取消装箱会在您尝试从包装类到原语的反方向转换时自动发生。有关自动装箱的更多信息,请参考位于的在线文档。
解决方案 2
使用 Integer.parseInt() helper 方法将字符串转换为 Int 数据类型。例如:
它是如何工作的
Integer 类包含 valueOf()和 parseInt()方法,用于将字符串或 Int 类型转换为整数。Integer 类的 valueOf()类型有两种不同的形式,可用于将字符串转换为整数值。每种方法的不同之处在于它们接受的参数数量。第一个 valueOf()方法只接受字符串参数。如果可能的话,该字符串将被解析为一个整数值,然后返回一个包含该字符串值的整数。如果字符串没有正确地转换成整数,那么该方法将抛出 NumberFormatException。
Integer 的 valueOf()方法的第二个版本接受两个参数:一个将被解析为整数的 String 参数和一个表示用于转换的基数的 int 参数。
注意
许多 Java 类型类都包含 valueOf()方法,可用于将不同的类型转换成该类的类型。String 类就是这种情况,因为它包含许多不同的 valueOf()方法,可用于转换。有关字符串类或任何其他类型类包含的不同 valueOf()方法的更多信息,请参见位于的在线 Java 文档
Integer 类的 parseInt()方法也有两种不同的形式。其中一个接受一个参数:要转换成整数的字符串。另一种形式接受两个参数:要转换成整数的字符串和基数。第一种格式使用最广泛,它将字符串参数解析为有符号的十进制整数。如果字符串中不包含可分析的无符号整数,将引发 NumberFormatException。第二种格式不太常用,它返回一个 Integer 对象,该对象保存由给定基数的字符串参数表示的值,前提是该字符串中包含一个可解析的无符号整数。
注意
parseInt()和 valueOf()最大的区别之一是 parseInt()返回一个 Int,valueOf()从缓存中返回一个整数。
3-7.迭代字符串中的字符
问题
您希望迭代一个文本字符串中的字符,以便可以在字符级别操作它们。
解决办法
使用字符串帮助器方法的组合来获得对字符级字符串的访问。如果在循环上下文中使用 String helper 方法,可以很容易地按字符遍历字符串。在下面的示例中,使用 toCharArray()方法分解名为 str 的字符串。
相同的策略可以用于传统版本的 for 循环。可以创建一个索引,允许使用 charAt()方法访问字符串中的每个字符。
这两种解决方案都会产生以下结果:
注意
第一个示例使用 toCharArray()生成一个新的字符数组。因此,使用传统 for 循环的第二个示例可能会执行得更快。
它是如何工作的
String 对象包含可用于执行各种任务的方法。这个配方的解决方案演示了许多不同的字符串方法。可以对字符串调用 toCharArray()方法,以便将字符串分解成字符,然后将这些字符存储在一个数组中。这个方法非常强大,当需要执行这个任务时,它可以节省一点时间。调用 toCharArray()方法的结果是一个 char[],然后可以使用索引遍历它。这个菜谱的解法就是这样。增强的 for 循环用于遍历 char[]的内容,并打印出它的每个元素。
String length()方法用于查找字符串中包含的字符数。结果是一个 int 值,这在 for 循环的上下文中非常有用,如这个配方的解决方案所示。在第二个示例中,length()方法用于查找字符串中的字符数,以便可以使用 charAt()方法迭代这些字符。charAt()方法接受一个 int 索引值作为参数,并返回位于字符串中给定索引处的字符。
通常,两个或多个字符串方法的组合可用于获得各种结果。在这种情况下,在同一个代码块中使用 length()和 charAt()方法提供了将字符串分解成字符的能力。
3-8.查找文本匹配
问题
您希望在文本中搜索特定的字符序列。
解决方案 1
利用正则表达式和 String matches()辅助方法来确定存在多少个匹配。要做到这一点,只需将表示正则表达式的字符串传递给 matches()方法,以匹配任何要匹配的字符串。这样做时,该字符串将与调用()时匹配的字符串进行比较。一旦被评估,matches()将产生一个布尔结果,表明它是否匹配。以下代码摘录包含一系列使用这种技术的示例。代码中包含的注释解释了每个匹配测试。
示例中输出的每个结果都是真的,只有第二个示例例外,因为它不匹配。
解决方案 2
与 String matches()方法相比,使用正则表达式模式和 Matcher 类可以获得性能更好、功能更丰富的匹配解决方案。尽管 matches()方法在大多数情况下可以完成工作,但是在某些情况下,您需要一种更灵活的匹配方式。使用此解决方案需要三个步骤:
- 将模式编译成模式对象。
- 使用模式上的 matcher()方法构造一个 Matcher 对象。
- 在匹配器上调用 matches()方法。
在下面的示例代码中,演示了模式和匹配器技术:
前面的例子将产生一个真值,就像解决方案 1 中演示的它的变体一样。
它是如何工作的
正则表达式是查找匹配的好方法,因为它们允许定义模式,这样应用就不必显式地查找精确的字符串匹配。当您想要查找用户输入到程序中的文本的匹配时,它们会非常有用。但是,如果您试图将字符串与您在程序中定义的字符串常量进行匹配,那么它们可能是多余的,因为 String 类提供了许多可用于此类任务的方法。然而,在几乎每个开发人员的生活中,肯定会有正则表达式派上用场的时候。它们几乎可以在今天使用的每一种编程语言中找到。Java 使它们易于使用和理解。
注意
尽管如今正则表达式在许多不同的语言中使用,但每种语言的表达式语法各不相同。有关正则表达式语法的完整信息,请参见位于的在线文档。
使用正则表达式最简单的方法是在 String 对象上调用 matches()方法。将一个正则表达式传递给 matches()方法将产生一个布尔结果,表明该字符串是否匹配给定的正则表达式模式。此时,了解什么是正则表达式以及它是如何工作的是很有用的。
一个正则表达式是一个字符串模式,可以与其他字符串进行匹配,以确定其内容。正则表达式可以包含许多不同的模式,这使它们能够是动态的,因为它们能够匹配许多包含相同格式的不同字符串。例如,在这个配方的解决方案中,下面的代码可以匹配几个不同的字符串:
这个例子中的正则表达式字符串是“我爱 Java [0-9]!”,它包含模式[0-9],表示 0 到 9 之间的任何数字。因此,任何显示“I love Java”后跟数字 0 到 9 以及一个感叹号的字符串都将匹配正则表达式字符串。要查看可以在正则表达式中使用的所有不同模式的列表,请参阅上一节中 URL 处的在线文档。
Pattern 和 Matcher 对象的组合也可以用来获得与 String matcher()方法类似的结果。Pattern 对象可用于将字符串编译成正则表达式模式。如果一个编译过的模式被多次使用,它可以为应用提供性能增益。您可以将基于字符串的正则表达式传递给 Pattern.compile()方法,就像传递给 String matches()方法一样。结果是一个编译后的模式对象,可以与字符串进行匹配以进行比较。通过对给定的字符串调用模式对象的 matcher()方法,可以获得 Matcher 对象。一旦获得了 Matcher 对象,就可以使用下面三种方法中的任何一种来将给定的字符串与模式进行匹配,这三种方法都返回一个表示匹配的布尔值。解决方案 2 的以下三行代码可以作为使用 Pattern.matches()方法的替代解决方案,但不考虑编译模式的可重用性:
- Matcher matches()方法尝试将整个输入字符串与模式进行匹配。
- Matcher lookingAt()方法试图将输入字符串与开头的模式进行匹配。
- Matcher find()方法扫描输入序列,寻找字符串中的下一个匹配序列。
在这个配方的解决方案中,对 Matcher 对象调用 matches()方法,以便尝试匹配整个字符串。在任何情况下,正则表达式对于匹配字符串和模式都非常有用。使用正则表达式的技术在不同的情况下会有所不同,使用哪种方法最适合这种情况。
3-9.替换所有文本匹配
问题
您已经在文本正文中搜索了特定的字符序列,并且希望用另一个字符串值替换所有匹配项。
解决办法
使用正则表达式模式获得匹配器对象;然后使用 Matcher 对象的 replaceAll()方法将所有匹配替换为另一个字符串值。下面的示例演示了这种技术:
该示例将产生以下结果:
它是如何工作的
Matcher 对象的 replaceAll()方法使得查找和替换包含在文本主体中的字符串或字符串的一部分变得容易。为了使用 Matcher 对象的 replaceAll()方法,必须首先通过将正则表达式字符串模式传递给 Pattern.compile()方法来编译模式对象。通过调用 matcher()方法,使用产生的 Pattern 对象获得 Matcher 对象。下面几行代码显示了这是如何实现的:
获得 Matcher 对象后,调用它的 replaceAll()方法,传递一个字符串来替换所有与编译模式匹配的文本。在这个配方的解决方案中,字符串“9”被传递给 replaceAll()方法,因此它将替换字符串中匹配“[0-9]”模式的所有区域。
3-10.确定文件后缀是否匹配给定的字符串
问题
您正在从服务器读取一个文件,您需要确定它是什么类型的文件,以便正确地读取它。
解决办法
通过对给定的文件名使用 endsWith()方法来确定文件的后缀。在下面的示例中,假设变量 filename 包含给定文件的名称,并且代码使用 endsWith()方法来确定 filename 是否以特定字符串结尾:
假设文件名变量中包含文件名及其后缀,这段代码将读取其后缀,并确定给定变量代表的文件类型。
它是如何工作的
如前所述,String 对象包含许多可用于执行任务的助手方法。String 对象的 endsWith()方法接受一个字符序列,然后返回一个布尔值,该值表示原始字符串是否以给定的序列结尾。对于这个配方的解决方案,在 if 块中使用了 endsWith()方法。一系列文件后缀被传递给 endsWith()方法,以确定 filename 变量所代表的文件类型。如果任何文件名后缀匹配,则打印一行,说明文件的类型。
3-11.制作可以包含动态信息的字符串
问题
您希望生成一个能够包含动态占位符的字符串,以便该字符串可以根据应用数据的变化而变化。
解决方案 1
利用 String format()内置方法生成包含动态数据占位符的字符串。下面的示例演示了一个包含动态占位符的字符串,该占位符允许将不同的数据插入到同一字符串中。在本例中,随着温度变量的变化,字符串也会动态改变。
输出:
解决方案 2
如果希望将字符串的内容打印出来,而不是存储起来供以后使用,可以使用 System.out.printf()方法在字符串中定位动态值。下面的示例演示了与解决方案 1 中相同的概念,只是这次没有使用 String.format()方法,而是简单地打印出一个字符串,并且在运行时用动态内容替换传递给 System.out.printf()方法的占位符。
输出:
它是如何工作的
当您需要使用动态字符串内容时,format()实用程序可以派上用场。format()内置方法允许在字符串中放置占位符,这样占位符将在运行时被动态内容替换。format 方法接受一个字符串和一系列变量,这些变量将用于在运行时用动态内容替换字符串中的占位符。占位符必须专门指定用于替换它们的内容类型。表 3-1 包含 String.format()函数的每个占位符或转换类型的列表。
表 3-1。String.format()转换类型
|
转换
|
内容类型
|
| — | — |
| b | 布尔 |
| h | 十六进制 |
| s | 线 |
| c | Unicode 字符 |
| d | 十进制整数 |
| o | 八进制整数 |
| x | 十六进制整数 |
| e | 计算机科学记数法中的浮点十进制数 |
| f | 浮点十进位数字 |
| g | 根据舍入后的精度和值,使用计算机化的科学记数法或十进制格式的浮点 |
| a | 带有效数字和指数的十六进制浮点数 |
| t | 日期/时间 |
| n | 特定于平台的行分隔符 |
每个占位符必须以%字符开头,以表示它是字符串中的占位符。占位符还可以包含标志、宽度和精度指示器,以帮助适当地格式化动态值。应该使用以下格式来构建每个占位符:
第二个解决方案演示了如何利用 System.out.printf()方法,该方法接受与 System.format()方法相同的参数。这两者的主要区别在于 System.out.printf()方法对于打印格式化内容来说非常方便。如果您的应用需要存储格式化的值,那么您更有可能使用 String.format()方法。
摘要
版权声明:
本文来源网络,所有图片文章版权属于原作者,如有侵权,联系删除。
本文网址:https://www.bianchenghao6.com/h6javajc/3978.html