本稿の意図
Java Silver SE11受験に臨むにあたり、受験直前に総復習するための流し見用として、さまざまな詰まりやすい点をまとめています
本稿の構成とその理由
黒本と紫本の練習問題2個ずつ、計4種類を行い、間違えた問題のみでやり直し、それでも間違えたものや、なんとなくで正解できたもの(=理解が著しく足りてないものや怪しいもの)について、ポイントを列挙していきます。常識的な部分や、2,3度触れればインプットできる要素は除外するという形です。
本稿が合わない人
これを書いてる人は競プロ(AtCoder)で入茶して即逃げしましたが、逆に言えばギリ茶くらいの感覚はわかるので、二次元配列や二重ループなどの問題はほぼ間違えなかったため、そういった問題は列挙されていません。配列操作が苦手だあ〜という方で、継承関係やモジュール周りが得意な方は合わないかもしれません。
以下、雑多に列挙
ジャンルごとにまとめているような。
頻出の文字列イコール問題
StringはtoStringしても参照を返すだけだが、String以外にtoStringすると新しくStringを作る StringBuilderでも新しくStringを作る
インターンはすでにリテラルの文字列があるかどうかなのでnew Stringにインターンした場合は別になる
文字列のイコールやequalsではなく、配列のイコールを問う問題の場合は注意。equalsを使うと配列の場合は同値性ではなく同一性になるため、参照が同じかどうかの確認になってしまう
ラムダ式
ブロック内の変数をラムダ式内で使うにはfinalか実質的にfinalでなければならない
ラムダ式のパラメータ(引数)は実質的にはローカル変数として扱われるため、同じブロックのローカル変数で同じ変数名が定義されている場合はコンパイルエラーとなる
switch
条件はハッシュコードなのでnullを渡すとぬるぽになる
Javac java コマンド関連
javacコマンドは−dオプションを使って出力先を指定しない限り、ソースファイルと同じ場所にクラスファイルが出力される
javaコマンドは引数に完全修飾クラス名を指定する(カレントディレクトリから見て) -cpオプションをつければクラスパス指定で実行できる
--describe-moduleオプションはモジュールの設定を調べる
矢印は依存しているものに対して向く
exportsしているパッケージには向かない
usesは別のモジュールが実装クラスを提供していることを表している
モジュール内のプログラムを実行するには
java --module-path モジュールのルートディレクトリ -m 実行したいモジュール名/完全修飾クラス名
--module-pathは -p でも可
モジュール記述子(module-info.java)の情報を表示するのは
jdeps --check
モジュール検索パスの順番
- ルートモジュール
- コンパイルモジュール
- アップグレードモジュール
- システムモジュール
- アプリケーションモジュール
module-info.javaはモジュールのルートディレクトリに配置する 何も使わなければカラにしてOK
モジュールを実行するとモジュールに対してクラスパスが通るのでモジュール内のプログラムはモジュール内のディレクトリにアクセス可能
観測可能なモジュールリストを確認するのは java --list-modules
jdepsはモジュール型JDKにおけるJavaクラス間の依存関係を分析する
--module-source-path 複数のモジュールをコンパイルするためのオプション
モジュール同士でrequiresが循環する循環依存はNG
インターフェース
public強制
ひし形継承(Aインターフェースを継承したBとCのインターフェースがそれぞれ同じメソッドを持ち、それぞれ実装したDクラスではBとCどちらのメソッドを使うか指定しないとコンパイルエラー)
インターフェースがインターフェースを継承する場合は多重継承OK
暗黙的にpublic abstractで、具象クラスで実装する際はpublicよりアクセス修飾子を締めることはできない。必ずpublicを維持しなければならない
継承や抽象クラスへの実装を挟みまくってるようなものは、実装を忘れているインターフェースがないか注意
staticメソッドであれば実装していても記述していい defaultとは別の考え方
オーバーライド オーバーロード
Aクラスに無いものはBクラスであったとしても、A型で定義した以上はBクラスのインスタンスだとしても使えない
Aクラスにあって、Bクラスで実装もしくはオーバーライドしたようなものは、A型で定義したBクラスのインスタンスだとしても実装先のBクラスのものを使える
オーバーライドは強く、仮にスーパークラスのメソッド内で同名メソッドを参照する場合でもオーバーライド先の同名子クラスのメソッドに行き先が向く
オーバーロードの場合、型が厳密な方を優先する
プライベートはオーバーライドできない
メソッドはオーバーライドになるが、インスタンス変数の場合は型のほうが優先される。
親と子で同じ名称の変数があっても、型のほうが優先となる。
シグニチャ(メソッド名、引数)は同じ※厳守
戻り値は同じ型かサブクラスなら可
アクセス修飾子は、同じか緩める方向なら可
throwsは新たに付け加えたら別扱い すでにある場合はスローする例外と同じ型かサブクラスなら可
オーバーライドはするなら厳密に、でなければオーバーロードにせよ
引数の型が同じの場合、戻り値がサブクラスの型でも可
引数の型が違う場合はオーバーロードで、その場合は戻り値はなんでも可
コレクション
List.of Map.of Set.of などの不変なコレクションに操作を行った場合はコンパイルエラーではなく例外になる
キャスト
longからStringはキャストできない 1+"2"だと12になるのは別の変換の仕組み
キャストしたければString.valueOf(i);を使う
intにcharを代入する場合は暗黙的な型変換が行われる
サブクラス型のものをスーパークラス型のものに先祖返りさせる代入の場合はキャスト不要
スーパークラス型のものをサブクラス型にさせる代入の場合はキャストが必要(互換性がある場合のみ)
初期化の罠
クラスのメンバ変数なら宣言だけでも勝手に型に応じて初期化される
メソッド内のローカル変数の場合は宣言だけでも可だが、その後に使う場合はどこかで初期化しなければならない
if文の分岐で初期化する場合、trueでもfalseでも初期化できるようにしておかなければ、その後の使用時にコンパイルエラーになる 問題で記述されている条件式がなんであれ、trueでもfalseでも初期化できるようになっているかが重要
String[] str = new String[2];
の場合、それぞれの配列の要素はただのnullなので、concatを使おうとしただけでぬるぽになる
char型の初期値は空文字('\u0000')になる(nullではないので注意)
var にした場合はlist = {1,2,3}のようにはできず、newしなければならない
初期化子{}は変数宣言と同時でないと使えない
一旦宣言してからはだめ
定数は未初期化の場合、コンストラクタを通じて必ず初期化をする必要がある
いくつかコンストラクタがあって、どれかが未初期化定数の初期化を行っていない場合はコンパイルエラーになる
例外とエラー
検査例外、非検査例外、エラーはそれぞれ異なる
無限ループの場合はエラー
非検査例外はキャッチせずとも問題ない
throwしたエラーと同じ型(親なら可?)でなければいけない、サブクラスのエラーだけキャッチしようとすると怒られる
tryに引数? try(~~~) という形はtry-with-resourcesで、Javaが自動的にリソースをクローズしてくれるので、明示的にクローズする必要がなくなる。リソースがAutoCloseableインターフェース(またはそのサブインターフェースのCloseable)を実装している必要がある
ぬるぽの法則
printlnでnullが渡った場合は、toStringではなく"null"という文字列を出力するので大丈夫、これは特別
基本的にはnullを”参照”しようとするとぬるぽになる
雑多
Classの中のClass=インナークラス 修飾子はなんでもOK
do-while(など)でif内ではないcontinueやbreakがある場合、その下に何かしら処理が書いてあると「絶対に通らない」のでコンパイルエラー
JDK11では32ビットはなし JavaFXもなし Web Startは削除された アプレットも削除された
関数型インターフェースの型パラメータのTは参照型のみ使える
staticなメソッドからはstaticではないものは参照できない
mapでキーを取り出すはkeySetでutil.set型、バリューを取り出すにはvaluesでutil.Collection型
1個ずつ取り出すにはmap.forEach((k,v) -> set(v));
型の互換性
int[3]のプリミティブ配列は、long[] argで受け取れなくはないが、変換が必要になる
Object argなら、配列を単なるオブジェクトとしてそのまま処理できるのでこちらが優先される
StringBuilderのdeleteCharAtは (int index)であり、直接'A'のように指定しない
staticインポートは import static の記述順 使いたいメソッドは名前までインポートする
JavaDBはSE8まで
sort()の比較は><ではなく引き算を使う