2026年2月1日日曜日

【Java】Maven×H2で作る「消える」データベース環境:macOS Sequoiaでの検証ガイド

 Java開発において、データベース(DB)操作の習得は必須です。今回はMavenを使用してプロジェクトを構築し、アプリ終了と共にデータが消えるH2インメモリデータベースを、Javaプログラムから制御する手順を解説します。




1. 開発環境(Mac)


今回の検証は以下の最新環境で実施しました。Macユーザーの方は、ご自身の環境と照らし合わせてみてください。

  • OS: macOS Sequoia 15.6.1

  • Java: javac 25.0.1

  • Maven: Apache Maven 3.9.12

  • Shell: zsh



2. Mavenプロジェクトの作成


まずはターミナルを開き、プロジェクトの雛形を作成します。

Bash
mvn archetype:generate \
  -DgroupId=com.example \
  -DartifactId=h2-demo \
  -DarchetypeArtifactId=maven-archetype-quickstart \
  -DinteractiveMode=false

作成された h2-demo ディレクトリに移動します。

Bash
cd h2-demo


3. pom.xml の編集


H2データベースを使用するために、ライブラリの依存関係を追加します。pom.xml を開き、<dependencies> セクションに以下を追記してください。

XML
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <version>2.2.224</version>
</dependency>


4. Javaプログラムの実装:インメモリDBの制御


src/main/java/com/example/App.java を以下の内容に書き換えます。

Java
package com.example;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
import java.sql.ResultSet;

public class App {
    public static void main(String[] args) {
        // メモリ上に「db_demo」を作成(接続した瞬間に生成される)
        String url = "jdbc:h2:mem:db_demo"; 
        String user = "sa";
        String password = "";

        try (Connection conn = DriverManager.getConnection(url, user, password);
             Statement stmt = conn.createStatement()) {
            
            System.out.println("--- DB接続完了 (macOS Environment) ---");

            // 1. テーブル作成
            stmt.execute("CREATE TABLE EMPLOYEES (ID INT PRIMARY KEY, NAME VARCHAR(255))");
            System.out.println("Table 'EMPLOYEES' を作成しました。");

            // 2. データの挿入
            stmt.execute("INSERT INTO EMPLOYEES VALUES(101, '田中 太郎')");
            stmt.execute("INSERT INTO EMPLOYEES VALUES(102, '鈴木 次郎')");

            // 3. データの抽出 (SELECT)
            ResultSet rs = stmt.executeQuery("SELECT * FROM EMPLOYEES");
            while (rs.next()) {
                System.out.println("社員番号: " + rs.getInt("ID") + ", 氏名: " + rs.getString("NAME"));
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
        
        System.out.println("--- アプリを終了します。メモリ上のデータは破棄されました ---");
    }
}


5. Macでの実行と結果確認


コマンドの実行場所

これら全てのコマンドは、ステップ1で作成したプロジェクトのルートディレクトリ(h2-demo)の直下で実行してください。

Bash
# 現在の場所を確認
pwd

# コンパイルと実行
mvn compile
mvn exec:java -Dexec.mainClass="com.example.App"

実行結果の確認

ターミナルに次が出力されれば、正常に動作しています。

Plaintext
--- DB接続完了 (macOS Environment) ---
Table 'EMPLOYEES' を作成しました。
社員番号: 101, 氏名: 田中 太郎
社員番号: 102, 氏名: 鈴木 次郎
--- アプリを終了します。メモリ上のデータは破棄されました ---


6. 【検証メモ】なぜ「消える」のか?


  1. インメモリ型の仕組み: H2の接続URLに jdbc:h2:mem: と記述すると、データはディスクではなく**メモリ(RAM)**上に置かれます。

  2. 実データの不在: 実行後、Finderでフォルダ内を確認しましたが、.db ファイル等は一切作成されていませんでした。

  3. メリット: 「実験のたびに環境を汚さない」「爆速で動作する」という、Macをクリーンに保ちたい開発者にとって理想的な学習環境です。




まとめ


  • Mavenを使えば、ライブラリ管理は pom.xml 一枚で完結。

  • H2インメモリモードは、環境構築不要でSQLの練習に最適。

  • 最新の macOS Sequoia & Java 25 環境でもスムーズに動作。


この記事が、Javaでのデータベース学習の第一歩になれば幸いです。




2026年1月31日土曜日

【Java】シャローコピー vs ディープコピー:参照の罠からコードを守る

 Javaでオブジェクトをコピーする際、単に = で代入したり、標準の clone() を使ったりすると、思わぬバグを引き起こすことがあります。その原因である「シャローコピー(浅いコピー)」と、解決策としての「ディープコピー(深いコピー)」について解説します。



1. シャローコピー(Shallow Copy / 浅いコピー)


仕組み


シャローコピーは、オブジェクトの**「参照(メモリ上の住所)」だけ**をコピーします。

コピー元とコピー先は、メモリ上の同じ実体(インスタンス)を指している状態です。

Javaでの実装例


Java
class Person {
    String name;
    Person(String name) { this.name = name; }
}

public class Main {
    public static void main(String[] args) {
        Person alice = new Person("Alice");
        
        // シャローコピー(参照のコピー)
        Person copyAlice = alice; 

        copyAlice.name = "Bob"; // コピー側を書き換えると...

        System.out.println(alice.name);     // Bob(元の方も変わってしまう!)
    }
}

なぜ問題か?:意図せずオリジナルデータが破壊されるため、バグの特定が非常に困難になります。



2. ディープコピー(Deep Copy / 深いコピー)


仕組み


ディープコピーは、メモリ上に実体そのものを新しく作成し、値をコピーします。

コピー元とコピー先は完全に別の住所を持つため、互いに影響を与えません。

Javaでの推奨実装:コピーコンストラクタ


Java
class Person {
    String name;
    Person(String name) { this.name = name; }

    // コピーコンストラクタ(自分と同じ型を受け取って新しく作る)
    Person(Person other) {
        this.name = other.name; 
    }
}


3. 【深掘り】clone()メソッドは使わないのが正解?


Javaには標準で Object.clone() というメソッドが存在しますが、現在では**「使用を避けるべき」**というのが一般的なエンジニアの共通認識です。

なぜ clone() は避けられるのか?


  1. Cloneableインターフェースの設計不備: インターフェースなのにメソッドが定義されておらず、振る舞いが特殊。

  2. 例外処理の強制: 常に CloneNotSupportedException をキャッチする必要があり、コードが汚れる。

  3. シャローコピーのリスク: デフォルトの super.clone() はシャローコピーを行うため、結局自分で深いコピーの実装を書く手間が変わらない。


結論: 新しくコードを書くなら、コピーコンストラクタか、ファクトリメソッドを作成するのがJavaのベストプラクティスです。



4. 不変(Immutable)オブジェクトならコピーは不要


「コピーしなければならない状況」を物理的に無くすのが、最も賢い設計です。それが不変(Immutable)オブジェクトです。


仕組み


一度作成したら中身を変更できないクラスにすれば、シャローコピーをしても副作用が起きません。

  • Java標準の例: StringInteger など。

  • 自作の例: フィールドを final にし、セッター(setter)を作らない。

Java
public final class ImmutableUser {
    private final String name; // finalで書き換え禁止
    public ImmutableUser(String name) { this.name = name; }
    public String getName() { return name; }
}


メリット:

  • どこに渡しても中身が変わる心配がないため、コピーを作る必要自体がなくなります。

  • メモリ消費を抑えられ、スレッドセーフ(マルチスレッドでも安全)になります。



まとめ:使い分けの指針


状況採用すべき手法
基本の代入シャローコピー(ただし変更しないことが前提)
元データを保護して編集したいディープコピー(コピーコンストラクタ推奨)
クラスを自作する場合可能な限り「不変(Immutable)」にする

clone() の複雑さに悩まされるより、「不変にする」あるいは「新しく作る(コピーコンストラクタ)」というシンプルな戦略をとることが、堅牢なJavaプログラムへの近道です。



2026年1月29日木曜日

【Java】Mavenで外部ライブラリ(Log4j2)を導入し、ログ出力させるまでの全手順

 Java開発の標準ツール「Maven」を使い、外部ライブラリ(今回はLog4j2)をプロジェクトに組み込んで実際に動作させるまでの流れを解説します。

単純にライブラリを追加するだけでなく、実際にログが表示されるまでの「設定のポイント」についても触れます。


1. 開発環境(Mac)


今回の検証環境です。

  • OS: macOS Sequoia 15.6.1

  • Java: javac 25.0.1

  • Maven: Apache Maven 3.9.12

    Bash
    % mvn -version
    Apache Maven 3.9.12 (848fbb4bf2d427b72bdb2471c22fced7ebd9a7a1)
    


2. Mavenプロジェクトの作成


ターミナルから、プロジェクトの雛形を生成します。

Bash
mvn archetype:generate -DgroupId=com.example -DartifactId=my-app -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false


3. 外部ライブラリの導入(pom.xml)


Mavenで外部ライブラリを使うには、pom.xml<dependencies> セクションに定義を追記します。今回はログ出力ライブラリ「Log4j2」を導入します。

XML
<dependencies>
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-api</artifactId>
        <version>2.20.0</version>
    </dependency>
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-core</artifactId>
        <version>2.20.0</version>
    </dependency>
</dependencies>


4. プログラムの実装


src/main/java/com/example/App.java に、導入したライブラリを使ってログを出力するコードを記述します。

Java
package com.example;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class App {
    private static final Logger logger = LogManager.getLogger(App.class);

    public static void main(String[] args) {
        logger.info("Hello, Maven! Log4jでログを出力しました。");
    }
}


5. ログを表示させるための重要設定(XML)


外部ライブラリを導入しただけでは、ログレベルの設定によりメッセージが表示されないことがあります。 src/main/resources/log4j2.xml を作成し、出力設定を記述します。

XML
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
        </Console>
    </Appenders>
    <Loggers>
        <Root level="info">
            <AppenderRef ref="Console"/>
        </Root>
    </Loggers>
</Configuration>


6. 実行と結果確認


Mavenコマンドでコンパイルと実行を行います。clean を挟むことで、設定ファイルの反映を確実に行います。

Bash
mvn clean compile exec:java -Dexec.mainClass="com.example.App"

実行ログ

無事に外部ライブラリを経由したログが出力されました。

Plaintext
[INFO] 
[INFO] --- exec:3.6.3:java (default-cli) @ my-app ---
20:24:06.080 [com.example.App.main()] INFO  com.example.App - Hello, Maven! Log4jでログを出力しました。
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------


まとめ


Mavenを使えば、pom.xml に定義を書くだけで簡単に外部ライブラリを導入できます。 ただし、今回のようなログライブラリの場合、「ライブラリの導入」と「設定ファイル(XML)の配置」はセットであるという点に注意が必要です。





2026年1月28日水曜日

【Java】「Utilクラス」の肥大化を防ぐ。デフォルトメソッドで実現するスマートな共通処理

 Java 8で導入された「デフォルトメソッド(default)」。

これは単なる新機能ではなく、私たちが長年悩まされてきた**「共通処理をどこに書くべきか」**という問題に対する、Javaからのスマートな回答です。

今回は、「属性(状態)を持たない共通処理」をインターフェースに集約するメリットを、管理やテストの効率化という視点で解説します。



1. なぜ「〇〇Util」への丸投げが問題になるのか?


多くのプロジェクトでは、StringUtilDateUtil といった共通クラスを作ります。しかし、これにはいくつかの課題があります。

  1. 「モノ」と「振る舞い」が離れ離れになる

    本来、そのデータ自身ができるはずの操作(例:自分のデータをCSV形式にする)が、遠く離れたUtilクラスに書かれるため、コードの全体像が把握しづらくなります。

  2. ユーティリティクラスの肥大化

    「どこに書くべきか迷った便利メソッド」がすべて特定のUtilクラスに集まり、数千行の巨大なファイルになりがちです。

  3. テストの柔軟性が下がる(staticメソッドの壁)

    Utilクラスのメソッドは通常 static です。static メソッドはテスト時に中身を差し替える(モック化する)のが難しく、テストがそのUtilの実装に強く依存してしまいます。




2. デフォルトメソッドによる「共通機能のトッピング」


デフォルトメソッドを使えば、共通処理を関連する**「インターフェース」**の中に戻すことができます。


具体例:CSV出力機能の実装


Java
public interface CsvExportable {
    // 実装クラスには「CSVにしたいデータ配列」を用意させる
    String[] getCsvData();

    // 共通の振る舞い:属性(フィールド)が不要な加工処理をここに書く
    default String toCsvRow() {
        return String.join(",", getCsvData());
    }
}


3. 設計のコツ:デフォルトメソッドとUtilクラスの使い分け


共通処理の置き場所に迷ったら、**「その処理は、特定の型が持つべき『振る舞い』か? それとも、どこからでも使える『汎用的な道具』か?」**という視点で整理しましょう。


① デフォルトメソッド:「特定の型に閉じられた振る舞い」


その処理が、特定のインターフェースが表す役割(ドメイン)にのみ関係する**「振る舞い(Behavior)」**である場合は、デフォルトメソッドを使います。

  • 設計意図: 「『CSV出力可能(CsvExportable)』という型である以上、当然持っているべき共通の操作」を定義する。

  • : CSVの行生成、データのバリデーションロジックなど。

  • メリット: ロジックがインターフェース内に隠蔽(カプセル化)されるため、関係のないクラスから不要なメソッドが見えず、設計がクリーンになります。


② Utilクラス:「型に依存しない横断的な道具」


その処理が特定の役割(型)には依存せず、システム全体で共通して利用される**「横断的な道具」**である場合は、Utilクラスが適しています。

  • 設計意図: 「特定のオブジェクトの状態や役割に関わらず、静的に処理を完結させたい汎用ツール」として定義する。

  • : ログ出力(LogUtil)、日付の変換(DateUtil)、数学的な計算処理。

  • メリット: システムのどこからでも Util.method() と呼ぶだけで即座に利用でき、インフラ層のコードとして再利用性が高まります。

項目デフォルトメソッドUtilクラス
設計概念振る舞い(Behavior)ユーティリティ(Tool)
対象特定の型にのみ関係するシステム全体(型に依存しない)
テストメソッドのオーバーライドにより差し替え可能結合度が強く、差し替えが困難(static)


4. まとめ

  • Utilクラス(static)への過度な依存は、テストの柔軟性を奪い、保守性を下げる原因になる。

  • 特定の型(役割)にのみ関係する共通の振る舞いは、デフォルトメソッドとして実装する。

  • システム全体で使う横断的な処理は、今まで通りUtilクラスとして実装する。


「とりあえずUtilクラス」を卒業して、「これはどの責務に属する振る舞いか?」を意識する。それだけで、あなたのコードはもっとテストしやすく、オブジェクト指向らしい洗練されたものになるはずです。




【Javaで学ぶアルゴリズム】ユークリッドの互除法で最大公約数を求め

前回の記事では Arrays.sort() を使った並び替えを学びましたが、今回は一歩進んで、有名な数学的アルゴリズムを自分の手で実装してみましょう。

取り上げるテーマは、紀元前から伝わる世界最古のアルゴリズムの一つ「ユークリッドの互除法」です。


1. ユークリッドの互除法とは?


2つの整数の最大公約数(GCD: Greatest Common Divisor)を効率よく見つけるための手法です。


アルゴリズムの定理


2つの整数 X, Y (X ≧ Y) について、


X を Yで割った余りを Rとすると、XY の最大公約数は、YRの最大公約数に等しい」

 

この性質を利用し、余り R が 0$になるまで計算を繰り返すことで、どんなに大きな数字同士でも最大公約数を導き出すことができます。




2. Javaでの実装例(自作ロジック)


まずは、アルゴリズムの仕組みを理解するために、while 文を使って自作してみましょう。

Java
public class GCDSample {
    public static void main(String[] args) {
        int x = 1071;
        int y = 1029;
        
        int result = getGcd(x, y);
        System.out.println(x + " と " + y + " の最大公約数は " + result + " です。");
    }

    // 最大公約数を求めるメソッド
    public static int getGcd(int x, int y) {
        // R = 0 となるまで、YとRを変えながらループ
        while (y != 0) {
            int r = x % y; // 剰余Rを求める
            x = y;         // 次の計算のためにYをXに代入
            y = r;         // 次の計算のためにRをYに代入
        }
        return x; // 余りが0になった時の除数が最大公約数
    }
}


3. アルゴリズムの挙動を追う


例として、X = 1071, Y = 1029 の場合をシミュレーションしてみます。

  1. 1071 ÷ 1029 = 1 余り 42

  2. 1029 ÷ 42 = 24 余り 21

  3. 42 ÷ 21 = 2 余り 0

  4. 余りが 0 になったので、その時の除数 21が最大公約数!

大きな数字でも、驚くほど少ない回数で答えに辿り着けます。




4. 【必見】Java標準ライブラリで一発解決!


ここまでロジックを解説してきましたが、実はJavaの標準ライブラリ java.math.BigInteger を使うと、自作する必要すらありません。

Java
import java.math.BigInteger;

public class GCDMain {
    public static void main(String[] args) {
        // valueOfを使用してBigIntegerインスタンスを作成
        BigInteger b1 = BigInteger.valueOf(1071);
        BigInteger b2 = BigInteger.valueOf(1029);
        
        // gcdメソッドを呼び出すだけ
        BigInteger gcd = b1.gcd(b2);
        
        System.out.println("最大公約数は: " + gcd); // 実行結果: 21
    }
}

なぜこれを知っておくべきか


  • 正確性: long 型を超えるような巨大な数値でも計算可能です。

  • 堅牢性: 内部で最適化されたアルゴリズムが採用されており、信頼性が高いです。

  • 実務の知恵: ロジックを理解した上で、実務では「車輪の再発明」を避け、標準機能を賢く使うのがプロの技術です。



5. まとめ


  • 基本: 「X ÷ Yの余り」を次の除数にするループを回す。

  • 応用: 最大公約数(GCD)がわかれば、最小公倍数(LCM)は (X × Y) ÷ GCD$ で求まる。

  • Javaの凄さ: BigInteger.gcd() を使えば一瞬で実装完了。


アルゴリズムの仕組みを知ることは、プログラミングの「思考力」を鍛えてくれます。一方で、便利な標準機能を知ることは「生産性」を高めてくれます。

両方の能力を鍛えたいものです。






【Java】Maven×H2で作る「消える」データベース環境:macOS Sequoiaでの検証ガイド

 Java開発において、データベース(DB)操作の習得は必須です。今回は Maven を使用してプロジェクトを構築し、アプリ終了と共にデータが消える H2インメモリデータベース を、Javaプログラムから制御する手順を解説します。 1. 開発環境(Mac) 今回の検証は以下の最新...