Skip to content

Files

Latest commit

c4e46db · Oct 12, 2021

History

History
917 lines (600 loc) · 39.9 KB

File metadata and controls

917 lines (600 loc) · 39.9 KB

十一、杂项

在本章中,我们将介绍以下内容:

  • Java 7 中的处理周
  • 在 Java7 中使用货币
  • 使用 NumericShaper.Range 枚举支持数字显示
  • Java7 中的 JavaBean 改进
  • 在 Java7 中处理区域设置和 Locale.Builder 类
  • 处理空引用
  • 在 Java7 中使用新的位集方法

导言

本章将介绍 Java 7 中许多新添加的内容,这些内容不适合前面的章节。其中许多增强具有潜在的广泛应用,例如在 Java7 配方中的处理语言环境和 Locale.Builder 类中讨论的 java.lang.Objects类和 java.util.Locale类改进。其他的则更加专业化,比如对 java.util.BitSet类的改进,这在中使用了 Java7配方中的新位集方法。

在处理周数和货币方面有了一些改进。当前周和每年周数的计算受区域设置的影响。此外,现在可以确定平台上可用的货币。这些问题在 Java 7 和中的处理周中说明,在 Java 7配方中使用货币。

添加了一个新的枚举,简化了不同语言中数字的显示。使用 NumericShaper.Range 枚举来支持数字配方的显示,其中讨论了 java.awt.font.NumericShaper类在这方面的使用。Java7 配方中的JavaBean 改进讨论了 JavaBeans 支持方面的改进。

还有一些增强功能,它们不保证单独的配方。本导言的其余部分将专门讨论这些主题。

Unicode 6.0

Unicode 6.0是 Unicode 标准的最新版本。Java7 支持这个版本,增加了数千个字符和许多新方法。此外,正则表达式模式匹配支持 Unicode 6.0,使用**\u\x**转义序列。

许多新的字符块被添加到 Character.UnicodeBlock类中。Java 7 中增加了 Character.UnicodeScript枚举,以表示在Unicode 标准附录 24:脚本名称中定义的字符脚本。

有关 Unicode 标准附件 24:脚本名称的更多信息,请访问http://download.oracle.com/javase/7/docs/api/index.html

Character类中添加了几个方法以支持 Unicode 操作。下面说明了它们在字符串中的用法朝鲜圆, 这是基于区域设置的朝鲜元中文显示名称,以及中国大陆使用的简化脚本。将以下代码序列添加到新应用程序:

int codePoint = Character.codePointAt("朝鲜圆", 0);
System.out.println("isBmpCodePoint: " + Character.isBmpCodePoint(codePoint));
System.out.println("isSurrogate: " + Character.isSurrogate('朝'));
System.out.println("highSurrogate: " + (int)Character.highSurrogate(codePoint));
System.out.println("lowSurrogate: " + (int)Character.lowSurrogate(codePoint));
System.out.println("isAlphabetic: " + Character.isAlphabetic(codePoint));
System.out.println("isIdeographic: " + Character.isIdeographic(codePoint));
System.out.println("getName: " + Character.getName(codePoint));

执行时,输出应如下所示:

isBmpCodePoint:true

发布错误:错误

高级代理:55257

低代:57117

isAlphabetic:对

isIdeographic:true

getName:CJK 统一表意文字 671D

由于字符不是 Unicode 代理代码, highSurrogatelowSurrogate方法结果没有用处。

有关 Unicode 6.0 的更多信息,请访问http://www.unicode.org/versions/Unicode6.0.0/

原始类型及比较方法

Java7 引入了新的静态方法来比较原始数据类型 Boolean, byte, long, shortint。每个包装器类现在都有一个 compare方法,该方法将数据类型的两个实例作为参数,并返回一个表示比较结果的整数。例如,您以前可能需要使用 compareTo方法来比较两个布尔变量 x 和 y,如下所示:

Boolean.valueOf(x).compareTo(Boolean.valueOf(y))

您现在可以按如下方式使用 compare方法:

Boolean.compare(x,y);

虽然对于布尔数据类型而言,这对 Java 来说是新的, compare方法以前可用于 doublesfloats。此外,在 7 中,用于将字符串转换为数值的 parse, valueofdecode方法将接受数据类型为 Byte, Short, Integer, LongBigInteger的前导加号(+),以及之前接受该符号的 Float, DoubleBigDecimal

全球记录器

java.util.logging.Logger类有一个新方法 getGlobal,用于检索名为 GLOBAL_LOGGER_NAME的全局记录器对象。当 Logger类与 LogManager类一起使用时, Logger类的静态字段全局容易发生死锁,因为这两个类将互相等待以完成初始化。 getGlobal方法是访问 global logger对象的首选方法,以防止此类死锁。

JavaDocs 改进

从 Java7 开始,JavaDocs 有了显著的改进。从结构的角度来看,HTML 页面的生成现在是通过使用 HTMLTree类创建文档树来完成的,这将导致更准确的 HTML 生成和更少的无效页面。

JavaDocs 也有一些外部更改,其中一些更改是为了遵守新的第 508 节可访问性指南。开发这些功能是为了确保用于将基于 web 的文本转换为音频输出的屏幕阅读器能够准确地翻译 HTML 页面。这主要导致在表格上添加了更多的标题和标题。JavaDocs 现在还使用 CSS 样式表来简化对页面外观的更改。

JVM 性能增强

Java HotSpotTM 虚拟机的性能得到了改进。这些改进中的大多数不在开发人员的控制之下,并且本质上是专门化的。感兴趣的读者将在上找到有关这些增强功能的更多详细信息 http://docs.oracle.com/javase/7/docs/technotes/guides/vm/performance-enhancements-7.html

Java 7 中的处理周数

有些申请涉及一年中的周数和一年中的当前周数。众所周知,一年中有 52 周,但 52 周乘以每周 7 天等于每年 364 天,而不是实际的 365 天。周号用于表示一年中的一周。但这是如何计算的呢?Java7 引入了几种方法来支持确定一年中的星期。在本食谱中,我们将研究这些方法,并了解如何计算周相关值。ISO 8601标准提供了表示日期和时间的方法。 java.util.GregorianCalendar类支持本标准,以下章节中描述的除外。

准备好了吗

要使用这些基于周的方法,我们需要:

  1. 创建 Calendar类的实例。
  2. 适当使用其方法。

怎么做。。。

抽象 java.util.Calendar类的一些实现不支持周计算。为了确定 Calendar实现是否支持周计算,我们需要执行 isWeekDateSupported方法。如果提供了支持,则返回 true。要返回当前日历年的周数,请使用 getWeeksInWeekYear方法。要确定当前日期的星期,请使用以 WEEK_OF_YEAR为参数的 get方法。

  1. 创建一个新的控制台应用程序。在 main方法中增加以下代码:

    Calendar calendar = Calendar.getInstance();
    if(calendar.isWeekDateSupported()) {
    System.out.println("Number of weeks in this year: " + calendar.getWeeksInWeekYear());
    System.out.println("Current week number: " + calendar.get(Calendar.WEEK_OF_YEAR));
    }
  2. Execute the application. Your output should appear as follows, but the values will be dependent upon the date the application was executed:

    本年周数:53

    本周编号:48

它是如何工作的。。。

创建了 Calendar类的一个实例。这通常是 GregorianCalendar类的一个实例。 if语句由 isWeekDateSupported方法控制。它返回了 true,这导致了 getWeeksInWeekYearget方法的执行。在字段 WEEK_OF_YEAR中传递了 get方法,该字段返回当前周数。

还有更多。。。

可以使用 setWeekDate方法设置日期。此方法有三个参数指定年、周和日。它提供了一种基于周设置日期的便捷技术。下面通过将年份设置为 2012 年,将一周设置为一年中的第 16 周,将一天设置为一周中的第三天来说明此过程:

calendar.setWeekDate(2012, 16, 3);
System.out.println(DateFormat.getDateTimeInstance(
DateFormat.LONG, DateFormat.LONG).format(calendar.getTime()));

执行此代码时,我们得到以下输出:

2012 年 4 月 17 日 12:00:08 下午 CDT

计算一年中第一周和最后一周的方式取决于区域设置。 GregorianCalendar类的 WEEK_OF_YEAR字段范围从 1 到 53,其中 53 表示闰周。一年的第一周是:

  • 最早的七天时期
  • 从一周的第一天开始(getFirstDayOfWeek
  • 至少包含一周中的最少天数(getMinimalDaysInFirstWeek

getFirstDayOfWeekgetMinimalDaysInFirstWeek方法依赖于区域设置。例如, getFirstDayOfWeek方法返回一个整数,表示区域设置一周中的第一天。在美国是星期天,但在法国是星期一。

一周中的第一周和最后一周可能有不同的日历年。考虑下面的代码序列。日历设置为 2022 年第一周的第一天:

calendar.setWeekDate(2022, 1, 1);
System.out.println(DateFormat.getDateTimeInstance(
DateFormat.LONG, DateFormat.LONG).format(calendar.getTime()));

执行时,我们得到以下输出:

2021 年 12 月 26 日 12:15:39 下午中央标准时间

这表明这一周实际上是从上一年开始的。

此外, TimeZoneSimpleTimeZone类有一个 observesDaylightTime方法,如果时区遵守夏令时,则返回 true。下面的代码序列创建一个 SimpleTimeZone类的实例,然后确定是否支持夏令时。使用的时区为中央标准时间CST):

SimpleTimeZone simpleTimeZone = new SimpleTimeZone(
-21600000,
"CST",
Calendar.MARCH, 1, -Calendar.SUNDAY,
7200000,
Calendar.NOVEMBER, -1, Calendar.SUNDAY,
7200000,
3600000);
System.out.println(simpleTimeZone.getDisplayName() + " - " +
simpleTimeZone.observesDaylightTime());

执行此序列时,应获得以下输出:

中央标准时间-真

在 Java7 中使用 Currency 类

java.util.Currency类引入了四种新方法来检索有关可用货币及其属性的信息。此配方说明了以下方法的使用:

  • getAvailableCurrencies:此方法返回一组可用货币
  • getNumericCode:此方法返回货币的 ISO 4217 数字代码
  • getDisplayName:此重载方法返回表示货币显示名称的字符串。一个方法是传递一个 Locale对象。返回的字符串特定于该区域设置。

准备好了吗

getAvailableCurrencies方法是静态的,所以应该针对类名执行。其他方法针对 Currency类的实例执行。

怎么做。。。

  1. 创建一个新的控制台应用程序。在 main方法中增加以下代码:

    Set<Currency> currencies = Currency.getAvailableCurrencies();
    for (Currency currency : currencies) {
    System.out.printf("%s - %s - %s\n", currency.getDisplayName(),
    currency.getDisplayName(Locale.GERMAN),
    currency.getNumericCode());
    }
  2. When the application is executed, you should get output similar to the following. However, the first part of each may differ depending on the current locale.

    朝鲜元-北欧元-408

    欧元-欧元-978

    荷兰盾-荷兰盾-528

    福克兰群岛镑-福克兰基金-238

    丹麦克朗-Dänische 克朗-208

    伯利兹元-伯利兹元-84

它是如何工作的。。。

代码序列从生成表示当前系统配置的 Currency对象的 Set开始。重载的 getDisplayName方法针对集合的每个元素执行。 Locale.GERMAN参数用于说明此方法的使用。最后显示的值是货币的数字代码。

使用 NumericShaper.Range 枚举支持数字显示

在此配方中,我们将演示如何使用 java.awt.font.NumericShaper.Range枚举来支持使用 java.awt.font.NumericShaper类显示数字。有时,希望使用与当前使用的语言不同的语言显示数字。例如,在关于蒙古语的英语教程中,我们可能希望用英语解释数字系统,但使用蒙古语数字显示数字。 NumericShaper类提供了这种支持。新的 NumericShaper.Range枚举简化了这种支持。

准备好了吗

要使用 NumericShaper.Range枚举显示数字:

  1. 创建一个 HashMap来保存显示属性信息。
  2. 创建一个 Font对象来定义要使用的字体。
  3. 指定要显示文本的 Unicode 字符范围。
  4. 创建一个 FontRenderContext对象来保存有关如何测量要显示的文本的信息。
  5. 创建一个 TextLayout实例,并在 paintComponent方法中使用它来呈现文本。

怎么做。。。

我们将演示如何使用 NumericShaper.Range枚举来显示蒙古语数字。这是中示例的简化版本 http://download.oracle.com/javase/tutorial/i18n/text/shapedDigits.html

  1. 创建一个扩展 JFrame类的应用程序,如下所示。我们将说明 NumericShaper类在 NumericShaperPanel类中的用法:

    public class NumericShaperExample extends JFrame {
    public NumericShaperExample() {
    Container container = this.getContentPane();
    container.add("Center", new NumericShaperPanel());
    this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    this.setTitle("NumericShaper Example");
    this.setSize(250, 120);
    }
    public static void main(String[] args) {
    new NumericShaperExample();.setVisible(true)
    }
    NumericShaper.Range enumeration using, for digit display}
  2. 接下来,将 NumericShaperPanel类添加到项目中,如下所示:

    public class NumericShaperPanel extends JPanel {
    private TextLayout layout;
    public NumericShaperPanel() {
    String text = "0 1 2 3 4 5 6 7 8 9";
    HashMap map = new HashMap();
    Font font = new Font("Mongolian Baiti", Font.PLAIN, 32);
    map.put(TextAttribute.FONT, font);
    map.put(TextAttribute.NUMERIC_SHAPING,
    NumericShaper.getShaper(NumericShaper.Range. MONGOLIAN));
    FontRenderContext fontRenderContext =
    new FontRenderContext(null, false, false);
    layout = new TextLayout(text, map, fontRenderContext);
    }
    public void paintComponent(Graphics g) {
    Graphics2D g2d = (Graphics2D) g;
    layout.draw(g2d, 10, 50);
    }
    }
  3. 执行应用程序。您的输出应如下所示:

How to do it...

它是如何工作的。。。

main方法中,创建了 NumericShaperExample类的一个实例。在其构造函数中,创建了一个 NumericShaperPanel类的实例,并将其添加到窗口的中心。设置了窗口的标题、默认关闭操作和大小。接下来,窗口被显示出来。

NumericShaperPanel类的构造函数中,创建了一个文本字符串和一个 HashMap来保存显示的基本特性。此映射与要显示的字符串和映射一起用作 TextLayout构造函数的参数。文本以蒙古文显示,使用蒙古文白体字体和蒙古文范围。我们使用这个字体来演示 NumericShaper类的新方法。

NumericShaper类添加了新的方法,使得用不同的语言显示数值更容易。 getShaper方法重载,有一个版本接受 NumericShaper.Range枚举值。该值指定要使用的语言。添加了 NumericShaper.Range枚举,以表示给定语言中数字的一系列 Unicode 字符。

paintComponent方法中, Graphics2D对象被用作 draw方法的参数,以将字符串呈现给窗口。

还有更多。。。

getContextualShaper方法用于控制与不同脚本一起使用时数字的显示方式。这意味着如果在数字之前使用日语脚本,则显示日语数字。该方法采用一组 NumericShaper.Range枚举值。

shape方法还使用一个范围来指定在数组中给定开始和结束索引的 char 数组所使用的脚本。 getRangeSet方法返回 NumericShaper实例使用的一组 NumericShaper.Range

Java7 中的 JavaBean 增强

JavaBean是一种为 Java 应用程序构建可重用组件的方法。它们是遵循某些命名约定的 Java 类。Java7 中添加了几个 JavaBean 增强。在这里,我们将重点关注 java.beans.Expression类,它在执行方法时非常有用。已添加 execute方法以促进此功能。

准备好了吗

使用 Expression类执行方法:

  1. 如果需要,为方法创建一个参数数组。
  2. 创建 Expression类的实例,指定要针对其执行方法的对象、方法名称和所需的任何参数。
  3. 对表达式调用 execute方法。
  4. 如有必要,使用 getValue方法获得方法执行的结果。

怎么做。。。

  1. 创建一个新的控制台应用程序。创建两个类: JavaBeanExample,其中包含 main方法和 Person类。 Person类包含一个名称字段以及构造函数、getter 方法和 setter 方法:

    public class Person {
    private String name;
    public Person() {
    this("Jane", 23);
    }
    public Person(String name, int age) {
    this.name = name;
    }
    public String getName() {
    return name;
    }
    public void setName(String name) {
    this.name = name;
    }
    }
  2. JavaBeanExample类的 main方法中,我们将创建 Person类的一个实例,并使用 Expression类执行其 getNamesetName方法:

    public static void main(String[] args) throws Exception {
    Person person = new Person();
    String arguments[] = {"Peter"};
    Expression expression = new Expression(null, person, "setName", arguments);
    System.out.println("Name: " + person.getName());
    expression.execute();
    System.out.println("Name: " + person.getName());
    System.out.println();
    expression = new Expression(null, person, "getName", null);
    System.out.println("Name: " + person.getName());
    expression.execute();
    System.out.println("getValue: " + expression.getValue());
    }
  3. Execute the application. Its output should appear as follows:

    姓名:简

    姓名:彼得

    姓名:彼得

    获取值:彼得

它是如何工作的。。。

Person类使用了一个字段 name。 getNamesetName方法来自 main方法,其中创建了一个 Person实例。 Expression类的构造函数有四个参数。本例中未使用第一个参数,但可用于定义所执行方法的返回值。第二个参数是将对其执行方法的对象。第三个参数是包含方法名称的字符串,最后一个参数是包含方法使用的参数的数组。

在第一个序列中,使用参数 Peter执行 setName方法。应用程序的输出显示名称最初为 Jane,但在执行 execute方法后更改为 Peter

在第二个序列中,执行 getName方法。 getValue方法返回方法执行的结果。输出显示 getName方法返回 Peter

还有更多。。。

java.bean包的类还有其他增强功能。例如, toString方法在 FeatureDescriptorPropertyChangeEvent类中被重写,以提供更有意义的描述。

Introspector类提供了一种学习 JavaBean 的属性、方法和事件的方法,而无需使用反射 API,这可能会很繁琐。该类添加了一个 getBeanInfo方法,该方法使用 Inspector类的控制标志来影响返回的 BeanInfo对象。

添加了 Transient注释以控制包含的内容。属性的 true值表示应忽略带注释的特征。

一个新的构造函数被添加到接受一个 InputSource对象的 XMLDecoder类中。此外,还添加了 createHandler方法,该方法返回一个 DefaultHandler对象。此处理程序用于解析由 XMLEncoder类创建的 XML 存档。

XMLEncoder类中添加了一个新的构造函数。这允许使用具有特定缩进的特定字符集将 JavaBeans 写入到 OutputStream

在 Java7 中处理 Locale 和 Locale.Builder 类

java.util.Locale.Builder类已添加到 Java7 中,并提供了创建区域设置的简单方法。 Locale.Category枚举也是新的,它使使用不同的区域设置进行显示和格式化变得容易。我们将首先研究 Locale.Builder类的使用,然后研究其他语言环境改进,以及Locale.Category枚举的使用。。。部分

准备好了吗

要构建并使用新的 Locale对象:

  1. 创建 Builder类的实例。
  2. 使用类的相关方法设置所需的属性。
  3. 根据需要使用 Locale对象。

怎么做。。。

  1. 创建一个新的控制台应用程序。在 main方法中,添加以下代码。我们将创建一个基于东亚美尼亚语的新语言环境,使用意大利的拉丁语脚本。通过使用 setWeekDate方法显示 2012 年第 16 周第三天的日期来演示区域设置。此方法在 Java 7 配方中的*处理周

    Calendar calendar = Calendar.getInstance();
    calendar.setWeekDate(2012, 16, 3);
    Builder builder = new Builder();
    builder.setLanguage("hy");
    builder.setScript("Latn");
    builder.setRegion("IT");
    builder.setVariant("arevela");
    Locale locale = builder.build();
    Locale.setDefault(locale);
    System.out.println(DateFormat.getDateTimeInstance(
    DateFormat.LONG, DateFormat.LONG).format(calendar.getTime()));
    System.out.println("" + locale.getDisplayLanguage());

    中进行了更详细的讨论*

  2. 第二个示例使用简化脚本构建基于中文的区域设置,在中国大陆使用:

    builder.setLanguage("zh");
    builder.setScript("Hans");
    builder.setRegion("CN");
    locale = builder.build();
    Locale.setDefault(locale);
    System.out.println(DateFormat.getDateTimeInstance(
    DateFormat.LONG, DateFormat.LONG).format(calendar.getTime()));
    System.out.println("" + locale.getDisplayLanguage());
  3. When executed, the output should appear as follows:

    2012 年 4 月 17 日 CDT 时间下午 7:25:42

    亚美尼亚语

    2012 年 4.月 17 日 下午 07 时 25 分 42 秒

    中文

它是如何工作的。。。

Builder对象已创建。使用这个对象,我们应用了一些方法来设置语言环境的语言、脚本和区域。然后执行 build方法并返回 Locale对象。我们使用此区域设置来显示日期和区域设置的显示语言。这是执行了两次。首先是亚美尼亚语,然后是汉语。

还有更多。。。

能够标记一条信息以指示所使用的语言非常重要。标签用于此目的。IETF BCP 47标准定义了一组标准标签。Java7 符合这个标准,并添加了几个方法来处理标记。

该标准支持标记扩展的概念。这些扩展可用于提供有关区域设置的更多信息。有两种类型:

  • Unicode 语言环境扩展
  • 私用扩展

Unicode 语言环境扩展由Unicode 通用语言环境数据存储库CLDR)(定义 http://cldr.unicode.org/ )。这些扩展涉及非语言信息,例如货币和日期。CLDR 维护区域设置信息的标准存储库。专用扩展用于指定特定于平台的信息,例如与操作系统或编程语言相关的信息。

有关 IETF BCP 47 标准的更多信息,请参见http://tools.ietf.org/rfc/bcp/bcp47.txt

扩展由键/值对组成。该键为单个字符,值的格式如下:

SUBTAG ('-' SUBTAG)*

SUBTAG由一系列字母数字字符组成。对于 Unicode 区域设置扩展,该值必须至少为两个字符,但长度不得超过 8 个字符。对于专用扩展名,允许使用 1 到 8 个字符。所有扩展字符串都不区分大小写。

Unicode 语言环境扩展的密钥是u,而对于私用扩展,则是x。可以将这些扩展添加到区域设置以提供附加信息,例如要使用的日历编号类型。

下表列出了可使用的键:

|

关键代码

|

描述

| | --- | --- | | ca | 确定日期的日历算法 | | co | 排序规则类型语言中使用的顺序 | | ka | 用于指定排序的排序规则参数 | | | 货币类型信息 | | nu | 编号系统 | | va | 通用变型 |

键和类型的示例见下表:

|

键/类型

|

意思

| | --- | --- | | 努阿姆洛 | 亚美尼亚小写数字 | | 加州印第安人 | 印度历法 |

添加了几个方法来使用这些扩展。 getExtensionKeys方法返回与区域设置一起使用的所有键的一组 Character对象。类似地, getUnicodeLocaleAttributesgetUnicodeLocaleKeys方法返回一组字符串,列出可用的属性和 Unicode 键。如果没有可用的扩展,则这些方法返回一个空集。如果键已知, getExtension方法或 getUnicodeLocaleType方法将返回包含该键值的字符串。

对于给定的区域设置, getScript, getDisplayScripttoLanguageTag方法分别返回脚本、脚本的可显示名称和区域设置的格式良好的BCP 47标记。 getDisplayScript方法还将返回脚本的可显示名称,给定区域设置作为参数。

下一节将讨论如何使用 setDefault方法来控制同时使用两个不同区域设置的信息显示。

使用 Locale.Category 枚举来显示使用两个不同 Locale 的信息

Locale.Category枚举已添加到 Java 7 中。它有两个值, DISPLAYFORMAT。这允许为格式类型资源(日期、数字和货币)和显示资源(应用程序的 GUI 方面)设置默认区域设置。例如,应用程序的一部分可以设置格式以适应一种语言环境,例如 JAPANESE,同时在另一种语言环境中显示相关信息,例如 GERMAN.

考虑下面的例子:

Locale locale = Locale.getDefault();
Calendar calendar = Calendar.getInstance();
calendar.setWeekDate(2012, 16, 3);
System.out.println(DateFormat.getDateTimeInstance(
DateFormat.LONG, DateFormat.LONG).format(calendar.getTime()));
System.out.println(ocale.getDisplayLanguage());
Locale.setDefault(Locale.Category.FORMAT, Locale.JAPANESE);
Locale.setDefault(Locale.Category.DISPLAY, Locale.GERMAN);
System.out.println(DateFormat.getDateTimeInstance(
DateFormat.LONG, DateFormat.LONG).format(calendar.getTime()));
System.out.println(locale.getDisplayLanguage());

当执行此代码序列时,您应该得到如下类似的输出。初始日期和显示语言可能因默认区域设置而异。

2012 年 4 月 17 日 CDT 时间下午 7:15:14

英语

2012/04/17 19:15:14 CDT

英语

检索默认区域设置,并使用 setWeekDate方法设置日期。在 Java 7 配方中的使用周中对该方法进行了更详细的讨论。接下来,打印日期和显示语言。重复显示,但使用 setDefault方法更改了默认区域设置。显示资源更改为使用 Locale.JAPANESE,格式类型资源更改为 Locale.GERMAN。产出反映了这一变化。

处理空引用

一个相当常见的例外是 java.lang.NullPointerException。当试图对包含 null 值的引用变量执行方法时,会发生这种情况。在本配方中,我们将研究可用于解决此类异常的各种技术。

已经引入了 java.util.Objects类,它提供了许多静态方法,用于解决需要处理空值的情况。此类的使用简化了空值的测试。

还有更多。。。第节检查空列表的使用,可以使用空列表代替返回 null。 java.util.Collections类有三个返回空列表的方法。

准备好了吗

要使用 Objects类重写 equalshashCode方法:

  1. 重写目标类中的方法。
  2. 使用 Objects类“ equals方法避免显式代码检查 equals方法中的空值。
  3. 使用 Objects类的 hashCode方法可以避免需要显式代码来检查 hashCode方法中的空值。

怎么做。。。

  1. 创建一个新的控制台应用程序。我们将创建一个 Item类来演示 Objects类的使用。在 Item类中,我们将重写 equalshashCode方法。这些方法是由 NetBeans 的 insert code 命令生成的。我们使用这些方法,因为它们说明了 Objects类的方法,并且结构良好。首先创建如下类:

    public class Item {
    private String name;
    private int partNumber;
    public Item() {
    this("Widget", 0);
    }
    public Item(String name, int partNumber) {
    this.name = Objects.requireNonNull(name);
    this.partNumber = partNumber;
    }
    public String getName() {
    return name;
    }
    public void setName(String name) {
    this.name = Objects.requireNonNull(name);
    }
    public int getPartNumber() {
    return partNumber;
    null referenceshandling}
    public void setPartNumber(int partNumber) {
    this.partNumber = partNumber;
    }
    }
  2. 接下来,覆盖 equalshashCode方法,如下所示。它们提供检查空值的代码:

    @Override
    public boolean equals(Object obj){
    if (obj == null) {
    return false;
    }
    if (getClass() != obj.getClass()) {
    return false;
    }
    final Item other = (Item) obj;
    if (!Objects.equals(this.name, other.name)) {
    return false;
    }
    if (this.partNumber != other.partNumber) {
    return false;
    }
    return true;
    }
    @Override
    public int hashCode() {
    int hash = 7;
    hash = 47 * hash + Objects.hashCode(this.name);
    hash = 47 * hash + this.partNumber;
    return hash;
    }
  3. 通过添加 toString方法

    @Override
    public String toString() {
    return name + " - " + partNumber;
    }

    完成本课程

  4. 接下来,在 main方法中增加以下内容:

    Item item1 = new Item("Eraser", 2200);
    Item item2 = new Item("Eraser", 2200);
    Item item3 = new Item("Pencil", 1100);
    Item item4 = null;
    System.out.println("item1 equals item1: " + item1.equals(item1));
    System.out.println("item1 equals item2: " + item1.equals(item2));
    System.out.println("item1 equals item3: " + item1.equals(item3));
    System.out.println("item1 equals item4: " + item1.equals(item4));
    item2.setName(null);
    System.out.println("item1 equals item2: " + item1.equals(item2));
  5. Execute the application. Your output should appear as follows:

    第 1 项等于第 1 项:真

    第 1 项等于第 2 项:真

    第 1 项等于第 3 项:假

    第 1 项等于第 4 项:假

    线程“main”java.lang.NullPointerException中出现异常

    位于 java.util.Objects.requirennull(Objects.java:201)

    在 packt.Item.setName(Item.java:23)

    位于 packt.NullReferenceExamples.main(NullReferenceExamples.java:71)

正如我们将很快看到的, NullPointerException是尝试为项的名称字段分配空值的结果。

它是如何工作的。。。

equals方法中,首先进行测试以确定通过的对象是否为空。如果是,则返回 false。进行了测试,以确保这些类属于同一类型。然后使用 equals方法查看两个名称字段是否相等。

Objects类“ equals方法的行为如下表所示。相等的含义由第一个参数的 equals方法确定:

|

第一个论点

|

第二个论点

|

退换商品

| | --- | --- | --- | | 非空 | 非空 | true如果是同一对象,则为 false | | 非空 | 无效的 | false | | 无效的 | 非空 | false | | 无效的 | 无效的 | true |

最后一个测试比较了两个整数 partNumber字段是否相等。

Item类的 hashCode方法中, Objects类的 hashCode方法应用于名称字段。如果此方法的参数为 null,则它将返回 0,否则它将返回参数的哈希代码。然后使用 partNumber来计算散列码的最终值。

注意在两个参数构造函数中使用了 requireNonNull方法和 setName方法。该方法检查非空参数。如果参数为 null,则抛出一个 NullPointerException。这有效地捕获了应用程序早期的潜在错误。

requireNonNull方法被第二个版本重载,该版本接受第二个字符串参数。当发生异常时,此参数更改生成的消息。将 setName方法的主体替换为以下代码:

this.name = Objects.requireNonNull(name, "The name field requires a non-null value");

重新执行应用程序。异常消息现在将显示如下:

线程“main”java.lang.NullPointerException 中出现异常:名称字段需要非空值

还有更多。。。

还有其他几个 Objects类方法可能会引起兴趣。此外,第二节将检查空迭代器的使用,以避免空指针异常。

附加对象类方法

Objects类的 hashCode方法重载。第二个版本采用数量可变的对象作为参数。该方法将使用此对象序列生成哈希代码。例如, Item类的 hashCode方法可以写成:

@Override
public int hashCode() {
return Objects.hash(name,partNumber);
}

deepEquals方法对两个对象进行了深入的比较。这意味着它比较的不仅仅是参考值。两个空参数被认为是完全相等的。如果两个参数都是数组,则调用 Arrays.deepEqual方法。对象的相等性由第一个参数的 equals方法确定。

compare方法用于比较返回负值、零值或正值的前两个参数,具体取决于参数之间的关系。通常,返回 0 表示参数相同。负值表示第一个参数小于第二个参数。正值表示第一个参数大于第二个参数。

如果该方法的参数相同,或者如果两个参数都为 null,则该方法将返回零。否则,使用 Comparator接口的 compare方法确定返回值。

Objects类“ toString方法用于确保即使对象为 null 也返回字符串。以下顺序说明了此重载方法的使用:

Item item4 = null;
System.out.println("toString: " + Objects.toString(item4));
System.out.println("toString: " + Objects.toString(item4, "Item is null"));

执行时,该方法的第一次使用显示单词null。在第二个版本中,字符串参数显示如下:

toString:null

toString:项为空

使用空迭代器避免空指针异常

避免 NullPointerException的一种方法是在无法创建列表时返回非空值。返回一个空的 Iterator可能是有益的。

在 Java 7 中, Collections类添加了三个新方法,它们返回一个 Iterator、一个 ListIterator或一个 Enumeration,所有这些方法都是空的。通过返回空,可以使用它们,而不会引发空指针异常。

为了演示空列表迭代器的使用,创建一个新方法,返回一个泛型 ListIterator<String>,如下代码所示。 if语句用于返回 ListIterator或空 ListIterator:

public static ListIterator<String> returnEmptyListIterator() {
boolean someConditionMet = false;
if(someConditionMet) {
ArrayList<String> list = new ArrayList<>();
// Add elements
ListIterator<String> listIterator = list.listIterator();
return listIterator;
}
else {
return Collections.emptyListIterator();
}
}

使用以下 main方法测试迭代器的行为:

public static void main(String[] args) {
ListIterator<String> list = returnEmptyListIterator();
while(())String item: list {
System.out.println(item);
}
}

当它执行时,应该没有输出。这表示迭代器为空。如果我们返回 null,我们将收到一个 NullPointerException

Collections类的静态 emptyListIterator方法返回一个 ListIterator,其方法工作如下表所示:

|

方法

|

行为

| | --- | --- | | hasNext``hasPrevious | 始终返回 false | | next``Previous | 总是抛出 NoSuchElementException | | remove``set | 总是抛出 IllegalStateException | | add | 总是抛出UnsupportedOperationException | | nextIndex | 始终返回 0 | | previousIndex | 总是返回-1 |

emptyIterator方法将返回一个具有以下行为的空迭代器:

|

方法

|

行为

| | --- | --- | | hasNext | 始终返回 false | | next | 总是抛出 NoSuchElementException | | remove | 总是抛出 IllegalStateException |

emptyEnumeration方法返回一个空枚举。其 hasMoreElements将始终返回 false,其 nextElement将始终抛出 NoSuchElementException异常。

在 Java 7 中使用新的位集方法

java.util.BitSet类通过最新版本的 Java 获得了几个新方法。它们旨在简化对大型位集的操作,并提供对位位置信息的更容易访问。位集可用于优先级队列或压缩数据结构。这个食谱展示了一些新方法。

准备好了吗

要使用新的 BitSet方法:

  1. 创建一个 BitSet的实例。
  2. 根据需要对 BitSet对象执行方法。

怎么做。。。

  1. 创建一个新的控制台应用程序。在 main方法中,创建 BitSet对象的实例。然后声明一个长数字数组,并使用静态 valueOf方法将 BitSet对象设置为该长数组的值。添加一个 println语句,这样我们可以看到我们的长数字在 BitSet:

    BitSet bitSet = new BitSet();
    long[] array = {1, 21, 3};
    bitSet = BitSet.valueOf(array);
    System.out.println(bitSet);

    中的表示方式

  2. 接下来,使用 toLongArray方法将 BitSet转换回一个长数字数组。使用 for 循环打印出数组中的值:

    long[] tmp = bitSet.toLongArray();
    for (long number : tmp) {
    System.out.println(number);
    }
  3. Execute the application. You should see the following output:

    {0,64,66,68,128,129}

    1

    21

    3

它是如何工作的。。。

在创建 BitSet对象之后,我们创建了一个包含三个 long数字的数组,这些数字表示我们希望在 BitSet中使用的位序列。 valueOf方法采用这种表示并将其转换为位序列。

当我们打印出 BitSet时,我们看到序列{0,64,66,68,128,129}。 BitSet中的每个数字代表在我们的位序列中设置的位的索引。例如,0 表示数组中的 long数字 1,因为用于表示该数字的位的索引位于位置 0。同样,位 64、66 和 68 被设置为表示我们的 long编号 21。序列中的第 128 位和第 129 位被设置为代表我们的 long数字 3。在下一节中,当我们使用 toLongArray方法将 BitSet返回到其原始形式时,我们颠倒了这个过程。

在我们的示例中,我们使用了一个 long数字数组。 byte, LongBufferByteBuffer阵列也有类似的 valueOf方法。当使用 LongBufferByteBuffer数组时, valueOf方法不会修改缓冲区, BitSet不能转换回缓冲区。相反,必须使用 toLongArray方法或将 BitSet转换为字节数组的类似 toByteArray方法来转换 BitSet

还有更多。。。

有两种新方法可用于定位集合或清除 BitSet中的位。方法 previousSetBit将表示特定索引的整数作为其参数,并返回表示所设置的 BitSet中最近位的整数。例如,在前面的示例中添加以下代码序列(使用长数字 {1, 21, 3}):表示的 BitSet

System.out.println(bitSet.previousSetBit(1));

这将导致输出为整数 0。这是因为我们将索引 1 的参数传递给了 previousSetBit方法,在 BitSet中设置的最接近的前一位位于索引 0 处。

previousClearBit方法以类似的方式运行。如果我们在前面的示例中执行以下代码:

System.out.println(bitSet.previousClearBit(66));

我们将得到整数 65 的输出。位于索引 65 处的位与我们的论点 66 最接近。如果 BitSet中不存在这样的位,两种方法都将返回-1。