0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【Java復習3日目】サンプルコードから学び直すJava入門③

Posted at

サンプルコード(クラス初期化子とインスタンス初期化子)

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)がディレクトリ内に格納される。

a/A1.java
// パッケージと同名のディレクトリ中にソースファイルを置く
package a;

// 異なるディレクトリに置かれたソースファイルからでも、"import a.A1[A2]"のインポート宣言によって単純名で利用可能
public class A1 { ... }
class A2 { ... }

一意なパッケージ名の命名

一意パッケージ名を与える場合、インターネットアドレス逆順に並べたパッケージ名で命名する。

クラスファイルの検索

インポート宣言によってインポートされるクラスファイルは、以下の3箇所から検索される。

  1. ブートストラップクラスパスJavaプラットフォームを構成するクラス。
  2. インストール型拡張機能Java拡張機能機構を構成するクラス。
  3. ユーザクラスパス拡張機能機構を利用しないユーザ定義クラス。-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 <完全限定名>.*;

サンプルコード(派生とコンストラクタ, ポリモーフィズム, 拡大変換と縮小変換)

SuperConstructor.java
// スーパークラス
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つのことが言える。

  1. extendsを付与せずに定義されたクラスは、すべてObjectクラスのサブクラスとなる。
  2. Object型をとる仮引数には、すべてのクラス型の実引数をとることができる。(=ポリモーフィズム)

Javaでは多重継承はサポートされない。

また、finalprivateキーワードを付与したクラスメソッドサブクラス継承されない
なお、finalクラス内のすべてのメソッドは自動的にfinalメソッドとなる。

is-A関係をもつクラスオブジェクトの参照可能範囲

スーパークラスクラス型変数は、サブクラスオブジェクトを参照することができる。
サブクラスオブジェクトは、スーパークラスフィールドを持っているため。
サブクラスは一種のスーパークラスである

一方で、サブクラスクラス型変数は、スーパークラスオブジェクトを参照することはできない。
スーパークラスオブジェクトは、サブクラスフィールドを持っていないため。
スーパークラスは一種のサブクラスでない

拡大変換と縮小変換

参考: Java復習1日目

参照型拡大変換(widening reference conversion, アップキャスト)は、参照可能範囲が広いスーパークラスへの変換を指し、
縮小変換(narrowing reference conversion, ダウンキャスト)は、参照可能範囲が狭いサブクラスへの変換を指す。

ただし、参照型における縮小変換は原則避ける必要がある。


サンプルコード(静的結合と動的結合, instanceof演算子)

Polymorphism.java
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では、動的結合方式が採用される。

オブジェクト指向の三大要素

オブジェクト指向における三大要素は、以下の通り。

  1. クラス(カプセル化)
  2. 継承(inheritance)
  3. 多相性(polymorphism)

instanceof演算子

instanceof演算子
<クラス型変数> instanceof <クラス>
// クラス型変数が指定したクラス型に暗黙的にキャストできる下位クラスであればtrueを、
// そうでなければfalseを返却

サンプルコード(抽象クラスの役割)

Abstract.java
// 抽象クラス(スーパークラス)
// -> 関連性のある下位クラス同士をグループ化することで、多相性を活用できるようにする
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の起動
% javadoc <オプション> <パッケージ名> <ソースファイル> @<引数ファイル>

抽象クラスとインタフェース

抽象クラスクラスに対して単一継承(single inheritance)しか認められないのに対し、
インタフェースクラスに対して多重継承(multiple inheritance)が認められる。

ただし、インタフェースへの多重継承は認められない。


サンプルコード(String.format()メソッド)

StringFormat.java
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: 参照する値

サンプルコード(コマンドライン引数)

CommandLine.java
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()メソッドが受け取る。


サンプルコード(ラッパクラス)

Wrapper.java
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つ。

  1. 基本型の特性をクラス変数として提供

    Boolean型を除いて、基本型の最大・最小値を表すMIN_VALUEMAX_VALUEが提供
  2. 対応する基本型の値をもったクラス型オブジェクトを生成可能にする
  3. 2.に関連し、値に関する各種操作をメソッドとして提供

    整数値toString()メソッドを持たないが、Integer型toString()メソッドをもつ。

サンプルコード1(例外処理)

ExceptionHandler.java
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.
throwsキーワードを付与せずExceptionを送出しようとした場合
sw(0 .. checked/1 .. unchecked): 0
Exception in thread "main" java.lang.Error: Unresolved compilation problem: 
        Unhandled exception type Exception

サンプルコード(資源付きtry文)

TryWithResource.java
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

用語集

用語 内容
コンポジション(合成) インスタンス内部的に別のインスタンスをもつ構造。
→クラスAhas-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)メソッドを通じて伝播すること。
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?