ここでは、オブジェクト指向の三大要素のうちのカプセル化
について説明します
パッケージ
パッケージとは、クラスやインターフェースなどのファイルをグループ化して管理する仕組みのことです
パッケージを使うことで、以下のようなメリットがあります
- 名前の衝突を回避できる
同じ名前のクラスでも、異なるパッケージに所属していれば区別できます
これにより、クラス名の衝突を気にせずに開発を進めることができます - コードの可読性・保守性が向上する
関連するクラスをまとめてパッケージにすることで、コードの構造が整理され、可読性や保守性が向上します - アクセス制限を設けられる
パッケージ単位でクラスの可視性を制御できます
これにより、外部に公開したくないクラスを隠蔽したり、特定のクラスからのアクセスのみを許可したりすることができます - 再利用性が高まる
パッケージとしてまとめることで、他のプロジェクトや開発者との間でコードを共有しやすくなります
パッケージの作成
パッケージを作成するには、ファイルの先頭にpackage
文を記述します
例えば、pkgA
というパッケージを作る場合は、以下のように記述します
package pkgA;
同一ファイル内にあるすべてのクラスがこのパッケージに分類されます
パッケージは階層化することもできます
階層化するには、.
(ピリオド)でパッケージ名をつなげます
例えば、pkgA
パッケージの中にpkgB
というパッケージを作る場合は、以下のように記述します
package pkgA.pkgB;
importの宣言
パッケージに属するクラスを使うには、import
文を使ってパッケージ名とクラス名を指定します
例えば、pkgA
パッケージのPkgTestA
クラスを使う場合は、以下のように記述します
import pkgA.PkgTestA;
import
文には*
を使って、パッケージ内のすべてのクラスを一括で指定することもできます
例えば、pkgA
パッケージ内のすべてのクラスを使う場合は、以下のように記述します
import pkgA.*;
また、パッケージはディレクトリ構造に対応しています
たとえば、下図のようなフォルダ構成の場合のサンプルプログラムを提示します

・src\pkgA\pkgB\PkgTestB.java
package pkgA.pkgB;
public class PkgTestB {
public void test(){
System.out.println("in PkgTestB()");
}
}
・src\pkgA\PkgTestA.java
package pkgA;
public class PkgTestA {
public void test(){
System.out.println("in PkgTestA()");
}
}
・src\PkgTest1.java
import pkgA.PkgTestA;
import pkgA.pkgB.PkgTestB;
public class PkgTest1 {
public static void main(String[] args) {
PkgTestA a = new PkgTestA();
PkgTestB b = new PkgTestB();
a.test();
b.test();
}
}
実行結果
in PkgTestA()
in PkgTestB()
Javaのパッケージ例
Javaのパッケージの例を3つ示します
java.lang
パッケージ
Javaの基本的なクラスやインターフェースが入っているパッケージです
例えば、String
やObject
やMath
などがあります
このパッケージは自動的にインポートされるので、明示的に書く必要はありませんjava.util
パッケージ
Javaのユーティリティクラスやインターフェースが入っているパッケージです
例えば、List
やMap
やScanner
などがあります
このパッケージは自動的にインポートされないので、使うときは明示的に書く必要がありますjavax.swing
パッケージ
JavaのGUIコンポーネントやイベントハンドラが入っているパッケージです
例えば、JFrame
やJButton
やActionListener
などがあります
このパッケージも自動的にインポートされないので、使うときは明示的に書く必要があります
カプセル化
クラスの内部のデータを保護するため、外部からのアクセスを制限する概念をカプセル化
といいます
クラスの利用者は、公開されているメソッドにアクセスすることで、クラスの機能を利用することができます
カプセル化により、クラスの利用者は内部の構造を理解しなくてもクラスの機能を利用することができたり、外部からの間違ったアクセスを未然に防ぐことができます
Javaでは、public、protected、privateなどのアクセス修飾子やstatic修飾子、final修飾子を指定して実現します
アクセス修飾子
アクセス修飾子は、クラスやメンバー、関数などを、どの範囲から参照可能かを制御するのに用いられます
クラス、インタフェース、メソッド、コンストラクタ、メンバ変数の修飾子として利用できます
アクセス修飾子の一覧は次の通りです
アクセス修飾子 | 呼び名 | 意味・機能 |
---|---|---|
public | パブリック | ほかのどのクラスからでも呼び出せる |
protected | プロテクティッド | サブクラスまたは同じパッケージ内のクラスからしか呼び出せない |
なし | デフォルト | 同じパッケージ内からしか呼び出せない |
private | プライベート | 同じクラス内からしか呼び出せない |
public公開範囲

protected公開範囲

デフォルト公開範囲

private公開範囲

Java基礎編7日目で提示したPersonクラスの変数をカプセル化したサンプルプログラムを以下に示します
package sample;
// ⼈クラス
class Person {
// クラスで使⽤するデータの「名前」と「年齢」をフィールドとして定義する
// 同⼀クラス内でのみアクセス可能
private String name;
private int age;
// コンストラクタ(引数つき)
Person (String name, int age) {
this.name = name;
this.age = age;
}
// name変数のセッター⇒外部から値を書き込むための関数
public void setName(String name) {
this.name = name;
}
// age変数のセッター⇒外部から値を書き込むための関数
public void setAge(int age) {
this.age = age;
}
// name変数のゲッター⇒外部から値を取得するための関数
public String getName() {
return this.name;
}
// age変数のゲッター⇒外部から値を取得するための関数
public int getAge() {
return this.age;
}
// ⾃⼰紹介をするメソッド
public void introduce () {
System.out.println ("私の名前は" + name + "です。" + age + "歳です。");
}
}
public class Sample{
public static void main(String[] args) {
// ⼈クラスのインスタンスを⽣成する
Person person = new Person ();
person.setName("⼭⽥太郎");
person.setAge(20);
System.out.println ("私の名前は" + person.getName() + "です。" + person.getAge() +"歳です。");
person.introduce();
}
}
実⾏結果
私の名前は⼭⽥太郎です。20歳です。
私の名前は⼭⽥太郎です。20歳です。
このサンプルプログラムでは、フィールドの変数をprivateとして定義しています
private String name;
private int age;
privateと宣言しているため、クラス外からは直接アクセスすることはできません
外部からこのクラスのname、ageに値をセットする関数、値を取得する関数をpublicで用意しています
もし、ageを直接変更できるようにすると、1000などと年齢とはほど遠い値も入ってしまいます
しかし、setAge関数内で範囲外の値のときはエラーを返すなどとしておけば、間違った値の代入を防ぐことができ、データを保護することができるようになります
static修飾子
static修飾⼦は静的メンバーを定義する修飾⼦のことで、メソッド、フィールドに付加することができます
staticで修飾されたフィールドをクラス変数、メソッドをクラス(または静的)メソッドと呼びます
⼀⽅、staticが付加されない場合は、インスタンス変数、インスタンスメソッドと呼びます
クラス変数、クラスメソッドはクラスのインスタンスを⽣成しなくても呼び出すことが出来るという特徴があります
以下にサンプルプログラムを示します

上の図は、以下のサンプルプログラムのクラス変数、クラスメソッド、インスタンス変数、インスタンスメソッドについて説明しています
class Test{
static int staticNum = 0; // クラス変数⇒static領域に保存され共有される
int num=0; // インスタンス変数⇒インスタンスが生成されたときヒープ領域に保存される
}
public class Sample{
public static void main(String[] args){
System.out.println("staticNum=" + Test.staticNum);
Test t1 = new Test();
t1.num = 1;
t1.staticNum = 1;
Test t2 = new Test();
t2.num = 2;
t1.staticNum = 2;
System.out.println("t1.num=" + t1.num);
System.out.println("t2.num=" + t2.num);
System.out.println("t1.staticNum=" + t1.staticNum);
System.out.println("t2.staticNum=" + t2.staticNum);
System.out.println("Test.staticNum=" + Test.staticNum);
}
}
実行結果
staticNum=0
t1.num=1
t2.num=2
t1.staticNum=2
t2.staticNum=2
Test.staticNum=2
System.out.println("staticNum=" + Test.staticNum);
クラス変数のため、staticNumはクラス名.変数名
でアクセス可能です
一方インスタンス変数はクラス名.変数名
でアクセスできないため
System.out.println("staticNum=" + Test.num);
このように記述するとエラーになります
t1.num = 1;
t2.num = 2;
それぞれのインスタンス変数に値を代入しているため、値は異なります
一方、
t1.staticNum = 1;
t1.staticNum = 2;
System.out.println("t1.staticNum=" + t1.staticNum);
System.out.println("t2.staticNum=" + t2.staticNum);
System.out.println("Test.staticNum=" + Test.staticNum);
static領域にあるため共有され、最後に代入した値が表示されます
以下は、インスタンスメソッドとクラスメソッドを使用したサンプルプログラムです
class Test{
private int num=0;
public static int add(int a,int b){ // クラスメソッドの定義
return a+b;
}
public int getNum(){ // インスタンスメソッドの定義
return num;
}
}
public class Sample{
public static void main(String[] args){
int x;
x = Test.add(1,2);
System.out.println("add="+x);
// x = Test.getnum(); // コンパイルエラーになる
Test t = new Test();
x = t.getNum();
System.out.println("getnum="+x);
}
}
実行結果
add=3
getnum=0
クラスメソッドは、クラス変数同様にクラス名.メソッド名
でアクセスします
final修飾子
final修飾子は、後から変更してはいけないものに付加します
クラス、メソッド、フィールドそれぞれに付加した場合のふるまいは次のとおりです
クラス | サブクラスが作成できない |
メソッド | サブクラスでオーバーライドできない |
フィールド | 値を変更できない |
クラスにfinal修飾子を付加したサンプルプログラムです
- クラスの継承が禁止されている例
final class Test{
private int num=0;
public int getNum(){
return num;
}
}
class Test1 extends Test{
public int getNum(){
return super.getNum();
}
}
public class Sample{
public static void main(String[] args){
int x;
Test t = new Test();
x = t.getNum();
System.out.println("getnum="+x);
}
}
実行結果
Sample.java:9: error: cannot inherit from final Test
class Test1 extends Test{
^
1 error
- メソッドのオーバーライドが禁止されている例
class Test{
private int num=0;
public final int getNum(){
return num;
}
}
class Test1 extends Test{
public int getNum(){
return super.getNum();
}
}
public class Sample{
public static void main(String[] args){
int x;
Test t = new Test();
x = t.getNum();
System.out.println("getnum="+x);
}
}
実行結果
Sample.java:11: error: getNum() in Test1 cannot override getNum() in Test
public int getNum(){
^
overridden method is final
1 error
- フィールドの値の変更が禁止されている例
class Test{
public static final double PI=3.14;
double r;
Test(double r){
this.r = r;
}
double getArea(){
return this.r * this.r * this.PI;
}
}
public class Sample{
public static void main(String[] args){
double x;
Test t = new Test(2);
Test.PI=3.5;
x = t.getArea();
System.out.println("area="+x);
}
}
実行結果
Sample.java:20: error: cannot assign a value to static final variable PI
Test.PI=3.5;
^
1 error
上記のようにフィールドの値を変更できないようにしたフィールドのことを定数
といいます
定数は、一般的に英大文字で表します
コメント