大変お世話になっております。
反逆する武士
uematu tubasaです。
初回投稿日時:2023年6月17日(令和5年6月17日)
ガベージコレクションの対象
以下のプログラムを実行し、5行目が終了したときにガベージコレクションの対象となるインスタンスはどれか。
Object a = new object();
Object b = new object();
Object c = a;
a = null;
b = null;
【答え】2行目で作成したインスタンスだけがガベージコレクションの対象となる。
【解説】
インスタンスはメモリ上に作られるため、無制限に作るとメモリ空間を使い切ってしまいます。
したがって、限りのあるメモリを有効に使うためにも、利用されないインスタンスを削除し、空きスペースを作らなければなりません。
ガベージコレクタというJVMの機能があります。
それがメモリ上に使われなくなったインスタンスを探し、見つかればそのインスタンスを破棄して、メモリを解放することを「ガベージコレクション」と呼びます。
ガベージコレクションの対象はどこからも参照されなくなったインスタンスです。
1行目と2行目でインスタンスが作成され、3行目でaの参照先がcというObject型変数に渡され、その後4行目と5行目で参照先が外されていきます。
したがって、どこからも参照されないのは2行目で作成したインスタンスのみとなります。
staticなメンバに関するルール
staticで修飾されたフィールドやメソッドはインスタンスが作られるメモリ領域とは別のstatic領域に作られます。
staticメソッドからstaticではないフィールドやメソッドにはアクセスできません。
逆に、staticではないフィールドやメソッドからはstaticなフィールドやメソッドにはアクセスできます。
static int a = 0;
public Sample(int a){
this.a = a;
}
上記のようなコンストラクタを想定すれば、当然ですね。
staticなフィールドやメソッドはクラスのロード後、すぐに使用することができます。
一方で、staticではないフィールドやメソッドはインスタンスがないと使用することができません。
したがって、staticメソッドがstaticではないフィールドやメソッドにアクセスするということになれば、使用できないフィールドやメソッドを使用してしまうかもしれません。
そういったことを防ぐ仕組みが存在しているのです。
可変長引数に関する問題
可変長引数は、その名のとおり、その数を変更できる引数のことです。
宣言方法としては、引数の型の直後にピリオド3つ「...」を付けて宣言します。
private static int add(int... a) {
int total = 0;
for(int i = 0; i < a.length; i++) {
System.out.println(a[i]);
}
}
この場合、大カッコ([])を使用し、引数を配列として扱います。
可変長引数を使用するときは、以下の2点に注意する必要があります。
1、同じ型で数の可変な引数をまとめられるだけで、異なる型はまとめられない。
2、可変長引数以外の引数を受け取る必要がある場合は、可変長引数を最後の引数にすること。
【問題】
以下の中から、メソッドの宣言として正しいものを選びなさい。
A . void method(void){};
B . void method(int value...){};
C . void method(int... values, String name){};
D . void method(int... a, int... b){};
E . 選択肢Cと選択肢Dの両方とも正しい。
F . 選択肢はすべて正しい
G . 選択肢はすべて間違っている
【回答】G
Aは論外、Bは変数の後にピリオドが記述されてしまっているためコンパイルエラーとなる。
Cは可変長引数を最後に記述していないため、コンパイルエラー。
Dは可変長引数を2つ記述しているため、1つ目の可変長引数が最後に記述されていないという判定になる。
return文に関する問題
return文は呼び出し元のメソッドに値を戻すという機能の他に、呼び出し元に制御を戻すという機能も備えています。
したがって、voidにてreturnを記述することは可能です。
return文が現れた時点で強制的に制御が戻され、return文以降のメソッドの中の処理は続行されません。
【問題】以下のプログラムをコンパイル、実行したときの結果は?
public void method(int num){
if(num < 0) return;
System.out.println("A");
return;
System.out.println("B");
)
【回答】5行目でコンパイルエラー。
【解説】
4行目で、if文も何も設定されていないreturn文が存在するため、必ずreturnする。
したがって、5行目以降は「到達不可能なコードがある」と判定され、コンパイルエラーとなる。
オーバーロードの問題
【問題】次のメソッドをオーバーロードしていないメソッド定義を選びなさい。(2つ選択)
int calc(double a, int b){
return (int) a + b;
}
A . int calc(int a){}
B . double calc(double a, int b){}
C . int calc(double a, double b){}
D . int calc(double num1, double num2){}
E . int calc(){}
F . int calc(int a, double b){}
【回答】B,D
【解説】
Bはメソッドの戻り値の型が異なっているため、オーバーロードではない。
Dはメソッドの引数の変数名が異なっているため、オーバーロードではない。
オーバーロードとなるのは、引数の数や型、順番が異なるメソッドが該当するということに注意。
コンストラクタの問題
コンストラクタはインスタンス化したときに実行されるメソッドの一種です。
いくつかのルールが存在しています。
1、メソッド名をクラス名と同じにすること
2、戻り値型は記述できない
3、newと一緒にしか使用できない(インスタンス生成時以外は呼び出しができない)
明示的にコンストラクタが記述されない場合はデフォルトコンストラクタが自動生成される。
言い換えるならば、明示的にコンストラクタが記述された場合は、デフォルトコンストラクタが自動生成されない。
【問題】以下のプログラムを実行したとき、どのような結果になるか。
public class Sample {
Sample(){
System.out.println("A");
}
{
System.out.println("B");
}
}
public class Main {
public static void main (String[] args){
Sample s = new Sample();
}
}
【回答】「B」「A」と表示される。
【解説】
クラス内部で定義される初期化子({})はすべてのコンストラクタで共通する前処理を記述します。
なぜ初期化子({})が必要かと申しますと、コンストラクタのオーバーロードで、複数のコンストラクタで共通の処理をするとき、記述が煩雑になるからです。
【ちなみに】
もし、クラス変数を初期化するための何らかの処理を記述したいのであれば、初期化子ではなく、static初期化子を使用する。
public class Sample {
static int num;
static {
num = 10;
}
public Sample(){
num = 100;
}
}
インスタンス化しなくても、ロードされた瞬間にstaticフィールドに値がセットされます。
コンストラクタのオーバーロード問題
【問題】以下のプログラムをコンパイル、実行したときの結果とは?
public class Sample {
public Sample(){
System.out.println("A");
this("B");
}
public Sample(String str){
System.out.println(str);
}
}
【回答】コンパイルエラーになる。
【解説】
オーバーロードされたコンストラクタから、別のコンストラクタを呼び出すにはthisを使用します。
※thisを使用する場合は、呼び出し元のコンストラクタの最初に記述しなければなりません。
上記の問題の場合、thisを最初に記述していないため、コンパイルエラーになります。
アクセス修飾子問題
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);
}
}
【回答】コンパイルエラーとなる。
【解説】
すべてのクラスからアクセス可能なParentを継承したChildクラスは当然のことながら、Parentクラスにアクセスすることができます。
しかしながら、Parentクラスのフィールドはアクセス修飾子が無しになりますので、同じパッケージに属するクラスからしかアクセスできません。
したがって、Childクラスでnumという変数を出したとしても、定義されていないとなり、コンパイルエラーです。
異なるパッケージに属するクラスからフィールドへのアクセス制御に関する問題
package other;
public class Book {
private String isbn;
public void setIsbn(String isbn){
this.isbn = isbn;
}
protected void printInfo(){
System.out.println(isbn);
}
}
package ex27;
import other.Book;
public class StoryBook extends Book{}
package ex27;
public class Main {
public static void main(){
StoryBook story = new StoryBook();
story.setIsbn("xxx-x-xxxxxx-xx-x");
story.printInfo();
}
}
【問題】上記のプログラムをコンパイル、実行した結果は何か?
【回答】コンパイルエラーが発生する。
【解説】
StoryBookクラスのprintinfo()はスーパークラス(Book)にて定義されているのだが、アクセス修飾子がprotectedなので、アクセス可能。
しかしながら、MainクラスはBookクラスを継承していないし、パッケージも異なるため、printinfo()というメソッドにアクセスすることはできない。
したがって、コンパイルエラー。
あくまで、アクセスしようとしているクラスはMainクラスのMainメソッドということを忘れないようにする。
以上です。