21.3 java.langとjava.utilパッケージのその他の主要なクラス（Systemクラス、システムプロパティ、プロパティファイル、国際化対応、Localeクラスなど）～Java Basic編

### はじめに

#### 自己紹介

皆さん、こんにちは、Udemy講師の斉藤賢哉です。私はこれまで、25年以上に渡って企業システムの開発に携わってきました。特にアーキテクトとして、ミッションクリティカルなシステムの技術設計や、Javaフレームワーク開発などの豊富な経験を有しています。
様々なセミナーでの登壇や雑誌への技術記事寄稿の実績があり、また以下のような書籍も執筆しています。
* [「マスタリング Java EE 5」（2007年）](https://amzn.asia/d/j0txqK5)
* [「アプリケーションアーキテクチャ設計パターン」（2017年）](https://amzn.asia/d/9avaCyF)

いずれも**Java EE**（**Jakarta EE**）を中心にした企業システム開発のための書籍です。中でも [「アプリケーションアーキテクチャ設計パターン」](https://amzn.asia/d/9avaCyF)は、（Javaに限定されない）比較的普遍的なテーマを扱っており、内容的にはまだまだ陳腐化していないため、興味のある方は是非手に取っていただけると幸いです（中級者向け）。

#### Udemy講座のご紹介

この記事の内容は、私が講師を務めるUdemy講座『**Java Basic編**』の一部の範囲をカバーしたものです。『**Java Basic編**』はこちらの[**リンク**](https://www.udemy.com/course/learn-java-se-basic/?referralCode=594C958456356851C97C)から購入できます（セールス対象外のためいつも同じ価格）。また**定価の約30%OFFで購入可能なクーポン**をQiita内で定期的に発行していますので、興味のある方は、ぜひ私の他の記事をチェックしてみてください。

この講座は、以下のような皆様にお薦めします。
* Javaの言語仕様や文法を正しく理解すると同時に、現場での実践的なスキル習得を目指している方
* 新卒でIT企業に入社、またはIT部門に配属になった、新米システムエンジニアの方
* 長年IT部門で活躍されてきた中堅層の方で、学び直し（リスキル）に挑戦しようとしている方
* 今後、フリーランスエンジニアとしてのキャリアを検討している方
* 「**Chat GPT**」のエンジニアリングへの活用に興味のある方
* 「**Oracle認定Javaプログラマ**」の資格取得を目指している方
* IT企業やIT部門の教育研修部門において、新人研修やリスキルのためのオンライン教材をお探しの方

#### この記事を含むシリーズ全体像

この記事はJava SEの一部の機能・仕様を取り上げたものですが、一連のシリーズになっており、**シリーズ全体でJava SEを網羅**しています。また認定資格である「**Oracle認定Javaプログラマ**」（Silver、Gold）の範囲もカバーしています。シリーズの全体像および「Oracle認定Javaプログラマ」の範囲との対応関係については、以下を参照ください。

https://qiita.com/KenyaSaitoh/items/4711a078a851510bc4da

## 21.3 java.langとjava.utilパッケージのその他のクラス

### チャプターの概要

このチャプターでは、java.langパッケージとjava.utilパッケージに所属する、その他の利用頻度の高いクラスとそのAPIについて学びます。

### 21.3.1 Systemクラスの特徴とシステムプロパティ

#### Systemクラスの特徴

java.lang.Systemは、当該Javaプログラムのシステム環境を表すためのクラスです。このクラスには、様々なスタティックなフィールドやメソッドが定義されていますが、オブジェクトを生成することはできません。
このクラスには、以下のようなスタティックフィールドがあります。

* in  … java.io.InputStream型。標準入力を表す。
* out … java.io.PrintStream型。標準出力を表す。
* err … java.io.PrintStream型。標準エラー出力を表す。

この中でも、特に馴染みのあるoutフィールドは標準出力を表すもので、java.io.PrintStream型として定義されています。PrintStreamクラスの詳細はここでは割愛しますが、データを簡易的に出力するための機能を持ったクラスと理解しておけば十分でしょう。PrintStreamクラスの代表的なAPIはprintln()メソッドで、指定された文字列を出力して改行します。従って`System.out.println()`と呼び出すことで、指定された文字列が当該Javaプログラムのシステム環境の標準出力に書き込まれます。Eclipse上でJavaプログラムを実行する場合は、Eclipseのコンソールビューが標準出力先になるため、ここに文字列が表示されます。

#### Scannerクラスと標準入力

Systemクラスのinフィールドは標準入力、すなわちキーボードからの入力を表します。ここでは、このSystem.inフィールドとjava.util.Scannerクラスを組み合わせることで、キーボードから入力された文字列をJavaプログラムに読み込む方法を紹介します。Scannerクラスとは、標準入力やファイルなどからデータを読み込んで、文字列を取り出したり、正規表現によるパターンマッチングを行うためのクラスです。ただしファイルからの入力やパターンマッチングという用途では、他にも様々な効率的な方法があるため、ここでは標準入力から読み込んだ文字列を処理するためのクラスとして取り上げます。
Scannerクラスには幾つかのコンストラクタがありますが、標準入力では以下を使用します。

* Scanner(InputStream)

またScannerクラスにおいて、標準入力から読み込んだ文字列を取り出すためには、以下のようなAPIを使用します。

|API(メソッド）|説明|
|--|--|
|String **nextLine**()|標準入力された一行分の文字列を取り出す。|
|String **next**()|標準入力された文字列のうち、次の空白文字までの文字列を取り出す。|

それでは、具体的にコードを見ていきましょう。以下は、System.inフィールドとScannerクラスにより、キーボードから入力された文字列を処理するためのコードです。

```java:snippet (pro.kensait.java.basic.lsn_21_3_1.Main_SystemIn)
public static void main(String[] args) {
    Scanner scan = new Scanner(System.in); //【1】
    String str = scan.nextLine(); //【2】
    System.out.println("取り込んだ文字列 => " + str);
    scan.close();
}
```

ScannerクラスのコンストラクタにSystem.inフィールドを指定する【1】ことで、キーボードから入力された文字列を読み込むためのScannerオブジェクトを生成します。次にこのScannerのnextLine()メソッドを呼ぶと【2】、キーボードからの入力待ち状態になります。キーボードからの入力が終わり改行すると、入力された文字列が変数strに読み込まれ、処理が先に進みます。なおこのコードのnextLine()メソッドを、next()メソッドに変更することもできます。こうするとキーボードから入力された文字列が、next()、next()と呼び出すたびに順番に取り出されます。

#### Systemクラスの主要なAPI

Systemクラスには数多くのAPIが定義されていますが、ここではその中から主要なものを取り上げます。

|API(メソッド）|説明|
|--|--|
|static void **exit**(int)|指定されたステータスで、現在実行しているJavaプログラムを終了する。|
|static String **getenv**(String)|指定された環境変数の値を返す。|
|static String **getProperty**(String)|指定されたキーに対応するシステムプロパティの値を返す。|
|static Properties **getProperties**()|システムプロパティのリストを、Propertiesオブジェクトとして返す。|

まずexit()メソッドです。通常Javaプログラムは、メインメソッドが終了するとそのまま終了します。もし何らかの要件により途中でJavaプログラムを終了させる必要がある場合は、exit()メソッドを呼び出してください。

```java:snippet
System.exit(0);
```

exit()メソッドに指定する値は、このプロセス（Javaプログラム）の終了ステータスとして扱われます。終了ステータスは慣習的に、0の場合は正常終了を、0以外の場合は異常終了を表すものとされています。
次にgetenv()メソッドです。このメソッドは、環境変数を調べるために使用します。例えば環境変数"COMPUTERNAME"であれば、以下のように取得します。

```java:snippet_1 (pro.kensait.java.basic.lsn_21_3_1.Main)
String computer = System.getenv("COMPUTERNAME");
```

最後にgetProperty()メソッドですが、このメソッドによって取得されるシステムプロパティについては、次項で説明します。

#### システムプロパティ

Javaランタイムは当該プログラムのシステム環境に関する情報を、システムプロパティとして保持しています。
システムプロパティには数多くの種類がありますが、その中でも特に主要なものを以下に示します。

【表21-3-1】主要なシステムプロパティ

|分類|システムプロパティ名|説明|
|--|--|--|
|Java実行環境|java.home|Javaのインストール先ディレクトリ|
||java.version|Javaのバージョン|
||java.vendor|Javaの提供ベンダー|
||java.vm.name|JVMの実装名|
||java.class.version|「Javaクラス形式」のバージョン|
||jdk.module.main|モジュール名 ※『Java Advanced編』参照|
||jdk.module.path|モジュールパス ※『Java Advanced編』参照|
||java.class.path|Javaクラスパス|
|OS環境|os.name|OS名|
||os.arch|OSのアーキテクチャ|
||os.version|OSのバージョン|
|区切り文字|file.separator|ファイル区切り文字|
||path.separator|パス区切り文字|
||line.separator|改行コード|
|ユーザー環境|user.name|ユーザのアカウント名|
||user.home|ユーザのホームディレクトリ|
||user.dir|当該プログラムを実行した現在のディレクトリ|

システムプロパティの値は、SystemクラスのgetProperty()メソッドで取得します。例えば以下のようにすると、当該Javaプログラムを実行しているユーザーのホームディレクトリを取得することができます。

```java:snippet_2 (pro.kensait.java.basic.lsn_21_3_1.Main)
System.getProperty("user.home");
```

Javaプログラム内でシステム環境に関する情報が必要になった場合は、このようにしてシステムプロパティを調べます。
システムプロパティを調べる用途としてよく使われるのが、区切り文字や改行コードの取得です。区切り文字（ファイルやパス）や改行コードは、以下のようにOSによって異なります。

* ファイル区切り文字 … Windowsでは"¥"、macOSやLinuxでは"/"。
* パス区切り文字     … Windowsでは";"、macOSやLinuxでは":"。
* 改行コード         … Windowsでは"¥r¥n"、macOSやLinuxでは"\n"。

文字列リテラル内にこういった文字（"¥"、"/"など）を直接埋め込んでしまうと、環境（OS）に依存したコードになってしまいます。そこでシステムプロパティからこれらの文字を取得することで、このような問題を回避します。例えば文字列の中に改行コードを埋め込む必要がある場合は、以下のようにすると良いでしょう。

```java:snippet_3 (pro.kensait.java.basic.lsn_21_3_1.Main)
String separator = System.getProperty("line.separator");
String message = "こんにちは！" + separator + "私はAlice、25歳です。";
```

### 21.3.2 プロパティファイル

#### プロパティファイルとは

プロパティファイルとはプログラム内で使用する静的な情報を記述するためのファイルで、主に以下のような情報を定義します。

* 設定情報（データベースとの接続情報など）
* メッセージ（エラーメッセージなど）
* 将来的に変更になる可能性の高い値（消費税率など）

これらの値は、ソースコードに直接記述するよりも専用のファイルに外出しした方が、Javaプログラムとしての保守性が向上するのは明らかでしょう。
Javaにおけるプロパティファイルは、以下のようなルールで記述します。

* 一行ごとにキーと値の組み合わせを記述する。
* キーと値は"="、":"、半角スペースのいずれかで区切る。
※"="、":"で区切れば、値に半角スペースを埋め込み可能
* 行頭に"#"、"!"を記述することで、その行をコメントアウトする。

このようにして作成されたプロパティファイルは、".properties"という拡張子で保存します。
なおプロパティファイルはこのように、キーと値を組み合わせたシンプルな構造のため、表現力には限界があります。より複雑なデータ構造を表現したい場合は、XMLファイルやYAMLファイルを使う必要がありますが、これらについては本コースの対象外になります。
それではここで、プロパティファイルの記述例を見ていきましょう。

```
# Aliceのデータ
name=アリス
age 25
address:中央区 1-1-1
```

このような人物の属性情報は、実際のアプリケーションではデータベースで管理するのが一般的ですが、ここでは便宜上プロパティファイルに定義しています。次項以降では、このプロパティファイルを読み込んで、そこから値を取得するための方法を説明します。

なおJava 8まではプロパティファイルの文字コードは「ISO 8859-1」と決められていたため、日本語を含むデータを記述した場合は、JDKに付属する「native2asciiコマンド」を使い事前に「ISO 8859-1」に変換しておく必要がありました。
このコマンドは以下のように使います。

```
native2ascii  オリジナルのプロパティファイル名  変換後のプロパティファイル名
```

ただしJava 9からは、文字コードとしてUTF-8が使えるようになったため、昨今ではこのコマンドを使用する機会はほとんどありません。

#### プロパティファイルの配置場所と読み込み方

プロパティファイルの配置には、以下のような戦略があります。

* 戦略A：ファイルシステム上の任意の場所に配置する
* 戦略B：クラスパス上に配置する

どちらにするかは、そこに記述される情報の特性によって選択します。例えばデータベースとの接続情報など、実行環境に依存する情報を記述したプロパティファイルは、Javaプログラムの外部にあたるファイルシステムに配置する方（戦略A）が望ましいでしょう。その一方でエラーメッセージや消費税率など、実行環境には関わらない情報を記述したプロパティファイルは、クラスパスの設定された場所や、クラスファイルと同じJARファイル内に配置する方（戦略B）が良いでしょう。

続いてプロパティファイルを読み込むための方法には、以下のようなものがあります。

(方法1) java.util.Propertiesクラスを利用する
(方法2) java.util.ResourceBundleクラスを利用する

java.util.Propertiesクラスを利用する方法(方法1)では、プロパティファイルは、ファイルシステム上の任意の場所から読み込む（戦略A）ことも、クラスパスから読み込む（戦略B）ことも可能です。一方java.util.ResourceBundleクラスを利用する方法(方法2)では、プロパティファイルは基本的にはクラスパスから読み込みます（戦略B）。

#### プロパティファイルの読み込み方法1：Propertiesクラス利用

ここでは、java.util.Propertiesクラスによってプロパティファイルを読み込む方法を説明します。
以下は、ファイルシステム上の特定の場所、具体的にはホームディレクトリに配置された"MyResource.properties"ファイルを読み込み、プロパティ値を取得するためのコードです。

```java:snippet_1 (pro.kensait.java.basic.lsn_21_3_2.Main)
Properties props = new Properties(); //【1】
props.load(Files.newBufferedReader(
        Paths.get(System.getProperty("user.home"), "MyResource.properties"),
        StandardCharsets.UTF_8)); //【2】
String name = props.getProperty("name"); //【3】
int age = Integer.parseInt(props.getProperty("age")); //【4】
String address = props.getProperty("address"); //【5】
```


まず引数のないコンストラクタにより、Propertiesクラスのオブジェクトを生成します【1】。次にPropertiesオブジェクトのload()メソッドに、FilesクラスのnewBufferedReader()メソッド呼び出しの戻り値（java.io.Reader）を渡すことで、プロパティファイルを読み込みます【2】。ファイル読み込み処理は『Java Advanced編』で取り上げますので説明は割愛しますが、ここではホームディレクトリに配置された"MyResource.properties"ファイルを読み込んでいます。プロパティファイルをPropertiesに読み込んだら、getProperty()メソッドにキーを渡すことで、対応するプロパティ値を取得します【3、4、5】。このとき取得される値は文字列のため、age（年齢）のように数値型として扱いたい場合は、Integer.parseInt()メソッドを呼び出すなどして文字列から適切に変換します。なお本来は【2】の処理でIOExceptionが発生するため例外ハンドリングが必要ですが、このコードでは便宜上、割愛しています。

#### プロパティファイルの読み込み方法2：ResourceBundleクラス利用

ここでは、java.util.ResourceBundleクラスによってプロパティファイルを読み込む方法を説明します。以下は、クラスパス上に配置された"MyResource.properties"ファイルを読み込み、プロパティ値を取得するためのコードです。

```java:snippet_2 (pro.kensait.java.basic.lsn_21_3_2.Main)
ResourceBundle rb = ResourceBundle.getBundle("MyResource"); //【1】
String name = rb.getString("name"); //【2】
int age = Integer.parseInt(rb.getString("age")); //【3】
String address = rb.getString("address"); //【4】
```

まずResourceBundleクラスのgetBundle()メソッドに、拡張子を除いたプロパティファイル名を指定します【1】。するとクラスパス上から"MyResource.properties"が検索され、ResourceBundleオブジェクトに読み込まれます。プロパティファイルをResourceBundleに読み込んだら、getString()メソッドにキーを渡すことで、対応するプロパティ値を取得します【2、3、4】。取得される値は文字列のため、必要に応じて他の型への変換が必要な点は、Propertiesクラスによる読み込みと同様です。
さてPropertiesクラスを利用しても、クラスパスからプロパティファイルを読み込むことは可能ですが、ResourceBundleクラスの方が実装は効率的です。従ってPropertiesクラスとResourceBundleクラスは、以下のように使い分けると良いでしょう。
* ファイルシステムから読み込む場合 … Propertiesクラスを使う
* クラスパスから読み込む場合 … ResourceBundleクラスを使う

### 21.3.3 国際化対応

#### 国際化対応とロケール情報

1つのアプリケーションやサービスをグローバルに展開する、というケースは、昨今では珍しいことではないでしょう。様々な国や地域で利用されるアプリケーションを開発するにあたっては、言語、文字、通貨、日時、数値の表記など、様々な要素が国や地域によって異なるという点を、考慮しなければなりません。
Javaにはこのように、国や地域による差異を切り替えるための仕組みが、言語として備わっています。そのすべての機能をここで紹介することはできませんが、まず日時の違いについてはDate and Time APIによって対応がなされているという点は、既出のとおりです。
それ以外の要素については、ベースになっているのがロケール情報です。ロケール情報とは「地理的・政治的・文化的な特定の地域を表す概念」とされていますが、「言語と国」の組み合わせと考えると分かりやすいでしょう。例えば「日本語と日本」は1:1ですが、公用語が英語とフランス語の2言語であるカナダの場合は「英語とカナダ」「フランス語とカナダ」になりますので、言語と国は常に1:1とは限りません。

#### Localeクラスのオブジェクト生成

Javaでは、ロケール情報はjava.util.Localeクラスによって表します。
Localeクラスのオブジェクト生成には、以下のような方法があります。
* コンストラクタを使う
* ロケール定数を使う
* APIを使う

この中から、まずは、コンストラクタを使う方法から見ていきましょう。Localeクラスには、以下のようなコンストラクタが用意されています。

(1)Locale(String)
(2)Locale(String, String)
(3)Locale(String, String, String)

(1)は言語コードのみを指定するコンストラクタ、(2)は言語コードと国コードを指定するコンストラクタ、(3)はそれに加えて派生情報を指定するものです。言語コードは「ISO 639-1」として標準化されており、代表的なものに日本語の"ja"、英語の"en"、フランス語の"fr"、中国語の"zh"などがあります。国コードは「ISO 3166-1」として標準化されており、代表的なものに日本の"JP"、アメリカの"US"、フランスの"FR"、中国の"CN"などがあります。派生情報は製品やWebブラウザ固有のコードで、2つ以上ある場合はアンダースコアで区切るものとされています。
Localeクラスのオブジェクトをコンストラクタによって生成する具体例を、以下に示します。

```java:snippet_1 (pro.kensait.java.basic.lsn_21_3_3.Main)
Locale locale1 = new Locale("ja"); // 「日本語」
Locale locale2 = new Locale("ja", "JP"); // 「日本語と日本」
Locale locale3 = new Locale("en", "US"); // 「英語とアメリカ」
Locale locale4 = new Locale("en", "CA"); // 「英語とカナダ」
Locale locale5 = new Locale("fr", "CA"); // 「フランス語とカナダ」
```

次にロケール定数を使う方法です。Localeクラスには、あらかじめよく使われる言語と国の組み合わせについては定数が用意されています。以下にその代表例を示します。

```java:snippet_2 (pro.kensait.java.basic.lsn_21_3_3.Main)
Locale locale1 = Locale.JAPANESE; // 「日本語」
Locale locale2 = Locale.JAPAN; // 「日本語と日本」
Locale locale3 = Locale.US; // 「英語とアメリカ」
Locale locale4 = Locale.CANADA; // 「英語とカナダ」
Locale locale5 = Locale.CANADA_FRENCH; // 「フランス語とカナダ」
```

#### LocaleクラスのAPI

ここでは、LocaleクラスのAPIの中から特に主要なものを紹介します。
|API(メソッド）|説明|
|--|--|
|static Locale **getDefault**()|システムのデフォルトロケールを表すLocaleオブジェクトを返す。|
|String **getLanguage**()|このLocaleの言語コードを返す。|
|String **getCountry**()|このLocaleの国コードを返す。|
|String **getVariant**()|このLocaleの派生情報を返す。|
|String **toLanguageTag**()|このLocaleを表す「IETF言語タグ」を返す。※"ja-JP"など|

これらのAPIにより、以下のような情報を取得できます。

```java:snippet_3 (pro.kensait.java.basic.lsn_21_3_3.Main)
Locale locale = Locale.getDefault();
System.out.println(locale.getLanguage()); // "ja"
System.out.println(locale.getCountry()); // "JP"
System.out.println(locale.toLanguageTag()); // "ja-JP"
```

#### プロパティファイルの国際化対応

国際化対応の一環として、ユーザー向けに出力するメッセージを各国の言語に応じて切り替えたい、という要件が考えれます。ResourceBundleクラスには、ロケール情報によってプロパティファイルを切り替えることで、このような要件を実現する機能が備わっています。以下のコードを見てください。

```java:snippet_4 (pro.kensait.java.basic.lsn_21_3_3.Main)
Locale locale = new Locale("en", "US"); //【1】
ResourceBundle rb = ResourceBundle.getBundle("MyResource", locale); //【2】
```

まずLocaleオブジェクトを生成します【1】。ここでは「英語とアメリカ」を表すLocaleを、コンストラクタで生成しています。次にResourceBundleクラスのgetBundle()メソッドでファイルを読み込むときに、生成したLocaleオブジェクトを第二引数に指定します【2】。するとクラスパス上から、"MyResource"の後ろにアンダースコア区切りで言語コードと国コードが付加された"MyResource_en_US.properties"という名前のファイルが検索され、ResourceBundleに読み込まれます。つまりあらかじめ「言語と国」毎にプロパティファイルを用意し、Localeに応じて読み込むプロパティファイルを切り替えることで国際化対応を実現する、というわけです。
なおLocaleオブジェクトを生成するときに`new Locale("en")`といった具合に言語コードのみを指定した場合は、検索されるプロパティファイルは言語コードのみが付加されたもの（この例では"MyResource_en.properties"）になります。

#### 数値フォーマットの国際化対応

国際化対応の一環として、ユーザー向けに出力する数値のフォーマットをロケール情報に応じて切り替えたい、という要件が考えれます。レッスン17.1.3で取り上げたNumberFormatクラスには、ロケール情報によってフォーマットを切り替える機能が備わっています。NumberFormatクラスのオブジェクトは、ファクトリメソッドであるget〇〇Instance()メソッドによって取得しますが、これらのメソッドにはロケール情報を引数に取るオーバーロードメソッドがあります。その中でも代表的なものは、通貨フォーマット用オブジェクトを取得するためのgetCurrencyInstance()メソッドです。
ここではロケール情報によって、NumberFormatクラスの通貨フォーマットがどのように変わるのか、その挙動を確認してみましょう。以下のコードを見てください。

```java:snippet_5 (pro.kensait.java.basic.lsn_21_3_3.Main)
NumberFormat nf1 = NumberFormat.getCurrencyInstance(); //【1】
NumberFormat nf2 = NumberFormat.getCurrencyInstance(Locale.US); //【2】
NumberFormat nf3 = NumberFormat.getCurrencyInstance(Locale.UK); //【3】
String str1 = nf1.format(10000); // "¥10,000"
String str2 = nf2.format(10000); // "$10,000.00"
String str3 = nf3.format(10000); // "£10,000.00"
```

まずgetCurrencyInstance()メソッドによって、通貨フォーマット用のNumberFormatオブジェクト生成します。ここではデフォルトロケール（日本）の通貨フォーマット用オブジェクト【1】、アメリカの通貨フォーマット用オブジェクト【2】、そしてイギリスの通貨フォーマット用オブジェクト【3】を、それぞれ生成しています。
このコードを実行すると、各国の通貨フォーマットに合わせて、変数str1には文字列"¥10,000"が、変数str2には文字列"$10,000.00"が、変数str3には文字列"£10,000.00"が、それぞれ格納されます。

### 21.3.4 java.util.Objectsクラスの特徴とAPI

#### Objectsクラスの特徴とAPI

java.util.Objectsクラスは、オブジェクトを操作するためのユーティリティクラスです。Objectsクラスには数多くのAPIが定義されていますが、このレッスンでは比較的よく使われる2つのAPIを紹介します。
|API(メソッド）|説明|
|--|--|
|static boolean **equals**(Object, Object)|2つのオブジェクトが同じ型の場合は、両者の等価性をequals()メソッドで判定して返す。2つのオブジェクトが異なる型の場合は、falseを返す。2つのオブジェクトのうち片方がnull値の場合はfalseを返すが、両者ともnull値の場合は、trueを返す。|
|static int **hash**(Object...)|指定されたオブジェクトからハッシュ値を生成して返す。|

#### Objects#equals()メソッドの使用方法

まずequals()メソッドについてです。通常各クラスはjava.lang.Objectクラスで定義されたequals()メソッドをオーバーライドしており、要件に応じて等価性を判定する処理を実装します。ただしequals()メソッドを呼び出す場合は、事前にnullチェックが必要です。また等価性の判定では「両者ともにnull値の場合は等価と見なす」といった要件が一般的ですが、これを毎回実装するのは非効率です。java.util.Objectsクラスのequals()メソッドにはこのような処理が施されているため、このAPIを利用すると実装負担を軽減することができます。
このメソッドにより文字列の等価性を判定するコードは、以下のようになります。

```java:snippet_1 (pro.kensait.java.basic.lsn_21_3_4.Main)
String str1 = "foo";
String str2 = "foo";
Objects.equals(str1, str2); // true
```

以下のように両者の型が異なる場合は、falseが返ります。

```java:snippet_2 (pro.kensait.java.basic.lsn_21_3_4.Main)
Integer val1 = 100;
Long val2 = 100L;
Objects.equals(val1, val2); // false
```

以下のように両者がともにnull値の場合は、trueが返ります。

```java:snippet_3 (pro.kensait.java.basic.lsn_21_3_4.Main)
Object o1 = null;
Object o2 = null;
Objects.equals(o1, o2); // true
```

#### Objects#hash()メソッドの使用方法

次にhash()メソッドです。このメソッドは、可変引数を渡すことで、それらの属性からハッシュ値を計算して返却するユーティリティです。
チャプター14.1で説明したように、Eclipseでは「hashCode()およびequals()の生成」によって、選択されたフィールドからhashCode()メソッドとequals()メソッドを自動生成することができます。実はこのときjava.util.Objectsクラスがインポートされ、hashCode()メソッドの実装ではObjectsクラスのhash()メソッドが、equals()メソッドの実装ではObjectsクラスのequals()メソッドが、それぞれ使用されています。
例えばname（String型）、age（int型）、address（String型）という3つのフィールドを持つPersonクラスがあった場合、EclipseでhashCode()メソッドおよびequals()メソッドを自動生成すると、以下のようなコードが出力されます。

```java:snippet
@Override
public int hashCode() {
    return Objects.hash(address, age, name);
}
@Override
public boolean equals(Object obj) {
    if (this == obj)
        return true;
    if (obj == null)
        return false;
    if (getClass() != obj.getClass())
        return false;
    Person other = (Person) obj;
    return Objects.equals(address, other.address) && age == other.age
            && Objects.equals(name, other.name);
}
```

### このチャプターで学んだこと

このチャプターでは、以下のことを学びました。

1.	Systemクラスの特徴とシステムプロパティの種類について。
2.	プロパティファイルの読み込み方と配置場所について。
3.	ロケール情報とは、言語と国の組み合わせであること。
4.	プロパティファイルや数値フォーマットを国際化対応する方法について。
5.	java.util.Objectsクラスの使い方（equals()メソッドとhash()メソッド）について。