Java Basic Textbook

ゼロから始めるJava
IntelliJで手を動かして学ぶ基礎テキスト

環境構築からオブジェクト指向、コレクション・例外・Stream APIまで。Spring BootなどのWebフレームワーク学習にそのまま進めるレベルの基礎力を、実際にコードを書きながら身につけるための自習用テキストです。

対象:プログラミング初学者〜他言語経験者 ゴール:Webフレームワーク学習の入口 全6章 / 24節

このテキストの進め方

  1. 各節は ① 内容(解説+ハンズオン)② 理解度チェック問題 の2部構成です。
  2. コードは必ずIntelliJに自分で入力して実行してください。コピー&ペーストより、手で打つほうが文法が身につきます。
  3. 「やってみよう」ボックスは実際に手を動かす課題です。飛ばさずに取り組みましょう。
  4. 理解度チェックは、まず自力で解いてから「答えを見る」ボタンで確認します。2問以上間違えたらその節を読み直すのがおすすめです。
  5. コードブロックはIntelliJの標準テーマ(Darcula)と同じ配色です。自分の画面と見比べながら学習できます。
CHAPTER 01

開発環境の構築

この章のゴール:Javaが動く仕組みを理解し、IntelliJ IDEAで自分のPCに開発環境を作って、最初のプログラムを実行できるようになる。

1.1

Javaという言語を知る

内容

Javaとは

Javaは1995年に登場した、世界で最も広く使われているプログラミング言語のひとつです。銀行や官公庁の大規模システム、Webサービスのサーバーサイド、Androidアプリなど、「壊れては困る大きなシステム」の中核で長年使われ続けています。皆さんが最終的に学びたいSpring BootのようなWebフレームワークも、Javaの文法・オブジェクト指向の知識を土台にしています。

Javaの最大の特徴 — 「一度書けば、どこでも動く」

Javaのソースコード(.javaファイル)は、コンパイラ(javac)によってバイトコード(.classファイル)という中間形式に変換されます。バイトコードはOSが直接実行するのではなく、JVM(Java Virtual Machine:Java仮想マシン)という「通訳役」のソフトウェアが実行します。

Windows用・Mac用・Linux用のJVMがそれぞれ用意されているため、開発者は同じ.classファイルをどのOSでも動かせます。これが有名なスローガン “Write Once, Run Anywhere”(一度書けば、どこでも動く) の意味です。

ソースコード Main.java 人間が書く コンパイル javac バイトコード Main.class 中間形式(共通) JVM Windows JVM macOS JVM Linux 同じ .class ファイルが、JVMさえあればどのOSでも動く
1-1 コンパイルと実行の流れ(Write Once, Run Anywhere)

JDK・JRE・JVMの関係

Javaの学習を始めると、よく似た3つの用語が出てきます。関係は「入れ子」になっていると覚えてください。

  • JVM … バイトコードを実行する仮想マシン本体。
  • JRE(Java Runtime Environment)… JVM+実行に必要な標準ライブラリ。動かすための環境
  • JDK(Java Development Kit)… JRE+コンパイラ(javac)などの開発ツール。開発するための環境。開発者がインストールするのはこれです。
JDK(開発キット) javac(コンパイラ)/ デバッガ などの開発ツール JRE(実行環境) 標準ライブラリ(String, List など) JVM(仮想マシン) バイトコードを実行する本体
1-2 JDK ⊃ JRE ⊃ JVM の入れ子関係。開発者はJDKを入れれば全部入り
PPOINT
  • Javaは「コンパイル → バイトコード → JVMが実行」という2段階で動く。
  • 開発者がインストールするのはJDK。JREとJVMはその中に含まれる。
  • JDKには複数の配布元(Oracle JDK、Eclipse Temurinなど)があるが、中身の仕様は共通。学習にはLTS(長期サポート)版のJDK 21がおすすめ。
この節の理解度チェック
Q1

Javaのソースコードをコンパイルすると生成されるファイルの拡張子は何ですか。また、そのファイルを実行するソフトウェアの名前は何ですか。

答えを見る
解答

拡張子は .class(バイトコード)。それを実行するのは JVM(Java仮想マシン) です。

Q2

「Write Once, Run Anywhere」が実現できる理由を、「バイトコード」「JVM」という2つの言葉を使って説明してください。

答えを見る
解答例

JavaのプログラムはOSごとに変換されるのではなく、共通のバイトコードにコンパイルされる。各OSにはそのOS用のJVMが用意されており、JVMがバイトコードを解釈・実行するため、同じバイトコードがOSを問わず動作する。

Q3

JDK・JRE・JVMを「含んでいる範囲が大きい順」に並べてください。また、Javaで開発を行う人がインストールすべきものはどれですか。

答えを見る
解答

大きい順に JDK ⊃ JRE ⊃ JVM。開発者がインストールするのはJDK(コンパイラなどの開発ツールを含むため)。

1.2

JDKとIntelliJ IDEAの導入

内容

IDE(統合開発環境)を使う理由

Javaはメモ帳とコマンドラインだけでも開発できますが、実務ではIDE(統合開発環境)を使うのが標準です。本テキストでは、Java開発で最も広く使われているJetBrains社のIntelliJ IDEAを使用します。IDEを使うと次のような支援が受けられます。

  • コード補完 … 途中まで入力すると候補を提示してくれる(so と打つと System.out.println() の候補が出る、など)。
  • エラーの即時表示 … 文法ミスをその場で赤波線で教えてくれる。
  • 実行・デバッグ … ボタンひとつでコンパイルから実行まで行える。
  • リファクタリング支援 … 変数名の一括変更などを安全に行える。

手順1:IntelliJ IDEA(Community版)のインストール

IntelliJ IDEAには有償のUltimate版と無償のCommunity版があります。本テキストの範囲(Java基礎)はCommunity版で十分です。

  1. 公式サイト(jetbrains.com/idea)のダウンロードページを開く。
  2. お使いのOS(Windows / macOS / Linux)を選び、Community Editionをダウンロードする。ページ下部にあることが多いので注意。
  3. インストーラーを実行し、基本的にすべてデフォルト設定のまま進めてインストールを完了する。

手順2:JDKのインストール(IntelliJから行うのが最も簡単)

JDKは単体でもインストールできますが、IntelliJの新規プロジェクト作成画面から直接ダウンロードするのが最も簡単で失敗がありません。

  1. IntelliJを起動し、ようこそ画面で New Project をクリック。
  2. 左側で New Project(Javaプロジェクト)を選択。
  3. JDK のプルダウンを開き、Download JDK... を選択。
  4. Version: 21(LTS)、Vendor: Eclipse Temurin を選んで Download。数分でJDKの導入が完了します。
!つまずきポイント
  • 日本語化について:IntelliJは設定(Plugins)から「Japanese Language Pack」を導入すると日本語化できます。ただしWeb上の情報は英語UI前提が多いため、本テキストではメニュー名を英語表記で説明します。
  • バージョンの違い:Javaは半年ごとに新バージョンが出ますが、基礎文法はほぼ変わりません。本テキストのコードはJDK 17以降であればすべて動作します。

手順3:プロジェクトを作る

Javaでは、プログラム一式をプロジェクトという単位で管理します。以下の設定で最初のプロジェクトを作りましょう。

項目設定値意味
Namejava-basicプロジェクト名。半角英数字にする
Location任意のフォルダ日本語やスペースを含まないパスが安全
Build systemIntelliJ第6章まではこれでOK(6.2でMavenを学ぶ)
JDK21手順2で導入したもの
Add sample codeチェックON雛形コードが自動生成される

Create を押すとプロジェクトが開き、左側にプロジェクトツール(ファイル一覧)、右側にエディタが表示されます。srcフォルダがソースコードの置き場所です。

やってみよう 1-1

上記の手順どおりに、IntelliJ IDEA Community版のインストール → JDK 21のダウンロード → プロジェクト java-basic の作成まで進めてください。エディタに Main.java が表示されれば成功です。

この節の理解度チェック
Q1

IDEを使うメリットを2つ以上挙げてください。

答えを見る
解答例

コード補完が使える/文法エラーを入力中に指摘してくれる/ボタンひとつでコンパイル・実行できる/変数名変更などのリファクタリングを安全に行える、など。

Q2

本テキストで推奨しているIntelliJ IDEAのエディション名と、JDKのバージョンは何ですか。

答えを見る
解答

Community Edition(無償版)と、LTS版のJDK 21(Eclipse Temurinなど)。

Q3

プロジェクト内で、Javaのソースコードを置くフォルダの名前は何ですか。

答えを見る
解答

src フォルダ(source の略)。

1.3

はじめてのプログラム — Hello, World!

内容

雛形コードを読み解く

プロジェクト作成時に生成された Main.java を、次の最小構成に書き換えてみましょう。

src/Main.java
public class Main {
    public static void main(String[] args) {
        System.out.println("Hello, World!");
    }
}

たった5行ですが、Javaの重要な約束事が詰まっています。1行ずつ見ていきます。

コード意味
public class Main { }「Mainという名前のクラス」の宣言。Javaのコードは必ずクラスの中に書く。クラス名とファイル名(Main.java)は一致させる
public static void main(String[] args)mainメソッド。プログラムの実行開始地点(エントリーポイント)。この「呪文」はこの形で丸暗記してよい(意味は第3章で全部わかる)
System.out.println("...")カッコ内の内容を画面(コンソール)に出力して改行する命令
;(セミコロン)文の終わりを示す。Javaでは文の末尾に必ず必要

実行してみる

エディタ左側、mainメソッドの行番号の横にある緑の▶ボタンをクリックし、Run 'Main.main()' を選びます。画面下部のRun(コンソール)に次のように表示されれば成功です。

Run: Main
Hello, World!
Process finished with exit code 0

この▶ボタンの裏側では、1.1で学んだ「javacでコンパイル → JVMで実行」が自動的に行われています。exit code 0 は「正常終了」の意味です。

エラーに慣れる

わざとセミコロンを消して実行してみてください。IntelliJが赤波線で警告し、実行するとコンソールにエラーが出ます。エラーメッセージは敵ではなく、間違い箇所を教えてくれるヒントです。「何行目の・何が」問題なのかを読む習慣を最初につけましょう。

やってみよう 1-2
  1. println をもう1行追加し、自分の名前を出力してください。
  2. printlnprint に変えると出力がどう変わるか確認してください(改行の有無)。
  3. わざと ; を消したり " を片方消したりして、どんなエラー表示になるか観察してください。
PPOINT
  • Javaのプログラムはクラスの中のmainメソッドから始まる。
  • 文の終わりにはセミコロン。ブロックは波かっこ { } で囲む。
  • Javaは大文字・小文字を区別する(Systemsystemと書くとエラー)。
この節の理解度チェック
Q1

Javaプログラムの実行が始まるメソッドの名前と、その決まり文句(シグネチャ)を書いてください。

答えを見る
解答

mainメソッドpublic static void main(String[] args)

Q2

次のコードにはエラーが2箇所あります。指摘して修正してください。

Quiz.java
public class Quiz {
    public static void main(String[] args) {
        system.out.println("Hello")
    }
}
答えを見る
解答

system → 正しくは大文字始まりの System(Javaは大文字小文字を区別する)。② 文末にセミコロン ; がない。正しくは System.out.println("Hello");

Q3

printlnprint の違いは何ですか。

答えを見る
解答

println は出力の最後に改行するprint改行しない(次の出力が同じ行に続く)。

Q4

【実技】「こんにちは、Java!」と「今日から勉強を始めます」を2行で出力するプログラムを、実際にIntelliJで書いて実行してください。

答えを見る
解答例
Main.java
public class Main {
    public static void main(String[] args) {
        System.out.println("こんにちは、Java!");
        System.out.println("今日から勉強を始めます");
    }
}

第1章のまとめ

  • Javaは .java(ソース)→ .class(バイトコード)→ JVMで実行、という流れで動く。
  • 開発者はJDKをインストールする。JDK ⊃ JRE ⊃ JVM。
  • IntelliJ IDEA Community版+JDK 21で環境構築。プロジェクトの src にコードを置く。
  • プログラムは main メソッドから始まり、文末はセミコロン、大文字小文字は区別される。
CHAPTER 02

Javaの基本文法

この章のゴール:変数・演算子・条件分岐・繰り返し・配列という「どんなプログラムにも登場する5つの道具」を使いこなせるようになる。

2.1

変数とデータ型

内容

変数 = 値にラベルを付けた「箱」

変数とは、値を一時的に記憶しておくための「名前付きの箱」です。Javaでは箱を作るときに、どんな種類の値を入れる箱なのか(=型)を必ず宣言します。これがJavaが静的型付け言語と呼ばれる理由で、型の間違いをコンパイル時に発見できるという大きな利点があります。

int age = 20; 型:int(整数) 20 変数名:age ・箱には「入れられる値の種類」が決まっている ・int の箱に "こんにちは" は入れられない ・= は「代入」:右の値を左の箱に入れる
2-1 変数のイメージ。「型 変数名 = 値;」で宣言と代入を同時に行う

基本データ型(プリミティブ型)

Javaには8つの基本型がありますが、まずは太字の4つを使えれば十分です。

意味備考
int整数int age = 20;約±21億まで。整数の第一選択
long大きな整数long p = 10000000000L;末尾にL。intで足りないとき
double小数double pi = 3.14;小数の第一選択
boolean真偽値boolean ok = true;truefalse のみ
char1文字char c = 'A';シングルクォートで囲む
byte / short / float省メモリ用当面使わなくてよい

文字列はString型(参照型)

複数文字の文字列は、基本型ではなくStringというクラス(参照型)で扱います。ダブルクォートで囲むこと、頭が大文字であることに注意してください。基本型と参照型の違いは第3章以降で深掘りしますが、今は「Stringだけ仲間が違う」と覚えておけばOKです。

src/VariableSample.java
public class VariableSample {
    public static void main(String[] args) {
        // 宣言と同時に代入(初期化)
        int age = 20;
        double height = 172.5;
        boolean isStudent = true;
        String name = "佐藤";   // 文字列は " " で囲む

        // 変数は後から値を入れ替えられる
        age = 21;               // 再代入(型は書かない)

        // 文字列と変数は + で連結できる
        System.out.println(name + "さんは" + age + "歳です");
        System.out.println("身長:" + height + "cm 学生:" + isStudent);
    }
}
Run: VariableSample
佐藤さんは21歳です
身長:172.5cm 学生:true

変数の命名ルール

  • 英数字と_が使える。数字始まりは不可。
  • キャメルケースで書くのがJavaの慣習(例:userNametotalPrice)。
  • intclass などの予約語は使えない。
  • 意味の分かる名前を付ける(a より age)。

定数(final)と型推論(var)

snippet
final double TAX_RATE = 0.10;  // finalを付けると再代入禁止=定数。名前は大文字+_
// TAX_RATE = 0.08;            // → コンパイルエラーになる

var message = "hello";          // varと書くと右辺から型を推論(String型になる)

型変換(キャスト)

小さい型から大きい型へは自動で変換されますが(例:int → double)、大きい型から小さい型へは明示的なキャストが必要で、情報が失われることがあります。

snippet
int a = 10;
double b = a;          // OK:自動変換(10.0になる)

double x = 3.9;
int y = (int) x;       // 明示的キャスト:小数部は切り捨てられ 3 になる

int p = 5, q = 2;
System.out.println(p / q);          // 3 ではなく 2(int同士の割り算は整数)
System.out.println((double) p / q); // 2.5(どちらかをdoubleにする)
!初学者が必ずハマる:整数同士の割り算

5 / 2 の結果は 2.5 ではなく 2 です。int同士の演算結果はintになり、小数部は切り捨てられます。小数の結果が欲しいときは、どちらかを double にキャストしてください。

やってみよう 2-1

自分のプロフィール(名前・年齢・身長・プログラミング経験の有無)を4つの変数に格納し、「私は◯◯です。◯歳、身長◯cm、経験:true/false」と1行で出力するプログラムを書いてください。クラス名は Profile とします(src上で右クリック → New → Java Class で新規作成できます)。

この節の理解度チェック
Q1

次の値を格納するのに最も適切な型をそれぞれ答えてください。(a) 商品の個数 (b) 商品の単価にかかる消費税率0.1 (c) 会員かどうか (d) ユーザーの氏名

答えを見る
解答

(a) int (b) double (c) boolean (d) String

Q2

System.out.println(7 / 2); の出力は何ですか。また 3.5 と出力するにはどう書き換えればよいですか。

答えを見る
解答

出力は 3(int同士の割り算は小数切り捨て)。System.out.println(7 / 2.0);(double) 7 / 2 のように、どちらかを小数(double)にすれば 3.5 になる。

Q3

次のコードのうち、コンパイルエラーになる行はどれですか。理由も答えてください。

Quiz.java
int count = 10;          // ア
count = "たくさん";       // イ
final int MAX = 100;     // ウ
MAX = 200;               // エ
答えを見る
解答

:int型の変数にString(文字列)は代入できない。:final付きの変数(定数)には再代入できない。ア・ウは正しい。

Q4

Javaの変数名の慣習である「キャメルケース」とはどんな書き方ですか。「合計金額」を表す変数名を例に書いてください。

答えを見る
解答

先頭の単語は小文字、2語目以降の先頭だけ大文字にする書き方。例:totalPrice

2.2

演算子

内容

算術演算子

演算子意味結果
+加算(文字列なら連結)7 + 29
-減算7 - 25
*乗算7 * 214
/除算7 / 23(int同士)
%剰余(あまり)7 % 21

%(あまり)は地味に見えて頻出です。偶数判定(n % 2 == 0)や「3の倍数のときだけ〜する」のような周期処理で活躍します。

代入を省略する複合代入・インクリメント

snippet
int score = 10;
score = score + 5;   // 「今の値に5足して入れ直す」
score += 5;          // 上と同じ意味(複合代入)。-= *= /= %= もある

int count = 0;
count++;             // count = count + 1 と同じ(インクリメント)
count--;             // 1減らす(デクリメント)

比較演算子 — 結果はboolean

演算子意味例(a=5のとき)結果
==等しいa == 5true
!=等しくないa != 5false
> / >=より大きい / 以上a >= 5true
< / <=より小さい / 以下a < 5false
!「=」と「==」は別物/Stringの比較はequals
  • =代入==比較。条件式に = を書くのは典型的なバグです。
  • 文字列(String)の比較に == を使ってはいけません。参照型の==は「同じオブジェクトか」を見るため、内容が同じでもfalseになることがあります。内容の比較は str.equals("abc") を使います(詳しくは5.1節)。

論理演算子 — 条件の組み合わせ

演算子意味
&&かつ(AND):両方trueならtrueage >= 20 && age < 65
||または(OR):どちらかtrueならtrueday == 6 || day == 7
!〜でない(NOT):真偽を反転!isMember
src/OperatorSample.java
public class OperatorSample {
    public static void main(String[] args) {
        int price = 1200;
        int point = price % 100;              // 下2桁をポイントに
        boolean isExpensive = price >= 1000;  // 比較結果はbooleanに入れられる

        System.out.println("ポイント:" + point);
        System.out.println("高額商品?:" + isExpensive);

        int age = 22;
        boolean canDrink = age >= 20 && age <= 120;  // かつ
        System.out.println("飲酒可?:" + canDrink);
    }
}
やってみよう 2-2

変数 int minutes = 130; を「◯時間◯分」に変換して出力してください(ヒント:/%)。さらに、int year = 2024; がうるう年かどうかをbooleanで出力してください(条件:4で割り切れる かつ 100で割り切れない、または 400で割り切れる)。

この節の理解度チェック
Q1

次の式の結果をそれぞれ答えてください。(a) 17 % 5 (b) 17 / 5 (c) 10 >= 10 (d) true && false (e) !(3 < 5)

答えを見る
解答

(a) 2 (b) 3 (c) true (d) false (e) false(3<5はtrue、その反転)

Q2

「変数 n が偶数である」ことを表す条件式を書いてください。

答えを見る
解答

n % 2 == 0(2で割ったあまりが0)。

Q3

「点数 score が0以上100以下の範囲内である」ことを表す条件式を、論理演算子を使って書いてください。

答えを見る
解答

score >= 0 && score <= 100。数学のように 0 <= score <= 100 とは書けない点に注意。

Q4

x += 3; と同じ意味のコードを、複合代入演算子を使わずに書いてください。

答えを見る
解答

x = x + 3;

2.3

条件分岐(if / switch)

内容

if文 — 条件によって処理を分ける

プログラムは通常、上から下へ一直線に実行されます。if文を使うと「条件がtrueのときだけ実行する」という分かれ道を作れます。

開始 score >= 60 ? 条件式を評価 true 「合格」と表示 false 「不合格」と表示 合流して続行
2-2 if-else文の流れ。条件式の結果(true/false)で通る道が変わる
src/IfSample.java
public class IfSample {
    public static void main(String[] args) {
        int score = 75;

        if (score >= 90) {
            System.out.println("素晴らしい!");
        } else if (score >= 60) {
            System.out.println("合格です");      // 今回はここが実行される
        } else {
            System.out.println("不合格です");
        }
    }
}
  • if (条件式) { } … trueのときだけ実行。
  • else if … 上の条件がfalseだったときの追加条件。上から順に判定され、最初にtrueになった1つだけが実行される。
  • else … どれにも当てはまらなかったとき。
PPOINT:else if は「順番」が命

上の例で score >= 60 を先に書くと、90点でも「合格です」で止まってしまいます。範囲の狭い(厳しい)条件から順に書くのが鉄則です。

switch文 — 値による多分岐

「変数の値が1なら…、2なら…、3なら…」のように特定の値との一致で分岐する場合はswitchが読みやすくなります。Java 14以降では矢印(->)を使った新しい書き方が推奨です。

src/SwitchSample.java
public class SwitchSample {
    public static void main(String[] args) {
        int day = 3;

        // 新しいswitch(Java 14以降・推奨):breakが不要で安全
        switch (day) {
            case 1 -> System.out.println("月曜日");
            case 2 -> System.out.println("火曜日");
            case 3 -> System.out.println("水曜日");
            case 6, 7 -> System.out.println("週末!");   // 複数の値をまとめられる
            default -> System.out.println("その他の曜日");
        }

        // 従来のswitch:case毎にbreakが必要(忘れると下に落ちる)
        switch (day) {
            case 3:
                System.out.println("水曜日(従来型)");
                break;   // これを忘れるとcase 6,7…も実行されてしまう
            default:
                System.out.println("その他");
        }
    }
}
やってみよう 2-3

変数 int temperature の値によって、30以上「暑い」/ 15以上30未満「快適」/ 15未満「寒い」と出力するプログラムを書いてください。値をいろいろ変えて、3パターンすべての出力を確認しましょう。

この節の理解度チェック
Q1

次のコードで score = 95 のとき、出力は何になりますか。このコードの問題点も指摘してください。

Quiz.java
if (score >= 60) {
    System.out.println("合格");
} else if (score >= 90) {
    System.out.println("素晴らしい");
}
答えを見る
解答

出力は「合格」。95は最初の条件 score >= 60 でtrueになるため、後の score >= 90 は判定されない。条件の範囲が広いものを先に書いているのが問題で、「素晴らしい」が絶対に出力されないコードになっている。厳しい条件(>= 90)を先に書くべき。

Q2

従来型のswitch文で、各caseの最後に書き忘れると意図しない動きになるキーワードは何ですか。忘れるとどうなりますか。

答えを見る
解答

break。忘れると処理が止まらず、次のcaseの処理まで続けて実行されてしまう(フォールスルー)。矢印型(->)のswitchならbreak不要でこの事故が起きない。

Q3

【実技】変数 int rank が 1のとき「金」、2のとき「銀」、3のとき「銅」、それ以外は「入賞外」と出力するコードを、矢印型switchで書いてください。

答えを見る
解答例
snippet
switch (rank) {
    case 1 -> System.out.println("金");
    case 2 -> System.out.println("銀");
    case 3 -> System.out.println("銅");
    default -> System.out.println("入賞外");
}
2.4

繰り返し(for / while)

内容

for文 — 回数が決まっている繰り返し

「10回繰り返す」「1から100まで」のように回数や範囲が決まっている繰り返しにはfor文を使います。

src/ForSample.java
public class ForSample {
    public static void main(String[] args) {
        //   ①初期化      ②条件     ③更新
        for (int i = 1; i <= 5; i++) {
            System.out.println(i + "回目のループ");
        }
    }
}
① 初期化(最初の1回だけ) int i = 1 ② 条件判定 i <= 5 ? true ③ ブロック内の処理を実行 println(...) ④ 更新 i++ ②に戻る(②→③→④を繰り返す) false ループ終了
2-3 for文の実行順序。①は最初の1回だけ、以降は②→③→④のサイクル
Run: ForSample
1回目のループ
2回目のループ
3回目のループ
4回目のループ
5回目のループ

while文 — 条件を満たす間、繰り返す

「回数は分からないが、条件を満たす間は続ける」ときはwhile文です。do-whileは「まず1回実行してから条件を見る」変種です。

src/WhileSample.java
public class WhileSample {
    public static void main(String[] args) {
        int hp = 100;
        int turn = 0;

        while (hp > 0) {          // HPが残っている間、戦い続ける
            hp -= 30;
            turn++;
            System.out.println(turn + "ターン目:残りHP " + hp);
        }
        System.out.println(turn + "ターンで戦闘不能になった");
    }
}
!無限ループに注意

whileの中で条件に関わる変数を更新し忘れると、ループが永遠に終わらない「無限ループ」になります(上の例なら hp -= 30; を消した場合)。もし起きたら、IntelliJの実行パネル左にある赤い■(停止)ボタンで止められます。

break と continue

snippet
for (int i = 1; i <= 10; i++) {
    if (i == 4) {
        continue;   // この回だけスキップして次の回へ
    }
    if (i == 7) {
        break;      // ループ全体を打ち切る
    }
    System.out.print(i + " ");
}
// 出力:1 2 3 5 6

二重ループ

ループの中にループを入れることもできます。九九の表が定番の練習です。

src/KukuSample.java
for (int i = 1; i <= 9; i++) {          // 段(外側)
    for (int j = 1; j <= 9; j++) {      // 各段の中(内側)
        System.out.print(i * j + "\t"); // \t はタブ文字
    }
    System.out.println();               // 1段終わったら改行
}
やってみよう 2-4:FizzBuzz

プログラミングの登竜門「FizzBuzz」に挑戦しましょう。1から30まで数え、3の倍数なら「Fizz」、5の倍数なら「Buzz」、両方の倍数(15の倍数)なら「FizzBuzz」、それ以外は数字を出力してください。ヒント:15の倍数の判定を最初に書くのがコツです(2.3のPOINT参照)。

この節の理解度チェック
Q1

for (int i = 0; i < 5; i++) のループは何回実行されますか。また、iの値の変化を順に書いてください。

答えを見る
解答

5回。iは 0 → 1 → 2 → 3 → 4 と変化し、5になった時点で条件 i < 5 がfalseとなり終了。「0始まり・<」の組み合わせは配列処理(2.5)で頻出の形。

Q2

breakcontinue の違いを説明してください。

答えを見る
解答

breakループ全体をその場で打ち切るcontinueその回の残り処理だけをスキップし、次の繰り返しに進む

Q3

次のwhile文の問題点を指摘してください。

Quiz.java
int i = 0;
while (i < 10) {
    System.out.println("Hello");
}
答えを見る
解答

ループ内で i が更新されていないため、条件 i < 10 が永遠にtrueのままの無限ループになる。ブロック内に i++; を追加すれば10回で終了する。

Q4

【実技】1から100までの合計(5050)をfor文で計算して出力するプログラムを書いてください。

答えを見る
解答例
snippet
int sum = 0;
for (int i = 1; i <= 100; i++) {
    sum += i;
}
System.out.println(sum);   // 5050

合計用の変数をループので宣言するのがポイント(中で宣言すると毎回0に戻ってしまう)。

2.5

配列

内容

配列 = 同じ型の箱を並べたもの

生徒40人分の点数を変数40個で管理するのは大変です。配列を使えば、同じ型の値をまとめてひとつの変数で扱えます。各箱には0から始まる番号(インデックス)が付きます。

int[] scores = {80, 65, 92, 74, 58}; scores (配列全体の名前) 80 65 92 74 58 [0] [1] [2] [3] [4] インデックスは 0 から始まる ← 最重要ポイント! 要素数5の配列の最後は scores[4](scores[5]はエラー)
2-4 配列のイメージ。要素にはインデックスでアクセスする
src/ArraySample.java
public class ArraySample {
    public static void main(String[] args) {
        // 作り方1:値を並べて初期化
        int[] scores = {80, 65, 92, 74, 58};

        // 作り方2:サイズだけ決めて後から入れる(初期値は0)
        String[] names = new String[3];
        names[0] = "佐藤";
        names[1] = "鈴木";
        names[2] = "高橋";

        System.out.println(scores[0]);       // 80(最初の要素)
        System.out.println(scores.length);   // 5(要素数)

        // for文と組み合わせるのが定番(iを0からlength未満まで)
        int sum = 0;
        for (int i = 0; i < scores.length; i++) {
            sum += scores[i];
        }
        System.out.println("平均点:" + (double) sum / scores.length);

        // 拡張for文:全要素を順に取り出すだけならこちらが簡潔
        for (String name : names) {
            System.out.println(name + "さん");
        }
    }
}
PPOINT:2種類のforの使い分け
  • 拡張for(for-each) … 全要素を順に読むだけなら最優先。書き間違いが起きない。
  • 通常のfor … インデックス番号が必要なとき(「i番目」を表示したい、要素を書き換えたい等)。
!頻出エラー:ArrayIndexOutOfBoundsException

要素数5の配列に scores[5] でアクセスすると、実行時にArrayIndexOutOfBoundsExceptionという例外(第5章)が発生します。最後の要素は length - 1 番目。「0始まり」を常に意識しましょう。

やってみよう 2-5

int[] scores = {80, 65, 92, 74, 58}; について、①最高点 ②60点以上の人数 の2つをfor文で求めて出力してください(ヒント:最高点は「暫定1位」を覚える変数 max を用意し、より大きい値が出たら更新します)。

この節の理解度チェック
Q1

int[] a = {10, 20, 30}; のとき、(a) a[1] の値 (b) a.length の値 (c) 最後の要素にアクセスする式、をそれぞれ答えてください。

答えを見る
解答

(a) 20(0始まりなので2番目の要素) (b) 3 (c) a[2] または a[a.length - 1]

Q2

次のコードは実行するとエラーになります。原因と修正方法を答えてください。

Quiz.java
int[] nums = {1, 2, 3, 4, 5};
for (int i = 0; i <= nums.length; i++) {
    System.out.println(nums[i]);
}
答えを見る
解答

条件が i <= nums.length のため、最後に i = 5nums[5] にアクセスしてしまい ArrayIndexOutOfBoundsException が発生する(有効なのは[0]〜[4])。i < nums.length(<=ではなく<)に修正する。

Q3

「インデックス番号を使わず、配列の全要素を順に処理する」ためのfor文の書き方を何と呼びますか。String[] names の全要素を出力する例も書いてください。

答えを見る
解答

拡張for文(for-each文)。例:

snippet
for (String name : names) {
    System.out.println(name);
}
Q4

【実技】int[] data = {3, 8, 1, 9, 5}; の合計と平均(小数)を出力するプログラムを書いて実行してください。

答えを見る
解答例
snippet
int[] data = {3, 8, 1, 9, 5};
int sum = 0;
for (int n : data) {
    sum += n;
}
System.out.println("合計:" + sum);                        // 26
System.out.println("平均:" + (double) sum / data.length); // 5.2

平均を出すとき (double) キャストを忘れると 5 になってしまう(2.1の復習)。

第2章のまとめ

  • 変数は「型 変数名 = 値;」。主要な型は int / double / boolean / String。int同士の割り算は切り捨て。
  • 比較は ==>=、組み合わせは &&(かつ)||(または)。Stringの比較は equals
  • 分岐は if / else if / else(厳しい条件から書く)と switch(値の一致)。
  • 繰り返しは回数が決まっていれば for、条件次第なら whilebreakで打ち切り、continueでスキップ。
  • 配列はインデックス0始まり。最後は length - 1。全件処理は拡張forが簡潔。
CHAPTER 03

メソッドとクラスの基礎

この章のゴール:処理を「メソッド」に、データと処理のまとまりを「クラス」に整理できるようになる。オブジェクト指向(第4章)の土台となる最重要チャプター。

3.1

メソッド — 処理に名前を付けて再利用する

内容

メソッドとは

メソッドとは、一連の処理に名前を付けてまとめたものです。料理のレシピの「だしを取る」のように、よく使う手順を部品化しておき、必要なときに呼び出して使います。メリットは3つあります。

  • 再利用:同じコードを何度も書かなくてよい。
  • 見通し:mainが「何をしているか」の目次のように読める。
  • 修正が1箇所で済む:処理を直すときメソッドの中だけ直せばよい。

メソッドの構文 — 入力(引数)と出力(戻り値)

呼び出し側(main) int ans = add(3, 5); 引数(材料を渡す): 3 と 5 add メソッド(処理の工場) int add(int a, int b) { return a + b; } 戻り値(結果を返す): 8 → ansに入る
3-1 メソッド=材料(引数)を受け取り、結果(戻り値)を返す小さな工場
src/MethodSample.java
public class MethodSample {
    public static void main(String[] args) {
        greet();                          // ① 引数なし・戻り値なし
        greet("鈴木");                    // ② 引数あり(オーバーロード)

        int ans = add(3, 5);              // ③ 戻り値を変数で受け取る
        System.out.println("3 + 5 = " + ans);

        double bmi = calcBmi(60.0, 1.7);  // ④ 複数の引数
        System.out.println("BMI: " + bmi);
    }

    // 戻り値なし → 型の場所に void と書く
    static void greet() {
        System.out.println("こんにちは!");
    }

    // 同名でも引数の型・数が違えば定義できる(オーバーロード)
    static void greet(String name) {
        System.out.println("こんにちは、" + name + "さん!");
    }

    // int を返すメソッド:returnで値を返し、そこで処理終了
    static int add(int a, int b) {
        return a + b;
    }

    static double calcBmi(double weightKg, double heightM) {
        return weightKg / (heightM * heightM);
    }
}

構文を分解すると次のとおりです(staticの意味は3.4節で説明します。今は「mainから直接呼ぶメソッドにはstaticを付ける」と覚えてください)。

構文
static 戻り値の型 メソッド名(引数の型 引数名, ...) {
    処理;
    return 戻り値;   // 戻り値の型がvoidならreturn文は省略可
}
PPOINT
  • 引数(ひきすう)=メソッドに渡す入力。戻り値=メソッドが返す出力(1つだけ)。
  • 戻り値が不要なら型を void にする。
  • return は値を返すと同時にメソッドをその場で終了させる。
  • 同名メソッドを引数違いで複数定義することをオーバーロードと呼ぶ。
やってみよう 3-1

2.4で作ったFizzBuzzを、static String fizzBuzz(int n) というメソッドに作り変えてください。数値を受け取り、"Fizz"/"Buzz"/"FizzBuzz"/数字の文字列を返すようにし、mainのfor文からは System.out.println(fizzBuzz(i)); と呼び出します。「表示する処理」と「判定する処理」を分ける、実務でも重要な感覚です。

この節の理解度チェック
Q1

「引数」と「戻り値」をそれぞれ一言で説明してください。また、戻り値がないメソッドの戻り値の型には何と書きますか。

答えを見る
解答

引数=メソッドに渡す入力。戻り値=メソッドが呼び出し元へ返す出力(結果)。戻り値がない場合は void と書く。

Q2

次のメソッドを呼び出す int result = square(4); を実行すると、resultの値はいくつになりますか。

Quiz.java
static int square(int n) {
    return n * n;
}
答えを見る
解答

16。引数nに4が渡り、4 * 4 の結果がreturnで返される。

Q3

同じ名前のメソッドを、引数の型や個数を変えて複数定義することを何と呼びますか。

答えを見る
解答

オーバーロード(多重定義)。第4章で学ぶ「オーバーライド」と名前が似ているので区別して覚えること。

Q4

【実技】税抜価格(int)を受け取り、10%の消費税を加えた税込価格(int)を返すメソッド taxIncluded を定義し、mainから1000円で呼び出して1100と表示させてください。

答えを見る
解答例
snippet
public class TaxSample {
    public static void main(String[] args) {
        System.out.println(taxIncluded(1000));   // 1100
    }

    static int taxIncluded(int price) {
        return (int) (price * 1.1);
    }
}
3.2

クラスとインスタンス

内容

クラス=設計図、インスタンス=実物

ここからがJava学習のハイライト、オブジェクト指向の入口です。オブジェクト指向とは、プログラムを「データ(状態)と処理(振る舞い)を持つモノ(オブジェクト)の集まり」として組み立てる考え方です。

クラスはモノの設計図インスタンス(オブジェクト)は設計図から作られた実物です。たい焼きの「型」と「たい焼き」の関係でよく例えられます。1つの型(クラス)から、中身の違うたい焼き(インスタンス)を何個でも作れます。

クラス Hero(設計図) フィールド(データの項目) String name int hp メソッド(振る舞い) attack() new Hero() new Hero() インスタンス1(実物)変数: hero1 name = "勇者アレン" hp = 100 それぞれが自分のデータを持つ インスタンス2(実物)変数: hero2 name = "戦士ベルタ" hp = 150 同じ設計図でも中身は別 1つのクラスから、独立したインスタンスをいくつでも作れる
3-2 クラス(設計図)とインスタンス(実物)。newで実体化する

クラスを定義して使う

クラスは「フィールド(そのモノが持つデータ)」と「メソッド(そのモノができること)」で構成します。IntelliJで src を右クリック → New → Java Class で Hero クラスを作りましょう。

src/Hero.java
public class Hero {
    // フィールド:このモノが持つデータ(状態)
    String name;
    int hp;

    // メソッド:このモノの振る舞い。フィールドを直接使える
    void attack() {
        System.out.println(name + "は攻撃した!");
    }

    void damage(int point) {
        hp -= point;
        System.out.println(name + "は" + point + "のダメージ!残りHP:" + hp);
    }
}
src/Main.java
public class Main {
    public static void main(String[] args) {
        // newでインスタンスを生成し、変数に入れる
        Hero hero1 = new Hero();
        hero1.name = "勇者アレン";     // ドット(.)でフィールドにアクセス
        hero1.hp = 100;

        Hero hero2 = new Hero();
        hero2.name = "戦士ベルタ";
        hero2.hp = 150;

        hero1.attack();      // メソッドもドットで呼ぶ
        hero2.damage(30);    // hero2のhpだけが減る(hero1は無関係)
    }
}
Run: Main
勇者アレンは攻撃した!
戦士ベルタは30のダメージ!残りHP:120

参照型の正体 — 変数には「住所」が入っている

2.1で「Stringは参照型」と言いました。実は、インスタンスを入れた変数に入っているのは実体そのものではなく、実体の場所を指す「参照(住所)」です。だから次のコードでは、コピーしたつもりでも同じ実体を指します。

snippet
Hero a = new Hero();
a.hp = 100;
Hero b = a;        // 実体のコピーではなく「同じ実体への参照」のコピー
b.hp = 50;
System.out.println(a.hp);   // 50!(aとbは同じHeroを指している)
やってみよう 3-2

Book クラスを作ってください。フィールドは String titleint price、メソッドは書名と価格を「『◯◯』:◯円」と表示する void printInfo()。mainで自分の好きな本を2冊(2インスタンス)作り、それぞれ表示してください。

この節の理解度チェック
Q1

「クラス」と「インスタンス」の関係を、身近な例え(設計図と実物など)を使って説明してください。また、インスタンスを生成するキーワードは何ですか。

答えを見る
解答例

クラスは「たい焼きの型(設計図)」、インスタンスは「その型から焼かれたたい焼き(実物)」。1つのクラスから複数のインスタンスを作れ、それぞれ独立したデータを持つ。生成には new を使う。

Q2

クラスが持つ「データ」と「振る舞い」は、それぞれJavaの用語で何と呼びますか。

答えを見る
解答

データ=フィールド、振る舞い=メソッド。合わせて「メンバ」と呼ぶ。

Q3

次のコードの出力は何になりますか。理由も説明してください。

Quiz.java
Hero x = new Hero();
x.hp = 80;
Hero y = new Hero();
y.hp = 80;
y.hp -= 30;
System.out.println(x.hp);
答えを見る
解答

80。xとyは別々にnewされた独立のインスタンスなので、yのhpを減らしてもxには影響しない(もし Hero y = x; なら同じ実体を指すため50になる)。

3.3

コンストラクタとthis

内容

コンストラクタ — 生成時の初期化を保証する

3.2の書き方には弱点があります。namehp の設定を忘れても動いてしまい、名前がnull(未設定)の勇者が生まれてしまうのです。そこで使うのがコンストラクタ:newされた瞬間に自動で実行される特別なメソッドです。

  • メソッド名をクラス名と完全に同じにする。
  • 戻り値の型は書かない(voidも書かない)。
src/Hero.java(改良版)
public class Hero {
    String name;
    int hp;

    // コンストラクタ:new Hero("勇者アレン", 100) のときに実行される
    Hero(String name, int hp) {
        this.name = name;   // this.name=フィールド、name=引数
        this.hp = hp;
    }

    void attack() {
        System.out.println(name + "は攻撃した!");
    }
}
src/Main.java
Hero hero1 = new Hero("勇者アレン", 100);   // 生成と初期化が1行で完了
hero1.attack();

// Hero hero2 = new Hero();   // ← コンパイルエラー!
// 引数付きコンストラクタを定義すると、引数なしのnewはできなくなる
// (=初期化忘れをコンパイラが防いでくれる)

thisの意味

this は「このインスタンス自身」を指すキーワードです。上の例では、引数名 name とフィールド名 name が同じため、this.name と書いて「フィールドの方のname」であることを明示しています。この this.フィールド = 引数; はJavaの最頻出イディオムなので、指が覚えるまで書きましょう。

PPOINT:IntelliJの自動生成を使おう

フィールドを書いた状態でエディタ上で右クリック → Generate...(Alt+Insert / Cmd+N)→ Constructor を選ぶと、コンストラクタを自動生成できます。仕組みを理解した上で、入力はIDEに任せるのがプロの書き方です。

やってみよう 3-3

3.2で作った Book クラスにコンストラクタを追加し、mainを new Book("Java入門", 2800) の形に書き換えてください。Generate機能も試すこと。

この節の理解度チェック
Q1

コンストラクタが通常のメソッドと違う点を2つ挙げてください。

答えを見る
解答

① 名前がクラス名と同じでなければならない。② 戻り値の型を書かない(voidも不可)。また、newの瞬間に自動で呼ばれる点も特徴。

Q2

this.name = name; というコードの、左辺の this.name と右辺の name はそれぞれ何を指していますか。

答えを見る
解答

左辺 this.nameこのインスタンスのフィールド、右辺 nameコンストラクタ(メソッド)の引数。同名のときはthisでフィールド側を明示する。

Q3

【実技】Product クラス(フィールド:String name, int price)にコンストラクタを定義し、mainで「消しゴム・120円」の商品を生成して System.out.println(p.name + ":" + p.price + "円"); で表示してください。

答えを見る
解答例
snippet
public class Product {
    String name;
    int price;

    Product(String name, int price) {
        this.name = name;
        this.price = price;
    }
}

// main側
Product p = new Product("消しゴム", 120);
System.out.println(p.name + ":" + p.price + "円");
3.4

staticの理解 — インスタンスに属さないメンバ

内容

staticとは「クラスに1つだけ」

ここまでのフィールドやメソッドは、インスタンスごとに存在しました(hero1のhpとhero2のhpは別物)。これに対して static を付けたメンバは、インスタンスではなくクラスそのものに属し、全体で1つだけ存在します。

src/Counter.java
public class Counter {
    static int total = 0;   // staticフィールド:全インスタンスで共有
    int myCount = 0;        // 通常のフィールド:インスタンスごと

    void countUp() {
        total++;            // 共有カウンタ
        myCount++;          // 自分専用カウンタ
    }
}
src/Main.java
Counter a = new Counter();
Counter b = new Counter();
a.countUp();
a.countUp();
b.countUp();

System.out.println(a.myCount);      // 2(aは2回)
System.out.println(b.myCount);      // 1(bは1回)
System.out.println(Counter.total);  // 3(全体で3回。クラス名でアクセス)

staticメソッド

staticなメソッドはインスタンスを作らずにクラス名から直接呼べます。特定のインスタンスの状態(フィールド)に依存しない、道具的な処理に向いています。実は皆さんはもう使っています:Math.max(3, 5)Integer.parseInt("123") はstaticメソッドです。

snippet
// Mathクラスのstaticメソッド:newせずにクラス名.メソッド名で呼ぶ
System.out.println(Math.max(3, 5));      // 5
System.out.println(Math.pow(2, 10));     // 1024.0
System.out.println(Math.random());       // 0.0以上1.0未満の乱数

int n = Integer.parseInt("123");         // 文字列→intの変換(頻出!)

mainの呪文が全部読めるようになった

第1章で丸暗記した public static void main(String[] args) を、今なら分解できます。

部品意味
publicどこからでも呼べる公開メソッド(詳細は4.1・6.1)
staticインスタンスを作らなくても呼べる(JVMが最初に呼ぶために必要)
void戻り値なし
mainJVMが探しに来る決められた名前
String[] args起動時に渡されるコマンドライン引数(文字列の配列)
!staticメソッドからインスタンスメンバは直接使えない

staticなmainの中から、staticでないメソッドやフィールドを直接呼ぶとエラーになります(「どのインスタンスの?」が特定できないため)。newしてインスタンス経由で呼ぶのが正解です。初学者エラーの定番なので、出会ったら「staticの世界とインスタンスの世界が混ざったな」と考えてください。

この節の理解度チェック
Q1

staticフィールドと通常のフィールドの違いを「誰に属するか」「いくつ存在するか」の観点で説明してください。

答えを見る
解答

通常のフィールドはインスタンスに属し、インスタンスの数だけ存在する。staticフィールドはクラスに属し、全体で1つだけ存在して全インスタンスに共有される。

Q2

Math.max(1, 2) のように、newせずに呼び出せるのはMathクラスのmaxがどんなメソッドだからですか。

答えを見る
解答

staticメソッドだから。staticメソッドはクラスに属するため、インスタンスを生成せず「クラス名.メソッド名」で呼べる。

Q3

本文のCounterの例で、さらに b.countUp(); を1回実行した場合、a.myCount / b.myCount / Counter.total はそれぞれいくつになりますか。

答えを見る
解答

a.myCount = 2(変化なし)、b.myCount = 2Counter.total = 4(共有なので合計回数)。

第3章のまとめ

  • メソッドは「入力=引数、出力=戻り値」の処理部品。returnで値を返して終了。同名・引数違いはオーバーロード。
  • クラス=設計図(フィールド+メソッド)、インスタンス=newで作る実物。変数に入るのは「参照」。
  • コンストラクタはnew時に自動実行される初期化専用の特別メソッド。this.field = 引数; は最頻出イディオム。
  • staticメンバはクラスに1つだけ。Math.max() のようにクラス名から直接呼べる。
CHAPTER 04

オブジェクト指向プログラミング

この章のゴール:オブジェクト指向の三本柱「カプセル化・継承・ポリモーフィズム」を理解する。Spring Bootのコードが読めるかどうかは、この章の理解にかかっている。

4.1

カプセル化 — データを守る

内容

フィールドを外から触り放題にしない

3章のHeroには問題があります。hero1.hp = -9999; のように、外からおかしな値を自由に入れられてしまうのです。カプセル化とは、フィールドを外部から隠し(private)、公開された窓口メソッド(getter/setter)経由でのみ読み書きさせる設計です。窓口でチェックを行えば、不正な値からデータを守れます。

外部のコード (mainなど) Hero クラス(カプセル) 窓口:setHp(int) チェックしてから代入 窓口:getHp() 値を読み出す private int hp 外から直接は 見えない・触れない 直接アクセスは禁止(private)
4-1 カプセル化。privateで隠し、公開メソッド(窓口)経由でのみアクセスさせる

アクセス修飾子とgetter/setter

メンバの公開範囲を決めるキーワードをアクセス修飾子と呼びます(全種類は6.1節)。まずは2つ:public(どこからでもOK)と private(同じクラス内のみ)。カプセル化の定石は「フィールドはprivate、メソッドはpublic」です。

src/Hero.java(カプセル化版)
public class Hero {
    private String name;   // 外部から直接触れない
    private int hp;

    public Hero(String name, int hp) {
        this.name = name;
        this.hp = hp;
    }

    // getter:読み出し用の窓口(命名は get + フィールド名)
    public int getHp() {
        return hp;
    }

    // setter:書き込み用の窓口。ここでデータを守る!
    public void setHp(int hp) {
        if (hp < 0) {
            this.hp = 0;      // 不正な値は補正(0未満にはさせない)
        } else {
            this.hp = hp;
        }
    }

    public String getName() {
        return name;
    }
    // nameのsetterをあえて作らない=「名前は生成後に変更不可」という設計
}
src/Main.java
Hero hero = new Hero("勇者アレン", 100);
// hero.hp = -9999;          // コンパイルエラー:privateには触れない
hero.setHp(-9999);           // 窓口経由 → チェックが働き0に補正される
System.out.println(hero.getName() + "のHP:" + hero.getHp());   // 0
PPOINT
  • getter/setterもIntelliJの Generate...(Alt+Insert / Cmd+N)→ Getter and Setter で自動生成できる。
  • setterを作らなければ「読み取り専用」にできる。公開する窓口を選ぶこと自体が設計
  • この「private+getter/setter」の形はSpringでもDBと対応するクラス(エンティティ)などで日常的に登場する。
やってみよう 4-1

BankAccount クラスを作ってください。フィールドは private int balance(残高)。メソッドは deposit(int amount)(入金:正の値のみ受付)、withdraw(int amount)(出金:残高不足なら「残高不足です」と表示して何もしない)、getBalance()。mainから入金・出金・残高不足のケースを試してください。

この節の理解度チェック
Q1

カプセル化とは何をすることですか。「private」「メソッド」という言葉を使って説明してください。

答えを見る
解答例

フィールドをprivateにして外部から直接アクセスできないよう隠し、公開したメソッド(getter/setter)経由でのみ読み書きさせること。窓口で値のチェックができるため、不正なデータからオブジェクトを守れる。

Q2

フィールド private int age; に対する、Javaの命名慣習に沿ったgetterとsetterのメソッド名を書いてください。

答えを見る
解答

getter:getAge()、setter:setAge(int age)。boolean型の場合はgetterを isXxx() とする慣習もある。

Q3

「読み取りはできるが、外部から書き換えられない」フィールドにしたい場合、getter/setterをどう用意すればよいですか。

答えを見る
解答

getterだけを公開し、setterを作らない。(さらに厳密にするならフィールドをfinalにして、コンストラクタでのみ設定する)

4.2

継承 — 共通部分を親クラスにまとめる

内容

コードの重複を「親子関係」で解決する

ゲームに勇者(Hero)と魔法使い(Wizard)がいるとします。どちらも「名前・HP・攻撃」を持ちますが、魔法使いだけ「魔法を唱える」ができる——こんなとき、共通部分をコピペで二重に書くのは最悪手です。継承を使えば、共通部分を親クラス(スーパークラス)にまとめ、子クラス(サブクラス)は差分だけを書くことができます。

Character(親クラス) name, hp attack() 共通部分をここに集約 extends(〜は…の一種、is-a関係) Hero(子クラス) name / hp / attack() を引き継ぐ + 追加:必殺技 special() Wizard(子クラス) name / hp / attack() を引き継ぐ + 追加:魔法 castSpell()
4-2 継承。子は親のメンバを引き継ぎ、差分(追加・変更)だけを書く
src/Character.java(親)
public class Character {
    String name;
    int hp;

    public Character(String name, int hp) {
        this.name = name;
        this.hp = hp;
    }

    public void attack() {
        System.out.println(name + "は攻撃した!");
    }
}
src/Wizard.java(子)
public class Wizard extends Character {   // extendsで継承

    public Wizard(String name, int hp) {
        super(name, hp);   // super(...) = 親のコンストラクタを呼ぶ(先頭に書く)
    }

    // 追加:子クラス独自のメソッド
    public void castSpell() {
        System.out.println(name + "は魔法を唱えた!");
    }

    // 変更:親のメソッドを上書き(オーバーライド)
    @Override
    public void attack() {
        System.out.println(name + "は杖で殴った!");
    }
}
src/Main.java
Wizard wiz = new Wizard("魔法使いメル", 70);
wiz.attack();      // 魔法使いメルは杖で殴った!(オーバーライド版が動く)
wiz.castSpell();   // 魔法使いメルは魔法を唱えた!(独自メソッド)

継承の3つのキーワード

キーワード役割
extends継承の宣言。class 子 extends 親。Javaは単一継承(親は1つだけ)
super親を指すキーワード。super(...)で親コンストラクタ、super.method()で親メソッドを呼べる
@Override「親メソッドの上書きです」という目印(アノテーション:6.3節)。付けるとスペルミス等をコンパイラが検出してくれるので必ず付ける
!オーバーロードとオーバーライドの区別
  • オーバーロード(3.1):同じクラス内で、同名・引数違いのメソッドを複数定義。
  • オーバーライド(4.2):継承関係で、親とまったく同じシグネチャのメソッドを子が上書き。
PPOINT:継承は「is-a関係」のときだけ

「Wizard is a Character(魔法使いはキャラクターの一種)」が自然に成り立つときだけ継承を使います。「車はエンジンを持つ(has-a)」のような関係は、継承ではなくフィールドとして持たせるのが正解です。

やってみよう 4-2

親クラス Animal(フィールド:name、メソッド:cry()で「…」と鳴く)を作り、子クラス DogCatcry() をオーバーライドして「ワン!」「ニャー」と鳴かせてください。@Override を付けること。

この節の理解度チェック
Q1

継承を使う最大のメリットは何ですか。また、継承を宣言するキーワードは何ですか。

答えを見る
解答

共通のフィールド・メソッドを親クラスにまとめ、コードの重複をなくせること(子は差分だけ書けばよい)。キーワードは extends

Q2

子クラスのコンストラクタから親クラスのコンストラクタを呼び出す書き方と、その記述位置のルールを答えてください。

答えを見る
解答

super(引数); と書く。コンストラクタの先頭(1行目)に書かなければならない。

Q3

「オーバーライド」と「オーバーロード」の違いを説明してください。

答えを見る
解答

オーバーライドは継承した親のメソッドを、子が同じシグネチャで上書きすること。オーバーロードは同じクラス内で同名メソッドを引数の型・数を変えて複数定義すること。

Q4

@Override アノテーションを付けることのメリットは何ですか。

答えを見る
解答

「オーバーライドのつもり」がメソッド名のスペルミスや引数違いでただの新メソッド定義になってしまうミスを、コンパイラがエラーとして検出してくれる。読み手への目印にもなる。

4.3

ポリモーフィズム — 同じ呼び出しで違う動き

内容

「親の型」で「子の実体」を扱える

ポリモーフィズム(多態性)は、オブジェクト指向の中でも最初は掴みにくい概念ですが、要点は2つだけです。

  1. 子クラスのインスタンスは、親クラスの型の変数に入れられる(Character c = new Wizard(...))。
  2. その状態でメソッドを呼ぶと、変数の型ではなく実体(new した方)のオーバーライド版が動く
src/Main.java
public class Main {
    public static void main(String[] args) {
        // 親の型の配列に、いろんな子の実体を混ぜて入れられる
        Character[] party = {
            new Hero("勇者アレン", 100),
            new Wizard("魔法使いメル", 70),
            new Wizard("賢者ソラ", 60)
        };

        // 呼び出すコードは1種類なのに、動きはそれぞれ違う!
        for (Character c : party) {
            c.attack();
        }
    }
}
Run: Main
勇者アレンは攻撃した!
魔法使いメルは杖で殴った!
賢者ソラは杖で殴った!
呼び出し側は1種類 c.attack(); (cはCharacter型) 実体に応じて動きが変わる 実体がHeroなら 「攻撃した!」 実体がWizardなら 「杖で殴った!」(オーバーライド版) 将来 Ninja クラスを追加しても 呼び出し側のコードは修正不要!
4-3 ポリモーフィズム。同じ呼び出しコードのまま、実体ごとに違う動きになる

なぜ嬉しいのか

呼び出し側(for文)は「相手がCharacterの一種であること」しか知りません。それでも正しく動き、将来 Ninja クラスを追加しても呼び出し側は1文字も変えなくてよいのです。「呼び出し側を変えずに種類を増やせる」——これがポリモーフィズムの価値で、フレームワークが皆さんの作ったクラスを呼び出せるのも同じ仕組みです。

PPOINT:親の型に入れると「親にあるものしか呼べない」

Character c = new Wizard(...) のとき、c.castSpell() はコンパイルエラーになります(Character型にcastSpellは無いため)。呼べるメソッドは変数の型で決まり、どの実装が動くかは実体で決まる——この2文がポリモーフィズムの核心です。

この節の理解度チェック
Q1

Character c = new Wizard("メル", 70); という代入が許される理由を「is-a関係」という言葉を使って説明してください。

答えを見る
解答例

WizardはCharacterを継承しており「Wizard is a Character(魔法使いはキャラクターの一種)」というis-a関係が成り立つ。子の実体は親の一種なので、親の型の変数で受け取れる。

Q2

上記の変数cに対して c.attack() を呼ぶと、CharacterとWizardどちらのattack()が実行されますか。理由も答えてください。

答えを見る
解答

Wizardのattack()(オーバーライド版)。どの実装が動くかは変数の型ではなく実体(newしたクラス)で決まるため。

Q3

同じ変数cに対して c.castSpell() を呼ぶとどうなりますか。

答えを見る
解答

コンパイルエラーになる。呼び出せるメソッドは変数の型(Character)で決まり、CharacterにcastSpell()は定義されていないため。(どうしても呼びたい場合は ((Wizard) c).castSpell(); とキャストする)

Q4

ポリモーフィズムの実務上のメリットを「呼び出し側」という言葉を使って1文で説明してください。

答えを見る
解答例

新しい種類(子クラス)を追加しても呼び出し側のコードを修正せずに済むため、変更に強いプログラムになる。

4.4

抽象クラスとインターフェース

内容

抽象クラス — 「中身は子に書かせる」親

4.2のCharacterには実は違和感があります。「キャラクター一般」がどう攻撃するかは決めようがないのに、attack()の中身を書いてしまっている点です。そこで抽象クラス(abstract class)を使うと、「attackというメソッドは必ずある。ただし中身は子クラスが必ず実装せよ」と強制できます。

src/Character.java(抽象クラス版)
public abstract class Character {
    String name;
    int hp;

    public Character(String name, int hp) {
        this.name = name;
        this.hp = hp;
    }

    // 抽象メソッド:中身なし(セミコロンで終わる)。子に実装を強制する
    public abstract void attack();

    // 普通のメソッドも持てる(共通処理はここに書く)
    public void showStatus() {
        System.out.println(name + " HP:" + hp);
    }
}

// Character c = new Character("?", 10);  // エラー:抽象クラスはnewできない

インターフェース — 「できること」の契約

インターフェース(interface)は、抽象クラスをさらに徹底したもので、原則「メソッドの一覧(=できることの契約)だけ」を定めます。クラスは implements で契約に署名し、宣言されたメソッドをすべて実装する義務を負います。継承(extends)は1つしかできませんが、インターフェースは複数実装できます

interface Attackable(契約書) void attack(); 「attackできること」を約束 中身(実装)は書かない ✎ implements = 契約にサイン class Hero implements Attackable attack()を実装する義務を負う class Slime implements Attackable 継承関係がなくても契約は結べる 契約さえ守っていれば、呼び出し側は Attackable型 としてまとめて扱える (ポリモーフィズムがクラスの親子関係を超えて使える)
4-4 インターフェース=実装義務を課す「契約書」。implementsでサインする
src/InterfaceSample.java
interface Attackable {
    void attack();   // インターフェース内は自動的に public abstract
}

interface Healable {
    void heal();
}

// 複数のインターフェースを同時に実装できる(カンマ区切り)
class Priest implements Attackable, Healable {
    @Override
    public void attack() {
        System.out.println("僧侶はメイスで攻撃!");
    }

    @Override
    public void heal() {
        System.out.println("僧侶は回復魔法を唱えた!");
    }
}

public class InterfaceSample {
    public static void main(String[] args) {
        Attackable a = new Priest();   // インターフェース型で受ける
        a.attack();
    }
}

使い分けの目安

観点抽象クラスインターフェース
表すもの「〜の一種」(is-a)+共通実装「〜できる」(can-do)という契約
フィールド持てる原則持てない(定数のみ)
実装済みメソッド持てる原則なし(defaultメソッドは例外)
数の制限extendsは1つだけimplementsはいくつでも
キーワードabstract class / extendsinterface / implements
PPOINT:インターフェースはSpring理解の鍵

Springなどのフレームワークは「インターフェースに対してプログラミングする」文化が徹底しています。「呼び出し側は契約(インターフェース)だけを知り、実装クラスは差し替え可能にする」——この設計思想を今のうちに体に入れておくと、Web開発の学習が一気に楽になります。

やってみよう 4-4

インターフェース Shape(メソッド:double area())を定義し、Circle(半径rを持つ)と Rectangle(幅wと高さhを持つ)に実装してください。mainでは Shape[] shapes に両方を入れ、拡張for文で全図形の面積を出力します(円周率は Math.PI)。

この節の理解度チェック
Q1

抽象クラスを直接 new するとどうなりますか。また、抽象メソッドを持つクラスを継承した子クラスは何をしなければなりませんか。

答えを見る
解答

抽象クラスはnewできず、コンパイルエラーになる。子クラスはすべての抽象メソッドをオーバーライド(実装)する義務がある(しない場合は子も抽象クラスにするしかない)。

Q2

クラスがインターフェースを実装するときに使うキーワードは何ですか。また、実装できるインターフェースの数に制限はありますか。

答えを見る
解答

implements複数実装可能(カンマ区切り)。一方、クラスの継承(extends)は1つだけ。

Q3

「is-a(〜の一種)」と「can-do(〜できる)」という観点で、抽象クラスとインターフェースの使い分けを説明してください。

答えを見る
解答例

共通のデータや実装を持つ「〜の一種」という親子関係(is-a)には抽象クラス。データを持たず「〜という操作ができる」という能力・契約(can-do)を表すにはインターフェースを使う。

第4章のまとめ

  • カプセル化:フィールドはprivate、アクセスはgetter/setter経由。窓口でデータを守る。
  • 継承:extendsで共通部分を親に集約。superで親を参照、@Overrideで上書き。is-aのときだけ使う。
  • ポリモーフィズム:親(またはインターフェース)の型で子の実体を扱える。呼べるメソッドは型で、動く実装は実体で決まる。
  • 抽象クラス/インターフェース:実装を子に強制する仕組み。インターフェースは複数実装でき、Spring設計の基礎になる。
CHAPTER 05

実践的なJava機能

この章のゴール:実務コードに毎日登場する4点セット——文字列操作・コレクション・例外処理・Stream API——を使えるようになる。Webフレームワークのコードはこれらの塊でできている。

5.1

文字列の操作

内容

Stringはメソッドを持つオブジェクト

Stringは参照型、つまりクラスです。文字列そのものが多くの便利メソッドを持っています。まずは頻出メソッドを一気に手で打って覚えましょう。

src/StringSample.java
public class StringSample {
    public static void main(String[] args) {
        String s = "Hello, Java World";

        System.out.println(s.length());              // 17:文字数
        System.out.println(s.toUpperCase());         // HELLO, JAVA WORLD
        System.out.println(s.toLowerCase());         // hello, java world
        System.out.println(s.contains("Java"));      // true:含むか
        System.out.println(s.startsWith("Hello"));   // true
        System.out.println(s.indexOf("Java"));       // 7:最初に現れる位置
        System.out.println(s.substring(7, 11));      // Java:7文字目から11文字目の手前まで
        System.out.println(s.replace("World", "入門")); // Hello, Java 入門
        System.out.println("  空白あり  ".trim());     // 前後の空白を除去

        // 分割:カンマ区切り文字列を配列に(CSV処理の基本)
        String csv = "りんご,みかん,ぶどう";
        String[] fruits = csv.split(",");
        for (String f : fruits) {
            System.out.println(f);
        }
    }
}

最重要:文字列の比較は equals

2.2でも触れた注意点をここで確定させます。参照型に対する == は「同じインスタンスを指しているか」の比較であり、内容の比較ではありません。文字列の内容比較は必ず equals を使います。

snippet
String input = new String("yes");

System.out.println(input == "yes");        // false になり得る(別インスタンス)
System.out.println(input.equals("yes"));   // true:内容の比較はこちら
System.out.println(input.equalsIgnoreCase("YES"));  // true:大文字小文字無視

大量連結には StringBuilder

Stringは不変(immutable)——一度作ると内容を変更できず、+ での連結は毎回新しいStringを作ります。ループで何千回も連結すると非効率なため、その場合はStringBuilderを使います。

snippet
StringBuilder sb = new StringBuilder();
for (int i = 1; i <= 5; i++) {
    sb.append(i).append(",");   // 連結してもインスタンスは1つのまま
}
String result = sb.toString();  // 最後にStringへ変換
System.out.println(result);     // 1,2,3,4,5,

整形出力:printf / String.format

snippet
double price = 1234.5;
// %s:文字列 %d:整数 %f:小数 %n:改行
System.out.printf("価格は%.1f円です%n", price);          // 価格は1234.5円です
String msg = String.format("%sさん(%d歳)", "佐藤", 20); // 変数に整形結果を入れる
やってみよう 5-1

メールアドレスの文字列 String email = "taro.yamada@example.com"; から、①@より前(ローカル部) ②@より後ろ(ドメイン) を取り出して出力してください(ヒント:indexOf("@")substring、または split)。

この節の理解度チェック
Q1

2つのString変数 ab の「内容が等しいか」を正しく判定する式を書いてください。a == b がダメな理由も説明してください。

答えを見る
解答

a.equals(b)== は参照(同じインスタンスを指すか)の比較なので、内容が同じでも別インスタンスならfalseになることがあるため。

Q2

"programming".substring(3, 7) の結果は何ですか。

答えを見る
解答

"gram"。インデックス3(0始まりで4文字目の g)から、7の手前まで。終了インデックスの文字は含まれない点に注意。

Q3

ループ内で文字列を大量に連結する場合、Stringの + ではなく何を使うべきですか。その理由も答えてください。

答えを見る
解答

StringBuilder(appendで連結)。Stringは不変で、+連結のたびに新しいインスタンスが作られて非効率だが、StringBuilderは1つのインスタンス内で追記できるため。

Q4

【実技】String date = "2026-07-16";split で分解し、「2026年7月16日」と出力してください(ヒント:月・日の先頭の0は Integer.parseInt でintにすると消せます)。

答えを見る
解答例
snippet
String date = "2026-07-16";
String[] parts = date.split("-");
int year  = Integer.parseInt(parts[0]);
int month = Integer.parseInt(parts[1]);
int day   = Integer.parseInt(parts[2]);
System.out.println(year + "年" + month + "月" + day + "日");
5.2

コレクションフレームワーク

内容

配列の弱点を解決する「伸び縮みする入れ物」

配列はサイズが固定で、後から要素を追加できません。実務ではコレクション——サイズが自動で伸び縮みする入れ物——を使うのが基本です。代表は次の3種類で、それぞれインターフェース(4.4!)として定義され、実装クラスを選んで使います

List 順番あり・重複OK 0: "りんご" 1: "みかん" 2: "りんご" 主な実装:ArrayList 用途:一覧・履歴など 使用頻度 No.1 Map キーと値のペア "apple" → 150 "banana" → 100 キーで値を高速に検索 主な実装:HashMap 用途:辞書・設定・ID→データ Set 重複なし "赤" "青" 同じ値を入れても1つになる 主な実装:HashSet 用途:重複排除・所属判定
5-1 コレクション3兄弟。List(順序)・Map(対応表)・Set(重複なし)

ArrayList — 最頻出のコレクション

<String> のような山かっこはジェネリクスと呼ばれ、「この入れ物にはStringだけ入れられる」と型を指定する仕組みです。intなどの基本型は直接入れられないため、対応するクラス(ラッパークラス:IntegerDouble など)を指定します。

src/ListSample.java
import java.util.ArrayList;
import java.util.List;    // java.util パッケージの読み込み(6.1節)

public class ListSample {
    public static void main(String[] args) {
        // 変数はインターフェース型(List)で受けるのが定石(4.4の実践!)
        List<String> fruits = new ArrayList<>();

        fruits.add("りんご");            // 追加(サイズは自動で伸びる)
        fruits.add("みかん");
        fruits.add("ぶどう");

        System.out.println(fruits.get(0));        // りんご:取得
        System.out.println(fruits.size());        // 3:要素数(lengthではない)
        System.out.println(fruits.contains("みかん")); // true

        fruits.remove("ぶどう");                   // 削除
        fruits.set(1, "メロン");                   // 置き換え

        for (String f : fruits) {                 // 拡張forが使える
            System.out.println(f);
        }
    }
}

HashMap — キーで値を引く対応表

src/MapSample.java
import java.util.HashMap;
import java.util.Map;

public class MapSample {
    public static void main(String[] args) {
        Map<String, Integer> prices = new HashMap<>();  // キーの型, 値の型

        prices.put("apple", 150);     // 登録
        prices.put("banana", 100);
        prices.put("apple", 180);     // 同じキーは上書きされる

        System.out.println(prices.get("apple"));      // 180
        System.out.println(prices.get("melon"));      // null(存在しないキー)
        System.out.println(prices.containsKey("banana")); // true

        // 全ペアを回す
        for (Map.Entry<String, Integer> e : prices.entrySet()) {
            System.out.println(e.getKey() + " = " + e.getValue() + "円");
        }
    }
}
PPOINT
  • 宣言は List<String> list = new ArrayList<>(); のように左辺をインターフェース型にするのが定石。後で実装を差し替えやすい。
  • 要素数は配列が length、Stringが length()、コレクションが size()。紛らわしいので整理して覚える。
  • Mapに存在しないキーで get すると null が返る。nullの扱いは5.3の例外にも関わる重要テーマ。
やってみよう 5-2

List<Integer> にテストの点数を5つ追加し、拡張forで合計と平均を出力してください。②Map<String, String> で「都道府県 → 県庁所在地」を3件登録し、キーを1つ指定して値を出力してください。

この節の理解度チェック
Q1

次の用途に最も適したコレクションを List / Map / Set から選んでください。(a) 商品IDから商品名を検索したい (b) 訪問者の名前を来た順に記録したい(同じ人が2回来ることもある) (c) アンケートに登場した回答の「種類」だけを知りたい

答えを見る
解答

(a) Map(キー=ID、値=商品名) (b) List(順序を保ち重複も可) (c) Set(重複が自動で排除される)

Q2

List<Integer> nums = new ArrayList<>(); について、(a) 山かっこ <Integer> の仕組みの名前 (b) int ではなく Integer と書く理由、を答えてください。

答えを見る
解答

(a) ジェネリクス(入れられる型の指定)。(b) ジェネリクスには基本型を指定できないため、intに対応するラッパークラスIntegerを使う。

Q3

Mapに put("A", 1) した後、さらに put("A", 9) するとどうなりますか。また、存在しないキーを get すると何が返りますか。

答えを見る
解答

同じキーへのputは値が上書きされ、"A"の値は9になる。存在しないキーのgetは null を返す。

Q4

配列・String・ArrayListそれぞれの「要素数(文字数)」の取得方法を書き分けてください。

答えを見る
解答

配列:arr.length(フィールド)。String:s.length()(メソッド)。ArrayListなどコレクション:list.size()

5.3

例外処理 — エラーと上手に付き合う

内容

例外とは「実行時に起きる異常事態」

文法が正しくても、実行してみると失敗する処理があります。0での割り算、配列の範囲外アクセス、nullのメソッド呼び出し、存在しないファイルの読み込み……。Javaではこれらの異常事態を例外(Exception)というオブジェクトで表し、try-catchで捕まえて対処できます。捕まえなければプログラムはそこで異常終了(クラッシュ)します。

src/ExceptionSample.java
public class ExceptionSample {
    public static void main(String[] args) {
        String input = "abc";   // 本来は数字が来るはずの入力

        try {
            // 例外が起きる可能性のある処理をtryで囲む
            int n = Integer.parseInt(input);   // "abc"は数値化できない!
            System.out.println("2倍は" + n * 2);
        } catch (NumberFormatException e) {
            // 例外が起きたらここへジャンプしてくる
            System.out.println("数値を入力してください:" + e.getMessage());
        } finally {
            // 例外の有無にかかわらず必ず実行される(後片付け用)
            System.out.println("処理を終了します");
        }

        System.out.println("プログラムは止まらず続行できる");
    }
}
Run: ExceptionSample
数値を入力してください:For input string: "abc"
処理を終了します
プログラムは止まらず続行できる

例外クラスの階層 — これも継承(4.2)でできている

例外はすべてクラスであり、継承ツリーを構成しています。大事な分類は「検査例外(catchかthrows宣言をコンパイラに強制される。例:IOException)」と「非検査例外(RuntimeException系。強制されない。多くはバグ由来)」の2つです。

Throwable Error 回復不能(メモリ枯渇等)扱わない Exception プログラムで対処する例外 RuntimeException(非検査) NullPointer / ArrayIndexOutOfBounds ... 検査例外 IOException など
5-2 例外クラスの階層。RuntimeException系はcatchを強制されない

自分で例外を投げる:throw と throws

メソッド側で異常を検知したら、throwで例外を投げて呼び出し元に知らせます。4.1のsetterで行った「黙って補正」よりも、「不正な引数だと明確に伝える」設計が実務では好まれます。

snippet
public void setHp(int hp) {
    if (hp < 0) {
        // 例外を生成して投げる。メッセージで理由を伝える
        throw new IllegalArgumentException("HPは0以上を指定してください: " + hp);
    }
    this.hp = hp;
}

// 検査例外を自分で処理せず、呼び出し元に委ねる場合は throws 宣言
public String readFile(String path) throws IOException {
    // ファイル読み込み処理...
}
!Java名物:NullPointerException(ぬるぽ)

参照型変数がnull(何も指していない)のままメソッドを呼ぶと発生する、Javaで最も有名な例外です。Mapのget結果(5.2)など「nullかもしれない値」を使う前には、if (x != null) のチェックを習慣にしましょう。スタックトレース(赤いエラーメッセージ)には発生したクラス名・行番号が出るので、恐れず読む練習をしてください。

やってみよう 5-3

int[] nums = {10, 20, 30}; に対してわざと nums[5] にアクセスして例外を発生させ、①スタックトレースの行番号を確認 → ②try-catchで ArrayIndexOutOfBoundsException を捕まえて「範囲外です」と表示するよう修正、の2段階を体験してください。

この節の理解度チェック
Q1

try / catch / finally の3つのブロックの役割をそれぞれ説明してください。

答えを見る
解答

try:例外が起きる可能性のある処理を囲む。catch:指定した例外が発生したときに実行される対処処理。finally:例外の有無にかかわらず必ず実行される(リソースの後片付けなど)。

Q2

次の処理で発生する例外の名前をそれぞれ答えてください。(a) String s = null; s.length(); (b) int[] a = new int[3]; a[3] = 1; (c) Integer.parseInt("abc");

答えを見る
解答

(a) NullPointerException (b) ArrayIndexOutOfBoundsException (c) NumberFormatException。いずれもRuntimeException系(非検査例外)。

Q3

「検査例外」と「非検査例外」の違いを、コンパイラの扱いの観点から説明してください。

答えを見る
解答

検査例外(IOExceptionなど)はtry-catchするかthrows宣言するかをコンパイラに強制される。非検査例外(RuntimeExceptionとその子孫)は強制されない(多くはnullチェック漏れなどのバグ由来のため、コードの修正で防ぐのが本筋)。

Q4

メソッド内で「引数が不正」と判明したとき、例外を発生させるキーワードと、典型的に使われる例外クラス名を答えてください。

答えを見る
解答

キーワードは throw(例:throw new IllegalArgumentException("理由");)。例外クラスは IllegalArgumentException が定番。

5.4

ラムダ式とStream API

内容

ラムダ式 — 「処理そのもの」を値のように渡す

ここまで、メソッドに渡せるのは値(intやStringなど)でした。ラムダ式を使うと「処理そのもの」を引数として渡せます。書き方は (引数) -> 処理。モダンなJavaコード(そしてSpringのコード)には必ず登場します。

src/LambdaSample.java
import java.util.List;

public class LambdaSample {
    public static void main(String[] args) {
        List<String> names = List.of("佐藤", "鈴木", "高橋");  // 変更不可の簡易List

        // forEach:各要素に対して「渡された処理」を実行する
        names.forEach(name -> System.out.println(name + "さん"));

        // ラムダの中身が複数行なら { } で囲む
        names.forEach(name -> {
            String msg = "こんにちは、" + name + "さん";
            System.out.println(msg);
        });
    }
}

Stream API — コレクション処理の流れ作業ライン

Stream APIは、コレクションの要素をベルトコンベアに流して加工するイメージの機能です。「絞り込む(filter)→ 変換する(map)→ 集める(collect)」をラムダ式で繋げて書きます。for文より「何をしたいか」が読み取りやすいのが利点です。

元のList 85, 42, 91, 58, 73 ① filter(絞り込み) n >= 60 合格点だけ通す → 85, 91, 73 ② map(変換) n + "点" 文字列に加工 → "85点","91点".. ③ toList(収集) 結果を新しい Listにまとめる 中間操作(filter / map …)はいくつでも連結できる。元のListは変化しない
5-3 Streamパイプライン。要素が流れ作業で絞り込み・変換されていく
src/StreamSample.java
import java.util.List;

public class StreamSample {
    public static void main(String[] args) {
        List<Integer> scores = List.of(85, 42, 91, 58, 73);

        // 60点以上を絞り込み、"◯点"に変換して、新しいListに集める
        List<String> passed = scores.stream()
                .filter(n -> n >= 60)       // 条件に合う要素だけ通す
                .map(n -> n + "点")          // 各要素を変換する
                .toList();                   // 結果をListにする

        System.out.println(passed);          // [85点, 91点, 73点]

        // 集計系:count / sum / average なども一撃
        long count = scores.stream().filter(n -> n >= 60).count();
        int sum = scores.stream().mapToInt(n -> n).sum();
        System.out.println("合格者:" + count + "人 / 合計:" + sum + "点");

        // 同じ処理をfor文で書くと…(比較してみよう)
        int total = 0;
        for (int n : scores) {
            total += n;
        }
        System.out.println("for文の合計:" + total);
    }
}
メソッド種類役割
filter(n -> 条件)中間操作条件がtrueの要素だけ残す
map(n -> 変換)中間操作各要素を別の値に変換する
sorted()中間操作並べ替える
toList()終端操作結果をListに集める
count() / sum()終端操作件数・合計を求める
forEach(n -> 処理)終端操作各要素に処理を実行する
PPOINT
  • Streamは「中間操作をいくつか繋ぎ、最後に終端操作で締める」構造。終端操作を呼ぶまで実行されない。
  • まずは filter / map / toList / forEach の4つが書ければ十分。
  • for文が悪いわけではない。単純な変換・絞り込みはStream、複雑な手続きはforと使い分ける。
やってみよう 5-4

List<String> names = List.of("sato", "suzuki", "takahashi", "ito"); から、①5文字以上の名前だけを、②すべて大文字に変換して、③Listに集めて出力してください(ヒント:s.length()s.toUpperCase())。同じ処理をfor文でも書いて、読みやすさを比較してみましょう。

この節の理解度チェック
Q1

ラムダ式 n -> n * 2 を日本語で読み下してください。

答えを見る
解答例

「nを受け取って、n * 2(の値)を返す処理」。矢印の左が引数、右が処理(結果)。

Q2

Streamの filtermap の役割の違いを説明してください。

答えを見る
解答

filter条件に合う要素だけを残す(絞り込み。要素数が減り得る)map各要素を別の値に変換する(要素数は変わらない)

Q3

次のコードの出力は何になりますか。

Quiz.java
List<Integer> nums = List.of(1, 2, 3, 4, 5, 6);
long ans = nums.stream()
        .filter(n -> n % 2 == 0)
        .count();
System.out.println(ans);
答えを見る
解答

3。偶数(2, 4, 6)だけがfilterを通過し、countで件数が数えられる。

Q4

【実技】List.of(120, 350, 80, 500, 260)(商品価格)から「200円以上の商品の合計金額」をStreamで計算して出力してください。

答えを見る
解答例
snippet
List<Integer> prices = List.of(120, 350, 80, 500, 260);
int total = prices.stream()
        .filter(p -> p >= 200)
        .mapToInt(p -> p)
        .sum();
System.out.println(total);   // 1110

第5章のまとめ

  • 文字列の内容比較は equals。大量連結は StringBuildersplit / substring / contains が頻出。
  • コレクションは List(順序)/ Map(キー→値)/ Set(重複なし)。宣言は List<String> list = new ArrayList<>(); が定石。
  • 例外は try-catch-finally で対処し、throw で自分から投げられる。NullPointerExceptionに注意。
  • ラムダ式 (n) -> 処理 で処理を渡せる。Streamは filter → map → toList の流れ作業。
CHAPTER 06

Webフレームワークへの橋渡し

この章のゴール:Spring Bootなどのフレームワーク学習を始めるために必要な「実務Javaの常識」——パッケージ、Maven、アノテーション——を押さえ、次のステップへの地図を手に入れる。

6.1

パッケージとアクセス修飾子

内容

パッケージ = クラスを整理するフォルダ

実務のプロジェクトはクラスが数百個になります。それらを整理する仕組みがパッケージで、実体はフォルダ構造そのものです。名前は世界で衝突しないよう、ドメイン名を逆順にした形(例:com.example.shop)が慣習です。

src/com/example/shop/Item.java
package com.example.shop;   // 先頭で所属パッケージを宣言(フォルダと一致させる)

public class Item {
    public String name;
}
src/com/example/app/Main.java
package com.example.app;

import com.example.shop.Item;   // 別パッケージのクラスはimportして使う
import java.util.ArrayList;     // 標準ライブラリも同じ仕組み(5章で使ったもの)

public class Main {
    public static void main(String[] args) {
        Item item = new Item();
        item.name = "Java入門書";
        System.out.println(item.name);
    }
}
PPOINT:importはIntelliJにやらせる

未importのクラス名を書くと赤くなりますが、その場で Alt+Enter(Mac:Option+Enter) を押せばIntelliJが自動でimport文を追加します。java.lang パッケージ(String、Systemなど)だけはimport不要です。

アクセス修飾子の全4種類

4.1では public / private の2つを使いました。パッケージを学んだ今、全4種類を整理できます。

修飾子アクセスできる範囲使いどころ
publicどこからでも公開API・窓口メソッド
protected同一パッケージ+子クラス子クラスにだけ見せたい部品
(修飾子なし)同一パッケージ内のみパッケージ内部の部品(デフォルト)
private同一クラス内のみフィールド全般・内部処理

迷ったら「まずprivate、必要になったら広げる」が原則です。公開範囲は狭いほど安全です。

やってみよう 6-1

IntelliJでsrcフォルダを右クリック → New → Package で com.example.game を作り、これまでのHeroクラスをドラッグで移動してみてください(IntelliJがpackage文を自動修正してくれます)。Mainからimportして動くことを確認しましょう。

この節の理解度チェック
Q1

パッケージの役割と、パッケージ名の一般的な命名慣習を答えてください。

答えを見る
解答

多数のクラスをフォルダ構造で整理し、名前の衝突を防ぐ仕組み。命名はドメイン名の逆順(例:example.com → com.example.プロジェクト名)が慣習。

Q2

別パッケージのクラスを使うために、ファイルの先頭付近に書くべき文は何ですか。また、import不要で使えるパッケージはどれですか。

答えを見る
解答

import パッケージ名.クラス名;java.lang パッケージ(String、System、Integerなど)だけはimportなしで使える。

Q3

アクセス修飾子を「公開範囲の広い順」に4つ並べてください。

答えを見る
解答

publicprotected → (修飾子なし/パッケージプライベート) → private

6.2

Mavenとライブラリ管理

内容

ライブラリと「ビルドツール」

実務では、他人が作った便利なクラス群=ライブラリを組み合わせて開発します。しかしライブラリを手作業でダウンロードして管理するのは大変です(AがBに依存し、BがCに依存し……)。そこで使うのがビルドツールで、JavaではMaven(メイヴン)とGradleが二大勢力です。役割は主に2つ:

  1. 依存関係の管理:設定ファイルに「使いたいライブラリ名」を書くだけで、インターネット上のリポジトリから自動ダウンロードしてくれる。
  2. ビルドの自動化:コンパイル → テスト → パッケージング(jarファイル化)を一連のコマンドで実行してくれる。

pom.xml — Mavenプロジェクトの心臓部

Mavenプロジェクトのルートには pom.xml という設定ファイルが置かれ、<dependencies> に使いたいライブラリを列挙します。ライブラリは「groupId(作者・組織)/ artifactId(ライブラリ名)/ version」の3点セットで特定します。

pom.xml(抜粋)
<project>
    <groupId>com.example</groupId>
    <artifactId>my-app</artifactId>
    <version>1.0.0</version>

    <dependencies>
        <!-- JSON変換ライブラリJacksonを使いたい、と宣言するだけ -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.17.0</version>
        </dependency>
    </dependencies>
</project>
PPOINT:IntelliJでのMavenの使い方
  • 新規プロジェクト作成時に Build system: Maven を選ぶと、pom.xml付きのプロジェクトが生成される。
  • pom.xmlを編集したら、エディタ右上に出るMavenの更新アイコン(🔄)をクリック(または右側のMavenツールウィンドウ → Reload)。ライブラリが自動ダウンロードされ、importできるようになる。
  • Spring Bootのプロジェクトも中身は「依存関係がたくさん書かれたMaven(またはGradle)プロジェクト」にすぎない。pom.xmlが読めれば怖くない。
やってみよう 6-2

IntelliJで Build system: Maven を選んで新規プロジェクトを作り、生成されたpom.xmlを開いて groupId / artifactId / version がどこに書かれているか確認してください。余裕があれば上記のJackson依存を追記 → Reloadし、External Libraries にjacksonが増えることを観察しましょう。

この節の理解度チェック
Q1

Mavenのようなビルドツールの主な役割を2つ答えてください。

答えを見る
解答

依存関係(ライブラリ)の管理:設定に書くだけで自動ダウンロード。②ビルドの自動化:コンパイル・テスト・パッケージングを自動実行。

Q2

Mavenでライブラリを1つ特定するための3つの情報(3点セット)を答えてください。

答えを見る
解答

groupId(作者・組織)、artifactId(ライブラリ名)、version(バージョン)。

Q3

使いたいライブラリは、pom.xmlのどのタグの中に追記しますか。また追記後にIntelliJで行うべき操作は何ですか。

答えを見る
解答

<dependencies> タグの中に <dependency> として追記する。追記後はMavenのReload(更新アイコン)を実行してライブラリをダウンロードさせる。

6.3

アノテーション — Springを読み解く鍵

内容

@マークの正体

4.2で登場した @Override のような @〜アノテーション(注釈)と呼びます。それ自体は処理を持たず、クラスやメソッドに「目印(メタ情報)」を貼り付ける仕組みです。目印は、コンパイラやフレームワークが読み取って意味を持ちます

アノテーション誰が読む?意味
@Overrideコンパイラ親メソッドの上書き宣言。ミスを検出させる
@Deprecatedコンパイラ・IDE「非推奨。使わないで」の目印(取り消し線で表示される)
@RestControllerSpring「このクラスはWebリクエストの受付係」と登録する
@GetMapping("/hello")Spring「このURLへのアクセスはこのメソッドで処理」と対応付ける
@AutowiredSpring「必要なインスタンスをSpringが自動で注入する」目印

Spring Bootのコードを"読んで"みる

まだ書けなくて構いません。この教材を終えた皆さんなら、実際のSpring Bootコードの構造が読めるはずです。

HelloController.java(Spring Bootの実例)
package com.example.demo;                          // 6.1 パッケージ

import org.springframework.web.bind.annotation.GetMapping;   // 6.2 Mavenで入れた
import org.springframework.web.bind.annotation.RestController; // ライブラリのimport

@RestController                    // 6.3 「Web受付係」の目印
public class HelloController {     // 3.2 ただのpublicクラス

    @GetMapping("/hello")          // 「/hello へのアクセスはここ」の目印
    public String hello() {        // 3.1 Stringを返すただのメソッド
        return "Hello, Spring Boot!";
    }
}

どうでしょう。1行残らず、この教材で学んだ要素の組み合わせです。フレームワークとは「アノテーションを目印に、皆さんのクラスをポリモーフィズム(4.3)の仕組みで呼び出してくれる巨大なライブラリ」なのです。

PPOINT

アノテーションを見たら「誰が(コンパイラ?フレームワーク?)この目印を読んで、何をしてくれるのか」と考えるクセをつけましょう。Spring学習の質問の9割は、実はこの問いに帰着します。

この節の理解度チェック
Q1

アノテーションとは何ですか。「処理」「目印」という言葉を使って説明してください。

答えを見る
解答例

クラスやメソッドに付ける目印(メタ情報)。それ自体は処理を持たず、コンパイラやフレームワークが目印を読み取って特別な扱いをしてくれる。

Q2

この教材で学んだ、コンパイラが読み取るアノテーションを1つ挙げ、その効果を説明してください。

答えを見る
解答

@Override。オーバーライドの意図を宣言し、シグネチャの不一致(スペルミスなど)があればコンパイルエラーとして検出してくれる。

Q3

上のHelloControllerの例で、ブラウザから /hello にアクセスしたときに hello() メソッドが実行されるのは、どのアノテーションの働きによるものですか。

答えを見る
解答

@GetMapping("/hello")。SpringがこのURLとメソッドの対応付けを読み取り、リクエストが来たらメソッドを呼び出してくれる(クラスを受付係として登録するのは @RestController)。

6.4

総合演習と次のステップ

内容

Webアプリの全体像を知っておく

最後に、これから学ぶSpring Bootアプリの典型的な構造を見ておきましょう。Webアプリは層(レイヤー)に分かれ、それぞれの層が「クラス+アノテーション+インターフェース」で作られます。

🌐 ブラウザ HTTP Spring Boot アプリケーション Controller リクエストの受付係 @RestController Service 業務ロジック担当 @Service Repository データの出し入れ係 @Repository DB SQL
6-1 Spring Bootの典型構造。層の間はインターフェース(4.4)で繋がれる

総合演習:ミニ商品管理プログラム

本書の総仕上げです。Webの手前までを全部使って、コンソール版の商品管理を作ってみましょう。以下の仕様を、まず自力で。詰まったら各章に戻って構いません。

総合演習(30〜60分)
  1. com.example.shop パッケージを作る(6.1)
  2. Item クラス:privateフィールド name(String)と price(int)、コンストラクタ、getter(4.1)。priceが負ならコンストラクタで IllegalArgumentException を投げる(5.3)
  3. ItemRepository クラス:内部に List<Item> を持ち、add(Item)findAll() を提供(5.2)
  4. Main:商品を4〜5個登録し、(a)全商品を「名前:価格円」形式で一覧表示(拡張for)、(b)Streamで「300円以上の商品名の一覧」と「全商品の合計金額」を出力(5.4)
  5. 発展:Discountable インターフェース(int discountedPrice())を定義してItemに実装し、2割引価格も表示(4.4)
総合演習の解答例を見る
解答例
com/example/shop/Item.java
package com.example.shop;

public class Item implements Discountable {
    private String name;
    private int price;

    public Item(String name, int price) {
        if (price < 0) {
            throw new IllegalArgumentException("価格は0以上: " + price);
        }
        this.name = name;
        this.price = price;
    }

    public String getName() { return name; }
    public int getPrice() { return price; }

    @Override
    public int discountedPrice() {
        return (int) (price * 0.8);
    }
}
com/example/shop/Discountable.java
package com.example.shop;

public interface Discountable {
    int discountedPrice();
}
com/example/shop/ItemRepository.java
package com.example.shop;

import java.util.ArrayList;
import java.util.List;

public class ItemRepository {
    private List<Item> items = new ArrayList<>();

    public void add(Item item) {
        items.add(item);
    }

    public List<Item> findAll() {
        return items;
    }
}
com/example/app/Main.java
package com.example.app;

import com.example.shop.Item;
import com.example.shop.ItemRepository;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        ItemRepository repo = new ItemRepository();
        repo.add(new Item("ノート", 150));
        repo.add(new Item("ペン", 120));
        repo.add(new Item("Java入門書", 2800));
        repo.add(new Item("マウス", 1500));

        // (a) 一覧表示
        for (Item item : repo.findAll()) {
            System.out.println(item.getName() + ":" + item.getPrice()
                    + "円(2割引:" + item.discountedPrice() + "円)");
        }

        // (b) Streamで集計
        List<String> expensive = repo.findAll().stream()
                .filter(i -> i.getPrice() >= 300)
                .map(i -> i.getName())
                .toList();
        int total = repo.findAll().stream()
                .mapToInt(i -> i.getPrice())
                .sum();
        System.out.println("300円以上:" + expensive);
        System.out.println("合計:" + total + "円");
    }
}

次のステップへの地図

順番学ぶことひとこと
1SQLとデータベースの基礎Webアプリ=DBの読み書き。SELECT/INSERT/UPDATE/DELETEだけでも先に触れておくと楽
2HTTPの基礎リクエスト/レスポンス、GET/POST、ステータスコード(200, 404, 500)を知る
3Spring Boot入門Spring Initializr(start.spring.io)でプロジェクトを作り、6.3のHelloControllerを実際に動かすところから
4Spring + DB(Spring Data JPA / MyBatis)Repositoryを本物のDBに接続。ここまで来ればミニWebアプリが作れる
P最後のPOINT

フレームワーク学習で詰まったら、それは大抵「フレームワークが難しい」のではなく「土台のJava文法のどれかが曖昧」なだけです。そのときは、いつでもこの教材の該当章に戻ってきてください。

この節の理解度チェック(修了確認)
Q1

Webアプリの典型的な3つの層(Controller / Service / Repository)の役割をそれぞれ一言で答えてください。

答えを見る
解答

Controller:ブラウザからのリクエストの受付係。Service:業務ロジック(計算・判断)の担当。Repository:データベースへのデータの出し入れ係。

Q2

6.3のHelloControllerのコードに登場した文法・概念を、この教材の章番号とともに3つ以上挙げてください。

答えを見る
解答例

package宣言とimport(6.1)、Mavenで導入したライブラリの利用(6.2)、アノテーション(6.3)、publicクラスの定義(3.2)、戻り値Stringのメソッド(3.1)——など。

Q3

【修了問題】総合演習のプログラムに「商品名で検索して価格を返す機能」を追加するとしたら、どのクラスにどんなメソッドを追加しますか。シグネチャ(戻り値・メソッド名・引数)を設計してください。見つからない場合の挙動も考えてみましょう。

答えを見る
解答例

ItemRepositoryに public Item findByName(String name) を追加する(データの検索はRepositoryの責務)。実装はforやStreamの filter で探す。見つからない場合は「nullを返す」「例外を投げる」などの選択肢があり、それを決めること自体が設計。この「見つからないかもしれない値」を安全に扱う Optional という仕組みが次の学習テーマとしておすすめ。

第6章のまとめ / 修了おめでとうございます!

  • パッケージでクラスを整理し、importで利用。アクセス修飾子は「まずprivate」。
  • Mavenのpom.xmlに依存を書けばライブラリは自動で入る。Spring Bootプロジェクトの正体もMaven/Gradle。
  • アノテーションは目印。フレームワークはそれを読んで皆さんのクラスを呼び出す。
  • Spring Bootのコードは、本書の知識の組み合わせでもう読める。次はSQL・HTTP・Spring Boot入門へ!

ゼロから始めるJava — IntelliJで手を動かして学ぶ基礎テキスト

全6章・24節|各節「①内容 → ②理解度チェック」構成|次のステップ:SQL → HTTP → Spring Boot