0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【作成中】【JavaSilver17黒本・紫本】重要ポイントまとめ

0
Last updated at Posted at 2025-11-13

目的

Java Silver17黒本の学習を進める中で得られた知見を章ごとにまとめ、復習に役立てること

Java Silver17黒本目次

開始P 終了P 内容 完了① 完了② 完了③
1 12 30 Javaの概要と簡単なJavaプログラムの作成 11/4 11/18 12/7
2 32 116 Javaの基本データ型と文字列の操作 11/7 11/19 12/7
3 118 204 演算子と制御構造 11/10 11/25 12/7
4 206 296 クラスの定義とインスタンスの使用 11/11 11/26 12/7
5 298 356 継承とインターフェース 11/13 11/26 12/8
6 358 398 例外処理 11/14 11/27 12/8
7 400 486 模擬問題1 12/4 12/15
8 488 568 模擬問題2 12/7 12/16
9 紫本の重要ポイント

学習の予定

(1)1~6章を3周する
(2)7~8章(模擬試験)を実施する

*25/12/28追記
黒本だけでは自分の実力的に一発合格が不安になったため、紫本の練習問題・模擬問題も利用する。
紫本から得た知識は、9章という扱いで記述する。

第1章:Javaの概要と簡単なJavaプログラムの作成

🟥2025/10/31(金)

(1)パッケージがもたらす3つの効果

  1. 名前空間(「パッケージ名.クラス名」の完全修飾クラス名)を提供し、名前の衝突を避ける
  2. アクセス修飾子と組み合わせて、アクセス制御機能を提供する
  3. クラスの分類を可能にする

(2)パッケージ宣言のルール

パッケージ宣言は、必ずソースコードの先頭行に記述する。

package sample;
public class Test {
    //any code
}

(3)インポート宣言を省略可能な2つのクラス

  1. java.langパッケージに属するクラス(頻繁に利用されるため)
    例:java.lang.String、java.lang.Integer
  2. 同じパッケージに属するクラス

(4)エントリーポイントとなるメソッドの定義5つ

■エントリーポイント...JVMが最初に処理を始めるメソッド。
要は、public static void main(String[] args)
この中で変えられるのはargsのみ。

  1. publicであること=公開されていること
  2. staticであること=インスタンスを生成しなくても実行できること
  3. voidであること=戻り値は戻せない
  4. メソッド名はmainであること
  5. 引数はString配列型を1つ受け取ること

🟥2025/11/4(火)

(5)javaコマンドの構文

■javaコマンド...JVMを起動するためのコマンド。
JVMは、起動後に指定されたクラスをロードし、このクラスのmainメソッドを呼び出す。
構文:java 完全修飾クラス名 [引数 引数 ・・・]
←この引数を、起動パラメータやコマンドライン引数と呼ぶ。

起動パラメータとして指定されたデータは、JVMによってString配列型オブジェクトに格納され、mainメソッドの引数として使われる。

例:java Main java one twoとしたとき、mainメソッドでsysout args[0]すると「java」が出力される。

(6)javaコマンドで実行できるもの

  1. mainメソッドを持つクラスファイル
  2. jarファイル内のメインクラス
  3. モジュールに含まれるメインクラス

Javaのプログラムを実行するには、javacコマンドではなくjavaコマンドを使おう。

■コマンド...ユーザーがコンピュータに特定の操作や処理を指示するための命令文。
・コマンドラインインターフェース(CLI)を通じて入力され、システムに対して直接的な操作を行うことができる。
・オペレーティングシステム(OS)やソフトウェアの機能を細かく制御するために使用される。
・ファイルの作成、ネットワーク設定の変更をはじめ、プログラムの実行(例:javaコマンド)も可能。

(7)起動パラメータの注意点

・起動パラメータは、スペースによって区切られる
・ダブルクォーテーションで囲った部分は、一つの文字列として扱われる
・¥でエスケープされると、"は効果を発揮しなくなる

(8)javacコマンドとjavaコマンドの違いと注意点

人間がソースコードを作成し、.javaファイルとして保存したのち、、、
■javacコマンド...ソースコード(.java)をコンパイルし、.classファイルを生成するコマンド。
■javaコマンド...javacコマンドにて生成された.classファイルを実行するコマンド。

コンパイルするときのjavacコマンドの引数には、拡張子の.javaまで含むソースファイル名を記述する
実行するときのjavaコマンドの引数には、クラスファイルのファイル名ではなく完全修飾クラス名を記述する(要は、.classの指定は不要で、クラス名だけ書けということ)

例:

javac Sample.java
java Sample

Java SE 11からはソースファイルモードが実装されており、コンパイルと実行を同時にできるようになった。
例:java Sample.java

(9)無名パッケージに対するアクセス 🔷25/11/18追記

無名パッケージに対しては、無名パッケージからしかアクセスできない。
明示的にパッケージ宣言したクラスから、無名パッケージに属するクラスにアクセスしようとすると、コンパイルエラーが発生する。

第2章:Javaの基本データ型と文字列の操作

(1)リテラルの基本

■リテラル...ソースコード中に記述する値のこと。
Javaには5つのリテラルがあり、整数、浮動小数点、真偽、文字、nullがある。それぞれ、int型、double型、boolean型、char型がデフォルト。
・リテラルが他のデータ型であることを明示するときは、「100L」や「3.0F」のように、値の後ろに接尾辞をつける。

(2)整数リテラルの注意点

整数リテラルは、10進数だけでなく、2進数や8進数、16進数でも記述できる。その場合、接頭辞をつけることになる。
・2進数...接頭辞「0b」をつける。例:0b0111111
・8進数...接頭辞「0」をつける。例:077
・16進数...接頭辞「0x」をつける。例:0x3F

(3)アンダースコア「_」を用いた整数リテラルの記述

使う目的:桁数の多い数値リテラルの見やすさを向上させるため。
以下の2つのルールに従えば、出現する場所や回数は自由。

  1. リテラルの先頭と末尾には記述できない
  2. 記号の前後には記述できない

(4)識別子の命名規則

■識別子:変数やメソッド、クラスなどの名前のこと
■予約語、キーワード:プログラムの文を表現するために必要な用語のこと
 例:intやdoubleなどのデータ型、forやifなどの文脈を表現する言葉

・識別子の命名には、次のような規則がある。

  1. 予約語を識別子として使うことはできない
  2. 使える記号は、①アンダースコア「_」と②$マークのみ
  3. 数字から始めてはいけない(2文字目以降であれば可)

(5)変数の型の互換性

char型とshort型...文字と数値は互換性あり。キャスト式が必要。
char型とString型...基本データ型と参照型は互換性なし。

(6)varを使った型推論

■var...ローカル変数を宣言する際のデータ型を推論する機能
 コンパイル時にコンパイラが代入演算子=の右辺から変数のデータ型を推論し、そのデータ型に置き換える。
 そのため、以下2点の場合はコンパイルエラーとなる。

  1. 変数が初期化されていない(右辺がないならvarは意味ないってこと)
  2. nullで初期化されている(nullで型推論するとはどういうことだと怒られる)

他にも、メソッドの戻り値からローカル変数の型を推論することも可能。
例:var a = sample();

ただし、ラムダ式だけではその式を扱う変数の型を推論できない。
例:var a = () -> {};

また、配列の初期化式も、右辺から何型の配列か特定できないため、推論できない。
例:var a = {1, 2, 3};

■ラムダ式:1回だけ使うような小さな関数(処理)を、名前をつけずに書く方法。
 書き方:(引数) -> { 処理 }

🟥2025/11/5(水)

(7)Stringオブジェクトを作成する方法2つ

  1. newを使ってインスタンス化する
  2. ダブルクォーテーション「"」で括った文字列リテラルを記述する

(8)immutable(不変)なクラス

オブジェクトの中には、一度セットしたフィールドの値を後から変更できないオブジェクトが存在する。
代表例:java.lang.Stringクラス、java.io.Fileクラス

そのため、文字列を変更するには、新しくインスタンスを作らなければいけない。
■StringクラスのreplicaAllメソッド...置換した結果の文字列を持った新しいStringクラスを作り、そのインスタンスへの参照を戻す。

(9)StringクラスのcharAtメソッド

インスタンスが保持している文字列から、引数で指定された位置にある1文字だけを抜き出して戻す。※番号は0から始まる
例:String str = "abcde"; str.charAt(4);の場合、eが出力される。
※ここでcharAt(5)とした場合、文字列の範囲外にアクセスしたことを示す例外java.lang.StringIndexOutOfBoundsExceptionがスローされる

a b c d e
0 1 2 3 4

(10)StringクラスのindexOfメソッド

引数で指定された文字が文字列のどの位置に存在するかを調べるためのメソッド。
例:String str = "abcde"; str.indexOf(c);の場合、2が戻される。
※また、引数で指定した文字が存在しなければ、「-1」が戻される。
※StringBuilderクラスにも同名のメソッドが存在し、使い方も全く一緒。

a b c d e
0 1 2 3 4

(11)Stringクラスのsubstringメソッド

文字列から任意の文字列を抽出するメソッド。
下記の場合、aの左に0、bの左に1、という風に番号が振られていく。
例:String str = "abcde"; str.substring(2,4);の場合、「cd」が戻される。

a b c d e

(12)Stringクラスのreplaceメソッド

文字列を置き換えるためのメソッド。第1引数に指定した文字列があれば、順番にすべて置換する。
例:String str = "aaaa"; str.replace("aa","b");の場合、「bb」が戻される。
※ここで、str.replace("aa",'b')と引数の型を一致させなかった場合、コンパイルエラーが発生することに注意。

(13)StringクラスのstartsWithメソッド

文字列が指定された文字で始まるかどうかを調べるためのメソッド。
調べた結果は、真偽値(true / false)で戻される。
例:String str = "abc"; str.startsWith("a")の場合、trueが戻される。
また、Stringクラスの多くのメソッドは、新しいStringインスタンスを作って戻すことを覚えておこう。

■メソッドチェイン...メソッドの戻り値に対してさらにメソッドを呼び出す記述
例:下の場合、「cc」が記述される。

String str = "abcde";
String str2 = str.substring(1,3).replace("b","c");
System.out.println(str2);

(14)Stringクラスのconcatメソッド

インスタンスが保持する文字列を、引数として渡された文字列と連結し、新しい文字列を戻すメソッド。
例:下の場合、「Hello, Java!」が戻される。

String str = "Hello, ".concat("Java!");
System.out.println(str);

※非常に似ているメソッドとしてappendメソッドがあるが、これはStringBuilderクラスのメソッドであることに注意!

(15)StringBuilderクラスのcapacityメソッド

StringBuilderのインスタンスの現在のバッファの容量を戻すメソッド。
例:下の場合、「21」が戻される(5+デフォルトバッファ数の16)。

StringBuilder sb = new StringBuilder("abcde");
System.out.println(sb.capacity());

★そもそも...なぜStringBuilderクラスができたか。
①文字列を連結させる際、「+」で連結させたい
②そのとき、Stringクラスは不変であるため、連結の度にインスタンスが作られメモリをムダに消費してしまう。
例:String str = "abd"+"de"+"fg"←abd、de、fg、abcde、abcdefgという5つのインスタンスが作られてしまう。
③それを解消するために生まれたクラスが、StringBuilderクラス。

■StringBuilderクラス...保持している文字列+16文字文のバッファを持ったクラス。

(16)StringBuilderクラスのappend、reverse、replaceメソッド

■appendメソッド...StringBuilderインスタンスの文字列に新しい文字列を追加するメソッド。
■reverseメソッド...StringBuilderインスタンスの文字列を反転するメソッド。
■replaceメソッド...第1引数と第2引数で指定した範囲の文字列を、第3引数の文字列に置き換えるメソッド。
例:下の場合、「eaba」が戻される。

StringBuilder sb = new StringBuilder();
sb.append("abcde");
sb.reverse();
sb.replace(1,3,"a");
System.out.println(sb);

🟥2025/11/6(木)

(17)テキストブロック

複数行にわたる文字列リテラルを簡単に記述できる記法の一つ。

String msg = """
        <html lang = "ja">
        <head>
            <meta charset="UTF=8">
            ・・・
        """

従来の書き方から変わった点は4つある。

  1. トリプルクォーテーション(""")で文字列リテラルを囲んだ
  2. +演算子を使った複数行の連結が不要になった
  3. ダブルクォーテーションのエスケープ(¥")が不要になった
  4. 改行文字(¥n)も不要になった

【書き方の注意点】
テキストブロックが開始されるトリプルクォーテーションの後は改行しなければいけない。

【インデントの注意点】
テキストブロック内の改行は、インデントが自動で調整される。
具体的には、テキストブロック内の最もインデント量が少ない行に基づいて行われる。
下記の例だと、テキストブロック内で最もインデント量が少ないのは下の"""であるため、
1つインデントが開いた状態で出力される。

String str = """
                 A
                  B
                   C
                """
System.out.println(str);

➡結果は、
■A
■■B
■■■C

(18)Javaにおける2つの「同じ」

■同一...その変数が同じインスタンスであること。「==」を使って確かめる。
■同値...インスタンスが持つ値の内容が同じであること。「equalsメソッド」を使って確かめる。

※注意点は、文字列リテラルの比較時。
文字列リテラルはプログラム中に頻繁に登場し、いちいちインスタンス化していてはメモリを圧迫する
➡他のリテラルと異なり、インスタンスとは異なるメモリ空間に文字列リテラルが作られ、
 そこへの参照がString型変数に代入される。
 もし、同じ文字列リテラルがプログラム内に再登場すれば、同じ参照が使い回される。
➡こうした使いまわしの仕組みを「コンスタントプール」と呼ぶ。

そのため、下記ではtrueが出力される。

String a = "sample";
String b = "sample";
System.out.println(a == b);

※このコンスタントプールは、あくまで文字列リテラルの自動生成の話。
new演算子を使ってプログラマーが明示的に「新しいインスタンスを作る」ことを記述した場合には、その都度インスタンスが作られ、それぞれの変数が異なる参照を持つ。

そのため、下記ではfalseが出力される。

String a = new String("sample");
String b = "sample";
System.out.println(a == b);

(19)Stringクラスのinternメソッド

コンスタントプールを含むメモリ内の文字列を探して、再利用するためのメソッド。
本来、String型を2つnewして同じ内容を書き込むと、参照は異なる。
しかし、internメソッドを使うと、参照は1つになる。
そのため、下記はtrueとなる。

String str1 = new String("def");
String str2 = new String("def");
System.out.println(str1.intern() == str2.intern());

(20)printlnメソッドに参照を渡すと起こること

printlnメソッドに参照を渡すと、
参照先のインスタンスのtoStringメソッドを呼び出し、その結果を表示する。
オーバーライドをしていない場合、全クラスが継承しているObjectクラスのtoStringが呼び出され、ハッシュコードが表示される。

(21)配列型変数の宣言

基本ルールは、データ型もしくは変数名の後ろに大カッコ[]をつける のみ。
下記の変数宣言は、すべてコンパイルが成功する。

int[] array;
int array[];

int[][] arrayA:
int arrayB[][][];

int[] arrayA[];
int[][] arrayB[];

(21)配列型変数と配列インスタンスの違い

int[] a = new int[3];があったとき、
配列型変数は、int[] aの部分。
配列インスタンスは、new int[3]の部分。

配列型変数の[]は、あくまで配列インスタンスの参照を格納する宣言でしかないため、
int[3] aという宣言はできず、コンパイルエラーとなる。

(21)配列インスタンスの生成方法の注意点

  1. 配列インスタンスの生成時は、必ず要素数を指定しなければいけない
    例:int[] array = new int[];←要素数を書いていないため、コンパイルエラー。

  2. 要素数の指定は、int型の整数値で記述しなければいけない
    例:int[] array = new int[2.3];←double型になっているため、コンパイルエラー。

  3. 多次元配列のインスタンスの生成時は、1次元目の要素数は省略できない
    例:int[][] array = new int[][3];←1次元目の要素数を書いていないため、コンパイルエラー。
    例:int[][] array = new int[5][];←OK

  4. 多次元配列を使う場合は、変数とインスタンスで次元数を一致させなければいけない
    例:int[] a = new int[2][4];←次元数が異なるため、コンパイルエラー。

(22)配列の要素のデフォルト値

配列の要素は次のように決まっている。

デフォルト値
整数型 0
浮動小数点型 0.0
真偽値型 false
整数型 ¥u0000
オブジェクト型 null

そのため、オブジェクト型の配列変数をデフォルト値(null)で初期化し、
その変数を参照しようとすると、NullPointerExcepotionが発生する。

(23)String型配列の要素のややこしさ

下記の例だと、
まず4つの箱の連なった配列変数が生まれて、その変数への参照がarrayに格納される。
その後、配列変数の各箱に、ABCDへの参照が格納される。

そして、array[0]をnullにした場合は、Aへ続く参照がなくなり、
nullというリテラルが残るので、
出力結果は「nullBCD」となる。

String[] array = {"A","B","C","D"}
array[0] = null;
for (String str : array){
    System.out.println(str);
}

(24)配列インスタンスの生成・初期化と配列型変数の宣言と参照の代入

・4つのステップを初期化子{}を使って一度に行うのが一番簡単。
int[] array = {2,3}
int[] array = new int[]{2,3}
※②のように、newと初期化子{}を両方使った場合、[]の中に要素数を記述してはいけない。
 理由は、初期化子{}の中に記述した値の数によって、自動的に配列の要素数が決まるため。
 NG:int[] array = new int[2]{2,3}

・また、初期化子{}だけを記述した場合、要素数ゼロの配列インスタンスが生成される。
下記2つのコードは同じ意味。
int[] array = {};
int[] array = new int[0];

・また、多次元配列の初期化は、初期化子をネストすることで実現できる。
int[][] array = {{2,3},{4,5}}

(25)非対称な配列

多次元配列では、2次元目以降の配列の要素数を揃える必要がない。
そのため、3つの要素をもつ1次元目の配列が、
2つ、3つ、4つとそれぞれ異なる要素数を持つ配列への参照を持つことも可能。

🟥2025/11/7(金)

(26)実務における金額計算の型

★資格試験からはズレるけど、実務で大事なこと ↓↓

金額計算や数量などの「正確な数値計算」を扱うときは、型選びが重要。

具体的には、
金額計算にdouble型を使ってはいけない!
代わりに、BigDecimal型を使うのが原則。

理由:doubleは誤差が出るから
double(浮動小数点数型)は、2進数で近似的に小数を表現している。
そのため、10進数の「0.01」などは正確に表現できず、浮動小数点誤差(丸め誤差)が発生する。

double a = 0.1;
double b = 0.2;
System.out.println(a + b); // 結果:0.30000000000000004

ややこしいけど、
プログラムとしてdoubleを記述する際には10進数で表現しているが、
ソフトウェア内部での数値は2進数で扱われる。
そのため、進数の変換を繰り返すと、正確に変換ができない数値の場合に誤差が発生する。

▲誤差が発生する例
10進数「0.3」→2進数「0.010011001100...」
2進数「0.010011001100」→10進数「0.2998046875」

✅ 正しいやり方:BigDecimalを使う

BigDecimalは任意精度の10進数を扱うクラスで、
金融・会計システムなどでは標準的に使われている。

import java.math.BigDecimal;

BigDecimal a = new BigDecimal("0.1");
BigDecimal b = new BigDecimal("0.2");
BigDecimal sum = a.add(b);
System.out.println(sum); // 結果:0.3

※Stringで初期化しているのは、
new BigDecimal(0.1) のように書くとまた誤差を引きずるため

(27)継承・実装関係にあるクラスやインターフェースのインスタンスの動作

継承もしくは実装関係にある型同士であれば、配列として扱うことができる。

public interface A {}
public class B implements A {}
A[] array = new B[]{new B(), new B()};

(28)java.util.ArrayListクラスの特徴

  1. オブジェクトであればどのような型でも扱える
  2. 必要に応じて要素数を自動的に増やす
  3. 追加した順に並ぶ
  4. nullも値として追加できる
  5. 重複した値も追加できる
  6. 任意の場所に値を挿入できる
  7. スレッドセーフではない※


■スレッド...プログラムの処理における1つの流れ(処理の流れ)。
■並行処理...スレッドを複数作って動作する処理形態。
■スレッドセーフ...並行処理をしたときに、意図しない結果になることを防ぐ機能が備わっている状態。
例:複数のスレッドによるインスタンスの同時共有が起こると、値が意図しない数値になってしまう。
この場合の対処として、
①処理のたびにインスタンスにロックをかけて安全性を高めて処理を遅くするか、
②ロックをかけずに安全性が低く単一処理が高速なプログラムとするか、の2択を迫られる。
どちらも選択肢としてはアリ。
①をスレッドセーフなクラス、②をスレッドセーフでないクラスと表現する。
ArrayListクラスは、②のスレッドセーフではないクラス。

(28)ジェネリクスを指定していないArrayListインスタンスの型推論

■ジェネリクス...クラスが扱える型をコンパイル時に決めることができる仕組み。
基本的に、コレクションAPI(例:ArrayList)はオブジェクトであればどんな型の集合でも扱える。

ArrayList list = new ArrayList();
list.add(new Object()); //オブジェクト型インスタンス
list.add("test"); //String型インスタンス
list.add(new Integer(10)); //Integer型インスタンス

しかし、ダウンキャストをした際は、Object型でデータが返ってくるため、
例外ClassCastExceptionが生じる可能性がある。

for (int i = 0; i < list.size(); i++){
    String str = (String) list.get(i); //ここでClassCastExceptionが発生!
}

こうした例外を避けるために、ジェネリクスを利用する。
コレクションで扱える型をStringに制限した場合、
ArrayList<String> list = new ArrayList<String>();となり、listにはString型しか入らない。
そうすることによって、listに入る型は制限される一方で、ダウンキャスト時の例外を避けることが可能となる。

(29)ArrayListのaddメソッド

ArrayListに要素を追加するためのメソッド。
第一引数にて、追加する場所を指定することもできる。
例:list.add(2, "B")
※しかし、追加する場所がArrayListに存在しない場合(1つ目の要素の左からインデックスを振り、0からスタートのため勘違いしやすい)は、IndexOutOfExceptionが発生するため注意。

(30)ArrayListのsetメソッド

ArrayListの要素を置き換えるためのメソッド。元の要素を上書きする。

list.add("A")
list.add("B")
list.set(0, "C")
System.out.println(list);
//出力結果は、「CB」になる。

(30)ArrayListのremoveメソッド

ArrayListの要素を削除するためのメソッド。
インデックスを指定して削除する他にも、
引数で受け取ったインスタンスと同じ要素を削除することも可能。
※条件に合致した要素が複数あった場合、最初の要素のみが削除されることに注意!

また、removeメソッドによってリストから要素が削除された場合、
後ろの要素が繰り上がる。
そのため、次のようなコードでは「A」のみが出力される。

list.add("A")
list.add("B")
list.add("C")
for (String str = list) {
    if ("B".equals(str)){
        list.remove(str);
    } else {
        System.out.println(str);
    }
}

また、このようにremoveメソッドにより要素が移動した後に読み出しを行うと、
例外ConcurrentModificationExceptionがスローされるため、注意。

(31)固定長のリストを作る方法

ArrayListは「動的配列」と呼ばれ、要素数を動的に増やせることが特徴。
反対に、固定長(=要素を削除・追加できない)のリストを作る方法には2通りがある。

  1. ArraysクラスのasListメソッドを使い、配列からリストのインスタンスを生成する
  2. Listインターフェースのofメソッドを使い、インスタンスを生成する

固定長のため、下記のように要素を変化させようとすると、
例外java.lang.UnsupportedOperationExceptionが発生する。

Integer[] array = {1, 2, 3};
var list = Array.asList(array);
list.remove(1);

第3章:演算子と制御構造

(1)代入演算子と演算子の動作

加算代入「+=」や減算代入「-=」の動作に注意しよう。

演算子 使用例 意味
= a = 10; 変数aに10を代入する
+= a += 10; a=a+10と同じ。
-= a -= 10; a=a-10と同じ。
*= a *= 10; a=a*10と同じ。
/= a /= 10; a=a/10と同じ。

また、下記コードのような代入の挙動を取ることにも注意。

int a = 3;
int b = a += 5; //ここで、a=3+5=8となり、以降a=8かつb=8の状態になる。
system.out.println(a + b); //8+8=16になる。
// 16が出力される。

(2)データ型と型変換

★振り返り:基本データ型(プリミティブ型)の情報一覧

種類 デフォルト データ型 ビット数 桁数 値の範囲・内容
真偽値 boolean 1 - true, false
文字 char 16ビット - '\u0000' ~ '\uFFFF'(Unicode文字)
整数 byte 8ビット 約3桁 -128 ~ 127
整数 short 16ビット 約5桁 -32,768 ~ 32,767
整数 int 32ビット 約10桁 -2,147,483,648 ~ 2,147,483,647
整数 long 64ビット 約19桁 -9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807
浮動小数点 float 32ビット 有効桁数 約7桁 ±3.40282347×10³⁸(約±10³⁸)
浮動小数点 double 64ビット 有効桁数 約15~16桁 ±1.79769313486231570×10³⁰⁸(約±10³⁰⁸)

そして、型変換にはいくつかルールがある。

変換方向 / 状況 キャスト要否 結果 / 備考
小さい型 → 大きい型 int i = byteVal; 不要 自動型変換(拡大変換)
大きい型 → 小さい型 byte b = (byte) intVal; 必要 明示的キャストが必要
リテラル値が範囲内 byte b = 10; 不要 コンパイル時に安全と判断される
リテラル値が範囲外 byte b = 128; コンパイルエラー 範囲外なのでNG
変数が範囲内の値(キャストなし) int i = 10; byte b = i; コンパイルエラー 実行時まで安全か判断できない
変数が範囲内の値(キャストあり) int i = 10; byte b = (byte) i; 必要 コンパイルOK・実行時もOK(値は10)
変数が範囲外の値(キャストあり) int i = 130; byte b = (byte) i; 必要 コンパイルOKだが実行時に値が変化(→ -126)

(3)インクリメントとデクリメント

■インクリメント演算子...「++」で表され、変数の値を1加算する。
■デクリメント演算子...「--」で表され、変数の値を1減算する。

また、前置するか後置するかで演算の処理が変わる
前置:a = ++a + a; ➡ a = 11 + 11; //演算子のついたaの値を変化させ、演算実行
前置:a = a++ + a; ➡ a = 10 + 11; //後のaの値を変化させ、演算実行

(4)ちょっと変わった関係演算子

一般的な関係演算子は、a==b(同一)やa=>b(以上)など。
風変わりなものとして、a instanceof b (aがbと同じクラスかbのサブクラスのインスタンスであればtrue)がある。
直訳の通りで、「aはbのインスタンスである。〇か×か?」という話。

また、大小を比較する関係演算子**「<」「>」「<=」「>=」は、数値型同士の比較にしか使えない**ため、注意! boolean型同士の比較とかできないよ!

(5)数値リテラルの計算時の注意

数値リテラルはデフォルトがint型のため、
計算結果が小数点を含む場合、小数点以下が切り捨てられる。
例:int result = 30 - 12 / (2 * 5) + 1;の場合、
  12/10の結果が1.2ではなく1になるため、result=30となる。

(6)equalsメソッドのオーバーロード

下記のような処理が存在するため、注意しよう。

【状況】
・呼び出し先クラスSampleでequalsメソッドをオーバーロードし、受け取る引数をSampleクラスに指定する。
・呼び出し元クラスMainで、Object型の変数aと変数bをequalsメソッドで比較する。

【結果】
Mainで呼び出したequalsメソッドは実引数がObject型であるため、
仮引数がSample型であるSampleのequalsメソッドは呼び出されず、
オーバーロードされていないデフォルトのequalsメソッドが呼び出される。

(7)equalsメソッドとnullの関係

equalsメソッドは、引数にnullを渡されるとfalseを戻す。

(8)ビット演算子

演算子 演算 説明
& AND 両方のビットが1の場合に1を返す
| OR どちらかのビットが1の場合に1を返す
^ XOR ビットが異なる場合に1を返す
~ NOT ビットを反転させる(0を1に、1を0に)
<< 左シフト ビットを左に指定した回数だけシフトし、右側に0を埋める
>> 右シフト(符号付き) ビットを右に指定した回数だけシフトし、符号ビットを保持
>>> 右シフト(符号なし) ビットを右に指定した回数だけシフトし、左側に0を埋める

(9)if文の{}省略

if文の中カッコ{}は省略可能で、
省略した場合は次の一文だけがif文の条件に合致したときの処理として実行される。
if文の{}を省略した直後にelseがあった場合、elseは起動し、その場合も{}は省略可能。

また、Javaのif文には「then」「end if」というキーワードは存在しないため、そこも注意。

(10)if-else if-else文の{}省略

if文と同様に{}省略可能で、次の一文のみが実行対象となる。
注意点は、else if文は「else」と「if」の間で改行することはできない。

もし中カッコ{}を使わずにelseとifの間で改行した場合、
elseブロック内に新しいif文が記述されているものと解釈される。

if (条件式A)
    //条件式Aに合致したときの処理
else
if(条件式B)
    //条件式Bに合致したときの処理
else
    //条件式Bに合致しなかったときの処理

このコードに中カッコをつけると、下記のようになる。

if (条件式A) {
    //条件式Aに合致したときの処理
} else {
    if(条件式B)
        //条件式Bに合致したときの処理
    else
        //条件式Bに合致しなかったときの処理
}

(11)switch文

構文は下記の通りで、
条件によって処理を分岐するif文に対し、値によって処理を分岐することが特徴。

switch(条件式) {
    case 値 : 処理
        break;
    case 値 : 処理
        break;
    case 値 : 処理
        break;
}

条件式が戻す値と一致するcase式が実行される。
条件式が戻せる値の型には制限があり、次のように覚えるとよい。

  1. int型以下の整数型とそのラッパークラス
  2. 文字と文字列
  3. 列挙型(enum※)

※■enum...複数の定数をひとつにまとめておくことができる型。

(12)switch文のcase値

分岐するために使う値を「case値」と呼ぶが、
case値として使用できる値は、次の条件を満たす必要がある。

  1. 条件式が戻す値と同じ型か互換性がある型であること
  2. 定数※であるか、コンパイル時に値を決めることができること
  3. nullでないこと
    ※final宣言された定数もしくはリテラルが該当する

(13)switch文のbreak

switch文では、breakを使った時点でswitch文を抜けることが可能。
breakを記述しなかった場合、以降に現れるすべてのcase式の処理が、
breakが現れるまで実行される。
(この性質は「フォールスルー」と呼ばれる)
このとき、default式も処理の対象になることに注意!
※default式は、switch文のどこに記述しても問題ない。

🟥2025/11/10(月)

(14)switch「式」

switch文では、フォールスルーを避けるためにbreakを多用するため、
冗長なコードになりがち。
→これを解決するために、switch式という新しい構文が生まれた。
 次のように**矢印「->」**を使うことが特徴で、caseごとのbreakは必要ない。
 switchブロックの末尾に「;」を付けるのを忘れずに!

switch (i) {
    case 1 -> なんたら;
    case 2 -> かんたら;
    case 3 -> うんたら;
};

(15)switch式におけるdefault

switch式では、すべてのパターンが網羅できるようにcaseを記述しなければいけない。
網羅できないのであれば、defaultを記述しなければコンパイルエラーになる。

(16)switch式のなかのブロック

switch式のブロックは、一致するcaseがあったときに複数の処理を実行したい場合に利用する。
ブロック内で値を戻すときは、yieldを使う。
間違えがちだが、switch式の中にreturnやbreakを書くとコンパイルエラーとなる。

String result = switch (i) {
    case 1 -> {
        System.out.println("case 1");
        yield "one";
    }
    case 2 -> "two";
    case 3 -> "three";
    default -> throw new IllegalArgumrntException("value:"+i);
};
実行結果:
case 1
one
two
three

(17)do-while文

少なからず1回は処理を実行し、その後条件判定を行い繰り返し処理を行う構文。
ポイントは、

  1. doの後ろにはカッコ()がつかない
  2. whileの後ろに条件式を記述する
  3. セミコロン;で終了する
do {
    //繰り返し処理
} while i(条件式) ;

(18)while文、do-while文における{}の省略

if文と同様、中カッコの省略が可能。
ただし、省略した場合は、直後の1つの文だけが繰り返し処理として扱われる。

int cnt = 0;
while (cnt++)
    System.out.println("A"); //←この文だけが繰り返しの対象となる!
    System.out.println("B");

また、do-while文で{}を省略した場合は、
doの後ろには1つしか文を記述してはいけない。
記述した場合はコンパイルエラーになる。

//これはコンパイルエラーになる!
int cnt = 0;
do
    System.out.println("A");
    System.out.println("B");
while (cnt++ < 5);

(19)for文の()内のルール

for文の構文は下記の通り。

for (初期化文; 条件文; 更新文){
    // 繰り返し処理
}

・初期化文においては、同時に複数の変数の宣言・初期化を行うことができる。
 ただし、その場合、変数は同じ型でなければならない。
 違う型を宣言・初期化した場合、コンパイルエラーが発生する。

 また、for文の初期化文で使った変数は、for文の外で使うことはできない。
 外で使った場合、コンパイルエラーが発生する。

・更新文においては、初期化文で宣言された複数の変数の値を同時に更新できる。
 メソッドを呼び出すことも可能。
 注意点として、処理の順番は、
 ①初期化文→②条件文→③繰り返し処理→④最後に更新文となる。

・条件文においては、カンマ「,」を使って初期化文や更新文のように複数記述することはできない。
 複数記述したい場合は、論理演算子を使って複合条件にしなければならない

for (int i= 0, j=0; i<3 && j<5; i++){
    //繰り返し処理
}

 また、条件文と更新文は省略することができ、省略した場合は無限ループが発生する。

(20)拡張for文

for文をシンプルな型にしたもの。
拡張for文で扱える集合には、配列のほか、java.lang.Iterableを実装するクラスがある。
代表例として、java.util.ArrayListを覚えておけばよい。

for (型 変数名 : 集合){
    //繰り返し処理
}

(21)繰り返し構文とインクリメント

条件式の判定とインクリメント判定のタイミングがややこしい。
下記例をもとに覚えよう。

int num = 10;
while (num++ <= 10) {
    num++;
}

この式では、条件式(num++ <= 10)にてnum <= 10の判定がされてから、++が行われる。
この時点でnumは11になった上で、繰り返し処理に遷移する。
そして、繰り返し処理のnum++が行われ、num=12。
ここで終わらないのがややこしく、
2回目の条件式の判定でnum 12 <= 10でfalseになり処理から抜けるのだが、
++が発動し、num=13になった状態でフィニッシュする。

(22)繰り返し構文におけるcontinue

breakと同様に、繰り返し処理を制御するためのキーワード。
breakが繰り返しを中断し、ループから抜けるのに対し、
continueはその回の繰り返し処理をスキップし、次の回のループにうつる。

(23)ラベル

ラベルを使うと、breakやcontinueのときに制御を移す箇所を自由に指定できる。
例えば、二重ループの内側でbreakした場合には、内側のループだけを抜ける。
しかし、下記のようにラベルを使うと、外側のループも抜けることになる。

sample:
for (int i=0; i<10; i++){
    for (int j=0; j<10; j++){
        if(3<j){
            break sample;
        }
    }
}

ラベルはループにつけられることが多いが、さまざまな箇所につけることが可能。
・コードブロック a:{}
・すべてのループ文と分岐 b:for (//条件式){}
・式 d:System.out.println(x);
・代入 e:x=2; 
・return文 f:return 0;
・tryブロックとthrow文 g:try{//処理} finally{h: throw 例外;}

第4章:クラスの定義とインスタンスの使用

(1)ガベージコレクション

・利用されなくなったインスタンスを解放することで、メモリに空きスペースを作るJVMの機能。
・ガベージコレクションの対象は、どこからも参照されなくなったインスタンス。
→参照元の変数にnullを代入すると、そのインスタンスはガベージコレクションの対象になる。

(2)staticなフィールドへのアクセス

・Javaでは、プログラムの実行に必要なクラスを読み込んで実行する。
 クラスファイルを読み込むことをロードと呼ぶが、ロード後、クラスファイルの内容はstaticな部分(意:動かない、静的な)であるstatic領域と、それ以外のヒープ領域に分離される。
・インスタンスが生成されるときには、ヒープ領域にあるクラス定義に従ってインスタンスが生成される。
・インスタンスが生成されてもstaticなフィールドはインスタンス内には存在せず、
 「クラス名.フィールド名」と記述してアクセスする。
 インスタンスの作成後であれば、「参照.フィールド名」でもアクセス可能。
例:Sample.num=10;s.num=10;

※スッキリJavaに出てきた、勇者のパーティーを作るとき、勇者たちそれぞれに財布を持たせるのではなく、パーティーで1つの財布を共有する場合に「static int 財布」を作ると便利という話がこれ。

(3)staticなメンバと非staticなメンバの関係性

・staticなメンバは、インスタンスがなくても使える
・非staticなメンバは、インスタンスがないと使えない
➡staticなメソッドから非staticなメソッドを呼び出そうとした場合、
 そのメソッドが存在するかは分からない。よってコンパイルエラーになる。
 逆に、staticなメンバ同士であれば、いつでも呼び出すことができるし、
 非static→staticの呼び出しも可能。

(4)メソッドのシグニチャ

■シグニチャ...メソッド名と引数のリストのセットのこと。

JVMは、プログラムで動作させるメソッドを選ぶ際、次の3つを見ている。

  1. 参照(どのインスタンスのメソッド?)
  2. クラス名(どのクラスに定義されているstaticなメソッド?)
  3. シグニチャ

メソッド名が同じでも、オーバーロードされている場合は、引数が異なる場合が多々ある。
その場合、メソッド名のあとに引数リストを見て、呼び出し元の実引数に対して仮引数が一致するメソッドをJVMは呼び出す。
仮引数が一致するメソッドがない場合、コンパイルエラーが発生する。

(5)順次処理

■順次処理...メソッドが記述した順に実行される性質のこと。
順次処理では、後続の処理で使う変数などは、使う箇所よりも前で宣言されていなければならない。変数が宣言よりも前に使われている場合、コンパイルエラーが発生する。

(6)可変長引数

■可変長引数...メソッド宣言時、引数の個数を自由に変更できる引数のこと。
・次のように引数の型の直後にピリオド3つ「...」を付けて宣言する。

void sample (int... num) {
    //do something;
}

・可変長引数として渡された値を使うときには、配列と同様に大かっこ[]を使う。

void sample (int... num) {
    for (int i=0; i<num.length; i++) {
        System.out.println(num[i]);
    }
}

・なお、可変長引数を使うときは、次の2点に注意する必要がある。

  1. 異なる型はまとめられない(配列と考えればわかりやすい)
  2. 可変長引数以外の引数も同時に受け取る場合は、可変長引数を最後の引数にする※
    ※そうしないと、どこまでが第一引数か第二引数かが分からなくなるため

(7)return文の注意点

次のコードのように必ずreturn文が実行される場合、注意点がある。
実行されないことが明白なコードがあるときは、
コンパイラは「到達不能なコードがある」としてコンパイルエラーを発生させる。

void sample(int num) {
    return;
    System.out.println(num); //ここでコンパイルエラーが発生!
}

(8)オーバーロードしたメソッドの呼び出し

下記コードにおいて、mainメソッドで呼び出したメソッドは、一つのメソッドに限定することができない。
理由:double型はint型より範囲が大きく、暗黙の型変換によって互換性が保たれるデータ型であり、2つのint型を実引数として渡した呼び出しはどちらのメソッドも該当するから。

よって、JVMは「あいまいなメソッド呼び出し」としてコンパイルエラーを発生させる。

public class Main {
    public static void main(String[] args) {
        Main m = new Main();
        System.out.println(m.calc(2,3));
    }
    private double calc(double a, int b) {
        return (a+b) / 2;
    }
    private double calc(int a, double b) {
        return (a+b) / 2;
    }    
}

(9)コンストラクタのルールとアクセス修飾子

■コンストラクタ...生成されたインスタンスがほかのインスタンスから使われる前に、事前準備を整える「前処理」をするためのメソッドの一種。
コンストラクタの作成には、次の3つのルールがある。

  1. メソッド名をクラス名と同じにすること
  2. 戻り値型は記述できない
  3. newと一緒にしか使えない(インスタンス生成時以外は呼び出しできない)

これ以外のルールは特にない。例えば、アクセス修飾子※は好きにつけてよい。

アクセス修飾子 内容
public すべてのクラスからアクセス可能
protected 同じパッケージに属するか、継承しているサブクラスからのみアクセス可能
なし(デフォルト) 同じパッケージに属するクラスからのみアクセス可能
private クラス内からのみアクセス可能

(10)コンストラクタと初期化子

コンストラクタもメソッドの一種であるため、オーバーロードして複数定義することができる。
このとき、すべてのコンストラクタで一部だけ共通の処理をする必要があったとする。
その場合、初期化子を使い、同じコードの記述を避けることが得策である。

初期化子「{}」は、クラスブロック直下にフィールドやメソッド、コンストラクタと並べて記述する。
コンストラクタより先に実行される。

public class Sample {
    {
        //初期化子で行う共通の前処理;
    }
}

(11)static初期化子

staticフィールドは、インスタンス生成前には値を変えることができない。
しかし、static初期化子を指定することで、値を変えることが可能。

public class Sample {
    static int num;
    static {
        num = 10;
    }
    public Sample() {
        num = 100;
    }
}

🟥2025/11/11(火)

(12)デフォルトコンストラクタ

プログラマがクラスにコンストラクタを一つも定義しなかった場合、
コンパイラは引数なしのコンストラクタをコンパイル時に定義する。
これをデフォルトコンストラクタという。

もし、プログラマがコンストラクタを1つでも定義していた場合、
デフォルトコンストラクタは作られない。
➡引数指定のないインスタンス生成をすると、コンパイルエラーが発生する。

(13)コンストラクタ内から別のコンストラクタを呼び出す

「this」を使って呼び出すことが可能。

public class Sample {
    public Sample() {
        this(null, 0);
    }
    public Sample(String str, int num){
        System.out.println("ok.");
    }
}

この際、注意点としてコンストラクタ呼び出しのコードよりも前には処理を記述してはいけない。
記述するとコンパイルエラーになる。

(14)importとアクセス修飾子の関係

他パッケージを自パッケージにimportすることによって、
他パッケージの要素を引用する際に完全修飾クラス名は省くことができる。

一方、パッケージが絡むアクセス修飾子のルールはそのまま適用されることに注意。
下記の場合、Parentクラスの変数numはデフォルトのアクセス修飾子のため、同一パッケージからしかアクセスできない。そのため、Childクラスの6行目でコンパイルエラーが発生する。

package ex26;

public class Parent {
    int num = 10;
}
package other;
import ex26.Parent;

public class Child extends Parent {
    public static void main(String[] args) {
        System.out.println(num);
    }
}

(15)インスタンスの作られ方とアクセス修飾子の関係

インスタンスは1つのクラスから作られているのではなく、
複数のクラスの定義から作られている。

親クラスを継承した子クラスのインスタンスを生成するとき、
そのインスタンスは親クラスの定義と子クラスの定義をそれぞれ併せ持ち、
親クラスのアクセス修飾子は変わらず存在することになる。

(16)基本データ型の値をメソッドに渡すときの処理

基本データ型の値を実引数としてメソッドを呼び出すとき、
その実引数は呼び出し元からコピーされて、別の存在として渡される。

そのため、呼び出した先のメソッドで値が更新されても、
呼び出し元の値は変わらない。
変えたい場合は、セッターメソッドを利用するしかない。

(17)参照型の値をメソッドに渡すときの処理

参照型の引数では、呼び出し元から呼び出し先のメソッドに参照値がコピーされて渡る。
そのため、2つのメソッドが参照するインスタンスは同じになる。
この場合、呼び出し先のメソッドでのインスタンスの値変更は、
呼び出し元のインスタンスの値も変更する。

(18)instanceof演算子

ある参照先のインスタンスが特定の型と互換性があるかどうかを確認する演算子。
左オペランドの変数が、右オペランドの型と互換性があればtrueを、なければflaseを返す。

Java SE 16以降は、instanceof演算子による型の互換性チェックと一緒にダウンキャストも行えるようになった。
これをinstanceof演算子のパターンマッチングと呼ぶ。
例:a instanceof B b 変数aがBクラスと互換性があるなら、aをBクラスの変数bに代入し、trueを返す。

instanceof演算子のパターン変数には、ローカル変数の宣言と同様にスコープがある。
下記コードのように、if文の条件式として宣言されたパターン変数は、ifブロック内でしか使えないため、elseブロック内で使われるとコンパイルエラーが発生する。

private static void test(A a) {
    if (a instanceof B b) {
        b.hello();
    } else {
        b.hello(); //bのスコープ外のため、コンパイルエラー発生。
    }
}

(19)タプルとレコード

■タプル...名前、年齢、住所など、関係する複数のデータをまとめて1つの単位として扱うデータ構造。
■レコード...データを保持する変更不可能なクラスを簡単に記述するための機能。Java SE 16から導入されている。Pythonなど別の言語では、名前付きタプルとも呼ばれる。

タプルでは、構成するデータ要素を配列のように番号で扱うため、どのデータにアクセスするかわかりにくいという側面があった。
→データ要素に名前を付けて、名前でアクセスできるようにしたものが名前付きタプル。

名前付きタプルは、以前のJavaでも作れたものの、
hashCodeメソッドやequalsメソッド、toStringメソッド等をオーバーライドしておく必要があり、
いちいち記述が面倒という側面があった。

→これらを自動的に作ってくれる機能がレコード。
下記コードだけで、toStringメソッド、hashCodeメソッドとequalsメソッド、getterメソッド、publicなコンストラクタがすべて自動的に実装される。

public record Customer(String name, int age) {
    //
}
public final class Person {
    private final String name;
    private final int age;

    public Person(String name, int age) {  // コンストラクタ
        this.name = name;
        this.age = age;
    }

    public String name() { return name; }  // getter(アクセサ)
    public int age() { return age; }
}

構文としては、アクセス修飾子 record 型名(カンマ区切りで構成要素の列挙){}
他の知識としては、
・java.lang.Recordクラスを継承したクラスとして宣言されている
・finalクラスであるため、レコードを継承することはできない。
・レコードを宣言すると、引数は自動的にprivateかつfinalで修飾される。

(20)レコード宣言時のルール

レコードを宣言できる箇所は、クラスの宣言と同じ。
理由は、レコードはコンパイルするとクラスになるため。

  1. クラスが独立したクラス(トップレベルクラス)
  2. クラス内のメンバとして宣言するインナークラス
  3. メソッド内で宣言するローカルクラス

宣言できるアクセス修飾子も、クラスの宣言と同じ。

  1. トップレベルクラス...publicかデフォルト
  2. インナークラス...すべて
  3. ローカルクラス...アクセス修飾子不可

(21)レコード内に宣言できるもの

  1. コンストラクタ
  2. メソッド
  3. staticフィールド
  4. static初期化子
  5. インナークラスやインナーインターフェース

staticではないフィールドが宣言できない理由は、
不変なデータを受け渡すものとしてレコードが設計されているため。

(22)レコードで自動生成されるgetter

次のようなルールに基づいて作られる。

  1. メソッド名は、レコードの構成要素として宣言された構成要素名と同じ
    (int valueが仮引数であれば、getValue()ではなくvalue()がgetterメソッドになる)
  2. publicなメソッドとして公開される
  3. throws句を持たない
  4. getterメソッド自体に型パラメータを持たせることはできない。レコードに定義された型パラメータしか使えない。
  5. 自動生成されるgetterの戻り値は、対応するフィールドとまったく同じ型になる。

(23)レコードのコンストラクタ

3種類存在する。

  1. 標準コンストラクタ...コンパイラによって自動的に生成される。
    目的は、フィールドの初期化。

  2. 代替コンストラクタ...プログラマが明示的に宣言する。
    目的は、プログラマが意図した処理の追加。
    いくつか宣言時のルールが存在する。
    ①標準コンストラクタと同じ引数を受け取ること
    ②代替コンストラクタ内で、すべてのフィールドを初期化すること
    ③アクセス修飾子は、レコードそのもののアクセス修飾子よりも厳しいものを使ってはいけない

  3. コンパクトコンストラクタ...標準コンストラクタで受け取る引数の検証を目的としている。(24)で解説。

クラスのコンストラクタは、プログラマが手動で設定するとデフォルトコンストラクタが設定されないが、
レコードは標準コンストラクタ(canonical constructor)を必ず持ち、代替コンストラクタを設定しても「this」で呼び出すか、引数が一致する代替コンストラクタを設定しなければならない。

(24)コンパクトコンストラクタ

レコード名だけで宣言するメソッド。
コンパクトコンストラクタを用意することで、
自動的に生成される標準コンストラクタでフィールドに引数の値を代入する前に、
その値の妥当性を検証することが可能。

public record Customer(String name, int age, String address) {
    public Customer {
        //引数チェックと例外チェック
    }
}

生成時には、いくつかの注意点が存在する。

  1. returnを記述してはいけない
    コンパクトコンストラクタは、コンパイルされることで標準コンストラクタと組み合わさって1つのコンストラクタになる。そのため、標準コンストラクタの処理が終わっていないにも関わらずreturnをしてしまうと、フィールドの初期化ができないままになってしまうため、return禁止。
  2. 明示的に別のコンストラクタを呼び出してはならない
    呼び出したコンストラクタから標準コンストラクタが呼び出される上に、コンパクトコンストラクタと標準コンストラクタが組み合わさるために、2回も標準コンストラクタが呼び出されることになってしまうため。
  3. フィールドにアクセスしてはならない
    フィールドの初期化は標準コンストラクタ内で実施するもののため、コンパクトコンストラクタ内では実施しない。
    これらは組み合わさる関係だが、その処理順番はコンパクト→標準のため、コンパクトの処理段階ではフィールドが初期化されていない。
  4. 1つしか宣言できない
    組み合わさる際に、標準からしてどれと組み合わさればいいか分からないため。

(25)レコードと実装

下記コードでは、Testインターフェースを実装することで、Dataレコードにはvalueメソッドが存在するが、レコードをコンパイルする際に同名のvalueメソッド(ゲッター)で自動でオーバーライドされる。
さらに、オーバーライドされる前のvalueメソッドは、戻り値がないvoidだったのに対し、
オーバーライドされた後のvalueメソッドはString型を返すため、この2つのメソッドに互換性がないとして、コンパイルエラーが発生する。

public interface Test {
    default void value() {
        System.out.println("A");
    }
}
public record Data(String value) implements Test {

}
public class Sample {
    public static void main(String[] args) {
        Data data = new Data("B");
        System.out.println(data.value());
    }
}

(26)その他

・メソッドの引数に可変長引数を指定する場合、必ず最後の引数として指定しなければいけない。
例:method(int... a, int... b)←これは、第一引数が「最後の引数」としての可変長引数ではないため、コンパイルエラー。
・コンストラクタから別コンストラクタを呼び出すときは、必ず先頭行で呼び出す!
・レコードのコンストラクタは、レコードそのものよりも厳しいアクセス修飾子は使えない。
・レコードのコンストラクタをオーバーロードした際、標準コンストラクタを明示的に呼び出さないとコンパイルエラー。*親クラスのコンストラクタ呼び出しと似ている?

第5章:継承とインターフェース

(1)継承で引き継がないもの

次の2つは継承しても親クラスから引き継がない。

  1. コンストラクタ
  2. privateなフィールドやメソッド

(2)インターフェースの特徴

・インターフェースで規定する抽象メソッドは、他のクラスから扱えるようにするために、
すべてコンパイラによって自動でpublicで修飾される。なお、protectedやprivateで修飾することはできない。

・抽象メソッドは中身(ブロック含む)を記述してはいけない。

public interface Sample {
    public void hello(); //OK
    public void hello() {} //NG
}

・インターフェースには、デフォルトメソッドと、インスタンスを作らなくても使えるクラスメソッド(staticで修飾されたメソッド)以外の実装は記述できない。

・実はフィールドも定義することができ、下記ルールを満たせばOK。

  1. public static finalで修飾すること(省略した場合は、暗黙的に指定したとみなされる)
  2. 定数扱いとなるため、初期値を設定すること

(3)インターフェースのデフォルトメソッド

インターフェースを実現したクラスが持つべきデフォルトの実装を定義するもの。
目的:共通処理をすべての実現クラスで実装したり、抽象クラスを間に挟む必要をなくすため。
インタフェースに定義する他メソッドと同様に、デフォルトメソッドも自動的にpublicで修飾される。

default 戻り値型 メソッド名(引数の型 引数名) {
    //処理内容
}

【注意点】
java.lang.Objectクラスに定義されているメソッドを、
インターフェースでデフォルトインターフェースとしてオーバーライドすると、
コンパイルエラーになる。

🟥2025/11/12(水)

(4)デフォルトメソッドの呼び出し

クラス間の継承で、サブクラスのメソッドからスーパークラスのメソッドを呼び出す際には、superを使う。

インターフェースのデフォルトメソッドを呼び出すには、
さらにインターフェース名をsuperの前に付け加える。
構文:インターフェース名.super.メソッド名();  例:A.super.sample();

ただし、この構文が使えるのは、デフォルトメソッドを持つインターフェースを直接実現したクラスだけ!
間に別のインターフェースが挟まっている場合は呼び出すことはできない。

(5)インターフェースの多重実現の注意点

多重実現をした際の注意点として、
親インターフェース同士に同じシグニチャのデフォルトメソッドが存在する場合がある。

この場合、下記のようにデフォルトメソッドをオーバーライドし、どちらのデフォルトメソッドを使うのかを明示しなければ、コンパイルエラーが発生する。

public class Main implements A, B {
    public static void main(String[] args) {
        new Main().test();
    }
    @override
    public void test() {
        A.super.test();
    }
}

(6)抽象クラス

■抽象クラス...インターフェースとクラスの両方の性質を持ったクラス。
【特徴】
・実装を持つ具象メソッドと、実装を持たない抽象メソッドの両方を持つことができる。
・抽象クラスに定義した具象メソッドは、その抽象クラスを継承したサブクラスが引き継ぐ。
・抽象メソッドは、そのサブクラスでオーバーライドして実装し直さなければいけない。
・抽象メソッドを持っているため、インスタンス化できない。
・抽象クラスは継承して利用されることが前提。
具象クラスだけでなく抽象クラスが抽象クラスを継承することも可能で、
その場合は新しい抽象メソッドを追加したり、既存の抽象メソッドをオーバーライドして実装することができる。
・インターフェースでフィールドを定義するときは、定数フィールドしか定義できないが、抽象クラスにはフィールドが定義できる。

(7)具象クラスのインスタンスを抽象クラス型の変数に入れる

可能。
抽象クラス型の変数であっても、ポリモーフィズムに則って、そのサブクラスのインスタンスを格納できる。

abstract class Animal {
    abstract void makeSound();
}

class Dog extends Animal {
    void makeSound() {
        System.out.println("ワンワン");
    }
}

class Cat extends Animal {
    void makeSound() {
        System.out.println("ニャー");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal dog = new Dog(); // ✅ OK!
        Animal cat = new Cat(); // ✅ OK!

        dog.makeSound(); // ワンワン
        cat.makeSound(); // ニャー
    }
}

(8)メソッドのオーバーライド

■オーバーライド...サブクラスでスーパークラスに定義されたメソッドを「再定義」すること。上書きではないため、元のメソッドが無くなっているわけではない。

【ルール】
・メソッドのシグニチャ(メソッド名、引数リストの型・数・順番)は同じでなければいけない
・戻り値は同じ型であることが基本だが、その型のサブクラスであれば設定が可能。
これを共変戻り値と呼ぶ。

public Number method() {
    //any code
}
public Integer method() { //java.lang.Number型のサブクラス
    //any code
}

(9)オーバーライドしたメソッドのアクセス修飾子

オーバーライドしたメソッドのアクセス修飾子は、
オーバーライドする前のアクセス修飾子と同じか、より緩いものでなければいけない。
理由:ポリモーフィズムの考えに基づき、ざっくりオーバーライドする前のものとして扱えなければいけないのに、アクセス修飾子がより厳しい場合にはそのように扱えなくなるから。

(10)継承関係にある2つのクラスの同名フィールド

どちらのフィールドが使われるのかは、次のルールに従って決定される。
・フィールドを参照した場合には、変数の型で宣言されたほうを使う
・メソッドを呼び出した場合には、メソッド内の指示に従う(変数の型ではなく、その中に実際に入っているインスタンスに従う)

class Parent {
    String name = "親クラスのname";

    void showName() {
        System.out.println("親クラスのname(): " + name);
    }
}

class Child extends Parent {
    String name = "子クラスのname";

    @Override
    void showName() {
        System.out.println("子クラスのname(): " + name);
    }
}

public class Main {
    public static void main(String[] args) {
        Parent p = new Child();  // 型はParent、実体はChild
        Child c = new Child();   // 型も実体もChild

        System.out.println("p.name = " + p.name); // (1)
        System.out.println("c.name = " + c.name); // (2)

        p.showName(); // (3)
        c.showName(); // (4)
    }
}
実行結果は、
p.name = 親クラスのname
c.name = 子クラスのname
子クラスのshowName(): 子クラスのname
子クラスのshowName(): 子クラスのname

(11)継承と実装の同時実行

ポリモーフィズムは、継承関係にあるクラス同士だけでなく、
インターフェースとの実現の関係でも成り立つ。

そのため、クラスAとクラスBとインターフェースCがあり、クラスBがクラスAを継承し、同時にインターフェースCを実装しているとき、
インターフェースCに規定されたメソッドを呼び出した場合に、クラスBのスーパークラスであるクラスAのメソッドが実行される。

(12)継承とメソッドの関係

インスタンスがどのようなメソッドやフィールドを持っていたとしても、
それを扱っている型で定義されているもの以外は使えない。
次のコードでは、Aクラスで定義されていないメソッドを使ったため、コンパイルエラーが発生する。

public class A {
    public void hello() {
        //any code
    }
}
public class B extends A {
    public void sample() {
        //any code
    }
}
public class Main {
    public static void main(String[] args) {
        A a = new B;
        a.sample();
    }
}

★継承やインターフェースが絡む問題は、まずコードを追う前に関係を図示したほうがよい

(13)継承関係における型変換

・継承関係にある場合、
サブクラスをスーパークラス型に変換することをアップキャストと呼び、
スーパークラス型で扱っていたインスタンスを元の型に戻すことをダウンキャストと呼ぶ。

・アップキャストは、extendsの宣言に基づいてコンパイラが簡単に型変換を行える。
一方、ダウンキャストは自動では行えない。Aクラスを継承したBクラスをAクラスとして扱っていたときに、それをBクラスの変数に入れようとすると、コンパイラは互換性があると判断する材料がないため、コンパイルエラーを起こす。

この判断材料として、キャスト式を記述する必要がある。
これは、コンパイラに対する互換性の保証とみなされる。
例:B b = (B) a;

(14)型の中身に互換性がない状態での型変換

キャスト式を記述して明示的に型変換をすると、コンパイルは成功する。
しかし、型の中身に互換性がない場合は、実行時に例外がスローされることを覚えておこう。

🟥2025/11/13(木)

(15)継承関係にあるクラスでのthis

thisは、インスタンス自身への参照を持っている。
サブクラスのインスタンスは、スーパークラスのインスタンスと差分のインスタンスの両方で構成されている。
thisを使った場合、あくまでそのインスタンスは自身の情報のみを参照するため、継承関係にあるクラスの情報は拾えない。

(16)継承関係にあるクラスのインスタンスを作ったときのコンストラクタの動作

クラスが継承関係にあるとき、スーパークラスの存在はサブクラスの共通部分を抽出して定義した、いわば「基盤」であり、先に基盤を作ってから差分を追加して載せたい。
そのため、Javaではスーパークラスのインスタンスが持つコンストラクタが先に実行されなければならない。
それを実現するために、サブクラスのコンストラクタには、スーパークラスのコンストラクタを呼び出す「super();」が、コンパイラによって先頭行に追加される。

(17)明示的に親クラスのコンストラクタを呼び出す

(16)にて、「サブクラスのコンストラクタには、スーパークラスのコンストラクタを呼び出す「super();」が、コンパイラによって先頭行に追加される」と書いたが、super();がプログラマにより明示されている場合には、自動挿入は行われない。

もしコンストラクタが複数あり、最初に呼び出されるコンストラクタにsuper();がない場合でも、そのコンストラクタがsuper();をもつコンストラクタをthis();で呼び出している場合は、コンパイルエラーは起きない。

挙動は下記表にまとめる。

コンストラクタの形 コンパイラの動作
super(...) を明示 自動で super(); は追加されない
this(...) を明示 自動で super(); は追加されない(呼び出された先のコンストラクタで super(...) が必要)
どちらも書かない 自動的に super(); が補われる
最終的にどのコンストラクタでも super(...) が呼ばれない コンパイルエラー(親クラスの初期化が行われないため)

(18)インターフェースのprivateメソッド

インターフェースにprivateメソッドを追加することができる。
目的:デフォルトメソッドをより簡潔にするため。

これにより、複数のデフォルトメソッド内で同じ処理をしていた場合、その処理をまとめることでコードを簡潔にできる。

【特徴】
・default修飾子は必要ない。
・処理をもたない抽象メソッドをprivateにすることはできない。

(19)シールクラス

指定したクラスにのみ継承を許可する修飾子。
ポイントは、sealedでクラスを修飾し、permitsのあとに継承を許可するクラスをカンマ区切りで列挙すること。

下記コードでは、AクラスとBクラスのみが、SuperClassを継承できるようになっている。

public sealed class SuperClass permits A, B {
    public void test() {
        System.out.println("test");
    }
}

(20)シールクラスの継承

シールクラスを継承したサブクラスは、次の3つのいずれかの修飾子で修飾されなければいけない。されない場合は、コンパイルエラーが発生する。

修飾子 用途
final それ以上継承を深くすることを禁止する
sealed permitsで許可したクラスのみ継承可能
non-sealed 制限なく自由な継承が可能※

※親クラスから子クラスはsealedでしっかり制限して、子クラスからは自由に継承していいですよ、親→子の部分を好き勝手されなければいいですよ、の場合はnon-sealedがいいのかも。

(21)シールインターフェース

sealedは、クラスだけでなくインターフェースも修飾することができる。

・シールインターフェースを継承したサブインターフェースは、
sealed か non-sealed で修飾されなければいけない。
されなければコンパイルエラーが発生する。

・シールインターフェースを実装したクラスは、
final か sealed か non-sealed で修飾されなければいけない。
されなければコンパイルエラーが発生する。

(22)その他

インターフェースのデフォルトメソッドとしてtoString()をオーバーライドすると、コンパイルエラーが発生する。

第6章:例外処理

(1)「例外」とは

■例外...プログラム中に発生する何らかの「トラブル」を指す。
トラブルは、プログラマーの不注意で作り込まれるバグだけでなく、
要件や仕様の間違い、実行マシンの不具合やほかのソフトウェアとの連携不具合など、
その種類は多岐にわたる。

プログラマーには、そうしたさまざまな事態に対応する
「万が一の場合に備えたプログラミング」が要求される。

■例外処理...トラブルが発生したときに「どのように対処すべきか」を記述した処理のこと。
Javaでは、例外をtry-catchという構文で記述する。
例外が発生する可能性がある処理をtryブロックでくくり、
例外が発生した時の処理をcatchブロックに記述する。

try {
    //例外が発生する可能性がある処理
} catch (例外クラス型 変数) {
    //例外が発生した時の処理
}

(2)try-catch-finally文

try-catch文では、例外が発生しうる処理と、例外発生時の処理を記述した。
しかし、接続中のネットワークの切断やデータベース接続の解放など、例外発生の有無にかかわらず必ず実行したい処理というのが存在する。
そうした処理は、try-catch-finally文のfinallyブロックに記述する。

try {
    //例外が発生する可能性がある処理
} catch (例外クラス型 変数) {
    //例外が発生した時の処理
} finally {
    //例外発生の有無にかかわらず実行したい処理
}

🟥2025/11/14(金)

(3)catchブロックが複数記述されている場合の例外処理

tryブロック内で複数の種類の例外がスローされる可能性がある場合は、
catchブロックも複数用意し、それぞれの例外に応じた処理を記述する。

このような複数のcatchブロックが存在するコードが出題された場合は、
catchブロックの例外クラスの型を確認しよう。

例外クラスといっても、普通のクラスであるため、
ほかのクラスと同様にポリモーフィズムを使うことが可能。

→SampleExceptionを継承したSubSampleExceptionがスローされたとき、
 1つ目のcatchブロックはこのインスタンスへの参照をスーパークラスであるSampleException型で受け取ることが可能。
 そのため、対応した例外処理が「あった」と判断され、2つ目のcatchブロックが実行されることはない。

このような到達できないコードを記述した場合、コンパイルエラーが発生する。

※こういう場合は、子のcatchを先に書いて、範囲が狭→広になるようにすると良い。
つまり、SubSampleExceptionを先にcatchブロックとして書いておくと、コンパイルエラーが発生しない。

(4)try-catch-finallyの出現順

ルールとして、try、catch、finallyの出現順は変えることができない。
変えて記述した場合、コンパイルエラーが発生する。

(5)returnとfinallyの処理順序

catchブロック内でreturnされていても、finallyブロックに記述された処理は必ず実行される。
処理順番は、
returnによって呼び出し元のメソッドに制御が戻る前に、finallyブロックの処理が実行される。

catchブロックとfinallyブロックの両方でreturnが記述されていた場合、
finallyブロックのreturnの値が最終的に戻り値を格納する専用の変数に格納されて、戻される。

(6)returnで値を戻す専用の変数の値をfinallyブロックで変更できるか

基本データ型の場合と参照型の場合で異なる。
①基本データ型の場合、catchブロックで値をreturnしたら、finallyブロックで値を変更できない。
②参照型の場合、参照先のインスタンスの値を変えることが可能。

(7)try-catch-finallyのtryとfinallyの登場回数

catchブロックは何個も記述できるのに対し、
tryブロックとfinallyブロックは1個ずつしか記述できない。
記述した場合は、コンパイルエラーになる。

また、catchブロックは省略することも可能。
省略するのは、そのメソッド内では例外処理の方法を決められない場合。

(8)例外とエラーとそれらを表すクラス

■例外...プログラムで対処できるトラブル
■エラー...プログラムで対処できないトラブル

エラー例...実行マシンのメモリ不足、ディスクへの書き込み・読み取り権限がない、ネットワーク接続ができない、など

共通している特徴として、スロー(通知)できるということ。
これを表すクラスが、java.lang.Throwableクラス。
その直下には、
例外を表すjava.lang.Exceptionクラスと、エラーを表すjava.lang.Errorクラスがある。

また、Javaの例外は、例外処理の有無をコンパイラがチェックするチェック例外と、
チェックしない非チェック例外に分かれる。
Exceptionクラスとそのサブクラスは基本的にチェック例外だが、
java.lang.RuntimeExceptionとそのサブクラスだけは非チェック例外として扱われる。

例外とエラーの図
引用:https://digital-literacy88.com/java-exception-organize/

(9)チェック例外のルール

チェック例外であるExceptionクラスを継承している例外クラスは、
try-catchしているか、throws句で宣言しているかのどちらかを強制される。

一方、非チェック例外であるRuntimeExceptionとそのサブクラスは、throwsとtry-catchを強制されない。

★歴史的に、プログラマが例外処理を忘れてあとで致命的な障害を引き起こすことが多かった。そのため、絶対にコンパイラのところで食い止められるように、チェック例外という制約が追加された。

(10)「エラー」の深堀り

エラーは例外と違って、「プログラムで対処する」ことを求められていない。
そのため、try-catchしたり、throwsで宣言したりする必要はない。

なお、エラーは例外処理することを「求められていない」だけであって、
キャッチして処理することが可能。

(11)代表的な例外クラス

データ構造 / 状況 投げる例外 備考
配列 ArrayIndexOutOfBoundsException Array 専用、範囲外アクセスで発生
String StringIndexOutOfBoundsException String 専用、文字列範囲外アクセスで発生
List / ArrayList / LinkedList IndexOutOfBoundsException 特化例外がないコレクションアクセスで発生
その他のインデックス操作API IndexOutOfBoundsException 汎用、範囲外アクセスで発生
型キャスト ClassCastException 不正な型変換を行ったときに発生
null参照 NullPointerException null※ に対してメソッド呼び出しやフィールド参照を行ったときに発生
不正な引数 IllegalArgumentException メソッドに不正な引数を渡したときに発生
不正な状態 IllegalStateException オブジェクトの状態がメソッド実行条件を満たさないときに発生

※null...リテラルの一種で、変数が「何もしない」ことを表現するためのデータ。

(12)代表的なエラークラス

データ構造 / 状況 投げるエラー 備考
再帰深すぎ StackOverflowError スタックが枯渇したときに発生するエラー
初期化ブロックで例外 ExceptionInInitializerError static 初期化中に例外が発生した場合に投げられる
物理メモリ不足 OutOfMemoryError JVM ヒープが不足した場合に発生するエラー

(13)マルチキャッチ機能

catchブロックを複数作成し、複数の例外に対応する場合、
数が多くなるほど可読性が落ちることに繋がる。

その場合、同じ例外処理をするのであれば、
マルチキャッチ機能を使い、
catchブロックの例外処理を1つにまとめることで簡略化を図れる。

try {
    //3種類の例外が発生する処理
} catch (AException | BException | CException e) {
    //例外処理
} finally {
    //例外処理
}

★マルチキャッチでは、継承関係にあるクラスを同時に扱うことはできない。
 同一のcatchブロックで扱った場合、コンパイルエラーが発生する。
 理由:親クラスが子クラスの例外も内包しているため、親クラスだけをcatchすれば問題ないため。

(14)try-with-resources(リソース付きtryブロック)

プログラムの中で扱うリソースを自動的に閉じる目的で用いられる文。
■リソース...プログラムからアクセスするためのデータやインスタンスを指す。

原則として、
リソースは、使うときに開き、使い終わったら閉じなければいけない。
理由:リソースはアクセスできる数に限りがあったり、対象となるリソースへのアクセス権を限定したりするものがあるため。

問題は、リソースを閉じるのは「任意」であって、必須ではないこと。
これを防ぐために、自動的にリソースを閉じるための構文として導入されたのが、
try-with-resources文。

try-with-resourcesの目的は、例外処理ではなく、リソースの閉じ忘れを防ぐこと。
よって、例外処理のtryブロックと異なり、
catchブロックかfinallyブロック、もしくはその両方が省略可能である。

import java.io.FileReader;
import java.io.IOException;

public class SimpleTryWithResources {
    public static void main(String[] args) {
        try (FileReader fr = new FileReader("sample.txt")) {
            int ch;
            while ((ch = fr.read()) != -1) {
                System.out.print((char) ch);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

【ポイント】
・try の括弧内で リソースを宣言するだけ
・finally で close() を書かなくても自動で閉じられる
・1行で簡単にリソース管理ができる
・複数のリソースを記述する場合は、「;」で区切る

(15)try-with-resourcesで扱えるリソースの種類

try-with-resources文のカッコで扱えるのは、
java.lang.AutoCloseableインターフェース もしくは
java.io.Closeableインターフェースを実装したクラス。

これらの違いは、導入された目的。
java.lang.AutoCloseableインターフェースは、
try-with-resourcesが導入されたときに、それに対応する形で導入された。

一方、Closeableインターフェースは、
ストリームを扱うクラスがそれぞれ実装していたcloseメソッドが
共通の型として扱えるように作られたもの。
Closeableインターフェースは、AutoCloseableインターフェースのサブインターフェースとして実装されているため、アップキャストすればAutoCloseable型でも扱えることができる。

(16)try-with-resourcesのリソース宣言

try-with-resourcesで自動的に閉じる対象とするリソースは、
tryに続くカッコだけでなく、
tryブロックの前に宣言を記述することも可能。

public class SampleUsing {
    public static void main(String[] args) throws Exception {
        SampleResource resource = new SampleResource();
        try (resource) {
            //do something
        }
    }
}

なお、リソースは実質的にfinalではなくてはならず、
あとからリソースの値を書き換えるコードを記述した場合、コンパイルエラーとなる。

(17)try-with-resourcesでリソースが閉じられる順番

リソースは、tryブロックの処理が終了すると、
宣言したときと逆の順番で自動的に閉じていく。

(18)try-with-resourcesで例外処理を行ったときの処理順番

例外発生の有無にかかわらず、リソースのクローズはtryブロックを抜けるタイミングで必ず実行される。

そのため、tryブロック内で例外が発生しなければ、
リソースのクローズ → finallyブロック の順に実行される。

例外が発生すれば、
リソースのクローズ → catchブロック → finallyブロック の順に実行される。

(19)try-with-resourcesにおける「抑制された例外」

tryブロック内で発生した例外はcatchブロックでキャッチされるが、
リソースに関連する部分で例外が起きた場合、tryブロック内の例外に隠れてしまい、キャッチされない。
これを「抑制された例外」と呼ぶ。

抑制された例外を発見する場合は、
java.lang.ThrowableクラスのgetSuppressedメソッド(抑制を得る)を使う。
これは、抑制された例外の配列を取り出し、コンソールに表示するメソッド。

(20)その他

・配列の要素外アクセス→ArrayIndexOutOfException
・起動パラメータを記述しなかった場合(例:java Mainのみ)、配列型変数の中身がnullになることはない。あくまで、「要素が一つも入っていない配列」という扱い。

🟥2025/11/17(月)
問題集2周目突入する

第7章:模擬問題1

1回目:正答率50%...つらい。学んだ点を書いていく。

(1)別のパッケージに属するクラスを利用する際のimportの指定方法
import staticから始まるstaticインポートは、指定されたクラス内のstaticメンバ(staticフィールドとstaticメソッド)を利用するときに使うもの。

(2)internメソッドの処理
internメソッドによって生成されたString型のインスタンスは、コンスタントプールに生成される。
→つまり、明示的にnewによってヒープ領域に生成したインスタンスとは格納場所が異なることに注意。
【Point】Stringインスタンスは、原則としてコンスタントプールに生成され、使いまわされる(internメソッドで同参照を持ってくるときも同様)。しかし、newや実行時に生成される文字列のみヒープ領域に作られる。

(3)サブクラスで定義するメソッドにおけるオーバーライド条件

  1. 戻り値型が同じかもしくはサブクラスであること
  2. メソッドのシグネチャ(引数の名前と、引数の型・数・順番)が親クラスのメソッドと完全に同じであること。intとIntegerは異なるよ!!
  3. アクセス修飾子が同じか緩いこと

(4)System.out.printlnメソッドの注意点
このメソッドは、引数として渡されたObject型のtoStringメソッドを呼び出し、その結果を出力する。
また、引数がnullだったときは、nullという文字列を出力する。

もし、toStringメソッドがオーバーライドされていた場合、それに内容が変化するため注意。

(5)メソッドの2つの命名規則

  1. 数字、ドル記号($)、アンダースコア(_)を含むことができる
  2. 数字で始めることはできない。2進数を表す「0b」や8進数を表す「0」もだめ。

(6)switch文の条件式にnullが入ると...
普通にNullPointerExceptionが発生する。staticとかついてても関係なしにぬるぽ発生。

(7)1つのクラスに2つ以上のコンストラクタがある場合...
すべてのコンストラクタにsuper()が必要になる。
もし、親クラスに該当する引数のコンストラクタがなかった場合、コンパイルエラーが発生する。

(8)「var」は予約語ではないため、変数名として使える。

第8章:模擬問題2

(1)ローカル変数の初期化
ローカル変数は、必ず初期化してからしか使えない!

void sample() {
    String s;
    System.out.println(s); // ❌ コンパイルエラー
}

クラス変数やインスタンス変数であれば、JVMが自動で変数の初期化を行う。

class Sample {
    String s;
    int i;
}
Sample sample = new Sample();
System.out.println(sample.s); // null
System.out.println(sample.i); // 0

(2)Javaのソースファイルのコンパイルについて
Javaのソースファイルは、publicなクラスと同名でないとコンパイルできない。
下記ソースファイルがあったとき、ファイル名は「A.java」である必要がある。

public class A {}
class B {}

(3)staticメソッドが他の要素にアクセスするとき
staticメソッドから、インスタンスフィールドやインスタンスメソッドにアクセスすることはできない。
staticメソッドからアクセスできるのは、staticなもののみ!

(4)ヌルポの親クラス
NullPointerExceptionは、RuntimeExceptionのサブクラス。
よって、NullPointerExceptionがスローされるとき、RuntimeExceptionでキャッチできることを覚えておこう。ちなみに、RuntimeExceptionなので非チェック例外。

(5)キャストに関わる要素の処理順
「()>メソッド呼び出し>キャスト」の順番で処理が行われる。
「カッコめき」のゴロで覚えてみるか...
例:final class C extends B implements AB b=new C;、そしてCクラスとAクラスにのみ定義されているtestメソッドがあったとき、③以外はコンパイルエラーとなる。理由は、Bクラスにtestメソッドがなく、③のみキャストが最優先で行われているから。
b.test();
(A)b.test();
((A)b).test();

(6)サブクラスで親クラスの同名のフィールドを定義したときの挙動
サブクラスにスーパークラスのフィールドと同名のフィールドがあった場合、アクセスるのがフィールドなのかメソッドなのかによって動作が変わる。
サブクラス内のメソッドでは、superを使ってアクセスしない限り、サブクラスに定義したフィールドが使われる。一方、メソッドではなくフィールドに直接アクセスした場合、その変数の型に応じて、どちらのフィールドにアクセスするかが決まる。

(7)レコードクラスとnullの関係
レコードだからどう、というのはなく、
・レコードの引数が参照型であれば、nullを受け入れる。
・レコードの引数が基本データ型であれば、nullを受け入れない。

ちなみに、引数がString型であれば、空文字も受け入れることが可能。

(8)ArrayListクラスとnullの関係
ArrayListクラスはnullを許容する。
で、ジェネリクスの場合は、いろんな型が入れられる。

List<Object> list = new ArrayList<>();
list.add("abc");     // String
list.add(100);       // Integer
list.add(3.14);      // Double
list.add(null);      // null

(9)ObjectクラスをItem型にダウンキャストしようとすると...
基本無理。理由は、Item型が中身に入っているとは限らないから。
でも、中身がItemなら可能。

Item item1 = new Item();
Object obj = item1;         // アップキャスト(安全)
Item item2 = (Item) obj;    // ダウンキャスト(安全に可能)

(10)for文と戻り値が絡むときの注意点
for文内にreturnがあるとしても、breakなどでfor文を抜けたときに戻り値を戻せなくなる可能性がある場合、コード全体がコンパイルエラーになる。

(11)インターフェースのデフォルトメソッドの呼び出し方
子クラスでオーバーライドしたメソッドから、インターフェースに定義されているデフォルトメソッドにアクセスするには、インターフェース名.super.メソッド名()を使用する。

(12)staticメソッドの呼び出し
クラス名.メソッド名が書かれているとき、それはstaticメソッドを呼び出している印。

(13)抽象クラスと抽象メソッドの関係
・abstractで修飾された抽象メソッドは、抽象クラスにしか定義できない。

abstract class A {
    abstract void method();
}

・抽象クラスに定義する抽象メソッドは、abstractで修飾しなければいけない。

abstract class A {
    abstract void method();
}

他にも、
・抽象クラスでも具象メソッドを定義することは可能。

abstract class A {
    void method() {}
}

・具象メソッドがサブクラスでオーバーライドされるのを防ぐため、finalで修飾することも可能。

abstract class A {
    final void method() {}
}

(14)break実はややこしいよ
普通は、breakはループを抜ける際に使われる。
ただし、switch文の中で使われた場合、ただ単にそのswitch文を抜けるだけ。

第9章紫本から得た知見

① 基本データ型・配列・リテラル・型変換

配列と初期値

  • 配列は参照型であり、new によってヒープ領域に確保される
  • そのため、各要素は 型ごとのデフォルト値 で初期化される

デフォルト値一覧

  • int0
  • double0.0
  • booleanfalse
  • char → '\u0000'
  • 参照型(Stringなど) → null

リテラルと型

  • 整数リテラルはデフォルトで int
  • 浮動小数点リテラルはデフォルトで double
  • longLfloatF を付けないとコンパイルエラーになる場合がある
  • 数値リテラルの中に _(アンダースコア)を連続で使用することも可能

型変換

  • 拡張変換(小 → 大)は自動
  • 縮小変換(大 → 小)はキャストが必要
  • キャストしても値の保証はされない(オーバーフロー等に注意)
  • 基本データ型間の演算では、byte, short, char は自動で int に変換される
  • int × double は結果が double になる

var の注意点

  • ローカル変数のみ使用可
  • 宣言と同時に初期化が必須
  • null 単体では型推論不可
  • カンマ区切りで複数宣言不可(例:var x1=1, x2=2; はコンパイルエラー)

ソースファイルモード

  • java Test.java のように直接ソースファイルを実行する場合
    • ソースファイル名と public 指定クラス名が異なっていても実行可能
    • ソースファイル内の 最初のクラスmain(String[] args) メソッドが必須
      • ない場合はコンパイルエラー
  • javac でのコンパイル条件
    1. クラス名と javac クラス名 を一致させる
    2. public クラスがある場合は、ソースファイル名と一致させる(ない場合は不要)
  • 無名パッケージに属する場合、protected やパッケージプライベートのクラス同士は自由に呼び出せる

その他注意点

  • println() に渡す式は戻り値が必要(戻り値なしのメソッドはコンパイルエラー)
  • String はイミュータブル
  • 配列やコレクションを println() で表示すると、ハッシュコード形式になる
  • equals() は String クラスと Object クラスで挙動が異なる
  • final メンバ変数は初期化必須
  • static メソッドからは static メンバにアクセス可能だが、インスタンスメンバにはインスタンス生成が必要
  • トップレベルクラスに宣言できるのは、publicパッケージプライベートのみ。反対に、privatestaticはダメ。
  • publicstaticはクラスのメンバに指定するもので、ローカル変数には使用できない。

② 演算子・型変換・優先順位

算術演算子

  • +, -, *, /, %
  • int 同士の割り算は int になる
  • 割り算のゼロ除算は RuntimeException(ArithmeticException)が発生する

インクリメント・デクリメント

  • 前置 (++x / --x):先に値を増減してから式に使う
  • 後置 (x++ / x--):式で使った後に値を増減

比較演算子

  • == は値または参照の一致を比較
  • != は不一致
  • equals() はオブジェクトの内容を比較
    • StringBuilder 等の異なるクラスとの比較は false

論理演算子

  • && (AND)、|| (OR)、! (NOT)
  • 短絡評価あり(右辺は必要な場合のみ評価)

ビット演算子

  • &, |, ^, ~, <<, >>, >>>
  • byte / short / char は演算時に int に自動変換される
    short s = shortの変数+byteの変数はコンパイルエラーになる!short型じゃなくint型に入れる or int型にキャストする必要がある。

型変換

  • 小さい型 → 大きい型:自動
  • 大きい型 → 小さい型:キャスト必要
  • int × double → double
  • float → double → Double のような変換は自動で行われない(オートボクシングに注意)

③ 制御文(if / switch / ループ)

if文

  • 条件式は boolean 型のみ
  • else if / else は任意で追加可能
  • ローカル変数を if ブロック内で初期化しても、ブロック外では未初期化扱いになる

switch文

  • 使用可能な型:
    • byte / short / char / int
    • ラッパークラス(Byte, Short, Character, Integer)
    • String
    • enum
  • case に指定できるのはコンパイル時定数(final の値)だけ
  • break を書かないとフォールスルー
  • Java 12以降の Switch式では
    • case L: yield expr; または
    • case L -> expr; が使用可能
  • 変数は case に指定不可、定数のみ

for文

  • 初期化式; 条件式; 更新式 の順で評価
  • 拡張 for 文(foreach)は配列や Iterable のみ使用可
  • ブロック内で宣言した変数はブロック外で使用不可

while / do-while

  • while は条件式が true の間繰り返す
  • do-while は少なくとも 1 回は実行される
  • do-while の条件式の後には必ずセミコロンを付ける

④ クラス・コンストラクタ・初期化

コンストラクタ

  • クラスのインスタンス生成時に呼び出される特別なメソッド
  • 別のコンストラクタを呼び出す場合は、必ず先頭行で1回のみ this(...) を使用
  • 引数なしコンストラクタは自動生成される場合あり(他にコンストラクタがなければ)

オーバーロード優先順位

  1. 引数の型が完全一致するもの
  2. 基本データ型の変換(拡張変換)
  3. ラッパークラスへの変換(オートボクシング)
  4. 可変長引数(0個でも受け取れる)

クラス変数(メンバ変数)

  • 初期化なしでもコンパイル可能(デフォルト値が入る)
  • final 変数は宣言時またはコンストラクタで必ず初期化が必要
  • static メソッドからは static メンバに直接アクセス可能
  • インスタンスメンバにアクセスする場合は必ずインスタンスを生成する
  • top-level クラスのアクセス修飾子は public または省略のみ指定可能
  • ローカル変数は初期化が必須(未初期化の場合コンパイルエラー)

⑤ コレクション(ArrayList / HashMap など)

ArrayList

  • add(index, value) → 指定位置に追加、戻り値なし
  • set(index, value) → 元の値を返す
  • remove(index) → 削除した要素を返す
  • ジェネリクスを指定しない場合、Object 型として扱われる
  • int や double などの基本データ型は直接扱えない(ラッパークラスが必要)

HashMap

  • 要素の追加は put(key, value) を使用
  • 出力は {key=value, ...} 形式
  • key はユニーク、value は重複可

その他

  • ArrayList.mismatch(Object[] a, Object[] b) → 最初に不一致となるインデックスを返す
  • List と ArrayList の関係:
    • List はインターフェース
    • ArrayList は List を実装した具象クラス
    • new List<>() はできない(Listはインターフェースだから)
    • List.of()Arrays.asList() でリストを生成可能。ただし戻り値は List 型
  • List と ArrayList が入り混じった場合、型の互換に注意
  • コレクションは、基本データ型を扱えない。そのため、次のコードはコンパイルエラー。ArrayList<double> list=new ArrayList<>();

⑥ 継承・インターフェース・オーバーライド

クラス継承時のメソッド呼び出し

  • コンパイル時点での参照型でメソッドの可視性が決まる
  • private メソッドはサブクラスから参照不可
  • 例:

// Parentクラス
private static void method(){}

// Childクラス
public static void method(){}

Parent obj = new Child();
obj.method(); // コンパイルエラー

インターフェースの static メンバ

  • static 変数 → インターフェース名.変数名 または 参照.変数名
  • static メソッド → インターフェース名.メソッド名
  • 例:
    • 変数:Interface.obj または i.obj
    • メソッド:Interface.run();

抽象メソッド

  • 暗黙的に public
  • オーバーライド時も public 必須

デフォルトメソッド

  • 複数インターフェースで同名 → 衝突、オーバーライド必須
  • 抽象メソッドと同名 → 抽象メソッド優先
  • 主目的:既存インターフェースへのメソッド追加による後方互換性保持

抽象クラスの注意点

  • 抽象メソッドがなくても抽象クラスを名乗れる

List と ArrayList の関係

  • List はインターフェース
  • ArrayList は List を実装した具体クラス
  • List 型変数には ArrayList インスタンスを代入可能
  • new List<>() は不可(インターフェースはインスタンス化不可)
  • List.of() や Arrays.asList() でリストを生成可能(戻り値は List 型)

インターフェースの変数

  • 暗黙で public static final
  • アクセス:インターフェース名.変数名 または 参照名.変数名

その他

  • CharSequenceを実装しているクラスとして、StringクラスやStringBuilderクラスがある。オーバーライドの際は関連してくるかもしれないため注意!

⑦ 新しい型・修飾子(sealed / record)

sealed インターフェース・クラス

継承先 使用可能修飾子 備考
クラス final / sealed / non-sealed
インターフェース sealed / non-sealed final不可
  • 子クラスにて non-sealed が宣言されている場合、シールクラスの継承またはシールインターフェースの実装が必須

レコードクラス(体系的整理)

1. 基本性質

  • 目的: データの入れ物(値オブジェクト)を簡潔に定義するためのクラス
  • 継承関係:
    • 暗黙的に java.lang.Record を継承している
    • ユーザー側で extends は不可
    • レコード自体を親クラスとして継承されることも不可
    • インターフェースの実装は可能

2. フィールドとアクセサ

  • フィールドは暗黙で private final
  • フィールドに対応するアクセサは getXxx() ではなく、xxx() メソッド
  • インスタンス変数は状態(フィールド)のみ持つ
    • 追加の状態変数を持つことは基本的に推奨されない

3. コンストラクタ

  • 標準コンストラクタ(引数付き)とコンパクトコンストラクタが存在
    • 標準コンストラクタ: this を使える
    • コンパクトコンストラクタ: this は使用不可
  • コンストラクタではフィールドの値を検証したり加工することが可能
  • 引数名はフィールド名と一致する(命名規則上 record は使用不可)

4. 命名規則

  • クラス名・レコード名
    • 1文字目が小文字でもコンパイルは通るが慣習的に NG
    • Record はレコード名として使用可能
    • record は予約語のため不可
  • 引数名
    • record は使用不可(予約語)

5. まとめ(覚え方)

項目 制約・ルール
継承 できない(extends 不可)
親クラスとして 継承されない
インターフェース実装 可能
クラス 暗黙で final←実はsealedインターフェースの実装が可能ということ
フィールド 暗黙で private final
アクセサ xxx() 形式
コンストラクタ 標準は this 使用可、コンパクトは不可
クラス名 慣習的には大文字スタート、record は不可
引数名 record は不可

メンバアクセスに関する補足

  • メソッドが static の場合、インスタンス型ではなく 参照型を基準にアクセスが決まる

⑧ 例外処理

throws 指定された例外

  • オーバーライド時、親クラスと同じかサブクラスのチェック例外を指定可能
  • オーバーライド時、省略可
  • 非チェック例外は自由に指定可能
  • アクセス修飾子は子クラスになるにつれ緩くする必要があるが、throws のオーバーライドは逆で、子は親以上の例外を出せない

Throwable

  • 詳細メッセージ取得:getMessage()

multi-catch

  • 例外の参照変数(Exception e)は暗黙的に final となるため再代入不可

Exception 階層

  • FileNotFoundExceptionIOException のサブクラス

プログラム実行時の例外

  • コンパイルエラーとは違い、例外発生までは処理は実行されるため、途中結果が出力される場合あり
  • Integer.parseInt(s) で発生する NumberFormatExceptionArithmeticException とは無関係

RuntimeException の例

  • IllegalArgumentException
    • NumberFormatException
  • ArithmeticException

⑨ Javaプログラムの実行

  • Javacコマンドによるソースファイルのコンパイルでは、
    ソースファイル名とpublicクラス名が一致していなければいけない。
    例:javac Main.javaのとき、publicがついたクラスはMainでなければいけない。
  • publicクラスがなくても、コンパイルは問題なくできる。publicクラスがある場合のみ、上記の制約が発生する。
  • cdはチェンジディレクトリで、指定したディレクトリに自分の現在地を変えるコマンド。
  • dirはディレクトリで、自分が現在いるディレクトリに存在するファイルとフォルダをすべて出力するコマンド。

⑩ その他試験対策

「暗黙的にこう決まってるよ!」のまとめ

■インターフェース

  • フィールド➡public static final
    • public→子クラスでは、public以外で修飾できない!
    • static→
    • final→
interface A {
    int X = 10;//実はpublic static final。
}
  • メソッド➡public abstract
    • public→子クラスでは、public以外で修飾できない!
    • abstract→
interface A {
    void foo(); //実はpublic abstract。
}
  • defaultメソッド➡public
    • public→子クラスでは、public以外で修飾できない!
interface A {
    default void bar() {}
}

■レコード

  • コンポーネント(引数によりできたフィールド)➡private final
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?