【Java入門】第11回 アクセス修飾子とスコープ|Javaオブジェクトのアクセス可能範囲
2024.06.14
Javaプログラム内の変数やメソッド、クラスにはアクセスできる範囲が定められており、他のプログラムやメソッドからいつでも参照できるわけではありません。
今回はこれらにアクセス可能な範囲や、範囲を指定する方法について説明します。
目次
アクセス修飾子
アクセス修飾子とは?
Javaでは、クラスや変数へのアクセス可能な範囲を指定できる単語が定められており、この単語のことを「アクセス修飾子」と呼びます。
アクセス修飾子は、クラスおよびクラス内のメンバ(変数、メソッド)の定義時に、その前方に記述することで、これらに対して「どの範囲から利用することが可能か」を指定する修飾子です。
同時に、アクセス修飾子はメソッドやメンバ変数の存在確認の可否(可視性)を定めるものでもあります。
アクセス修飾子
アクセス修飾子一覧
Javaのアクセス修飾子は「public」「private」「protected」の3種類が準備されています。
設定の状態としては、修飾子を省略した場合を含めて、4つの状態があります。
各アクセス修飾子の基本的な考え方は以下の表のとおりです。
アクセス修飾子 | 説明(アクセス可能な範囲) |
public | アクセス制限なし(公開) |
private | 自クラス内からのみ可能 |
protected | 自クラスおよびサブクラスからのみ可能 |
(なし) | 対象のクラスと同一パッケージ内のクラスからのみ可能 |
Javaオブジェクトと指定可能なアクセス修飾子
クラスには基本的に「public」か、アクセス修飾子なしのいずれかでのみ宣言が可能です。
(インナークラスという「クラス内に作成するクラス」ではその他の2つも設定できますが、ここでは割愛します)
クラスのメンバ(メソッドおよびメンバ変数)については、全てのアクセス修飾子を設定、またはアクセス修飾子なしでの宣言が可能です。
これらに対して、メソッド内に宣言する変数(ローカル変数)については、アクセス可能な範囲がメソッド内に限定されますので、アクセス修飾子の設定はできません。
アクセス修飾子 | クラス | メソッド | 変数 (メンバ変数) | 変数 (ローカル変数) |
public | 〇 | 〇 | 〇 | × |
private | △(※1) | 〇 | 〇 | × |
protected | △(※1) | 〇 | 〇 | × |
(なし) | 〇 | 〇 | 〇 | 〇 |
クラスとアクセス修飾子
一般的なクラスには「publicを指定」「アクセス修飾子なし」のいずれかのみが定義可能です。
アクセス修飾子を省略した場合は、同一のパッケージ内のクラスからのみ参照可能なクラスになります。
アクセス修飾子 | 同一パッケージ内のクラス | パッケージ外のクラス |
public | 〇 | 〇 |
(なし) | 〇 | × |
メソッド・メンバ変数とアクセス修飾子
メソッドやクラスのメンバ変数には、全てのアクセス修飾子が設定可能です。
「private」を指定した場合は自クラス内からのみアクセスが可能、「protected」を指定した場合は自クラスおよびサブクラスからのみアクセスが可能です。
アクセス修飾子を指定しなかった場合は、クラス同様に同一パッケージ内からのみアクセスが可能になります。このため、たとえサブクラスであっても外部のパッケージに作成したクラスからはアクセス不可(不可視)になる点に注意してください。
アクセス修飾子 | 同一クラス内 | 同一パッケージ内 | サブクラス | 全ての範囲 |
public | 〇 | 〇 | 〇 | 〇 |
private | 〇 | × | × | × |
protected | 〇 | 〇 | 〇 | × |
(なし) | 〇 | 〇 | △(※2) | × |
変数のスコープ
スコープとは?
アクセス修飾子はクラス、メソッド、メンバ変数にアクセス可能な範囲を設定することができました。
対して、ローカル変数はアクセス修飾子を付加できませんが、メソッド内でアクセス可能な範囲が決められています。
この「変数にアクセス可能な範囲」のことを「スコープ」と呼びます。
変数のスコープ
クラス内に定義されたメンバ変数と、メソッド内で宣言されたローカル変数では、アクセス可能な範囲が異なります。
また、ローカル変数はアクセス修飾子が設定できませんが、同一メソッド内であってもアクセス可能な範囲が決められています。
メンバ変数のスコープ
メンバ変数にアクセス可能な範囲は、アクセス修飾子によって決まります。
1.同一クラス内からのアクセス
クラス内に定義したメンバ変数は、どのアクセス修飾子が設定されている場合でも、同一クラス内であればどこからでもアクセスが可能です。
2.サブクラス内からのアクセス
サブクラスでもアクセス可能なアクセス修飾子(publicまたはprotected)が付いたメンバ変数であれば、サブクラス内でも同様にどこからでもアクセスすることができます。
また、アクセス修飾子が付加されていないメンバ変数は、同一パッケージ内のサブクラスに限り、どこからでもアクセスすることが可能です。
3.外部クラスからのアクセス
クラス外部からのメンバ変数へのアクセスは、対象のメンバ変数に付加されたアクセス修飾子の設定に従います。
ローカル変数のスコープ
メソッド内で宣言されたローカル変数は、そのメソッド内でしか利用できません。
まず、メソッド内に宣言された変数にアクセスできるのは、その変数を宣言した以降の、メソッド内のコードに限ります。
また、メソッド内であっても、変数が宣言されている階層(「{」と「}」で囲まれた範囲)の外側では、宣言した以降の場所であってもアクセスできません。
Javaでは、この「{」と「}」で囲まれた範囲のことを「ブロック」と呼びます。
{
// ブロックの範囲はここから
int x = 0;
// ブロックの範囲はここまで
}
// ブロックの外側では x にはアクセスできない
ブロック内で宣言した変数は、ブロックの外側からアクセスすることができませんが、ブロックの内側に作成した下位のブロックからは、上位のブロックで宣言されている変数にアクセスすることができます。
下記のサンプルコードでは、以下の5つの変数を宣言して、アクセス可能な範囲を確認します。
・int x1
mainメソッドの直下の階層で宣言した変数(9行目)
・int x2
mainメソッドの中にブロック(1)を作成して、その内側で宣言した変数(12行目)
・int x3
ブロック(1)の中に、さらにブロック(2)を作成して、その内側で宣言した変数(15行目)
・int x4
mainメソッドの直下の階層だが、ブロック(1)(2)が閉じられた後に宣言した変数(35行目)
・int member
ScopeCheckクラスのメンバ変数として宣言した変数(46行目)
サンプルコードでは、各ブロック内で上記5個の変数の値を出力します。コメントアウトされている行は、変数が利用できない位置を示します。
/** スコープ確認用クラス */
public class ScopeCheck {
/**
* mainメソッド
* @param args
*/
public static void main(String[] args) {
int x1 = 10;
{ // ここからブロック(1)
int x2 = 100;
{ // ここからブロック(2)
int x3 = 1000;
System.out.println(member);
System.out.println(x1);
System.out.println(x2);
System.out.println(x3);
//System.out.println(x4); // x4 は宣言前
// ブロック(2)ここまで
}
System.out.println(member);
System.out.println(x1);
System.out.println(x2);
//System.out.println(x3); // x3 はアクセス不可(ブロック外)
//System.out.println(x4); // x4 は宣言前
// ブロック(1)ここまで
}
int x4 = 10000;
System.out.println(member);
System.out.println(x1);
// System.out.println(x2); // x2 はアクセス不可(ブロック外)
// System.out.println(x3); // x3 はアクセス不可(ブロック外)
System.out.println(x4);
}
/** メンバ変数 */
int member = 1;
}
前述のとおり、メンバ変数は記述した場所に関わらず、同一クラス内のどこからでもアクセスが可能です。このため、変数memberはmainメソッドよりも下に記述されていますが、上に記述されたmainメソッド内からでもアクセスできます。
次に、mainメソッドの開始直後にメソッド直下の階層で宣言した変数 x1 は、mainメソッド内の以降のコード内のどこからでもアクセスできます。
一方、mainメソッド内に作成したブロックの中で宣言した変数(x2、x3)や、ブロックの後で宣言した変数(x4)は、ブロックの外側や、変数を宣言するより前のブロックからはアクセスできません。
[変数x2のスコープ]
変数x3のスコープは宣言されているブロック(1)内になります。
ブロック(1)の内部にあるブロック(2)の内側からでもアクセスすることが可能です。
{ // ここからブロック(1)
int x2 = 100;
{ // ここからブロック(2)
int x3 = 1000;
System.out.println(member);
System.out.println(x1);
System.out.println(x2);
System.out.println(x3);
//System.out.println(x4); // x4 は宣言前
// ブロック(2)ここまで
}
System.out.println(member);
System.out.println(x1);
System.out.println(x2);
//System.out.println(x3); // x3 はアクセス不可(ブロック外)
//System.out.println(x4); // x4 は宣言前
// ブロック(1)ここまで
}
[変数x3のスコープ]
変数x3のスコープは宣言されているブロック(2)内だけになります。
{ // ここからブロック(2)
int x3 = 1000;
System.out.println(member);
System.out.println(x1);
System.out.println(x2);
System.out.println(x3);
//System.out.println(x4); // x4 は宣言前
// ブロック(2)ここまで
}
メソッド内でブロックを作成するケースとしては、条件分岐(if文)や、ループ処理(for文)などで利用するのが主な利用方法です。
for文と組み合わせて利用する場合は、for文の初期化式で宣言した変数も、組み合わせたブロックの終了までがアクセス可能な範囲になります。
下記のサンプルでは、変数countは直後のブロック内でのみアクセスが可能な変数になります。
for( int count = 0; count < 10; count++)
{
// このブロック内では変数 count にアクセス可能
System.out.println(count);
}
//これ以降はcountにはアクセスできない