Javaの継承とアクセス修飾子を完全解説!初心者でもわかる基本ルール
生徒
「Javaの継承では、親クラスのすべての機能がそのまま使えると聞いたのですが、制限があるんですか?」
先生
「いい質問ですね!継承では親クラスの多くの機能を引き継げますが、いくつかの例外があります。具体的にはコンストラクタやprivateで修飾されたメンバは引き継がれません。詳しく見ていきましょう!」
1. Javaの継承とは?
Javaの継承は、既存のクラス(親クラス)を元に新しいクラス(子クラス)を作成する仕組みです。継承を使うことで、親クラスの機能を再利用し、効率的にプログラムを作成できます。継承はextendsキーワードを使って宣言します。
次のコード例を見てみましょう:
public class Parent {
protected String message = "Hello from Parent";
public void showMessage() {
System.out.println(message);
}
}
public class Child extends Parent {
public void additionalFeature() {
System.out.println("This is a feature of Child class.");
}
}
public class Main {
public static void main(String[] args) {
Child child = new Child();
child.showMessage(); // 親クラスのメソッド
child.additionalFeature(); // 子クラスのメソッド
}
}
実行結果:
Hello from Parent
This is a feature of Child class.
このコードでは、ChildクラスがParentクラスを継承しており、親クラスのフィールドやメソッドを利用しています。
2. 継承で引き継がれないもの
継承では便利な機能が多いですが、次の2つは引き継がれません:
- コンストラクタ:親クラスのコンストラクタは子クラスに引き継がれません。
privateで修飾されたメンバ:親クラスのprivateフィールドやメソッドには、子クラスから直接アクセスできません。
次のコードで確認してみましょう:
public class Parent {
private String secret = "Hidden";
public Parent(String name) {
System.out.println("Parent constructor called with name: " + name);
}
}
public class Child extends Parent {
public Child(String name) {
super(name); // 明示的に親クラスのコンストラクタを呼び出す
}
}
public class Main {
public static void main(String[] args) {
Child child = new Child("Java");
// System.out.println(child.secret); // コンパイルエラー: secretはアクセスできない
}
}
実行結果:
Parent constructor called with name: Java
この例では、secretフィールドがprivateのため子クラスから直接アクセスできず、コンストラクタもsuper()を使って明示的に呼び出しています。
3. アクセス修飾子と継承
親クラスのメンバにはアクセス修飾子が設定されています。これにより、継承時のアクセス範囲が異なります:
private:子クラスからはアクセスできません。- デフォルト:同じパッケージ内の子クラスからはアクセス可能。
protected:異なるパッケージでも継承関係にあればアクセス可能。public:どこからでもアクセス可能。
以下のコードで確認してみましょう:
public class Parent {
protected String shared = "Accessible in Child";
}
public class Child extends Parent {
public void showShared() {
System.out.println(shared);
}
}
public class Main {
public static void main(String[] args) {
Child child = new Child();
child.showShared();
}
}
実行結果:
Accessible in Child
4. 試験対策:継承のポイント
Javaの継承に関する問題を解くには、以下の点を理解しておく必要があります:
- 継承ではコンストラクタが引き継がれない。
privateなフィールドやメソッドには子クラスから直接アクセスできない。- アクセス修飾子に応じて継承時のアクセス範囲が異なる。
super()を使って親クラスのコンストラクタを呼び出す。
5. superキーワードの役割と使いどころ
Javaの継承では、superキーワードが重要な役割を持ちます。
superは「親クラスのメンバ」を明示的に参照するために使用され、特にコンストラクタやメソッドの呼び出しで頻繁に登場します。
子クラス独自の処理を追加しつつ、親クラスの初期化や振る舞いを正しく引き継ぐために欠かせません。
以下は、superを使って親クラスのメソッドを呼び出す例です:
public class Parent {
public void greet() {
System.out.println("Hello from Parent");
}
}
public class Child extends Parent {
public void greet() {
super.greet(); // 親クラスのメソッド呼び出し
System.out.println("Hello from Child");
}
}
public class Main {
public static void main(String[] args) {
Child child = new Child();
child.greet();
}
}
このようにsuperを使うことで、親クラスの処理を活かしながら子クラス独自の振る舞いを追加できます。
6. メソッドのオーバーライドとアクセス修飾子
継承では、親クラスのメソッドを子クラスで再定義する「メソッドのオーバーライド」がよく使われます。 オーバーライドを行う際は、アクセス修飾子の制限に注意が必要です。 基本ルールとして、親クラスよりもアクセス範囲を狭くすることはできません。
次の例で確認してみましょう:
public class Parent {
protected void show() {
System.out.println("Parent show");
}
}
public class Child extends Parent {
@Override
public void show() { // protected → public はOK
System.out.println("Child show");
}
}
この例では、親クラスのprotectedメソッドを、子クラスでpublicとしてオーバーライドしています。
アクセス範囲を広げることは可能ですが、逆に狭めるとコンパイルエラーになります。
7. 継承を使う際の設計上の注意点
継承は便利な仕組みですが、多用しすぎるとクラス同士の依存関係が強くなり、コードの可読性や保守性が低下する可能性があります。 「is-a関係(〜である)」が明確な場合にのみ継承を使うことが、良い設計につながります。
たとえば、「車は乗り物である」のように自然に言い換えられる関係であれば継承が適していますが、 単に機能を使い回したいだけの場合は、継承よりも別クラスとして切り出す方が安全なケースもあります。
- 親クラスは汎用的で安定した設計にする。
- 子クラスは親クラスの仕様に強く依存しすぎない。
- アクセス修飾子を使って不要な公開を避ける。
これらを意識することで、継承を活かした読みやすく拡張しやすいJavaプログラムを作成できます。
まとめ
本記事では、Javaの継承(extends)とアクセス修飾子(public / protected / default / private)の基本ルールを整理しました。
継承は「共通処理を親クラスにまとめて再利用する」ことで、保守性・拡張性を高められる一方、“何でも引き継げる”わけではない点が重要です。
とくに private メンバやコンストラクタは継承しても子クラスから直接参照できないため、設計段階で意図を明確にしておくとバグや混乱を減らせます。
以下に、継承のポイントを簡潔にまとめます:
-
親クラスの
privateメンバは子クラスからアクセスできない(カプセル化のため)。 -
コンストラクタは引き継がれず、
super()で明示的に呼び出して初期化の流れをつなぐ。 - アクセス修飾子により参照できる範囲が変わるため、親クラス側の公開設計が重要。
- 継承を使いすぎると責務が肥大化しやすい。必要に応じて委譲(composition)も検討する。
Javaの継承では、privateは「継承されない」のではなく「子クラスから直接アクセスできない」のがポイントです。
また、親クラスの初期化が必要な場合はsuper()でコンストラクタを呼び出し、アクセス修飾子の設計と合わせて安全に拡張しましょう。
最後に、protectedのメンバとsuper()によるコンストラクタ呼び出しを確認できるサンプルです:
public class Parent {
protected String message = "This is a protected message";
public Parent(String name) {
System.out.println("Parent constructor called: " + name);
}
}
public class Child extends Parent {
public Child(String name) {
super(name);
}
public void displayMessage() {
System.out.println(message);
}
}
public class Main {
public static void main(String[] args) {
Child child = new Child("Java Example");
child.displayMessage();
}
}
実行結果:
Parent constructor called: Java Example
This is a protected message
生徒
「継承の仕組みは分かった気がします!ただ、privateが継承されない理由がまだ少し不明です。」
先生
「ポイントは“継承されない”というより、子クラスから直接アクセスできないという点です。
privateは親クラス内部の実装詳細なので、外(子クラスを含む)に影響させない設計にできます。
もし子クラスに見せたい意図があるなら、protectedやpublic、またはgetterを用意するのが一般的ですね。」
生徒
「なるほど!super()を使ったコンストラクタ呼び出しも便利ですね。」
先生
「そうですね。親クラスの初期化は安全性に直結します。アクセス修飾子で公開範囲を絞りつつ、 継承は“拡張ポイント”として必要最小限にすると、読みやすく壊れにくいJava設計になりますよ。」