0
1

Java Gold 紫本

Last updated at Posted at 2024-06-23

enum P9

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

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

注意点メモ

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

enum.java
enum Card {
    SPADES,
    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();
}

Java.lang.Object P17

❌ equals(), hashCode() overt ride 要件 分からない

インタフェース P19

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

  • 定数(public static final)宣言可
    →宣言時に初期化しないとコンパイルエラー
  • 抽象メソッド宣言=public abstract 自動付与(publicのみ)
  • static / default + 具象メソッド
    → 自動的にpublic、protectd❌
    → static private⭕️ / default private ❌
  • defaultメソッド
    → toString(), equals(), hashCode() 定義❌
    → 実装クラスでオーバーライド可能
  • 親インタフェースへアクセス
    → <親インタフェース名> . super . <メソッド名>
  • メンバへのアクセス
    → クラスが優先される
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 implements MyInter {
    @Override 
    public void method() { X.super.method1(); }
    Main main = new Main();
    main.priority(); //コンパイルエラーにはならない
}
  • private + 具象メソッド⭕️

ネストクラス 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() {
            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のくだりは分からない

  • パラレル処理をするパイプライン

入出力(入出力をするためのクラスたち)

File クラス( java.io.File )

ディスクに保存されてるディレクトリ・ファイル → オブジェクトとして扱うクラス
メソッドを使って、ファイルに対する処理を実行

ストリーム

データのやりとり・入出力(プログラム ↔︎ ファイル)を連続して行う

  • 入力ストリーム・・・ファイルからデータを読み込む
  • 出力ストリーム・・・ファイルへデータを書き出す
    ーーーーーーーーーーーーーーーーーーーーーーーーーーーー
  • バイトストリーム・・・byte単位でデータを読み書き
  • キャラクタストリーム・・・char単位でデータを読み書き

ストリームの種類

  • 以下の抽象クラスを継承して、ストリームを提供する
バイトストリーム キャラクタストリーム
出力ストリーム OutputStream Writer
入力ストリーム InputStream Reader
  • ストリームの実装クラス
ストリーム クラス名 説明
バイトストリーム
FileInputStream ファイルを読み込み
FileOutputStream ファイルへ書き出し
DataInputStream 基本データ型を読み込み
DataOutputStream 基本データ型を書き出し
キャラクタストリーム
FileReader ファイルを読み込み
FileWriter ファイルへ書き込み
BufferedReader まとめてファイルを読み込み
BufferedWriter まとめてファイルへ書き出し

FileInputStream / FileOutputStream

FileInputStream_OutputStream.jpg

FileOutputInputStream.java
FileOutputStream fos = null;
FileInputStream fis = null;
try {
    fos = new FileOutputStream( new File("fileOutputInputStream.txt") );         //引数に指定したファイルにデータを書き出すストリーム生成(ファイルが存在しない場合、新規作成)
    fos.write(0);           //write(int b) data.txtに引数で指定したバイオデータを書き出す
    fos.write("abc".getBytes());        //"abc"の文字列をバイオデータとして書き出し
    fos.write(99);
    fis = new FileInputStream(new File("data.txt"));            //引数に指定したファイルのデータを読み込むストリーム生成(ファイルが存在しない場合、FileNotFoundException)
    int data = 0;
    while((data = fis.read()) != -1) {      //ファイルの読み込みが終わる→ -1返す
        System.out.println(data + " ");     //読み込んだデータの表示
    }
} catch (FileNotFoundException e) {
    System.err.println("ファイルがありません");
} catch (IOException e) {
    System.err.println("IO Error");
} finally {
    try { fos.close(); fis.close(); } catch (IOException e) {}
}

DataOutputStream_DataInputStream

単体では使えず、ほかのストリームと併用して使うこと
→ コンストラクタに他ストリームを指定する
DataInputStream_DataOutputStream.jpg

DataOutputInputStream.java
try (DataOutputStream dos = new DataOutputStream(new FileOutputStream("DataOutputInputStream.txt"));
     DataInputStream dis = new DataInputStream(new FileInputStream("DataOutputInputStream.txt"))){
        dos.writeInt(100);  //数値データ書き出し
        dos.writeUTF("hashimoto");  //文字列データ書き出し
        dos.writeUTF("橋本");
        System.out.println(dis.readInt());
        System.out.println(dis.readUTF());
        System.out.println(dis.readUTF());
} catch (IOException e) {
    e.printStackTrace();
}

FileReader_FileWriter

FileReader_FileWriter.java
try (FileWriter fw = new FileWriter(new File("FileWiterReader.txt"), true);  // 第二引数=true:追記 / false:先頭から書き込み
    FileReader fr = new FileReader(new File("FileWiterReader.txt"))) {
       fw.write("木村");
       fw.flush();  // 送信先にすぐに文字を書き出す 
       int i = 0;
       while((i = fr.read()) != -1){
           System.out.println((char)i);
       }
} catch (IOException e) {
   e.printStackTrace();
}

BufferedReader_BufferedWriter

BufferedReader_BufferedWriter.java
try (BufferedWriter bw = new BufferedWriter(new FileWriter("BufferdWriterReader.txt"));
     BufferedReader br = new BufferedReader(new FileReader("BufferdWriterReader.txt"))) {
        bw.write("おはよう");
        bw.newLine();  //改行文字を書き出す
        bw.write("こんばんわ");
        bw.flush();   //送信先へすぐに文字を書き出す
        String data = null;
        while((data = br.readLine()) != null) {     //readLine(): ファイルの最後に達すると null 返す、改行単位で読み込み
            System.out.println(data);
            System.out.println("-------------");
        }
} catch (IOException e) {
    e.printStackTrace();
}
------------------------------------------------------------
//結果
おはよう
こんばんわ
  • mark(),reset(),skip() を使ったBufferedReader,BufferedWriter
data.txt
apple
orange
banana
mark(),reset(),skip().java
try(BufferedReader br = new BufferedReader(new FileReader("data.txt"));) {
   System.out.println(br.readLine());  //出力【apple】
   br.mark(256);  //マークつけて、読み込める文字数指定(256バイト)
   System.out.println(br.readLine()); //出力【orange】
   System.out.println(br.readLine());  //出力【banana】
   br.reset();  //マーク位置まで読み込みをリセット
   System.out.println(br.readLine());  //出力【orange】
   br.skip(4);  //【bana】スキップ
   System.out.println(br.readLine());  //出力【na】
} catch(IOException e) {e.printStackTrace();}
------------------------------------------------------------
結果
apple
orange
banana
orange
na

Systemクラスの定数

定数 説明
public static final InputStream in キーボード入力
public static final PrintStream out ディスプレイ出力
public static final PrintStream err ディスプレイ出力
System.java
try (BufferedReader br = new BufferedReader(new InputStreamReader(System.in));) {
    String s = br.readLine();
    System.out.println("input : " + s);
} catch ( IOException e) {
    e.printStackTrace();
}

シリアライズ

数値・文字列データではなく、自分で独自に定義したクラスから生成したオブジェクトをそのまま入出力したい時

  • シリアライズ(直列化)・・・オブジェクトを出力ストリームに書き出す
  • デシリアライズ・・・シリアライズしたオブジェクトをメモリに復元すること
    • java.io.Serializable インタフェース・・・このインタフェースを実装するとシリアライズ可能なオブジェクトになる

シリアライズ.jpg

Serializable.java
class Employee implements Serializable {    //シリアライズの対象データ=インスタンス変数のみ、static変数は対象外、「transient」:明示的に対象外

    private int id;
    private String name;

    public Employee() {
        this.id = 100;
        this.name = "sakamoto";
        System.out.println("Employee()");
    }

    public Employee(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public int getId() {
        return this.id;
    }
    public String
     getName() {
        return this.name;
    }
}

ObjectInputStream_ObjectOutputStream

オブジェクトの入出力をするストリームを生成

ObjectInputStream_ObjectOutputStream.java
Employee emp = new Employee(100, "tazawa");
try(ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("Serializable.txt"));
    ObjectInputStream ois = new ObjectInputStream(new FileInputStream("Serializable.txt"));) {
        oos.writeObject(emp);   //オブジェクトをファイルへ書き出し(シリアライズ)
        Employee readEmp = (Employee)ois.readObject();  //ファイルからオブジェクトを読み込み(デシリアライズ)、戻り値=Objet型
        System.out.println("ID: " + readEmp.getId());
        System.out.println("名前: " + readEmp.getName());
} catch(ClassNotFoundException | IOException e) {
    e.printStackTrace();
}
  • スーパークラスでSerializableインタフェースを実装
    • → 子クラスで実装しなくとも、シリアライズ可能!
Serializable.java
class Employee implements Serializable {
    private int id;
    private String name;

    public Employee() {
        this.id = 100;
        this.name = "sakamoto";
        System.out.println("Employee()");
    }

    public Employee(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public int getId() {
        return this.id;
    }
    public String
     getName() {
        return this.name;
    }
}

class SubEmployee extends Employee {
    private int age;

    public SubEmployee() {
        this.age = 30;
        System.out.println("SubEmployee()");
    }

    public int getAge() {
        return this.age;
    }
}
  • デシアライズする時・・・スーパークラスのコンストラクタが呼び出される
継承例2.java
class Employee {
    private int id;
    private String name;

    public Employee() {
        this.id = 100;
        this.name = "sakamoto";
        System.out.println("Employee()");
    }

    public Employee(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public int getId() {
        return this.id;
    }
    public String
     getName() {
        return this.name;
    }
}

class SubEmployee extends Employee implements Serializable {
    private int age;

    public SubEmployee() {
        this.age = 30;
        System.out.println("SubEmployee()");
    }

    public int getAge() {
        return this.age;
    }
}
SubEmployee se = new SubEmployee();
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("SubEmployeeEmployee.txt"));
     ObjectInputStream ois = new ObjectInputStream(new FileInputStream("SubEmployeeEmployee.txt"));) {
        oos.writeObject(se);
        SubEmployee readSe = (SubEmployee)ois.readObject(); //デシリアライズの時=スーパークラスはインスタンス化される
}catch (ClassNotFoundException | IOException e) {
    e.printStackTrace();
}

------------------------------------------------------------
結果
Employee()
SubEmployee()
Employee()

シリアライズの注意点

  • 配列をシリアライズ・シリアライズしたオブジェクトが参照するオブジェクト
    • 要素・参照オブジェクト全てがシリアライズ可能である必要
  • static, transient 変数:シリアライズ対象外
  • スーパークラスがSerializableインタフェース実装
    • サブクラスは暗黙的にシリアライズ可能
  • サブクラスがSerializableインタフェース実装
    • デシリアライズする時はスーパークラスがインスタンス化される

java.io.Console クラス

コンソール上の入力・出力ができる
System.console() → Consoleオブジェクトを生成 

console.java
Console console = System.console();   //コンソール上で何かしら入力・Enter
while(true) {
    String str = console.readLine();  //ミュウ力した文字列を読みこむ
    if (str.equals("")){ break; }
    System.out.println("入力された文字:" + str);
}
  • パスワード入力を利用した例
readPassword().java
Console console = System.console(); 
String name = console.readLine("%s", "name:");
System.out.println("You are " + name);
char[] pw = console.readPassword("%s", "pw:");  //入力したパスワードが画面に表示されない
System.out.println("Your password " + pw); 
for(char c : pw) {
    System.out.print(c);
}

ストリームの書式化 P418

入出力ストリームのフォーマットを行う
java.util.Formatter, java.io.PrintWriter, java.lang.Stringクラス:format()

format().jpg

format().java
String corpName = "SE社";
String name = "tanaka";
int age = 20;

Formatter fm = new Formatter();
fm.format("会社名は %s です。 ¥n", corpName);
fm.format("名前:%2$s :年齢: %1$d ¥n", age, name);
System.out.println(fm);

System.out.format("会社名は %s です。 ¥n", corpName);
System.out.format("名前:%2$s :年齢: %1$d ¥n", age, name);

----------------------------------------------------------------
結果
会社名はSE社です
名前tanaka年齢20

会社名はSE社です
名前tanaka年齢20

ファイル操作するパッケージ

java.nio.file:ファイルへアクセスするクラスをいっぱい用意

  • Path オブジェクト(パス情報含む)を取得するクラス・インタフェース
    • Path インタフェース
    • Paths クラス
    • FileSystems クラス
    • FileSystem クラス
  • Paths.get() でPathオブジェクトを取得
Paths.java
Path path1 = Paths.get("data.txt");  //get() : パスオブジェクト取得
Path path2 = Paths.get("C:¥¥sample¥¥chap9¥¥13¥¥data.txt");
Path path3 = Paths.get("C:", "sample", "chap9", "13", "data.txt");

System.out.println(path1);
System.out.println(path2);
System.out.println(path3)

------------------------------------------
結果
data.txt
C:¥¥sample¥¥chap9¥¥13¥¥data.txt
C:/sample/chap9/13/data.txt
  • FileSystems.getPath() でPathオブジェクト取得
FileSystems.java
FileSystem fs = FileSystems.getDefault();
Path path1 = fs.getPath("data.txt");  //getPath() : パスオブジェクト取得
Path path2 = fs.getPath("C:¥¥sample¥¥chap9¥¥13¥¥data.txt");
Path path3 = fs.getPath("C:", "sample", "chap9", "13", "data.txt");

System.out.println(path1);
System.out.println(path2);
System.out.println(path3);

------------------------------------------
結果
data.txt
C:¥¥sample¥¥chap9¥¥13¥¥data.txt
C:/sample/chap9/13/data.txt
  • Pathオブジェクトの色々な情報を取得
Path.java
Path path = Paths.get("/sample/chap9/13/data.txt");
System.out.format("toString      :%s%n", path.toString());            //パスを文字列に
System.out.format("getFileName      :%s%n", path.getFileName());      //最後の要素(ディレクトリ・ファイル名)を取得
System.out.format("getName(0)      :%s%n", path.getName(0));          //指定した要素名を返す(ルートに一番近い要素:0)
System.out.format("getNameCount      :%d%n", path.getNameCount());    //ルート除く要素数を返す
System.out.format("toRoot      :%s%n", path.getRoot());               //パスのルートを返す
while ((path = path.getParent()) != null) {                           //親ディレクトリのパスを返す
    System.out.format("getParent   : %s%n", path); 
}

-----------------------------------------------------------------
結果
toString      :/sample/chap9/13/data.txt
getFileName      :data.txt
getName(0)      :sample
getNameCount      :4
toRoot      :/
getParent   : /sample/chap9/13
getParent   : /sample/chap9
getParent   : /sample
getParent   : /
  • Path.subPath() : 指定された分のパスを返す
subPath.java
Path path = Paths.get("/sample/chap9/13/data.txt");
System.out.format("1-4 :        %s%n", path.subpath(1, 4));  //第二引数-1
System.out.format("0-2 :        %s%n", path.subpath(0, 2));  //ルートに一番近い=0

-----------------------------------------------------------------
結果
1-4 :        chap9/13/data.txt
0-2 :        sample/chap9
  • パス情報をいじるメソッド
いじる.java
Path path = Paths.get("./data.txt");
System.out.format("toString() :        %s%n", path.toString());      
System.out.format("normalize() :        %s%n", path.normalize());           //冗長部分を削除したパスを返す   
System.out.format("toUri() :        %s%n", path.toUri());                   //URiを返す
System.out.format("isAbsolute() :        %s%n", path.isAbsolute());         //絶対パスの場合、true
System.out.format("toAbsolutePath() :        %s%n", path.toAbsolutePath()); //絶対パスを返す

-----------------------------------------------------------------
結果
toString() :        ./data.txt
normalize() :        data.txt
toUri() :        file:///Users/hachiyayuga/self-study/GoldJava/9/2practice/./data.txt
isAbsolute() :        false
toAbsolutePath() :        /Users/hachiyayuga/self-study/GoldJava/9/2practice/./data.txt
  • パスの結合
resolve().java
Path path1 = Paths.get("../../chap9");
Path path2 = Paths.get("18/X");
System.out.format("resolve : %s%n", path1.resolve(path2));  //引数に絶対パスを指定した場合=結合せず、絶対パスをそのまま返す

-----------------------------------------------------------------
結果
resolve : ../../chap9/18/X

Files クラス

java.nio.file.Files クラス : ディレクトリ・ファイルを操作するクラス

  • ファイル・ディレクトリを調べる
Files.java
Path p1 = Paths.get("data.txt");
Path p2 = Paths.get("sample/chap9/20/data.txt");
System.out.format("exists :    %s%n", Files.exists(p1));
try {
    System.out.format("isSameFile :    %s%n", Files.isSameFile(p1, p2));
} catch(java.io.IOException e){ }
System.out.format("isDirectory :    %s%n", Files.isDirectory(p1));
System.out.format("isRegularFile :    %s%n", Files.isRegularFile(p1));
System.out.format("isReadable :    %s%n", Files.isReadable(p1));
System.out.format("isExecutable :    %s%n", Files.isExecutable(p1));

-----------------------------------------------------------------
結果
exists :    false
isDirectory :    false
isRegularFile :    false
isReadable :    false
isExecutable :    false
  • ディレクトリの作成・削除
createDelete.java
Path p1 = Paths.get("ren");
Files.createDirectory(p1);                  //単一ディレクトリ作成
Files.delete(p1);                           //ディレクトリ削除、物理的にない=NoSuchFileException

Path p2 = Paths.get("/ren/tmp/x/y");
Files.createDirectories(p2);                //ない分のディレクトリも作成
Files.deleteIfExists(Paths.get("ren/9_9"));//物理的にある&空の場合のみ削除、空でないとき=DirectoryNotEmptyException
  • ファイルのコピー・移動
  • 第三引数に指定できる型
メソッド 定数  説明
copy()
REPLACE_EXISTING コピー先にあってもコピー
COPY_ATTRIBUTES ファイル属性もコピー
NOFOLOOW_LINKS コピー元:シンボリックリンク→コピー先もシンボリックリンク
move()
REPLACE_EXISTING コピー先にあってもコピー
ATOMIC_MOVE ファイルが破損しないことを保証
copy(),move().java
Path p1 = Paths.get("data.txt");
Path p2 = Paths.get("data2.txt");
Files.copy(p1, p2, StandardCopyOption.REPLACE_EXISTING); //p1をp2にコピー
Files.move(p1, p2, StandardCopyOption.REPLACE_EXISTING); //p1をp2に移動
  • Bufferd じゃなくてまとめて読み込む
data.txt
私の名前は榊丸大師です
29歳です
よろしくお願いいたします
readAllLines().java
Path path = Paths.get("data.txt");
List<String> lines = Files.readAllLines(path);   //1行ごとに要素を持つListを取得
for(String line : lines){
    System.out.println(line);
}

-----------------------------------------------------------------
結果
私の名前は榊丸大師です
29歳です
よろしくお願いいたします

ファイルのメタデータ(属性情報:サイズ、作成日、最終更新日、アクセス権限...)を調べる

  • java.nio.file.attribute パッケージで提供
  • Files.getAttribute():メタデータ取得
Files.getAttribute.java
Path path = Paths.get("data.txt");
Object obj1 = Files.getAttribute(path, "creationTime");
Object obj2 = Files.getAttribute(path, "lastModifiedTime");
Object obj3 = Files.getAttribute(path, "size");
System.out.format("creationTime  :   %s%n", obj1);
System.out.format("lastModifiedTime  :   %s%n", obj2);
System.out.format("size  :   %s%n", obj3);
  • Files.readAttributes():複数のメタデータを取得する
Files.readAttributes().java
Path path = Paths.get("data.txt");
BasicFileAttributes data = Files.readAttributes(path, BasicFileAttributes.class);  //java.nio.file.attribute.BasicFileAttributes:基本的なメタデータを参照できるオブジェクト
System.out.format("creationTime  :   %s%n", data.creationTime());
System.out.format("lastModifiedTime  :   %s%n", data.lastModifiedTime());
System.out.format("size  :   %s%n", data.size());
  • java.nio.file.attribute.DosFileAttributes:BasicFileAttributes+4属性のメタデータ
DosFileAttributes.java
Path path = Paths.get("data.txt");
DosFileAttributes data = Files.readAttributes(path, DosFileAttributes.class); 
System.out.format("isArchive  :   %s%n", data.isArchive());    //アーカイブ属性値返す
System.out.format("isHidden  :   %s%n", data.isHidden());      //隠し属性値返す
System.out.format("isReadOnly  :   %s%n", data.isReadOnly());  //読み取り専用属性値返す
System.out.format("isSystem  :   %s%n", data.isSystem());  //システムファイル属性値返す

ディレクトリ操作

getRootDirectories(),newDirectoryStream().java
FileSystem fs = FileSystems.getDefault();
Iterable<Path> dirs = fs.getRootDirectories();   //ルートディレクトリ取得
for(Path name: dirs) {
    System.out.println("RootDirectories : " + name);
}
Path path = Paths.get("ren");
try (DirectoryStream<Path> stream = Files.newDirectoryStream(path)) {
    for(Path file : stream){
        System.out.println(file.getFileName());
    }
} catch(IOException e){}
  • Files.walk(), Files.list():ファイルツリー探索
    ファイルツリー.jpg
list().java
Path path = Paths.get("ren");
Files.walk().forEach(System.out::println);  //ファイルツリー要素を持つストリーム返す Stream<Path>
~~~~~~~~~~~~~~~~~~~~~~~~~~~
Files.walk()
     .filter(s -> s.toString().endWith(".jpg"))

-----------------------------------------------------------------
結果
ren
ren/9_XX
ren/9_XX/a.txt
ren/9_XX/b.jpg
ren/9_XX/X
ren/9_XX/X/y.txt
~~~~~~~~~~~~~~~~~~~~~~~~~~~
ren/9_XX/b.jpg
list().java
Path path = Paths.get("ren/9_XX");
Files.list(path).forEach(System.out::println);   //引数で指定したディレクトリのみ探索
-----------------------------------------------------------------
結果
ren/9_XX
ren/9_XX/a.txt
ren/9_XX/b.jpg
  • find():条件に合致するパスのストリームを返す
find().java
Path p = Paths.get("ren");
log date = 1556699880000L;
Files.find(p,               //第一引数=探索するルートPath
           10,              //第二引数=探索する階層の深さ
           (path, attr) ->  //第三引数=BiPredicate、test(pathオブジェクト, BasicFileAttributes)
               path.toString().endWith(".jpg") &&
               attr.creationTime().toMillis() > date)
     .forEach(System.out::println);

  • lines():読み取った行 → Stream<String> で返す
lines().java
Path path = Paths.get("data.txt");
  System.out.println(
    Files.lines(path)
         .filter(s -> s.startsWith("a"))
         .map(word -> word.length())
         .collect(Collectors.toList())
  );

JDBC

Java アプリケーションからDBに接続するための仕様

  • 主なパッケージ
    • java.sql:DBにアクセスするとき使う
    • javax.sql:サーバ側の処理(接続プール、トランザクション)で使う
  • JDBC API
    • DB製品の違いを意識せず(依存しない)Javaプログラムを実装できるインタフェース
  • JDBCドライバ
    • JDBC API→DBを繋ぐため、DB製品向けのドライバが必要

JDBC_概要.jpg

【JDBCを使ったDB接続の流れ】

  1. import java.sql
  2. DB指定
  3. DB接続
  4. ステートメント(SQL)取得
  5. SQL実行
  6. 結果取得
  7. 接続のクローズ

【具体的に使用するクラス/インタフェース】

クラス/インタフェーズ 説明
java.sql.DriverManager DB接続
java.sql.DataSource 接続情報を保持
java.sql.Connection DBとの接続を表現
java.sql.PreparedStatement SQL文を表現
java.sql.ResultSet 結果を表現
jdbc.java
Connection con  = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
String sql = "SELECT name, age FROM staff";

try {
    //②DB指定
    String url = "jdbc:mysql://localhost:3306/sample";  
    //③DB接続
    con = DriverManager .getConnection(url, "root", "hatti0708");
    //④ステートメント取得
    pstmt = con.prepareStatement(sql);
    //⑤SQL実行
    rs = pstmt.executeQuery();
    //⑥結果の取得、一行ずつ処理(=レコードアクセス)
    while (rs.next()) {  //next()呼び出さず、取得処理をすると、SQLException
        System.out.println("name: " + rs.getString(2));  //列へのアクセス: get〇〇(列名/列番号)、〇〇=データ型、列番号は左から1〜
        System.out.println("age: " + rs.getInt(4));  //Object o_age = rs.getObjext("age") ⭕️
    }

} catch (SQLException e) {
    e.printStackTrace();
} finally {
    try {
        //⑦接続クローズ
        if(rs != null) rs.close();
        if(pstmt != null) pstmt.close();
        if(con != null) con.close();
    } catch (SQLException e) { e.printStackTrace(); }
}
  • DBに接続する時のURL構文
    JDBC_URL.jpg
    ※ ポート番号・・・ DB製品のデフォルト設定があるため、省略可能

SQLステートメント

PreparedStatemnt

executeQuery() -- SELECT

可変部分に「?」(INパラメータ)を設定 → set〇〇()で設定可能

PrepatedStatement.java
String sql = "SELECT dept_name FROM department " +
             "WHERE dept_code = ? OR dept_code = ? ";
PreparedStatement pstmt = con.prepareStatement(sql);
pstmt.setInt(1, 1);  //第一引数:一つ目の「?」、第二引数:1設定
pstmt.setInt(2, 3);  //第一引数:二つ目の「?」、第二引数:3設定
ResultSet rs = pstmt.executeQuery();
  • INパラメータ、try-with-resourceの併用
INtrywithresource.java
String sql = "SELECT dept_name FROM department " +
             "WHERE dept_code = ? OR dept_code = ? ";
// Connection、PreparedStatement、ResultSet = AutoClosable 継承済み
try(Connection con = DriverManager.getConnect(url, user, password);
    PreparedStatement pstmt = con.prepareStatement(sql)) {  //ResultSet オブジェクト:PreparedStatementオブジェクトがクローズされると同時にクローズされるので、try()内で記述する必要ない
    pstmt.setInt(1, 1);
    pstmt.setInt(2, 3);
    ResultSet rs = pstmt.executeQuery();    //該当するレコードない=空のResultSetオブジェクトを返す。NULLではない
}catch(SQLException e){e.printStackTrace();}

executeUpdate() -- INSERT

insert.java
String sql = "INSERT INTO department VALUES " +
             "(6, 'Planning', 'Yokohama', '999-9999-9999')";
Connection con = DriverManager.getConnect(url, user, password);
PreparedStatement pstmt = con.prepareStatement(sql);
int number = pstmt.executeUpdate();    //戻り値:更新行数

executeUpdate() -- UPDATE

update.java
String sql = "UPDATE department SET " +
             "dept_address='TOKYO' WHERE dept_code = 1";
Connection con = DriverManager.getConnect(url, user, password);
PreparedStatement pstmt = con.prepareStatement(sql);
int number = pstmt.executeUpdate();    //戻り値:更新行数

executeUpdate() -- DELETE

delete.java
String sql = "DELETE FROM  department " +
             "WHERE dept_code = 1";
Connection con = DriverManager.getConnect(url, user, password);
PreparedStatement pstmt = con.prepareStatement(sql);
int number = pstmt.executeUpdate();    //戻り値:更新行数

execute() -- SELECT、INSERT、UPDATE、DELETE いずれも可能

  • 戻り値: ResultSet(SELECTできた)=true、それ以外(更新できたor結果なし)=false
execute().java
String insertSQL = "INSERT INTO department VALUES " +
                   "(6, 'Planning', 'Yokohama', '999-9999-9999')";
String selectSQL = "SELECT dept_name FROM department " +
                   "WHERE dept_code = 1";
PreparedStatement ins_pstmt = con.prepareStatement(insertSQL);
PreparedStatement sel_pstmt = con.prepareStatement(selectSQL);
dis(ins_pstmt);
dis(sel_pstmt);

public void disp(PreparedStatement pstmt) {
    boolean isResultSet = pstmt.execute();
    if(isResultSet) {
        ResultSet rs = pstmt.getResultSet();   //ResultSetオブジェクト取得
        rs.next();
        System.out.println("検索結果:" + re.getString(1));
    } else {
        int count = pstmt.getUpdateCount();   //更新行数を取得
        System.out.println("更新行数:" + count);
    }
}
  • INパラメータの設定
setNull()、setObject().java
String sql = "INSERT INTO depertment VALUES " +
             "(?, ?, ?, ?)";
object pilot_number = null;
if(args.length == 1) pilot_number = args[0];
Connection con = DriverManager.getConnect(url, user, password);
PreparedStatement pstmt = con.prepareStatement(sql);
pstmt.setInt(1, 8);
pstmt.setString(2, "Support");
pstmt.setString(3, "Miyagi");
if(pilot_number == null) {
    pstmt.setNull(4, java.sql.Types.Null);
} else {
    pstmt.setObject(4, pilot_number, java.sql.Types.VARCHAR);
}
int number = pstmt.executeUpdate();

CallableStatemnt オブジェクト

ストアドプロシージャをJavaプログラムから呼び出すために使う

  • ストアドプロシージャ:SQLで書いた関数(複数の処理を一つにまとめたもの)、DBに格納する
ストアドプロシージャ(DB側).txt
DELIMITER //
CREATE PROCEDURE myprocedure (IN value INT, OUT total INT)
    BEGIN
        SELECT SUM(field2) INTO total FROM mytable WHERE field2 > value;
    END//
DELIMITER ;
ストアドプロシージャを呼び出す(Javaアプリケーション側).java
String sql = "{call myprocedure(?, ?)}";            //ストアドプロシージャ 第一引数:INパラメータ(ストアドプロシージャに渡す値)、第二引数:OUTパラメータ(ストアドプロシージャから受け取る値)
try(Connection con = DriverManager.getConnection();
    CallableStatement cstmt = con.prepareCall(sql);) {    //ストアドプロシージャ呼び出し
        cstmt.setInt(1, 110000);
        cstmt.registerOutParameter(2, java.sql.Types.BIGINT);     //OUTパラメータ設定(第一引数:何個目の?かを指定、第二引数:型を指定)
        cstmt.execute();
        int result = cstmt.getInt(2);       //OUTパラメータを取り出し
        System.out.println(result);
} catch (SQLException e) {e.printStackTrace()}

ResultSet 高度な使い方

高機能なResultSetオブジェクトを使いたい
ステートメント取得時 → ResultSetインタフェースの定数を指定する

  • ResultSetインタフェースの定数
定数 ResultSetの機能
CONCUR_READ_ONLY 更新できないResultSet
CONCUR_UPDATABLE 更新できるResultSet
TYPE_FORWARD_ONLY カーソルが前のみ進むResultSet
TYPE_SCROLL_INSENSITIVE カーソルが前後進む、DBの変更を反映しないResultSet
TYPE_SCROLL_SENSITIVE カーソルが前後進む、DBの変更を反映するResultSet
  • ResultSetインタフェースのカーソル操作メソッド
メソッド 移動
boolean absolute(int row) throws SQLexception 指定した行番号へ移動
boolean relative(int row) throws SQLexception 現在地から±移動
boolean next() throws SQLexception 一行後に移動
boolean previous() throws SQLexception 一行前に移動
boolean first() throws SQLexception 先頭行へ移動
boolean last() throws SQLexception 最終行へ移動
void afterLast() throws SQLexception 最終行の直後に移動
viod beforeFirst() throws SQLexception 先頭行の直前に移動
boolean getRow() throws SQLexception 現在の行番号を取得

ResultSet.jpg

ResultSet.java
String sql = "SELECT dept_cd, dept_name FROM department";
try(Connection con = DriverManager.getConnection(url, name, password);
    PreparedStatement pstmt = con.prepareStatement(
                                                    sql,
                                                    ResultSet.TYPE_SCROLL_INSENSITIVE,
                                                    ResultSet.CONCUR_UPDATABLE, //第三引数に指定できる定数= CONCUR_UPDATABLE /  CONCUR_READ_ONLY
                                                    )) {
    ResultSet rs = pstmt.executeQuery();
    rs.last();
    System.out.println("最後の行番号:" + rs.getRow());
    rs.afterLast();
    while(rs.previous()) {
        System.out.println(rs.getString(1) + rs.getString(2));
    }

}catch(SQLException e) {e.printStackTrace();}

------------------------------------------------------------
結果
最後の行番号7
8 Support
7 Planning



  • false・SQLException・コンパイルエラーのパターン
SQLException.java
String sql = "SELECT dept_name FROM department";
~~~省略~~~
ResultSet rs = pstmt.executeQuery();
rs.beforeFirst();
System.out.println(rs.getString(1));  //SQLException例外
false/コンパイルエラ.java
String sql = "SELECT dept_name FROM department " +
             "WHERE dept_code = 99";    //dept_code = 99 というレコードはない前提
ResultSet rs = pstmt.executeQuery(sql);
System.out.println(rs.first());     //false
System.out.println(rs.last());      //false
System.out.println(rs.beforeFirst());    //コンパイルエラー = beforeFirst(), afterLast() の戻り値はvoidだから、引数に指定できない
  • absolute()の引数に指定する数字の意味
    ResultSet_absolute.jpg

ResultSet上でデータ更新

 メソッド名 説明
void updateString(int columnIndex, String s) 第一引数で指定した列→String値に更新
void updateInt(int columnIndex, int i) 第一引数で指定した列→int値に更新
void updateRow() throws SQLException 変更をDBに反映
void moveToInsertRow() throws SQLException カーソルをインサート行に移動
void insertRow() throws SQLException 挿入行をDBに挿入
void deleteRow() throws SQLException DBから現在の行を削除

ロケール

ロケールオブジェクト

プログラムを実行する地域によって表記を変えたい時に使う

  • java.util.Local クラス
  • ロケール・・・「地域」の情報
ロケールオブジェクト取得.java

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