enum P9
enum
特定の値を列挙するオブジェクトのこと
→ 列挙型の実体 = 定数、メソッドを持つクラス
注意点メモ
・明示的なインスタンス化はできない
・オブジェクト.クラス定数でアクセス
・コンストラクタ、メソッド、変数定義可
・抽象メソッド、インタフェースのオーバーライド、実装可
・定数は書いた順番に管理
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 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
クラスの中に定義したクラス
- 存在を外部から隠したいクラスをネスト
- 外側のクラスのメンバの一つ
- インタフェース、抽象クラスを定義できる
class Outer {
class A {} //非staticクラス(インナークラス)
static class B {} //staticクラス
}
ネストクラスのルール
クラス | ルール |
---|---|
staic / 非static 共通 | ・外側のクラスと同名❌ ・アクセス修飾子使える ・abstruct, final 使える |
staticのみ | ・(非)staticメンバもてる ・外側クラスのインスタンスメンバへアクセス❌ |
非staticのみ | ・staticメンバもてない ・外側クラスのインスタンス / static変数へアクセス⭕️ |
ネストクラスへのアクセス
1. 別のクラスからアクセス
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. 外側のクラスからアクセス
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
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
- コンストラクタ 定義できない
interface MyInter { void method(); }
class Outer {
void method() {
new MyInter() {
public void method() {}
}.method(); //匿名クラスのメソッドの呼び出し
}
}
アノテーション(注釈)
目的
コードを解釈するコンパイラ、実行するJVMに付加情報を伝える
主なアノテーション
アノテーション | 付加情報 |
---|---|
@Override | スーパークラスのメソッドをオーバーライドする |
@Functionallnterface | 関数型インタフェースである |
@Deprecated | 非推奨の要素である |
@SuppressWarnings | コンパイラの警告を無効にする |
@SafeVarargs | 安全でない可変長引数の警告を無効にする |
@Functionallnterface
関数型インタフェースに付与(任意)
関数型インタフェースの要件を満たしてるかチェック
- 要件
@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: 注釈のついたプログラムが非推奨であることを伝える
- since = "" (default)
@SuppressWarnings( 引数 )
引数に指定したコンパイラの警告を無効にする
引数が必須
指定できる主な警告
警告 | 内容 |
---|---|
unchecked | 型を使う時などの警告 |
deprecation | @Deprecatedがつけられた要素の警告 |
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
-
@SuppressWarnings( 引数 ) と @SafeVarargs の違い
-
@SafeVarargs
- 付与した場所の呼び出し元の警告まで無効
-
@SuppressWarnings( 引数 )
- 付与した場所のみ無効
-
@SafeVarargs
カスタムアノテーション 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種類のカスタムアノテーションを定義する必要あり
import java.lang.annotation.*;
@Repeatable(MyAnnotContainer.class) //複数回使うアノテーションに@Repeatable 付与
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnot {
public String value();
}
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()
- エラーメッセージを取得
- void printStackTrace()
catch ブロックの注意点 P93
複数のcatchブロック
} catch (例外クラス 変数名) {
} catch (例外クラス 変数名) {
} //例外クラスに継承関係がある場合、サブクラスから定義する。スーパーから定義するとコンパイルエラー
マルチキャッチ
} catch (NumberFormatException | ArithmeticException e) {
//継承関係のあるクラスを列挙できない
//e は final
}
throws してるメソッド
オーバーライドする時の注意点
- 例外クラス:同じ or サブクラス
- RuntimeException, サブクラス:スーパークラスの例外に関係なくスローできる
- スーパーでthrowsされてても、サブでthrowsしなくてもいい
try-with-resources
暗黙的にリソースを解放してくれる
try-with-resource のとき:tryブロックのみ⭕️
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ブロック");
}
}
}
tryブロック内の実装
close():obj2
close():obj1 //複数のリソースを取得した場合:後に取得したclose()から実行
catchブロック:SQLException
finallyブロック
例外の抑制 P107
参考:https://relearn-java.com/exception/
- try-catch(-finally):複数の例外が発生した場合、伝わる例外が置き換えられちゃう問題
- なぜ置き換えられるか → 例外は一つしか伝播できない!
- try-resource-with:最初に発生した例外を伝え、その他はメソッドで取得できるように抑制
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 =アサーション無効
private void check(int point) {
assert ( point > 0 ) : "point = " + point;
//checkメソッドの処理↓
}
コレクション 過去問外しすぎもう一回やる
ラッパークラス
基本データ型の値 → 参照型として扱う専用のクラス
基本データ型との違い・・・値を操作するメソッドがある
コレクション
複数のオブジェクトをまとめて扱うやつ
インタフェース名 | データ(要素)の管理方法 |
---|---|
List(≒ サイズ変更できる配列) | ・順序づけて管理 ・重複 ⚪︎ |
Set(≒ 袋の中に要素を入れる) | ・順不同で管理 ・重複 × |
Queue | ・FIFO(First In First Out)で管理 |
Map | ・「キー」+「値」で管理 ・キーの重複 × |
イテレータ
コレクションの要素へ順番にアクセスするオブジェクト(≒ 要素を指すカーソル)
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():指してる要素を返す
}
A B C
Queue インタフェース
FIFO(First In First Out)でデータ管理
- 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 |
ジェネリクス
各コレクションのクラス:<⚪︎>に扱う型を指定
- ジェネリクス という機能
- <> =「型パラメータリスト」、扱う型を指定するために使う
ArrayList<String> list = new ArrayList<String>();
ArrayLisy<Object> list2 = new ArrayList<String>(); //型パラメータリストが異なるのでコンパイルエラー
ダイヤモンド演算子
ArrayList<String> list = new ArrayList<>(); //左辺の型パラメータリストから類推できるので、右辺は省略。 <>=ダイヤモンド演算子
ジェネリクスを使ってクラス定義
メリット
- 独自クラスを定義 → クラス生成時に扱う型を決められる!
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);
}
}
ジェネリクスを使ってメソッド定義
型パラメータリストの有効範囲=メソッド内だけ
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>なしでも⚪︎
}
}
ジェネリクスを使ってインタフェース定義
インタフェースはインスタンス化できない
→ 実装するクラス側で型を指定する
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);
}
}
継承を使ったジェネリクス
型パラメータリスト=どんな型でも指定できる!
→ 指定できる型を限定したい時に継承を使う
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のサブクラスでないため、コンパイルエラー
}
}
ワイルドカードを使ったジェネリクス
? を使って型パラメータを指定 → 型は実行時でないとわからない
:
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インタフェースを実装
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インタフェースを実装
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)
- 引数の配列からリストを作成
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の拡張 |
- ラムダ式の書き方
Function<String, String> f1 = (String str) -> { //引数が一つの場合=()省略可、インタフェースの宣言で、引数の型は分かるので省略可
return "Hello " + str; //一行の場合={}省略可、{}省略した場合、return; 省略可
}
String str1 = f1.apply("HY");
Function<String, String> f1 = str -> "Hello " + str
String str1 = f1.apply("HY");
- ラムダ式が外側の変数を使うとき→その変数は final でないといけない
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;
}
}
}
public static void main(String[] args) {
int i = 25;
Supplier<Integer> s = () -> i; //アクセスは問題ない
i++; //i は final なのでコンパイルエラー
System.out.println(s.get());
}
メソッド参照
インタフェース実装時、抽象メソッドの引数(個数・型)= 処理内のメソッドの引数(個数・型)→ メソッド参照で書ける
- staticメソッド参照
- インスタンスメソッド参照
- コンストラクタ参照
staticメソッド参照( 処理内のメソッド:staticメソッド)→ クラス名 :: メソッド名
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);
インスタンスメソッド参照( 処理内のメソッド:インスタンスメソッド)→ インスタンス変数名 :: メソッド名
List<Integer> list = List.of(3, 1, 2);
//ラムダ式
list.forEach( a -> System.out.print(a + " ") ); //Iterableインタフェースから継承したforEach()メソッド=コレクションの各要素に処理をする Comsumer インタフェースのaccept()が実装されている。
//インスタンスメソッド参照
list.forEach(System.out::print + " ") //コンパイルエラー= +演算子などで処理はできない!
//ラムダ式
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メソッド参照みたい...)
//ラムダ式
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"));
//IntegerクラスのhashCode() → static・インスタンスメソッドどちらもある
//ラムダ式
Function<Integer, Integer> obj = i -> i.hashCode(); //hashCode()=インスタンスメソッド
//staticメソッド参照
Function<Integer, Integer> obj = Integer::hashCode(); //コンパイルエラー=どちらか分からない
// → こういう場合、ラムダ式で実装すること!
コンストラクタ参照 クラス名::new
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; }
}
//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) |
//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) |
IntToDoubleFunction f1 = (int i) -> { return i * 3.14 }
IntToDoubleFunction f2 = (Integer i) -> { return i * 3.14 } //コンパイルエラー=引数は自動変換されない
Double ans1 = f1.applyAsDouble(100); // OK=戻り値は自動変換される
関数型インタフェースの合成
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"));
}
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
- 複数の処理をつなぐ仕組み(=パイプライン処理)をもってる
- パイプライン処理には、データソースが必要(処理のもとになるから)
- データソースとなるクラスは、ストリーム生成するメソッド持ってる
<処理の流れ>
- データソース
- ストリームオブジェクト生成
- 処理(パイプラインの途中) → 中間操作
- 処理(最後) → 終端操作
ストリームの種類
インタフェース名 | 説明 |
---|---|
Stream<T> | 汎用的なストリーム |
IntStream | int型に対応したストリーム |
LongStream | Long型に対応したストリーム |
DoubleStream | Double型に対応したストリーム |
終端操作を行うメソッド
//引数=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回だけ!!
long result = Stream.of("a", "b", "c").count(); //要素の数を返す
Stream<String> stream1 = Stream.of("a", "b", "c");
stream1.forEach(System.out::print); //引数で指定されたアクションを各要素に行う、引数=Consumerインタフェース
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
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);
//ストリームオブジェクトを配列に変換
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 というオブジェクト
Optional<Integer> op = Optional.of(10); // Optional.of() -> 引数で指定された値を持つOptionalを返す
System.out.println(op.get()); // T get() -> ある:値、ない:NoSuchElementException
op.isPresent(); // boolean isPresent() -> ある:true、ない:false
戻り値が Optional の終端操作
// Optional<T> max(Comparator com) -> comparatorに従って最大要素を取り出す
List<String> data = Arrays.asList("aaa", "bb", "c");
Optional<String> max = data.stream().max(Comparator.naturalOrder());
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クラスのメソッド)
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(): 重複を除く要素のストリームを返す
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個分の要素を持つストリームを返す
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(): 引数の処理をしたストリームを返す
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要素=多結果
- → 入れ子になっているストリームを平坦化する
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 + " "));
- sorted(): 自然順序に並べ替え
- sorted(Comparator.〇〇()): 明示的に並べ替え順序を指定
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インタフェース(戻り値なし)→ デバック機能として使う
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>
IntStream stream = Stream.of(1, 2, 3);
Stream<Integer> stream = stream.boxed();
ストリームインタフェースの暗黙の型変換をするメソッド
- IntStream、LongStreamインタフェースで提供
インタフェース名 | メソッド |
---|---|
IntStream | LongStream asLongStream() |
↑ | DoubleStream asDoubleStream() |
LongStream | DoubleStream asDoubleStream() |
IntStream streami = Stream.of(2, 3, 5);
DoubleStream streamd = streami.asDoubleStream();
終端操作 ~ collect()メソッド ~ P246
ストリームの要素 → まとめて1つのオブジェクトを取得
collect()メソッド: 引数にCollectorクラスのメソッドを指定
Collectorsクラスのメソッド
- toList()、joining()、summingInt()、averagingInt()
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へ変換
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)
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)
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) → 引数が一つの場合
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) → 引数が二つの場合
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つの場合
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でやる
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)
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(): ストリームの各要素に行いたい処理を指定
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):最大値、最小値を取得
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 |
今の実行環境で参照できるモジュール一覧を表示
% java --list-modules
java.base@11
java.compiler@11
モジュール定義
module foo {
exports xlib; //exports:どのパッケージを公開するか指定
requires java.base; //requires:依存するモジュール指定、java.base:全てのモジュールへ暗黙的に含まれるから、省略可能
}
モジュールのコンパイル
- ライブラリを提供する側
javac -d out/foo src/foo/ylib/XTest.java // javac -d <クラスファイルの生成場所> <コンパイルするソースファイル場所>
javac -d out/foo src/foo/module-info.java
- ライブラリを利用する側
javac -d out/client --module-path out/foo src/client/app/Main.java src/foo/module-info.java // --module-path <依存するモジュール場所(クラスファイル)>
モジュールの実行(クラスファイルで実行)
java --module-path out/foo;out/client --module client/app.Main
// --module <モジュール名+main()含むクラス名>
間接エクスポート
名前付きモジュール、その他のモジュール
- 名前付きモジュール:module-info.class ある
- その他モジュール:module-info.class ない
- 自動モジュール:モジュールパス上にある
- 無名モジュール:クラスパス上にある
- → 全てのパッケージをexports
- → モジュールパス上全てのモジュールをrequires
- → 参照が違う
モジュールの依存性確認
- オプションで確認
--describe-module //モジュールの説明出力
--show-module-resolution //モジュールの解決(モジュールの呼び出し)を出力
- コマンドで確認
jdeps -summary (-s) //依存関係のサマリーだけ出力
jdeps -jdkinternals //JDKのクラス・レベルの依存関係を検索
jdeps -dotoutput //DOTファイルの出力先を指定
jlink コマンド
JREのカスタマイズツール
オプション | 説明 |
---|---|
--add-modules | イメージに追加するモジュールを指定 |
--module-path (-p) | モジュールパスを指定 |
--compress (-c) | リソースの圧縮を有効化 |
--output | 出力ディレクトリ |
ServiceLoader
インタフェースの実装クラスを動的にロードする仕組み
- インタフェース、実装クラス提供側
module foo {
exports xlib;
provides xlib.MyInter with xlib.XTest; //provides <インタフェース> with <実装クラス>
}
- 実装クラスを利用する側
module client {
requires foo;
uses xlib.MyInter; // use <インタフェース>
}
ボトムアップ / トップダウン移行
Javaのアプリケーションをバージョンアップする
→ アプリをモジュール化する必要あり!
↓ 3つのモジュールを使用
ボトムアップ移行
最下位のモジュールから移行
- c.jar モジュール化 → 名前付きモジュールへ
- モジュールパス上へ配置
- 公開すべきパッケージを exports
- b.java → a.jar の順にモジュール化
トップダウン移行
最上位のモジュールから移行
- 全てのモジュールをモジュールパス上へ配置 → 自動モジュールへ
- a.jar からモジュール化 → 名前付きモジュール
- a.jarで必要なモジュールをrequires
並列処理
スレッド
プログラム実行 → Javaの実行環境がスレッド作成(処理の最小単位)→ 処理開始
→ 複数のスレッドに分けて実行= マルチスレッド
スレッド作成&開始
- Thread クラスを継承
- Runnable インタフェースを実装
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()できない)
スレッドの優先度
優先度の高いスレッドから実行される
メソッド名 | 説明 |
---|---|
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を受け取る ③処理を再開 |
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
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<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 を使うと解消する!!
ThreadA : A
main: add()
main: remove()
ThreadA : B
ThreadA : C
ThreadA : D
【 変更が反映されない理由 】
→ CopyOnWriteArrary:イテレーターを生成した時点の状態を参照する → イテレータ生成後のリストへ追加・削除しても反映されない
Executor フレームワーク
スレッドコードを簡単に書ける!
-
Executorオブジェクト(インタフェース)
- スレッド生成&指定したRunnable タスク(1つの処理)を実行するするオブジェクト
-
Executors クラス(実装クラス)
- Executorオブジェクトを取得するためのメソッド持つ
Executor executor = //Executor オブジェクト
executor.execute(new RunnableTask()); // = new Thread(new(RunnableTask())).start()
- ExecutorService インタフェース(Executorのサブインタフェース)
- スレッドの終了を管理するメソッドあり
- Executor:スレッドを実行するメソッドだけ
static ExecutorService newSingleThreadExecutor()
一つのスレッドでタスク処理する(Executorsクラスのメソッド)
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返す
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 : タスク実行・結果を返す
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 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)
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
スレッドプール
複数のスレッドを用意
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 クラス
複数スレッドで処理 → バリアポイント(待機する箇所)設定
- public CyclicBarrier (int num)
- 引数分のスレッドが待機状態になると、バリアポイントを通過
- public CyclicBarrier (int num, Runnable bariierAction)
- バリアポイントを通る時、bariierActionを実行
- public int await() throws InterruptedException, BrokenBarrierException
- 指定されたスレッド数がくるまで待機(バリアポイント)
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 | 参照型を使ってアトミック操作する |
//わからなすぎて飛ばす
パラレルストリーム
並行処理を行うストリーム
- パラレルストリームを生成するメソッド
メソッド名 | 説明 |
---|---|
default Stream parallelStream() | Collectionインタフェースで提供 コレクションを元に生成 |
S parallel() | BaseStream インタフェースで提供 ストリームを元に生成 |
boolean isParallel() | BaseStream インタフェースで提供 パラレルストリームならtrue |
S sequential() | BaseStream インタフェースで提供 ストリームを元にシーケンシャルストリーム返す |
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
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
単体では使えず、ほかのストリームと併用して使うこと
→ コンストラクタに他ストリームを指定する
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
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
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
apple
orange
banana
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 | ディスプレイ出力 |
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 インタフェース・・・このインタフェースを実装するとシリアライズ可能なオブジェクトになる
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
オブジェクトの入出力をするストリームを生成
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インタフェースを実装
- → 子クラスで実装しなくとも、シリアライズ可能!
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;
}
}
- デシアライズする時・・・スーパークラスのコンストラクタが呼び出される
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 console = System.console(); //コンソール上で何かしら入力・Enter
while(true) {
String str = console.readLine(); //ミュウ力した文字列を読みこむ
if (str.equals("")){ break; }
System.out.println("入力された文字:" + str);
}
- パスワード入力を利用した例
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()
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オブジェクトを取得
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オブジェクト取得
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 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() : 指定された分のパスを返す
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
- パス情報をいじるメソッド
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
- パスの結合
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 クラス : ディレクトリ・ファイルを操作するクラス
- ファイル・ディレクトリを調べる
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
- ディレクトリの作成・削除
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 | ファイルが破損しないことを保証 |
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 じゃなくてまとめて読み込む
私の名前は榊丸大師です
29歳です
よろしくお願いいたします
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():メタデータ取得
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():複数のメタデータを取得する
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属性のメタデータ
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()); //システムファイル属性値返す
ディレクトリ操作
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){}
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
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():条件に合致するパスのストリームを返す
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> で返す
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を使ったDB接続の流れ】
- import java.sql
- DB指定
- DB接続
- ステートメント(SQL)取得
- SQL実行
- 結果取得
- 接続のクローズ
【具体的に使用するクラス/インタフェース】
クラス/インタフェーズ | 説明 |
---|---|
java.sql.DriverManager | DB接続 |
java.sql.DataSource | 接続情報を保持 |
java.sql.Connection | DBとの接続を表現 |
java.sql.PreparedStatement | SQL文を表現 |
java.sql.ResultSet | 結果を表現 |
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(); }
}
SQLステートメント
PreparedStatemnt
executeQuery() -- SELECT
可変部分に「?」(INパラメータ)を設定 → set〇〇()で設定可能
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の併用
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
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
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
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
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パラメータの設定
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に格納する
DELIMITER //
CREATE PROCEDURE myprocedure (IN value INT, OUT total INT)
BEGIN
SELECT SUM(field2) INTO total FROM mytable WHERE field2 > value;
END//
DELIMITER ;
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 | 現在の行番号を取得 |
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・コンパイルエラーのパターン
String sql = "SELECT dept_name FROM department";
~~~省略~~~
ResultSet rs = pstmt.executeQuery();
rs.beforeFirst();
System.out.println(rs.getString(1)); //SQLException例外
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だから、引数に指定できない
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 クラス
- ロケール・・・「地域」の情報