はじめに
他言語の開発経験を数年経て次にJavaについて習うレベルの人向け(筆者がそうであるため)
かなり端折ったりはするけれどなるべく大切な部分は要約してまとめる
第一部 ようこそJavaの世界へ
Javaは以下の点がある
- プログラミングの学びやすく標準的な基本文法
- 大規模開発を支援するオブジェクト指向に対応
- 豊富に準備された便利な命令群
- 多様なコンピュータで同じように動作する汎用性
System.out.println("Hello world");
System.out.println(31 + 31);
int x;
x = 6;
第1章 プログラムの書き方
ソースコードの波括弧{...}で囲まれた部分をブロック(block)と呼ぶ
外側のブロックは、クラスブロック(class block)
内側のブロックは、メソッドブロック(method block)
2つのブロックの二重構造を持っている
コンピュータに対する指示・命令はメソッドブロックでかく
それより外の最初と最後の2行はお決まりのパターンであまり変わらない
1行目のpublic classの直後はプログラムの名前を指示する単語を書く
正式にはクラス名といい、大文字のアルファベットで始める名前をつける
- Javaのソースファイル名は、「クラス名.java」にしなければならない
- プログラム2行目のpublic static void main(String[] args)は覚える
- プログラムの流れは上から下ではなく外から内へ
- ブロックの開始と終了では正確に字下げを行い、カッコの対応とブロックの多重構造の見通しをよくすること
- ソースコード中に解説文を書き込むこともでき、この解説文をコメントという
- 表示や計算などJavaの命令を書いていく場所はメソッドブロックの内部で、ソースコードの2行目のmainで書くためmainメソッドとも呼ばれる。mainメソッドの中には文を順番に書いていく
- 変数とはデータを格納するためにコンピュータ内部に準備する箱のようなもの
- 変数に値を入れることを代入、取り出すことを取得と呼ぶ
- 変数以外にも名前をつけることがあり、それらの名前として使える文字や数字の並びのことを識別子と呼ぶ
- 予約語という「そもそも名前として使ってはいけない単語」がある
- すでに利用された変数名を再度使ってはならない
- 大文字・小文字・全角・半角の違いは区別される
- 小文字で始まるわかりやすい名前をつけることが望ましい
- プログラムで扱うことができるデータの出位のことをデータ型、または型という。9つだけ覚えておく
分類 | 型名 | 格納するデータ | 変数宣言の例 | 利用頻度 |
---|---|---|---|---|
変数 | byte | とても小さい整数 | byte glasses | △ |
short | 小さな整数 | short age | △ | |
int | 普通の整数 | int salary | ◎ | |
long | 大きな整数 | long worldPeople | △ | |
小数 | float | 少し曖昧でも良い小数 | float weight | △ |
double | 普通の小数 | double pi | ○ | |
真偽値 | boolean | trueかfalse | boolean isError | ○ |
文字 | char | 1つの文字 | char initial | △ |
文字列 | String | 文字の並び | String name | ◎ |
- 整数を格納できる4つの型(byte, short, int, long)、大体整数を代入したいならint
- 小数を格納できる2つの型(float, double)、浮動小数点型と総称されることもある、doubleを大体使う。厳密な計算ができないため金額の計算にこれらを使ってはいけない
- YESかNOかを格納できるboolean型(true, falseのどちらかのみ)、真偽値ともいう
- 1文字だけを格納できるchar型
- 文字列を格納できるString型
- 変数を宣言すると同時に値を代入することを変数の初期化と呼ぶ(例:int age = 22;)
- 変数に値を入れた後に別の値を代入すると、上書きされる
- 定数の宣言(final 型 定数 = 初期値;)とすれば最初の値から上書きされない(コンパイルエラーになる)
第2章 式と演算子
int a = 20;
int b = a + 5;
「b = a + 5」のようなものを式と呼ぶ
a,b,5をオペランド、+,=を演算子と呼ぶ
- 2つの二重引用符に囲まれた部分を文字リテラルとみなす(例:"アイウエオ")文字列中に二重引用符を用いる場合は¥を用いる("アイウ¥"エオ") => アイウ"エオ
- 演算子には優先順位がある優先順位の順番に処理を実行する、()で括られている場合、カッコ内の処理を優先する
- 演算子は、たくさんありますので自分で確認よろしくです
- インクリメント/デクリメント演算子 (++は値を1増やす、--は値を1減らす) a++ => a = a + 1 正し++や--は他の演算子と一緒に使わないこと
- 代入時の自動型変換、ある型で宣言された変数には、その型の値しか代入できないが、「小さい型」の値を「大きな型」の変数に代入する場合に限って、値が自動的にはこの型に変換されて代入される
- 強制的な型変換、キャスト演算子という(変換先の型名)式で示す 例:age = (int)3.2; 余程のことがない限り使わない
- 演算時の自動型変換、異なる方で演算を行なった場合は「意図的に大きな型」に統一されてから演算される
- 命令実行の分はJavaが準備してくれているさまざまな命令を呼び出すための文
- 画面に文字を表示する命令
System.out.print(...); - 2つの値を比較して大き方の数値を代入する
int m = Math.max(1,2); - 文字列を数値に変換
int n = Integer.parseInt("1"); - 乱数を発生させる
int r = new java.util.Random().nextInt(乱数の上限値); - キーボードから1行の文字列の入力を受け付ける
String s = new java.util.Scanner(System.in).nextLine(); - キーボードから1つの整数の入力を受け付ける
int input = new java.util.Scanner(System.in).nextInt();
第3章 条件分岐と繰り返し
- 文を実行させる順番のことを制御構文といい、代表的なものとして順次、分岐、繰り返しの3つがある
- if文
ifという命令を使えば分岐を行うことができる
ifの後の()ないには条件分岐を書く
変数がtrueかどうかのチェックを行うには「==」を使う
分岐条件が成立していたら、()の直後にあるブロックの中身だけ実行する
分岐条件が成立していなければ、elseの後ろにあるブロックの部分だけ実行する - while
whileという命令を使えば繰り返し制御を行うことができる
whileの後の()内には繰り返しを続ける条件を書く
繰り返すを続ける条件が成立している限り、何度でも直後のブロックの中身だけが繰り返し実行される - ブロックとは
ブロックとは複数の文をひとまとまりとして扱うためのもの
ルール1:内容が1文しかなければ、波括弧を省略可能。正し非推奨
ルール2:ブロック内で宣言した変数は、そのブロックが終わると同時に消滅する。範囲のことをスコープという - 条件式
if文やwhile文で利用される式の一種で「処理を分岐する条件」や「繰り返しを続ける条件」を表現するためのもの
if文は、trueなら第1ブロック、falseなら第2ブロックを実行する
while文は、条件式の評価結果がtrueなら、ブロックを繰り返し実行する - 2つ以上の条件を組み合わせた条件式を使う場合、論理演算子を使う(&&または||)
- 条件式はifのみ、if、elseの2つ条件式の中にさらに条件式の構文など
- switch文
全ての条件式が「変数==値」や「変数==変数」のように左辺と右辺が一致するかを比較する
比較する値が整数、文字列、または文字である
caseは「〜の場合」という条件を、case以降の処理にはbreak文を必ず、defaultは全ての条件に合致しないときの処理。不要な場合は省略可能 - do while文
ブロックを実行した後に条件式を評価する do{ブロック}while(条件式); - for文
初期化処理:最初の一回だけ実行される文、何周目のループかを記録するための変数を定義
繰り返し条件:ブロックの内容を実行する前に評価され、ループ継続の是非を判定、trueの間は繰り返す
繰り返し時処理:for文内のブロックを最後まで処理した直後に自動的に実行される文。i++でループ回数をプラス1するみたいな - ループ変数(初期化処理)
- ループ変数の名前は自由
- ブロック内で利用可能
- ブロック外では利用不可能
- 制御構造のネスト
分岐や繰り返しの制御構造の中に別の制御構造を含むことを入れ子、ネストという - 繰り返しの中断
for文やwhile文を用いた繰り返しの途中で中断したい場合、break文とcontinue文という2種類の中断方法がある
break文は、breakを囲んでいるもっとも内側の繰り返しブロックが即座に中断される
continue文は、現在の周回を中断して、同じ繰り返しの次の周回に進む - 無限ループとは、永久に繰り返し続ける制御構文のこと
第4章 配列
-
配列とは、同一種類の複数データを並び順で格納するデータ構造。配列の中の一つを要素という。配列の各要素には同一種類のデータしか格納できない
-
0から始まる番号の添え字でデータを取得できる
-
配列の作成
- 配列変数の宣言(要素の型[] 配列変数名)
int[] scores;
- 要素の作成と代入
scores = new int[5]
newはnew演算子と呼ばれるもので、指定された型の要素を[]内に指定された数だけ作成する
1.2.を同時に行うにはint[] scores = new int[5];
- 配列変数の宣言(要素の型[] 配列変数名)
-
配列の長さを調べる
int num = scores.length;
-
配列の利用方法
配列の添え字に代入scores[1] = 30;
-
配列の初期化
変数の値を取り出す前には必ず初期化が必要だが、配列の要素は自動的に初期化されるので必要ない -
省略記法(配列作成と初期化の省略記法)
- 要素の型[] 配列変数名 = new 要素の型[] {値1, 値2, .... };
- 要素の型[] 配列変数名 = {値1, 値2, .... };
-
配列とfor文
- ループによる全要素の利用
配列を順番で回すfor (int i = 0; i < 配列変数名.length; i++) { 配列変数名[i]を使った処理 }
- ループによる集計
合計や平均、集計や条件一致数などをするint[] scores = {20, 30, 40, 50, 80} for (int i = 0; i < scores.length; i++) { if (scores[i] >= 50) { scores[i]を使った処理 } }
- 添え字に対応した情報の利用
ランダムで表示することができるint[] seq = new int[10] // 塩基配列をランダムに生成 for (int i = 0; i < seq.length; i++) { seq[i] = new java.util.Randam().nextInt(4); } // 生成した塩基配列の記号表示 for(int i = 0; i < seq.length; i++ ¥) { switch(seq[i]) { case 0: System.out.print("A"); break; case 1: System.out.print("T"); break; case 2: System.out.print("G"); break; case 3: System.out.print("C"); break; } }
- ループによる全要素の利用
-
拡張for文
javaには、配列の要素を1つずつ取り出すループを簡単に書くための特殊なfor文(拡張for文)が準備されている
for (要素の型 任意の変数 : 配列変数名) {
...
}
public static void main(String[] args) {
int[] scores = {20, 30, 40, 50, 80}
for (int value : scores) {
System.out.println(value);
}
}
ループ変数や配列の添え字を記述する必要がなくなる
- 配列を理解する
下記は「1」ではなく「100」が出力される
int[] arrayA = {1, 2, 3};
int[] arrayB;
arrayB = arrayA;
arrayB[0] = 100;
System.out.println(arrayA[0]);
-
メモリと変数
コンピュータは使用するデータをメモリに記録する。メモリの中は碁盤の目のように区画整理されており、各区画には住所(アドレス)が振られている。変数を宣言すると、空いている区画(どこが選ばれるかわからない)を変数のために確保します「変数に値を代入する」とは、確保しておいた区画に値を記録することである。 -
メモリと配列
配列変数の宣言によりint[]型変数が、new演算子により配列の実体(要素の集まり)がメモリ上の区画に作成される。そして、配列変数には5つの要素丸ごとではなく、「最初の要素のアドレス」が代入される
int[] scores = new int[5]; を実行したときのメモリ上の様子
- int型の要素を5つ持つ配列がメモリ上に作成される
- int[]型の配列変数scoresがメモリ上に作成される
- 配列変数scoresに配列の先頭要素のアドレスが代入される
プログラムからscores[n]と指定されたら
- scoresから番地(8832)を取り出し、配列(先頭要素)を見つける
- 見つけた配列の先頭要素からn個後の要素の区画を読み書きする
配列変数scoresは、「配列の実態は8832番地にあります」と指し示す動作をしていることになる。これを参照と呼び、メモリ上の番地を代入する変数のことを参照型変数と呼ぶ。int型やboolean型は基本型変数といい、参照型変数とは区別して考える。
-
配列を複数の変数で参照する
arrayB = arrayA;
をすると配列変数arrayBにコピーされるのは、arrayAに入っている先頭番地で、結果変数arrayBは変数arrayAと同じ配列を参照する -
ガベージコレクション
boolean judge = true;
if (judge == true) {
int[] array = {1,2,3};
}
変数の寿命は自分が宣言されたブロックが終了するまでで、配列変数も同じ
if文の配列変数arrayはメモリから消滅する、一方newで確保された3つの要素は普通の変数ではないので、ブロックが終了しても寿命は迎えない。その結果、配列はどの配列変数からも参照されない状態でメモリに残ってしまう。これがゴミになる
しかし、Javaにはガベージコレクションという仕組みが常に動いており、実行中のプログラムが生み出したメモリ上のゴミを自動的に探し出して片付ける
- null
変数の寿命によって配列変数が配列を参照しなくなるには、nullを使用することで、意図的に配列を参照されないようにすることができる
int[] array = {1,2,3};
array = null;
array[0] = 10;
- int[]型などの参照型変数に代入すると、その変数は何も参照しなくなる
- int型などの基本型変数には代入することができない
- 他次元配列
1次元配列に縦の並びを加えると2次元配列になる-
2次元配列の宣言
要素の型[][] 配列変数名 = new 要素の型[行数][列数]
-
2次元配列の要素の利用
配列変数名[行の添え字][列の添え字]
-
2次元配列の利用
int[][] scores = new int[2][3]; scores[0][0] = 40; scores[0][1] = 50; scores[0][2] = 60; scores[1][0] = 40; scores[1][1] = 50; scores[1][2] = 60;
-
第5章 メソッド
-
メソッドを利用するメリット
メソッドとは、複数の文をまとめ、それを1つの処理として名前をつけたもので、部品の最小単位になる- プログラムの見通しが良くなり、全体を把握しやすくなる
- 機能単位に記述するため、修正範囲を限定できる
- 同じ処理を1つのメソッドにまとめることで、作業効率が上がる
-
メソッドの定義
メソッドを作成することをメソッドの定義という
public static 戻り値の型 メソッド名(引数リスト) {
メソッドが呼び出された時に実行される具体的な処理
}
- メソッドの呼び出し
メソッドを使用することをメソッドを呼び出すという
メソッド名(引数リスト)
public class Main {
public static void main (String[] args) {
System.out.println("メソッドを呼び出す");
hello();
System.out.println("メソッドを呼び出しが終わりました");
}
public static void hello() {
System.out.println("こんにちは");
}
}
上記の実行結果は
メソッドを呼び出す
こんにちは
メソッドを呼び出しが終わりました
- mainメソッド以外からのメソッドの呼び出し
ソースコード中に複数のメソッドが定義されている場合、その順序には特に意味や制約はない
mainメソッドより上に別のメソッドが定義されていても、プログラムは必ずmainメソッドから動き始める
メソッド名はソースコードを見て使う人が分かりやすく
-
引数とは
メソッドを呼び出す際に呼び出し元から値を渡すことができ、渡される値のことを引数という
引数には数値や文字列などを指定でき、その値や型、渡す引数の数は開発者が自由に決めることができる
引数として渡す値が複数ある場合は、値をカンマで区切って使用する
例)add(100, 20);
100と20を渡してaddメソッドを呼び出す引数の渡し方
- 何も渡さない:メソッド名()
- 値を1つ渡す:メソッド名(値)
- 値を複数渡す:メソッド名(値,値,・・・)
-
仮引数と実引数
渡す値のことを実引数、受け取る変数のことを仮引数と呼ぶ -
変数のスコープとローカル変数
mainメソッド内で宣言した変数xとyはmainメソッドのブロックの中でしか使用できない
public static void main(String[] args) {
int x = 100;
int y = 10;
add();
}
public static void add() {
int ans = x + y; ここでエラー
}
main()やadd()といったメソッド内で宣言した変数をローカル変数と呼ぶ
ローカル変数は、その変数が属するメソッド内だけで有効な存在であって、別のメソッドに属する同名のローカル変数とは全く別物
- 戻り値の利用
呼び出されたメソッドから、呼び出し元のメソッドへ値を返すことを値を戻すという、戻される値のことを戻り値という
値の戻し方
public static 戻り値の型 メソッド名(引数リスト) {
メソッドが実行された時に動く処理
return 戻り値;
}
戻り値で何も戻さない場合は「void」を指定します
-
オーバーロードの利用
同じ名前のメソッドを定義することをオーバーロードまたは多重定義という
仮引数が異なれば同じ名前のメソッドを複数定義することが許されている -
引数に配列を用いる
メソッドの引数にはint型やString型などの変数だけでなく、int[]のように配列型を指定できる基本型の変数をメソッド呼び出しで渡すと
- 呼び出し元の変数の内容が、呼び出し先に引数にコピーされる
- 呼び出し先で引数の内容を書き換えても、呼び出し元の変数は変化しない
参照渡しを行うと呼び出し先で加えた変更が呼び出し元にも影響する
配列をメソッド呼び出しで渡すと
- 呼び出し元の配列のアドレスが、呼び出し先の引数にコピーされる
- 呼び出し先で配列の実体を書き換えると、呼び出し元にも影響する
第6章 複数クラスを用いた開発
- ソースファイルの分割
複数のソースファイルに分けて開発するということは、複数のクラスに分けて開発すること
ファイルごとに開発を分担し、それぞれが並列して開発を進められる(分業しやすい)というメリットがある
JDKでプログラムを実行する場合
> java クラス名
JVMは起動時に指定されたクラスの中にあるmainメソッドを呼び出してプログラムを実行する
「渡された複数のクラスファイルのうち、mainメソッドが含まれているクラスの名前」を指定する必要がある
全てのクラスファイルを受け取っても、「どのクラスの中にmainメソッドがあるか」がわからないと起動できない
- パッケージを利用する
Javaには、各クラスをパッケージというグループに所属させて、分類・管理できるような仕組みがある
クラスをパッケージに所属させるためには、そのクラスのソースコードの先頭にpackage文を記述する
package calcapp.main;
public class Calc { ...
- パッケージを含むクラス名を指定する
- 完全限定クラス名の入力
例)int total = calcapp.logics.CalcLogic.tasu(a,b); - 完全限定クラス名の入力の省略
例)int total = CalcLogic.tasu(a,b);
名前空間
- パッケージ名自体の衝突を避ける方法
パッケージ名さえ異なればクラス名は重複してもよく、自由にクラス名をつけても構わない。しかし、パッケージ名が衝突すると、こられの前提は全て崩れてしまう
第二部 すっきり納得 オブジェクト指向
第7章 オブジェクト指向を始めよう
オブジェクト指向を用いるメリットは、「ラクして、楽しく、良いものを」作れる
なぜ、上記のメリットがあるのかというと
- 私たち人間が慣れ親しみ、よく把握している現実世界を真似して作られたプログラムもまた、私たち人間にとって把握しやすいものだから
- プログラム開発時に「手続きを想像して作る」必要はない。現実世界をお手本に、それを真似して作ればいい
- 現実世界の登場人物に変化があった場合、対応する部品(クラス)を修正、交換すれば簡単にプログラムを修正できる
オブジェクトと責務
-
サッカーで考えるオブジェクト指向
責務の割り当て...オブジェクト指向プログラミングでは、開発者はそれぞれの部品(クラス)に「責務」をプログラムとして書き込む -
オブジェクトの姿
それぞれのオブジェクトは属性と操作を持っている
オブジェクト指向の3大機能と今後の学習
- 3大機能とその位置付け
継承、多様性、カプセル化の3つ
第8章 インスタンスとクラス
-
オブジェクトを生み出す手順
開発者の仕事は以下の二つ- 各オブジェクトが負うべき責務を考え、「属性」「操作」の種類と内容を定義する
- 各オブジェクトを仮想世界に生み出し、動かす
-
クラスとオブジェクトが別である理由
オブジェクトを大量に作る必要があるため -
オブジェクトという用語の曖昧さ
オブジェクトという用語は、時々クラスのことを指して使われることもありかなり曖昧
「金型ではなく、その方から生み出された仮想世界で活動する実体」を厳密に示したい場合は、インスタンス
クラスからインスタンスを生成する行為をインスタンス化
インスタンスを生み出すための金型がクラスである -
登場人物クラスの作り方
public class Hero {
// 属性の定義
String name;
int hp;
// 操作の定義
public void attack() {...}
public void sleep() {...}
public void sit(int sec)() {...}
public void slip() {...}
public void run() {...}
}
- クラスの宣言定義
public class Hero {
...
}
- 属性の宣言方法
name:String型
hp:int型
public class Hero {
String name;
int hp;
}
- 属性の初期値指定と定数フィールド
public class Matango {
int hp;
int level = 10;
final int LEVEL = 10;
}
finalをつけると定数フィールドになる
- 操作の宣言方法
public class Hero {
String name;
int hp;
public void sleep() {
this.hp = 100; // 自分自身のhpフィールド
System.out.println(this.name + "は、眠って回復した!")
}
}
同じクラス内のフィールドにアクセスする場合、「this.」を省略しても動作するが、ローカル変数や引数にも同じhpという変数がある場合そちらが優先されるため、フィールドを用いる時には明示的にthisをつけるようにすること
-
クラス定義によって可能になる2つのこと
- そのクラスに基づいて、インスタンスを生成できるようになる
- そのクラスから生まれたインスタンスを入れる変数の型が利用できるようになる
-
神様クラスの作り方
public class Main {
public static void main(String[] args) {
// ここに処理を書く
}
}
- インスタンスの生成
クラス名 変数名 = new クラス名();
pubic class Main {
public static void main(String[] args) {
Hero h = new Hero();
}
}
- インスタンスのフィールド利用
変数名.フィールド名 = 値;
pubic class Main {
public static void main(String[] args) {
Hero h = new Hero();
h.name = "みなと";
h.hp = 100;
System.out.println("勇者" + h.name + "を生み出しました!");
}
}
- インスタンス利用のまとめ
インスタンスの生成にはnewを使う
フィールドを利用する場合は「変数名.フィールド名」と記述する
メソッドを呼び出す場合は「変数名.メソッド名()」と記述する
第9章 さまざまなクラス機構
newされた直後に自動的に実行されるメソッドをコンストラクタと呼ぶ
引数を渡すことで初期値に与えられた値を入れられる
public class クラス名 {
public クラス名() {
自動的に実行する処理
}
}
コンストラクタを作ると、インスタンスを生成する時には、必ず名前を指定する必要が生じる
複数のコンストラクタが定義されていた場合
newする時に渡した引数の型・数・順番に一致するコンストラクタが動作する
クラスに1つもコンストラクタが定義されていない場合に限って、「引数なし、処理内容なし」のコンストラクタ(デフォルトコンストラクタ)がコンパイル時に自動的に追加される
同一クラス内の別コンストラクタの呼び出しをJVMに依頼する
this(引数);
第10章 継承
-
似かよったクラスの開発
同じようなクラス(参考クラスのメソッドを全て持っているクラス)を作った場合の問題点
- 追加・修正に手間がかかる
クラスに新しいメソッドを加えた時や、Heroクラス内のメソッドを変更した場合、その変更を別クラスにも行う必要がある - 把握や管理が難しくなる
クラスは参考クラスを元にしているので、この2つのクラスのソースコードの大半が重複することになる
- 追加・修正に手間がかかる
-
継承による解決
継承すると上記が解決する
public class SuperHero extends Hero {
boolean flying; // 追加したカラム(メンバ)
}
public class 作りたいクラス名 extends 継承したいクラス名 {
// 継承クラスとの「差分」
}
-
継承関係の表現方法
クラスと継承したいクラスの関係を継承関係
元となるクラスを「スーパークラス、親クラス」などという
新しく定義されるクラスを「サブクラス、子クラス」などという -
継承のバリデーション
複数のクラスを親として1つの子クラスを定義するのを多重継承
正し複数の親クラスを持つことはできない(継承を何度もすることができる) -
オーバーライド
継承を用いて子クラスに宣言されたメンバの扱い- 親クラスに同じメンバがなければ、そのメンバは「追加」になる
- 親クラスに同じメンバがあれば、そのメンバは「上書き変更」される
-
継承やオーバーライドの禁止
Javaでは、宣言時にfinalが付けられているクラスは継承できない
public final class String extends Object...
クラス宣言、メソッド宣言にfinalをつけると継承禁止やオーバーライド禁止となる
-
インスタンスの多重構造
継承したクラスは子クラスと親クラスの二重構造になっている -
メソッドの呼び出し
インスタンスの外部からメソッドの実行依頼が届くと、多重構造のインスタンスは極力、子インスタンス部分のメソッドで対応しようとする -
親インスタンス部へのアクセス
親インスタンス部分のフィールドを利用する
super.フィールド名
親インスタンス部分のメソッドを呼び出す
super.メソッド名 -
継承を利用したクラスのコンストラクタ
Javaは、全てのコンストラクタは、その先頭で必ず内部インスタンス部分(親クラス)のコンストラクタを呼び出さなければならないというルールがある
同じクラスの別コンストラクタを呼び出すための「this()」に似た「super()」を記述すると親クラスのコンストラクタを呼び出すことができる
親クラスのコンストラクタの呼び出し
super(引数);
もしコンストラクタの1行目でsuper()を呼び出していない場合、コンパイラによって「super();」という行が自動的に挿入される -
親インスタンス部分が作れない状況
コンストラクタは引数一致していないと作れずエラーになる -
正しい継承
継承が「is-aの原則」というルールに則っている継承のこと
is-aの関係(子クラスis-a親クラス(子クラスは親クラスの一種である)) -
間違った継承
概念としてis-aの関係がないにもかかわらず、継承を使ってしまう -
汎化・特化の関係
子クラスになるほど「特殊で具体的なもの」に具体化(特化)
親クラスになるほど「抽象的で曖昧なもの」に一般化(汎化)
第11章 高度な継承
-
新しい「立場」で考える
開発者は二つの立場になってクラスを作成する- 現在、目の前のプログラム開発に必要なクラスを作る開発者(既存クラスを継承し子クラスを作る)
- 未来に備え、別の開発者が将来利用するであろうクラスを準備しておく開発者(親クラスとなるクラスを作っておく)
-
「未来に備える開発者」の役割
他の開発者が効率よく安心して利用できる継承の材料を作ること -
2つの不都合、3つの心配
不都合1:継承の材料となるクラスを作る時点では、その処理内容をまだ確定できない詳細未定のメソッドが存在する
不都合2:クラスには2つの利用方法(newによる利用、継承による利用)があり、開発者はそれを自由に選ぶことができる
第一の心配事:オーバーライドを忘れる
第二の心配事:「本当に何もしない」と区別がつかない
第三の心配事:意図せずnewして利用されてしまう
安全な「継承の材料」を実現するためには
- 詳細のメソッドを宣言
Javaには、「詳細未定のメソッド」を記述する専用の構文が準備されています
詳細未定のメソッド(抽象メソッド)を宣言
public abstract 戻り値の型 メソッド名(引数リスト);
abstractキーワードが付けられたメソッドは、抽象メソッドと呼ぶ
抽象メソッドを含むクラスの宣言
public abstract class クラス名 {
...
}
抽象クラスの制約
抽象クラスは、newによるインスタンスかが禁止される
- 抽象階層を上に遡ると
- 抽象メソッドが増える
内容は確定できないが、一応存在するという抽象メソッドが現れ始める - 抽象メソッドやフィールドが減っていく
クラスに定義してある抽象メソッドやフィールドが減っていく
- 抽象メソッドが増える
Javaでは抽象度が高い抽象クラスを、インターフェースとして特別に扱うことができる
インターフェースとして特別扱いできる2つの条件
- 全てのメソッドは抽象メソッドである
- 基本的にフィールドを一つも持たない
インターフェースの宣言
public interface インターフェース名 {
...
}
インターフェースの実装
public class クラス名 implements インターフェース名 {
...
}
インターフェースの効果
- 同じインターフェースをimplementsする複数の子クラスたちに共通のメソッド群を実装するように強制できる
- あるクラスがインターフェースを実装していれば、少なくともそのインターフェースが定めたメソッドは持っていることが保証される
クラスにはないインターフェースの特権
異なる実装が衝突する問題が発生しないため、複数の親インターフェースによる多重継承が認められている
public class クラス名 implements
親インターフェース名1, 親インターフェース名2, ... {
...
}
public class PrincessHero
implements Hero, Princess, Character {
...
}
- インターフェースの継承
継承元 | 継承先 | 使用するキーワード | 継承元の数 |
---|---|---|---|
クラス | クラス | extends | 1つ |
インターフェース | クラス | implements | 1つ以上 |
インターフェース | インターフェース | extends | 1つ以上 |
extendsとimplementsを一緒に使う
public class クラス名 extends 親クラス implements 親インターフェース1, 親インターフェース2, .... {
...
}
第12章 多態性
-
開発をラクにする多態性
多態性(polymorphism)はオブジェクト指向プログラミングを支える3大機能の1つで、多様性やポリモーフィズムと呼ばれることもある -
インスタンスを代入可能かチェックする
Javaには、キャストする前に「キャストしても大丈夫かどうか」をチェックできるinstanceof演算子が用意されています
安全にキャストできるか判定する
変数 instanceof 型名
-
インスタンスを曖昧に捉える
- 継承によりis-aの関係が成立しているなら、インスタンスを親クラス型の変数に代入することができる
- 親クラス型の変数に代入することは、そのインスタンスを曖昧に捉えることである
-
「箱の型」と「中身の型」の役割
- どのメソッドを利用できるかは、箱の型(対象をどう捉えているか)で決まる
- メソッドがどう動くかは、中身の型(対象が何であるか)で決まる
-
捉え方の変更
- キャスト演算子を用いれば、厳密な型への強制代入ができる
- 不正な代入が行われた場合、ClassCastExceptionが発生する
-
多態性
- 厳密には異なる複数のインスタンスを同一視して、親クラス型の配列にまとめて扱うことができる
- 同様に、親クラス型の引数や戻り値を利用して、厳密には異なる対象をまとめて処理できる
- 同一視して取り扱っても、個々のインスタンスは各クラスにおける定義に従い、異なる動作を行う
第13章 カプセル化
カプセル化とは、具体的にはフィールドへの読み書きやメソッドの呼び出しを制限する機能
- 4つのアクセス制御レベル
Javaでは、それぞれのメンバ(フィールドおよびメソッド)に対してアクセス制御の設定を行うことができる
制限のレベル | 名称 | 指定方法 | アクセスを許可する範囲 |
---|---|---|---|
制限が厳しい | private | private | 自分自身のクラスのみ |
↑ | package private | (何も書かない) | 自分と同じパッケージに属するクラス |
↓ | protected | protected | 自分と同じパッケージに属するか、自分を継承した子クラス |
制限が緩い | public | public | 全てのクラス |
privateやpublicなどはアクセス修飾子と呼ばれ、フィールドやメソッドを宣言する際、先頭に記述することでアクセス制御が可能
アクセス修飾子 フィールド宣言;
アクセス修飾子 メソッド宣言 { ... }
privateアクセス修飾
privateであっても、自分のクラスからthis.~での読み書きは可能
publicやpackage privateを利用する
メンバに関するアクセス修飾の定石
- フィールドはすべてprivate
- メソッドは全てpublic
getterメソッド、単にnameフィールドの中身を呼び出し元に返すだけの単純なメソッド
public フィールドの型 getフィールド名() {
return this.フィールド名;
}
setterメソッドとは、ある特定のフィールドに指定された値を代入するだけのメソッド
public void setフィールド名(フィールドの型 任意の変数名) {
this.フィールド名 = 任意の変数名;
}
-
getter/setterの存在価値
メリット1:ReadOnly、WriteOnlyのフィールドを実現できる
メリット2:フィールドの名前など、クラスの内部設計を自由に変更できる
メリット3:フィールドへのアクセスを検査できる -
非publicクラスとソースファイル
別パッケージのクラスから見えなくなってしまうpackage privateクラスですが、その代わりにpublicクラスでは許可されない次の2つが許可されている- クラスの名前はソースファイル名と異なっても良い
- 1つのソースファイルにクラスを複数宣言しても良い
-
メソッドでフィールドを保護する
第三部 もっと便利にAPI活用
第14章 Javaを支えるクラスたち
-
暗黙の継承
Javaの全てのクラスがtoString()を持っている
クラスを定義するとき、extendsで親クラスを指定しなければ、java.lang.objectを親クラスとして継承したとみなされる -
Objectクラスの存在価値
理由1:多態性を利用できるようになるから
理由2:全てのクラスが最低限備えるべきメソッドを定義できるから -
等値と等価の違い
等値:同一の存在であること(「==」を使った判定)
等価:同じ内容であること(「equals」を使った判定) -
toString()とequals()のオーバーライド
新しいクラスを開発したら、toString()とequals()をオーバーライドする必要性がないかを検討する -
staticが付けられたメンバ
静的メンバ(static member)とは、staticキーワードが付けられたフィールドやメソッドのこと
同じクラスから生成されたインスタンスでフィールドを共有したい場合には、フィールド宣言の先頭にstaticキーワードを追加する
staticキーワードを指定したフィールドは特に静的フィールド(static field)と言われ、次の3つの特殊な効果をもたらす
- フィールド変数の実体がクラスに準備される
静的フィールドへのアクセス方法クラス名.静的フィールド名
- 全インスタンスに、箱の分身が準備される
静的フィールドへのアクセス方法インスタンス変数名.静的フィールド名
- インスタンスを1つも生み出さなくても共有の箱が利用可能になる
public static final コンビネーション
多くの場合、staticはfinalやpublicと一緒に指定され、「変化しない定数を各インスタンスで共有するため」に利用される
- 静的メソッド
staticキーワードがついているメソッドは、静的メソッド(static method)またはクラスメソッド(class method)と呼ばれ、静的フィールドと合わせて静的メンバと称される、以下の3つの効果が現れる- メソッド自体がクラスに属するようになる
- インスタンスにメソッドの分身が準備される
- インスタンスを1つも生み出すことなく呼び出せる
クラス名.メソッド名();
インスタンス変数名.メソッド名();
第15章 文字列と日付の扱い
- 文字列を調査する
Stringクラスに備わる文字列調査のメソッド
操作 | メソッド定義 |
---|---|
内容が等しいか調べる | public boolean equals(Object o) |
大文字、小文字を区別せず内容が等しいか調べる | public boolean equalslgnoreCase(String s) |
文字列長を調べる | public int length() |
空文字か(長さが0か)を調べる | public boolean isEmpty() |
- 文字列を検索する
- 含まれるか否かだけを判定するもの
- 文字列のどこに含まれているかという位置情報を返すもの
操作 | メソッド定義 |
---|---|
一部に文字列sを含むか調べる | public boolean contains(String s) |
文字列sで始まるかを調べる | public boolean startsWith(String s) |
文字列sで終わるかを調べる | public boolean endsWith(String s) |
文字ch(またはstr)が最初に登場する位置を調べる | public int indexOf(int ch) / public int indexOf(String str) |
文字ch(またはstr)が後ろから検索して最初に登場する位置を調べる | public int lastIndexOf(int ch) / public int lastIndexOf(String str) |
- 文字列を切り出す
操作 | メソッド定義 |
---|---|
指定位置の1文字を切り出す | public char charAt(int index) |
指定位置から始まる文字列を任意の長さだけを切り出す | public String substring(int index) / public String substring(int index, int endIndex) |
- 文字列を変換する
操作 | メソッド定義 |
---|---|
大文字を小文字に変換する | public String toLowerCase() |
小文字を大文字に変換する | public String toUpperCase() |
前後の空白を除去する | public String trim() |
文字列を置き換える | public String replace(String before, String after) |
- StringBuilderを用いた連結
- appendメソッドを読んでバッファに文字列を追加していく(必要に応じた回数を呼び出す)
- 最後に一回だけtoString()を呼び、完成した連結済みの文字列を取り出す
Stringインスタンスの不変性
Stringインスタンスが保持する文字列情報は、インスタンス化した際に初期化され、以後2度と変化することはない(newで新しいインスタンスが生成・格納される)
-
正規表現の基本文法
- 通常の文字:その文字でなければならない
- ピリオド:任意の1文字であれば良い
- アスタリスク:直前の文字の0回以上の繰り返し
- 波カッコ:指定回数の繰り返し
- 角カッコ:いずれかの文字
- 角カッコ内のハイフン:指定範囲のいずれかの文字
- ハットとダラー:先頭と末尾
-
splitメソッド:文字列の分割
Stringクラスのsplitメソッドを使うと、1つの文字列を複数に分割できる -
replaceAllメソッド:文字列の置換
StringクラスのreplaceAllメソッドを使うと、文字列中でパターンに一致した箇所を別の文字列に置換できる -
日時情報を扱う2つの基本形式
形式1:long型の数値
形式2:Date型のインスタンス
形式3:人間が指定しやすい「6つのint」形式
形式4:人間が読みやすいString型のインスタンス -
Calendarクラスの利用
6つのint値からDateインスタンスを生成する
Calendar c = Calendar.getInstance();
c.set(年,月,日,時,分,秒); または c.set(Calendar.~, 値);
Date d = c.getTime();
Dateインスタンスから「6つのint値」を生成する
Calendar c = Calendar.getInstance();
c.setTime(d);
int year = c.get(Calendar.YEAR);
int month = c.get(Calendar.MONTH);
int day = c.get(Calendar.DAY_OF_MONTH);
int hour = c.get(Calendar.HOUR);
int minute = c.get(Calendar.MINUTE);
int second = c.get(Calendar.SECOND);
- SimpleDateFormatクラスの利用
StringからDateインスタンスを生成する
SimpleDateFormat f = new SimpleDateFormat(書式文字列);
Date d = f.parse(文字列);
DateインスタンスからStringを生成する
SimpleDateFormat f = new SimpleDateFormat(書式文字列);
String s = f.format(d);
第16章 コレクション
配列のように「順序をつけて並べて格納する」データ構造を特にリストと呼ばれている
コレクションクラスの一種で最も理解しやすいのがArrayList
特徴は以下の3つ
- import文を記述する
-
<>
記号を使って、格納するインスタンスの型を指定する - 宣言時のサイズ指定は不要であり、要素は随時追加できる
コレクションクラスは「どんな型のインスタンスでも格納できるように作られている」が、インスタンスではないものは格納できない
- ArrayListの宣言と操作
ArrayList<~~> 変数名 = new ArrayList<~~>();
要素を格納する(add()またはset())
要素を取り出す(get())
リストを調査する(size())
要素を削除する(clear())
newの後ろに記述する<>記号の中を省略することも可能
<>をダイヤモンド演算子と呼ぶことがある
- 要素を順に取り出す3つの方法
for文を用いたリスト要素の取り出し
for(int i=0; i < リスト変数.size(); i++) {
/* リスト変数.get(i) で要素を読み書き */
}
拡張for文を用いたリスト要素の取り出し
for(リスト要素の型 e : リスト変数) {
/* e で要素を読み書き */
}
- イテレータ
リストの中身を1つずつ取り出す最後の方法
リストに含まれる1つの箱を指定している
Iterator<リスト要素の型> it = リスト変数.iterator();
イテレータを用いたリスト要素の取り出し
Iterator<リスト要素の型> it = リスト変数.iterator();
while (it.hasNext()) {
リスト要素の型 e = it.next();
/* 要素eを用いた処理 */
}
-
連結されたリスト
コレクションフレームワークにはArrayListの他にLinkedListというクラスが準備されている
ArrayListは配列を応用して作られている、LinkedListは連結リストと呼ばれる構造を応用している
主な違いは以下の2つ- 要素の挿入や削除に対する違い
リストの途中に要素が挿入または削除される - 添え字の指定に対する違い
- 要素の挿入や削除に対する違い
-
ざっくり捉えるとどちらもList
-
コレクションクラスの全体像
色々なクラスやインターフェースを含んでいるインターフェースである -
java.util.HashSet
set関連クラスは複数の情報を重複なく格納する集合(セット)というデータ構造を実現するためのもの
Setの基本特性- それぞれの要素には、重複が許されない
- それぞれの要素には、基本的に順序関係がない
setとlistを比較したときは3点を注意しなければならない
- 重複した値を格納しようとすると無視される
- set()やget()がない
- 要素を1つずつ取り出す場合の順序は不明
-
Setの実装バリエーション
順序が保障されるSetバリエーション
LinkedHashSet:値を格納した順序に整列
TreeSet:自然順序付けで整列 -
ペアを格納するデータ構造
マップ(Map)とは、2つの情報をキー(key)と値(value)のペアとして格納するデータ構造 -
HashMapクラスの利用
HashMapのインスタンス化
Map<キーの型, 値の型> マップ変数 = new HashMap<キーの型, 値の型>();
Mapでは値の重複は許されているが、キーの重複は許されていない
- HashMapの中身を1つずつ取り出す
マップに格納された情報を1つずつ取り出す
for(キーの型 key : マップ変数.keySet()) {
値の型value = マップ変数.get(key);
/* keyとvalueを用いて何らかの処理を行う */
}
第17章 例外
-
不具合のないプログラムを目指す
動くコードは書けて当たり前、不具合対策こそが腕の見せ所 -
3種類の不具合と対処法
- 文法エラー(synta error)
- 実行時エラー(runtime error)
- 論理エラー(logic error)
-
例外的状況
開発者が開発時にテストをしっかり行い、コードを修正することで、本番での発生を予防できる- パソコンのメモリが足りなくなってしまった
- 存在すべきファイルが見つからない
- nullが入っている変数を利用しようとした
-
例外処理の方法
Javaでは例外処理にtryとcatchという2つのブロックを使用する(try-catch文)
tryとcatch、2つのブロックのうち通常、実行されるのはtryブロックだけで、catchブロックの処理は動かない。tryブロック内を実行中に例外的状況発生すると、処理は直ちにcatchブロックに移行する
Javaにおける例外処理の基本パターン
try {
通常実行される文
} catch(...) {
例外発生時に実行される文
}
-
例外の種類
- Error 系例外
- Exception 系例外
- RuntimeException 系例外
-
後片付け処理への対応
例外発生の如何を問わず必ず処理を実行する
try {
本来の処理
} catch (例外クラス 変数名) {
例外が発生した場合の処理
} finally {
例外があってもなくても必ず実行する処理
}
「後片付け処理」には必ずfinallyを使う
try-with-resources文
try(closeによる後片付けが必要な変数の宣言) {
本来の処理
} catch(例外クラス 変数名) {
例外が発生した場合の処理
}
- チェック例外の伝播とスロー宣言
スロー宣言による例外伝播の許可
アクセス修飾 戻り値 メソッド名(引数リスト) throws 例外クラス1, 例外クラス2, ... {
メソッドの処理内容
}
スロー宣言が及ぼす影響
- 呼び出される側のメソッドは、メソッド内部での例外のキャッチが義務ではなくなる
- 呼び出す側のメソッドは、例外を伝播してくる可能性があるメソッド呼び出しをtry-catch文で囲む義務が生まれる