Published on

Java基礎

Authors
  • avatar
    Name
    Kikusan
    Twitter

基礎構文

変数・定数宣言

 String str = null;    //変数
 final String STR = "msg";    //定数
 long l = 1111111111L;    //long型は最後にLで宣言
 float f = 1111.111F;    //float型は最後にFで宣言
 double d = 1111.111;    //小数点はdoubleになる
 char c = 'c';    //char型は''で
 str = "a\na\ta";    //\n:改行 \t:タブ
 String[] ss = {"a", "b"}; // 配列 

 //参照型としてintを宣言する
 Integer I = new Integer(32);
 int n = I.intValue();

 Integer I2 = 2;    // auto boxing
 int i = 1;    // auto unboxing
初期値
参照型null
booleanfalse
char\u0000
byte,short,int,long0
float,double0.0

演算子

演算子には優先順位があり、表の上から優先順位が高い。

演算子式の結合始点(右は右辺から実行)
++(後) --(後) (キャスト)
! ++(前) --(前)
new
* / %
+ -
< > <= >= instanceof
== !=
& ^ |
&& ||
?:
*= /= %=

if

 if (条件式) {
     //処理
 } else if (条件式) {
     //処理
 } else {
     //処理
 }

ちなみに を省略することもできて、
その場合は if句と else句の下一行が実行されます。

switch

 String singou = "red";
 switch (singou) {
     case "red":
            //処理
     break;
     case "yellow":
         //処理
     break;
     case "blue":
         //処理
     break;
     default:
         //処理
 }

defaultはどのケースでもないとき実行される。
breakがないとその下がすべて実行される。
使える型は、byte, short, int, char, enum, String

while dowhile

 while (条件式) {
     //処理
 }

 do {
     //処理
 } while (条件式);

条件式が真の間処理が実行される。
処理の中で条件式に使われる変数をインクリメントするのが鉄板。
while と dowhile の違いは判定の場所。

for

 for (int i=0; i<5; i++){
     if (i < 2) {
          continue;    //次のループ
     } else {
          break;       //ループ終了
     }
 }

for(①カウンター宣言; ②条件式; ③カウンタ変数の増減) と書く。
①、②,③はそれぞれ省略ができる。 for(;;); も間違いではない。
※java8ではfor文は実は非推奨なのだそうな、、、
ちなみに、labelを貼ると、その階層まで一気にbreakできる。

 class Nest {
    public static void main(String[] args) {
        int num[][] = {
                        {0, 1, 2},
                        {3, 4, 5},
                        {6, 7, 8}
                      };
        int i = 0;
        label:
        while(true) {
            int j = 0;
            while(j < 3) {
                System.out.print(num[i][j]);
                j++;
                if(i == 1) break label;
            }
            i++;
        }
    }
}

クラス構造

継承関係

 public class Animal {
    //コンストラクタ
    Animal(){
        System.out.println("new Animal created");
    }
    //引数付きコンストラクタ
    Animal(String species){
        System.out.println("new " + species + " created");
    }

    void animalMethod() {
        System.out.println("this is animal method!");
    }

    void characteristic() {
        System.out.println("I'm alive!");
    }
}
 //インターフェース宣言
 interface Breath {
    //メンバ変数は勝手にstatic finalになる
    String breath = "Breathing!";
    //インターフェースのメソッドは本文なし
    void breath(String breath);
}

次にAnimalクラスとインターフェースを実装した子クラス。

 //継承とインターフェース実装
 public class Dog extends Animal implements Breath{
    //コンストラクタは暗黙的に super() されるが、
    //明示することで親の引数付きコンストラクタを指定できる。
    Dog(){
        super("Dog");
    }

    //オーバーライド
    //インターフェースの抽象メソッドはオーバーライドしないとエラー。
    @Override
    public void breath(String breath) {
        System.out.println(breath);
    }

    //オーバーライド
    @Override
    public void characteristic() {
        System.out.println("I have 4 legs!");
    }
 }
 public class Main {

    public static void main(String[] args) {

        Dog dog = new Dog();        //①new Dog created

        dog.animalMethod();         //②this is animal method!

        //インターフェースのメンバ変数はすべてstatic final
        dog.breath(Breath.breath); //③Breathing!

        dog.characteristic();      //④I have 4 legs!

        Animal dogA = new Dog();  //⑤new Dog created

        //dogA.breath();          //⑥

        dogA.characteristic();    //⑦I have 4 legs!
    }
 }
  • ①はDogインスタンスの生成で、Dogクラスのコンストラクタが呼ばれている。
  • ②は継承されたメソッド。
  • ③はインターフェースのDogクラスでオーバーライドしたメソッド。
  • ④はAnimalクラスのDogクラスでオーバーライドしたメソッド。
  • ⑤はDogインスタンスをAnimal型変数に入れている。(逆はできない)
  • ⑥はAnimal型なので、breathは実装されておらず使用できないの意。
  • ⑦はAnimal型だがnewしたのはDogで、アップキャストされてもDogのcharacteristicになる。

返り値とオーバーロード

返り値を返すメソッドは宣言に返す型を頭につけ、return句を必ず書く。
オーバーロードは引数の数または型が違う、同じ名前のメソッドを作ること。

 class Calc {
    int sum(int a, int b) {
        return a + b;
    }
    //オーバーロード
    int sum(int a, int b, int c) {
        return a + b + c;
    }
 }

可変長引数メソッド

void variableLength(String... colors){
        for (String c : colors) {
            System.out.println(c);
        }
    }

ネストしたクラス

インナークラスやローカルクラスといった、情報隠蔽のためのクラス。

publicprotectedprivateabstractfinalstatic
インナークラス✖️
staticインナークラス
ローカルクラス✖️✖️✖️✖️
匿名クラス✖️✖️✖️✖️✖️✖️
/**
 * アウタークラス
 * インナークラスとは相互にprivateなメンバでもアクセスできる
 */
public class Outer {

    private String msg = "hello from outer!";
    private static String stMsg = "hello static from outer!";

    public static void main(String[] args) {
        Outer out = new Outer();
        // インナークラスのコンストラクタはアウタークラスのインスタンスが必要
        Inner in = out.new Inner();
        // staticインナークラスのコンストラクタ Comparatorなどの実装クラスを定義しておくときとかに使える
        StaticInner sIn = new StaticInner();
        in.inner(); // hello from outer! this is inner
        sIn.staticInner(); // hello static from outer! this is static inner
        StaticInner.staticInStatic(); // this is static in static
        out.test(); // hello from outer! this is no name inner
                    // this is no name
                    // this is local
    }
    /**
     * インナークラス
     * アウタークラスのメンバにはアクセスできる
     */
    public class Inner {
        private String inMsg = " this is inner";
        void inner() {
            System.out.println(msg + inMsg);
        }
    }

    /**
     * static インナークラス
     * インナークラスにはstaticをつけることができ、普通にコンストラクタを呼べる。
     */
    static class StaticInner {
        void staticInner() {
            // アウターのクラス変数なら使える
            System.out.println(stMsg + " this is static inner");
        }

        static void staticInStatic() {
            System.out.println("this is static in static");
        }
    }

    /**
     * 関数内のクラスをローカルクラス、無名クラスという
     */
    void test() {
        /*
         * 無名クラス
         * インターフェイスでもクラスでも、その型の実装インスタンスを生成する。
         */
        Inner i = new Inner() {
            void noName() {
                System.out.println("this is no name");
            }

            @Override
            void inner() {
                System.out.println(msg + " this is no name inner");
                noName(); // 定義したものは宣言の中でだけ使える(Innerの定義にはnoNameは含まれないため)
            }

        };
        i.inner();


        /**
         * ローカルクラス
         */
        class Local {
            void local() {
                System.out.println("this is local");
            }
        }

        Local l = new Local();
        l.local();

    }

}

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

抽象クラスは子クラスと、is-a関係である。
インターフェースは子クラスと、can-do関係である。

抽象クラスインターフェース
アクセス修飾子public, protectedpublic
変数の定義インスタンス変数,ローカル変数,クラス変数public static finalのクラス変数
継承多重継承不可多重継承可
メソッド定義具体的なメソッド,抽象メソッドで子に実装を強制メソッドの型のみ,defaultメソッドには処理を書ける。,staticメソッドもかける。
コンストラクタ,イニシャライザ実装可実装不可
abstract class Animal {
    String name;
 
    Animal(String name){
        this.name = name;
    }
    //具体的な処理を書ける。
    void sleep(){
        System.out.println("sleeping!");
    }
    //abstractメソッドは子クラスに実装を強制
    abstract void speak();
}
 
public interface MyInterface {
    /**
     * java8から、interface でもstatic関数は実装できる
     * ただし呼び出すには必ず、Static.staticFunc()とし、
     * 実装クラス.staticFunc()では呼び出せないし、オーバーライドもできない。
     * (インスタンスの挙動を決めるのがオーバーライドだから当たり前)
     */
    static void staticFunc() {
        System.out.println("this is static func");
    }

    /**
     * default句で実装可能
     * なお、スーパークラスを持つクラスにdefault句を持つインターフェイスを継承し、
     * 同じシグニチャを多重継承してしまった場合、スーパークラスが優先される
     */
    default void defaultFunc() {
        System.out.println(add("this is", " default func"));
    }

    /**
     * privateなinterface関数は実装が可能(default関数で使用するため)
     */
    private String add(String a, String b) {
        return a + b;
    }
}

enum型

 //列挙型 プログラム上では一つのクラスとして扱われる
 enum Output {
    OK, NG,
 }
 
 public class Enum {
 
    public static void main(String[] args) {
        Output out;
 
        out = Output.NG;
 
        switch (out) {
        case OK:
            System.out.println("OK!");
            System.out.println(out.ordinal()); // 0
            break;
        case NG:
            System.out.println("NG!");
            System.out.println(out.ordinal()); // 1
            break;
        }
    }
 }

アクセス修飾子とstatic

アクセス修飾子

javaのアクセス修飾子は4つ。

アクセス修飾子同一クラス同一パッケージサブクラスすべて
public
protected
デフォルト
private

これらを組み合わせることで データ隠蔽・カプセル化 ができる。

  • データ隠蔽:メンバ(属性や操作)を公開と非公開に分けて、外部から属性への直接アクセスを避ける。
  • カプセル化:オブジェクト内に属性とその属性にアクセスする操作を一つにまとめて持たせること。
 public class Parser {
 
    private String str = null;
 
    public String getStr() {
        return str;
    }
    private void setStr(String param) {
        str = param;
    }
 }

static

変数やメソッドに static を付けることで、クラスフィールド、クラスメソッドの実装ができる。

class StaticClass {
 
    //staticメンバ
    static String staticStr;
    static String getStaticStr(){
        return staticStr;
    }
    //インスタンスメンバ
    String instanceStr;
    String getInstatnceStr() {
        return instanceStr;
    }
 
    //staticイニシャライザ
    static {
        StaticClass.staticStr = "staticStr";
    }
    //インスタンスイニシャライザ
    {
        instanceStr = "instanceStr";
    }
 }
 
 class Main {
 
    public static void main(String[] args) {
 
        //static参照
        StaticClass.staticStr = StaticClass.staticStr + "Main";
        System.out.println(StaticClass.getStaticStr());  //staticStrMain
 
        //インスタンス参照
        StaticClass stsCls = new StaticClass();
        stsCls.instanceStr = stsCls.instanceStr + "Main";
        System.out.println(stsCls.instanceStr);  //instanceStrMain
    }
 }

イニシャライザは変数の初期化ができる。
ちなみに、イニシャライザはインスタンス生成の前、コンストラクタはインスタンス生成の後に実行される。
静的要素(staticメンバ)はアプリケーションが停止するまで保持されるのに対し、
インスタンスメンバはインスタンスが殺されるまでしか保持されない。

コレクション

コレクションは型を決めて、その型の値を連続して持つクラス。
List、Map、Set, Queue, Dequeがあり、それぞれ特徴がある。
(クラスはこの3つの下にあり、List,Map,Setはインターフェース)

List

順番がある。
ArrayListとLinkedListがある。
ArrayList: 検索がはやい。
LinkedList: 追加削除がはやい。

List<Integer> list = new ArrayList<>();
 
list.add(10);
list.add(20);
System.out.println(list.get(1));  //20

Set

値の重複を許さない。
HashSetとTreeSet、LinkedHashSetがある。
HashSet: 順番がない。
TreeSet: 値順にソートされる。
LinkedHashSet: 追加された順にソートされる。

Set<Integer> hashSet = new HashSet<Integer>();
 
hashSet.add(10);
hashSet.add(20);
hashSet.add(10);
System.out.println(hashSet.size());  //2

Map

keyと値を持つ。

HashMapとTreeMap、LinkedHashMapがある。
HashMap: 順番なし。
TreeMap: keyの順番にソート。
LinkedHashMap: 追加順にソート。

Map <String, Integer> hashMap = new HashMap<>();
 
hashMap.put("sato", 50);
hashMap.put("ito", 60);
 
System.out.println(hashMap.get("sato"));  //50

Queue

FIFOでデータを扱う。

Queue<String> queue = new ArrayDeque<>();
queue.add("A");
queue.add("B");

System.out.println(queue.poll()); // A

Deque

FIFOに加えてLIFOでもデータを扱える。

Deque<String> deque = new ArrayDeque<>();
deque.addFirst("A");
deque.addLast("B");

System.out.println(deque.pollLast()); // B

拡張for文

for(中身の型 中身の変数名 : 配列及びコレクション)
配列及びコレクションにすべての中身に処理を行うことができる。

上のコレクションを使ってみるとこうなる。

for (Integer data : list) {
    System.out.println(data);  //10, 20
}
 
//Map.Entry<K,V>はMapの中身の型(インターフェース)
//hashMap.entrySet()の戻り値の型はSet<Map.Entry<K,​V>>
for (Map.Entry<String, Integer> score : hashMap.entrySet()) {
    System.out.println(score.getKey() + " : " + score.getValue());
    //ito : 60
    //sato : 50
}

例外処理

例外処理はExceptionを使う。
例外を発生させ、catchし、加えて独自例外を発生させてみる。

public class ExceptionSample {
 
    public static void main(String[] args) {
 
        try {
 
            //ここでNumberFormatException発生
            String s = "No.1";
            System.out.println(Integer.parseInt(s));
 
        } catch (ArithmeticException e) {
 
            System.err.println("ArithmeticException: " + e.getMessage());
 
        //NumberFormatExceptionはArithmeticExceptionのサブクラスではないので
        //Exceptionにcatchされる
        } catch (Exception e) {
 
            //Messageを得るときはgetMessage
            System.err.println("Exception: " + e.getMessage());
 
        } finally {
            try {
                //MyExceptionをthrowsさせる
                getErr();
 
            //MyExceptionをcatch
            }catch(MyException e){
                System.err.println(e.getMessage());
            }
        }
    }
 
    //自分でcatchしないときは呼び出しもとにExceptionを返す。
    static void getErr() throws MyException {
        throw new MyException("MyException!");
    }
}
 
//独自Exceptionの定義はextendsで
//コンストラクタで設定できる。
class MyException extends Exception {
    public MyException(String s) {
        super(s);
    }
}

・出力

 Exception: For input string: "No.1"
 MyException!

マルチキャッチ

try {
  // 通常処理
} catch (AException | BException e) {
  // 例外処理
}

チェック例外

  • チェック例外: ソースに誤りがなくても発生する例外。catchの必要あり。
  • 非チェック例外: ソースの誤り、致命的エラーによる例外。catchの必要なし。
    (ソースの書き方で事前に例外を回避できるためor致命的なものはソースの問題ではないため)
クラス説明
Throwable全てのエラーと例外のスーパークラス。throwができるようになる
Errorアプリケーションがキャッチすべきでない重大な問題。そのため非チェック例外となる
Exceptionアプリケーションでキャッチされる可能性のある例外
RuntimeException非チェック例外。この他の例外はチェック例外

assert

java -ea Mainのように-eaをつけると仕込んでおいたassert文を実行することができる。

// 条件式がtrueならば、AssertionErrorを投げず、
// 条件式がfalseならば、与えられたメッセージを持つAssertionErrorが投げられる。
assert name != null : "name is null";
public class Main {
    public static void main(String[] args) {
        Item a = new Item("a", 100);
        Item b = new Item("b", 120);
        Item c = new Item("c", 80);

        List<Item> list = Arrays.asList(a, b, c);

        Collections.sort(list);
        list.forEach(System.out::println);
        // c $80
        // a $100
        // b $120
    }
}

try-with-resource

closeが求められていたオブジェクトを勝手に閉じてくれるようになった。

package modern.java;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;

public class With {
    public static void main(String[] args) {
        // try-with-resources構文を使うことでclose省略
        try(
                FileInputStream fi = new FileInputStream("file.csv");
                InputStreamReader is = new InputStreamReader(fi);
                BufferedReader br = new BufferedReader(is);
        ) {
            String line;
            int i = 0;

            while ((line = br.readLine()) != null) {
                if (i == 0) {
                    System.out.println(line);
                } else {
                    Person p = new Person();
                    String[] row = line.split(",");
                        p.setId(row[0]);
                        p.setName(row[1]);

                        // 処理

                        System.out.println(p.getId() + " " + p.getName());

                    System.out.print("\n");
                    i++;
                }
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

AutoCloseableもしくはCloseableの実装がされたものに使える。
発生するExceptionは、
AutoCloseable → Exceptionなのに対し、
Closeable → IOException。

Format

Date

LocalDateTimeはJava8から導入。
DateクラスやCalendarクラスは標準API。

public static void main(String[] args) {
 
    //LocalDateTimeの基礎
    LocalDateTime d = LocalDateTime.now();
    System.out.println(d.getYear());
    System.out.println(d.getMonth());
    System.out.println(d.getDayOfMonth());
    System.out.println(d.getHour());
    System.out.println(d.getMinute());
    System.out.println(d.getSecond());
    System.out.println(d.getNano());
 
    //特定日時を指定
    d = LocalDateTime.of(2015, 12, 15, 23, 30, 59);
    System.out.println(d.plusDays(20));       //2016-01-04T23:30:59
    System.out.println(d.minusDays(20));      //2015-11-25T23:30:59
    System.out.println(d.withDayOfMonth(20)); //2015-12-20T23:30:59
 
    //時刻の切り捨て
    LocalDateTime.of(2015, 12, 15, 23, 30, 59).truncatedTo(ChronoUnit.HOURS); // 2015-12-15T23:00
 
    //来月1日の12時
    d =
        LocalDateTime.now()
        .plusMonths(1)
        .withDayOfMonth(1)
        .withHour(12)
        .truncatedTo(ChronoUnit.HOURS);
 
    //文字列へ変換
    DateTimeFormatter f = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
    d = LocalDateTime.parse("2015/12/15 23:30:59", f);
    System.out.println(d.format(f));    //2015/12/15 23:30:59
    f = DateTimeFormatter.ofPattern("yyyy/MM/dd");
    System.out.println(d.format(f));    //2015/12/15
}

Number

ロケールに従って、通貨記号などをフォーマットできる。

NumberFormat a = NumberFormat.getInstance();
NumberFormat b = NumberFormat.getIntegerInstance();
NumberFormat c = NumberFormat.getCurrencyInstance();
NumberFormat d = NumberFormat.getPercentInstance();

double num = 1000.1;

System.out.println(a.format(num)); // 1,000.1
System.out.println(b.format(num)); // 1,000
System.out.println(c.format(num)); // ¥1,000
System.out.println(d.format(num)); // 100,010%

Optional

Java8からnullチェックを簡潔に、可読性高く行えるようになった。

package modern.java;

import java.util.Arrays;
import java.util.List;
import java.util.Optional;

public class Option {
    public static void main(String[] args) {
        Optional<Integer> optionalValue = IntegerList.get(2);
        //optionalをorElseでnullのときの代替値にできる
        Integer i = optionalValue.orElse(-1) + 100;

        System.out.println(i);
    }
}

/**
 * OptionalはNullになりえる値をラップして返すのに使える
 * Nullチェックを簡潔にし、Nullがあり得ることを明示できる
 */
class IntegerList {
    private final static List<Integer> INTEGER_LIST = Arrays.asList(new Integer[]{1,2,null});

    public static Optional<Integer> get(int index) {
        //ラップして返す
        return Optional.ofNullable(INTEGER_LIST.get(index));
    }
}

関数型インターフェースとラムダ式、StreamAPI

関数型インタフェース

インタフェース戻り値型メソッド
Supplier<T>Tget()
Consumer<T>voidaccept(T)
BiConsumer<T,U>voidaccept(T,U)
Predicate<T>booleantest(T)
BiPredicate<T,U>booleantest(T,U)
Function<T,R>Rapply(T)
BiFunction<T,U,R>Rapply(T,U)
UnaryOperator<T>Tapply(T)
BinaryOperator<T>Tapply(T,T)
Runnablevoidrun()
Callable<V>Vcall()

StreamAPI

中間操作

メソッド内容
distinct重複を除く(equalsで判定)
filterT -> booleanに一致する要素だけのStreamを返す
limitn個に切り詰める
map各要素にT -> Uを反映したStreamを返す
peekそのままStreamを返しつつ、forEachができる
skip最初のn個を破棄したStreamを返す
sortedComparatorの条件で並び替えができる

終端処理

メソッド内容
allMatch全ての要素がT -> booleanに一致するかを返す
anyMatchいずれかの要素がT -> booleanに一致するかを返す
collectコレクションにまとめる
count要素数を返す
findAny最初に処理した要素を返す(Optionalの中身は基本先頭だが、並列時にランダムになる)
findFirst先頭の要素を返す(ない場合空のOptional)
forEach全ての要素にT -> void を行う
maxComparatorの条件で最大の要素を返す
minComparatorの条件で最小の要素を返す
noneMatch全ての要素がT -> booleanを満たさないかを返す
reduce(T, T) -> Tを受け取り、逐次結合していく
toArray配列にまとめる

プリミティブ型のStream

プリミティブ型のStreamはIntStreamのように独自のものを使用する。
IntStreamのスーパーインタフェースはBaseStreamであり、Streamではない。

並列ストリーム

Collection.parallelStreamを使用することで、並列処理を使用できる。
要素の並びは実行順に関係ない。並びに準じることもできるけど、パフォーマンス的に意味ない。
副作用がある場合は、synchronizedで同期処理が必要。

副作用のある処理(ステートフル)

ラムダ式の中では、finalな外部オブジェクトのみが使える。
なお、外部オブジェクトが持っている値は変更してもコンパイルエラーにはならない。
↑のような操作を副作用のある処理という。
これは基本避けるべきで、理由は

  • 関数型インタフェースは遅延実行なので、渡した後いつ実行されるかわからないから。
  • 関数型インタフェースに切り出して再利用ができないから

Collectors

https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/util/stream/Collectors.html

 // Accumulate names into a List
 List<String> list = people.stream()
   .map(Person::getName)
   .collect(Collectors.toList());

 // Accumulate names into a TreeSet
 Set<String> set = people.stream()
   .map(Person::getName)
   .collect(Collectors.toCollection(TreeSet::new));

 // Convert elements to strings and concatenate them, separated by commas
 String joined = things.stream()
   .map(Object::toString)
   .collect(Collectors.joining(", "));

 // Compute sum of salaries of employee
 int total = employees.stream()
   .collect(Collectors.summingInt(Employee::getSalary));

 // Group employees by department
 Map<Department, List<Employee>> byDept = employees.stream()
   .collect(Collectors.groupingBy(Employee::getDepartment));

 // Compute sum of salaries by department
 Map<Department, Integer> totalByDept = employees.stream()
   .collect(Collectors.groupingBy(Employee::getDepartment,
                                  Collectors.summingInt(Employee::getSalary)));

 // Partition students into passing and failing
 Map<Boolean, List<Student>> passingFailing = students.stream()
   .collect(Collectors.partitioningBy(s -> s.getGrade() >= PASS_THRESHOLD));

使用例

package modern.java;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

/**
 * StreamAPIとラムダ式を理解する
 * ラムダとは概念的に関数をJavaが表現できるようにしてくれたもの
 * (関数=object)
 * Stream操作
 * 1. Stream生成
 * 2. 中間操作 Streamを返す。
 * 3. 終端操作 コレクションや値を返す。
 * ラムダ式はすべて終端操作が実行される時点で実行される(遅延評価)
 * 終端操作メソッドを行うとStreamオブジェクトはクローズされる(使えなくなる)
 */
public class StreamAPI {
    public static void main(String[] args) {
        System.out.println("*無名クラスDEMO*********");
        anonymousClassDEMO();
        System.out.println("*ラムダDEMO*************");
        lambdaDEMO();
        System.out.println("*ConsumerDEMO***********");
        consumerDEMO();
        System.out.println("*SupplierDEMO***********");
        supplierDEMO();
        System.out.println("*PredicateDEMO**********");
        predicateDEMO();
        System.out.println("*FunctionDEMO***********");
        functionDEMO();
        System.out.println("*終端処理DEMO***********");
        streamTerminal();
        System.out.println("*中間処理DEMO***********");
        streamIntermediate();

    }

    /**
     * Funcインターフェースを実装した無名クラスをローカルクラスとして宣言
     */
    static void anonymousClassDEMO() {
        Func f = new Func() {
            public int calc(int x, int y) {
                return x + y;
            }
        };
        System.out.println(f.calc(1, 2));
    }

    /**
     * 無名クラスをラムダ式で書く
     * 左辺から型推論で型を判定、関数型インターフェースなので式まで決定。
     */
    static void lambdaDEMO() {
        Func f = (x, y) -> x + y;
        System.out.println(f.calc(1, 2));
    }

    /**
     * Streamオブジェクトの生成
     * データの集合をラムダ式で処理するためのオブジェクト
     */
    static Stream<String> streamCreate1() {
        List<String> list = Arrays.asList("A", "B", "C", "D");
        Stream<String> stream = list.stream();
        //Filesクラスから行を持つStreamもツクレル
        //直接作るには Stream<String> stream = Stream.of("A", "B");
        return stream;
    }

    static IntStream streamCreate2() {
        int[] arr1 = { 1, 2, 3, 4, 5, 1 };
        IntStream stream = Arrays.stream(arr1);
        //LongStream DoubleStreamもある
        return stream;
    }

    /**
     * 終端操作
     */
    static void streamTerminal() {
        List<String> list = Arrays.asList("A","BC","DEF","GH","I", "A");
        // countはlongで個数を返す
        System.out.println(list.stream().count());
        // min, maxはComparatorを受け取り、次要素を比較し、与えられた式でmax, minを返す
        // 文字数の小さいものは小さいという式
        String max1 = list.stream().max(
                (o1,o2) -> o1.length() - o2.length()
                ).orElse("最大値なし");
        String min1 = list.stream().min(
                (o1,o2) -> o1.length() - o2.length()
                ).orElse("最小値なし");
        System.out.println(max1);
        System.out.println(min1);

        // 戻り型のOptionalはほしい戻り値をラップした型
        // orElseメソッドで中身をとりだせ、nullである場合の戻り値を書ける。(orElseは空のOptionalかを判定している)
        List<String> list2 = Arrays.asList();
        String max2 = list2.stream().max(
                (o1,o2) -> o1.length() - o2.length()
                ).orElse("最大値なし");
        System.out.println(max2);

        int[] arr = {1,2,3,4,5};
        // sumは合計
        System.out.println(Arrays.stream(arr).sum());
        // averageは平均(戻り値OptionalDouble)
        System.out.println(Arrays.stream(arr).average().orElse(0.0));

        // reduceは全てに処理を行う
        //引数1つで戻り値Optional
        Optional<String> optional = list.stream().reduce((x, y) -> x + y);
        System.out.println(optional.orElse("null"));
        //引数2つで戻り値<T> 第一引数は初期値
        String str = list.stream().reduce("Z", (x, y) -> x + y);
        System.out.println(str);

        //<R> r collect(Supplier<R> s, BiConsumer<R,? super T>, a, BiCumsumer<R,R> c)
        // 可変オブジェクトにStream内の要素を集める
        ArrayList<String> list3 =
                list.stream()
                .collect(
                        // Supplierを生成
                        () -> new ArrayList<String>(),
                        // 個々のデータをどう集めるか
                        (l, s) -> l.add(s),
                        // 複数の可変コンテナをどう結合するか(並列処理のため)
                        (l1, l2) -> l1.addAll(l2)
                        );
        list3.forEach(System.out::println);

        // <R,A> R collect(Collector<<? super T, A, R> c) Collectorクラスで簡潔に書ける
        Set<String> set = list.stream().collect(Collectors.toSet());
        set.stream().forEach(System.out::println);
    }

    /**
     * 中間操作
     */
    static void streamIntermediate() {
        List<String> list = Arrays.asList("KLMN", "ABc", "dE", "FGH", "iJ");
        //filterはPredicateを引数にとり、式がTrueのものを集めたStreamを返す。
        list.stream()
            .filter(s -> s.length() >= 3)
            .forEach(System.out::println);

        //mapはFunctionを引数にとり、データを加工してStreamを返す。
        list.stream()
            .map(s -> s.toUpperCase())
            .forEach(System.out::println);
        //mapで型変換
        IntStream iStream =
            list.stream()
                .mapToInt(s -> s.length());
        iStream.forEach(System.out::println);

        //sortedは<T>がComparableなら自然順序でソートしたStreamを返す。
        list.stream()
            .sorted()
            .forEach(System.out::println);
        //Conparatorを引数に入れて、次の要素と比較するソート順もかける
        list.stream()
            .sorted((s1,s2) -> s1.length() - s2.length())
            .forEach(System.out::println);

        Stream<Person> stream = Stream.of(
                new Person("1", "taro"),
                new Person("2", "jiro"),
                new Person("3", "saburo")
                );
        //Comparatorを引数に入れることで<T>のなにを基準にするかを書ける
        stream.sorted(
                    Comparator.comparing(Person::getName) //.reversed() で逆順
             ).forEach(System.out::println);
    }

    /**
     * メソッド参照 class::method
     * 引数なしで書ける
     */
    static void methodRef() {
        List<Integer> l = Arrays.asList(1, 2, 3);
        //l.forEach(i -> System.out.print(i));
        l.forEach(System.out::print);
    }

    /**
     * コンストラクタ参照 class::new
     * オブジェクトを関数型インターフェースに渡せる
     */
    static void constructorRef() {
        Supplier<ArrayList<Integer>> s = ArrayList::new;
        List<Integer> l = s.get();
    }

    /*********************************************
     * java.util.functionパッケージ
     ********************************************/

    /**
     * Consumer<T>.accept(T t)
     * T型を受け取り何かを行う(戻り値なし)
     */
    static void consumerDEMO() {
        List<String> list = Arrays.asList("A", "B", "C");
        //forEachはストリームオブジェクトが持つ要素すべてになんらかの処理を行う
        list.stream().forEach(System.out::println);
        //Tが決まっている IntConsumer, LongConsumer, DoubleConsumerもある
        //2つ引数を受け取れるBiConsumerもある
        //参照型をプリミティブ型を受け取る ObjIntConsumer, ObjLongConsumer, ObjDoubleConsumerもある
    }

    /**
     * Supplier<T>.get()
     * 引数なしでT型の値を返す。
     */
    static void supplierDEMO() {
        Stream
                // generate()がSupplierを求めているので、ラムダ式にできる
                .generate(() -> Math.round(Math.random()))
                //generateは無限にT型(ここではroundの戻り値)の値を生成して保持するので、limitで終わらせる
                .limit(10)
                .forEach(System.out::print);
        System.out.print("\n");
        //Tが決まっている BooleanSupplier, IntSupplier, LongSupplier, DoubleSupplierもある
    }

    /**
     * Predicate<T>.test(T t)
     * T型を受け取りbooleanで返す
     */
    static void predicateDEMO() {
        List<String> list = Arrays.asList("ABC", "DE", "EGHI");
        // TはString 文字が3文字かを判定する
        Predicate<String> p = s -> s.length() >= 3;
        // Streamすべての要素でtest
        boolean all = list.stream().allMatch(p);
        boolean any = list.stream().anyMatch(p);
        boolean none = list.stream().noneMatch(p);
        System.out.println(all);
        System.out.println(any);
        System.out.println(none);
        // Tが決まっている IntPredicate, LongPredicate, DoublePredicateもある
        // 2つの引数を受け取る BiPredicateもある
    }

    /**
     * Function<T,R>.apply(T t)
     * T型で受け取った引数を処理し、R型の値を返す
     */
    static void functionDEMO() {
        /**
         * 〇T型のみ決まっている
         *      IntFunction<R>, LongFunction<R>, DoubleFunction<R>
         * 〇R型のみ決まっている
         *      ToIntFunction<T>, ToLongFunction<T>, ToDoubleFunction<T>
         * 〇T・R型が決まっている
         *      IntToLongFucntion, IntToDoubleFunction, LongToIntFunction, DoubleToIntFunction, DoubleToLongFunction
         * 〇二つの引数を受け取る Rは戻り型
         *      BiFunction<T,U,R>, ToIntBiFunction<T,U>, ToLongBiFunction<T,U>, ToDoubleBiFunction<T,U>
         * 〇受け取りと戻り値の型が同じとき
         *      UnaryOperator<T>
         * 〇二つの引数を受け取り同じ型を返す <T,T,T>
         *      BinaryOperator<T>, IntBinaryOperator, LongBinaryOperator, DoubleBinaryOperator
         */

        // iterateはUnaryOperatorを第二引数にとり,第一引数を初期値として繰り返したStreamを返す
        Stream.iterate(1, x -> x * 10)
            .limit(5)
            .forEach(System.out::println);
    }
}

/**
 * Functionパッケージはこうなっている
 * FunctionalInterfaceは1メソッドのインターフェースを厳密に判定
 */
@FunctionalInterface
interface Func {
    int calc(int x, int y);
}

ラムダ式の省略

一定条件を満たすと、いろいろと省略ができる。

//基本形
Predicate javaChecker = (String s) -> { return s.equals("Java"); };
//引数一つのとき、型省略
Predicate javaChecker = (       s) -> { return s.equals("Java"); };
//型省略したとき、()も省略
Predicate javaChecker =         s  -> { return s.equals("Java"); };
//一行かつreturnがないとき、{}省略
Consumer buyer =         goods     ->   System.out.println(goods + "を購入しました");
//returnも{}と同時省略
Predicate javaChecker =         s  ->          s.equals("Java");

マルチスレッド

  • 並行処理: シングルコアで並列化。計算自体が速くなるわけではない。
  • 並列処理: マルチコアで並列化。コアが多いので計算自体が速くなる。
  • スレッドセーフ: 並列処理でも競合・デッドロック・ライブロックが起きないクラス

Thread

スレッドを作成できる。
上のソースでmainが先か、subが先かはスレッド作成処理のスピード次第

public class SampleThread extends Thread {
    @Override
    public void run() {
        System.out.println("sub");
    }
}
public class Main {
    public static void main(String[] args) {
        Thread t = new SampleThread();
        t.start(); // 新スレッドを作成

        System.out.println("main");

    }
}

Runnable

ThreadにはコンストラクタでRunnableを渡すことでrun()の実装をすることができる。

public class Main {
    public static void main(String[] args) {
        /*
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("sub");
            }
        });
        以上の実装をRunnableが関数型インタフェースなのを利用して↓で書ける
        */
        Thread t = new Thread(() -> {
          System.out.println("sub");
        });
        t.start();
        System.out.println("main");
    }
}

Executorフレームワーク

スレッドプールを使用して効率的に並行処理を実現するインタフェースとクラス群
ExecutorServiceもしくはScheduledExecutorServiceを実装する。

ExecutorService

public class Main {
    public static void main(String[] args) {
        // シングルスレッドを生成
        // 固定数の場合,Executors.newFixedThreadPool(3)など
        ExecutorService exec = Executors.newSingleThreadExecutor();
        for (int i = 0; i < 5; i++) {
            exec.submit(() -> {
                System.out.println(Thread.currentThread().getId());
            });
        }
        
        System.out.println(Thread.currentThread().getId());
    }
}
// 1
// 13
// 13
// 13
// 13
// 13

// ↑execのスレッドプールには1つしかスレッドがないからIDは統一
public class Main {
    public static void main(String[] args) {
       // 必要に応じてスレッドを増減
        ExecutorService exec = Executors.newCachedThreadPool();
        for (int i = 0; i < 5; i++) {
            exec.submit(() -> {
                System.out.println(Thread.currentThread().getId());
            });
        }
        exec.shutdown();
        System.out.println(Thread.currentThread().getId());
    }
}
// 13
// 15
// 14
// 16
// 17
// 1
// 必要に応じてスレッドを増減させている
// 60秒未満であればスレッドは再利用され、それ以上は破棄される新しくスレッドが作成される。

ScheduledExecutorService

  • 遅延実行
public class Main {
    public static void main(String[] args) {
        ScheduledExecutorService exec = Executors.newSingleThreadScheduledExecutor();
        System.out.println(Thread.currentThread().getId());
        // 3秒後に別スレッドで発火
        // 繰り返し実行させる場合はscheduledAtFixedRate
        // ↑起動時間タイミング毎のインターバルを指定できるが、前の処理が長引くとその分遅延する
        // 処理時間に関係なくインターバルを一定にしたい場合はscheduleWithFixedDelay
        exec.schedule(
                () -> {
                    System.out.println(Thread.currentThread().getId());
                    System.out.println("finish");
                    exec.shutdown();
                }, // 実行処理
          3L, // 遅延させる時間
                TimeUnit.SECONDS); // TimeUnit単位
    }
}

Future

(Scheduled)ExecutorServiceにタスクを登録した戻り値として受け取り、
スレッドの結果を知ることができる。

public class Main {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService exec = Executors.newSingleThreadExecutor();
        // Future<戻り値の型>で結果を受け取る
        Future<String> future = exec.submit(() -> {
            try {
                System.out.println("start");
                Thread.sleep(5000);
                System.out.println("end");
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }, "finish"); // 第二引数で固定の戻り値を設定できる
        exec.shutdown();

        Thread.sleep(5000);
        System.out.println("main start");
        // .get()で結果を受け取る
        // 実行自体はsubmitの段階で別スレッドで開始しているが、結果が受け取れるまでmainスレッドで待つ
        String result = future.get();
        System.out.println("main finish");
        System.out.println(result);
        // start
        // main start
        // end
        // main finish
        // finish
    }
}

Callable

処理結果を返したり例外をスローするマルチスレッドタスク定義のための関数型インタフェース

Runnableでは戻り値を返すことができないので、そのためのクラス。

    public static void main(String[] args) {
        ExecutorService exec = Executors.newFixedThreadPool(2);

        List<Future<Boolean>> futures = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            // Callableをsubmitに渡す
            futures.add(exec.submit(() -> Thread.currentThread().getId() % 2 == 0));
        }

        exec.shutdown();
        System.out.println("start");
        futures.forEach(future -> {
            try {
                System.out.println(future.get());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        });
    }
public class Main {
    public static void main(String[] args) {
        ExecutorService exec = Executors.newFixedThreadPool(2);

        List<Future<Boolean>> futures = new ArrayList<>();
        for (int i = 0; i < 2; i++) {
            // Callableをsubmitに渡す
            futures.add(exec.submit(() -> {
                if (Thread.currentThread().getId() % 2 == 0) {
                    throw new Exception("My Exception");
                }
                return true;
            }));
        }

        exec.shutdown();
        System.out.println("start");
        futures.forEach(future -> {
            try {
                System.out.println(future.get());
            } catch (InterruptedException e) {
                e.printStackTrace();
            // 別スレッドのExceptionはExecutionExceptionとして投げられる
            } catch (ExecutionException e) {
                var e2 = e.getCause();
                System.out.println(e2.getClass().toString()); // class java.lang.Exception
                System.out.println(e.getMessage()); // java.lang.Exception: My Exception
            }
        });
    }
}

CyclicBarrier

複数スレッドの同期化処理

/**
 * CyclicBarrierを受け取れるRunnable実装
 */
public class Task implements Runnable {
    private CyclicBarrier barrier;

    public Task(CyclicBarrier barrier) {
        super();
        this.barrier = barrier;
    }

    @Override
    public void run() {
        long id = Thread.currentThread().getId();
        System.out.println("START:" + id);

        int r = new Random().nextInt(10);

        try {
            Thread.sleep(r * 100);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("END:" + id);
        try {
            this.barrier.await();  // barrierによって他の処理を待つ。
        } catch (BrokenBarrierException | InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}
public class Main {
    public static void main(String[] args) {
        ExecutorService exec = Executors.newFixedThreadPool(5);

        CyclicBarrier barrier = new CyclicBarrier(
                5, // awaitが5つ揃うまで待つ
                () -> {
                    System.out.println("all done"); // Runnableの最終処理
                });
        for (int i = 0; i < 5; i++) {
            exec.submit(new Task(barrier));
        }
        exec.shutdown();
        //START:16
        //START:14
        //START:15
        //START:17
        //START:13
        //END:16
        //END:17
        //END:13
        //END:14
        //END:15
        //all done
    }
}

排他制御

同時に変数を更新しないようにブロックできる。
デッドロックに注意!!!

public class ExclusionControl implements Runnable {
    private int num = 100;

    public int getNum() {
        return num;
    }

    /**
     * synchronizedメソッド
     * このクラスのインスタンスについて、同時にこの関数が実行されずに、処理を待たせることができる
     */
    public synchronized void add(int amount) {
        this.num = this.num + amount;
    }

    @Override
    public void run() {
        /*
        synchronizedブロック
        引数で指定したインスタンスについて、synchronizedブロックの処理を同時に進めることはできない。
        メソッド、ブロック両方同じインスタンスについて同期される。
        */
        synchronized (this) {
            add(100);
        }
    }
}
public class Main {
    public static void main(String[] args) throws InterruptedException {
        ExclusionControl ex = new ExclusionControl();
        ExecutorService exec = Executors.newFixedThreadPool(2);

        for (int i = 0; i < 10; i++) {
            exec.submit(ex);
        }

        Thread.sleep(200);
        System.out.println(ex.getNum());
        exec.shutdown();
    }
}

java.util.concurrentパッケージ

並行プログラミングのユーティリティ・パッケージ

単純な変数の増減であれば、java.util.concurrent.atomicのクラスを使うと原子性を保てる。
これは、読み取りから書き込みまでを同時に行うようにできているため(他の処理が割り込むラグがない)

public class AtomicValue {
  private AtomicInteger num = new AtomicInteger(0);

  public void add(int num) {
    this.num.addAndGet(num);
  }
}

他にも、CopyOnWriteArrayListのようなスレッドセーフなクラスがある。

ReentrantLockクラスを使用すると、ロックを取得できる。
同じインスタンスに対して同時にロックすることはできない。

public class ExclusionControl implements Runnable {

    private static final ReentrantLock lock = new ReentrantLock();

    private String s;

    public ExclusionControl(String s) {
        super();
        this.s = s;
    }

    @Override
    public void run() {
        // lockとunlock 必ずfinallyでunlockをすること。
        try {
            lock.lock();
            System.out.println("START:" + s);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("END:" + s);
        } finally {
            lock.unlock();
        }
    }
}
public class Main {
    public static void main(String[] args) throws InterruptedException {
        ExecutorService exec = Executors.newFixedThreadPool(2);

        exec.submit(new ExclusionControl("1"));
        exec.submit(new ExclusionControl("2"));
        exec.submit(new ExclusionControl("3"));

        exec.shutdown();
    }
}
// START:2
// END:2
// START:1
// END:1
// START:3
// END:3

ファイルとストリーム

ストリームはデータの入出力のこと。
ストリームを使えばディスク、デバイス、プログラム、メモリなどの入力元と出力元を扱える。

  • ストリームの基底クラス
文字ストリームバイトストリーム
入力java.io.Readerjava.io.InputStream
出力java.io.Writerjava.io.OutputStream

ストリームはfinallyブロックで必ずcloseメソッドを呼び出す。(物理リソースを解放するため)
try-with-resourceを使うといい。

  • 文字入力ストリーム
クラス処理
FileReaderファイルからReader定義のread()で文字を一文字ずつ処理する
BufferedReader行ごとや指定文字数ごとに処理する(バッファを使用する)
  • 文字出力ストリーム
クラス処理
FileWriterファイルに一文字ずつ書き込む
BufferedReaderバッファに書き込みを行い、それをまとめてディスクに書き込む
public class Main {
    public static void main(String[] args) {
        try (FileWriter out = new FileWriter("output.txt", true);// trueで追記モード
             BufferedWriter writer = new BufferedWriter(out);) {
            writer.newLine(); // システム定義の改行文字を入れる
            writer.write("buffering write");
            writer.flush(); // バッファに残っている(かも)しれない書き込みをディスクにうつす。

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
  • バイト入力ストリーム
クラス処理
FileInputStreamファイルから指定のByteを読み込む
BufferedInputStreamバッファを使用して読み込む
  • バイト出力ストリーム
クラス処理
FileOutputStreamファイルへ指定のByteを書き込む
BufferedOutputStreamバッファを使用して書き込む
public class Main {
    // コピーでinputoutput両方使う
    public static void main(String[] args) {
        try (FileInputStream fis = new FileInputStream("sample.jpg");
             BufferedInputStream bis = new BufferedInputStream(fis);
             FileOutputStream fos = new FileOutputStream("sample_bk.jpg", false); // falseなら指定する必要もないが、上書きモード
             BufferedOutputStream bos = new BufferedOutputStream(fos);
        ) {
            byte[] data = null;
            while ((data = bis.readNBytes(1024)).length != 0) { // 1024Byteごと書き込み
                bos.write(data);
            }
            // bos.write(bis.readAllBytes()); これでもいい
            bos.flush(); // バッファを使用した書き込みは最後にflush

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

Serializable

メモリ上にあるインスタンスをストリームとして出力することをシリアライズ、
逆にファイルからインスタンスを作り直すことをデシリアライズという。

シリアライズはオブジェクトの参照先まですべてプリミティブ型/Serializableオブジェクトでなければならない。

/**
 * Serializableクラス
 */
public class Item implements Serializable {

    private String name;
    private int price;

    public Item(String name, int price) {
        this.name = name;
        this.price = price;
    }

    public String getName() {
        return name;
    }

    public int getPrice() {
        return price;
    }

    @Override
    public String toString() {
        return name + " $" + price;
    }
}
/**
 * シリアライズサンプル
 */ 
public class Main {
    public static void main(String[] args) {
        try (FileOutputStream fos = new FileOutputStream("sample.ser", false);
             ObjectOutputStream oos = new ObjectOutputStream(fos);
        ) {
            Item item = new Item("banana", 10000);
            oos.writeObject(item); // シリアライズ
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
/**
 * デシリアライズサンプル
 */
public class Main {
    public static void main(String[] args) {
        try (FileInputStream fis = new FileInputStream("sample.ser");
             ObjectInputStream ois = new ObjectInputStream(fis);
        ) {
            Item item = (Item) ois.readObject(); // デシリアライズ
            System.out.println(item); // banana $10000
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

なお、transientやstatic修飾子がついたフィールドは、シリアライズ対象にならない。
SerializableオブジェクトはwriteObject,readObjectをオーバーライドすることでカスタムシリアライズ処理を作成できる。

Comparable

同じクラスのインスタンスが比較できるようになる。

/**
 * Comparableサンプル
 */
public class Item implements Comparable<Item> {

    private String name;
    private int price;

    public Item(String name, int price) {
        super();
        this.name = name;
        this.price = price;
    }

    public String getName() {
        return name;
    }

    public int getPrice() {
        return price;
    }

    @Override
    public String toString() {
        return name + " $" + price;
    }

    /**
     * compareToを実装する
     * 相手より前に並べる -> 負の整数
     * 相手より後に並べる -> 正の整数
     * 並びを変える必要なし -> 0
     */
    @Override
    public int compareTo(Item other) {
        if (this.price < other.price) {
            return -1;
        } else if (this.price > other.price) {
            return 1;
        } else {
            return 0;
        }
    }
}

nioパッケージ

Path

パスを扱えるインタフェース

Path path = Paths.get("dir", "sample.txt"); // OS間の区切り文字違いを吸収してくれる
path = new File("sample.txt").toPath(); // Fileとも変換可能
path = Paths.toAbsolutePath(); // 絶対パスもとれる

Files

Fileクラスの、ファイル操作部分を担うクラス

if (Files.exists(path)) {
  Files.createFile(path);
}
System.out.println(Files.getPosixFilePermissions(path));

Path base = Paths.get(".");
Files.walk(base) // walkは再帰的にフォルダ内のパスをStreamにしてくれる
     .forEach(System.out::println);

jdbc

クラス概要close時
ConnectionDBコネクションを管理するjdbcリソースとコネクションを破棄する
PreparedStatementSQL命令を管理するjdbcリソースとResultSetを解放する
ResultSetSELECT結果を管理するjdbcリソースとカーソル情報を解放する

アノテーション

アノテーション意味
Overrideメソッドのスーパークラスにオーバーライド対象がないとコンパイルエラーを出す
Deprecated下位互換性を保つために残したメソッドを、非推奨であることを示す
SuppressWarningsコンパイラの警告をオフにする
//アノテーションの読み込まれるタイミング
@Retention(RetentionPolicy.RUNTIME) // 実行時にデータを取得できる
// SOURCE: コンパイル時破棄される情報
// CLASS: デフォルト. classファイルには残るが実行時データ取得できない
@Target({
    ElementType.TYPE,         // クラスにアノテーションする
    ElementType.METHOD,       // メソッドにアノテーションする
    ElementType.CONSTRUCTOR,  // コンストラクタにアノテーションする
    ElementType.FIELD,         // 変数にアノテーションする
})
public @interface AnnotationClass {
    String version = "0.0.1"; // interfaceと同じく定数を持つ

    String value(); // valueがデフォルト アノテーションの単一引数のときセットされる
    int num(); // アノテーションからデータをセットできる
}
@AnnotationClass(value = "Sample class", num = 0)
class Sample{

    @AnnotationClass(value = "field", num = 1)
    public String field;

    @AnnotationClass(value = "constructor", num = 2)
    public Sample(String field) {
        this.field = field;
    }

    @AnnotationClass(value = "method", num = 3)
    public String getField() {
        return field;
    };

}
    public static void main(String[] args)
            throws ClassNotFoundException, NoSuchMethodException, SecurityException, NoSuchFieldException {
        Class<Sample> cls = Sample.class; // 対象クラス
        // クラスアノテーション
        AnnotationClass aClass = cls.getAnnotation(AnnotationClass.class);
        System.out.println(aClass.value()); // Sample class
        System.out.println(aClass.num()); // 0

        // フィールドアノテーション
        Field field = cls.getField("field");
        AnnotationClass aField = field.getAnnotation(AnnotationClass.class);
        System.out.println(aField.value()); // field
        System.out.println(aField.num()); // 1

        // コンストラクタアノテーション
        Constructor<?> constructor = cls.getConstructor(String.class);
        AnnotationClass aConstructor = constructor.getAnnotation(AnnotationClass.class);
        System.out.println(aConstructor.value()); // constructor
        System.out.println(aConstructor.num()); // 2

        // メソッドアノテーション
        Method method = cls.getMethod("getField");
        AnnotationClass aMethod = method.getAnnotation(AnnotationClass.class);
        System.out.println(aMethod.value()); // method
        System.out.println(aMethod.num()); // 3
    }

総称型とワイルドカード

総称型

使うまでなんの型が入るかわからないとき、総称型が使える。 まずはクラスの総称型。

// 型を汎用化することでいろいろな型で使えるようになる
// クラス宣言で<> の中に適当なクラス名を宣言する
// <T extends Number> のようにクラス範囲を指定できる※wildcard参照
// T は慣習(Type)。何でも構わない
class GenericSample<T> {
    private T value;
    public void setValue(T val) {
        value = val;
    }
    public T getValue() {
        return value;
    }
}
public class Generics {
    public static void main(String[] args) {
        GenericSample i = new GenericSample<>();
        i.setValue(10);
        System.out.println(i.getValue());  //10
        GenericSample s = new GenericSample<>();
        s.setValue("Hello");
        System.out.println(s.getValue());  //Hello
    }
}

次にコンストラクタ、メソッドの総称型。

class MethodConstractorSample {

    // コンストラクタ前に仮型引数を置く
    <T> MethodConstractorSample(T arg) {
        System.out.println(arg);
    }
    // メソッド戻り値の前に仮型引数を置く
    // (戻り値にTも使える)
    public <T> boolean genericSample(T arg) {
        T t = arg;
        if (t != null) {
            return true;
        } else {
            return false;
        }
    }
}

ワイルドカード

ワイルドカード: 総称型のクラスやメソッドを使う際、実行(呼び出される)するまで型が分からないとき使う。

class WildCard {
    // List<? extends Number> でNumber型及びNumberを継承するクラスを指定 
    // implementsされているクラスもextendsで指定できる 違和感
    // List<? super Number> でNumber型及びNumberのスーパークラスを指定
    // 上2つは違うとコンパイルすらできない
    // List<?>もしくはList で何クラスでも良し
    public List<?> createList(boolean s) {
        if (s) {
            return new ArrayList<Number>();
        } else {
            return new ArrayList<String>();
        }
    }
}

//        WildCard w = new WildCard();
//        List l = w.createList(false);
//        l.add("String");
//        System.out.println(l.get(0)); // String

ローカライズ

Locale

Localeクラスを使用するとi18nに対応したロケール情報を処理できる。

Locale locale = Locale.getDefault(); // OSのロケール情報
locale = new Locale("ja", "JP"); // 言語, 国を指定
locale = Locale.JAPAN; // 日本のロケール情報を使用
locale = new Locale.Builder()
            .setLanguage("jp")
            .setRegion("JP")
            .build(); // Builderを使用
locale = Locale.forLanguageTag("ja-JP"); // IETF言語タグで生成
System.out.println(locale.toLanguageTag()); // IETF言語タグを生成 ja-JP

Properties

Mapを実装してあるPropertiesクラスを使用することで、propertiesファイルに記載した設定を読み込むことができる。
JavaSE9からプロパティファイルはUTF-8を指定して読み込めるようになった。

Properties prop = new Properties();
try (FileReader fr = new FileReader("settings.properties", Charset.forName("UTF-8"))) {
    prop.load(fr);
    prop.forEach((k, v) -> System.out.println(k + "=" + v));
} catch (IOException e) {
    e.printStackTrace();
}

ResourceBundle

ロケールとファイル名を使用し、クラスパスに置いておいたプロパティファイルを取得する。
基底名_言語コード_国_地域コード_バリアントコード.propertiesファイルを用意しておくと、自動でそのファイルを取得してくれる。
右の情報から順に省略可能。

# settings.properties
hello=こんにちは
# settings_en_US.properties
hello=hello
public static void main(String[] args) {
    // 基底名を、クラスパスが通っているフォルダの、packagename.filenameで指定する。
    ResourceBundle resource = ResourceBundle.getBundle("settings"); 
    // デフォルトロケールのja_JPファイルは用意していないため、settings.propertiesが読み込まれる
    System.out.println(resource.getString("hello")); // こんにちは

    resource = ResourceBundle.getBundle("settings", Locale.US);
    System.out.println(resource.getString("hello")); // hello
}

モジュールシステム

JavaSE9から導入されたパッケージ分離システム。
ソースフォルダのトップにmodule-info.javaを作成することで、モジュールシステムを利用することになる。
モジュールシステムを利用すると、外部に使わせるクラスを制限できる(public未満protected以上ができる)
また、リフレクションによるアクセスも、制限することができる。
逆に、外部ライブラリ(java.baseモジュール以外)を利用する際はmodule-info.javaで宣言が必要。

モジュールシステムでは、クラスパスの他にモジュールパスを利用する。(java --module-path)

/**
 * module-info.java
 */
module module.name { // モジュール名を宣言
  exports package.name; // 外部に公開するパッケージを指定
  requires java.net.http; // java.base以外を内部で使う場合、requiresが必要になる
  opens package.name; // リフレクションを許可するパッケージを指定
  uses work.sehippocampus.Hello; // SPIでアプリケーション側で使用する ↓SPIを参照
  provides work.sehippocampus.lib.HelloImpl; // SPIでライブラリ側で使用する ↓SPIを参照
} 
  • 自動モジュール: モジュールパスに存在するモジュールシステムで作られていないjar
    自動でモジュールとして認識され、すべてのクラスが使えるようになる。
  • 無名モジュール: クラスパスに存在するjar
    モジュールパスに存在するjarの中身は無名モジュールから使用することができるが、反対はできない

ボトムアップ移行: jdepsなどを使用して依存されている側から順に無名モジュールを名前付きモジュールに変更すること

トップダウン移行: 無名モジュールをモジュールパスに移動させ、自動モジュールにすること

SPI

Service Provider Interface
JDBCのようにAPI(インタフェース)だけがあり、第三者が実装をすること。
依存性逆転の原則を守るためのインタフェースの利用を行っている。

ServiceLoader

SPIをさせるためのクラスで、インタフェースを実装したクラスを検索できる。

アプリケーション側

package work.sehippocampus;

public interface Hello {
    public String hello();
}
public class Main {
    public static void main(String[] args) {
        // ServiceLoaderによって実装クラスを検索し、インスタンスを生成する
        for ( Hello hello : ServiceLoader.load(Hello.class)) {
            System.out.println(hello.hello()); // Hello!
        }
    }
}

ライブラリ側

package work.sehippocampus.lib;

import work.sehippocampus.Hello;

public class HelloImpl implements Hello {
    @Override
    public String hello() {
        return "Hello!";
    }
}
# META-INF/services/work.sehippocampus.Hello
work.sehippocampus.lib.HelloImpl # ファイル名をインタフェース名、中身に実装クラスを列挙する

セキュリティ対策

セキュアコーディングについては、SEI CERT Oracle Cording Standard for Javaにまとまっており(日本語版)、
中でも重要なトップ10がTop 10 Secure Coding Practicesにまとまっている

  • 整数オーバーフロー: 事前条件テスト or アップキャスト or BigIntegerを使用する
  • 情報漏洩
    • 必ず例外処理する(Dos攻撃やメモリリークなどでスタックトレースから攻撃に必要な情報が漏れる)
    • コアダンプやログを必要以上に長く持たない
    • インスタンスをガベージコレクションに処理されるように設計する
  • Dos対策
    • 同時接続数を制限する
    • ファイル読み込みサイズを制限する(Files.size()を利用)
    • try-with-resourceでリソースを解放
  • SecurityManager: JVMにSecurityManagerを登録することでセキュリティポリシーを適用させることができる(java -Dオプションでもできる)
  • SQLインジェクション: PreparedStatementを使用する
  • シリアライズオブジェクト: 機密情報を扱うシリアライズオブジェクトは、transientを使用して書き出しできないようにする
    (バイトデータ解析で中身がわかるため)