Javaを使ってみよう †
- 概要
- Oracle
- オブジェクト指向
- OpenJDK Version 8
[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が動作する原理を理解しよう †
- クロスプラットフォーム (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プログラム †
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
変数を使ってみよう †
public class MyApp {
public static void main(String[] args) {
String msg;
msg = "Hello World Again!";
System.out.println(msg);
}
}
$ java MyApp
Hello World Again!
さまざまなデータ型を使おう †
- 文字列: String ... ダブルクォーテーションで囲う
- 文字: char ... シングルクォーテーションで囲う
- 整数: byte, short, int, long ... long型は数値の末尾に L を付与
- 浮動小数点: float, double ... float型は数値の末尾に F を付与
- 論理値: boolean ... true or false
- 特殊文字: 改行 \n, タブ \t
データの演算をしてみよう †
- 四則演算: + - * / %
- 加算/減算: ++ --
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
public class MyApp {
public static void main(String[] args) {
int x = 5;
// x = x + 12;
x += 12;
System.out.println(x);
}
}
$ java MyApp
17
public class MyApp {
public static void main(String[] args) {
String s;
s = "hello " + "world";
System.out.println(s);
}
}
$ java MyApp
hello world
データ型とメモリの関係を理解しよう †
- 基本データ型 (プリミティブ型)
- byte, short, int, long, float, double, boolean, char
- 変数宣言時に即座に値を格納するメモリ領域を確保
- 参照型
- String, Array (int[]), Class
- 変数宣言時には値の番地 (アドレス) を格納するメモリ領域のみ確保
- 値が格納された時点ではじめて値を格納するメモリ領域が確保
- 値が格納されたメモリ領域の先頭アドレス (ポインタ) の値が変数宣言時のメモリ領域に格納
基本データ型と参照型を理解しよう †
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
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
- ※ 配列 b には 配列 a が格納されているメモリ領域の番地が入る
- ※ そのため a も b も同じ {3, 5, 7} の値を指し示している
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
- ※ 文字列は配列の場合とは異なりよく使うので基本データ型と似たような挙動
- ※ 文字列は変更が不可になっており違う文字列を割り当てると別の領域に新しくデータを確保する
- ※ そのため実行結果が world world にはならない
コンストラクタを使ってみよう †
- 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!
- ※ 引数無しで User クラスがインスタンス化された場合に引数が1つの constructor が呼ばれる
パッケージとアクセス権を理解しよう †
- package: 関連するクラスをまとめたり名前の衝突を避けることが可能
- 組織のドメインを逆にしたものを使うことが推奨されている
- package: com.yujidev.myapp
- package: com.yujidev.myapp.model
- アクセス修飾子
- Top Level
- public, (package private) * 何も付けない場合
- main() メソッドは必ず public
- Field, methods, ...
- public, (package private), private, protected
- private: 同じクラスからのみ呼び出し可能
- protected: 同じパッケージ内またはサブクラスから呼び出し可能
パッケージを使ってみよう †
/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);
}
}
パッケージをコンパイルしてみよう †
- 起点となる main メソッドがあるクラスをコンパイルすると芋づる式にコンパイルが実行される
- コンパイル
$ javac com/yujidev/myapp/MyApp.java
$ java com.yujidev.myapp.MyApp
hi! tom
[admin] hi! bob
hello! bob
イニシャライザを使ってみよう †
- static イニシャライザ: static {...}
- クラスを使う前に何らかの初期化処理が可能
- 主な用途はクラス変数の初期化
- インスタンスイニシャライザ: {...}
- コンストラクタ: インスタンス化された後に実行
- インスタンスイニシャライザ: インスタンス化される前に実行
- 主な用途はコンストラクタがオーバーロードされていて複数あった場合に共通処理を書く
// 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
列挙型を作ってみよう †
- クラスのように自分で作ることができるデータ型
- 定数をまとめたもの
- 内部的には特殊なクラスとして扱われている
- 列挙型を定義すると ordinal メソッドが使えるようになる
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
例外処理を扱ってみよう †
- プログラム実行中に予期しない結果が発生した時に適切な処理をする方法
- 例外処理の流れは try で検知した例外を catch 中の e でエラーメッセージを受け取りエラー出力 (err) に表示させる等
- 例外クラス Exception を継承した独自の例外クラスを作成することも可能
- コンストラクタでエラーメッセージを取得可
- throw で例外を独自の例外クラスに投げる
- finally: 例外発生有無に関わらず最後に何らかの後処理を実行可能
// 例外
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 --
ラッパークラスを使ってみよう †
- 基本データ型にはそれに対応する参照型クラスが存在
- Wrapper Class
- int -> Integer
- double -> Double
- 参照型の引数しか受け付けないモノもあり
- auto boxing と auto unboxing
- 参照型の値が null のまま auto unboxing を行うと NullPointerException 発生
/*
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)
ジェネリクスを使ってみよう †
- 汎用化されたデータ型でクラスやインターフェースが作成可能
- 通常あるデータ型で作成したクラスを別のデータ型で作成するにはそれぞれのデータ型のクラスが必要
- ジェネリクスを利用することで一つの汎用化されたクラスに集約可能
- 実行時に型を指定
- 参照型のみ利用可能
- ジェネリクスの記号には Type の意で T が用いられる
// 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
スレッド処理を実装してみよう †
- コンピュータの処理単位
- 複数の処理を同時に実行可能
- main() の処理も Thread 上で実行
- Runnable インターフェースを実装したクラスを new Thread(); に渡す
- Runnable の抽象メソッド run() を Override する
// 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
.............................................................................................................................*****************************************************************************************************************************
**********************************************************************************************************************************************************************************************************************
**********************************************...........................................................................................................................................................................................................................................
............................................................................................................................................*******************************************************************************************************************
無名クラス、ラムダ式を使おう †
- 無名クラスを使って Thread をより短く書くことが可能
- 関数型インターフェース: 抽象メソッドを一つだけ持つインターフェース
// 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 から関数型インターフェースはラムダ式という特殊な記法で置き換え可能
- ラムダ式: (引数) -> {処理}
// 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を使ってみよう †
- Java 8 から導入
- ArrayList 等の集合データと合わせて使われる
- List に格納されている各値に対して何らかの処理をしたい場合に活用可能
- 従来の記法よりもシンプルに書くことが可能
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クラスを使ってみよう †
- Java 8 から導入された java.time パッケージのクラス
- DateTimeFormatter を import することで好きなフォーマットで出力可能
- 日時クラスは変更が不可 (immutable)
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!