JavaSilver SE17 学習メモ
JavaSilverSE17の取得に向け、以下の黒本・紫本での学習あるいはAIに聞いたり自分で動かしてみた結果について、個人的におさえるべき要点のメモ書き。
黒本:徹底攻略Java SE 17 Silver問題集[1Z0-825]対応
紫本:オラクル認定資格教科書 Javaプログラマ Silver SE 17(試験番号1Z0-825)
1章 Java概要
同じファイル内に複数クラス書く場合、1クラスだけpublic class指定可(指定しなくてもOK)。
指定したら、ファイル名をそのpublicクラスと同じ名前にする。mainメソッド有無は関係なし。
(ただし、ソースファイルモード実行の場合はその限りではない。)
◆コンパイル
javac -d (クラスファイル生成場所) (ソース名)でパッケージのディレクトリ構成を生成してコンパイル(クラスファイル生成)
◆実行
実行時は完全修飾クラス名(FQCN)を指定。パッケージ構成通りにクラスファイルを配置する。
java ~.javaでソースファイルモード実行。クラスはメモリ上に作成されるのでファイルは作成されない。
mainメソッドを持つクラスを一番上に書く必要あり(publicじゃなくてもOK)。
--source (Javaバージョン番号)指定で.java以外の拡張子のソースも実行可能。
例)java --source 17 Sample.xyz
クラスパス指定-cpについて。
デフォルトだとカレントディレクトリを起点(ルート)としてパッケージ構成をたどるが、
-cp付けると起点(ルート)を好きに指定して実行できる。
例)java -cp classes com.se.Main
指定しなければカレントからjava com.se.Main
(試験範囲外?)
デフォルトコンストラクタなど自動作成されるコード確認:コンパイル後、javap (クラス名).class
2章 基本データ型と文字列操作
識別子に予約語は使用不可。使える文字は、英字、数字、記号_ $(1文字目は数字以外)。
Java 予約語一覧:
ローカル変数は型によらず、自分で初期化が必要。使う前に初期化してるか、初期化してなくても使わなければOK。初期化せず使うとコンパイルエラー。特に、ifなどによる分岐で初期化されないケースがあるままの使用もNG。
(フィールドや配列は自動初期化あり)
final定数は宣言時に初期化しなくてもいいが、代入は1度限り。
final String s; s = "Java"; ←OK
変数宣言を1文で行うルール。
データ型は1度のみ記載。変数宣言のみと、初期化まで行う記述混ざっててもOK。
例)float f1, f2 = 3.1415F;
ただし、var使用時は複数同時宣言は不可。
var a1 = 10, a2 = 20; ←これはエラー。
配列宣言の[]の書き方は自由。
1次元:int[] a、int a[]
2次元:int[][] a、int a[][]、int[] a[]
3次元以上:int[][] a[]とかint[] a[][][]もOK
配列宣言。newして同時に初期化なら、要素数は書かないこと。
int[] a = new int[3]; ←OK。要素数を省略int[]だとエラー
int[] a = new int[]{10, 20, 30}; ←OK。int[3]だとエラー
配列リテラル(newなしの初期化)は、配列宣言と同時にしか使えない。既存の配列変数へ代入はダメ。
a = {10, 20, 30}; ←これはエラー
2次元以上の配列は宣言時に1次元目の要素数は必須。2次元目の要素数を省略すると2次元目の配列インスタンスが生成されない。
int[][] a = new int[][3]; ←エラー
2次元目でそれぞれ要素数を変えることも可能(ジャグ配列)。
int[][] a = new int[][]{{10}, {10, 20}, {10, 20, 30}};
varは配列にも使える。ただし、宣言時に型が分かるときだけ。
var a = new int[]{1, 2, 3}; ←OK(var[]じゃないことに注意)
var a = {1, 2, 3}; ←これはエラー。右辺を配列リテラルだけだと型が分からないのでNG。
配列のclone()メソッドは、配列インスタンスは複製されるが、<u>各要素の参照までは複製されず同じ参照を共有となる</u>ため注意(浅いコピー)。
- テキストブロック
開始「"""」の直後に文字列NG。終了「"""」直前は文字列OK。
ダブルクォーテーションはそのまま「"」か「\"」でもOK。3連続を含めるときは開始終了と区別させるためエスケープ「\"""」。
行末尾に「\」で改行がエスケープされ結果が改行されなくなる。
行末尾の余分な空白は取り除かれるので、末尾にスペース入れるときはスペースのあとに「\s」入れる(これ自体でもスペース1個分になる)。
終了「"""」の行でも余分な空白は取り除かれるので注意。
↓のs1とs2は同じく空文字。
String s1 = "";
String s2 = """
""";
インデントは1行目の"""を除いて、最後の"""までを含む最も浅いところ</u>**から基準に調整される。
String s = """
abcde
fgh
igk
""";
|⇒ここから
- StringBuilder (可変文字列)
インスタンス生成時、内部配列に<u>16文字分</u>のバッファを持つ。
new StringBuilder(); :16文字
new StringBuilder("abc"); :3+16文字
内部配列のサイズはcapacity()、実際の文字数はlength()で確認可能。
末尾へ追加はappend、間への挿入はinsert。
deleteやreplaceやsubstringは開始~終了のインデックスを指定する。
StringBuilderには等価性を判断するequals()は用意されてないので、toStringでString型にしてからequalsを使って比較する。
| メソッド | メモ |
|---|---|
| delete(int start, int end) | 開始位置、終了位置を指定。 |
| reverse() | 反転する。元の文字列が変化する。 |
| substring(int start, int end) | 元のオブジェクトは変更されない。String型変数に代入して使う。 |
| replace(int start, int end, String str) | Stringクラスとは違って、開始終了を指定(対象文字列の引数指定なし) |
| ・・・ | |
| ・・・ | |
リテラルの範囲 覚える用メモ
| データ型 | サイズ | 大体の範囲 |
|---|---|---|
| byte | 8ビット | -128~127 |
| short | 16ビット | ±32_000 (-32768~32767) |
| char | 符号なし16ビット | 0~65535 'd'は10進数値で100。これだけ覚えてあとは増減 |
| int | 32ビット | ±2_100_000_000 (-2147483648~2147483647) |
| long | 64ビット | intより大きい |
2_200_000_000 ←これだけでコンパイルエラー。これ自体がintリテラルだが範囲超えているため。LつけたらOK。
数値リテラルの_区切りは、先頭・末尾以外、また記号(. L F 0b 0x)の隣以外ならつけてOK(連続123___456もあり)。
char型は特殊文字もOK。'\n'とか'"'
Stringクラスの代表的なメソッド
参考:https://qiita.com/MagicShowTime/items/6f788fa9e2d525ccdbb0
Stringオブジェクトはイミュータブルなので、文字列操作のメソッドでも元々の文字列は変更されない。生成された新オブジェクトを別変数に格納するかメソッドチェインして使う。
| メソッド | メモ |
|---|---|
| indexOf(String str, int fromIndex) | 位置指定したら、それ以降で出現した場所を返す。 |
| replace(char oldChar, char newChar) replace(CharSequence target, CharSequence replacement) |
文字列の最後まで置換する。 引数の型の組合せとして(char, char)か(String, String)はOK。<u> replace("aa", 'b')とかはダメ</u> |
| replaseAll(String regex, String replacement) | 正規表現OKバージョン。単純な置換はreplaceを使う。 |
| length() | <u>半角も全角も1文字カウント</u> |
| endsWith(String suffix) | true/falseを返す。引数にchar型はダメ |
| startsWith(String prefix) startsWith(String prefix, int toffset) |
true/falseを返す。引数にchar型はダメ。 開始位置を指定可能 |
| concat(String str) | 文字列を末尾に連結 |
| valueOf(Object obj) valueOf(char data[]) |
valueOf(null)を呼ぶとより引数が具体的な(char[] data)の方が呼ばれ、NPE発生する。valueOf((Object)null)とすると"null"が作られる。 |
| compareTo(String anotherString) | 辞書順比較。等しければ0、引数の文字列より小さい場合は負、大きい場合は正。 |
| toString() | すでに文字列なので、return this;となっており自分自身の参照を返す(新オブジェクトは作らない)。 |
3章 演算子と条件分岐
整数リテラルはデフォルトでint型、小数リテラルはデフォルトでdouble型。
基本、大きい型から小さい型へはキャストが必要。ただし、
- <u>定数のとき</u>・・・コンパイル時に値が確定しているので<u>収まる範囲であればキャスト不要</u>
byte b1 = 127;←右辺はint型だが収まるのでOK
byte b2 = (byte)128;←これは収まらないのでキャストする(ただし値が変わる)
ただし小数やlongの場合は、値が収まる場合でも、キャストしないとエラー。
float f = 10.0;←これはエラー。右辺はdouble型なのでキャストするかFつける。
int i = 10L;←これも値は収まるが左辺は明示的にlong値なのでエラー。 - <u>変数が入るとき</u>・・・コンパイル時に値が確定しないので、キャスト必要。
int a = 127; byte b = (byte)a;
char → 数値型(byte / short)へはキャストが必要(範囲外があるため)。
それ以外(int / long / float / double)への変換はキャスト不要。
char a = 'd'; byte b = (byte)a;
異なる型どうしの演算の結果は、基本的には大きい方の型になる。
ただし(byte/short/char)の演算は常にint型に昇格されるので注意。
short s1 = 1; short s2 = s1 + 2; ←これはエラー。キャストするか、int型を用意する。
ビット演算子は以下。テスト出る??
| 演算 | 演算子 | 説明 |
|---|---|---|
| & | AND | |
| | | OR | |
| ^ | XOR |
0b0011 ^ 0b0101みたいに使う。 |
| ~ | NOT |
~0b000111みたいに使う。intは負の値も含むので最上位は符号ビット。 |
| << (シフト回数) | 左シフト | |
| >> (シフト回数) | 算術右シフト | 符号を保つ。-8 >> 2 → -2
|
| >>> (シフト回数) | ゼロ埋め右シフト | 符号無視。-8 >>> 2 → 1073741822
|
ラッパークラス 変換ルール
-
「同型」なら自動でボクシング/アンボクシング
int → Integer:オートボクシング
Integer → int:オートアンボクシング
例)
Integer a = 10; Double d = 10.0; Long l = 10L;
int a = new Integer(10); double d = new Double(10.0); -
「型変換+ボクシング/アンボクシング」は基本ダメ!
-
ワイドニング+ボクシング
int → long → Long
例)Long a = 10; Double d = 10; :コンパイルエラー -
ナローイング+ボクシング(ただし定数ならOK)
int → byte → Byte
Double → double → int
例)int a = 10; Byte b = a; :コンパイルエラー
例)Byte b = 10; :コンパイル時に確定する定数でbyteの範囲内なのでOK
-
Stringオブジェクトの同一性と等価性
(紫本 問題3-8, 3-9)
- 文字列リテラルで生成したStringはプールで管理される。
コンパイル時に同じリテラルが使われたら、すでにプールにあるオブジェクトを参照する。
new Stringで明示的に作成したらプールではなくインスタンス用のメモリに新しく生成されるので別参照になる。 - intern()メソッドは文字列の内容をみて、プールにあればその参照を返す。
例)
String s = "java";
String j = "j";
String s1 = j + "ava";
⇒これはコンパイル時に確定しないため新しく生成されるので、sとは別の参照になる。
String s2 = (j + "ava").intern();
⇒こうすると同じ内容の文字列"java"がプールにあるので、sと同じ参照になる。
注意)以下のようなケースでもプールに作られるので、internは同じ参照を返す。
//コンパイル時にプールに"def"を作成
String s1 = new String("def"); //ヒープに"def"を新規作成
String s2 = new String("def"); //ヒープに"def"を新規作成
// intern() は文字列プールにある同じ参照を返す
System.out.println(s1.intern() == s2.intern()); //true
- <u>StringBuilderで作る文字列はStringとは完全別物として考える。</u>つまり・・
- toString使っても新しく生成される
- Stringクラスのequalsの引数はString型以外の場合はfalseにするので、StringBuilderとの内容比較には使えない。
if文
else ifは途中で改行できない。
{}を省略した場合、別のif文として扱われる。
if(true)
System.out.println("AAA");
else
if(true)
System.out.println("BBB");
else
↓
if(true) {
System.out.println("AAA");
} else {
if(true) {
System.out.println("BBB");
} else {
}
}
switch文・switch式
defaultの記載は、switch文なら任意、switch式なら必須(必ず値を返す必要があるため)。
defaultとcaseの記載順は関係なく、上から順に評価実行されていく。
break書かないと、<u>一致したcaseから下まですべて実行される(フォールスルー)</u>。
->使えば、一致したcaseだけ実行されるのでbreak文が不要。
switch()内に指定できるのはbyte、short、int、char、String、enum(試験外)と、該当のラッパークラスのみ(longやfloat、doubleはダメ)。
null直接指定はコンパイルエラー。nullの変数を指定すると例外NullPointerException。
case値は定数のみOK。nullはダメ。変数の場合はfinalであればOK
switch(式)に指定したものと同じデータ型である必要あり。
例)
このcase値は計算結果はint型だが、
定数同士の計算でかつcharの範囲内(0〜65535)なのでOK。
switch("java".charAt(0)){
case 'a' + 106:
~処理~
}
char chr = 'a' + 106; //これもOK
switch式は、文中で使われてると「}」のあとに;必須。
また、switch式の中でreturnやbreakを使用してもswitchの外側に対して行われる。
: を使うとき・・
値を戻すためのyield(イールド)必要。書き忘れるとフォールスルー発生(break使用可)。
複数処理書くときは{ }ブロックなしでそのまま書ける。
->を使うとき・・
戻す値だけそのまま書ける。ただし複数処理を書いたときだけyield(イールド)使う。
複数処理書くときは{ }ブロックをつける。
4章 繰り返しと制御文の組み合わせ
whileやforの条件式にfalseや1==2など<u>コンパイル時にfalseが確定している指定</u>は到達不能のブロックになるのでコンパイルエラー。ただしdo-while文やif文はOK。
break ・・一番近いループから抜ける。
continue ・・一番近いループの現在の処理を中断して次のループへ移る。
ラベル使用で抜ける場所をコントロールできる。
ラベル宣言: (ラベル名):
使用方法: break (ラベル名);やcontinue (ラベル名);
使用可能場所: さまざまな場所に使用可能。コードブロック、ループと分岐(if, switch)、式、代入、return、try、throwなど。
while文・do-while文
do-while();を書き忘れたらコンパイルエラー。
for文・拡張for文
初期化式と増減は「,」区切りで複数可能。条件式は1つだけしか書けないので、複合する。
for(int i = 0, j = 0; a == b && c == d; i++, j++) {}
初期化式にvarによる型推論を使用可能。
3か所とも省略可能(条件式省略の場合は無限ループ)。
for( ; ; ) {}
<u>ただし、2つの;は省略できない。</u>
拡張for文の変数はfinalつきで宣言可能(ループ内での再代入は不可になる)。普通のfor文でも増減式書いていなければ可能。
(拡張for文の変数は値を入れ直しているのではなく、ループごとに新しく宣言し直しているイメージ)
for(final String s : str){}
for(final int i = 0; i < 5; ) {}
5章 クラスの宣言とインスタンス化
- クラスのメンバ変数の初期化
finalありの場合、自動初期化されないので初期化必須。
インスタンス変数(staticなし)・・・メンバ宣言時かコンストラクタかstatic初期化子
クラス変数(staticあり)・・・メンバ宣言時かstatic初期化子
finalなしの場合、デフォルト値に自動初期化されるので初期化は任意。
インスタンス変数(staticなし)・・・インスタンス生成時に自動初期化
クラス変数(staticあり)・・・クラスロード時自動初期化
可変長引数はメソッドに1つだけ、最後に記載する。
必ず型の直後に...をつけるint... num。(int num...はダメ)
(紫本 5.3 メソッドの呼び出し優先度)
オーバーロードしている場合、指定した実引数により以下の優先度で呼ばれるメソッドが決まる。
①データ型完全一致
②基本データ型の暗黙の型変換
③基本データ型とラッパークラスのオートボクシング/アウトボクシング
④可変長引数
例)
<u>完全一致したメソッドが優先して</u>呼ばれる。
この場合、method(1, 2)を呼ぶと①が呼ばれる。
①void method(int a, int b) {}
②void method(int y, int... y) {}
- <u>参照変数経由以外</u>での同じクラス内のメソッドの呼び方まとめ
staticメソッドからthis.は使えない(インスタンス生成しないから当然)。
メソッド名()だけで呼べるのは<u>同じクラス内で、かつインスタンスメソッド→インスタンスメソッド、または static→static</u>の場合!
| 呼び出すメソッド | 呼ぶ側 | 呼び出し方 |
|---|---|---|
| インスタンスメソッド | 同じクラス内のインスタンスメソッド | <u>メソッド名()またはthis.メソッド名()</u> |
| インスタンスメソッド | 同じクラス内のstaticメソッド | <u>×(呼べない)</u> |
| staticメソッド | 同じクラス内のインスタンスメソッド | <u>メソッド名()またはthis.メソッド名() またはクラス名.メソッド名()</u> |
| staticメソッド | 同じクラス内のstaticメソッド | <u>メソッド名()またはクラス名.メソッド名()</u> |
-
コンストラクタ
コンストラクタに指定するアクセス修飾子はなんでもOK。クラスのアクセス修飾子と同じでなくてもOK。
(例えばprivateのコンストラクタを定義して、メソッド経由でしかインスタンス化できないようにしたりできる) -
static初期化子
クラス内にstatic { }を記載すると、<U>クラスロード時(クラスを使用したタイミング)</U>に処理される。
staticフィールドの初期化などに使える。 -
インスタンス初期化子
クラス内に{ }を記載すると、<U>インスタンス生成時</U>に処理される。
コンストラクタ内の共通処理のまとめなどに使える。 -
インスタンス生成時の処理順
<U>(あれば)Mainクラスのstatic初期化子 → static初期化子 → インスタンス初期化子 → コンストラクタ</U>
(参考)インナークラス、ローカルクラス
たぶんSilverの範囲外。https://www.sejuku.net/blog/22637
-
インナークラス・・・クラスの中に定義するクラス。アクセス修飾子はなんでも付与可能。
-
ローカルクラス・・・メソッドの中に定義するクラス。アクセス修飾子は付与できない。
6章 継承とインタフェース
親クラスと子クラスでの同名メンバ定義について
子クラスのインスタンスは、<u>親クラスのインスタンス</u>と<u>差分(子)クラスのインスタンス</u>で構成される。
そのため、子クラスのコンストラクタを呼ぶと、先に親クラスのインスタンスを作る必要があるため、1行目に親クラスのコンストラクタが呼ばれてから、差分クラスのコンストラクタが呼ばれる。
this.はそれが書かれたクラスのインスタンスを参照することに注意。ただしそれがオーバーライドされたメソッドであれば、そのメソッドを呼び出す。
-
フィールドの場合・・
親クラス側が隠蔽される(staticやアクセス修飾子は関係なし)。
この場合、<u>参照変数の型によって、どっちにアクセスするか決まる。</u>
(フィールドはコンパイル時の型チェックで決める静的バインディングなので)
親クラスにgetterがある場合、子クラスでオーバーライドしてなければ、親クラスのフィールドが返される。 -
メソッドの場合・・
オーバーライドか、または隠ぺいの形。
親がprivateで子から見えてなければ継承されないので、同シグネチャでも定義可能。
親がstaticで子側でも同じくstaticにすると、隠ぺいになる。
staticはクラスに属するためオーバーライドはできない。そのため、
親と子でstaticかそうでないかが異なる同シグネチャは定義不可。
隠ぺい:同シグネチャでstaticをstaticで上書きすること。
コンストラクタ内で別のコンストラクタ呼ぶとき、最初の行でかつsuper(…)かthis(…)どちらか1回呼べる。両方は呼べない。
this(…)のときは必ず別のコンストラクタ。自分自身を呼ぶとコンパイルエラー(コンパイル時に検出されるのでStackOverflowErrorではない)。
インタフェース
インタフェースと抽象クラスはインスタンス化不可!
インタフェース同士は複数継承OK。例)interface A extends B, C {}
通常クラスは複数実装OK。例)class A implements B, C {}
実装クラスでインタフェースの抽象メソッドをオーバーライドしていなくても、
オーバーライドの条件を満たすメソッドが定義されているクラスを継承していれば、問題なく動作する。
インタフェースのメソッドは暗黙的にpublicなので、オーバーライド先では必ずpublicを指定する!
-
defaultメソッド
ほかの抽象メソッドと同様、defaultメソッドも暗黙的にpublicになる。ほかの修飾子つけるとエラー。
defaultメソッドでObjectクラスのメソッドのオーバーライドは不可。- 衝突
複数実装でdefaultメソッドが衝突(シグネチャ一致)した場合はオーバーライド必要。
なので、<u>両メソッドの戻り値の型に互換性※がないとオーバーライドでまとめられない</u>ので、その場合は複数実装不可。互換性ある場合は、より具体的な方の型に戻り値を統一(Object vs Stringなら<u>String</u>、Number vs Integerなら<u>Number</u>)。特に、どちらかがプリミティブ型(intなど)であれば確定でオーバーライド不可。
(※互換性:参照型で継承関係あり)
オーバーライド後、もともとのdafaultメソッドを呼び出したいときは継承元インタフェース名.super.メソッド名()でアクセス。1階層上のみ。
- 衝突
-
フィールド
- 暗黙で
public static finalがつく。なのでフィールド宣言時で初期化必須。 - アクセスには
インタフェース名.変数名か、実装していればクラスの参照変数.変数名、または実装クラスからは変数名そのままでもアクセス可能。
- 暗黙で
-
staticメソッド
重要!!
インタフェースのstaticメソッドは<u>インタフェース名.メソッド名()でのみ</u>呼び出せる。実装していても参照変数.メソッド名()では呼べない。実装クラスからでもメソッド名()ではアクセスできない。ただ、同インタフェース内のstaticメソッドからは、メソッド名()でアクセス可能。
(普通のクラスは参照変数からも呼べるし、継承先だとそのままメソッド名で呼べる)
(普通のクラスのstaticメソッドは継承されるが、インタフェースのstaticメソッドは継承されない) -
privateメソッド
インタフェース内の複数のdefaultメソッドやstaticメソッドに共通する処理をまとめるとき、privateメソッドを定義して処理を記述できる。privateなので、同じインタフェース内のdefaultメソッドやstaticメソッドから使用する。
staticメソッドから呼び出す場合は、private staticを指定する。
シールクラス
クラスにもインタフェースにもsealedつけられる。
インタフェースにつけた場合、permits指定先はクラス・インタフェースどちらでもOK(クラスには実装を強制、インタフェースには継承を強制)。
permitsにレコードクラス指定不可(レコードクラスはクラスの継承できないので)。
non-sealed指定するときは、シールクラスをextendsか、シールインタフェースをimplementsが必要。
sealed class順番に注意。
sealed指定時、同じソースファイル内のクラスを指定する場合はpermits省略可能(つまり、指定必須ではない)。
同じソースファイル内で指定外のクラスは、sealedクラスを継承しなければOK。
レコードクラス
暗黙的にjava.lang.Recordクラスを継承するため、レコードクラスは他のクラスを継承不可(インタフェースの実装は可)。また、暗黙でfinalなので普通のクラスもレコードクラスは継承不可。
普通のクラスと同様、publicかパッケージプライベート。
「record」はクラス名、レコード名、インタフェース名に使用不可( 「Record」はOK)。ただし予約語ではないので変数名には使える。
定義したコンポーネントはprivate final。
明示的にフィールド追加は不可。ただし、staticなフィールドはOK。
以下の3つは自動オーバーライド。明示的にオーバーライドも可能。
- hashCode()
- toString()
出力例⇒Sample[name=A, age=20, address=Tokyo] - equals()
参照先が同じ(同一)か、または各コンポーネント値が全部同じであればtrue。
コンストラクタについて。
<u>アクセス修飾子は、レコード定義と同じかより緩いものを指定する。</u>
(通常クラスは自由につけられることに注意!)
自動追加される標準コンストラクタのほか、明示的に書くコンストラクタは2種類。
-
代替コンストラクタ
this.ですべてのコンポーネントを初期化しない場合、他のコンストラクタを呼び出して<u>全てのコンポーネントの初期化必須(finalのため)。</u>コンストラクタ呼び出しが入ってないとエラー。 -
コンパクトコンストラクタ
引数を省略。<u>標準・代替コンストラクタ内の初期化処理前に実行される</u>ので、初期化前の値の検証、変換などが可能。
return使用不可(初期化されないままインスタンス化されてしまうのを防ぐため)。
別のコンストラクタ呼び出し不可。また、コンパクトコンストラクタは1個まで。
初期化前なのでフィールドアクセス不可。<u>⇒this.は使えない!</u>
パターンマッチング
パターン変数のスコープは<u>パターンに確実にマッチする範囲</u>(ローカル変数とは異なる)。マッチしていることが確定していれば{}外でもOK。if(obj instanceof String s && <u>s.length == 3</u>) {}みたいな短絡評価(ショートサーキット)での使い方でも、評価時にマッチ確定しているのでOK(この場合&や||だとNG)。
対象がnullだと確定でfalseを返す。
Object s = null; if(s instanceof String str) { } ←false
コレクション
- ジェネリクスについて
・内部処理のKとかVは、コレクション生成時に具体的に型が決まる!
(HashMap<Number, String>なら、K→Number、V→String)
・コレクション生成時、中身を書かないダイヤモンド演算子<>は右辺だけOK。左辺<…>の中身は省略できない!
ただしvar list = new ArrayList<>();はOK(Objectになる)
・両方に書く場合は、必ず同じ型にする。
・<…>そのものを省略の場合、要素はObject型になる。以下の書き方は問題なし。
例)ArrayList list = new ArrayList();
例)ArrayList list = new ArrayList<>();
- ArrayList
removeメソッドで要素を削除すると、<u>後ろの要素が繰り上がる!</u>
拡張for中でのremoveメソッドの動き注意(カーソルは動かない)。
特に削除後、後ろの要素を読み込むと例外ConcurrentModificationException発生。
主なメソッドと、戻り値のメモ↓
| メソッド定義 | 内容 |
|---|---|
| boolean add(E e) | trueを返す |
| void add(int index, E element) | 位置指定した場合は戻り値なし! |
| E remove(int idx) | 削除した要素を返す |
| boolean remove(Object o) | trueを返す(消す要素内容は指定してるからね) |
| E set(int idx, E e) | 置き換え前の要素を返す |
- HashSet
・数学の集合のイメージ。重複不可。
・メソッド名は基本ArrayListと同じ。
・toString()は[20, 10, 30]のように出力。順不同。
- HashMap
・Key-Valueで管理。Keyは重複不可。
・要素追加するメソッドはput(addではない!)
・toString()は{DE=Germany, UK=United Kingdom, FR=France}のように出力。順不同。
| メソッド定義 | 内容 |
|---|---|
| V put(K key, V value) | キーが既にある場合は置き換え前の値を返す |
| V removed(Object key) | 削除した要素の値を返す |
| Set<K> keySet() | キー一覧をSetで返す(重複ないから) |
| Collection<V> values() | 値一覧をCollectionで返す(重複あるかもだから) |
- 配列⇒リストの生成
・Arraysクラスのstatic <T> List<T> asList(T... a)を使用する。戻り値はList型のため、ArrayList型に入れられないことに注意。作られたListは固定サイズなので、追加や削除など要素数が変わる操作は<u>UnsupportedOperationException</u>になる。(List型なのでコンパイルはできるが…)
・List, Map, Setインタフェースのstatic <E> List<E> of(E... elements)を使用する。これは完全にイミュータブル(変更不可)なので、いかなる変更も<u>UnsupportedOperationException</u>になる。
★ オーバーライドの条件
-
メソッドのシグネチャ(メソッド名、引数リスト):同一
(引数の数、順番、<u>型も完全一致!</u>) -
戻り値の型:同一、または、サブクラスの型でもOK
-
アクセス修飾子:同一、または、より公開範囲の広いもの
-
staticメソッドはクラスに属すためオーバーライドできない(隠ぺい)
privateメソッドも、継承されないのでオーバーライドできない。 -
throws指定:同一、または、サブクラスの例外を指定。あるいは省略か非チェック例外の追加。
★ オーバーロードの条件
-
メソッド名は同一。引数の数、型、順番どれかを変えれば成立。
-
戻り値の有無(voidか、その他か)と、アクセス修飾子は関係なし
<u>⇒つまり、これだけ変えてもオーバーロードにならない!</u>
7章 例外処理
Exception以下のクラスは・・
・RuntimeExceptionとそのサブクラスは非チェック例外。
・それ以外はすべてチェック例外
メソッド定義においてチェック例外クラスをthrowsに指定した場合、<u>実際にthrow処理書いてなくても</u>、呼び出し側では処理が必須。ないとコンパイルエラー。
処理は、対応する例外クラスでcatchするか、もしくはさらにthrowsするか。
throwsには、実際にthrowするもののスーパークラスを指定可。ただし、呼び出し側のcatchにはそのthrows指定クラス以上しか指定できない(ダウンキャストされないから)。
throws指定したメソッドのオーバーライドは、<u>呼び出し元でキャッチできる範囲でthrowsの変更が可能!</u>
(省略か非チェック例外追加か、または同じ例外クラスかそのサブクラスか)
tryやcatchブロックでreturn文を入れても、finallyは実行されてから呼び出し元に戻る。
finallyブロックでもreturnを入れると、戻り値が上書きされる。
- multi-catch
以下の2つはコンパイルエラー。
・継承関係のある例外クラスを書く
例)catch(IOException | Exception e) {} ←これはダメ
・例外変数の再代入 (通常のcatchは可能)
捕捉した例外インスタンスは、記載した例外クラスの共通の親の型になる。
例)
catch (IOException | SQLException e)
⇒この場合、eはExceptionの型になる。
- try-with-resources
try()内に書けるリソースはjava.lang.AutoCloseableインタフェース実装したものじゃないとコンパイルエラー(java.io.CloseableもOK。実装しているので)。
varによる型推論が可能。ただしcatchの()内ではダメ。
例)try(var fw = new FileWriter("AAA.txt")) {}
try()の前で生成したリソースオブジェクトの指定も可能。try(fw)
tryブロックのみの定義が可能(普通のtry文ではcatchかfinally必須)。
try()で指定した変数は実質的にfinalである必要があるため、tryの外であっても再代入は不可。
例外発生してもしなくても、close()はtry終了直後に行われる。catchやfinallyの処理はそのあと。
なお、リソースを複数書いた場合は、書いた逆順でcloseされる。
例)try(fw1; fw2) {} はfw2⇒fw1の順
ちなみに・・・
close() の中で例外が発生した場合でも、他のリソースのclose()は続行。
複数の例外が出た場合は<u>最初の例外のみがスロー</u>され、後続は「抑制された例外(Suppressed Exception)」 として最初の例外インスタンスの内部に保持される。
抑制された例外を扱うにはThrowableクラスのgetSuppressedメソッドをcatch内の例外インスタンスに対して使う。
★ 主な例外クラスメモ
java.lang.Object
└── java.lang.Throwable
├── java.lang.Error // JVMやシステムの重大なエラー
│ ├── VirtualMachineError
│ │ ├── OutOfMemoryError
│ │ └── StackOverflowError
│ └── AssertionError
│
└── java.lang.Exception // アプリケーションが処理すべき例外
├── java.lang.RuntimeException // 非チェック例外(Unchecked)
│ ├── NullPointerException
│ ├── IllegalArgumentException
│ │ └── NumberFormatException
│ ├── IllegalStateException
│ └── IndexOutOfBoundsException // リスト
│ ├── ArrayIndexOutOfBoundsException // 配列
│ └── StringIndexOutOfBoundsException // 文字列
│
├── IOException // チェック例外(Checked)
│ ├── FileNotFoundException
│ └── EOFException
│
├── SQLException
├── ClassNotFoundException
├── InterruptedException
└── ParseException
(参考)
Stringのプールと似たような仕組みで、ラッパークラスのキャッシュがある。ラッパークラスの一部は、頻出する小さい値をキャッシュしてメモリ節約・高速化。ただしnew Integer(100)のように生成した場合に限っては必ず別のオブジェクト作られる。これはStringと同じ。
| クラス | キャッシュ範囲 |
|---|---|
| Byte, Short, Integer, Long | -128~127 |
| Character | 0~127 |
| Boolean | true / false |
Discussion