0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Java Gold 紫本

Last updated at Posted at 2024-06-23

enum P9

enum
特定の値を列挙するオブジェクトのこと

→ 列挙型の実体 = 定数、メソッドを持つクラス

注意点メモ

・明示的なインスタンス化❌
・アクセス:列挙型.クラス定数
・コンストラクタ、メソッド、変数定義可
・抽象メソッド、インタフェースのオーバーライド、実装可
・定数は書いた順番に管理

enum.java
enum Card {
    SPADES,    //Card.SPADES.ordinary(); -> 列挙型の位置を返す(0から始まる)
    CLUBS,
    DIAMONDS,
    HEARTS
}
 コンパイルすると...
final class Card extends java.lang.Enum<Card> {
    public static final Card SPADES = new Card();   //列挙した値を名前とするクラス定数に、インスタンス化した列挙型オブジェクトを格納する
    public static final Card CLUBS = new Card();
    public static final Card DIAMONDS = new Card();
    public static final Card HEARTS = new Card();

    public static Card[] values() {・・・・・}                //列挙した値すべて返す
    public static Card valueOf(java.lang.String) {・・・・・} //引数で指定した値を返す
}
抽象メソッドの利用.java
enum Unknown {
    // A, B  //コンパイルエラー(抽象メソッドをオーバーライドしていないから)
    A{ String method() { return "A"; } },
    B{ String method() { return "B"; } };
    abstract String method();
}

Java.lang.Object P17

ハッシュコード:Java実行環境がオブジェクトを識別する時に使う整数値

  • equals(), hashCode() をオーバーライドする時のルール
要件 説明
object.hashCode() == object.hashCode(); 同じオブジェクトに使っても、同じ整数値返る
object1.equals(object2) == true

object1.hashCode() == object2.hashCode()
equals()trueなら、hashCode()もtrue
object1.equals(object2) == false

object1.hashCode() == object2.hashCode() true,falseどっちでも
falseの方がパフォーマンス向上するかも
object.hashCode() == object.hashCode(); false
object1.equals(object2) == false

インタフェース P19

インタフェース
必要な操作をまとめたクラスの仕様書(取り決め)

  • 定数(public static final)宣言可
    →宣言時に初期化しないとコンパイルエラー
  • 抽象メソッド宣言=public abstract 自動付与(publicのみ)
  • static / default + 具象メソッド
    → 自動的にpublic、protectd❌
    → static private⭕️ / default private ❌
  • defaultメソッド
    → toString(), equals(), hashCode() 定義❌
    → 実装クラスでオーバーライド可能
  • 親インタフェースへアクセス
    → <親インタフェース名> . super . <メソッド名>
  • メンバへのアクセス
    → クラスが優先される
インタフェース内での定義.java
interface Foo {
    private static void p1() {
        System.out.println("Foo : p1()");
    }
    private void p2() {
        System.out.println("Foo : p2()");
    }
    static void methodA() {
        System.out.println("Foo : methodA()");
        p1();
        p2();   //コンパイルエラー
    }
    default void methodB() {
        System.out.println("Foo : methodB()");
        p1();
        p2();
    }
}

publc class MyClass implements Foo {}

public class Main {
    public static void main(String[] args) {
        MyClass obj = new MyClass();
        obj.methodA();    //コンパイルエラー 参照変数を使って、staticメソッドは呼び出せない
    }
}
interfaceの実装.java
interface A {
    void method(); //抽象メソッド
    void priority(); //抽象メソッド
}
interface X extends A { //サブインタフェース
    @Override 
    default void method() { System.out.println("X"); }
    @Override 
    default void priority() { System.out.println("X"); }
}
class Y impletents A { //実装クラス
    @Override 
    public void priority() { System.out.println("Y"); }
}

class Main extends Y implements X {
    @Override 
    public void method() { X.super.method(); }   //親インタフェース.super.メソッド
    Main main = new Main();
    main.priority(); //コンパイルエラーにはならない、Javaではクラスが優先される→Yクラスのpriority()が呼ばれる
}

ネストクラス P30

クラスの中に定義したクラス

  • 存在を外部から隠したいクラスをネスト
  • 外側のクラスのメンバの一つ
  • インタフェース、抽象クラスを定義できる
ネストクラス.java
    class Outer {
        class A {} //非staticクラス(インナークラス)→ ローカルクラス、匿名クラス
        static class B {} //staticクラス
    }

ネストクラスのルール

クラス ルール
staic / 非static 共通 ・外側のクラスと同名❌
・アクセス修飾子使える
・abstruct, final 使える
staticのみ ・(非)staticメンバもてる
・外側クラスのインスタンスメンバへアクセス❌
非staticのみ ・staticメンバもてない
・外側クラスのインスタンス / static変数へアクセス⭕️

ネストクラスへのアクセス

1. 別のクラスからアクセス

Main.java
class Outer {
    private int val1 = 100;
    private static int val2 = 200;
    class A {
        void method1() {
            System.out.println(val1);
            System.out.println(val2);
        }
        static void method2(){} //コンパイルエラー
    }
    static class B {
        void method1() {
            System.out.println(val1); //コンパイルエラー
            System.out.println(val2);
        }
        static void method2() {
            System.out.println(val1); //コンパイルエラー
            System.out.println(val2);
        }
    }
}
//外部クラス
public class Main {
    public staic void main(String[] args) {
        Outer.A a = new Outer().new A(); //非staticクラスのインスタンス化
        Outer.B b = new Outer.B(); //staticクラスのインスタンス化
    }
}

2. 外側のクラスからアクセス

Main.java
public class Main {
    class A {
        void methodA() {}
    }
    static class B {
        static void methodB() {}
    }
    public static void main(String[] args) {
        new Main().new A().methodA();
        new Main.B().methodB(); //new B().methodB() ⭕️
        Main.B.methodB(); //B.methodB() ⭕️
    }
}

ローカルクラスのルール

クラスのメソッド内に定義したクラス

  • アクセス修飾子 使えない
  • static 使えない
  • 外側のクラスのメンバにアクセスできる
  • 外側クラスのメソッドの引数、ローカル変数にアクセス→各変数はfinal
Main.java
class Outer {
    private static int a = 1;
    private int b = 2;
    void methodOuter(final int c, int d) {
        final int e = 5; int f = 6;
        class A {
            void method() {
                System.out.println(a);
                d = 100; //暗黙的に「final」付与のためコンパイルエラー
                f = 100; //暗黙的に「final」付与のためコンパイルエラー
            }
        }
    }
}

匿名クラスのルール

匿名クラス

  • クラス名を指定しない、定義&インスタンス化を一緒にする
  • サブクラスとして or インタフェース実装クラスとして定義
  • static, abstract, final 使えない
  • 外側クラスのメンバにアクセスできる
  • 外側クラスのメソッドの引数、ローカル変数にアクセス→各変数はfinal
  • コンストラクタ 定義できない
Main.java
interface MyInter { void method(); }
class Outer {
    void method() {
        new MyInter() {       //new スーパークラス・インタフェース() {};
            public void method() {}
        }.method(); //匿名クラスのメソッドの呼び出し
    }
}

アノテーション(注釈)

目的
コードを解釈するコンパイラ、実行するJVMに付加情報を伝える

主なアノテーション

アノテーション 付加情報
@Override スーパークラスのメソッドをオーバーライドする
@Functionallnterface 関数型インタフェースである
@Deprecated 非推奨の要素である
@SuppressWarnings コンパイラの警告を無効にする
@SafeVarargs 安全でない可変長引数の警告を無効にする

@Functionallnterface

関数型インタフェースに付与(任意)
関数型インタフェースの要件を満たしてるかチェック

  • 要件
interface.java
@Functionallnterface
initerface Function<T> {
    void foo(T t);       //抽象メソッドを持つ
    String toString();   //Objectクラスのpublicメソッド=抽象メソッドとして宣言できる
    static void X() {};  //staticメソッド,defaultメソッド宣言できる
}

@Deprecated

プログラマが使うのを薦ないことを示す

  • @Deprecatedの補足情報
    • since = "" (default)
      • 非推奨になったバージョンを示す
    • forRemoval = false (default)
      • true: 将来のバージョンで注釈のついたプログラムを削除することを伝える
      • false: 注釈のついたプログラムが非推奨であることを伝える

@SuppressWarnings( 引数 )

引数に指定したコンパイラの警告を無効にする
引数が必須

指定できる主な警告

警告 内容
unchecked 型を使う時などの警告
deprecation @Deprecatedがつけられた要素の警告
Main.java
    import java.util.*;

    public class Main {
        @SuppressWarnings(value = {"unchecked", "deprecation"}) // メンバ(value)1つのみ→ 「value=」省略可 
        public static void main(String[] args)  {
            Date date = new Date("2024/06/25"); //@Deprecated 付与されたコンストラクタを使用
            List list = new ArrayList(); //<型>が未定義
        }
    }

@SafeVarargs

@SafeVarargs をつけられるメソッドは以下3つ
→ final、static、private

カスタムアノテーション P65

独自のアノテーションを作成する
@interface -> アノテーション型となる(java.lang.annotation.Annotationインタフェースを継承したインタフェースになる)

  • 独自アノテーションに適応させるアノテーション(メタアノテーションについて)
メタアノテーション 内容
@Documented APIドキュメント作成時、アノテーション情報を含める
@Target アノテーションの適用場所を指定
@Rentention アノテーションをどこまで存在させるか指定
@Inherited スーパークラス→サブクラス へアノテーションを継承させる(クラスアノテーションのみ)
@Repeatable 同じ場所に複数回、アノテーションを使う時
  • アノテーション:任意でメンバを持てる
    • メソッド:暗黙的に「public abstract」
    • 戻り値は以下のいずれか
      • 基本データ型
      • String 型
      • Class 型
      • 列挙型
      • アノテーション型
      • 左記の型の一次元配列
  • メンバにデフォルト値を設定できる

@Target

  • カスタムアノテーションの適用場所 = ElementType列挙型で用意
ElementType列挙型 適用場所
TYPE クラス、インタフェース、enum
FIELD フィールド
METHOD メソッド
PARAMETER メソッドパラメータ
CONSTRUCTOR コンストラクタ
LOCAL_VARIABLE ローカル変数
ANNOTATION_TYPE メタアノテーション
PACKAGE パッケージ
TYPEPARAMETER 型パラメータ
TYPE_USE
MODULE モジュール

@Rentention

定数 存在範囲
SOURCE ソースコードのみ存在させる(コンパイラが破棄)
CLASS クラスファイルまで存在させる(実行時VMは無視)
RUNTIME 実行まで存在

@Repeatable

2種類のカスタムアノテーションを定義する必要あり

①複数使えるアノテーション自体の定義.java
import java.lang.annotation.*;
@Repeatable(MyAnnotContainer.class) //複数回使うアノテーションに@Repeatable 付与
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnot {
    public String value();
}
②MyAnootが複数回使われた時、データを保持する専用のアノテーション(コンテナアノテーション).java
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotContainer {
    public MyAnnot[] value();
}

例外・例外処理 P88

例外:プログラム実行時に発生したエラー
→ スローされた例外をキャッチしないと、プログラムは強制終了。。。
→ 強制終了せず実行を続けるためには、例外処理が必要!
→ RuntimeException 以外の Exceptionのサブクラスが check例外

カテゴリ クラス名 エラー発生条件
Errorのサブクラス
(unchecked例外)
AssertionError
StackOverflowError
NoClassDefFoundError
assert文でfalseになる
アプリの再帰が多すぎる
クラスファイルが見つからない
RuntimeException サブクラス
(unchecked例外)
ArrayIndexOutOfBounds
ArrayStoreException
ClassCastException
IllegalStateException
DateTimeException
MissingResourceException
ArithmeticException
NumberFormatException
存在しない要素にアクセス
不正な型を配列に格納
間違ったキャストをした
間違ったメソッドの呼び方
日付/時間に間違った処理
リソースが見つからない
ゼロ除算
整数でない文字列を変換
RuntimeException以外の
Exceptionサブクラス
(checked例外)
IOException
FileNotFoundException
ParseException
SQLException
入出力のとき
ファイル入出力で目的ファイルない時
解析中のエラー
DBにエラー 
  • Throwableクラスのメソッド
    • void printStackTrace()
      • エラートレース(エラーの発生場所を特定)出力
    • String getMessage()
      • エラーメッセージを取得

catch ブロックの注意点 P93

複数のcatchブロック

catch.java
} catch (例外クラス 変数名) {

} catch (例外クラス 変数名) {

} //例外クラスに継承関係がある場合、サブクラスから定義する。スーパーから定義するとコンパイルエラー

マルチキャッチ

catch.java
} catch (NumberFormatException | ArithmeticException e) {
    //継承関係のあるクラスを列挙できない
    //e は final
}

throws してるメソッド

オーバーライドする時の注意点

  • 例外クラス:同じ or サブクラス
  • RuntimeException, サブクラス:スーパークラスの例外に関係なくスローできる
  • スーパーでthrowsされてても、サブでthrowsしなくてもいい

try-with-resources

暗黙的にリソースを解放してくれる
try-with-resource のとき:tryブロックのみ⭕️

Main.java
import java.sql.*;

class MyResource implements AutoCloseable {  //AutoCloseable=close()のみ宣言、実装時にオーバーライドが必要 
    private String msg;
    public MyResource(String msg) { this.msg = msg; }
    public void close() throws Exception
        System.out.println("close():" + msg);
    }
}
public class Main {
    public static void main(String[] args) {
        try (MyResource obj1 = new MyResource("obj1");  //クローズ対象のリソースを生成(var ⚪︎)、final 扱い
             MyResource obj2 = new MyResource("obj2"); ) { //try(**AutoClosable,Closeableインタフェースの実装クラス**)
            System.out.println("tryブロック内の実装");
            throw new SQLException();
        } catch () {  //try{}終了 → 暗黙的にclose()実行 → リソース解放
            System.out.println("catchブロック:SQLException");
        } finally {
            System.out.println("finallyブロック");
        }
    }
}
実行結果.txt
tryブロック内の実装
close():obj2
close():obj1     //複数のリソースを取得した場合:後に取得したclose()から実行
catchブロック:SQLException
finallyブロック

例外の抑制  P107

参考:https://relearn-java.com/exception/

  • try-catch(-finally):複数の例外が発生した場合、伝わる例外が置き換えられちゃう問題
    • なぜ置き換えられるか → 例外は一つしか伝播できない!
  • try-resource-with:最初に発生した例外を伝え、その他はメソッドで取得できるように抑制
Main.java
import java.sql.*;

class MyResource implements AutoCloseable {  //AutoCloseable=close()のみ宣言、実装時にオーバーライドが必要 
    private String msg;
    public MyResource(String msg) { this.msg = msg; }
    public void method() throws Exception
        throw new SQLException("method()でのエラー:");
    }
    public void close() throws Exception
        System.out.println("close():" + msg);
        throw new SQLException("close()でのエラー:" + msg);
    }
}
public class Main {
    public static void main(String[] args) {
        try (MyResource obj1 = new MyResource("obj1")) {
            obj1.method();
        } catch (SQLException e) { //try{}抜 → close()のSQLExceptionは、obj1.method() の付加情報として伝播(=例外の抑制)
            System.out.println("e.getMessage():" + e.getMessage()); //e = obj1.method()のSQLExceptionを参照
            System.out.println("e.getSuppressed()で取り出した情報");
            Throwable[] errAry = e.getSuppressed(); //obj1.method()のSQLException以外のobj1.close()のSQLExceptionを取得(=抑制された例外を配列で取得)
            System.out.println("抑制例外数" + errAry.length());
            for(Throwable ex : errAry) {
                System.out.println(ex.getMessage());
            }
        } finally {
            System.out.println("finallyブロック");
        }
    }
}

アサーション機能

目的:バグを残さない

  • true であるべき箇所に assert(条件式) : "メッセージ(=falseの場合[AssertionError]のメッセージ)"
  • java -ea Main:-ea =アサーション有効
  • java -da Main:-da =アサーション無効
Main.java
private void check(int point) {
    assert ( point > 0 ) : "point = " + point;
    //checkメソッドの処理↓
}

コレクション 過去問外しすぎもう一回やる

ラッパークラス

基本データ型の値 → 参照型として扱う専用のクラス
基本データ型との違い・・・値を操作するメソッドがある

コレクション

複数のオブジェクトをまとめて扱うやつ

コレクション_楷書.jpg

インタフェース名 データ(要素)の管理方法
List(≒ サイズ変更できる配列) ・順序づけて管理
・重複 ⚪︎
Set(≒ 袋の中に要素を入れる) ・順不同で管理
・重複 ×
Queue ・FIFO(First In First Out)で管理
Map ・「キー」+「値」で管理
・キーの重複 ×

イテレータ

コレクションの要素へ順番にアクセスするオブジェクト(≒ 要素を指すカーソル)

イテレータ.jpg

Main.java
TreeSet<String> set = new TreeSet<String>();
set.add("C"); set.add("A"); set.add("B");
Iterator<String> iter = set.iterator();  //Iterator iterator():Collectionインタフェースで宣言,各オブジェクトで実装。要素を指すポインタを返す
while (iter.hasNext()) {  //hasNext():次の要素がある場合、true
    System.out.println(iter.next() + " "); //next():指してる要素を返す
}
console.txt
A B C

Queue インタフェース

FIFO(First In First Out)でデータ管理

キュー.jpg

  • Queue インタフェースの主なメソッド
操作 メソッド名 内容
挿入 boolean add(E e)
boolean offer(E e)
要素追加=true、容量制限で追加ムリ=IllegalStateException
要素追加=true、それ以外=false
削除 E remove()
E poll()
先頭の取得・削除、空の場合=NoSuchElementException
先頭の取得・削除、空の場合=null
検査 E element()
E peek()
先頭の取得、空の場合=NoSuchElementException
先頭の取得・削除、空の場合=null

ジェネリクス

各コレクションのクラス:<⚪︎>に扱う型を指定

  • ジェネリクス という機能
  • <> =「型パラメータリスト」、扱う型を指定するために使う
ジェネリクス.java
ArrayList<String> list = new ArrayList<String>();
ArrayLisy<Object> list2 = new ArrayList<String>();  //型パラメータリストが異なるのでコンパイルエラー

ダイヤモンド演算子

ダイヤモンド演算子.java
ArrayList<String> list = new ArrayList<>(); //左辺の型パラメータリストから類推できるので、右辺は省略。 <>=ダイヤモンド演算子

ジェネリクスを使ってクラス定義

メリット

  • 独自クラスを定義 → クラス生成時に扱う型を決められる!
Main.java
class Gen<T> {  //クラス名の後に型パラメータリストを指定(仮の型:T)
    private T var1; //適応させたい場所に型パラメータを指定する
    //private static T var2;  staticメンバには指定できない
    public Gen(T var1) {this.var1 = var1;}
    public T getVar1() {return var1;}
    public void setVar1(T var1) {this.var1 = var1;}
}

public class Main {
    public static void main(String[] args) {
        Gen<String> g1 = new Gen<>("ABC");  //型パラメータリストの型を指定->g1オブジェクト内で扱う型が決まる(T -> String)
        Gen<Integer> g2 = new Gen<>(2);
    }
}

ジェネリクスを使ってメソッド定義

型パラメータリストの有効範囲=メソッド内だけ

Main.java
class Gen {
    public <T> T methodA(T value) { return value; }  //戻り値・引数=型パラメータ使用
}

public class Main {
    pubcli static void main(String[] args) {
        Gen g = new Gen();
        String stg = g.<String>methodA("abc");
        // String stg = g.methodA("abc"); -> 引数で類推できるので<String>なしでも⚪︎
    }
}

ジェネリクスを使ってインタフェース定義

インタフェースはインスタンス化できない
→ 実装するクラス側で型を指定する

Main.java
interface MyIn<T> { void method(T t); } //インタフェース名の後に型パラメータリスト指定、引数に適用

class Foo implements MyIn<String> { //実装クラスで型を指定
    public void method(String s) { System.out.println(S); }
}

public class Main {
    public static void main(String[] args) {
        new Foo.method("ABC");  //実装を利用する側では型の指定はしない
        new MyIn<Integer>() {  //匿名クラスでジェネリクスを使用する例
            public void method(Integer i) { System.out.println(i); }
        }.method(100);
    }
}

継承を使ったジェネリクス

型パラメータリスト=どんな型でも指定できる!
→ 指定できる型を限定したい時に継承を使う

Main.java
class Gen<T extends Number> {  //Number or そのサブクラスを型に指定できる
    private T var;
    public Gen(T var) { this.var = var; }
}

public class Main {
    public static void main(String[] args) {
        Gen<Integer> g1 = new Gen<>(100);  //Integer型を扱う
        Gen<Double> g2 = new Gen<>(3.14);  //Double型を扱う
        Gen<String> g3 = new Gen<>("abc")  //Numberのサブクラスでないため、コンパイルエラー
    }
}

ワイルドカードを使ったジェネリクス

? を使って型パラメータを指定 → 型は実行時でないとわからない

:

Main.java
class X {
    public String toString() { return "X"; }
}

class Y extends X {
    public String toString() { return "Y"; }
}

public class Main {
    public static void method1(List<? extends X> list) {  //listの要素は、Xかそのサブクラスのみ
        //ワイルドカード=実行時まで型は分からない  → add()でなんらかのオブジェクトを格納するとエラー
        list.add(new X());
        list.add(new Y());
    }
    public static void method2(List<? super Y> list) {  //listの要素は、Yかそのスーパークラス
        list.add(new X());  //エラー
        list.add(new Y());  //<? super type> のみtypeと同じ型のオブジェクトは追加 ○
    }
    public static void main(String[] args) {
        List<X> l1 = new Arraylist<>(); l1.add(new X());
        List<Y> l2 = new Arraylist<>(); l2.add(new Y());
    }
}

オブジェクトの順序づけ P158

ソート順序を独自に決める

二つのインタフェースを実装する!

  • java.lang.Comparable
    • 自然順序づけを提供するインタフェース
    • public int compareTo(T o) のみ宣言 : T=比較対象のデータ型
      • → 実装クラスでオーバーライドして、並び順を決める
      • → 比較対象のオブジェクト自身に compareTo() を実装
  • java.util.Comparator
    • 比較対象のオブジェクトから独立した、比較ルールクラスを定義
    • public int compare(T o1, T o2) 宣言
      • → 実装クラスでオーバーライドして、並び順を決める
      • → Comparableの compare() と同様処理
  • IntegerクラスでComparableインタフェースを実装
Comparable.java
public final class Integer extends Number implements Comparable<Integer> {  //Comparableインタフェースで扱うクラスを指定。Integerクラスで実装するから<Integer>
    public int compareTo(Integer anotherInteger) {
        return compare(this.value, anotherInteger.value);
    }

    public static int compare(int x, int y) {
        return (x < y) ? -1 : ((x == y) ? 0 : 1);  //自オブジェクト・引数のオブジェクトの比較
    }
}
  • 独自クラスでComparatorインタフェースを実装
Comparable.java
class Employee {
    private Integer id;
    public Integer getId() { return id; }
}
class MyRule implements Comparator<Employee> {
    public int compare(Employee e1, Employee e2) {
        return e1.getId().compareTo(e2.getId());  //e1.getId()とe2.getId()のIntegerオブジェクトをIntegerクラスのcompareTo()で比較 → 自然順序でソート
    }
}

コレクションが提供する便利なメソッド

Collections クラス

メソッド 説明
static void sort(List list) 自然順序づけに従って、昇順にソート
static void sort(List list, Comparator c) コンパレータが示す順序に従って、ソート
static void reverse(List< ? > list) 要素の順番を逆にする

Arrays クラス

  • static void sort(Object[] a)
    • 要素を昇順にソート
  • static List asList(T... a)
    • 引数の配列からリストを作成
array.java
Object[] array = { "aa", 10 };
Arrays.sort(array);  //違う型を持つ配列をソート → ClassCastException

String[] args = {"A", "B", "C"};
List<String> list = Arrays.asList(args);
list.add("D");  //Arrays.asList() -> 固定サイズのlistになる。追加すると、UnsupportedOperationException

of( ) メソッド

  • List, Set, Map で使用できる
  • いかなる変更もダメ
  • nullダメ

関数型インタフェース P182

定義されている抽象メソッドが1つだけのインタフェース

  • ラムダ式、メソッド参照で使うために導入された
  • java.util.function パッケージで提供

var (ローカル変数の型推論)

var 使える箇所

  • ローカル変数の初期化時
  • for 文のインデックス
  • 拡張 for のローカル変数
  • try-with-resourceのローカル変数
    使えない箇所
  • メソッドの引数、戻り値
  • コンストラクタの引数
  • フィールド
  • catchブロック

ラムダ式

インタフェースの実装をシンプルにできる方法!
サブクラスの実装+メソッドのオーバーライド

  • 主な関数型インタフェース
インタフェース名 抽象メソッド
Function<T, R> R apply(T t)
BiFunction<T, U, R> R apply(T t, U u)
Consumer<T> void accept(T t)
BiConsumer<T, U> void accept(T t, U u)
Predicate<T> boolean test(T t)
BiPredicate<T, U> boolean test(T t, U u)
Supplier<T> T get()
UnaryOperator<T> T apply(T t) ※Functionの拡張
BinaryOperator<T> T apply(T t1, T t2) ※BiFunctionの拡張
  • ラムダ式の書き方
省略しない書き方.java
Function<String, String> f1 = (String str) -> {  //引数が一つの場合=()省略可、インタフェースの宣言で、引数の型は分かるので省略可 
    return "Hello " + str;  //一行の場合={}省略可、{}省略した場合、return; 省略可
}
String str1 = f1.apply("HY");
省略した書き方.java
Function<String, String> f1 = str -> "Hello " + str
String str1 = f1.apply("HY");
  • ラムダ式が外側の変数を使うとき→その変数は final でないといけない
実質的 final.java
public class Main {
    int a = 10;
    public void method() {
        final int b = 20;
        int c = 30;  //実質的final
        int d = 40;
        d = 50;      //※再代入すると、実質的finalにはならない!!
        int e = 60;  //実質的final
        Function<String, String> f1 = (String str) -> {
            System.out.println(a);
            System.out.println(b);
            System.out.println(c);
            System.out.println(d);  //コンパイルエラー
            System.out.println(e);
            e = 100;  //コンパイルエラー
            return "Hello" + str;
        } 
    }
}
実質的 final.java
public static void main(String[] args) {
    int i = 25;
    Supplier<Integer> s = () -> i;  //アクセスは問題ない
    i++;  //i は final なのでコンパイルエラー
    System.out.println(s.get());
}

メソッド参照

インタフェース実装時、抽象メソッドの引数(個数・型)= 処理内のメソッドの引数(個数・型)→ メソッド参照で書ける

  • staticメソッド参照
  • インスタンスメソッド参照
  • コンストラクタ参照

staticメソッド参照( 処理内のメソッド:staticメソッド)→ クラス名 :: メソッド名

staticメソッド参照.java
List<Integer> list = Arrays.asList(3, 1, 2);

//ラムダ式
Consumer<List<Integer>> con1 = list -> Collection.sort(list);

//static メソッド参照
Consumer<List<Integer>> con1 = Collection::sort;

con1.accept(list);

インスタンスメソッド参照( 処理内のメソッド:インスタンスメソッド)→ インスタンス変数名 :: メソッド名

forEach()の例.java
List<Integer> list = List.of(3, 1, 2);

//ラムダ式
list.forEach( a -> System.out.print(a + " ") ); //Iterableインタフェースから継承したforEach()メソッド=コレクションの各要素に処理をする Comsumer インタフェースのaccept()が実装されている。

//インスタンスメソッド参照
list.forEach(System.out::print + " ") //コンパイルエラー= +演算子などで処理はできない!
インスタンス変数を宣言していない時.java
//ラムダ式
UnaryOperator<String> obj = s -> s.toUpperCase();

//インスタンスメソッド参照
UnaryOperator<String> obj = s ::toUpperCase(); //コンパイルエラー=s変数を宣言なしで使っているから

//省略なしで記述
class Uo implements UnaryOperator<String> {
    @Override 
    public String apply() {
        return s.toUpperCase();  //コンパイラ「このS何者??」
    }
}

//インスタンスメソッドを呼びだすインスタンス変数が指定できない時の書き方
UnaryOperator<String> obj = String ::toUpperCase(); //クラス名で指定する(staticメソッド参照みたい...)
インスタンスメソッド参照に引数がある場合.java
//ラムダ式
BiFunction<String, Integer, Character> bf1 = (s, i) -> s.charAt(i);
System.out.println(bf1.apply("Java", 2));

//インスタンスメソッド参照 OKパターン
BiFunction<String, Integer, Character> bf1 = String ::charAt;
System.out.println(bf1.apply("Java", 2));

//インスタンスメソッド参照 NGパターン
BiFunction<Integer, String, Character> bf1 = String ::charAt; //「第一引数=メソッド実行する対象オブジェクト、第二引数=メソッドの引数」で渡すことが決まってるから!
System.out.println(bf1.apply(2, "Java"));
static(インスタンス)メソッドの区別がつかない.java
//IntegerクラスのhashCode() → static・インスタンスメソッドどちらもある

//ラムダ式
Function<Integer, Integer> obj = i -> i.hashCode(); //hashCode()=インスタンスメソッド

//staticメソッド参照
Function<Integer, Integer> obj = Integer::hashCode(); //コンパイルエラー=どちらか分からない
// → こういう場合、ラムダ式で実装すること!

コンストラクタ参照 クラス名::new

Main.java
public class Main {
    public static void main(String[] args) {
        //Supplier<Foo> obj1 = () -> new Foo();
        Supplier<Foo> obj1 = Foo ::new
        System.out.println(obj1.get().a);
        /* class test implements Supplier<Foo> {
            public Foo get() {
                return Foo obj1 = new Foo();
            }
        }*/

        //Function<Integer, Foo> obj2 = i -> new Foo(i);
        Function<Integer, Foo> obj2 = Foo::new;
        System.out.println(obj2.apply(10).a);
    }
}

class Foo {
    int a = 0;
    Foo() { }
    Foo(int a) { this.a = a; }
}
配列を生成するパターン.java
//Function<Integer, String[]> array = i -> new String[i];
Function<Integer, String[]> array = String[]::new;
System.out.println(array.apply(10).length);

基本データ型に特化した関数型インタフェース

  • 引数、戻り値にint型を使う関数型インタフェース
インタフェース名 抽象メソッド
IntFunction<R> R apply(int value)
IntConsumer void accept(int value)
IntPredicate boolean test(int value)
IntSupplier int getAsInt()
IntUnaryOperator int applyAsInt(int value)
IntBinaryOperator int applyAsInt(int value1, int value2)
基本データ型特化インタフェース.java
//IntFunction<String[]> array = index -> new String[index];
IntFunction<String[]> array = String[]::new;
String[] obj1 = array.apply(5);

IntSupplier obj2 = "Java"::length;
int num2 = obj2.getAsInt();

様々なパターンの関数型インタフェース 命名規則が決まっている

インタフェース名 抽象メソッド
ToIntFunction<T> int applyAsInt(T t)
ToIntBiFunction<T, U> int applyAsInt(T t, U u)
IntToDoubleFunction double applyAsDouble(int value)
IntToLongFunction long applyAsLong(int value)
ObjectIntConsumer void accept(T t, int value)
ラムダ式の型の自動変換.java
IntToDoubleFunction f1 = (int i) -> { return i * 3.14 }
IntToDoubleFunction f2 = (Integer i) -> { return i * 3.14 }  //コンパイルエラー=引数は自動変換されない
Double ans1 = f1.applyAsDouble(100);  // OK=戻り値は自動変換される

関数型インタフェースの合成

andThen(),compose().java
Function<String, Character> f1 = s -> s.charAt(0);
Function<Character, Boolean> f2 = c -> Character.isUpperCase(c);

// 自身.andThen(after) ①自身②after->結果返す
Function<String, Boolean> f3 = f1.andThen(f2);
System.out.println(f3.apply("Java"));

/*class F3 implements Function<String, Boolean> {
    public boolean apply(String str) {
        Character c = f1.apply("Java");
        Boolean b = f2.apply(c); 
        return b;
    }*/

//自身.compose(before) ①before②自身→結果返す
Function<String, Boolean> f4 = f2.compose(f1);
System.out.println(f4.apply("Java"));

}
and(),or(),negate().java
LongPredicate p1 = n -> (n % 3) == 0;
LongPredicate p2 = n -> (n % 5) == 0;

//一方 true -> true返る
LongPredicate p3 = p1.or(p2);
System.out.println(p3.test(15));

//両方 true -> true返る
LongPredicate p4 = p1.and(p2);

//反対の結果が返る
LongPredicate p5 = p4.negate();

ストリームAPI

データ(コレクション、配列...)を元に集計処理をするAPI

  • 複数の処理をつなぐ仕組み(=パイプライン処理)をもってる
  • パイプライン処理には、データソースが必要(処理のもとになるから)
  • データソースとなるクラスは、ストリーム生成するメソッド持ってる
    <処理の流れ>
  1. データソース
  2. ストリームオブジェクト生成
  3. 処理(パイプラインの途中) → 中間操作
  4. 処理(最後) → 終端操作

ストリームAPI.jpg

ストリームの種類

インタフェース名 説明
Stream<T> 汎用的なストリーム
IntStream int型に対応したストリーム
LongStream Long型に対応したストリーム
DoubleStream Double型に対応したストリーム

終端操作を行うメソッド

allMatch(), anyMatch(), noneMatch().java
//引数=Predicateインタフェース
List<String> data = Arrays.asList("yuga", "ren", "shou");
boolean result1 = data.stream().allMatch(s -> s.length() >= 3);  //指定された条件に全ての要素が一致するか
boolean result2 = data.stream().anyMatch(s -> s.length() >= 3);  //指定された条件にいずれかの要素が一致するか
boolean result3 = data.stream().noneMatch(s -> s.length() >= 3);  //指定された条件に全ての要素が一致しないか

Stream<String> stream1 = data.stream();
boolean result4 = stream1.allMatch(s -> s.length() >= 3);
boolean result5 = stream1.anyMatch(s -> s.length() >= 3);  //コンパイルエラー ※1streamオブジェクトに対して、終端操作は1回だけ!!
count(), forEach().java
long result = Stream.of("a", "b", "c").count();  //要素の数を返す

Stream<String> stream1 = Stream.of("a", "b", "c");
stream1.forEach(System.out::print);  //引数で指定されたアクションを各要素に行う、引数=Consumerインタフェース
reduce()メソッド.java
Stream<Integer> stream = Stream.of(10, 20, 30);
int result = stream.reduce(0, (a, b) -> a + b);  // T reduce(T value, BinaryOperator<T> bo) value:初期値
System.out.println(result);

// 結果:60
reduce()メソッド.java
inaryOperator<Integer> operator = (a, b) -> a + b;
Stream<Integer> stream2 = Stream.of(40, 50, 60); 
Optional result2 = stream2.reduce(operator);  // Optional<T> reduce(BinaryOperator bo)
System.out.println(result2);
toArray()メソッド.java
//ストリームオブジェクトを配列に変換
int[] ary1 = IntStream.range(1, 10).toArray();  //IntStreamインタフェースで提供されるtoArray()メソッド、戻り値=int[]
int[] ary2 = IntStream.rangeClosed(1, 10).toArray();  // int[] toArray();

Object[] ary3 = Stream.of("a", "b").toArray();  //Streamオブジェクトで提供されるtoArray()メソッド、戻り値=Object[]

String[] ary4 = Stream.of().toArray(String[] :: new);  // A[] toArray(IntFunction<A[]> if)

Optional クラス  P228

一つの値を保持しているクラス

  • 値を保持しているかどうかで処理が違うメソッドを持つ
  • 値が存在しない場合 = empty というオブジェクト
get()、isPresent()メソッド.java
Optional<Integer> op = Optional.of(10);  // Optional.of() -> 引数で指定された値を持つOptionalを返す
System.out.println(op.get());  // T get() -> ある:値、ない:NoSuchElementException
op.isPresent();  // boolean isPresent() -> ある:true、ない:false

戻り値が Optional の終端操作

max()メソッド.java
// Optional<T> max(Comparator com) -> comparatorに従って最大要素を取り出す
List<String> data = Arrays.asList("aaa", "bb", "c");
Optional<String> max = data.stream().max(Comparator.naturalOrder());
findFirst()、findAny()メソッド.java
List<String> data = Arrays.asList("あ", "い", "う", "え", "お");
Optional<String> first = data.stream().findFirst();  //Optional<T> findFirst() -> 最初の要素を返す
Optional<String> any = data.stream().findAny();  //Optional<T> findAny() -> いずれかの要素を返す
Stream<String> stream = Stream.empty();
Optional<String> empty = stream.findFirst();

System.out.println(first.get());
any.ifPresent(System.out::println);  // void ifPresent(Consumer<T> con) -> 値が存在=Consumerのacccept()を呼び出す。終端操作ではない。Optionalのメソッドの一つ
empty.ifPresent(r -> System.out.println("result:" + r));
  • Optional.get()のNoSuchElementException を自身で指定したい(終端操作ではなく、Optionalクラスのメソッド)
orElse()、orElseGet()、orElseThrow()メソッド.java
Stream<Double> stream = Stream.empty();
Optional<Double> result = stream.findFirst();

//値が存在=値を返す、値がない=返す値を指定
System.out.println(result.orElse(0.0));  // T orElse(T other) -> 戻り値:other
System.out.println(result.orElseGet(() -> Math.random()));  // T orElseGet(Supplier other) -> 戻り値:Suppliler の get()を呼び出す、戻り値の型は合わせないとコンパイルエラー
System.out.println(result.orElseThrow(IllegalArgumentException::new)); // T orElseThrow(Suppiler other) -> 戻り値:Supplierで生成した例外をスローする

中間操作

取得したストリーム ー(処理)→ 新しいストリーム生成

  • 中間操作をするメソッド群= java.util.stream.Streamインタフェースで宣言
  • filter(): trueで返る要素のストリームを返す
  • distinct(): 重複を除く要素のストリームを返す
filter()、distinct()メソッド.java
Stream<String> stream = Stream.of("akari", "masato", "asako", "");
stream.filter(s -> s.startWith("a"))  // Stream<T> filter(Predicate<T> predicate)
      .forEach(x -> System.out.print(x + "" ));
//結果: akari asako

Stream<String> stream2 = Stream.of("akari", "masato", "asako", "", "akari");
stream2.distinct()  // Stream<T> distinct()
      .forEach(x -> System.out.print(x + "" ));
//結果: masato asako
  • skip(): 先頭からn個の要素をスキップしたスト
  • limit(): n個分の要素を持つストリームを返す
limit()、skip()メソッド.java
IntStream.iterate(1, n -> n + 1)  //初期値:1、1ずつ加算した要素の無限ストリーム返す
         .skip(100L)  // Stream<T> skip(long n)
         .limit(5L)  // Stream<T> limit(log n)
         .forEach(x -> System.out.print(x + " "));
  • map(): 引数の処理をしたストリームを返す
map()メソッド.java
Stream<String> streama = Stream.of("sakura", "momo", "itsuki");
Stream<String> streamb = streama.map(s -> s.toUpperCase());  // Stream<T> map(Function<T> function)
streamb.forEach(x -> System.out.print(x + " "));
  • map(): 1要素=1結果
  • flatMap(): 1要素=多結果
    • → 入れ子になっているストリームを平坦化する
flatMap()メソッド.java
List<Integer> data1 = Arrays.asList(10);
List<Integer> data2 = Arrays.asList(20, 30);
List<Integer> data3 = Arrays.asList(40, 50, 60);
List<List<Integer>> dataList = Arrays.asList(data1, data2, data3);

//map()の場合
dataList.stream()
        .map(data -> data.stream())
        .forEach(l -> {
            l.forEach(x -> System.out.print(x + " "));
        });

//flatMap()の場合
dataList.stream()
        .flatMap(data -> data.stream())
        .forEach(x -> System.out.print(x + " "));

flatMap().jpg

  • sorted(): 自然順序に並べ替え
    • sorted(Comparator.〇〇()): 明示的に並べ替え順序を指定
sorted()メソッド.java
Stream.of("tatsumi", "igarashi", "kojima")
      .sorted()  // Stream<T> sorted()
      .forEach(System.out.print(x + " "));
Stream.of("tatsumi", "igarashi", "kojima")
      .sorted(Comparator.reverseOrder())  // Stream<T> sorted(Comparator comparator)、逆順
      .forEach(x -> System.out.print(x + " "));
  • peek(): 引数がConsumerインタフェース(戻り値なし)→ デバック機能として使う
peek()メソッド.java
List<String> list = Stream.of("one", "two", "three", "four", "four");
                    .filter(s -> s.length() > 3)
                    .peek(e -> System.out.println("filter後の確認" + e)) // Stream<T> peek(Consumer consumer)
                    .map(String::toUpperCase)
                    .forEach(System.out::println);

ストリームインタフェースの型変換するメソッド

インタフェース名 Stream 生成 DoubleStream 生成 IntStream 生成 LongStream 生成
Stream map() mapToDouble() mapToInt() mapToLong()
DoubleStream mapToObject() map() mapToInt() mapToLong()
IntStream mapToObject() mapToDouble() map() mapToLong()
LongStream mapToObject() mapToDouble() mapToInt() map()
  • ※ 基本データ型のストリーム → ラッパークラスのStreamへ変換する= boxed()メソッド
    • IntStream -> Stream <Integer>
    • DoubleStream -> Stream <Double>
    • LongStream -> Stream <Long>
boxed()メソッド.java
IntStream stream = Stream.of(1, 2, 3);
Stream<Integer> stream = stream.boxed();

ストリームインタフェースの暗黙の型変換をするメソッド

  • IntStream、LongStreamインタフェースで提供
インタフェース名 メソッド
IntStream LongStream asLongStream()
DoubleStream asDoubleStream()
LongStream DoubleStream asDoubleStream()
暗黙の型変換.java
IntStream streami = Stream.of(2, 3, 5);
DoubleStream streamd = streami.asDoubleStream();

終端操作 ~ collect()メソッド ~ P246

ストリームの要素 → まとめて1つのオブジェクトを取得
collect()メソッド: 引数にCollectorクラスのメソッドを指定

Collectorsクラスのメソッド

  • toList()、joining()、summingInt()、averagingInt()
toList()、joining()、summingInt()、averagingInt().java
Stream<String> stream1 = Stream.of("tatsumi", "igarashi", "kojima");
List<String> result = stream1.collect(Collectors.toList());  //要素をListに蓄積する

Stream<String> stream2 = Stream.of("tatsumi", "igarashi", "kojima");
String result2 = stream2.collect(Collectors.joining(" | "));  //要素を連結 → 1つのString
//結果: tatsumi | igarashi | kojima

Stream<String> stream3 = Stream.of("tatsumi", "igarashi", "kojima");
Integer result3 = stream3.collect(Collectors.summingInt(t -> t.length()));  //結果の合計値 Collector<T,?,Integer> summingInt(ToIntFunction<T> function)
//結果: 21

Stream<String> stream4 = Stream.of("tatsumi", "igarashi", "kojima");
Double result4 = stream4.collect(Collectors.averagingInt(t -> t.length())); //結果の平均 Collector<T,?,Integer> .averagingInt(ToDoubleFunction<T> function)
//結果: 7
  • toSet()、toMap()メソッド: Set、Mapへ変換
toSet()、toMap()メソッド.java
Stream<String> stream = Stream.of("tatsumi", "igarashi", "kojima");
Set<String> set = stream.collect(Collectors.toSet());

Stream<String> stream2 = Stream.of("tatsumi", "igarashi", "kojima");
Map<String, String> map = stream2.collect(Collectors.toMap(s -> s, String::toUpperCase));  //  toMap(Function<T> key, Function<T> value)
//結果: {tatsumi=TATSUMI, igarashi=IGARASHI, kojima=KOJIMA}
  • toMap(Function key, Function value, BinaryOperator function)
toMap().java
Stream<String> stream = Stream.of("tatsumi", "igarashi", "kojima");
Map<Integer, String> map = stream.collect(Collectors.toMap(
                                s -> s.length(),
                                s -> s,
                                (s1, s2) -> s1 + ":" + s2));  //第三引数=キーが同じ場合の処理
  • toMap(Function key, Function value, BinaryOperator function, Supplier mapSuppiler)
toMap().java
Stream<String> stream = Stream.of("tatsumi", "igarashi", "kojima");
Map<Integer, String> map = stream.collect(Collectors.toMap(
                                s -> s.length(),
                                s -> s,
                                (s1, s2) -> s1 + ":" + s2),
                                TreeMap::new);  // 第4引数=格納するMapを指定 
  • Collectors <T,?,Map <K,List<T>>> groupingBy(Function<T> function) → 引数が一つの場合
groupingBy()メソッド.java
Stream<String> stream = Stream.of("belle", "akko", "ami", "bob", "nao");
Map<String, List<String>> map =
            stream.collect(Collectors.groupingBy(s -> s.substring(0, 1)));  //①apply()に要素を渡す②Mapのキーになる値を返す③同じキーを返す要素をListでグループ化
System.out.println(map);
//結果: {a=[akko, ami], b=[belle, bob], n=[nao]}
  • Collectors <T,?,Map <K,List<T>>> groupingBy(Function<T> function, Collector collect) → 引数が二つの場合
groupingBy().java
Stream<String> stream = Stream.of("belle", "akko", "ami", "bob", "nao");
Map<String, String> map = 
            stream.collect(Collectors.groupingBy(s -> s.substring(0, 1), Collectors.joining()));  //第3引数=グループ化した要素同士に行いたい処理
//結果: {a=akkoami, b=bellebob, n=nao}
  • Collectors <T,?,Map <K,List<T>>> groupingBy(Function<T> function, Suppiler suppiler,Collector collect) → 引数が3つの場合
groupingBy().java
Stream<String> stream = Stream.of("belle", "akko", "ami", "bob", "nao");
Map<String, String> map = 
            stream.collect(Collectors.groupingBy(s -> s.substring(0, 1),
                           TreeMap::new,  //戻り値のMap型を指定する
                           Collectors.joining()));
  • partitioningBy(Predicate predicate)
    → グルーピング処理をPredicateでやる
partitioningBy().java
Stream<Integer> stream = Stream.of(3, 5, 7, 9);
Map<Boolean, List<Integer>> map = stream.collect(
                           Collectors.partitioningBy(s -> s > 5));
 // 結果: {false=[3, 5], true=[7, 9]}                          
  • partitioningBy(Predicate predicate, Collector collector)
partitioningBy().java
Stream<Integer> stream = Stream.of(3, 5, 7, 9);
Map<Boolean, Integer> map = stream.collect(
                           Collectors.partitioningBy(s -> s > 5),
                           Collectors.summingInt(n -> n));
 // 結果: {false=8, true=16}  
  • maping(): ストリームの各要素に行いたい処理を指定
maping().java
Stream<String> stream = Stream.of("akira", "rimi", "sonoko");
String result = stream.collect(Collectors.maping(
                       s -> s.toUpperCase(),  //第1引数=各要素に行いたい処理
                       Collectors.joining(":")));  //第2引数=マップ後に行いたい処理
//結果: AKIRA:RIMI:SONOKO
  • maxBy(Comparator comparator)、minBy(Comparator comparator):最大値、最小値を取得
maxBy()、minBy().java
Stream<String> stream = Stream.of("naoki", "akko", "ami")
Optional<String> result = stream.collect(Collectors.minBy(Comparator.naturalOrder()));
//結果: akko

モジュール・システム

モジュールとは
クラス < パッケージ < モジュール

  • クラス:プログラムの最小単位
  • パッケージ:クラスをグループ化
  • モジュール:パッケージをグループ化

モジュールのメリット

  • 信頼性の高い構成
    • モジュール間の依存性を明記、認識
  • 強力なカプセル化
    • エクスポート機能
  • スケーラブルなJavaプラットフォーム
    • 必要なモジュールのみ組み込める
  • プラットフォームの整合性向上
    • 利用されたくない機能は隠せる
  • パフォーマンスの向上
  • 主なモジュール
モジュール 説明 含まれるパッケージ
java.base Java SE Platform の基盤になるAPI java.lang, java.util, java.text
java.desktop GUIなどのAPI java.awt, javax.swing
java.sql JDBC API java.sql, javax.sql

今の実行環境で参照できるモジュール一覧を表示

terminal.java
% java --list-modules
java.base@11
java.compiler@11

モジュール定義

module-info.java
module foo {
    exports xlib;  //exports:どのパッケージを公開するか指定
    requires java.base;  //requires:依存するモジュール指定、java.base:全てのモジュールへ暗黙的に含まれるから、省略可能
}

モジュールのコンパイル

  • ライブラリを提供する側
terminal.java
javac -d out/foo src/foo/ylib/XTest.java  // javac -d <クラスファイルの生成場所> <コンパイルするソースファイル場所>
javac -d out/foo src/foo/module-info.java
  • ライブラリを利用する側
terminal.java
javac -d out/client --module-path out/foo src/client/app/Main.java src/foo/module-info.java  // --module-path <依存するモジュール場所(クラスファイル)>

モジュールの実行(クラスファイルで実行)

terminal.java
java  --module-path out/foo;out/client --module client/app.Main 
// --module <モジュール名+main()含むクラス名>

間接エクスポート

間接エクスポート.jpg

名前付きモジュール、その他のモジュール

  • 名前付きモジュール:module-info.class ある
  • その他モジュール:module-info.class ない
    • 自動モジュール:モジュールパス上にある
    • 無名モジュール:クラスパス上にある
      • → 全てのパッケージをexports
      • → モジュールパス上全てのモジュールをrequires
      • → 参照が違う

mumei .jpg
自動モジュール.jpg

モジュールの依存性確認

  • オプションで確認
terminal.java
--describe-module //モジュールの説明出力
--show-module-resolution //モジュールの解決(モジュールの呼び出し)を出力
  • コマンドで確認
jdeps.java
jdeps -summary (-s) //依存関係のサマリーだけ出力
jdeps -jdkinternals  //JDKのクラス・レベルの依存関係を検索
jdeps -dotoutput   //DOTファイルの出力先を指定

jlink コマンド

JREのカスタマイズツール

オプション 説明
--add-modules イメージに追加するモジュールを指定
--module-path (-p) モジュールパスを指定
--compress (-c) リソースの圧縮を有効化
--output 出力ディレクトリ

ServiceLoader

インタフェースの実装クラスを動的にロードする仕組み

  • インタフェース、実装クラス提供側
module-info.java
module foo {
    exports xlib;
    provides xlib.MyInter with xlib.XTest;  //provides <インタフェース> with <実装クラス>
}
  • 実装クラスを利用する側
module-info.java
module client {
    requires foo;
    uses xlib.MyInter;  // use <インタフェース>
}

ボトムアップ / トップダウン移行

Javaのアプリケーションをバージョンアップする
→ アプリをモジュール化する必要あり!
↓ 3つのモジュールを使用
システムの移行.jpg

ボトムアップ移行

最下位のモジュールから移行

  1. c.jar モジュール化 → 名前付きモジュールへ
    1. モジュールパス上へ配置
    2. 公開すべきパッケージを exports
  2. b.java → a.jar の順にモジュール化

ボトムアップ移行.jpg

トップダウン移行

最上位のモジュールから移行

  1. 全てのモジュールをモジュールパス上へ配置 → 自動モジュールへ
  2. a.jar からモジュール化 → 名前付きモジュール
    1. a.jarで必要なモジュールをrequires

トップダウン移行.jpg

並列処理

スレッド

プログラム実行 → Javaの実行環境がスレッド作成(処理の最小単位)→ 処理開始
→ 複数のスレッドに分けて実行= マルチスレッド

スレッド作成&開始

  1. Thread クラスを継承
  2. Runnable インタフェースを実装
Main.java
public class Main {
    public static void main(String[] args) {
        //①Thread クラスを継承した時のインスタンス化
        ThreadA theadA = new TheadA();

        //②Runnable インタフェースを継承した時のインスタンス化
        Thread  threadA = new Thread(new ThreadA());

        //②Runableインタフェースをラムダ式で記述したパターン
        Thread  threadA = new Thread(() -> {
            System.out.println("①Threadクラスを継承");
        });

        //(共通)スレッド実行開始 -> run()呼び出し
        threadA.start();

    }
}

//①Threadクラスを継承
class ThreadA extends Thread {
    public void run() {
        System.out.println("①Threadクラスを継承");
    }
}

//②Runnable インタフェースを実装
class ThreadA implements Runnable {
    public void run() {
        System.out.println("②Runnable インタフェースを実装");
    }
}

スレッドの状態

  • start() → 実行可能状態
  • run() → 実行状態
  • 終了したスレッド → 再実行✖️(2回 start()できない)

スレッドの状態.jpg

スレッドの優先度

優先度の高いスレッドから実行される

メソッド名 説明
static Thread currentThread() いま実行中のスレッドを取得
final String getName() スレッド名を返す
final int getPriority() 優先度を返す
final void setPriority(int value) 優先度を変更、1~10、デフォルト=5

スレッドを制御

  • スレッド制御するThreadのメソッド
メソッド名 処理
static void sleep(long time) throws interruptedException スレッドをtimeミリ秒休止
final void join() throws interruptedException 実行中のスレッドが終わるまで待機
static void yield() 実行スレッドを休止→他スレッドに実行機会与える
void interrupt() ①休止中のスレッドに割り込む
②割り込まれたスレッド:interruptedExceptionを受け取る
③処理を再開
Main.java
class Main {
    public static void main(String[] args) {
        Thread  threadA = new Thread(() -> {
            System.out.println("theadA: sleep 開始");
            try {
                Thread.sleep(5000);  //sleep()を呼ぶと、このスレッドは(引数)ミリ秒休止、ThreadAのsleep()
            } catch (InterruptedException e) {
                System.out.println("threadA:割り込みをキャッチしました");
            }
            System.out.println("threadA:処理再開");
        });
        threadA.start();

        try {
            System.out.println("main: sleep 開始");
            Thread.sleep(2000); //mainのsleep()
            System.out.println("main: sleep 終了");
            threadA.interrupt();  //スレッドへ割り込み
        } catch (InterruptedException e) {
            System.out.println("main: 割り込みをキャッチしました");
        }
    }
}

//結果
main: sleep 開始
threadA: sleep 開始
main: sleep 終了
threadA: 割り込みをキャッチしました
threadA: 処理開始

排他制御、同期制御

複数のスレッド → 共有のオブジェクト使用 → スレッドを制御

  • 排他制御: オブジェクトを独占して実行する
  • 同期制御: スレッド同士で実行のタイミングを合わせる
  • Objectクラスの同期制御メソッド
メソッド名 処理
final void wait() throws InterruptedException 現在のスレッドを待機
final void wait(long time) throws InterruptedException time時間スレッドを待機
final void notify() オブジェクトの待機中スレッド1つ再開
final void notifyAll() オブジェクトの待機中スレッド全て再開

synchronizedがないものに使用すると、IllegalMonitorStateException

Main.java
public class Main {
    public static void main(String[] args) {
        Share share = new Share();
        ThreadA threadA = new ThreadA(share);
        ThreadB threadB = new ThreadB(share);
        threadA.start(); threadB.start();
    }
}
class ThreadA extends Thread {
    private Share share;
    public ThreadA(Share share) { this.share = share };
    public void run() {
        for(int i = 0; i < 5; i++){ share.set(); }
    }
}
class ThreadB extends Thread {
    private Share share;
    public ThreadA(Share share) { this.share = share };
    public void run() {
        for(int i = 0; i < 5; i++){ share.print(); }
    }
}
//共有して使うオブジェクト
class Share {
    private int a = 0;
    private String b;
    public synchronized void set() {  //このメソッド実行時、Shareオブジェクトはロック
        while(a != 0) {
            try {
                wait();
            } catch(InterruptedException e) { }
        }
        notify();
        a++; b = "data";
        System.out.println("set() a :" + a + " b : " + b);
    }
    public synchronized void print() {  ////このメソッド実行時、Shareオブジェクトはロック
        while(b == null) {
            try {
                wait();
            } catch(InterruptedException e) { }
        }
        notify();
        a--; b = null;
        System.out.println("  print() a :" + a + " b : " + b);
    }
}
//結果
set() a : 1 b: data
  print() a : 0 b: null
set() a : 1 b: data
  print() a : 0 b: null
set() a : 1 b: data
  print() a : 0 b: null
set() a : 1 b: data
  print() a : 0 b: null
set() a : 1 b: data
  print() a : 0 b: null

資源の競合

  • デットロック:ロックが一生解けなくなること
  • ライブロック:デットロックはされてないが、進まない処理を続けてる状態
  • スレッドスタベーション:スレッドが共有オブジェクトの使用を待ち続けている状態

並列コレクション

java.util.concurrent パッケージ:並行処理機能のあるコレクションクラスを提供

Queue インタフェースを拡張したやつら

  • BlockingQueue インタフェースの実装クラス
インタフェース / クラス名 説明
BlockingQueue インタフェース 要素の追加・取得→キューの状態を見て待機する
SynchronousQueue クラス BlockingQueue を実装したキュー
LinkedBlockingQueue クラス リンクノードに基づいたキュー
ArrayBlockingQueue クラス 配列に基づいたキュー
  • BlockingQueue インタフェースのメソッド(4形式)
例外のスロー 特殊な値 ブロック タイムアウト
挿入 add(e) offer(e) put(e) offer(e, time, unit)
削除 remove() poll() take() poll(time, unit)
検査 element() peek() × ×
  • ブロック:操作が完了するまでスレッドをブロック

  • タイムアウト:処理を中止するまで指定された時間ブロック

    • e: 追加する要素
    • time: キューが空or一杯の場合→待機時間
    • unit: timeの単位
  • BlockingDeque インタフェースのメソッド

最初の要素
例外のスロー 特殊な値 ブロック タイムアウト
挿入 addFirst(e) offerFirst(e) putFirst(e) offerFirst(e, time, unit)
削除 removeFirst() pollFirst() takeFirst() pollFirst(time, unit)
検査 getFirst() peekFirst() × ×
最後の要素
例外のスロー 特殊な値 ブロック タイムアウト
挿入 addLast(e) offerLast(e) putLast(e) offerLast(e, time, unit)
削除 removeLast() pollLast() takeLast() pollLast(time, unit)
検査 getLast() peekLast() × ×

Map インタフェースを拡張したやつら

インタフェース / クラス名 説明
ConcurrentMap インタフェース
ConcurrentHashMap クラス ConcurrentMap を実装したクラス
  • ConcurrentMap インタフェースのメソッド
メソッド名 説明
V putIfAbsent(K key, V value) 指定したキーにバリューがない場合、バリューをセット
boolean remove(Object key, Object value) キー&バリュー削除、キーない場合:何もしない
V replace(K key, V value) キー&バリューある場合、バリュー置換
boolean replace(K key, V oldValue, V newValue) キーのバリュー置換
  • ArrayListクラス、Setインタフェースを拡張したやつら
クラス名 説明
CopyOnWriteArrayList 配列のコピーを作成
CopyOnWriteArraySet 内部でCopyOnWriteArrayListを使う
  • 並行処理機能を持ってないコレクションの例
ArrayList.java
arrayList<String> list = new ArrayList<String>(); // → List<String> list = new CopyOnWriteArrayList<String>();
list.add("A"); list.add("B"); list.add("C"); list.add("D");
new Thread( () -> {
    Iterator itr = list.iterator();
    while(itr.hasNext()) {
        System.out.println("ThreadA :" + itr.next());
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) { e.printStackTrace(); }
    }
} ).start();

try {
    Thread.sleep(1000);
} catch (InterruptedException e)  { e.printStackTrace(); }
list.add("E"); System.out.println("main: add()");
list.remove(2); System.out.println("main: remove()");  

//ConcurrentModificationException が発生する → 繰り返し処理中に要素の追加・削除を行なったため

→ CopyOnWriteArrayList を使うと解消する!!

CopyOnWriteArrayListを使った結果.txt
ThreadA : A
main: add()
main: remove()
ThreadA : B
ThreadA : C
ThreadA : D

【 変更が反映されない理由 】
→ CopyOnWriteArrary:イテレーターを生成した時点の状態を参照する → イテレータ生成後のリストへ追加・削除しても反映されない

Executor フレームワーク

スレッドコードを簡単に書ける!

  • Executorオブジェクト(インタフェース)
    • スレッド生成&指定したRunnable タスク(1つの処理)を実行するするオブジェクト
  • Executors クラス(実装クラス)
    • Executorオブジェクトを取得するためのメソッド持つ
スレッド生成.java
Executor executor = //Executor オブジェクト
executor.execute(new RunnableTask());  // = new Thread(new(RunnableTask())).start()
  • ExecutorService インタフェース(Executorのサブインタフェース)
    • スレッドの終了を管理するメソッドあり
    • Executor:スレッドを実行するメソッドだけ

static ExecutorService newSingleThreadExecutor()

一つのスレッドでタスク処理する(Executorsクラスのメソッド)

newSingleThreadExecutor().java
ExecutorService service = null;
try {
    service = Executors.newSingleThreadExecutor();
    System.out.println("service.execute()");
    for (int i = 0; i < 3; i++) {  //1回目のタスク処理(execute())が終わらないと2回目が実行されない → newSingleThreadExecutorだから
        service.execute(() -> {
            System.out.println("thread task");
            for(int a = 0; a < 5; a++) {
                try {
                    Thread.sleep(500);
                    System.out.prinln("*");
                } catch (InterruptedException e) { e.printStackTrace(); }
            }
            System.out.println();
        });
    } finally {
        service.shutdown();  // void shutdown() -> これ以降のタスクは受け付けない(待機中のタスクを実行)
        System.out.println("ex.shutdown()");
    }
}
// 結果
service.execute()
ex.shutdown()
thread task * * * * *
thread task * * * * *
thread task * * * * *
  • ExecutorServiceのメソッド
    • boolean isShutdown():Executorがシャットダウン→ true
    • boolean isTerminated():シャットダウン+タスク全て完了→ true
    • List<Runnable> shutdownNow():実行中のタスク停止→ 待機中タスクのリスト

Future<?> submit(Runnable task) =ExecutorServiceインタフェースのメソッド

Runnableタスク送信 → タスク結果を表すFuture返す

submit()メソッド.java
ExecutorService service = null;
try {
    service = Executors.newSingleThreadEcecutor();
    Future<?> result1 = service.submit(() -> System.out.println("hello"));
    System.out.println(result1.get());  // V get() -> タスク結果を取得(Futureインタフェースのメソッド)
    Future<Boolean> result2 = service.submit(() -> System.out.println("hello"), true); // <T> Future<T> submit(Runnable task, T result) -> 第一引数:Runnableタスク、第二引数:正常にタスクが終了した時に返す値
    System.out.println(result2.get()); 
} catch (InterruptException | ExecutionException e) {
    e.printStackTrace();
} finally {
    if(service != null) service.shutdown();
}

//結果
hello
null  -> 正常にタスクが完了null
hello
true

java.util.concurrent.Callableインタフェース

スレッド開始元に処理結果(オブジェクト)を返す

  • 関数型インタフェース
  • V call() throws Exception : タスク実行・結果を返す
Callableインタフェース.java
ExecutorService service = null;
try {
    service = Executors.newSingleThreadExecutor();
    Future<Date> result = service.submit(() -> new Date());  // V call() throws ... 実装
    System.out.println(result.get()): 
} catch(InterruptedException | ExecutionException e) {
    e.printStackTrace();
} finally {
    if(service != null) service.shutdown();
}

ScheduledExecutorService インタフェース(ExecutorService継承)

タスクのスケジュールができる

ScheduledExecutorService.java
ScheduledExecutorService service = null;
try {
    service = Executors.newSingleThreadScheduledExecutor();
    Runnable task1 = () -> System.out.println("task1");
    Callable<Date> task2 = () -> new Date();

    ScheduledFuture<?> result1 = service.schedule(task1, 3, TimeUnit.SECONDS); //指定された時間後、タスク実行
    ScheduledFuture<Date> result2 = service.schedule(task1, 3, TimeUnit.SECONDS);
    System.out.println(result2.get());
    
} finally {
    if(service != null) service.shutdown();
}

//結果
Fri Feb 21 13:48:29 JST 2020
task1
  • ScheduledFuture> scheduleAtFixedRate(Runnable command, long delay, long period, TimeUnit unit)
  • ScheduledFuture> scheduleWithFixedDelay(Runnable command, long delay, long period, TimeUnit unit)
scheduleWithFixedDelay().java
ScheduledExecutorService service = null;
try {
    service = Executors.newSingleThreadScheduledExecutor();
    Runnable task = () -> System.out.println(new Date());
    scheduleWithFixedDelay(task, 2, 2, TimeUnit.SECONDS)
    Thread.sleep(10000);  //mainスレッドを10秒間スリープして、その間にタスクを定期実行
} finally {
    service.shutdown();
}

//結果
Fri Feb 21 13:48:20 JST 2020
Fri Feb 21 13:48:22 JST 2020
Fri Feb 21 13:48:24 JST 2020
Fri Feb 21 13:48:26 JST 2020

スケジュール.jpg

スレッドプール

複数のスレッドを用意

スレッドプール.java
ExecutorService service = null;
    service = Executors.newCachedThreadPool();  //新規スレッドを作成
    // service = Executors.newFixedThreadPool(2);
    Runnable task = () -> {
        String name = Thread.currentTread().getName();
        System.out.println(name + ": start");
        Thread.sleep(3000);
        System.out.println(name + ": end");
    };
    for(int i = 0; i < 5; i++){
        service.execute(task);
    }
    service.shutdown();

//結果(newCachedThreadPool()) 5つのスレッド生成 → 5回タスク実行
pool-1-thread-1 : start
pool-1-thread-4 : start
pool-1-thread-2 : start
pool-1-thread-5 : start
pool-1-thread-3 : start
pool-1-thread-5 : end
pool-1-thread-1 : end
pool-1-thread-2 : end
pool-1-thread-3 : end
pool-1-thread-4 : end

//結果(newFixedThreadPool(2))  2つのスレッドでタスクを実行
pool-1-thread-1 : start
pool-1-thread-2 : start
pool-1-thread-1 : end
pool-1-thread-2 : end

java.util.concurrent.CyclicBarrier クラス

複数スレッドで処理 → バリアポイント(待機する箇所)設定

バリアポイント.jpg

  • public CyclicBarrier (int num)
    • 引数分のスレッドが待機状態になると、バリアポイントを通過
  • public CyclicBarrier (int num, Runnable bariierAction)
    • バリアポイントを通る時、bariierActionを実行
  • public int await() throws InterruptedException, BrokenBarrierException
    • 指定されたスレッド数がくるまで待機(バリアポイント)
CyclicBarrier.java
public class Main {
    void exec (CyclicBarrier barrier) {
        System.out.println(Thread.currentThread().getName() + "start");
        Thread.sleep((int)(Math.random() * 3000));
        barrier.await();
        System.out.println(Thread.currentThread().getName() + "end");
    }
    public static void main(String[] args) {
        ExecutorService service = null;
        service = Executors.newFixedThreadPool(4);
        CyclicBarrier barrier = new CyclicBarrier(2, () -> System.out.println("task "));
        for (int i = 0; i < 4; i++) {
            service.execute(() -> new Main().exec(barrier));
        }
    }
    if(service != null) service.shutdown();
}
//結果
pool-1-thread-2start
pool-1-thread-3start
pool-1-thread-4start
pool-1-thread-1start
task
pool-1-thread-3end
pool-1-thread-1end
task
pool-1-thread-4end
pool-1-thread-2end

アトミック変数

アトミック:分割不可能な操作のこと

  • java.util.concurrent.atomicパッケージ:アトミック操作を簡単に実装できるクラスを提供
    → syhchronizedなど使ってロック制御しなくてもアトミック操作ができる!
  • java.util.concurrent.atomicパッケージの主なクラス
クラス名 説明
AtomicBoolean boolean型を使ってアトミック操作する
AtomicInteger int型を使ってアトミック操作する
AtomicLong long型を使ってアトミック操作する
AtomicReference 参照型を使ってアトミック操作する
AtomicInteger.java
//わからなすぎて飛ばす

パラレルストリーム

並行処理を行うストリーム

  • パラレルストリームを生成するメソッド
メソッド名 説明
default Stream parallelStream() Collectionインタフェースで提供
コレクションを元に生成
S parallel() BaseStream インタフェースで提供
ストリームを元に生成
boolean isParallel() BaseStream インタフェースで提供
パラレルストリームならtrue
S sequential() BaseStream インタフェースで提供
ストリームを元にシーケンシャルストリーム返す
パラレルストリーム.java
List<String> list = Arrays.asList("aaa", "bb", "c");
Stream<String> stream1 = list.parallelStream();
System.out.println("stream1:" + stream1.isParallel());
Stream<String> stream2 = list.stream();
System.out.println("stream2:" + stream2.isParallel());
Stream<String> stream3 = stream2.parallel();   //シーケンシャルストリーム→パラレルストリーム
System.out.println("stream3:" + stream3.isParallel());

//結果
stream1: true
stream2: false
stream3: true

※ ForkJoinPoolのくだりは分からない

  • パラレル処理をするパイプライン
0
1
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
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?