演習用フォルダ
ソースファイルは、次のフォルダ内にパッケージごとに保存します。コマンドプロンプトでこのフォルダに移動してから演習を進めてください。
cd /d C:\local-doc\github\pro3\09-a-1C:\local-doc\github\pro3\09-a-1> は現在のフォルダを表すプロンプトです。入力するのは > より後ろのコマンドだけです。1. 学習目標
- Javaにおけるアクセス修飾子を理解し、これを用いたプログラムを作成できる。
- Javaにおけるパッケージを理解し、これを用いたプログラムを作成できる。
目次
- 9.1 パッケージ
- 9.2 アクセス修飾子
参考:このスライドでは扱わない内容
以下の内容はこのスライドでは扱わない。目を通しておく。
- ユニークなパッケージ名
- パッケージ名の生成方法
- スタティックなインポート宣言
参考文献:
『Java言語プログラミングレッスン 第3版(下) オブジェクト指向を始めよう』
- 第17章
- p.211~217
パッケージ(package)とは?
- クラスをグループ分けするための仕組み。
- 同じ名前のクラスを、異なるパッケージに分けて定義できる。
- 同じ名前のクラスがあっても、それぞれを別のものとして扱える。
- そのため、「名前空間を分ける」などともいう。
例
packageA
└── PasswordChecker クラス
packageB
└── PasswordChecker クラスパッケージが異なるので、同じ名前でもそれぞれ異なるクラスとして扱われる。
それぞれを識別するために、
パッケージ名.クラス名で表現する。
これは完全限定名という。
packageA.PasswordChecker
packageB.PasswordChecker以下の3つの要領を理解する。
1. パッケージの中にクラスを作るとき
そのクラスにおいて、
package宣言を記述する。
2. パッケージの中のクラスを使うとき
使う側のクラスにおいて、
import宣言を記述する。
3. import宣言の方法
import 宣言には、
- 完全限定名を使う方法
*を使う方法
がある。
パッケージの実体
パッケージの実体は「フォルダ」である。
これから実装するクラスとパッケージの関係
パッケージ「mytool」
└── MyPen クラス
パッケージ「myfood」
└── MyApple クラス
パッケージ「cooking」
└── Main クラス第9回演習用フォルダ内に、以下のフォルダを作る。
mytool
myfood
cookingMyPen クラスは mytool パッケージ内に作る。
そのため、mytool フォルダ内に以下のソースプログラムを作成する。
MyPen.java
package mytool;
public class MyPen {
public String getName() {
return "Pen";
}
}ポイント
package mytool;によって、パッケージ名を指定する。
MyApple クラスは myfood パッケージ内に作る。
そのため、myfood フォルダ内に以下のソースプログラムを作成する。
MyApple.java
package myfood;
public class MyApple {
public String getName() {
return "Apple";
}
}ポイント
package myfood;によって、パッケージ名を指定する。
Main クラスは cooking パッケージ内に作る。
そのため、cooking フォルダ内に以下のソースプログラムを作成する。
Main.java
package cooking;
import mytool.*;
import myfood.*;
public class Main {
public static void main(String[] args) {
MyPen p = new MyPen();
MyApple a = new MyApple();
System.out.println("I have a " + p.getName());
System.out.println("I have an " + a.getName());
}
}フォルダと各ソースファイルが以下のようになっているか確認する。
09-a-1
├─ mytool
│ └─ MyPen.java
│
├─ myfood
│ └─ MyApple.java
│
└─ cooking
└─ Main.java必ず第9回の演習用フォルダにカレントディレクトリを移動してから行う。
コンパイル
C:\local-doc\github\pro3\09-a-1>javac mytool\MyPen.java
C:\local-doc\github\pro3\09-a-1>javac myfood\MyApple.java
C:\local-doc\github\pro3\09-a-1>javac cooking\Main.java実行
C:\local-doc\github\pro3\09-a-1>java cooking.Main実行結果
I have a Pen
I have an Apple各クラスをコンパイルしてから、cooking.Main を実行する。
解説1:package宣言
package 宣言で、各クラスが属するパッケージを記述した。
Mainクラス
package cooking;
import mytool.*;
import myfood.*;
public class Main {
public static void main(String[] args) {
.....
}
}MyPenクラス
package mytool;
public class MyPen {
public String getName() {
return "Pen";
}
}MyAppleクラス
package myfood;
public class MyApple {
public String getName() {
return "Apple";
}
}Main クラスでは、別のパッケージにある MyPen と MyApple クラスを使うために import 宣言した。
package cooking;
import mytool.*;
import myfood.*;
public class Main {
public static void main(String[] args) {
MyPen p = new MyPen();
MyApple a = new MyApple();
System.out.println("I have a " + p.getName());
System.out.println("I have a " + a.getName());
}
}* を使ったimport
import mytool.*;これは、mytool パッケージに属するすべてのクラスを表す。
MyPenクラスだけを使用する場合
完全限定名を用いて、
import mytool.MyPen;とする。
パッケージは階層化できる。
具体例
mytool パッケージの中に colorpen パッケージを作り、その中に RedPen クラスを作る。
mytool
├── MyPen クラス
└── colorpen
└── RedPen クラス
myfood
└── MyApple クラス
cooking
└── Main クラスmytool フォルダ内に、
colorpenフォルダを作る。
RedPen クラスは colorpen パッケージ内に作る。
そのため、colorpen フォルダ内に以下のソースプログラムを作成する。
RedPen.java
package mytool.colorpen;
public class RedPen {
public String getName() {
return "Red Pen";
}
}パッケージ名
階層化されたパッケージ名は、
mytool.colorpenとなる。
これは、mytool パッケージ内の colorpen パッケージだからである。
パッケージ名は、
.(ドット)で区切る。
RedPen クラスを使う処理を Main クラスに加える。
Main.java
package cooking;
import mytool.*;
import myfood.*;
import mytool.colorpen.*;
public class Main {
public static void main(String[] args) {
MyPen p = new MyPen();
MyApple a = new MyApple();
RedPen r = new RedPen();
System.out.println("I have a " + p.getName());
System.out.println("I have an " + a.getName());
System.out.println("I have a " + r.getName());
}
}フォルダと各ソースファイルが以下のようになっているか確認する。
09-a-1
├─ mytool
│ │ MyPen.java
│ │ MyPen.class
│ │
│ └─ colorpen
│ └─ RedPen.java
│
├─ myfood
│ ├─ MyApple.java
│ └─ MyApple.class
│
└─ cooking
├─ Main.java
└─ Main.class必ず第9回の演習用フォルダにカレントディレクトリを移動してから行う。
コンパイル
C:\local-doc\github\pro3\09-a-1>javac mytool\colorpen\RedPen.java
C:\local-doc\github\pro3\09-a-1>javac cooking\Main.java実行
C:\local-doc\github\pro3\09-a-1>java cooking.Main実行結果
I have a Pen
I have an Apple
I have a Red PenRedPen と Main をコンパイルする。
ほかのクラスは変更していないので、コンパイル不要。
解説3
RedPen クラスの package 宣言時、階層化されたパッケージ名はドット「.」で区切って宣言した。
package mytool.colorpen;
public class RedPen {
public String getName() {
return "Red Pen";
}
}また、
- パッケージの階層関係と一致するようにフォルダを構成する。
- ソースファイルを対応するフォルダに格納する。
という関係になっている。
無名パッケージ
- パッケージに属さない(パッケージがない)ことを無名パッケージという。
Javaには、様々な役割のクラス群がある。
例えば、
Stringクラス- 基本型のラッパークラス
IntegerDouble- など
は、
java.langパッケージに含まれている。
java.langの特徴
java.lang パッケージに限り、import 宣言なしで利用可能。
つまり、java.lang パッケージはJava言語の最も基本的なパッケージとして、暗黙的に使用できる。
- パッケージはクラスをまとめるために使うもの。
- 名前空間を分ける。
- パッケージに入れるクラスでは、
package宣言をする。 - パッケージ内のクラスを使う側では、
import宣言をしてパッケージを指定する。 - パッケージは階層化できる。
- 各クラスファイルは、パッケージ名に応じたフォルダの中に入れる。
アクセス修飾子とは
復習:Javaのもっとも簡単なプログラム
class Hello {
public static void main(String[] args) {
System.out.println("Hello World!");
}
}ここで、
publicと書いている部分について、
public と書くのか?」という疑問が生じる。
Javaには、
- クラス
- フィールド
- メソッド
を必要に応じて隠蔽する機構がある。
これはProcessingでも可能。
この隠蔽する機構をアクセス制御という。
アクセス制御には、以下の3つのアクセス修飾子がある。
publicprotectedprivate
public
- どこからでも呼び出せる。
protected
- 同じパッケージか、そのサブクラスのみ呼び出せる。
private
- 同じクラスからしか呼び出せない。
指定なし
アクセス修飾子を指定しない場合。
- 同じパッケージからしか呼び出せない。
注意
クラス、インタフェースに対しては、
protected
privateは指定不可。
| アクセス修飾子 | 自クラス | 同じパッケージ サブクラス | 同じパッケージ 他クラス | 異なるパッケージ サブクラス | 異なるパッケージ 他クラス |
|---|---|---|---|---|---|
public | ○ | ○ | ○ | ○ | ○ |
protected | ○ | ○ | ○ | ○ | × |
| 指定なし | ○ | ○ | ○ | × | × |
private | ○ | × | × | × | × |
覚え方
アクセス可能な範囲が広い順に並べると、
public
↓
protected
↓
指定なし
↓
privateとなる。
ただし、protected は異なるパッケージのサブクラスからアクセス可能という点に注意する。
条件
MyPenはpublicMyAppleはアクセス修飾子を指定しない
という場合を考える。
パッケージ構成
mytool
└── public指定の MyPen クラス
myfood
└── アクセス指定なしの MyApple クラス
cooking
└── Main クラス結果
MyPen クラスは Main から呼び出せる。
一方、
MyApple クラスは Main から呼び出せない。
理由は、MyApple が myfood パッケージに属しているのに対して、Main は cooking パッケージに属しており、MyApple にアクセス修飾子が指定されていないためである。
MyApple クラスのアクセス修飾子を指定なしにしてみる。
変更後の MyApple.java
package myfood;
class MyApple {
public String getName() {
return "Apple";
}
}変更点は、
public class MyAppleから
class MyAppleにしたこと。
つまり、
publicを削除する。
必ず第9回の演習用フォルダにカレントディレクトリを移動してから行う。
コンパイル
C:\local-doc\github\pro3\09-a-1>javac myfood\MyApple.java実行
C:\local-doc\github\pro3\09-a-1>java cooking.Main実行結果
Exception in thread "main" java.lang.IllegalAccessError:
tried to access class myfood.MyApple from class
cooking.Main
at cooking.Main.main(Main.java:9)結果
MyApple クラスへのアクセスについてのエラーが表示される。
これは、MyApple がアクセス修飾子なしになったことで、異なるパッケージにある cooking.Main からアクセスできなくなったため。
条件
MyApple が public クラスであっても、そのメソッドが private の場合は呼び出せない。
つまり、
MyAppleクラス自体 → public
getNameメソッド → privateという状態では、
MyAppleクラス自体を呼び出すことはできる。- しかし、
getName()メソッドを呼び出すことはできない。
パッケージ構成
mytool
└── public指定の MyPen クラス
myfood
├── public指定の MyApple クラス
└── private指定の getName メソッド
cooking
└── Main クラスMyPen クラスは Main から呼び出せる。
MyApple クラスを呼び出せても、private 指定されたメソッドは呼び出しできない。
MyApple クラスを public に戻し、
getNameメソッドを private にしてみる。
MyApple.java
package myfood;
public class MyApple {
private String getName() {
return "Apple";
}
}変更内容
クラス:
class MyAppleから
public class MyAppleへ戻す。
メソッド:
public String getName()から
private String getName()へ変更する。
コンパイル
C:\local-doc\github\pro3\09-a-1>javac myfood\MyApple.java実行
C:\local-doc\github\pro3\09-a-1>java cooking.Main実行結果
I have a Pen
Exception in thread "main" java.lang.IllegalAccessError:
tried to access method
myfood.MyApple.getName()Ljava/lang/String; from class
cooking.Main
at cooking.Main.main(Main.java:12)結果の確認
MyPen クラスのメソッド実行は成功しているので、
I have a Penと正しく表示される。
一方、MyApple クラスのメソッド実行では、アクセスエラーが発生した旨が表示される。
つまり、
MyAppleクラス → publicなのでアクセス可能
getNameメソッド → privateなのでアクセス不可となる。
アクセス修飾子の種類
3つある。
publicprotectedprivate
ただし、アクセス修飾子の指定には「省略」を含めて4パターンある。
publicprotectedprivate- 指定なし
アクセス制御により、カプセル化を実現できる。
カプセル化によって、
- オブジェクト内部の実装を隠蔽する
- より安全なプログラムを作る
- バグの少ないプログラムを作る
ことができる。
全体まとめ
パッケージ
package- クラスをグループ分けする仕組み。
- 名前空間を分けることができる。
- 同じクラス名でも、異なるパッケージなら別のクラスとして扱える。
- パッケージの実体はフォルダ。
- クラスをパッケージに所属させるには
package宣言を使う。 - 別パッケージのクラスを利用するには
import宣言を使う。 import mytool.*;のように*を使うことができる。import mytool.MyPen;のように完全限定名で指定することもできる。- パッケージは階層化できる。
- 階層化されたパッケージ名は
.で区切る。 - パッケージの階層関係と一致するようにフォルダを構成する。
- パッケージに属さない場合は無名パッケージという。
java.langはJavaの最も基本的なパッケージで、import宣言なしで暗黙的に利用できる。
アクセス制御
Javaでは、
- クラス
- フィールド
- メソッド
を必要に応じて隠蔽できる。
この仕組みをアクセス制御という。
アクセス修飾子は、
public
protected
privateの3種類。
さらに、アクセス修飾子を指定しない指定なしを含めると4パターン。
アクセス範囲
| 指定 | アクセス可能な範囲 |
|---|---|
public | どこからでも |
protected | 同じパッケージ、またはサブクラス |
| 指定なし | 同じパッケージ |
private | 同じクラスのみ |
※クラス・インタフェースには protected、private は指定できない。
カプセル化
アクセス制御によってカプセル化を実現できる。
その結果、
という利点がある。