20200823のJavaに関する記事は6件です。

ギャラリーから画像を選択し表示するAndoroidアプリ

この記事は、株式会社富士通システムズウェブテクノロジーが企画する「いのべこ夏休みアドベントカレンダー 2020」の22日目の記事です。
(お約束)本記事の掲載内容は私自身の見解であり、所属する組織を代表するものではありません。

はじめに

Andoroid Studioで画像を選択し画面に表示するソースを書いたので、本記事ではそのソース、手順などを紹介します。

背景

前回の記事(Google Cloud Vision APIを使って食べ物の写真を判定してみる)を踏まえ、選択した写真をAPIで判定するアプリを作ってみようと思い、1段階目として、ギャラリーから画像を選択する部分を記事にしました。

事前準備

下記に関しては今回のアドベントカレンダー16日目の記事「Androidアプリ「HelloWorld」を作って、好き勝手に弄ってみた」で記載されているので参照していただければと思います。ここでは省略します。

・Andoroid Studioのインストール
・プロジェクトの新規作成
・エミュレーターの作成と起動

エミュレーターの日本語化

おそらくデフォルトで英語になっているので、日本語化します。
エミュレーター上で[Settings]画面を表示し、[System]-[Languages & input]-[Languages]の順に選択します。
Englishだけ表示されていると思うので、[Language preferences]から日本語を選択すると、Englishの下に日本語が表示されるので、ドラッグで順番を入れ替えることで日本語化完了です。

エミュレーター_日本語化.PNG

エミュレーター上に画像を保存する

画像選択をするために、画像をエミュレーターで確認できるようにします。

方法としては2つ
・エミュレーターに画像をドラッグ&ドロップ
  DownLoadのフォルダに画像が入ります。
・Device File Explorerでアップロード
  [View]-[Tool Windows]-[Device File Explorer]で開きます。
  任意のフォルダで右クリックでファイル追加可能です。

ソース

ボタン押下でギャラリーを開き、選択した画像を画面に表示させます。

ギャラリーを開く

Intent intent = new Intent(Intent.ACTION_PICK, Media.EXTERNAL_CONTENT_URI);
startActivityForResult(intent, 1);

Intent(アクションの種類, 対象のアプリ)
startActivityForResult(インテント, リクエストコード)
Intentクラスで別のアプリでアクティビティを開始できます。
今回は、ギャラリー(画像一覧)を開いて選択した画像を取得しています。

Intentに関する詳細は一般的なインテントを参照。

引数に指定している内容はざっくりと以下のような意味です。
・Intent.ACTION_PICK      :データから選択したものを返すアクション
・Media.EXTERNAL_CONTENT_URI :画像データが取得できるURI
 ※「EXTERNAL_CONTENT_URI」はSDカード等の外部ストレージを参照します。内部ストレージの場合は「INTERNAL_CONTENT_URI」にすればよいみたいです。

startActivityForResultのパラメータで指定するリクエストコードは遷移元を判断するためのものです。
ここでは、1をハードコーディングで指定していますが、定数等で管理するのがいい気がします。

選択結果を表示する

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
      super.onActivityResult(requestCode, resultCode, data);

      if (requestCode == 1 && resultCode == RESULT_OK && null != data) {
            ImageView imgView = findViewById(R.id.imageView);
            BufferedInputStream inputStream = null;
            try {
                inputStream = new BufferedInputStream
                             (getContentResolver().openInputStream(data.getData()));
                imgView.setImageBitmap(BitmapFactory.decodeStream(inputStream));
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
      }
}

「onActivityResult」メソッドは前段の「startActivityForResult」の実行結果を受け取ります。
選択した結果を画面のImageViewに設定します。

また、引数のrequestCodeは「startActivityForResult」メソッドで指定したリクエストコードが返ってきます。
前段で述べた通り、遷移元を判断できるので下記のような書き方ができます。

public void button1_onClick(View view) {
      Intent intent = new Intent(Intent.ACTION_PICK, Media.INTERNAL_CONTENT_URI);
      startActivityForResult(intent, 1);
}

public void button2_onClick(View view) {
      Intent intent = new Intent(Intent.ACTION_PICK, Media.INTERNAL_CONTENT_URI);
      startActivityForResult(intent, 2);
}

@Override 
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
      super.onActivityResult(requestCode, resultCode, data);

      if (requestCode == 1) {
            // button1用の処理
      } else if (requestCode == 2) {
            // button2用の処理
      }
}

画面

上記までのソースをエミュレーターで実行すると、当初目的の「画像を選択し画面に表示する」ができました。

①ボタンを押下する
②③画像を選択
④選択した画像を表示

image.pngimage.png
image.pngimage.png

最後に

Andoroid Studioを初めて使いましたが、Form系アプリとソースが似ていたので、大きく苦労はしませんでした。

次回はAndoroidアプリからCloud Vision APIを呼び出すところをやってみたいです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Java Programmer Gold SE 8 資格まとめ(Java に慣れてる人向け)

記事の目的

Java Programmer Gold SE 8 試験に出題される内容をまとめました。
「昔から Java をやっているけど SE8 のことは微妙に知らない」というニッチな層向けです。

この記事を書いたきっかけ

先日 Java SE 8 Programmer II (1Z0-809) を受験して合格し、Oracle Certified Java Programmer, Gold SE 8 をゲットしました。
Java2 の時に一度取得しましたが、今の世間で必要とされている知識レベルとは明らかに乖離していたので、自分の知識をアップデートする意味でも勉強し直した次第です。Android 開発でも SE8 の知識は必須となりつつありますしね。
受験にあたって試験範囲を自分なりにまとめたものがあるのですが、試験終了とともにそのままお蔵入りさせるのも忍びないので、記事というかたちで残しておこうと思いました。どなたかのためになれば、大変嬉しく思います。

要点まとめ

Java の基本

数値リテラルの接頭辞

数値の先頭に付加される接頭辞によって何進数の数値かが決定する

  • "0b"…2進数
  • "0"…8進数
  • なし…10進数
  • "0x"…16進数

final変数を初期化するタイミング

final 修飾子を付与したインスタンス変数は、コンストラクタで初期化されていなくてはならない

イニシャライザやコンストラクタによる初期化の順番

オブジェクトの初期化に関わる処理は以下の順番で実行される

  1. static イニシャライザ
    クラスのロード時
  2. インスタンスイニシャライザ
    インスタンス生成時、コンストラクタ実行前
    変数を宣言しただけでは実行されない。インスタンス化が必要
  3. コンストラクタ

列挙値の継承とインターフェイスの実装

  • 列挙型のメソッドである name()toString() は自身の文字列表現を返す
  • name() は final なのでオーバーライド不可。toString() は可
  • 列挙型はインターフェイスの実装と、抽象メソッドの定義ができる。これらをした場合、各列挙値でオーバーライドする必要がある
  • 文字列から列挙値を得る場合、valueOf() メソッドが利用できる

列挙値についての注意点

  • 列挙値にはコンストラクタを定義できるが、new によるインスタンス化は許可されていないので、コンストラクタに public や protected をつけるとコンパイルエラーになる
    何もつけなければコンパイルは通るが、new することはできない(コンパイルエラーになる)
  • final 宣言のないフィールドを定義すると、そのフィールドの値は任意のタイミングで変更することができる
enum Direction {
    North(0), South(1);

    // final 宣言しなければ、利用する側でこの値を変更できる
    int index;
    private Direction(int index) { this.index = index; }
}

// Direction の index フィールドを変更してみた例
Direction d = Direction.North;
System.out.println(d.index);    // 0
d.index = 10;
System.out.println(d.index);    // 10

hashCode() と equals()

  • 複数のオブジェクトを同値とする条件は下記2点
    • hashCode() の戻り値(ハッシュ値)が同じ
    • equals() が true
  • コレクションでの重複チェックは以下の通り。両者が成立したら同値とみなされる
    1. ハッシュ値が同じかチェック
    2. ハッシュ値が同じものだけを対象に equals() で比較
  • hashCode() を実装する場合、同値のオブジェクトからは値が返るよう実装する
  • equals() の引数は Object。レシーバと同じ型にするとオーバーライドは成立せず、オーバーロードが成立してしまう。コンパイルエラーにはならない

インターフェイスのメソッド

  • インターフェイスに定義するインスタンスメソッドは public な抽象メソッドであればよい。abstract などの記述があってもコンパイルは通る
  • 複数のインターフェイスに同名の default メソッドが定義されている状況で、両方を継承または実装すると default メソッドが競合してコンパイルエラーになる
    ただし、継承または実装する側で default メソッドをオーバーライドするとエラーは解消する
interface Sample {
    // "public abstract"が有っても無くてもコンパイルは通る
    public abstract void execute();
}

interface A { default void execute() { System.out.println("A"); } }
interface B { default void execute() { System.out.println("B"); } }
interface C extends A, B {
    // このコメントを外すとコンパイルエラーが解消する
    // default void execute() { System.out.println("C"); }
}

オーバーライドが成立するための条件

  • メソッドをオーバーライドする際、戻り値はスーパークラスの型と同じかそのサブクラスでなければいけない
  • メソッドのオーバーライドは、以下が一致しなければ成立しない。順番が一致しなくてもオーバーロードとなる
    1. メソッド名
    2. 引数の型
    3. 引数の順番
  • static メソッドや static フィールドもサブクラスで再定義できる
    ただしオーバーライドではなく「隠蔽」なので、super キーワードを使って親の static メソッドを呼び出すことはできない

メソッドローカルのクラス

  • メソッドローカルなクラスからメソッド内の変数を直接参照できるが、変数は実質的に final でないとダメ。final 宣言が無くても、メソッドローカルクラスから参照すると自動的に final 扱いになる。これはラムダも同様
  • コンストラクタでメソッドの変数をメソッドローカルなクラスに与える場合は、元の変数を直接参照しないので final にしなくてもよい
  • メソッドローカルなクラスからメソッドの外側のクラスのメンバ変数を参照する場合も、そのメンバ変数を変更して構わない

ラムダ式

  1. 引数のデータ型は省略可
  2. 引数が1つなら『()』が省略可
  3. 右辺の式が1つなら、右辺からの戻り値があっても無くても『{}』が省略可
  4. 右辺の式が1つで、かつ戻り値を返すなら return が省略可

関数型インターフェイス

Function 関連

インターフェイス メソッド 戻り値の型
Function<T, R> apply(T t) R
UnaryOperator<T> apply(T t) T
BinaryOperator<T> apply(T t1, T t2) T
BiFunction<T, U, R> apply(T t, U u) R

Consumer 関連

インターフェイス メソッド 戻り値の型
Consumer<T> accept(T t) void
BiConsumer<T, U> accept(T t, U u) void

Predicate 関連

インターフェイス メソッド 戻り値の型
Predicate<T> test(T t) boolean
BiPredicate<T, U> test(T t, U u) boolean

Supplier

インターフェイス メソッド 戻り値の型
Supplier<T> get() T

プリミティブ型を受け取る関数型インターフェイス

  • int、long、double の3つにはそれぞれ専用の関数型インターフェイスが存在する
    boolean にも BooleanSupplier だけ は存在する
  • SupplierUnaryOperatorBinaryOperator のメソッド名は下記に従う
    ルール:【メソッド名】As【データ型】
    例:applyAsIntgetAsBoolean など
データ型 Function Consumer Predicate Supplier UnaryOperator BinaryOperator
int IntFunction<R> IntConsumer IntPredicate IntSupplier IntUnaryOperator IntBinaryOperator
long LongFunction<R> LongConsumer LongPredicate LongSupplier LongUnaryOperator LongBinaryOperator
double DoubleFunction<R> DoubleConsumer DoublePredicate DoubleSupplier DoubleUnaryOperator DoubleBinaryOperator
boolean - - - BooleanSupplier - -

プリミティブ型を返す関数型インターフェイス

インターフェイス名 メソッド 説明
ToIntFunction<T> applyAsInt(T t) T型の値から int を返す
ToIntBiFunction<T, U> applyAsInt(T t, U u) T型とU型の値から int を返す
ToLongFunction<T> applyAsLong(T t) T型の値から long を返す
ToLongBiFunction<T, U> applyAsLong(T t, U u) T型とU型の値から long を返す
ToDoubleFunction<T> applyAsDouble(T t) T型の値から double を返す
ToDoubleBiFunction<T, U> applyAsDouble(T t, U u) T型とU型の値から double を返す

メソッド参照

  • 関数型インターフェイスの中だけで使える
  • 引数を取る場合は static メソッド参照と同じ書き方にする

コレクション

Arrays#asList

  • Arrays#asList で生成されるリストは、引数に与えられた配列を参照しているだけ。元の配列に加えた変更もリストに反映される
  • Arrays#asList で生成されるリストは ArrayList<E>互換性がない。 キャストしても失敗する

コレクションクラスの性質と見分け方

  • List は原則インデックスの順番
  • SetMap はクラス名のプレフィクスによって性質を見分ける
    • Linked で始まるクラスは、挿入した順序が保たれる
    • Tree で始まるクラスは自然順などによってソートされる。自然順で並べると 数字→英字(大)→英字(小) となる

Queue インターフェイスの性質

  • 挿入/削除/検査のメソッドが2種類あり、メソッドの実行に失敗した時の挙動が違う
    • 例外が発生
      • 挿入 … add(E)
      • 削除 … remove()
      • 検査 … get()
    • null を返却
      • 挿入 … offer(E)
      • 削除 … poll()
      • 検査 … peek()
  • インデックスで要素にアクセスできない

Deque インターフェイスの性質

  • Queue との違いは、 Double Ended Queue の名前通り、先頭または末尾に要素を挿入または削除できること
    • 例外が発生
      • 挿入 … addFirst(E), push(E) / addLast(E)
      • 削除 … removeFirst() / removeLast()
      • 検査 … getFirst(), pop() / getLast()
    • null を返却
      • 挿入 … offerFirst(E) / offsetLast(E)
      • 削除 … pollFirst() / pollLast()
      • 検査 … peekFirst() / peekLast()
  • Queue と同じで、インデックスでは要素にアクセスできない
  • java.util.concurrent パッケージにある Queue インターフェイスの拡張にはタイムアウトを設定できるメソッドもある
    常に待ち時間が発生する訳ではなく、挿入や削除ができないとき待機が発生する
  • Deque の実装である ArrayDeque には null を入れることができない

ジェネリクス

ジェネリクスと型パラメータ

  • クラスに対して型パラメータを宣言する場合、以下の用途で型パラメータは使用できない
    • static メンバ
    • new 演算子によるインスタンスや配列の生成
    • instanceof による型の検証
    • .class の参照
  • 型パラメータが適用されたメソッドを使うときは型を明示してもしなくてもよい
  • 総称型の構文は以下の通り
    class クラス名<型パラメータ(, 型パラメータn)> {...}
  • クラス宣言で型パラメータを使う場合、extends キーワードは使えるが super キーワードは使えない
  • メソッドの構文は以下の通り
    宣言: <型パラメータ> 戻り値型 メソッド名(型 引数名) {...}
    使用: レシーバ.<型パラメータ>メソッド名(型 引数名) {...}

ワイルドカード

  • ワイルドカードは "*" ではなく "?""*" はKotlin)
  • ワイルドカードを class 宣言の型パラメータに使うことはできない
  • ワイルドカードの型は実行されるまで確定しないので、ワイルドカードを使って中のデータ型を指定したリストに追加するとコンパイルエラーになる
    例外として <? super XXXXX> で宣言されたリストに XXXXX と同じ型のオブジェクトを入れることはできる
  • ワイルドカードを使った場合、宣言されている内容から判断できる範囲で問題がなければコンパイルエラーにはならない。型に互換性がないことが明確になったら、コンパイルエラーになる

型パラメータにワイルドカードを使った場合にコンパイルエラーが起きるところ

  • 型パラメータにワイルドカードを使用すると、 実際にオブジェクトを追加するところ でコンパイルエラーになる

Comparable と Comparator

Comparable インターフェイス

  • java.lang パッケージに所属
  • 比較される対象になるためのインターフェイス
  • Comparable で実装すべきメソッドは compareTo(T o) のみ。比較相手より小さい場合は負の値を返し、等しい場合はゼロを、大きい場合は正の値を返す
  • クラス宣言時に Comparable<T> の型パラメータに与えるのは compareTo が引数に取るオブジェクトの型

Comparator インターフェイス

  • java.util パッケージに所属
  • 比較するためのインターフェイス。なお比較対象は Comparable インターフェイスを実装しているものに限らない
  • Comparator で実装すべきメソッドは以下の2つ(equals は任意)
    • compare(T t1, T t2)
    • equals(T o)
  • compare(T t1, T t2) では、t1がt2より大きい場合は正の値、t1とt2が等しい場合はゼロ、t1がt2より小さい場合は負の値を返す

Stream API

要素数が有限のストリームを生成

  • Collection#stream() -> Stream<E>
  • Arrays#stream(E[] array) -> Stream<E>
  • Arrays#stream(int[] array) -> IntStream
  • Arrays#stream(long[] array) -> LongStream
  • Arrays#stream(double[] array) -> DoubleStream
  • Stream#of(E… values) -> Stream<E>
  • Stream#empty() -> Stream<E>
  • IntStream#of(int… values) -> IntStream
  • LongStream#of(long… values) -> LongStream
  • DoubleStream#of(double… values) -> DoubleStream
  • IntStream#range()IntStream#rangeClosed() -> IntStream
  • LongStream#range()LongStream#rangeClosed() -> LongStream
  • BufferedReader#lines() -> Stream<String>
  • java.nio.file.Files#lines() -> Stream<String>

要素数が無限のストリームを生成

どちらも java.util.stream.Stream のメソッド

  • generate(Supplier<T> supplier)
    supplier から提供される要素を無限に保持する Stream を生成する
  • iterate(T seed, UnaryOperator<T> f)
    seed が最初の要素で、それ以降は前の要素に f をかけ合わせた要素を無限に保持する Stream を生成する

ストリームの終端操作

  • 同じストリームへの終端操作は1度だけ。複数回行うと IllegalStateException が発生する
  • 中間操作を呼び出しただけでは何も実行されず、終端操作を呼び出さない限り何も行われない。終端操作を呼び出すと全ての処理が実行される
  • パイプライン処理では、ストリーム内の要素が1つずつ順番に全ての中間/終端操作を実行されてから次の要素が処理される
    つまり 「要素1の中間操作×m→要素1の終端操作」⇒「要素2の中間操作×m→要素2の終端操作」⇒…⇒「要素nの中間操作×m→要素nの終端操作」 という順番

Stream#reduce の戻り値

Stream#reduceStream の中身を集約するためのメソッド。第1引数に初期値を設定するほうのメソッドでは、戻り値が Optional じゃない

  • reduce(BinaryOperator op) … 戻り値は Optional<T>
  • reduce(T initial, BinaryOperator op) … 戻り値は T

戻り値が Optional の終端操作

  • 戻り値が Optional になるのは以下のメソッド
    • findAny() … 実行するたびに結果が変わる可能性がある
    • findFirst() … 何度実行しても同じ結果が得られる
    • min()
    • max()
    • reduce(BinaryOperator op)
  • Stream の型によって Optional の型が違う。例えば findAny() なら以下の通り
    • Stream#findAny()Optional<T>
    • IntStream#findAny()OptionalInt
    • LongStream#findAny()OptionalLong
    • DoubleStream#findAny()OptionalDouble

Optional のインスタンスを生成する

  • Optional#of(T value) … valueにnullは入れられない
  • Optional#ofNullable(T value) … valueにnullを入れられる
  • OptionalInt#of(int value)
  • OptionalLong#of(long value)
  • OptionalDouble#of(double value)

Optional#ifPresent と Optional#isPresent

  • 値があるかどうか(null or not null)を判定するためのメソッドは以下の2つ
    1. isPresent() … 値の有無を判定する
    2. ifPresent(Consumer) … 値があれば Consumer を実行する

Optional から値を取り出すためのメソッドは複数存在する

Optional<T> から値を取得するメソッドは以下。値が存在する場合はその値が得られるが、存在しない場合の挙動が異なる

  • get()
    存在しない場合は NoSuchElementException
  • orElse(T)
    存在しない場合は引数を返す
  • orElseGet(Supplier<T>)
    存在しない場合は Supplier から返される値を返す
  • orElseThrow(Supplier<X extends Exception>)
    存在しない場合は Supplier から返される例外をスローする

特定のデータ型に特化した Optional の種類

Optional<T> の関連クラスと値を取り出すためのメソッドは以下

  • Optional<T>#get()
  • OptionalInt#getAsInt()
  • OptionalLong#getAsLong()
  • OptionalDouble#getAsDouble()

Stream と XXXStream の型変換

  • 同じ Stream 型同士の変換は map()
  • Stream<T> 型への変換は mapToObj()
  • IntStream 型への変換は mapToInt()
  • LongStream 型への変換は mapToLong()
  • DoubleStream 型への変換は mapToDouble()

Stream#collect() メソッドと Collector クラス

  • collect()Stream 内の要素を集約して、1つのオブジェクトを取得するメソッド
  • Collectors クラスは用途に合う Collector インターフェイスの実装を提供する
    その性質上、Stream クラスの一部メソッドと役割がかぶる

例外

Error、Exception、 RuntimeException

  • 全て Throwable のサブクラスで、java.lang パッケージに所属
  • ErrorExceptionThrowable のサブクラス
  • RuntimeExceptionException のサブクラス(Throwable の孫クラス)
  • try〜catch が必須となる checked 例外は RuntimeException 以外の Exception のサブクラスだけ

例外を catch する順番

  • 例外はサブクラスから順番に catch すること
  • スーパークラスをサブクラスより先に catch するとコンパイルエラーになる
    ※実行時エラーではない

例外のマルチキャッチ

  • 継承関係のある複数の例外をまとめてキャッチするとコンパイルエラー
  • マルチキャッチしたときの例外オブジェクトは暗黙的に final 扱いになり再代入は不可
    マルチキャッチでない場合は、final 扱いにならない

例外の rethrow

  • 1つのメソッドが複数種類の例外オブジェクトを catch して rethrow する場合、throws および catch メソッドで取る例外は一致しなくてもよい
  • catch で起こりうる全ての例外の親クラスを拾って、そのまま throw する場合など
// SE7以降では、2つの例外をまとめて catch してもよい
// executeからthrowされるのも、Exceptionではなく IllegalAccessExceptionとArighmeticException
private void execute(int number) throws IllegalAccessException, ArithmeticException {
    try {
        switch (number) {
            case 1:
                throw new IllegalAccessException();
            case 2:
                throw new ArithmeticException();
            default:
                System.out.println("No exception");
        }
    } catch (Exception e) {
        throw e;
    }
}

例外をthrowするメソッドのオーバーライド

  • throws 宣言つきのメソッドをサブクラスでオーバーライドする場合、オーバーライドしたメソッドに throws 宣言をするかどうかは自由
  • オーバーライドしたメソッドでも throws を宣言する場合の例外は、元のメソッドと同じかそのサブクラスでなければならない
  • RuntimeException とそのサブクラスは無条件で throws に指定できる。ただし非チェック例外なので、呼び出し元で catch しなくてもコンパイルは通る

try〜with〜resources 文

  • try〜with〜resources 文でのみ try 単体での使用が可能
  • 使用するリソースは java.lang.AutoCloseable か、そのサブインターフェイスである java.io.Closeable を実装する必要がある
  • AutoCloseableCloseable はどちらも close()throws 宣言があるため、リソースに使う場合も try の中で使う場合も、例外を catch しないとコンパイルエラー
    • AutoCloseable#close() throws Exception
    • Closeable#close() throws IOException
  • AutoCloseable もしくは Closeableclose() で例外が発生したら、catch で補足される
  • try ブロックとリソースのクローズの両方で例外が発生したら、close() で起きた例外は抑制された例外として catch された例外オブジェクトに Throwable の配列で格納され、getSuppressed() で取得が可能
  • try〜with〜resources 文で例外が起きたときの実行順序は以下
    1. try ブロック
    2. 宣言した逆順にリソースをクローズ(下記のコードだと、Nから1へ順番に閉じる)
    3. catch ブロック
    4. finally ブロック
// (1) -> ResourceNから1の順番にclose() -> (2) -> (3) の順に実行
try (Resource1; Resource2; ...; ResourceN) {
    (1)
} catch (Exception e) {
    (2)

    // getSuppressed() で撮れるのは Throwable の配列
    for (Throwable th : e.getSuppressed()) { ... }
} finally {
    (3)
}

アサーションとその利用方法

  • アサーションは、assert 文で false が検出されたら AssertionError を投げる機能で、文法は以下の通り assert boolean式: エラーメッセージ;
  • boolean 式は括弧で括っても括らなくてもOK
  • エラーメッセージは省略可
  • アサーションはデフォルトでは無効。java コマンドで "-ea" オプション("enable assertion" の略?)を明示的に指定しないと、AssertError はスローされない
  • 明示的に無効にするには、java コマンドで "-da" オプション("disable assertion" の略?)を指定する
  • アサーションの対象だと明示したいなら java -ea:【クラス名】 として、コロン区切りでアサーションの対象(-daなら非対象)を指定する

Date and Time API

クラス一覧

  1. LocalDate … 日付
  2. LocalTime … 時刻
  3. LocalDateTime … 日付+時刻
  4. OffsetTime … UTC/GMTからの差分を「+hh:mm」で含む時刻
  5. OffsetDateTime … UTC/GMTからの差分を「+hh:mm」で含む日付&時刻
  6. ZonedDateTime … UTC/GMTからの差分をタイムゾーンIDで含む日付+時刻
  • 全て Temporal インターフェイスを実装する。更にそのスーパーインターフェイスは TemporalAccessor
  • "Local〜" と "Offset〜" と "Zoned〜" の違いは、同じ日時を表現する方式の違い
  • LocalDateTime#toLocalDate()LocalDateTime#toLocalTime() でそれぞれへの変換が可能
  • 月を取得できるメソッドは2つ
    • getMonth()Month 列挙値で取得
    • getMonthValue() … int 値で取得

列挙型一覧

  1. Month:月を表す
    JANUARY, FEBRUARY, MARCH, …, DECEMBER
  2. DayOfWeek:曜日を表す
    SUNDAY, MONDAY, TUESDAY, …, SATURDAY

LocalDate / LocalTime / LocalDateTime のインスタンス化

  1. now()
  2. of(year, month, dayOfMonth, ...)
    それぞれのクラスが扱える値を引数にしてインスタンスを生成する
    引数 month には整数でなく Month 列挙値をセットしてもOK
    LocalDateTime のみ LocalDateLocalTime を引数に取るオーバーロードがある。年、月、日、時、分、秒、ミリ秒を引数に取るのもある
  3. parse(String)parse(String, DateTimeFormatter)
    文字列からインスタンスを生成
    DateTimeFormatter を併用すると任意のフォーマットの文字列でも可

LocalDate#of および LocalTime#of のオーバーロード

  • LocalDate#of には2つのオーバーロードメソッドがある。どちらも年月日は指定するが、それ以外の違いは以下の通り
    1. 第2引数が int
    2. 第2引数が Month 列挙値
  • LocalTime#of には3つののオーバーロードメソッドがあり、全て時分は指定するが、それ以外の違いは以下の通り
    1. 時分のみ
    2. 時分に加えて秒を指定
    3. 時分に加えて秒とナノ秒を指定

java.time.format.DateTimeFormatter

  1. ofPattern(String)
    フォーマットパターンを指定
  2. ofLocalizedDate(FormatStyle)
    日付のフォーマット方式を指定
  3. ofLocalizedTime(FormatStyle)
    時間のフォーマット方式を指定
  4. ofLocalizedDateTime(FormatStyle)
    日時のフォーマット方式を指定
  5. ofLocalizedDateTime(FormatStyle, FormatStyle)
    日時のフォーマット方式を、日付と時間で別々に指定
  6. static で事前定義されたフォーマッタ
    BASIC_ISO_DATE など

java.time.format.FormatStyle

FormatStyle は4種類

  • FormatStyle.FULL
    タイムゾーンを含む完全な書式。ゾーン情報を持たない LocalDateTime などに適用すると実行時に例外
  • FormatStyle.LONG
    タイムゾーンを含む簡潔な書式。ゾーン情報を持たない LocalDateTime などに適用すると実行時に例外
  • FormatStyle.MEDIUM
    タイムゾーンを含まない簡潔な書式
  • FormatStyle.SHORT
    タイムゾーンを含まない簡潔な日時までの書式
LocalDateTime dateTime = LocalDateTime.of(2020, 8, 14, 9, 54, 30);
ZonedDateTime zDateTime =
    ZonedDateTime.of(dateTime, ZoneId.systemDefault());

// FormatStyle.FULL … 『2020年8月14日 9時54分30秒』
println(fmt1.format(zDateTime));

// FormatStyle.LONG … 『2020/08/14 9:54:30 JST』
println(fmt2.format(zDateTime));

// FormatStyle.MEDIUM … 『2020/08/14 9:54:30』
println(fmt3.format(dateTime));

// FormatStyle.SHORT … 『20/08/14 9:54』
println(fmt4.format(dateTime));

Date and Time API のフォーマット文字列

  • "y" … 年
  • "M" … 月(大文字)
  • "d" … 日
  • "H" … 時(大文字)
  • "m" … 分
  • "s" … 秒
  • "S" … ミリ秒(大文字)
  • "n" … ナノ秒

日付/時刻の加減算

加算 … plus(TemporalAmount amountToAdd)
減算 … minus(TemporalAmount amountToSubtract)

  • PeriodDurationTemporalAmount インターフェイスを実装する
  • plusYearsplusWeeks など個々のフィールドに対する加減算も可能だが、複数のフィールドをまとめて扱う場合は PeriodDuration を引数にして plus を呼び出す

夏時間

  • 夏時間の適用開始日(時間を進める日)と適用終了日(時間を戻す日)は、同じ地域でも毎年異なる。ただし時刻は 02:00 で一定
  • 適用が開始されると、オフセットが1時間減り時刻が1時間進む
    02:00 から1時間進むので、01:59 の1分後は 03:00 になる
    適用開始日に 01:00 と 03:00 の間隔を取ると、2時間ではなく1時間
  • 適用が終了すると、オフセットが1時間増え時刻が1時間戻る
    02:00 から1時間戻るので、01:59 の1分後は 01:00 になる

Period:年月日

  • toString() した Period 表現は "P00Y00M00D" となる
    先頭の "P" は "Period" の頭文字
  • Period のインスタンスを生成する方法はいくつかある
    • between メソッドで2つの LocalDateLocalDateTime の差分を取る
    • ofYears(1) など ofXXXXX メソッドで生成。ただしメソッドチェーンは不可
  • Period は年、月、日、週で加減算できる
    加減算のメソッドは plusXXXXX / minusXXXXX。plusYears(1) など
    plusWeeks(3) で週を加算すると "P21D"。週の Period 表現が無い為
  • LocalDate / LocalDateTimeplus / minus メソッドで Period を引数にして日付の加減算ができる
// Period を使った年の加算
LocalDateTime dt = LocalDateTime.now();
Period p = Period.ofYears(1);

// これは以下の2行と同等(メソッドチェーンは意味がない)
// Period p1 = Period.ofDays(1);
// Period p = Period.ofYears(2);
Period p = Period.ofDays(1).ofYears(2);

Duration:時分秒

  • toString() した Duration 表現は "PT00H00M00S" となる
    先頭の "PT" は "Period of Time" の略称
  • Duration のインスタンスを生成する方法はいくつかある
    • between メソッドで2つの LocalDateTimeLocalTime の差分を取る
    • ofHours(1) など ofXXXXX メソッドで生成
    • of(1, ChronoUnit.HOURS) など of メソッドで ChromeUnit を併用して生成
  • Duration は日、時、分、秒、ミリ秒、ナノ秒で加減算できる
    日を加算したら PT24H で、ミリ秒以下だと PT0.13S となる
  • LocalTime / LocalDateTimeplus / minus メソッドで Duration を引数にして時分秒の加減算ができる

java.time.Instant

  • エポック時間からの経過を表現するクラス
  • インスタンスを生成する方法はいくつかある
    1. Instant#now()
    2. Instant#ofEposSecond(long)
    3. Instant#ofEpocMilli(long)
    4. LocalDateTime#toInstant(ZoneOffset)
    5. ZonedDateTime#toInstant()
  • LocalDateTime#toInstantZoneOffset が必要なのは LocalDateTime にタイムゾーンの情報が含まれていない為
  • Instant のタイムゾーンは常にUTC

入出力

java.io.Reader を使った読み込み

  • Reader
    • public int read()
      読み込んだ1文字を返す。末尾に到達したら -1
    • public int read(char[] buffer)
    • public int read(char[] buffer, int offset, int length)
      読み込んだ文字を配列の一部または全部に入れて文字数を返す。末尾に到達したら -1
  • BufferedReader
    • public String readLine()
      読み込んだ1行のテキストを返す。末尾に到達したら null

java.io.Writer を使った書き込み

  • Writer
    • public void write(char[] buffer)
    • public void write(char[] buffer, int offset, int length)
      配列の一部または全部を書き込む
    • public void write(int c)
      1文字書き込む
    • public void write(String str)
    • public void write(String str, int offset, int length)
      文字列の一部または全部を書き込む
  • BufferedWriter
    • public void newLine()
      改行文字を書き込む

java.io.Reader や java.io.InputStream の読み込み位置制御

  • skip(long bytes)
    現在位置から指定したバイト数だけ読み飛ばす
  • mark(int readAheadLimit)
    現在位置をマークして reset() で戻れるようにする。引数は マークを維持しつつ読み込むことができる文字数の上限
  • reset()
    mark() した位置に戻る
// ファイルの中身は "01234567"
// 実行結果は "025676"
try (BufferedReader br =
        new BufferedReader(new FileReader("sample.txt"))) {
    for (int i = 0; i < 3; i++) {
        br.skip(i); // iバイトを読み飛ばす
        System.out.print((char)br.read());
    }
    br.mark(3); // 現在位置("6"のあるINDEX=5の場所)をマーク
    System.out.print(br.readLine());
    br.reset(); // マークした位置(INDEX=5の場所)に戻る
    System.out.println((char)br.read());
} catch (IOException e) {
    e.printStackTrace();
}

java.io.PrintWriter

  • java.io.Writer のサブクラスで、プリミティブ型をそのまま出力できる
    boolean/char/char[]/int/float/long/String/Object が対象
  • printprintln 以外に format を使って文字列のフォーマットが可能
  • 自動フラッシュ機能を持つ
  • 同じ機能を持つクラスに PrintStream がある
    JDK1.2で PrintWriter にとって変わられたが、後方互換のため残っている

java.io.Console

  • 標準入力からデータを読み取る
  • System#console() でシングルトンインスタンスを取得。 利用不可なら null が返る
  • readLine() で、入力された文字列を String で取得
  • readPassword() で、入力された文字列を char[] で取得。パスワードの読み取りなので、入力時に文字列は表示しない
  • writer()reader() で、それぞれコンソールに紐づいている PrintWriterReader を取得できる
  • Console 自身にも printfformat メソッドがあり、コンソールに文字列を出力できる

シリアライズに関する注意点

  • java.io.Serializable インターフェイスを実装したクラスはシリアライズ可能
  • シリアライズ対象外になるのは以下のどちらか
    1. static 変数
    2. transient 宣言されているメンバ変数
  • スーパークラスがシリアライズ可能なら、サブクラスもシリアライズ可能
  • デシリアライズでは、インスタンス初期化ブロックも、コンストラクタも呼び出されない。ただし static 初期化ブロックは呼び出される
    また、以下のケースならスーパークラスのコンストラクタだけは呼び出される
    1. 自身は Serializable だが、スーパークラスが Serializable ではない
    2. スーパークラスに引数なしのコンストラクタがある
    3. サブクラスのコンストラクタで明示的に親のコンストラクタを呼び出していない
  • メンバ変数のシリアライズ可否は、フィールドではなく中身のデータ型で決まる
    変数の型が何であれ、中身が Serializable ならシリアライズ可能

NIO2

java.nio.file.Path(特徴)

  • 以下のメソッドで生成
    • Paths#get(String, String...)
    • FileSystem#getPath(String, String...)
  • 主な特徴は以下の通り。java.io.File にない機能を備える
    • ファイルやディレクトリのパスを扱う
    • ファイル属性(オーナー、パーミッションなど)の取得や変更が可能
    • シンボリックリンクを扱える
    • ファイル作成など、ディレクトリで発生したイベントの変更や監視が可能
  • java.io.File との相互変換が可能
    • File#toPath()
    • Path#toFile()

java.nio.file.Path(メソッド)

  • getRoot()
    パスのルートを返す。相対パスで生成した Path からは null
  • subpath(int start, int end)
    ルートを除く、インデックスで指定された範囲を抜き出した Path を生成する
  • getName(int)
    ルートを除く、インデックスで指定された部分を String で返す
  • relativize(Path)
    引数までの相対パスを返す
  • resolve(String)resolve(Path)
    パスを解決する。引数によって戻り値となる Path は異なる
    • 引数が相対パス … レシーバーに引数を連結して返す
    • 引数が絶対パス … 引数をそのまま返す
    • 引数が空 … レシーバー自身を返す
  • resolveSibling(String)resolveSibling(Path)
    レシーバーの親に対してパスを解決する。引数によって戻り値となる Path は異なる
    • 引数が絶対パス…レシーバーに引数を連結して返す
    • その他…レシーバーに引数を連結して返す
  • normalize()
    「.」や「..」など冗長な部分を除去して適切な形に変換したパスを返す
String sp1 = "/tmp/work1/sample.txt";
String sp2 = "/tmp/work2/dummy.txt";
Path p1 = Paths.get(sp1);
Path p2 = FileSystems.getDefault().getPath(sp2);
println(p1.getRoot());      // "/"
println(p1.subpath(0, 2));  // "tmp/work1"
println(p1.relativize(p2)); // "../../work2/dummy.txt"
println(p1.getName(0));     // "tmp"

Path rp = p1.resolve("../dat");
println(rp);                // "/tmp/work1/sample.txt/../dat"
println(rp.normalize());    // "/tmp/work1/dat"

// Windows の場合
Path wp = Paths.get("C:¥¥temp¥¥work¥¥sample.txt");
println(wp.getRoot());      // "C:¥"

java.nio.file.FileSystem

ファイルシステムへのインターフェイスを提供する。FileSystems クラスの static メソッドで取得

  • FileSystems#getDefault
  • FileSystems#getFileSystem
  • FileSystems#newFileSystem

java.nio.file.Files(ファイル操作)

以下は全て static メソッド

  • copy
    ファイルやディレクトリのコピー
    1. ディレクトリの中身まではコピーされない
    2. コピー先に同名のファイルが存在する時 CopyOption で上書き指定が無ければ例外
    3. デフォルトの挙動ではファイルをコピーしても属性はコピーされない
    4. CopyOption を指定せずにシンボリックリンクをコピーすると、リンクが示す実体がコピーされる
    5. 引数に与えるパスはコピー/移動をする場所ではなく、移動/コピー後のパス
  • move
    ファイルやディレクトリの移動。挙動は copy の1〜5と同じ
  • getAttribute(Path, String, LinkOption...)
    ファイルの属性値(ファイルサイズや更新日時など)を読み取る
  • Stream<String> lines(Path)
  • List<String> readAllLines(Path)
    ファイルの全ての行を返す。メソッドによって戻り値の型が異なる

java.nio.file.Files(ディレクトリの操作)

以下は全て static メソッド

  • createDirectory()
    ディレクトリを作成するが、親は作成しない
  • createDirectories()
    親も含めてディレクトリを作成する
  • deletedeleteIfExists
    ファイルを削除する。失敗したら前者は例外、後者は boolean 型で結果が返る どちらもディレクトリの中にファイルがあると削除できず、例外が発生する
  • newDirectoryStream(Path)
    ディレクトリ内のパスを Iterable の実装クラス DirectoryStream<Path> で返す
  • list(Path)
    ディレクトリ内のファイルやディレクトリの一覧を Stream<Path> で返す。引数がディレクトリでない場合は例外が発生する

java.nio.file.Files(ディレクトリの検索)

以下は全て static メソッド

  • walk()
  • walkFileTree()
    サブディレクトリも含めて再帰的に検索する
  • list()
    そのディレクトリだけを検索する
  • find()
    サブディレクトリも含めて再帰的に、条件に合致するものを検索する
  • これらの戻り値は全てストリームなので、パイプライン処理が可能

java.nio.file.attribute

ファイルの属性(作成時間、アクセス時間、所有者など)を表現するクラスが入ったパッケージ。属性情報のセットを示す属性ビューインターフェイスも含む

  • BasicFileAttributes インターフェイス
    基本的な属性情報
  • DosFileAttributes インターフェイス
    DOS系の属性情報
  • PosixFileAttributes インターフェイス
    Unix/Linuxの属性情報
  • AttributeView インターフェイス
    属性情報のセット。サブインターフェイスとして以下が存在する
    • BasicFileAttributeView
    • DosFileAttributeView
    • PosixFileAttributeView

並列処理

並行処理ユーティリティが提供する機能

  1. スレッドプール
    スレッドの生成と再利用
  2. 並行コレクション
    複数スレッドからの並行アクセスを適切に処理できるコレクション
  3. アトミック変数
    並行アクセスによって値の不整合を起こさないための分割不可能な一連の操作を実装(synchronized と同じ)
  4. カウウンティング・セマフォ
    有限なリソースに対して並行アクセス可能なスレッド数を自由に設定可能

並列コレクション

  • 並列処理に関わるAPIは以下。全て java.util.concurrent パッケージ
    • BlockingQueue インターフェイス
    • BlockingDeque インターフェイス
    • ConcurrentMap インターフェイス
    • ConcurrentHashMap クラス
    • CopyOnWriteArrayList クラス
    • CopyOnWriteArraySet クラス
  • 並列処理に対応していないコレクションオブジェクトを複数のスレッドから操作すると java.util.ConcurrentModificationException が発生する
  • 複数のスレッドから操作するときに拡張for文などでイテレータを使うと、イテレータを生成した時点のコレクションの中身が使用される

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

  • V getOrDefault(Object key, V defaultValue)
    key に紐づく値を返す。無ければ defaultValue を返す
  • V putIfAbsent(K key, V value)
    key に紐づく値がマップにない場合のみ追加
  • boolean remove(Object key, Object value)
    key と value の両方が一致する要素があれば削除
  • V replace(K key, V value)
    key に指定した値がマップにあれば置換
  • V replace(K key, V oldValue, V newValue)
    key と oldValue の両方が一致する要素があれば値を newValue に置換
// map の中身は [1, "One"], [2, "Two.2"], [3, "Three"]
Map<Integer, String> map = new ConcurrentHashMap<>();
map.put(1, "One");
map.putIfAbsent(1, "One.2"); // 追加しない(KEY=1の要素が既存)
map.put(2, "Two");
map.replace(2, "Two.2");           // 置換
map.replace(2, "Two.22", "Two.3"); // 置換しない(第2引数が既存の値と違う)
map.put(3, "Three");     // 削除
map.remove(3, "Three2"); // 削除しない(第2引数が既存の値と違う)

java.util.concurrent.CopyOnWriteArrayList クラス

  • 内部で保持している要素を変更するときは、古い要素が入った配列をコピーしてそれを変更し、差し替える
    そのため、リストのサイズが大きくなるとオーバーヘッドが肥大化する
  • 要素の変更中(新しい配列へのコピー中)に別のスレッドがリストにアクセスすると、古い配列の内容を参照することになる

java.util.concurrent.CyclicBarrier クラス

  • スレッドパーティー(強調して動作するスレッドの集合)内の各スレッドの足並みを揃えるための機能を提供
  • コンストラクタで協調させるスレッドの数を指定。第1引数は協調させるスレッドの数、第2引数があれば、トリップ(=バリアが解除)したときに実行される処理
    • new CyclicBarrier(int)
    • new CyclicBarrier(int, Runnable)
  • await() を実行したスレッドは CyclicBarrier のコンストラクタで指定した数のスレッドが await するまで待機する
    • await(long timeout) タイムアウトなし
    • await(long timeout, TimeUnit unit) タイムアウトあり
  • 4つのスレッドに対して new CyclicBarrier(3) とすると、3つのスレッドが await した時点でトリップが発生する
    しかし最後の1つは、さらに2つのスレッドが await するまでバリアを突破できないので、続きの処理は実行できない
// バリアの許容数が2つに対してスレッドが3本なので、
// "wait wait wait finish finish" と出て処理が止まる
ExecutorService service = Executors.newCachedThreadPool();
CyclicBarrier barrier = new CyclicBarrier(2);
for (int i = 0; i < 3; i++) {
    service.execute(() -> {
        try {
            System.out.print("wait ");
            barrier.await();
            System.out.println("finish ");
        } catch (BarrierBrokenException | InterruptedException ignore) {
        }
    });
}
service.shutdown();

ExecutorService の継承関係

  • Executorexecute(Runnabble)
    • ExecutorServicesubmit(Runnable) など
      • ThreadPoolExecutor
      • ForkJoinPool
    • ScheduledExecutorServiceschedule(Runnable,long,TimeUnit)
      • ScheduledThreadPoolExecutor

Executorsのメソッドとそれぞれに対応するExecutorServiceのスペック

  • newSingleThreadExecutor()
    単一のスレッドでタスクを実行
  • newCachedThreadPool()
    必要に応じて新規スレッドを生成、もしくは再利用してタスクを実行
  • newFixedThreadPool(int)
    固定数のスレッドを再利用してタスクを実行
  • newScheduledThreadPool(int)
    固定数のスレッドを再利用してタスクを実行。タスクはスケジュール可能
  • newSingleThreadScheduledExecutor()
    単一のスレッドでタスクを実行。タスクはスケジュール可能

ExecutorService#execute

  • Executors クラスの static メソッドで適切な性質の ExecutorService オブジェクトを取得して execute(Runnable) メソッドで実行
  • タスクの実行状態を確認するには以下のメソッドを使う
    • isShutdown()
      true なら新規タスクは受入不可(未実行か実行中のタスクがある可能性)
    • isTerminated()
      true なら新規タスクは受入不可、かつ全てのタスクが終了した状態
  • isShutdown() が true でなければ、新規タスクの execute が可能。true のときに execute すると例外が発生する

ExecutorService#submit

  • ExecutorService に複数のオーバーロードメソッドがあり、戻り値は Future。メソッドによって Future から get できるものが異なる
    • Future<T> submit(Runnable)
      タスクを実行。get できるのはタスクの成否
    • Future<?> submit(Runnable, T)
      第1引数のタスクを実行し、第2引数の値を返す。get できるのはタスクの成否
    • Future<T> submit(Callable<T>)
      タスクを実行。get できるのはタスクからの戻り値
  • タスクはすぐ実行されるが、 Future#get() を実行するとタスクが終了した後も値が取得できるまで待機時間が発生する
  • Future<T> からは submit したタスクのキャンセルもできる(可能な場合のみ)

Runnable と Callable

  • Runnable
    • 戻り値を返さない
    • チェック例外をスローできない
    • execute でも submit でも実行できる
  • Callable
    • 戻り値を返すことができる
    • チェック例外をスローできる
    • submit でしか実行できない

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

  • V get()
    タスクの実行結果を取得できる
  • boolean cancel(boolean)
    タスクのキャンセルを試みる
  • boolean isCancelled()
    タスクがキャンセルされたかを知る
  • boolean isDone()
    タスクが完了しているかどうかを知る

パラレルストリーム

  • 生成
    • Collection#parallelStream()
      コレクションからパラレルストリームを生成
  • 相互変換
    • BaseStream#parallel()
      シーケンシャルストリームからパラレルストリームを生成
    • BaseStream#sequential()
      パラレルストリームからシーケンシャルストリームを生成
  • 判定
    • BaseStream#isParallel()
      自身がパラレルストリームかどうかを判定
// パラレルストリームの生成
Arrays.asList(1, 2, 3).parallelStream().forEach(System.out::println);

// シーケンシャルストリームからパラレルストリームを生成
Stream.of("a", "b", "c").parallel().forEach(System.out::println);

// パラレルストリームからシーケンシャルストリームを生成
Arrays.asList("A", "B", "C").parallelStream().sequential().forEach(System.out::println);

Fork/Joinフレームワーク

※ 私はここを捨てました。真面目な方はちゃんと勉強しましょう

アトミック変数

  • Atomic〜 で始まる名前のクラスは、自身が扱う値への操作がスレッドセーフになることを保証する
  • 主なクラスは以下。Float や Double は存在しない
    • AtomicInteger … int型
    • AtomicLong … long型
    • AtomicBoolean … boolean型
    • AtomicReference … 参照型

JDBC

クエリの実行

  • 使用するクラスが所属するパッケージは java.sqljavax.sql
  • JDBC で DB に接続するための URL フォーマットは以下の通り
    jdbc:[DB名]//[host(:port)]/[db-name](?option)
  • 基本的な処理の流れは以下の通り
    1. DriverManager#getConnection(url,id,pass)Connection 取得
    2. Connection#createStatement()Statement 取得
    3. Statement#executeQuery(sql)ResultSet 取得
    4. ResultSet からクエリの結果を取り出す
  • JDBC3.0 以前では DriverManager#getConnection の前に Class.forName([JDBCドライバクラス名]) を実行する必要があった
  • executeQuery で実行するクエリの結果が0件でも戻り値は null ではなく、空の ResultSet になる
  • 1つの Statement で扱える ResultSet は1つだけ。最初の ResultSet を閉じずに別の ResultSet を取得すると、最初のは自動的に閉じられる。閉じられた ResultSet を操作しようとすると例外
    executeUpdate などを実行したときも自動で閉じる

Statementにあるメソッドの使い分け

  • ResultSet executeQuery(String sql)
    クエリの実行結果を返す
  • int executeUpdate(String sql)
    INSERT/UPDATE/DELETEなどのSQLを実行し、処理件数を返す
  • boolean execute(String sql)
    クエリを含む、あらゆる種類の SQL を実行して処理の結果として ResultSet が得られたかを返す。true なら ResultSet が返ってきており、false ならそうではない
    処理の結果 ResultSet が返ってきたなら Statement#getResultSet() で取得する。 ResultSet でなければ Statement#getUpdateCount() で処理件数を取得する

ResultSet の取得と性質

  • Connection#createStatement(type, concurrency)ResultSet の「スクロール可否」と「テーブルデータの更新可否」を順番に指定する
  • スクロール可否は以下の3種類で、全て ResultSet クラスの定数
    • TYPE_FORWARD_ONLY … 順方向にのみ移動可
    • TYPE_SCROLL_INSENSITIVE … 双方向に移動可。カーソルへの変更は反映しない
    • TYPE_SCROLL_SENSITIVE … 双方向に移動可。カーソルへの変更を反映する
  • テーブルデータの更新可否は以下の2種類で、全て ResultSet クラスの定数
    • CONCUR_READ_ONLY … 読み込みのみ
    • CONCUR_UPDATABLE … 更新可能
  • Statement#createStatement(..., ResultSet.CONCUR_UPDATABLE) で更新可能な ResultSet を取得する時、更新対象とするカラムを executeQuery の select 文に指定しておかなくてはならない

ResultSet のスクロール用メソッド

  • boolean absolute(int)
    指定した行(絶対位置を指定)へ移動。先頭行は1。0はbeforeFirst
  • boolean relative(int)
    相対位置を指定して移動。正の値なら次の行へ、負の値なら前の行にゆく
  • boolean next()
    ひとつ次の行に移動
  • boolean previous()
    ひとつ前の行に移動
  • boolean first()
    先頭行に移動
  • void beforeFirst()
    先頭行の直前に移動
  • boolean last()
    最終行に移動
  • void afterLast()
    最終行の直後に移動

更新可能な ResultSet

  • 更新
    updateStringupdateInt で変更し updateRow で変更を確定させる
    Statement のタイプが TYPE_SCROLL_INSENSITIVE でも updateRow しない限り、DB の中身どころか結果セットさえ変更されない
    • updateString(int, String)
    • updateInt(int, int)
    • updateRow()
  • 挿入
    moveToInsertRow で挿入行に移動し updateStringupdateInt で挿入内容を指定して insertRow で挿入する
    • moveToInsertRow()
    • insertRow()
  • 削除
    現在行を削除する
    • deleteRow()

ローカライズとフォーマット

java.util.Locale オブジェクトの取得

  1. Locale#getDefault()
    Java実行環境のロケール
  2. new Locale(String)
    引数は「言語コード」
  3. new Loacle(String, String)
    引数は「言語コード, 国コード」
  4. new Loacle(String, String, String)
    引数は「言語コード, 国コード, バリアント」
  5. new で生成した Locale.Builder に適切な値を設定して build() で生成

java.util.Properties

  • サポートするファイルフォーマットはテキストとXML
  • テキストファイルの場合、キーと値を = もしくは : で区切って列挙する
  • ファイルからプロパティリストをロード
    • load(InputStream)
    • load(Reader)
    • loadFromXML(InputStream)
  • プロパティリストを出力
    • list(OutputStream)
    • list(Writer)
    • entrySet()
    • forEach(BiConsumer)
  • プロパティの取得
    • getProperty(String)
      該当するKeyが無ければ null を返す
    • getProperty(String, String)
      該当するKeyが無ければ第2引数を返す

java.util.ListResourceBundle

  • リソースを .class ファイルで用意してクラスパスが張られている場所に置く
  • 使い方は以下の通り
    1. ListResourceBundle を継承した public クラスを定義
      public Object[][] getContents() をオーバーライド
      このクラスの完全修飾名(パッケージ名+クラス名)が基底名に、このクラスがデフォルトロケール用になる
      例:クラス名は "MyResource" とする
    2. 1とは異なるロケール用のクラスを ListResourceBundle を継承して定義する
      ネーミングルールは 「基底名言語コード国コード」
      例:英語ロケール用のクラス名は "MyResource_en_US" となる
    3. ResourceBundle#getBundle(基底名[, ロケール])ListResourceBundle オブジェクトを取得
    4. ResourceBundle#getXXXXX(キー文字列) で値を取り出す

java.util.PropertyResourceBundle

  • リソースを .properties ファイルで用意してクラスパスが張られている場所に置く
  • 使い方は以下の通り
    1. デフォルトロケール用のプロパティファイルを作成
      • 拡張子は .properties
      • 『キー名 = 値』のフォーマットで複数のプロパティを定義
      • ファイルは ISO-8859-1 エンコードされている必要がある
      • ファイルの命名規則は ListResourceBundle のクラス名と同じ
    2. 1とは異なるロケール用のファイルを作成
      • ファイルの命名規則は ListResourceBundle のクラス名と同じ
      • 英語ロケール用なら MyResource_en_US.properties となる
    3. ResourceBundle#getBundle(基底名[, ロケール])PropertyResourceBundle オブジェクトを取得
      • ListResourceBundle を取得するのと同じメソッド
    4. 以下のメソッドで値を取り出す。getInt はない
      • ResourceBundle#getObject(キー文字列)
      • ResourceBundle#getString(キー文字列)
      • ResourceBundle#getStringArray(キー文字列)

使用されるリソースバンドルの優先順位

  • ロケールに対応するリソースバンドルが存在しなければ MissingResourceException 例外が発生する
  • 同名のリソースバンドルが複数あっても例外にはならない
  • ファイル単位ではなく個々のプロパティ単位でリソースバンドルが検索される。検索の優先順位は以下の通り
    1. 言語コード、国コードが一致
    2. 言語コードが一致
    3. デフォルトロケールが一致
  • 指定したキーに対応するプロパティがどのクラス/ファイルにも存在しなければ MissingResourceException が発生する
  • ListResourceBundle クラスとプロパティファイルの両方が同じ名前で存在すると、ファイルよりクラスのほうが優先される

NumberFormatを使って数値をフォーマット

  • NumberFormat クラスの static メソッドを使って目的に応じたオブジェクトを取得
    • getInstance()
      デフォルトロケールの数値フォーマット
    • getInstance(Locale)
      任意のロケールの数値フォーマット
    • getCurrencyInstance(Locale)
      任意のロケールの通貨フォーマット
    • getIntegerInstance(Locale)
      任意のロケールの整数フォーマット
  • format(long) もしくは format(double) でフォーマット済み文字列を取得
  • parse(String) で文字列から数値を取得。戻り値は Number

更新履歴

  • 2020/08/23
    Qiita に公開
  • 2020/08/24
    関数型インターフェイスの一覧を表形式に変更
    列挙値に関するサンプルコードを追加
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

SpringSecurity TextEncryptor: 共通鍵暗号化・復号

はじめに

暗号化、復号はよく使う処理であり、SpringSecurityでは便利なインターフェイスが用意されており、簡単に使えますので、サンプルを作成してみます。

暗号化、復号インターフェス

テキスト用:org.springframework.security.crypto.encrypt.TextEncryptor
バイト配列用:org.springframework.security.crypto.encrypt.BytesEncryptor

乱数生成インターフェス

テキスト用:org.springframework.security.crypto.keygen.StringKeyGenerator
バイト配列用:org.springframework.security.crypto.keygen.BytesKeyGenerator

サンプル

package sample.security;

import java.util.Base64;

import org.apache.commons.codec.binary.Hex;
import org.springframework.security.crypto.encrypt.BytesEncryptor;
import org.springframework.security.crypto.encrypt.Encryptors;
import org.springframework.security.crypto.encrypt.TextEncryptor;
import org.springframework.security.crypto.keygen.KeyGenerators;

public class Test {

    public static void main(String[] args) {

        // 平文
        String orginalText = "私は◯◯が好きです。";

        // 共通鍵
        String encryptSecrerKey = "GOV2dkHGQcE1ZcX8";

        // Salt:乱数生成
        byte[] bytes = KeyGenerators.secureRandom(16).generateKey();
        String salt = new String(Hex.encodeHexString(bytes));

        // CBC方式
        TextEncryptor cbcTextEncryptor = Encryptors.text(orginalText, salt);
        // 暗号化
        String encryptedCbcData = cbcTextEncryptor.encrypt(orginalText);
        System.out.println("暗号化した結果(AES-CBC): " + encryptedCbcData);
        // 復号
        String decryptedCbcData = cbcTextEncryptor.decrypt(encryptedCbcData);
        System.out.println("復号した結果(AES-CBC): " + decryptedCbcData);

        // GCM方式
        TextEncryptor aesGcmTextEncryptor = Encryptors.delux(encryptSecrerKey, salt);
        // 暗号化
        String encryptedData = aesGcmTextEncryptor.encrypt(orginalText);
        System.out.println("暗号化した結果(AES-GCM): " + encryptedData);
        // 復号
        String decryptedData = aesGcmTextEncryptor.decrypt(encryptedData);
        System.out.println("復号した結果(AES-GCM): " + decryptedData);

        // バイナリ(CBC)
        BytesEncryptor binaryEncryptor = Encryptors.standard(encryptSecrerKey, salt);
        // 暗号化
        String encryptedBinaryData = Base64.getEncoder()
                .encodeToString(binaryEncryptor.encrypt(orginalText.getBytes()));
        System.out.println("暗号化した結果(バイナリCBC): " + encryptedBinaryData);
        // 復号
        byte[] decryptedBinaryData = binaryEncryptor.decrypt(Base64.getDecoder().decode(encryptedBinaryData));
        System.out.println("復号した結果(バイナリCBC): " + new String(decryptedBinaryData));

        // バイナリ(GCM)
        BytesEncryptor aesGcmBinaryEncryptor = Encryptors.stronger(encryptSecrerKey, salt);
        // 暗号化
        String gcmEncryptedBinaryData = Base64.getEncoder()
                .encodeToString(aesGcmBinaryEncryptor.encrypt(orginalText.getBytes()));
        System.out.println("暗号化した結果(バイナリGCM): " + gcmEncryptedBinaryData);
        // 復号
        byte[] gcmDecryptedBinaryData = binaryEncryptor.decrypt(Base64.getDecoder().decode(encryptedBinaryData));
        System.out.println("復号した結果(バイナリGCM): " + new String(gcmDecryptedBinaryData));
    }
}


実行結果例:

暗号化した結果(AES-CBC): a34e20774314b85322de3eaf6c10f6990befe353d382cc449039524ffb7042b442f2a7a00beeddc94c8ad1495f6a0fba
復号した結果(AES-CBC): 私は◯◯が好きです。
暗号化した結果(AES-GCM): 1f5c89aeabf9f05a3cb286d1eca6e06ecd3259e9e52cea96d80e41d60614e436657317b7e69ad5b3e8672adaadd2434af69cbd36b516462908bef26d1ecf
復号した結果(AES-GCM): 私は◯◯が好きです。
暗号化した結果(バイナリCBC): zQg1LujXz6lpMwVoqNvG+ETtDbEC/LE0kq1LKJ+4d8ntbPDHDsAC0e5w6Eym7Hps
復号した結果(バイナリCBC): 私は◯◯が好きです。
暗号化した結果(バイナリGCM): gSFtQJCV0OSkjdg8sf+WHxYAFIdZrgb6MFWsPTmzau67lzFVW+ZJP36bWPQQ0p8/QZ9FIgQ7isZm4fIYt1g=
復号した結果(バイナリGCM): 私は◯◯が好きです。

参考: https://docs.spring.io/spring-security/site/docs/5.2.1.RELEASE/reference/htmlsingle/#spring-security-crypto-encryption-text

以上

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Java + Oracle Database 12c で主要な SQL 型の値を取得するサンプルコード

概要

  • Oracle Database 12c にてテーブルに主要な SQL データ型のカラムを定義して、Java のプログラムからカラムの値を取得するサンプルコードを示す
  • 今回の動作確認環境: Oracle Database 12c Release 2 (12.2.0.1.0) Enterprise Edition (on Docker) + Oracle JDBC Thin driver (ojdbc8.jar) 19.7.0.0 + Java 14 (AdoptOpenJDK 14.0.2) + Gradle 6.6 + macOS Catalina

サンプルコード

ファイル一覧

├── build.gradle
└── src
    └── main
        └── java
            └── JdbcSample.java

build.gradle

plugins {
  id 'application'
  id 'java'
}

sourceCompatibility = JavaVersion.VERSION_14

repositories {
  mavenCentral()
}

dependencies {

  // 実行時に Oracle JDBC Driver を使うだけなら runtimeOnly を指定
  //runtimeOnly 'com.oracle.database.jdbc:ojdbc8:19.7.0.0'

  // 今回は oracle.jdbc.OracleTypes を使うため implementation を指定
  implementation 'com.oracle.database.jdbc:ojdbc8:19.7.0.0'
}

tasks.withType(JavaCompile) {
  // Java 14 のプレビュー機能を使う
  options.compilerArgs += ['--enable-preview']
}

application {
  // Java 14 のプレビュー機能を使う
  applicationDefaultJvmArgs = ['--enable-preview']
  mainClassName = 'JdbcSample'
}

JdbcSample.java

import oracle.jdbc.OracleTypes; // 普通に使うだけなら不要 (今回は Oracle 拡張 JDBC 型の情報を取得するために使用)

import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.Statement;
import java.sql.Types;
import java.util.Arrays;

class JdbcSample {

  public static void main(String[] args) throws Exception {

    // MySQL に接続
    String url = "jdbc:oracle:thin:@//localhost:1521/testdb";
    String user = "javarista";
    String password = "cafebabe";
    Connection con = DriverManager.getConnection(url, user, password);
    Statement stmt = con.createStatement();

    // テーブルを作成
    // いろいろな SQL データ型でカラムを定義
    // (Java 14 プレビュー機能で使えるヒアドキュメントっぽく書けるテキストブロック機能を使う)
    stmt.execute("""
      create table test (
        -- 文字列型
        my_char      CHAR(8 CHAR),        -- 固定長文字列 (基本は最大長 2000 bytes)
        my_varchar2  VARCHAR2(512 CHAR),  -- 可変長文字列 (基本は最大長 4000 bytes)
        my_clob      CLOB,                -- Character Large Object

        -- バイナリ型
        my_raw   RAW(256),  -- 可変長バイナリ (基本は最大長 2000 bytes)
        my_blob  BLOB,      -- Binary Large Object

        -- 真偽値型
        my_number_1  NUMBER(1),  -- 0 は false、それ以外の値は true として解釈される

        -- 整数型
        my_number_38  NUMBER(38),  -- 最大38桁
        my_smallint   SMALLINT,    -- Oracle データ型に変換される ANSI データ型
        my_integer    INTEGER,     -- Oracle データ型に変換される ANSI データ型

        -- 浮動小数点型
        my_binary_float      BINARY_FLOAT,      -- 単精度浮動小数点数 4 bytes
        my_binary_double     BINARY_DOUBLE,     -- 倍精度浮動小数点数 8 bytes
        my_float             FLOAT,             -- Oracle データ型 FLOAT(126) に変換される ANSI データ型
        my_double_precision  DOUBLE PRECISION,  -- Oracle データ型 FLOAT(126) に変換される ANSI データ型
        my_real              REAL,              -- Oracle データ型 FLOAT(63)  に変換される ANSI データ型

        -- 固定小数点型
        my_fixed_point_number  NUMBER(7, 4),   -- 固定小数点数
        my_numeric             NUMERIC(7, 4),  -- Oracle データ型に変換される ANSI データ型
        my_decimal             DECIMAL(7, 4),  -- Oracle データ型に変換される ANSI データ型

        -- 時間型
        my_date                      DATE,                        -- 年月日 + 時分秒
        my_timestamp                 TIMESTAMP(9),                -- 年月日 + 時分秒 + ナノ秒
        my_timestamp_with_time_zone  TIMESTAMP(9) WITH TIME ZONE  -- 年月日 + 時分秒 + ナノ秒 + タイムゾーン
      )""");

    // レコードを追加
    // (Java 14 プレビュー機能で使えるヒアドキュメントっぽく書けるテキストブロック機能を使う)
    stmt.execute("""
      insert into test values (
        -- 文字列型
        'Hello', -- CHAR
        'Hello', -- VARCHAR2
        'Hello', -- CLOB

        -- バイナリ型
        HEXTORAW('CAFEBABE'),  -- RAW
        HEXTORAW('CAFEBABE'),  -- BLOB,

        -- 真偽値型
        1,  -- 0 は false、それ以外の値は true として解釈される

        -- 整数型
        12345678901234567890123456789012345678,  -- NUMBER(38)
        32767,                                   -- SMALLINT
        2147483647,                              -- INTEGER

        -- 浮動小数点型
        123.0001,  -- BINARY_FLOAT
        123.0001,  -- BINARY_DOUBLE
        123.0001,  -- FLOAT
        123.0001,  -- DOUBLE PRECISION
        123.0001,  -- REAL

        -- 固定小数点型
        123.0001,  -- NUMBER(7, 4)
        123.0001,  -- NUMERIC(7, 4)
        123.0001,  -- DECIMAL(7, 4)

        -- 時間型
        TO_DATE('2001-02-03 04:05:06', 'YYYY-MM-DD HH24:MI:SS'),                                      -- DATE
        TO_TIMESTAMP('2001-02-03 04:05:06.999999999', 'YYYY-MM-DD HH24:MI:SS.FF9'),                   -- TIMESTAMP(9)
        TO_TIMESTAMP_TZ('2001-02-03 04:05:06.999999999 +00:00', 'YYYY-MM-DD HH24:MI:SS.FF9 TZH:TZM')  -- TIMESTAMP(9) WITH TIME ZONE
      )""");

    // レコードを取得
    ResultSet rs = stmt.executeQuery("select * from test");
    while (rs.next()) {

      // カラムの JDBC 型や SQL 型に対する Java オブジェクトの型を取得
      System.out.println("カラム名 - JDBC 型 - データベース固有の SQL 型 - Java オブジェクトの型");
      ResultSetMetaData rsmd = rs.getMetaData();
      for (int i = 1; i <= rsmd.getColumnCount(); i++) {
        System.out.println(
          rsmd.getColumnName(i) + " - " +
            getJdbcTypeName(rsmd.getColumnType(i)) + " - " +
            rsmd.getColumnTypeName(i) + " - " +
            rsmd.getColumnClassName(i));
      }
      System.out.println();

      // カラムの値を取得していく
      System.out.println("カラム名 - カラムの値");

      // 文字列型
      System.out.println("my_char=" + rs.getString("my_char"));
      System.out.println("my_varchar2=" + rs.getString("my_varchar2"));
      System.out.println("my_clob=" + rs.getClob("my_clob"));

      // バイナリ型
      System.out.println("my_raw=" + Arrays.toString(rs.getBytes("my_raw")));
      System.out.println("my_blob=" + rs.getBlob("my_blob"));

      // 真偽値型
      System.out.println("my_number_1=" + rs.getBoolean("my_number_1"));

      // 整数型
      System.out.println("my_number_38=" + rs.getBigDecimal("my_number_38"));
      System.out.println("my_smallint=" + rs.getInt("my_smallint"));
      System.out.println("my_integer=" + rs.getInt("my_integer"));

      // 浮動小数点型
      System.out.println("my_binary_float=" + rs.getFloat("my_binary_float"));
      System.out.println("my_binary_double=" + rs.getDouble("my_binary_double"));
      System.out.println("my_float=" + rs.getDouble("my_float"));
      System.out.println("my_double_precision=" + rs.getDouble("my_double_precision"));
      System.out.println("my_real=" + rs.getDouble("my_real"));

      // 固定小数点型
      System.out.println("my_fixed_point_number=" + rs.getBigDecimal("my_fixed_point_number"));
      System.out.println("my_numeric=" + rs.getBigDecimal("my_numeric"));
      System.out.println("my_decimal=" + rs.getBigDecimal("my_decimal"));

      // 時間型
      System.out.println("my_date=" + rs.getTimestamp("my_date"));
      System.out.println("my_timestamp=" + rs.getTimestamp("my_timestamp").toInstant());
      System.out.println("my_timestamp_with_time_zone=" + rs.getTimestamp("my_timestamp_with_time_zone").toInstant());
    }

    stmt.close();
    con.close();
  }

  // JDBC 型の名称を取得する
  private static String getJdbcTypeName(int type) throws IllegalAccessException {
    // Java 標準の JDBC 型から探す
    Field[] fs = Types.class.getDeclaredFields();
    for (Field f : fs) {
      if (type == f.getInt(null)) {
        return f.getName();
      }
    }
    // Oracle 拡張機能の JDBC 型から探す
    fs = OracleTypes.class.getDeclaredFields();
    for (Field f : fs) {
      if (type == f.getInt(null)) {
        return "OracleTypes." + f.getName();
      }
    }
    // 合致する JDBC 型が無かったので type 値を文字列化して返す
    return "" + type;
  }
}

実行結果

Gradle の run タスクで実行。

$ gradle run
Starting a Gradle Daemon (subsequent builds will be faster)

> Task :compileJava
注意:/Users/foo/bar/src/main/java/JdbcSample.javaはプレビュー言語機能を使用します。
注意:詳細は、-Xlint:previewオプションを指定して再コンパイルしてください。

> Task :run
カラム名 - JDBC 型 - データベース固有の SQL 型 - Java オブジェクトの型
MY_CHAR - CHAR - CHAR - java.lang.String
MY_VARCHAR2 - VARCHAR - VARCHAR2 - java.lang.String
MY_CLOB - CLOB - CLOB - oracle.jdbc.OracleClob
MY_RAW - VARBINARY - RAW - [B
MY_BLOB - BLOB - BLOB - oracle.jdbc.OracleBlob
MY_NUMBER_1 - NUMERIC - NUMBER - java.math.BigDecimal
MY_NUMBER_38 - NUMERIC - NUMBER - java.math.BigDecimal
MY_SMALLINT - NUMERIC - NUMBER - java.math.BigDecimal
MY_INTEGER - NUMERIC - NUMBER - java.math.BigDecimal
MY_BINARY_FLOAT - OracleTypes.BINARY_FLOAT - BINARY_FLOAT - java.lang.Float
MY_BINARY_DOUBLE - OracleTypes.BINARY_DOUBLE - BINARY_DOUBLE - java.lang.Double
MY_FLOAT - NUMERIC - NUMBER - java.lang.Double
MY_DOUBLE_PRECISION - NUMERIC - NUMBER - java.lang.Double
MY_REAL - NUMERIC - NUMBER - java.lang.Double
MY_FIXED_POINT_NUMBER - NUMERIC - NUMBER - java.math.BigDecimal
MY_NUMERIC - NUMERIC - NUMBER - java.math.BigDecimal
MY_DECIMAL - NUMERIC - NUMBER - java.math.BigDecimal
MY_DATE - TIMESTAMP - DATE - java.sql.Timestamp
MY_TIMESTAMP - TIMESTAMP - TIMESTAMP - oracle.sql.TIMESTAMP
MY_TIMESTAMP_WITH_TIME_ZONE - OracleTypes.TIMESTAMPTZ - TIMESTAMP WITH TIME ZONE - oracle.sql.TIMESTAMPTZ

カラム名 - カラムの値
my_char=Hello   
my_varchar2=Hello
my_clob=oracle.sql.CLOB@7c711375
my_raw=[-54, -2, -70, -66]
my_blob=oracle.sql.BLOB@3a44431a
my_number_1=true
my_number_38=12345678901234567890123456789012345678
my_smallint=32767
my_integer=2147483647
my_binary_float=123.0001
my_binary_double=123.0001
my_float=123.0001
my_double_precision=123.0001
my_real=123.0001
my_fixed_point_number=123.0001
my_numeric=123.0001
my_decimal=123.0001
my_date=2001-02-03 04:05:06.0
my_timestamp=2001-02-02T19:05:06.999999999Z
my_timestamp_with_time_zone=2001-02-03T04:05:06.999999999Z

BUILD SUCCESSFUL in 16s
2 actionable tasks: 2 executed

Oracle Database の真偽値型

Oracle Database JDBC開発者ガイド, 12cリリース2 (12.2) - Oracleデータへのアクセスと操作

BOOLEANデータベース型が存在しないため、getBooleanを使用すると必ずデータ型変換が実行されます。getBooleanメソッドは数値用の列に対してのみサポートされます。このような列に対してgetBooleanが適用されると、0(ゼロ)値はfalseとして、それ以外の値はtrueとして解釈されます。別の種類の列に適用された場合は、getBooleanは例外java.lang.NumberFormatExceptionを戻します。

参考資料

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Javaことはじめ

Eclipseショートカットキー

■ 補完(Ctrl + Space)
(sysout) System.out.println();
(syso) System.out.println();
(main) public static void main(String[] args) {}

■ ローカル変数を宣言(Ctrl + 2 → L)
new ArrayList(); → ArrayList arrayList = new ArrayList();

■ その他のショートカット
ソースコードのフォーマット(Ctrl + Shift + F)
コメントアウト(Ctrl + /)
クイックフィックス(Ctrl + 1)
1行移動、1行コピー(Alt + ↓↑、Ctrl + Alt + ↓↑)
1行削除(Ctrl + D)
メソッドの抽出(Alt + Shift + M)
呼び出し階層を開く(Ctrl + Alt + H)
宣言を開く(Ctrl + クリック)
型を開く(Ctrl + Shift + T)

プログラムの書き方

スッキリわかるJava入門を読んだ自分用のまとめ

▼クラスブロック+メインブロック

Main.java
public class Main {
    public static void main(String[] args) {

    }
}

メインブロック内の記述

Main.java
System.out.println("こんにちは");こんにちはと表示させる
System.out.println("二重引用符(¥"");「二重引用符(")」と表示させる

int age;//変数宣言(ageという箱を用意)
age = 20;//変数age 代入 20
int age = 20;//変数 ageを20で初期化
age = 32;//変数ageに再代入
final double Pi = 3.14;//定数として円周率を宣言

//:データ型(整数)
byte glasses = 2;//所持するメガネの数
short age = 20;//年齢
int salary = 180000;//給与金額
long worldPeople = 6900000000L;//世界の人口
//データ型(少数)
float weight = 56;//体重
double pi = 3.14;//円周率
//データ型(真偽値)
boolean isError = true;//trueかfalse
//データ型(文字、文字列)
char initial = F;//イニシャル1文字
String name = Haru;//名前

int m = Math.max(,);//数字を比較して大きい方を代入
int n = Interger.parseInt();文字列を数字に変換
int r = new java.util.Random().nextInt();//乱数を発生(最大①まで)
String s = new java.util.Scanner(System.in).nextLine();キーボードから文字入力
int i = new java.util.Scanner(System.in).nextInt();キーボードから整数入力

分岐

Main.java
//if
if (tenki == true) {
    System.out.println("晴れです");
}else {
    System.out.println("雨です");
}

//switch
int lucky = new java.util.Random().nextInt(3)+1;
System.out.println(lucky);
switch (lucky) {
case 1:
    System.out.println("大吉です");
    break;
case 2:
    System.out.println("吉です");
    break;
case 3:
    System.out.println("凶です");
}

//繰り返し処理の中断 break文
//今回だけ中断して次の周へ continue文

//論理演算子 &&(かつ)、||(または)

繰り返し

Main.java
for (int i = 0; i < 10; i++) {

}

do {
    if (i % 3 == 0) {
        System.out.println(i);
    }
    i++;
} while (i < 100);

配列

Main.java
int[] score;//int型の要素を代入できる配列変数scoreを用意
score = new int[5];int型の要素を5つ作成してscoreに代入

score.length//配列の要素数の取得

int[] scores1 = new int[] {1,2,3};//省略法1
int[] scores2 = {1,2,3};//省略法2

for (要素の型 変数名:配列変数名){//拡張for文
}

//例
int[] moneyList = {1,2,3};
for(int i=0; i < moneyList.length; i++) {
    System.out.println(moneyList[i]);
}
for(int m : moneyList) {
    System.out.println(m);
}

メソッド

Main.java
public static void hello() {//helloメソッド
    System.out.println("こんにちは");
}
hello();//helloメソッドを呼び出す

//例
public static void main(String[] args) {
    introduceOneself();
}
public static void introduceOneself() {
    String name = "はる";
    int age = 6;
    double height = 110.5;
    char zodiac = '午';
    System.out.println(name);
    System.out.println(age);
    System.out.println(height);
    System.out.println(zodiac);
}

//例(オーバーロード)
public static void main(String[] args) {
    String address = "hoge@hogehoho.hoge";
    String text = "メールの本文";
    email(address,text);
}
public static void email(String address, String text) {
    System.out.println(address + "に、以下のメールを送りました");
    System.out.println("件名:無題");
    System.out.println("本文:" + text);
}
public static void email(String title, String address, String text) {
    System.out.println(address + "に、以下のメールを送りました");
    System.out.println("件名:" + title);
    System.out.println("本文:" + text);
}

//例(三角形の面積)
public static void main(String[] args) {
    double triangleArea = calcTriangleArea(10.0,5.0);
    System.out.println(triangleArea);
}
public static double calcTriangleArea(double bottom, double height) {
    double area = bottom * height / 2;
    return area;
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

今日から携わるJava

Javaを勉強して必要なことのまとめ

Eclipseショートカットキー

■ 補完(Ctrl + Space)
(sysout) System.out.println();
(syso) System.out.println();
(main) public static void main(String[] args) {}

■ ローカル変数を宣言(Ctrl + 2 → L)
new ArrayList(); → ArrayList arrayList = new ArrayList();

■ その他のショートカット
ソースコードのフォーマット(Ctrl + Shift + F)
コメントアウト(Ctrl + /)
クイックフィックス(Ctrl + 1)
1行移動、1行コピー(Alt + ↓↑、Ctrl + Alt + ↓↑)
1行削除(Ctrl + D)
メソッドの抽出(Alt + Shift + M)
呼び出し階層を開く(Ctrl + Alt + H)
宣言を開く(Ctrl + クリック)
型を開く(Ctrl + Shift + T)

プログラムの書き方

スッキリわかるJava入門を読んだ自分用のまとめ
https://sukkiri.jp/books/sukkiri_java3

▼クラスブロック+メインブロック

Main.java
public class Main {
    public static void main(String[] args) {

    }
}

メインブロック内の記述

Main.java
System.out.println("こんにちは");こんにちはと表示させる
System.out.println("二重引用符(¥"");「二重引用符(")」と表示させる

int age;//変数宣言(ageという箱を用意)
age = 20;//変数age 代入 20
int age = 20;//変数 ageを20で初期化
age = 32;//変数ageに再代入
final double Pi = 3.14;//定数として円周率を宣言

//:データ型(整数)
byte glasses = 2;//所持するメガネの数
short age = 20;//年齢
int salary = 180000;//給与金額
long worldPeople = 6900000000L;//世界の人口
//データ型(少数)
float weight = 56;//体重
double pi = 3.14;//円周率
//データ型(真偽値)
boolean isError = true;//trueかfalse
//データ型(文字、文字列)
char initial = F;//イニシャル1文字
String name = Haru;//名前

int m = Math.max(,);//数字を比較して大きい方を代入
int n = Interger.parseInt();文字列を数字に変換
int r = new java.util.Random().nextInt();//乱数を発生(最大①まで)
String s = new java.util.Scanner(System.in).nextLine();キーボードから文字入力
int i = new java.util.Scanner(System.in).nextInt();キーボードから整数入力

分岐

Main.java
//if
if (tenki == true) {
    System.out.println("晴れです");
}else {
    System.out.println("雨です");
}

//switch
int lucky = new java.util.Random().nextInt(3)+1;
System.out.println(lucky);
switch (lucky) {
case 1:
    System.out.println("大吉です");
    break;
case 2:
    System.out.println("吉です");
    break;
case 3:
    System.out.println("凶です");
}

//繰り返し処理の中断 break文
//今回だけ中断して次の周へ continue文

//論理演算子 &&(かつ)、||(または)

繰り返し

Main.java
for (int i = 0; i < 10; i++) {
  ブロック  
}

while (i < 100) {
  ブロック
}

do {
    if (i % 3 == 0) {
        System.out.println(i);
    }
    i++;
} while (i < 100);

配列

Main.java
int[] score;//int型の要素を代入できる配列変数scoreを用意
score = new int[5];int型の要素を5つ作成してscoreに代入

score.length//配列の要素数の取得

int[] scores1 = new int[] {1,2,3};//省略法1
int[] scores2 = {1,2,3};//省略法2

for (要素の型 変数名:配列変数名){//拡張for文
}

//例
int[] moneyList = {1,2,3};
for(int i=0; i < moneyList.length; i++) {
    System.out.println(moneyList[i]);
}
for(int m : moneyList) {
    System.out.println(m);
}

メソッド

Main.java
//メソッドの定義
public static 戻り値の型 メソッド名(引数リスト) {
    メソッドが呼び出されたときに実行される具体的な処理
}

public static void hello() {//helloメソッド、voidは戻り値がなしのときに使用
    System.out.println("こんにちは");
}
hello();//helloメソッドを呼び出す

//例
public static void main(String[] args) {
    introduceOneself();
}
public static void introduceOneself() {
    String name = "はる";
    int age = 6;
    double height = 110.5;
    char zodiac = '午';
    System.out.println(name);
    System.out.println(age);
    System.out.println(height);
    System.out.println(zodiac);
}

//例(オーバーロード)
public static void main(String[] args) {
    String address = "hoge@hogehoho.hoge";
    String text = "メールの本文";
    email(address,text);
}
public static void email(String address, String text) {
    System.out.println(address + "に、以下のメールを送りました");
    System.out.println("件名:無題");
    System.out.println("本文:" + text);
}
public static void email(String title, String address, String text) {
    System.out.println(address + "に、以下のメールを送りました");
    System.out.println("件名:" + title);
    System.out.println("本文:" + text);
}

//例(三角形の面積)
public static void main(String[] args) {
    double triangleArea = calcTriangleArea(10.0,5.0);
    System.out.println(triangleArea);
}
public static double calcTriangleArea(double bottom, double height) {
    double area = bottom * height / 2;
    return area;
}

▼参考:メソッドの使い方
https://www.javadrive.jp/start/method/index1.html

複数クラスを用いた開発

Calc.java
public class Calc {
    public static void main(String[] args) {
        int a = 10; int b = 2;
        int total = CalcLogic.tasu(a, b);
        int delta = CalcLogic.hiku(a, b);
        System.out.println("足すと" + total + "、引くと" + delta);
    }
}
CalcLogic.java
public class CalcLogic {
    public static void main(String[] args) {
        public static int tasu(int a, int b) {
            return (a + b);
        }
        public static int hiku(int a, int b) {
            return (a - b);
        }
    }
}

インスタンスとクラス

オブジェクト指向

Main.java
public class Main {
    public static void main(String[] args) {
        Cleric c = new Cleric();
        c.name = "聖職者";
        c.SelfAid();
        c.pray(3);
    }
}
Cleric.java
import java.util.Random;

public class Cleric {
    String name;
    int hp = 50;
    final int MAX_HP = 50;
    int mp = 10;
    final int MAX_MP = 10;

    public void SelfAid() {
        System.out.println(this.name + "は、セルフエイドを唱えた");
        this.hp = this.MAX_HP;
        this.mp -= 5;
        System.out.println("HPが最大まで回復した");
    }

    public int pray(int sec) {
        System.out.println(this.name + "は" + sec + "秒間天に祈った!");

        int recover = new Random().nextInt(3) + sec;

        int recoverActual = Math.min(this.MAX_MP - this.mp, recover);

        this.mp += recoverActual;
        System.out.println("MPが回復した" + recoverActual);
        return recoverActual;
    }
}

様々なクラス機構

コンストラクタの条件
・メソッド名がクラス名と完全に等しい
・メソッド宣言に戻り値が記述されていない(voidもダメ)

public class クラス名 {
    クラス名() {
        自動的に実行する処理
    }
}

コンストラクタの特例
クラスに1つもコンストラクタが定義されていない場合に限って、「引数なし、処理内容なし」のコンストラクタがコンパイル時に自動的に追加される

Thief.java
public class Thief {
    String name;
    int hp;
    int mp;

    //new Thief("ハルタ", 40, 5)でインスタンス化する場合の記述
    public Thief(String name, int hp, int mp) {
        this.name = name;
        this.hp = hp;
        this.mp = mp;
    }
    //new Thief("ハルタ", 40)でインスタンス化する場合の記述(MPは5で初期化)
    public Thief(String name, int hp) {
        this(name, hp, 5);
    }
    //new Thief("ハルタ")でインスタンス化する場合の記述(HPは40,MPは5で初期化)
    public Thief(String name) {
        this(name, 40);
    }

    //new Thief()ではインスタンス化できないようにする(名前の無いThiefは存在させない)
}

上記のThiefクラスを利用したプログラム

Main.java
public class Main {
    public static void heal(int hp) {
        hp += 10;
    }
    public static void heal(Thief thief) {
        thief.hp += 10;
    }
    public static void main(String[] args) {
        int baseHp  = 25;
        Thief t = new Thief("ハルタ", baseHp);
        System.out.println(baseHp + ":" + t.hp);
        heal(baseHp);
        heal(t);
        System.out.println(baseHp + ":" + t.hp);
    }

    //実行結果
//  25:25(引数がint型の場合、変数baseHpの値が引数hpにコピーされる値渡しのため)
//  25:35(引数がクラス型の場合、変数tが示すアドレスが引数thiefにコピーされる参照渡しにより、t.hpとthief.hpはメモリの同じ場所を指すことになるため)
}

継承

Matango.java
//キノコモンスターのクラスを作成
public class Matango {
    int hp = 50;
    char suffix;
    public Matango(char suffix) {
        this.suffix = suffix;
    }
    public void attack(Hero h) {
        System.out.println("キノコの攻撃 " + this.suffix);
        System.out.println("10のダメージ");
        h.hp -= 10;
    }
}
PoisonMatango.java
public class PoisonMatango extends Matango {
    int poisonCount = 5;
    public PoisonMatango(char suffix) {
        super(suffix);
    }
    public void attack(Hero h) {
        super.attack(h);
        if (this.poisonCount > 0) {
            System.out.println("さらに胞子をばらまいた");
            int dmg = h.hp / 5;
            h.hp -= dmg;
            System.out.println(dmg + "ポイントのダメージ");
            this.poisonCount--;
        }
    }
}
Hero.java
public class Hero {
    String name;
    int hp = 150;
    public Hero(String name) {
        this.name = name;
        System.out.println("名前は" + this.name + "、体力は " + this.hp);
    }
}
Main.java
public class Main {
    public static void main(String[] args) {
        Hero h = new Hero("ヒロタ");
        Hero h2 = new Hero("ヒロタ2");

        Matango m1 = new Matango('A');
        m1.appear();
        m1.attack(h);

        PoisonMatango pm1 = new PoisonMatango('A');
        pm1.appear();
        pm1.attack(h2);
    }
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む