#contents ** Javaを使ってみよう [#l881580e] - 概要 -- Oracle -- オブジェクト指向 -- OpenJDK Version 8 - 環境 -- ローカル開発環境 (CentOS 6.7) - 公式 -- サイト: http://openjdk.java.net/ -- ドキュメント(英語): http://docs.oracle.com/javase/8/docs/api/ -- ドキュメント(日本語): http://docs.oracle.com/javase/jp/8/docs/api/ - OpenJDKインストール [vagrant@localhost java_lessons]$ sudo yum -y install java-1.8.0-openjdk-devel $ java -version openjdk version "1.8.0_131" OpenJDK Runtime Environment (build 1.8.0_131-b11) OpenJDK 64-Bit Server VM (build 25.131-b11, mixed mode) ** Javaが動作する原理を理解しよう [#jc950e6e] - クロスプラットフォーム (Windows, macOS, Linux) で動作 - Java プログラム を開発するには JDK (OpenJDK) が必要で JDK には JRE/JVM が含まれる - Java Code (.java) を Compile すると Class File (.class) が生成される - ある環境 (OS) で Compile して生成された Class File を異なる環境に JRE/JVM を入れて動作させることが可能 -- "Write once, run anywhere" - 用語 -- JDK: Java Development Kit -- JRE: Java Runtime Environment -- JVM: Java Virtual Machine ** はじめてのJavaプログラム [#s04d3f2d] - MyApp.java public class MyApp { public static void main(String[] args) { System.out.println("Hello World"); } } - コンパイル $ javac MyApp.java $ ls MyApp.class MyApp.java - 実行 $ java MyApp Hello World ** 変数を使ってみよう [#s786dbb3] - 変数を利用するには型と変数名を宣言する必要あり - MyApp.java public class MyApp { public static void main(String[] args) { String msg; msg = "Hello World Again!"; System.out.println(msg); } } - 実行結果 $ java MyApp Hello World Again! ** さまざまなデータ型を使おう [#x092b341] - 文字列: String ... ダブルクォーテーションで囲う - 文字: char ... シングルクォーテーションで囲う - 整数: byte, short, int, long ... long型は数値の末尾に L を付与 - 浮動小数点: float, double ... float型は数値の末尾に F を付与 - 論理値: boolean ... true or false - 特殊文字: 改行 \n, タブ \t ** データの演算をしてみよう [#a812a57a] - 四則演算: + - * / % - 加算/減算: ++ -- - MyApp.java public class MyApp { public static void main(String[] args) { int i; i = 10 / 3; System.out.println(i); // 3 i = 10 % 3; System.out.println(i); // 1 int x = 5; x++; System.out.println(x); // 6 x--; System.out.println(x); // 5 } } - 実行結果 $ java MyApp 3 1 6 5 - MyApp.java public class MyApp { public static void main(String[] args) { int x = 5; // x = x + 12; x += 12; System.out.println(x); } } - 実行結果 $ java MyApp 17 - MyApp.java public class MyApp { public static void main(String[] args) { String s; s = "hello " + "world"; System.out.println(s); } } - 実行結果 $ java MyApp hello world ** データ型とメモリの関係を理解しよう [#x553b342] - 基本データ型 (プリミティブ型) -- byte, short, int, long, float, double, boolean, char -- 変数宣言時に即座に値を格納するメモリ領域を確保 - 参照型 -- String, Array (int[]), Class -- 変数宣言時には値の番地 (アドレス) を格納するメモリ領域のみ確保 -- 値が格納された時点ではじめて値を格納するメモリ領域が確保 -- 値が格納されたメモリ領域の先頭アドレス (ポインタ) の値が変数宣言時のメモリ領域に格納 ** 基本データ型と参照型を理解しよう [#k72a20bd] - MyApp.java (プリミティブ型) public class MyApp { public static void main(String[] args) { int x = 10; int y = x; y = 5; System.out.println(x); System.out.println(y); } } - 実行結果 (プリミティブ型) $ java MyApp 10 5 - MyApp.java (参照型: 配列) public class MyApp { public static void main(String[] args) { int[] a = {3, 5, 7}; int[] b = a; b[1] = 8; System.out.println(a[1]); System.out.println(b[1]); } } - 実行結果 (参照型: 配列) $ java MyApp 8 8 - (&color(red){※};) 配列 b には 配列 a が格納されているメモリ領域の番地が入る - (&color(red){※};) そのため a も b も同じ {3, 5, 7} の値を指し示している - MyApp.java (参照型: 文字列) public class MyApp { public static void main(String[] args) { String s = "hello"; String t = s; t = "world"; System.out.println(s); System.out.println(t); } } - 実行結果 (参照型: 文字列) $ java MyApp hello world - (&color(red){※};) 文字列は配列の場合とは異なりよく使うので基本データ型と似たような挙動 - (&color(red){※};) 文字列は変更が不可になっており違う文字列を割り当てると別の領域に新しくデータを確保する - (&color(red){※};) そのため実行結果が world world にはならない ** コンストラクタを使ってみよう [#x10db614] - constructor: クラスがインスタンス化される時に必ず呼ばれる特殊なメソッド - クラス名と同じ名前のメソッド名 - メンバ変数 (クラスのフィールド) を参照する際には this キーワードを使用する - this() は クラスの constructor を意味する - MyApp.java ( constructor による初期化 ) class User { String name; // constructor User(String name) { this.name = name; } void sayHi() { System.out.println("hi! " + this.name); } } public class MyApp { public static void main(String[] args) { User tom; tom = new User("Tom"); System.out.println(tom.name); tom.sayHi(); } } - 実行結果 ( constructor による初期化 ) $ java MyApp Tom hi! Tom - MyApp.java ( constructor のオーバーロード ) class User { String name; // constructor User(String name) { this.name = name; } User() { this.name = "Me!"; } void sayHi() { System.out.println("hi! " + this.name); } } public class MyApp { public static void main(String[] args) { User tom; tom = new User(); System.out.println(tom.name); tom.sayHi(); } } - 実行結果 ( constructor のオーバーロード ) $ java MyApp Me! hi! Me! - MyApp.java ( this() を使った constructor のオーバーロード ) class User { String name; // constructor User(String name) { this.name = name; } User() { this("Me!"); } void sayHi() { System.out.println("hi! " + this.name); } } public class MyApp { public static void main(String[] args) { User tom; tom = new User(); System.out.println(tom.name); tom.sayHi(); } } - 実行結果 ( this() を使った constructor のオーバーロード ) $ java MyApp Me! hi! Me! - (&color(red){※};) 引数無しで User クラスがインスタンス化された場合に引数が1つの constructor が呼ばれる ** パッケージとアクセス権を理解しよう [#lc422261] - package: 関連するクラスをまとめたり名前の衝突を避けることが可能 - 組織のドメインを逆にしたものを使うことが推奨されている - package: com.yujidev.myapp -- MyApp --- main () - package: com.yujidev.myapp.model -- User --- name --- sayHi() -- AdminUser --- sayHello() --- sayHi() - アクセス修飾子 -- Top Level --- public, (package private) &color(red){*}; 何も付けない場合 --- main() メソッドは必ず public -- Field, methods, ... --- public, (package private), private, protected --- private: 同じクラスからのみ呼び出し可能 --- protected: 同じパッケージ内またはサブクラスから呼び出し可能 ** パッケージを使ってみよう [#y2ed2189] - ディレクトリ構造 /home/vagrant/java_lessons |--com | |--yujidev | | |--myapp | | | |--MyApp.java | | | |--model | | | | |--AdminUser.java | | | | |--User.java - /com/yujidev/myapp/MyApp.java package com.yujidev.myapp; import com.yujidev.myapp.model.User; import com.yujidev.myapp.model.AdminUser; // import com.yujidev.myapp.model.*; public class MyApp { public static void main(String[] args) { User tom = new User("tom"); // System.out.println(tom.name); tom.sayHi(); AdminUser bob = new AdminUser("bob"); // System.out.println(bob.name); bob.sayHi(); bob.sayHello(); } } - /com/yujidev/myapp/model/AdminUser.java package com.yujidev.myapp.model; public class AdminUser extends User { public AdminUser(String name) { super(name); } public void sayHello() { System.out.println("hello! " + this.name); } @Override public void sayHi() { System.out.println("[admin] hi! " + this.name); } } - /com/yujidev/myapp/model/User.java package com.yujidev.myapp.model; public class User { protected String name; public User(String name) { this.name = name; } public void sayHi() { System.out.println("hi! " + this.name); } } ** パッケージをコンパイルしてみよう [#q3a663d5] - 起点となる main メソッドがあるクラスをコンパイルすると芋づる式にコンパイルが実行される - コンパイル $ javac com/yujidev/myapp/MyApp.java - 実行結果 $ java com.yujidev.myapp.MyApp hi! tom [admin] hi! bob hello! bob ** イニシャライザを使ってみよう [#oa762e7a] - static イニシャライザ: static {...} -- クラスを使う前に何らかの初期化処理が可能 -- 主な用途はクラス変数の初期化 - インスタンスイニシャライザ: {...} -- コンストラクタ: インスタンス化された後に実行 -- インスタンスイニシャライザ: インスタンス化される前に実行 -- 主な用途はコンストラクタがオーバーロードされていて複数あった場合に共通処理を書く - MyApp.java // static class User { private String name; private static int count; // クラス変数 static { User.count = 0; System.out.println("Static initializer"); } { System.out.println("Instance initializer"); } public User(String name) { this.name = name; User.count++; System.out.println("Constructor"); } public static void getInfo() { // クラスメソッド System.out.println("# of instances: " + User.count); } } public class MyApp { public static void main(String[] args) { User.getInfo(); // 0 User tom = new User("tom"); User.getInfo(); // 1 User bob = new User("bob"); User.getInfo(); // 2 } } - 実行結果 $ java MyApp Static initializer # of instances: 0 Instance initializer Constructor # of instances: 1 Instance initializer Constructor # of instances: 2 ** 列挙型を作ってみよう [#uc7ac40a] - クラスのように自分で作ることができるデータ型 - 定数をまとめたもの - 内部的には特殊なクラスとして扱われている - 列挙型を定義すると ordinal メソッドが使えるようになる - MyApp.java enum Result { SUCCESS, // 0 ERROR, // 1 } public class MyApp { public static void main(String[] args) { Result res; res = Result.ERROR; switch (res) { case SUCCESS: System.out.println("OK!"); System.out.println(res.ordinal()); // 0 break; case ERROR: System.out.println("NG!"); System.out.println(res.ordinal()); // 1 break; } } } - 実行結果 $ java MyApp NG! 1 ** 例外処理を扱ってみよう [#s64af7ed] - プログラム実行中に予期しない結果が発生した時に適切な処理をする方法 - 例外処理の流れは try で検知した例外を catch 中の e でエラーメッセージを受け取りエラー出力 (err) に表示させる等 - 例外クラス Exception を継承した独自の例外クラスを作成することも可能 -- コンストラクタでエラーメッセージを取得可 -- throw で例外を独自の例外クラスに投げる - finally: 例外発生有無に関わらず最後に何らかの後処理を実行可能 - MyApp.java // 例外 class MyException extends Exception { public MyException(String s) { super(s); } } public class MyApp { public static void div(int a, int b) { try { if (b < 0) { throw new MyException("not minus!"); } System.out.println(a / b); } catch (ArithmeticException e) { System.err.println(e.getMessage()); } catch (MyException e) { System.err.println(e.getMessage()); } finally { System.out.println(" -- end -- "); } } public static void main(String[] args) { div(3, 0); div(5, -2); } } - 実行結果 $ java MyApp / by zero -- end -- not minus! -- end -- ** ラッパークラスを使ってみよう [#f0c24af0] - 基本データ型にはそれに対応する参照型クラスが存在 - Wrapper Class -- int -> Integer -- double -> Double - 参照型の引数しか受け付けないモノもあり - auto boxing と auto unboxing - 参照型の値が null のまま auto unboxing を行うと NullPointerException 発生 - MyApp.java /* Wrapper Class int -> Integer double -> Double */ public class MyApp { public static void main(String[] args) { // Integer i = new Integer(32); // int n = i.intValue(); Integer i = 32; // auto boxing i = null; int n = i; // auto unboxing } } - 実行結果 $ java MyApp Exception in thread "main" java.lang.NullPointerException at MyApp.main(MyApp.java:15) ** ジェネリクスを使ってみよう [#x4c4de1f] - 汎用化されたデータ型でクラスやインターフェースが作成可能 - 通常あるデータ型で作成したクラスを別のデータ型で作成するにはそれぞれのデータ型のクラスが必要 - ジェネリクスを利用することで一つの汎用化されたクラスに集約可能 - 実行時に型を指定 - 参照型のみ利用可能 - ジェネリクスの記号には Type の意で T が用いられる - MyApp.java // generics class MyData<T> { public void getThree(T x) { System.out.println(x); System.out.println(x); System.out.println(x); } } public class MyApp { public static void main(String[] args) { MyData<Integer> i = new MyData<>(); i.getThree(32); MyData<String> s = new MyData<>(); s.getThree("hello"); } } - 実行結果 $ java MyApp 32 32 32 hello hello hello ** スレッド処理を実装してみよう [#q39d2717] - コンピュータの処理単位 - 複数の処理を同時に実行可能 - main() の処理も Thread 上で実行 - Runnable インターフェースを実装したクラスを new Thread(); に渡す - Runnable の抽象メソッド run() を Override する - MyApp.java // Thread class MyRunnable implements Runnable { @Override public void run() { for (int i = 0; i < 500; i++) { System.out.print('*'); } } } public class MyApp { public static void main(String[] args) { MyRunnable r = new MyRunnable(); Thread t = new Thread(r); t.start(); for (int i = 0; i < 500; i++) { System.out.print('.'); } } } - 実行結果 $ java MyApp .............................................................................................................................*************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************.......................................................................................................................................................................................................................................................................................................................................................................................******************************************************************************************************************* ** 無名クラス、ラムダ式を使おう [#r2b07ce7] - 無名クラスを使って Thread をより短く書くことが可能 - 関数型インターフェース: 抽象メソッドを一つだけ持つインターフェース - MyApp.java (無名クラスを利用) // Thread public class MyApp { public static void main(String[] args) { new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 500; i++) { System.out.print('*'); } } }).start(); for (int i = 0; i < 500; i++) { System.out.print('.'); } } } - 実行結果 $ java MyApp ............................................................................................................................................................................................................................***********************************************************************************************************************************************************************************************************************************************************************************........................................................................................................................................................................................................................................................................................********************************************************************************************************************************************************************************************************************************* - Java version 8 から関数型インターフェースはラムダ式という特殊な記法で置き換え可能 - ラムダ式: (引数) -> {処理} - MyApp.java (ラムダ式) // Thread public class MyApp { public static void main(String[] args) { // new Thread(new Runnable() { // @Override // public void run() { // for (int i = 0; i < 500; i++) { // System.out.print('*'); // } // } // }).start(); // ラムダ式 // (引数) -> {処理} new Thread(() -> { for (int i = 0; i < 500; i++) { System.out.print('*'); } }).start(); for (int i = 0; i < 500; i++) { System.out.print('.'); } } } - 実行結果 $ java MyApp ................................................................................................................................................................................................................................................................................................................................********************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************.................................................................................................................................................................................... ** Stream APIを使ってみよう [#l2b53839] - Java 8 から導入 - ArrayList 等の集合データと合わせて使われる - List に格納されている各値に対して何らかの処理をしたい場合に活用可能 - 従来の記法よりもシンプルに書くことが可能 - MyApp.java import java.util.*; public class MyApp { public static void main(String[] args) { // Stream List<Integer> sales = new ArrayList<>(Arrays.asList(12, 30, 22, 4, 9)); /* Stream を使わない場合 */ // for (Integer sale : sales) { // System.out.println(sale); // } sales .stream() // 中間処理 .filter(e -> e % 3 == 0) // ラムダ式 引数 -> 処理 .map(e -> "(" + e + ")") // ラムダ式 // 終端処理 .forEach(System.out::println); // メソッド参照の記法 (メソッドを引数で渡す) } } - 実行結果 $ java MyApp (12) (30) (9) ** LocalDateTimeクラスを使ってみよう [#n01901be] - Java 8 から導入された java.time パッケージのクラス - DateTimeFormatter を import することで好きなフォーマットで出力可能 - 日時クラスは変更が不可 (immutable) - MyApp.java import java.time.*; import java.time.format.DateTimeFormatter; public class MyApp { public static void main(String[] args) { LocalDateTime d = LocalDateTime.now(); // LocalDateTime d = LocalDateTime.of(2016, 1, 1, 10, 10, 10); // LocalDateTime d = LocalDateTime.parse("2016-01-01T10:10:10"); System.out.println(d.getYear()); System.out.println(d.getMonth()); System.out.println(d.getMonth().getValue()); System.out.println(d.plusMonths(2).minusDays(3)); DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy!MM!dd!"); System.out.println(d.format(dtf)); } } - 実行結果 $ java MyApp 2017 MAY 5 2017-07-01T09:41:32.517 2017!05!04!