Java Silver SE11 6章
黒本をもとに学んだことをアウトプットしていきます。
主に問題を解いていて、間違えた箇所もしくは知らなかった内容になります。
クラス・インスタンス
JVMによりハードディスクに保存されているクラスファイルが読み込まれ、メモリ上にインスタンスを展開する。
例
public class Item {
public String name;
public int price;
public void printInfo() {
System.out.println(name + ", " + price);
}
}
public class Main {
public static void main(String[] args) {
Item item1 = new Item();
item1.price = 100;
item1.name = "Item";
System.out.println(item1.price); // 100
item1.printInfo(); // Item, 100
}
}
null
Javaには、データそのものを保持するプリミティブ型
とオブジェクトへの参照(リンク)を保持する参照型
がある。
参照を保持していないことを表現するリテラルがnull
である。
public class Null {
public static void main(String[] args) {
// 参照を保持していない
Object obj1 = null;
System.out.println(obj1); //null と表示される
}
ガベージコレクタ
メモリ空間には限りがある。空きスペースを確保するために、ガベージコレクタはメモリ上にある利用されていない不要なインスタンスを削除する。
この不要なインスタンスを探し、破棄する一連の流れをガベージコレクションと呼ぶ。
またガベージコレクションが起こるタイミングはJVMが決めるので、制御できない。
※gcメソッドがあるが、あくまでJVMに実行を促すだけである。
public class Garbage_collection {
public static void main(String[] args) {
Object a = new Object();
Object b = new Object();
Object c = a;
a = null;
b = null;
}
}
【ガベージコレクションの対象】
- Object a = new Object();
- Object b = new Object();
- Object c に参照があるので、aがnullでも対象にならない
- どこからも参照がないので削除対象になる
static
クラスファイルを読み込む(ロード)するときメモリ空間には、staticな部分は以下のように別の領域に配置される。
- static領域
staticで修飾されたフィールドやメソッドが配置される - ヒープ領域
static以外の部分が配置される
それぞれの領域に配置した後、ヒープ領域上にてクラス定義に従ったインスタンスが生成される。
よってstaticなフィールドは、インスタンスを生成しなくとも使用することができる。
【アクセスの可否について】
- staticなメソッドは、staticなメンバ(フィールドとメソッド)にしかアクセスできない
- staticでないメンバには、インスタンスが生成されていない可能性があるため
- 逆にstaticでないメソッドからstaticなメンバへは、アクセス可能
例
public class Sample {
static int num = 0;
}
public class Main {
public static void main(String[] args) {
Sample.num = 10;
Sample s1 = new Sample();
Sample s2 = new Sample();
s1.num +=10;
s2.num = 50;
System.out.println(Sample.num); // 50
}
}
このようにstaticなフィールドは、Sampleクラス間で共有するように使用できる。
メソッド
まずは構文を整理する。
アクセス修飾子 戻り値型 メソッド名(引数の型 引数名){
// メソッドでの処理
}
戻り値
retunr文で戻すデータの型と、メソッド宣言時での戻り値型は一致させなければならない。
戻り値を何も戻さない場合、戻り値型には「void」を指定する。
(つまり、何かしら戻り値型は記述する必要がある。)
メソッドを実行するとき、以下の条件でメソッドを探し出す。
- 参照 (どのインスタンスのメソッドか)
- クラス名 (どのクラスにあるstaticなメソッドなのか)
- シグニチャ (メソッド名と引数のリスト)
return文
以下の役割がある。
- 呼び出し元のメソッドに値を戻す
- メソッドでの処理を強制終了し、呼び出し元のメソッドに戻る
- 必ずreturn文が実行される場合、以降に処理を記述することはできない
例
public class Sample {
public void method(int num) {
if (num < 0) {
return;
}
System.out.print(num);
return;
// ↓ コンパイルエラー returnで制御を戻しているので、到達できない
System.out.print(num);
}
}
オーバーロード
メソッド名が同じでも引数が異なる場合、同名のメソッドを複数定義できる機能のこと。
引数が異なるとは、以下の条件が相違していることを指す。
- 引数の数
- 型
- 順番
「戻り値型やアクセス修飾子」が異なっていてもオーバーロードとは見なされず、コンパイルエラーになる。
例
public class Calc {
int calc(double a, int b) {
return (int) a * b;
}
///// 以下は4つはオーバーロードと認識されるのでOK /////
// 引数の数が違う
int calc(int a) { return 1;}
// 引数の型が違う
int calc(double a, double b) { return 1;}
// 引数の数が違う
int calc( ) { return 1;}
// 引数の順番が違う
int calc(int b, double a) { return 1;}
///// 以下はコンパイルエラー /////
// 戻り値型は関係なし
double calc(double a, int b) { }
// 引数の変数名も関係なし
int calc(double num1, int num2) { }
}
またオーバーロードできていても引数の型を判別できない場合、コンパイルエラーになる。
例
public class Main {
public static void main(String[] args) {
Main m = new Main();
System.out.print(m.calc(2, 3)); // 2.5
}
private double calc(double a, int b) {
return (a + b) / 2;
}
}
public class Main {
public static void main(String[] args) {
Main m = new Main();
System.out.print(m.calc(2, 3)); // コンパイルエラー
}
private double calc(double a, int b) {
return (a + b) / 2;
}
private double calc(int a, double b) {
return (a + b) / 2;
}
}
【解説】
数値リテラルはデフォルトではint型で解釈される。
そのため引数の型が一致していないように見えるが、double型はint型よりも範囲が大きいため、暗黙の型変換により互換される。
従ってJVMが「どちらのcalcメソッドを使用するのか判断できなくなる」のでコンパイルエラーになる。
コンストラクタ
インスタンスが生成されたタイミングで実行される、前処理のメソッドのこと。
コンストラクタには以下のルールがある。
- メソッド名とクラス名を同じにする
- 戻り値型は記述しない
- 記述した場合、通常のメソッドとして扱われる
- インスタンス生成時以外は使用できない
- アクセス修飾子は自由に設定可能
- コンストラクタ自体を省略可能
- この場合、自動的に引数なしの「デフォルトコンストラクタ」が追加される
- デフォルトコンストラクタは、コンストラクタを1つも記述しなかった場合のみ追加される
例
public class Sample {
public String name;
// コンストラクタ
public Sample() {
this.name = "field name";
}
// コンストラクタではない
void Sample() {
System.out.println("Sample()");
}
}
public class Main {
public static void main(String[] args) {
Sample s = new Sample();
s.Sample();
System.out.println(s.name);
}
}
Sample()
field name
戻り値型を記述している場合コンパイルエラーになるのではなく、通常のメソッドとして解釈される。
通常のメソッドが、クラス名と同じではいけないというルールはないので注意すること。
this
オーバーロードされた別のコンストラクタを呼び出すことができる。
thisは最初に記述する必要があり、最初でない場合コンパイルエラーになる。
例
public class Sample {
public Sample() {
// this("B"); ここならOK
System.out.println("A");
// コンパイルエラー thisは一番最初に記述する必要がある
this("B");
}
public Sample(String str) {
System.out.println(str);
}
}
public class Main {
public static void main(String[] args) {
Sample s = new Sample(); // コンパイルエラー
}
}
コンストラクタと初期化子
コンストラクタもメソッドに分類されるので、オーバーロードにより複数定義できる。
このときに初期化子{ }を使用することで、全てのコンストラクタに対して、共通処理を設定できる。
共通処理は、コンストラクタが実行される前に処理される。
例
public class Sample {
Sample(){
System.out.print("A");
}
Sample(String str){
System.out.print(str);
}
// コンストラクタの共通処理
{
System.out.print("B");
}
}
public class Main {
public static void main(String[] args) {
Sample s1 = new Sample(); // BA
Sample s2 = new Sample("C"); // BC
}
}
メンバに対するアクセス修飾子
4種類あるので、以下にまとめる。
アクセス修飾子 | アクセス可能な範囲 |
---|---|
public | どのクラスからでもアクセス可能 |
protected | 同じパッケージに属するクラス、継承した子クラス |
何も書かない | 同じパッケージに属するクラス |
private | 自分自身のクラスのみ |
例
package ex26;
public class Parent {
// アクセス修飾子は何も記述しない
int num = 10;
}
package other;
import ex26.Parent;
public class Child extends Parent{
public static void main(String[] args) {
System.out.println(num); // コンパイルエラー
}
}
何も書かれていないので、同じパッケージに属するクラスからしかアクセスできない。