読み込み中...

Javaのメンバ変数を完全マスター!初心者から上級者までの10選詳細ガイド

Javaのメンバ変数の詳しいガイドのイメージ画像 Java
この記事は約33分で読めます。

【サイト内のコードはご自由に個人利用・商用利用いただけます】

この記事では、プログラムの基礎知識を前提に話を進めています。

説明のためのコードや、サンプルコードもありますので、もちろん初心者でも理解できるように表現してあります。

本記事のサンプルコードを活用して機能追加、目的を達成できるように作ってありますので、是非ご活用ください。

※この記事は、一般的にプロフェッショナルの指標とされる『実務経験10,000時間以上』を満たす現役のプログラマチームによって監修されています。

※Japanシーモアは、常に解説内容のわかりやすさや記事の品質に注力しております。不具合、分かりにくい説明や不適切な表現、動かないコードなど気になることがございましたら、記事の品質向上の為にお問い合わせフォームにてご共有いただけますと幸いです。
(送信された情報は、プライバシーポリシーのもと、厳正に取扱い、処分させていただきます。)

はじめに

Javaのメンバ変数は、クラスから作られるオブジェクトの状態を保持する変数です。名前、年齢、残高、設定値など、オブジェクトごとに変わる値を扱います。

初心者がつまずきやすいのは、staticを付ける変数と付けない変数、privateで隠す変数と外部へ公開する値の区別です。Javaのメンバ変数の使い方は、宣言だけでなく、アクセス範囲、初期化、継承、カスタマイズまで一体で見ると整理できます。

Javaの仕様では、クラス内のフィールドがオブジェクトやクラスの状態を表するのが基本です。詳細はJava Language Specification 8.3 Fields、標準APIはJava SE 21 API Documentationで確認できます。

Javaの変数設計は、Java List型の扱いJavaアノテーションの基礎Javaのオーバーライドも合わせると、クラス設計の見通しがよくなります。

動作確認環境
  • Java SE 21 / JDK 21
  • 標準ライブラリのみ使用
  • コンソール出力は System.out.println を想定
📖 この記事で学べること
  • Javaのメンバ変数の意味、宣言位置、初期値の考え方
  • publicprivateprotected、指定なしの使い分け
  • サンプルコードを使った初期化、継承、staticfinalの確認
  • 初心者が避けたい注意点と、保守しやすい対処法
  • ゲッター、セッター、抽象クラスを使ったカスタマイズ

Javaのメンバ変数とは

Javaのメンバ変数は、classの本体に宣言する変数です。メソッド内のlocal variableと異なり、オブジェクトの状態として保持され、同じインスタンスの複数メソッドから参照できます。

最小形は、クラスの波かっこ内にint age;String name;を置く書き方です。インスタンスごとの値なら通常のフィールド、クラス全体で共有する値ならstaticを付けたクラス変数にします。

public class User {
    private String name;

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

結果: 期待される動きとして、Userオブジェクトごとに異なるnameを保持できるのが目安です。

Javaのメンバ変数を理解すると、クラスが何を覚えているかを読めます。初心者向けの徹底解説として、宣言位置、アクセス修飾子、初期化タイミングを押さえます。

Javaではメンバ変数をfieldとも呼びますし、ここがポイントです。Oracleの仕様文書でもフィールドという表現が使われるため、メンバ変数とフィールドは近い意味で扱うと読みやすくなります。

メンバ変数の基本的な定義と特徴

メンバ変数は、publicprotectedprivate、修飾子なしで公開範囲を決めます。範囲が広すぎると外部から直接書き換えられ、状態の整合性が崩れる注意点があるのがポイントです。

Javaのメンバ変数に関する用語と判断基準を早見表にまとめます。後続のサンプルコードでは、各行の意味をクラス定義で確認します。

項目主な書き方意味注意点使い方の目安
インスタンス変数private int age;オブジェクトごとに値を持つ外部公開を絞る状態を保持する
クラス変数static int count;クラスで値を共有する共有変更に注意する件数や定数候補を扱う
定数static final再代入しない値可変オブジェクト参照に注意する固定設定を表す
非公開private同一クラスだけで使う必要ならメソッド経由にする情報隠蔽に使う
公開public外部から直接見える値の変更を制御しにくい定数以外では慎重に使う
継承向けprotected同一パッケージと子クラスから見える継承先に影響が広がる拡張点を作る
パッケージ限定修飾子なし同じパッケージ内から見える境界が見えにくい小さな内部構造で使う
初期値int number;型ごとの既定値になる意図が伝わりにくい場合がある明示初期化を検討する
参照型String text;未設定ならnullNullPointerExceptionに注意するコンストラクタで埋める
コンストラクタ初期化this.name = name;生成時に値を渡す引数検証を忘れない必須値に向く
メソッド初期化initialize()任意のタイミングで設定する未初期化状態が残る再設定が必要な値に向く
ゲッターgetName()値を返す内部表現を漏らさない読み取りを公開する
セッターsetAge(int age)値を書き換える検証を入れる制約付き更新に使う
thisthis.age現在のオブジェクトを表す引数名との区別に使う代入先を明確にする
supersuper.name親クラス側を参照する同名フィールドに注意する継承の確認に使う
シャドーイング同名の変数近いスコープが優先される読み間違いが起きやすい名前を分ける
カプセル化privateとメソッド内部状態を守る過剰な公開を避ける設計の基準にする
不変性final再代入を防ぐ参照先の可変性は別問題変更しない値に使う
命名employeeName役割を名前に含める長すぎる名前を避ける業務語彙に合わせる
コメント//意図を補足する名前で伝わる内容は重ねない制約や理由を書く
配列String[] names複数値を持つサイズ固定に注意する単純な集合に使う
リストList<String>可変長の集合を持つ外部へ直接返さない追加削除がある値に使う
論理値boolean active状態の真偽を持つ否定名を避ける有効無効を表す
列挙型enum Status限定された値を扱う文字列直書きを避ける状態分類に使う
初期化ブロック{ ... }生成時の処理をまとめる複雑化しやすい通常はコンストラクタを優先する
読み取り専用公開getのみ外部から変更させない内部変更の有無を明確にするIDや作成日時に向く
検証ifで判定不正値を防ぐ例外か補正かを決めるセッターやコンストラクタに置く
例外IllegalArgumentException不正な引数を知らせるメッセージを具体化する呼び出し側へ失敗を返す
抽象クラスabstract class共通状態を持たせる密結合に注意する共通属性が明確なとき使う
設計レビュー公開範囲確認変更範囲を抑える初期設計で見落としやすい注意点を洗い出す

判断は「値をどこで持つか」「誰が触れるか」「いつ初期化するか」に集約できます。この三点を軸にすると、Javaのサンプルコードを読み取りやすくなります。

💡 Tips: Javaのフィールド宣言は、アクセス修飾子、staticfinal、型、名前、初期値の順に読むと構造を追いやすくなるのが一般的です。

メンバ変数の詳細な使い方

メンバ変数の使い方は、宣言、初期化、アクセス、スコープに分けると理解しやすくなるのが一般的です。Javaでは宣言位置で寿命と参照範囲が変わるため、初心者はクラス本体かメソッド内かを最初に確認します。

サンプルコード1:メンバ変数の基本的な定義方法

最も小さいメンバ変数の形です。SampleClass内にmemberVariableを置くと、Javaのクラスに属するフィールドとして扱われます。

public class SampleClass {
    // メンバ変数の定義
    int memberVariable;
}

結果: 期待される状態として、SampleClassの各インスタンスがint型のmemberVariableを持ちますが、これは押さえたい点です。

修飾子を省略しているため、同じパッケージ内から参照できるパッケージプライベートです。外部公開を避けたい値ならprivateを付け、必要な操作だけをメソッドで公開するのが現実的です。

宣言時に初期値も入れられます。初期値があると、Javaオブジェクト生成直後の状態が読み取りやすくなります。

public class SampleClass {
    // メンバ変数の定義と初期化
    int memberVariable = 10;
}

結果: 期待される状態として、生成されたSampleClassmemberVariableは初期値10を持ちますが、これは押さえたい点です。

ただし、すべてを宣言時に入れる必要はありません。生成時に決まる値は、constructorで受け取る使い方が自然です。

サンプルコード2:メンバ変数へのアクセス方法

アクセス可否は、同じクラス内か外部クラスかで変わります。クラス内部ではpublicVariableprivateVariableを直接読めますが、外部からはアクセス修飾子に従います。

public class SampleClass {
    // メンバ変数の宣言
    public int publicVariable = 100;
    private int privateVariable = 200;

    // publicメソッドからのアクセス
    public void accessFromPublicMethod() {
        System.out.println("public変数の値: " + publicVariable);
        System.out.println("private変数の値: " + privateVariable);
    }

    // privateメソッドからのアクセス
    private void accessFromPrivateMethod() {
        System.out.println("public変数の値: " + publicVariable);
        System.out.println("private変数の値: " + privateVariable);
    }
}

public class MainClass {
    public static void main(String[] args) {
        SampleClass sampleClass = new SampleClass();

        // publicメソッドを通じたメンバ変数へのアクセス
        sampleClass.accessFromPublicMethod();

        // public変数への直接アクセス
        System.out.println("public変数の値: " + sampleClass.publicVariable);

        // private変数への直接アクセス(コンパイルエラー)
        // System.out.println("private変数の値: " + sampleClass.privateVariable);
    }
}

結果: 期待される出力はpublic変数の値: 100private変数の値: 200を含み、コメントを外してprivateVariableへ直接アクセスするとコンパイルエラーになります。

publicの値は外部から読めますが、privateの値はaccessFromPublicMethodのような公開メソッドを経由します。Javaのカプセル化では、この経由点に検証や変換を置けますし、これが一つの目安です。

学習用の短いサンプルコードではpublicフィールドも出ます。実務的な設計に近づけるなら、Javaのエスケープ処理のように入力値を安全に扱う観点も合わせます。

サンプルコード3:メンバ変数の初期化方法

初期化には、宣言時、コンストラクタ、メソッドの三つがあると整理できます。Javaでの基準は、値が必須か、生成後に変わるか、呼び出し順に依存してよいかです。

public class SampleClass {
    int memberVariable = 0; // メンバ変数の初期化
}

結果: 期待される状態として、memberVariableはオブジェクト生成直後から0を保持すると整理できます。

コンストラクタで初期化すると、Javaオブジェクト生成時に必要な値を必ず通せます。名前やIDのように未設定では困る値に向きますが、覚えておくと役立つでしょう。

public class SampleClass {
    int memberVariable;

    public SampleClass() {
        memberVariable = 0; // コンストラクタ内でのメンバ変数の初期化
    }
}

結果: 期待される状態として、new SampleClass()が呼ばれるタイミングでmemberVariable0が入りますし、ここを基本と考えるとよいでしょう。

メソッド初期化には呼び出し忘れの注意点があります。initialize前に値を読むと、期待しないデフォルト値を使う可能性があります。

public class SampleClass {
    int memberVariable;

    public void initialize() {
        memberVariable = 0; // メソッド内でのメンバ変数の初期化
    }
}

結果: 期待される状態として、initialize()を呼び出した後にmemberVariable0へ更新されますし、ここを基本と考えるとよいでしょう。

必須値はconstructor、任意値はセッター、再初期化が必要な値は専用メソッドに寄せます。Javaの徹底解説としては、初期化場所をオブジェクトの寿命を表す設計情報として読みます。

サンプルコード4:メンバ変数のスコープとアクセス修飾子

スコープは変数を参照できる範囲で、アクセス修飾子は外部から見える範囲を制御すると理解できます。Javaではpublicprotected、修飾子なし、privateの順に公開範囲を比較します。

public class SampleClass {
    public int publicVariable = 1; // どのクラスからでもアクセス可能
    protected int protectedVariable = 2; // 同一パッケージ内またはサブクラスからアクセス可能
    int defaultVariable = 3; // 同一パッケージ内からアクセス可能
    private int privateVariable = 4; // 同一クラス内からのみアクセス可能

    public void displayVariables() {
        System.out.println("Public: " + publicVariable);
        System.out.println("Protected: " + protectedVariable);
        System.out.println("Default: " + defaultVariable);
        System.out.println("Private: " + privateVariable);
    }
}

結果: 期待される出力はPublic: 1Protected: 2Default: 3Private: 4です。同一クラス内のdisplayVariables()からは全フィールドにアクセスできます。

同じクラス内のメソッドはすべてのメンバ変数を読めます。別クラスからはパッケージや継承関係で結果が変わる点が注意点です。

public class AnotherClass {
    public static void main(String[] args) {
        SampleClass sampleClass = new SampleClass();

        System.out.println("Public: " + sampleClass.publicVariable); // アクセス可能
        // System.out.println("Protected: " + sampleClass.protectedVariable); // パッケージや継承関係によって可否が変わる
        // System.out.println("Default: " + sampleClass.defaultVariable); // 同一パッケージならアクセス可能
        // System.out.println("Private: " + sampleClass.privateVariable); // コンパイルエラー
    }
}

結果: 期待される出力は同一条件でアクセス可能なPublic: 1です。コメント行を有効にした場合、パッケージや継承関係に合わない参照はコンパイルエラーになります。

初心者はprivateを標準にして、必要な読み取りや更新をgettersetterで公開する方針から始めると安全です。公開範囲が狭いほど変更の影響範囲も読みやすくなります。

⚠️ 注意: protectedは「子クラスだけに公開」という意味ではありません。同一パッケージ内からも見えるため、パッケージ設計と合わせて判断すると考えられますし、ここがポイントです。

メンバ変数の応用例

応用例では、状態保持、継承時の参照、クラス全体で共有する値、不変の値、カプセル化を扱います。Javaのクラス設計では、これらの組み合わせがよく出ます。

Employeesuperstaticfinalを順に確認すると覚えるとよいでしょう。各サンプルコードの直後に期待される出力や状態を置き、動きと設計意図を対応させます。

サンプルコード5:クラス内でのメンバ変数の使い方

オブジェクトに名前や年齢といった状態を持たせる例です。Employeenameageをメンバ変数として保持し、displayInfoで表示します。

public class Employee {
    // メンバ変数の定義
    String name;
    int age;

    // コンストラクタでメンバ変数の初期化を行います
    public Employee(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // メンバ変数の値を表示するメソッド
    public void displayInfo() {
        System.out.println("名前: " + name + ", 年齢: " + age);
    }

    public static void main(String[] args) {
        Employee emp = new Employee("山田太郎", 25);
        emp.displayInfo();
    }
}

結果: 期待される出力は名前: 山田太郎, 年齢: 25です。

this.namethis.ageは現在のオブジェクト側のメンバ変数です。引数名とフィールド名が同じでも、thisで代入先が明確になります。

Javaでうるう年を判定する処理のような条件分岐でも、値をどこに保持するかでクラスの形が変わります。状態を持たない処理ならメソッド引数、状態を持つモデルならメンバ変数が向きますが、これは押さえたい点です。

サンプルコード6:継承時のメンバ変数の挙動

継承では、親クラスと子クラスに同名のメンバ変数を置くと読み取りが難しくなります。Javaのフィールドはメソッドのオーバーライドと違って同名で隠れるため、superで親側を明示する場面があります。

class ParentClass {
    String memberValue = "親クラスのメンバ変数";

    void show() {
        System.out.println("親クラスのメソッド: " + memberValue);
    }
}

class ChildClass extends ParentClass {
    String memberValue = "子クラスのメンバ変数";

    void show() {
        System.out.println("子クラスのメソッド: " + memberValue);
    }

    void showParentValue() {
        System.out.println("親クラスのメンバ変数: " + super.memberValue);
    }
}

public class Main {
    public static void main(String[] args) {
        ChildClass instance = new ChildClass();
        instance.show();
        instance.showParentValue();
    }
}

結果: 期待される出力は子クラスのメソッド: 子クラスのメンバ変数親クラスのメンバ変数: 親クラスのメンバ変数です。

同名フィールドがあると、どちらの値を読んでいるのかが見えにくくなります。継承設計では、親クラスと子クラスの状態名を不用意に重ねないほうが保守しやすくなります。

子クラスのメソッド: 子クラスのメンバ変数
親クラスのメンバ変数: 親クラスのメンバ変数

結果: 期待される出力例として、子クラス側の値とsuperで参照した親クラス側の値が別々に表示されますし、これが一つの目安です。

メソッドの振る舞いを差し替える場合は@Overrideを使います。Javaのオーバーライド解説と合わせると、フィールドの隠蔽との違いを整理できます。

サンプルコード7:メンバ変数を用いたオブジェクト指向プログラミングの実例

オブジェクト指向では、属性をメンバ変数、振る舞いをメソッドとしてまとめますが、覚えておくと役立つでしょう。下の例ではAnimalnameageを持ち、introduceで状態を文章にします。

class Animal {
    String name; // 名前を表すメンバ変数
    int age; // 年齢を表すメンバ変数

    void introduce() {
        System.out.println("名前は" + name + "です。年齢は" + age + "歳です。");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal dog = new Animal();
        dog.name = "ベル";
        dog.age = 3;
        dog.introduce();
    }
}

結果: 期待される出力は名前はベルです。年齢は3歳です。です。

このサンプルコードは学習用にフィールドへ直接代入しています。カスタマイズや入力チェックが必要なら、privateにしてsetNamesetAgeを経由します。

初心者は直接アクセスの練習と実際の設計を分けて考えますし、ここを基本と考えるとよいでしょう。Javaの徹底解説としては、動きを掴んでから情報隠蔽へ寄せる順序が分かりやすいです。

サンプルコード8:メンバ変数とstaticキーワードの組み合わせ

staticを付けたメンバ変数は、インスタンスではなくクラスに属します。Javaでカウント値や共有設定のように、全オブジェクトで同じ値を見る場合に使います。

public class SampleClass {
    public static int staticVariable = 100;

    public static void displayStaticVariable() {
        System.out.println("static変数の値: " + staticVariable);
    }

    public static void main(String[] args) {
        SampleClass.displayStaticVariable();
        staticVariable = 200;
        SampleClass.displayStaticVariable();
    }
}

結果: 期待される出力はstatic変数の値: 100の後にstatic変数の値: 200が続く形です。

staticVariableSampleClassに紐づく共有値です。ユーザー名や注文金額のような個別状態へstaticを付けると、別インスタンスの値まで同じになります。

static変数の値: 100
static変数の値: 200

結果: 期待される出力例として、共有されたstaticVariableの変更後の値が二回目の表示に反映されますが、これは押さえたい点です。

staticは「インスタンスなしで使える」だけで判断しません。共有してよいか、テスト時に状態が残らないか、並行処理で更新されないかが注意点です。

サンプルコード9:final修飾子を用いたメンバ変数の実装

finalを付けたメンバ変数は、初期化後に再代入できません。Javaの設定値や識別子など、変更されたくない値に向きます。

public class ExampleClass {
    final int finalVariable = 100;

    public void showFinalVariable() {
        System.out.println("finalVariable: " + finalVariable);
    }
}

結果: 期待される出力はshowFinalVariable()を呼び出したときにfinalVariable: 100です。

finalは変数への再代入を防ぐ修飾子です。参照型の中身が可変なら参照先の状態変更までは防がないため、不変設計では型の性質も確認します。

public class ExampleClass {
    final int finalVariable = 100;

    public void modifyFinalVariable() {
        finalVariable = 200;  // コンパイルエラー
    }
}

結果: 期待される結果として、finalVariableへ再代入しようとする行でコンパイルエラーになります。

この注意点は、static finalで定数を作るときにも関係すると考えられます。Stringやプリミティブ型は扱いやすい一方、可変コレクションを定数のように公開すると変更される余地が残ります。

サンプルコード10:エンカプセレーションを考慮したメンバ変数の設計

エンカプセレーションは、メンバ変数を外部から直接触らせず、メソッドを通じて読む・更新する考え方です。値の検証や表示形式の調整をクラス内に集め、カスタマイズしやすくします。

public class Person {
    private String name; // プライベートメンバ変数
    private int age; // プライベートメンバ変数

    // 公開されたゲッターメソッド
    public String getName() {
        return name;
    }

    // 公開されたセッターメソッド
    public void setName(String name) {
        this.name = name;
    }

    // 公開されたゲッターメソッド
    public int getAge() {
        return age;
    }

    // 公開されたセッターメソッド
    public void setAge(int age) {
        if (age > 0) {
            this.age = age;
        } else {
            System.out.println("年齢は0より大きい数値を設定してください。");
        }
    }
}

結果: 期待される状態として、nameageは外部から直接書き換えられず、setAgeでは0以下の値を受け付けない形になります。

ageの更新時に条件を置く点が重要です。外部がperson.age = -1のように直接代入できる設計だと、不自然な値を防ぎにくくなります。

public class Main {
    public static void main(String[] args) {
        Person person = new Person();
        person.setName("田中");
        person.setAge(25);

        System.out.println("名前: " + person.getName());
        System.out.println("年齢: " + person.getAge());
    }
}

結果: 期待される出力は名前: 田中年齢: 25です。

呼び出し側はsetNamegetAgeだけを意識します。内部のメンバ変数名を後から変更しても、公開メソッドの契約を保てば影響範囲を抑えられます。

名前: 田中
年齢: 25

結果: 期待される出力例として、セッターで設定した値をゲッター経由で取得した内容が表示されますし、これが一つの目安です。

gettersetterを全フィールドへ機械的に作る必要はありません。読み取りだけ許可する値、更新も許可する値、外部へ出さない値を分けることが、Java設計のカスタマイズにつながりますし、ここがポイントです。

ℹ️ 補足: JavaBeans形式のgetXxxsetXxxは多くのライブラリで扱われますが、ドメイン上の操作名が明確ならchangeNameactivateのようなメソッド名も選択肢になります。

メンバ変数の注意点と対処法

注意点は、公開しすぎ、初期化漏れ、同名変数、共有状態の誤用に集まります。Javaは多くの誤りをコンパイル時に検出しますが、読みにくい設計までは自動で防げません。

初心者はすべてpublicにして後で直す進め方を避けますが、これは押さえたい点です。privateを標準にし、必要な使い方だけを公開メソッドで表すと、注意点を早く減らせます。

情報隠蔽の考え方

情報隠蔽は、クラスの内部状態を外部から直接変更させない考え方です。値の整合性を守るなら、メンバ変数をprivateにし、必要な読み書きをメソッドへ集約します。

public class Person {
    private String name; // ここで情報隠蔽を行っている

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

結果: 期待される状態として、nameは外部から直接参照されず、getName()setName(String name)を通じて扱われますし、ここを基本と考えるとよいでしょう。

この対処法なら、後からsetNameへ空文字チェックを追加できます。外部コードがメンバ変数へ直接触れていないため、検証ロジック追加時の変更を小さくできます。

状態確認はtoString、読み取り専用ゲッター、ログ用メソッドで公開できると言えるでしょう。Javaでは状態確認と情報隠蔽を分けて設計します。

変数の命名規則と読みやすさ

変数名は、メンバ変数の役割を読む入口です。xdataだけでは意図が伝わりにくいため、employeeNamecreatedAtのように意味を含めます。

public class Employee {
    private String employeeName; // わかりやすい変数名
    private int employeeAge; // わかりやすい変数名

    public String getEmployeeName() {
        return employeeName;
    }

    public void setEmployeeName(String employeeName) {
        this.employeeName = employeeName;
    }

    public int getEmployeeAge() {
        return employeeAge;
    }

    public void setEmployeeAge(int employeeAge) {
        this.employeeAge = employeeAge;
    }
}

結果: 期待される状態として、employeeNameemployeeAgeが従業員の名前と年齢を保持する意図を名前から読み取れます。

型で分かる情報を重ねすぎず、業務上の意味を入れると読みやすくなります。String nameTextよりemployeeNameint numberよりretryCountのほうが追いやすい場合があるのが基本です。

ただし、クラス名と重複しすぎる名前は冗長です。Employeeクラス内ならnameageで十分な場合もあり、スコープに合わせて長さを調整します。

初期化漏れとnullの扱い

Javaの参照型メンバ変数は、明示的に初期化しないとnullになります。nullのままname.length()を呼ぶと、NullPointerExceptionが起きる可能性があるのが目安です。

必須の参照型はコンストラクタで受け取るか、宣言時に安全な初期値を入れます。任意値としてnullを許すなら、その契約が伝わる名前やドキュメントを用意します。

こうした注意点は、Javaの条件分岐やコレクションとも関係するのがポイントです。Java List型のサンプルを読むと、複数値をフィールドとして持つ場合の公開範囲も考えやすくなります。

メンバ変数のカスタマイズ方法

カスタマイズでは、保存だけでなく、読み取り時の表現、書き込み時の検証、継承先での拡張を設計します。Javaのクラスは状態と振る舞いを近くに置けるため、制約をメソッド内へ集めやすい構造です。

外部コードは名前を設定する、年齢を読むといった操作だけを呼び出せますし、これが一つの目安です。内部のメンバ変数をどう保存するかは、Javaクラス側で後から変更できます。

カスタムゲッターやセッターの作成方法

カスタムゲッターとセッターは、メンバ変数の出入り口に処理を追加する方法です。入力値のnullチェック、空文字の補正、表示用文字列の整形をクラス内にまとめられます。

public class Person {
    private String name;

    // カスタムゲッター
    public String getName() {
        return "名前は" + name + "です";
    }

    // カスタムセッター
    public void setName(String name) {
        if (name != null && !name.isEmpty()) {
            this.name = name;
        } else {
            this.name = "無名";
        }
    }
}

public class Main {
    public static void main(String[] args) {
        Person person = new Person();
        person.setName("太郎");
        System.out.println(person.getName());
    }
}

結果: 期待される出力は名前は太郎ですです。setNamenullや空文字を渡した場合は、期待される状態としてname無名が入ります。

表示用に整形したゲッターは便利ですが、純粋な名前だけが必要な場面で扱いにくくなる場合があります。用途が分かれるなら、getName()は値そのもの、getDisplayName()は表示用に分けますが、覚えておくと役立つでしょう。

セッターの検証は多くのJava設計で効果があります。入力値の制約をクラス内へ集めると、呼び出し側が同じチェックを何度も書かずに済みます。

拡張性を持たせたメンバ変数の設計思考

拡張性では、共通で持つメンバ変数と、子クラスごとに変える振る舞いを分けますし、ここを基本と考えるとよいでしょう。抽象クラスに共通のnameを置き、具体クラスでsoundを実装する形が例です。

public abstract class Animal {
    protected String name;

    public String getName() {
        return name;
    }

    public abstract void sound();
}

public class Dog extends Animal {

    public Dog(String name) {
        this.name = name;
    }

    @Override
    public void sound() {
        System.out.println(name + "がワンワンと鳴きました");
    }
}

public class Main {
    public static void main(String[] args) {
        Dog dog = new Dog("ポチ");
        System.out.println(dog.getName());
        dog.sound();
    }
}

結果: 期待される出力はポチポチがワンワンと鳴きましたです。

この例ではprotectedにより、子クラスのDogからnameを扱えます。継承階層が深いほど影響範囲が広がるため、必要がなければprivateprotectedメソッドの組み合わせも候補です。

アノテーションで制約やメタ情報を付ける設計もあります。Javaのメタ情報は、Javaアノテーションの解説と合わせると、フィールドに付く情報の扱いが見えやすくなるのが一般的です。

Javaのメンバ変数を効率的に利用するコツ

効率的な使い方の中心は、値を持つ理由が明確なメンバ変数だけを残すことです。一時的な計算結果までフィールドにすると、どの値が最新なのか分かりにくくなります。

public class Employee {
    // 社員の名前を保持するメンバ変数
    private String name;

    // 社員の年齢を保持するメンバ変数
    private int age;

    // 社員の名前を取得するメソッド
    public String getName() {
        return name;
    }

    // 社員の名前を設定するメソッド
    public void setName(String name) {
        this.name = name;
    }

    // 社員の年齢を取得するメソッド
    public int getAge() {
        return age;
    }

    // 社員の年齢を設定するメソッド
    public void setAge(int age) {
        this.age = age;
    }
}

public class Main {
    public static void main(String[] args) {
        Employee employee = new Employee();
        employee.setName("田中");
        employee.setAge(30);
        System.out.println("名前: " + employee.getName());
        System.out.println("年齢: " + employee.getAge());
    }
}

結果: 期待される出力は名前: 田中年齢: 30です。

このサンプルコードでは、nameageprivateにし、外部には必要なメソッドだけを出しています。コメントは理由を補う程度に留めると読みやすさを保てます。

計算途中だけで使う値はlocal variable、オブジェクトの状態として残す値はメンバ変数、全体で共有する値はstaticに整理するのが現実的です。この分類により、初心者でもJavaクラスの責務を追いやすくなります。

⚠️ 注意: メンバ変数を増やすほど、オブジェクトが持つ状態も増えます。状態が多いクラスは更新順序の影響を受けやすいため、メソッド引数や戻り値で済む値をフィールド化しない判断も必要です。

Javaの徹底解説としてメンバ変数を学ぶなら、サンプルコードの動作だけでなく設計上の意図も読みますし、ここがポイントです。どの値を隠し、どの操作を公開し、どの注意点をクラス内で吸収するかが保守性に直結します。

メンバ変数のカスタマイズは装飾的な書き換えではなく、利用者に見せる操作を整える作業です。カスタムゲッター、セッター、抽象クラス、finalを選べると、Javaのクラス設計は読みやすくなります。

まとめ

Javaのメンバ変数は、オブジェクトの状態を保持する中心的な仕組みです。宣言位置、アクセス修飾子、初期化方法、staticfinal、継承時の挙動をまとめて理解すると、クラスが管理する値を読み取りやすくなると整理できます。

初心者が最初に意識したいのは、privateを基本にして、必要な使い方だけをメソッドで公開することです。値の検証や表示形式の変更をクラス内へ集めると、呼び出し側を大きく変えずにカスタマイズできます。

すべてをメンバ変数にする必要はありません。一時的な値はローカル変数、共有値は慎重にstatic、変更しない値はfinal、外部公開が必要な値はゲッターやセッターで分けると注意点を減らせます。

徹底解説として扱ったサンプルコードは、Javaのメンバ変数の基本から応用までを横断していると理解できます。どのフィールドが誰から見えるのか、どのタイミングで初期化されるのかを確認すると理解が定着します。

著者: Japanシーモア編集部

Japanシーモアは、Web/IoT/APP/SYS 分野のプログラミング情報を体系的に提供するメディアです。本記事は編集部による執筆とAI支援を組み合わせて制作し、公開前に編集部が校正しています。誤りや改善案がございましたらお問い合わせよりご連絡ください。

※本記事は実在のエンジニア複数名で構成される Japanシーモア編集部が、AI支援を活用して作成・校正・公開しています。

関連記事