基本
オーバーロード
引数リストが異なるメソッドやコンストラクタを複数定義すること
- 引数リストとは、引数の
並び順
,データ型
,数
のこと 戻り値
,引数の変数名
,アクセス修飾子
は引数リストには含まれないため注意が必要
void method() {}
void method(int number) {}
void method(int number, String string) {}
void method(String string, int number) {}
オーバーロードの優先順位
完全一致
> 暗黙の型変換
> 1Boxing
> 可変長引数
public class Main {
public void method(int a) { System.out.println("method(int a)"); } // 1(完全一致)
public void method(long a) { System.out.println("method(long a)"); } // 2(暗黙の型変換)
public void method(Integer a) { System.out.println("method(Integer a)"); } // 3(Boxing)
public void method(int... a) { System.out.println("method(int... a)"); } // 4(可変長引数)
public static void main(String[] args) {
new Main().method(555); // method(int a)
}
}
オーバーライド
サブクラス内で、スーパークラスで定義しているメソッドと同じ名前でメソッドを再定義すること
要点
- アクセス修飾子は、スーパークラスと同じか、それより広い
- 戻り値は、スーパークラスと同じか、そのサブクラス
- メソッド名と引数リストは同じ
final
がついていないこと
注意点
- インスタンスメソッドを static メソッドで再定義することはできない(その逆も不可)
- 型パラメーターを変更することはできない
class Super {
void methodA() { System.out.println("Super: methodA()"); }
static void methodB() { System.out.println("Super: methodB()"); }
}
class Sub extends Super {
void methodA() { System.out.println("Sub: methodA()"); } // OK
static void methodA() { System.out.println("Sub: methodA()"); } // NG(インスタンスメソッドをstaticメソッドで再定義できない)
static void methodB() { System.out.println("Sub: methodB()"); } // OK
void methodB() { System.out.println("Sub: methodB()"); } // NG(staticメソッドをインスタンスメソッドで再定義できない)
}
class Super {
<T> Collection<T> foo(Collection<T> c) { return null; };
}
class Sub extends Super {
<T> Collection<T> foo(Collection<T> c) { return null; }; // OK
<T> List<T> foo(List<T> c) { return null; }; // OK(引数の型が異なるため問題ない)
<T> Collection<T> foo(Map c) { return null; }; // OK(引数の型が異なるため問題ない)
<T> List<T> foo(Collection<String> c) { return null; }; // NG(型パラメーターは変更できない)
}
コンストラクタ
- サブクラスでスーパークラスのコンストラクタを呼び出していない場合、サブクラスのコンストラクタには暗黙的に
super();
が追加される - そのため、スーパークラスに引数無しのコンストラクタが定義されていない場合は、明示的にスーパークラスのコンストラクタを呼ぶ必要がある
コンパイルが通らないコード
class Super {
int number;
Super(int number) { this.number = number; } // スーパークラスのコンストラクタ定義
}
class Sub extends Super {
Sub() { System.out.println("Subコンストラクタ"); } // 6行目でコンパイルエラー発生
}
解決策 ① - 明示的にスーパークラスのコンストラクタを呼ぶ
class Super {
int number;
Super(int number) { this.number = number; } // スーパークラスのコンストラクタ定義
}
class Sub extends Super {
Sub() {
super(555); // 明示的にスーパークラスのコンストラクタを呼ぶ
System.out.println("Subコンストラクタ");
}
}
解決策 ② - 引数なしのコンストラクタをスーパークラスに定義する
class Super {
int number;
Super() {} // 引数なしのコンストラクタをスーパークラスに定義する
Super(int number) { this.number = number; } // スーパークラスのコンストラクタ定義
}
class Sub extends Super {
Sub() { System.out.println("Subコンストラクタ"); }
}
実行順序について
コンストラクタと static イニシャライザが絡む場合のプログラムの実行順序については以下の通り
- main メソッドがあるクラスの static イニシャライザ
- スーパークラスの static イニシャライザ
- サブクラスの static イニシャライザ
- スーパークラスのコンストラクタ
- サブクラスのコンストラクタ
public class Main {
static { System.out.println("Main static block"); } // 1
public static void main(String[] args) {
new Sub();
}
}
class Super {
static { System.out.println("Super static block"); } // 2
Super() { System.out.println("Super constructor"); } // 4
}
class Sub extends Super {
static { System.out.println("Sub static block"); } // 3
Sub() { System.out.println("Sub constructor"); } // 5
}
String インスタンスの比較
Java の String クラスのインスタンス化 2 選
String str1 = "foo"
: コンスタントプール領域に"foo"が生成され、str1 はそれを参照するString str2 = new String("foo")
: ヒープ領域に"foo"が生成され、str2 はそれを参照する
- "foo"はコンスタントプール領域に生成され、pool1,pool2 共に同一のメモリを参照するため「true」となる
String pool1 = "foo";
String pool2 = "foo";
System.out.println(pool1 == pool2); // true
- "foo"はそれぞれヒープ領域に生成され、メモリ参照値が異なり、「false」となる
String heep1 = new String("foo");
String heep2 = new String("foo");
System.out.println(heep1 == heep2); // false
- こちらも 2 と同様に、メモリ参照値が異なり、「false」となる
String pool = "foo";
String heep = new String("foo");
System.out.println(pool == heep); // false
String.intern()
コンスタントプール領域を拡張するメソッド。String インスタンスの比較の文脈で出題される傾向が高いです。
要点
- 同じ値がコンスタントプール領域に存在する場合、その参照値を返す
- 同じ値がコンスタントプール領域に存在しない場合、コンスタントプール領域にその値を生成し、そのメモリ参照値を返す
String str1 = new String("foo");
String str2 = "foo";
String str3 = str1.intern(); // コンスタントコンスタントプール領域にはstr2で生成した"foo"が存在するため、str2が参照してる"foo"のメモリ参照値が返る
System.out.println(str1 == str2); // false
System.out.println(str1 == str3); // false
System.out.println(str2 == str3); // true
Footnotes
-
Boxing とは、プリミティブ型をラッパークラスに自動的に変換する機能です ↩