サンプルコード(クラス初期化子とインスタンス初期化子)
import java.util.Random;
class StaticInitializer {
// 静的フィールド(=クラス変数)
// -> クラスで共通して利用する変数
// => インスタンス間で共有
private static int staticCounter;
private static int instanceCounter = 0;
// 動的フィールド(インスタンス変数)
// -> インスタンス毎に利用する変数
// => インスタンス毎に異なる
private int staticId;
private int instanceId;
// クラス初期化子(=静的初期化子)
// -> クラスの初期化時に呼び出される処理
static {
Random rand = new Random();
staticCounter = rand.nextInt(10)* 100;
}
// インスタンス初期化子
// インスタンス生成時(=コンストラクタの呼び出し前)に呼び出される処理
{
instanceId = ++instanceCounter;
}
// コンストラクタ
// -> インスタンス初期化子の実行後に呼び出される
public StaticInitializer() {
staticId = ++staticCounter;
}
public int getStaticId() {
return staticId;
}
public int getInstanceId() {
return instanceId;
}
}
class Main {
public static void main(String[] args) {
StaticInitializer a = new StaticInitializer();
StaticInitializer b = new StaticInitializer();
StaticInitializer c = new StaticInitializer();
System.out.println("StaticId a: " + a.getStaticId() + ", InstanceId a: " + a.getInstanceId());
System.out.println("StaticId b: " + b.getStaticId() + ", InstanceId b: " + b.getInstanceId());
System.out.println("StaticId c: " + c.getStaticId() + ", InstanceId c: " + c.getInstanceId());
}
}
クラス初期化子とインスタンス初期化子
static {...}
で宣言するクラス初期化子
は、クラスの初期化時に呼び出される処理。
{...}
で宣言するインスタンス初期化子
は、インスタンスの初期化時(=コンストラクタ
の実行直前)に呼び出される処理。
なお、クラス
が初期化
されるタイミングは以下の通り。
インスタンス
の生成時クラスメソッド
の呼び出し時クラス変数
への値の代入時
→クラス変数
を利用する場合は"<クラス名>.<クラス変数>"で呼び出すため定数
でないクラス変数
の値の取得時
→変数
であることから、初期化
して値を厳密に定義する必要があるため
⇒定数
は値が既に厳密に定義されているため、取得時に初期化
は行われない。
また、インスタンス
が初期化
されるタイミングは以下の通り。
コンストラクタ
が呼び出される直前
クラス初期化子の利用場面
何らかの計算が必要なクラス変数
を初期化
する場合、クラス初期化子
を用いて初期化処理を記述する。
インスタンス初期化子の利用場面
コンストラクタ
を多重定義
(=オーバーロード
)する場合、それらの共通部分はインスタンス初期化子
を用いて初期化処理を記述する。
パッケージ
パッケージ宣言(package declaration)
によって、定義したクラス
やインタフェース
を特定のパッケージ
に所属させることができる。
なお、パッケージ宣言
は2つ以上置くことはできず、パッケージ名
の先頭文字は小文字にする。
ここで、パッケージ宣言
がない場合、そのソースファイル
中で定義したクラス
は無名パッケージ(unnamed package)
に属する。
また、基本的にパッケージ名
と同名であるディレクトリ
の中にパッケージ宣言
を行うソースファイル
を置く。
この場合、コンパイル
時に、ディレクトリ
内に置かれたソースファイル
で定義したクラスファイル
(=.class
)がディレクトリ
内に格納される。
// パッケージと同名のディレクトリ中にソースファイルを置く
package a;
// 異なるディレクトリに置かれたソースファイルからでも、"import a.A1[A2]"のインポート宣言によって単純名で利用可能
public class A1 { ... }
class A2 { ... }
一意なパッケージ名の命名
一意なパッケージ名
を与える場合、インターネットアドレス
を逆順に並べたパッケージ名
で命名する。
クラスファイルの検索
インポート宣言
によってインポート
されるクラスファイル
は、以下の3箇所から検索される。
ブートストラップクラスパス
…Javaプラットフォーム
を構成するクラス。インストール型拡張機能
…Java拡張機能機構
を構成するクラス。ユーザクラスパス
…拡張機能機構
を利用しないユーザ定義クラス。-classpath
オプションを用いて実行する。
ユーザクラスパス
は、以下のように実行する。
% java -classpath <検索ディレクトリ1>;<検索ディレクトリ2>;... <実行クラス名>
パッケージの役割
パッケージ
の主な役割は、以下の3つ。
役割 | 内容 |
---|---|
名前の衝突回避 |
同名クラス を完全限定名 の利用によって区別可能にする。 |
カテゴリによる分類 | 機能が似ているクラス をカテゴライズする。 |
カプセル化(アクセス制御) | 無関係クラス からのアクセス を制御できる。 |
クラスを利用可能にする型インポート宣言(type import declaration)
異なるパッケージ
に属するクラス
を、完全限定名(fully qualified name)
ではなく
単純名(simple name)
で利用できるようにするインポート宣言
の種類は、以下の2つ。
宣言 | 形式 |
---|---|
単一型インポート宣言(single-type-import declaration) | import <完全限定名>; |
オンデマンド型インポート宣言(type-import-on-demand declaration) | import <パッケージ名>.*; |
ただし、オンデマンド型インポート宣言
を用いてインポート
したパッケージ
同士に
同名クラス
が存在する場合はコンパイルエラー
が生じるため、
利用クラスが限定される場合は単一型インポート宣言
を用いる。
import java.util.*;
import java.sql.*;
Date a = new Date(); // Dateはutilパッケージにもsqlパッケージにも存在するため、コンパイルエラーが発生
クラスの静的メンバを利用可能にする静的インポート宣言(static import declaration)
異なるパッケージ
に属するクラス
のメンバ
を利用する場合は、静的インポート宣言
を用いる。
静的インポート宣言
も型インポート宣言
と同様、以下の2種類が存在する。
宣言 | 形式 |
---|---|
単一静的インポート宣言(single-static-import declaration) | import static <完全限定名>.<メンバ変数の識別子>; |
オンデマンド静的インポート宣言(static-import-on-demand declaration) | import static <完全限定名>.*; |
サンプルコード(派生とコンストラクタ, ポリモーフィズム, 拡大変換と縮小変換)
// スーパークラス
class A {
// "private"や"final"キーワードを用いたフィールドは継承されない
private int x;
private int y;
// コンストラクタ
A(int x, int y) {
this.x = x;
this.y = y;
}
void setX(int x) { this.x = x; }
void setY(int y) { this.y = y; }
int getX() { return x; }
int getY() { return y; }
}
// サブクラス
class B extends A{
private int z;
// コンストラクタ
// スーパークラスのコンストラクタはsuper(...)で呼び出す
// -> スーパークラスのコンストラクタを呼び出さない場合、引数を取らないコンストラクタsuper()が自動的に呼び出される
// => スーパークラスのコンストラクタを呼び出さない場合、
// スーパークラスで引数を取らないコンストラクタを定義していない場合はコンパイルエラーが発生
B(int x, int y, int z) {
super(x, y);
this.z = z;
}
void setZ(int z) { this.z = z; }
int getZ() { return z; }
}
class Main {
public static void main(String[] args) {
// スーパークラスのオブジェクト
A a = new A(10, 20);
// サブクラスのオブジェクト
B b = new B(30, 40, 50);
// スーパークラスのクラス型変数は、サブクラスのオブジェクトを参照できる
// -> 暗黙的に"A a1 = (A) b"の拡大変換が実行される
// => ポリモーフィズム
A a1 = b;
System.out.println( "x of Sub: " + a1.getX() );
System.out.println( "y of Sub: " + a1.getY() );
// サブクラスのクラス型変数は、スーパークラスのオブジェクトを参照できない
// -> 暗黙的には"B b1 = (B) a"の縮小変換は実行されない
B b1 = a; // コンパイルエラー
// -> スーパークラスのオブジェクトは、サブクラスで追加されたフィールドを持たないため
System.out.println("z of Main: " + b1.getZ();) // コンパイルエラー
}
}
派生とコンストラクタ, ポリモーフィズム
スーパークラス
を派生(derive)
したサブクラス
では、スーパークラス
のコンストラクタ
は直接的には継承されない。
ただし、super(...)
によってスーパークラス
のコンストラクタ
を呼び出すことができる。
また、サブクラス
でスーパークラス
のコンストラクタ
を明示的に呼び出さない場合、
サブクラス
のデフォルトコンストラクタ
を含むコンストラクタ
の先頭で、
スーパークラス
のデフォルトコンストラクタ
であるsuper()
が自動的に呼び出される。
さらに、Object
クラスはすべてのクラスの上位クラス
であるため、以下の2つのことが言える。
extends
を付与せずに定義されたクラスは、すべてObject
クラスのサブクラス
となる。Object
型をとる仮引数には、すべてのクラス型の実引数をとることができる。(=ポリモーフィズム
)
※Java
では多重継承
はサポートされない。
また、final
やprivate
キーワードを付与したクラス
・メソッド
もサブクラス
に継承されない。
なお、finalクラス
内のすべてのメソッド
は自動的にfinal
メソッドとなる。
is-A関係をもつクラスオブジェクトの参照可能範囲
スーパークラス
のクラス型変数
は、サブクラス
のオブジェクト
を参照することができる。
→サブクラス
のオブジェクト
は、スーパークラス
のフィールド
を持っているため。
⇒サブクラス
は一種のスーパークラス
である
一方で、サブクラス
のクラス型変数
は、スーパークラス
のオブジェクト
を参照することはできない。
→スーパークラス
のオブジェクト
は、サブクラス
のフィールド
を持っていないため。
⇒スーパークラス
は一種のサブクラス
でない
拡大変換と縮小変換
参照型
の拡大変換(widening reference conversion, アップキャスト)
は、参照可能範囲が広いスーパークラス
への変換を指し、
縮小変換(narrowing reference conversion, ダウンキャスト)
は、参照可能範囲が狭いサブクラス
への変換を指す。
ただし、参照型
における縮小変換
は原則避ける必要がある。
サンプルコード(静的結合と動的結合, instanceof演算子)
class Super {
private String name;
// コンストラクタ
public Super(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void print() {
System.out.println(getName() + ": Super");
}
}
class Sub extends Super {
// コンストラクタ
public Sub(String name) {
super(name);
}
// オーバーライドメソッド
// -> @Overrideアナテイションを用いて明示的に宣言
@Override public void print() {
System.out.println(getName() + ": Sub");
}
}
public class Polymorphism {
public static void main(String[] args) {
// サブクラスのオブジェクト
Sub sub = new Sub("Super or Sub");
// スーパークラスのクラス型変数にサブクラスのオブジェクトの「参照先」を渡す
// -> ポリモーフィズム
Super sup = sub;
// スーパークラスのクラス型変数からサブクラスのprint()メソッドが呼び出される
// -> メソッドを呼び出す「オブジェクト」のクラス型をもとに呼び出すメソッドが決定(=静的結合/早期結合)されるのではなく、
// オブジェクトの「参照先」のクラス型をもとに呼び出すメソッドが決定(=動的結合/遅延結合)
sup.print();
// スーパー/サブクラスのオブジェクトを格納する配列
Super[] a = {
new Super("Super 1"),
new Super("Super 2"),
new Sub("Sub 1"),
new Sub("Sub 2")
};
// 配列の構成要素毎に行う処理
for (Super i : a) {
// 構成要素がサブクラスである場合の処理
// -> instanceof演算子を用いて暗黙的にダウンキャストできる下位クラスであればtrueを返却
// => 下位クラスのメソッドを利用する場合は、明示的にダウンキャストを行う
if (i instanceof Sub) {
((Sub) i).print();
}
// 構成要素がスーパークラスである場合の処理
else {
i.print();
}
}
}
}
Super or Sub: Sub
静的結合(static binding)と動的結合(dynamic binding)
静的結合/早期結合(early binding)
では、呼び出すメソッドがコンパイル
時に決定され、
動的結合/遅延結合(late binding)
では、呼び出すメソッドが実行
時に決定される。
Java
では、動的結合
方式が採用される。
オブジェクト指向の三大要素
オブジェクト指向
における三大要素は、以下の通り。
- クラス(カプセル化)
- 継承(inheritance)
- 多相性(polymorphism)
instanceof演算子
<クラス型変数> instanceof <クラス>
// クラス型変数が指定したクラス型に暗黙的にキャストできる下位クラスであればtrueを、
// そうでなければfalseを返却
サンプルコード(抽象クラスの役割)
// 抽象クラス(スーパークラス)
// -> 関連性のある下位クラス同士をグループ化することで、多相性を活用できるようにする
abstract class Symbol {
// (Objectクラスで定義された)非抽象メソッドを抽象メソッドとしてオーバーライド
@Override public abstract String toString();
}
// 実装クラス(サブクラス)
class Slash extends Symbol {
// 抽象メソッドの実装
@Override public String toString() {
return "/";
}
}
// 実装クラス(サブクラス)
class Asterisk extends Symbol {
// 抽象メソッドの実装
@Override public String toString() {
return "*";
}
}
class Abstract {
public static void main(String[] args) {
Symbol[] a = {
new Slash(),
new Asterisk()
};
for (Symbol i : a) {
if (i instanceof Slash) {
System.out.println( "Slash: " + ((Slash) i).toString() );
}
else if (i instanceof Asterisk) {
System.out.println( "Asterisk: " +((Asterisk) i).toString() );
}
}
}
}
サンプルコード(文書化コメント)
/**
* クラスDocumentationCommentは、〜を表す。
* @author <実装者>
* {@code <変数名・値>}は〜を表す。
* @return <戻り値>
* @param <引数名> <解説>
* @see <参照先>
* @see <a href="<相対URLまたは絶対URL>"><ラベル></a>
* @see <完全限定名>#<メンバ> <ラベル>
*/
public class DocumentationComment {
...
}
文書化コメント(documentation comment)
参考: Javadoc ドキュメンテーションコメントの書き方
文書化コメント
は、javadoc
というツールを用いてプログラムの仕様書を生成する際に利用するコメントアウト。
また、javadoc
を起動するコマンドは以下の通り。
% javadoc <オプション> <パッケージ名> <ソースファイル> @<引数ファイル>
抽象クラスとインタフェース
抽象クラス
はクラス
に対して単一継承(single inheritance)
しか認められないのに対し、
インタフェース
はクラス
に対して多重継承(multiple inheritance)
が認められる。
ただし、インタフェース
への多重継承
は認められない。
サンプルコード(String.format()メソッド)
public class StringFormat {
public static void main(String[] args) {
// 形式(=フォーマット)の変換
// printf()メソッドとは異なり、String型の文字列に変換する
String s1 = String.format("%5d", 123);
String s2 = String.format("%9.3f", 123.45);
System.out.println("s1: " + s1);
System.out.println("s2: " + s2);
}
}
s1: 123
s2: 123.450
フォーマットを指定するString.format()メソッド
参考: Java復習2日目(printf()メソッド)
System.out.printf()
メソッドは出力先がコンソール
であるのに対し、
String.format()
メソッドは出力先がString
型文字列となる。
定義
String String.format(String format, Object... args)
// パラメータ
// format: 「出力フォーマット」を表す文字列
// args: 参照する値
サンプルコード(コマンドライン引数)
public class CommandLine {
// main()メソッドの引数argsは、コマンドライン引数(command-line argument)を表す
// -> "java <クラス名> <文字列1> <文字列2> ..."というように引数を指定できる。
public static void main(String[] args) {
for (String i : args) {
System.out.println(i);
}
}
}
% java CommandLine ABC DEF GHI
ABC
DEF
GHI
コマンドライン引数(command-line argument)
main(String[] args)
メソッドのargs
はコマンドライン引数
を表す。
実行時にコマンドライン引数
を付与した場合、main()
メソッドが受け取る。
サンプルコード(ラッパクラス)
public class Wrapper {
public static void main(String[] args) {
// ラッパクラスを用いた定義
// -> int型 → Integer型 へのオートボクシング(auto boxing)
Integer i = 3;
// 基本型を用いた定義
int j = 7;
System.out.println("Integer: " + i);
System.out.println("int: " + j);
}
}
ラッパクラス(wrapper class)
基本型
をクラス
として提供するクラス。
ラッパクラス
が作成された目的は、主に以下の3つ。
基本型
の特性をクラス変数
として提供
→Boolean
型を除いて、基本型
の最大・最小値を表すMIN_VALUE
・MAX_VALUE
が提供- 対応する
基本型
の値をもったクラス型オブジェクト
を生成可能にする- 2.に関連し、値に関する各種操作を
メソッド
として提供
→整数値
はtoString()
メソッドを持たないが、Integer型
はtoString()
メソッドをもつ。
サンプルコード1(例外処理)
import java.util.InputMismatchException;
import java.util.Scanner;
public class ExceptionHandler {
public static void main(String[] args) {
Scanner stdIn = new Scanner(System.in);
// try節 - 例外が送出(throw)される可能性のあるブロック
try {
System.out.print("100 / ");
// int型でない値が入力された場合はInputMismatchExceptionが発生
// -> その場合、ストリーム内に入力された値が残るため、Scanner.next()メソッドでのスキャンが可能
int n = stdIn.nextInt();
// 除数として0が入力された場合はArithmeticExceptionが発生
System.out.println("Ans.: " + (100 / n));
}
// catch節 - 例外を捕捉(catch)してエラーハンドリングを行うブロック
// InputMismatchExceptionが発生した場合の処理(=例外ハンドラ)
catch (InputMismatchException e) {
System.out.println("Exception: " + e);
String s = stdIn.next();
System.out.println("Omitted: " + s);
}
// ArithmeticExceptionが発生した場合の処理(=例外ハンドラ)
catch (ArithmeticException e) {
System.out.println("Exception: " + e);
}
// finally節 - 例外発生にかかわらず必ず実行されるブロック
finally {
System.out.println("End of calc.");
}
}
}
100 / ABC
Exception: java.util.InputMismatchException
Omitted: ABC
End of calc.
100 / 0
Exception: java.lang.ArithmeticException: / by zero
End of calc.
例外クラス
すべての例外は、Throwable
クラスの下位クラス
で定義され、
詳細メッセージ
であるString
型文字列と、原因
であるThrowable
オブジェクトで構成される。
また、Throwable
クラスのサブクラス
には、以下の2種類のクラスが定義される。
クラス | 内容 |
---|---|
Error |
プログラムが回復を期待できない重大な例外であり、捕捉 の必要がない。 |
Exception |
プログラムが回復を期待できる例外であり、捕捉 しなければならない。 |
ただし、Exception
クラスのサブクラス
であるRuntimeException
クラスは
非検査例外(unchecked exception)クラス
と呼ばれ、コンパイル
時に検査されないため、捕捉
する必要はない。
それ以外のException
クラスのサブクラス
は検査例外(checked exception)クラス
と呼ばれ、
コンパイル
時に検査されるため、捕捉
する必要がある。
サンプルコード2(例外処理)
import java.util.Scanner;
public class ThrowAndCatch {
// 検査例外(checked exception)を送出する可能性があるメソッドは"throws"キーワードを付与し、
// 発生した例外を送出できるようにする必要がある
static void check (int sw) throws Exception{
switch (sw) {
case 0: throw new Exception("This exception has to be catched.");
case 1: throw new RuntimeException("This exception does not have to be catched.");
}
}
// 検査例外を送出する可能性があるメソッドを呼び出す場合も同様に"throws"キーワードを付与し、
// 発生した例外を送出できるようにする必要がある
static void call(int sw) throws Exception{
check(sw);
}
public static void main(String[] args) {
Scanner stdIn = new Scanner(System.in);
System.out.print("sw(0 .. checked/1 .. unchecked): ");
int sw = stdIn.nextInt();
try {
call(sw);
}
// Exceptionクラスの下位クラスも代入可能であるため、
// RuntimeExceptionクラスのオブジェクトもここで捕捉される(=ポリモーフィズム)
// -> 仮引数の型に対して代入可能な例外はすべて捕捉される
catch (Exception e) {
// 例外の詳細メッセージを取得
// -> 動的結合によってオブジェクトの参照先に応じてメソッドが呼び出される
System.out.println(e.getMessage());
}
}
}
sw(0 .. checked/1 .. unchecked): 0
This exception has to be catched.
sw(0 .. checked/1 .. unchecked): 1
This exception does not have to be catched.
sw(0 .. checked/1 .. unchecked): 0
Exception in thread "main" java.lang.Error: Unresolved compilation problem:
Unhandled exception type Exception
サンプルコード(資源付きtry文)
import java.io.*;
import java.util.Scanner;
public class TryWithResource {
static void init() {
// 資源付きtry文(try-with-resources statement)
// -> java.lang.AutoClosableインタフェースを実装するリソースの解放処理(本来は記述必須)を自動的に行うtry文
try (
BufferedReader br = new BufferedReader( new FileReader("LastTime.txt") );
) {
String input = br.readLine();
System.out.println("your recent input: " + input);
}
catch (IOException e) {
System.out.println("You have never executed this program.");
}
}
static void write(String input) {
// 資源付きtry文(try-with-resources statement)
try (
FileWriter fw = new FileWriter("LastTime.txt");
) {
fw.write(input);
}
catch (IOException e) {
System.out.println("Something went wrong.");
}
}
public static void main(String[] args) {
Scanner stdIn = new Scanner(System.in);
// 呼び出し元メソッドで捕捉処理を記述しているため、try文は不要
init();
System.out.print("Input: "); String input = stdIn.nextLine();
// 呼び出し元メソッドで捕捉処理を記述しているため、try文は不要
write(input);
}
}
You have never executed this program.
Input: ABC
your recent input: ABC
Input: 123
用語集
用語 | 内容 |
---|---|
コンポジション(合成) |
インスタンス が内部的に別のインスタンス をもつ構造。→クラス A がhas-A 関係をもつクラスB において、A オブジェクトは内部的にB オブジェクトをもつ。 |
is-A(kind-of-A)関係 | クラスA をクラスB が継承している場合、クラスB は一種のクラスA となること。 |
上位/下位クラス(super/sub class) |
祖先/先祖クラス 。 |
直接上位/直接下位クラス(direct super/sub class) |
親/子クラス 。 |
多重継承(multiple inheritance) | 複数クラス から派生 を行うこと。 |
ポリモーフィズム(多相性, polymorphism) |
クラス型変数 が派生関係にあるクラスオブジェクト を参照できること。 |
ハッシュ値(hash code) | 個々のインスタンスを区別するためにObject クラスで定義される識別番号。 |
スタックトレース(stack trace) |
例外(exception) がメソッド を通じて伝播 すること。 |