目的
クラスを集め、カプセル化するパッケージを使えるようにし、また前回までに習ったクラスについても出来るようにする。
内容
Studentクラス、Teacherクラスを同じパッケージに作り、Student、Teacherをメンバーとして持つCouseworkクラスを作り、違うパッケージでこれらをimportするクラスを作る。
ソースプログラム
以下にStudentクラス、Teacherクラス、Couseworkクラス、Tester3クラスのソースプログラムを以下に示す。
Studentクラス
package lesson9;
import java.util.Random;
public class Student {
private static int counter;//クラス変数
private int id;//インスタンス変数
private int english;//インスタンス変数
private int math;//インスタンス変数
private int physics;//インスタンス変数
//クラス初期化子 役割:クラスが呼ばれると一番最初に呼ばれる。1回だけ!!!
static{
Random rand=new Random();
counter=rand.nextInt(100)+1;//主席番号をランダムにだす
}
//インスタンスメソッド 役割:他のクラスから変数idを取得するときidは非公開のインスタンス変数で直接呼び出せないから!!
public int getId(){
return id;
}
public int getEnglish(){
return english;
}
public int getMath(){
return math;
}
public int getPhysics(){
return physics;
}
//インスタンス初期化子 役割:インスタンスが生成されてコンストラクタが呼ばれるたびに一番最初に処理が行われる。
{
id=++counter;
}
//コンストラクタ (コピーも含む)
public Student(){ //引数なしのコンストラクタ
System.out.println(“引数なしのコンストラクタ”);
}
public Student(int english){ //引数1つコンストラクタ
this.english=english;
System.out.println(“引数1つのコンストラクタ”);
}
public Student(int english,int math){ //引数2つコンストラクタ
this(english);
this.math=math;
System.out.println(“引数2つのコンストラクタ”);
}
public Student(int english,int math,int physics){ //引数3つのコンストラクタ
this(english,math);
this.physics=physics;
System.out.println(“引数3つのコンストラクタ”);
}
public Student(Student s){ //コピーコンストラクタ
this(s.english,s.math,s.physics);
System.out.println(“コピーコンストラクタ”);
}
public String toString(){
return String.format(“英語:%3d点 数学:%3d点 物理:%3d点”,english,math,physics);
}
}
Teacherクラス
package lesson9;
public class Teacher {
private Student s;//Student型変数s・インスタンス変数
private int a;
private int b;
private int c;
private double GPA;//全体のGPA
private double gpas;
private double gpa1;//英語のGPA
private double gpa2;//数学のGPA
private double gpa3;//物理のGPA
//クラス初期化子 役割:クラスが呼ばれると一番最初に呼ばれる
static{
System.out.println(“先生がこれから3教科の点数を見てS,A、B、C、Dの5段階評価をします”);
}
//インスタンスメソッド
public void saiten(Student s){
this.s=new Student(s); //引数1~3つのコンストラクタとコピーコンストラクタが呼ばれる 仮引数がクラス型だから!!
//*\注意:this.s1=s1 はだめ!!
//System.out.println(“テストの点数は”);
//System.out.println(“英語:”+s.getEnglish());
//System.out.println(“数学:”+s.getMath());
//System.out.println(“物理:”+s.getPhysics());
//System.out.println(“です”);
a=s.getEnglish();
b=s.getMath();
c=s.getPhysics();
System.out.print(“英語の評価は”);
hantei(a);
gpa1=gpas;
System.out.print(“数学の評価は”);
hantei(b);
gpa2=gpas;
System.out.print(“物理の評価は”);
hantei(c);
gpa3=gpas;
GPA=(gpa1+gpa2+gpa3)/3;
System.out.printf(“3教科のGPAは%6.3f”,GPA);
System.out.println(“”);
}
//インスタンスメソッド
public void hantei(int t){
if(t>=90){
System.out.println(“S”);
gpas=4;
}
if(t>=80&&t<90){
System.out.println(“A”);
gpas=3;
}
if(t>=70&&t<80){
System.out.println(“B”);
gpas=2;
}
if(t>=60&&t<70){
System.out.println(“C”);
gpas=1;
}
if(t<60){
System.out.println(“D”);
gpas=0;
}
}
}
Couseworkクラス
package lesson9;
public class Cousework {
private Student st;
//private Teacher te;
//コンストラクタ
public Cousework(Student s){ // s=new Student(100,100,90)
this.st=new Student(s);//コピーコンストラクタ
//this.te=new Teacher(te);
}
public Student getSt(){
return new Student(st);//コピーコンストラクタ 引数1,2,3のコンストラクタ呼ばれ次にコピーコンストラクタ
}
}
Tester3クラス
package lesson9.id;
import lesson9.Teacher;
import lesson9.Student;
import lesson9.Cousework;;
public class Tester3 {
public static void main(String[] args){
Teacher t2=new Teacher();//インスタンス生成
Cousework c=new Cousework(new Student(100,100,90));//インスタンス生成
//①Studentクラスのコンストラクタが呼ばれStudentクラスが初期化される。
//②s=new Student(100,100,90)
//Cousework(Student s)=Cousework(new Student(100,100,90))
System.out.println(“今回のテストの学年最高点は”+c.getSt());
System.out.println(“今日はある生徒3人分の英語、数学、物理のテストの点数を見ていきます”);
System.out.println(“1人目の生徒の点数”);
Student s1=new Student(80,70,60); //例 50
System.out.println(“出席番号”+s1.getId());
System.out.println(“点数”);
System.out.println(“英語:”+s1.getEnglish());
System.out.println(“数学”+s1.getMath());
System.out.println(“物理”+s1.getPhysics());
t2.saiten(s1); //51
System.out.println(“”);
System.out.println(“2人目の生徒の点数”);
Student s2=new Student(50,100,90); //52
int x=s2.getId()-1;
System.out.println(“出席番号”+x);
System.out.println(“点数”);
System.out.println(“英語:”+s2.getEnglish());
System.out.println(“数学”+s2.getMath());
System.out.println(“物理”+s2.getPhysics());
t2.saiten(s2);
System.out.println(“”);
System.out.println(“3人目の生徒の点数”);
Student s3=new Student(60,40,90); //53
int y=s3.getId()-2;
System.out.println(“出席番号”+y);
System.out.println(“点数”);
System.out.println(“英語:”+s3.getEnglish());
System.out.println(“数学”+s3.getMath());
System.out.println(“物理”+s3.getPhysics());
t2.saiten(s3); //54
System.out.println(“”);
}
}
実行結果
3のソースプログラムの実行結果を以下の図1、2に示す。


考察
今回のプログラムはクラスが4つあるが、呼び出される順としては、mainメソッドをもつTester3クラス。Tester3クラスの最初の箇所に
import lesson9.Teacher;
import lesson9.Student;
import lesson9.Cousework;;
とあるがこれは、パッケージlesson9のTeacherクラス、Studentクラス、Couseworkクラスを別のパッケージlesson9.idのTester3クラスに使えるように宣言したもの。ただし、正しく使えるよう、Teacherクラス、Studentクラス、Couseworkクラスの3つクラスはpublicクラスとして宣言しなければならない。
Main文の最初にTeacher t2=new Teacher();とTeacherクラス型インスタンスを生成した為Teacherクラスのコンストラクタが呼び出せれるが、teacherクラスにはクラス初期化子が定義されているので、まずクラス初期化子が一番最初に呼び出される。今回のクラス初期化子はSystem.out.println(“先生がこれから3教科の点数を見てA、B、C、Dの5段階評価をします”);とあるので、これを出力する。今回、コンストラクタは定義してない為、Tester3クラスに戻る。次は、Cousework c=new Cousework(new Student(100,100,90));とCouseworkクラス型インスタンスを生成している。ここではまず、実行結果から見ても分かる通りnew Student(100,100,90)の部分でStudentクラス型インスタンスが生成されてStudentクラスのコンストラクタが呼び出される。This(・・・)を使って同一クラス内のコンストラクタの呼び出しをしている為、引数1つのコンストラクタから引数3つのコンストラクタの3つが呼び出されている。コンストラクタの初期化が終わったら、Couseworkのコンストラクタが呼ばれる。Couseworkのコンストラクタの仮引数sはs=new Student(100,100,90)となっている。コンストラクタ内のthis.st=new Student(s)のstはフィールドのインスタンス変数private Student stだ。new Student(s)はコピーコンストラクタを呼び出している。つまり、コンストラクタでテストの最高得点を設定する部分で、ここではnew演算子とコピーコンストラクタを用いて最高得点のインスタンスを生成している。仮引数stに受け取った得点のコピーを作って、そのコピーへの参照をフィールドStに代入している。その為、フィールドstは、仮引数に受け取った得点のコピーを参照することになる。もし、ここでmewによってインスタンスを生成せず、this.st=st;という単純な代入になると参照先が同じになってしまい、書き換えられてしまう可能性がある。
次に、プログラムは最高点をc.getSt()によって出力している。メソッドgetSt()の中の処理を見てみると、return new Student(st);と書いてあるが、この部分をreturn new st;と書くとメソッドgetStは、最高得点フィールドそのものへ参照を返却する。返却された参照を通じて、外部から最高得点フィールドの値が書き換えられる可能性がある。return new Student(st)と書くとメソッドgetStは、最高得点フィールドのコピーへの参照を返却する。返却された参照を通じて、外部から最高得点フィールドの値が書きかえられることはなくなる為、return new Student(st);と書いた。ここから言えることは、参照値を通じて外部から間接的に値を書き換えられてしまう為、不用意に参照型フィールドの値を返却してはいけない。「今日はある生徒3人分の英語、数学、物理のテストの点数を見ていきます」、「1人目の生徒の点数」をSystem.out.printlnで出力して、Studentクラス型インスタンスの生成 Student s1=new Student(80,70,60); を行いStudentクラスの引数1つのコンストラクタから引数3つコンストラクタを呼び出し初期化する。さらに、これによってStudentクラスが呼びだされたということで、クラス初期子が呼ばれクラス変数counterに乱数を利用しランダムに値を代入する。今回は、rand.nextInt(100)+1;なので1~100の数をランダムに代入する。ここで、もしcounterがクラス変数でなくインスタンス変数の場合は使えない。理由はクラス初期化子は、主にクラス変数を初期化するものであり、インスタンス変数を初期化することはできないからだ。また、インスタンス初期化子がある為、コンストラクタの中の処理で1番最初に行われる。今回は主席番号を調べるために、id=++counter;と書いてある。インスタンス初期化子は、インスタンス変数はもちろんクラス変数も使える。ここで、クラス変数counterは現時点で何番までの主席番号を与えたのかを表しており、クラスStudent型を利用するプログラム内で、Student型のインスタンスが幾つ生成されても(たとえ1個も生成させなくても)、そのクラスに所属するクラス変数の実体は、1個だけ作られる。インスタンス変数idは個々のインスタンスの主席番号を表していて、静的でないフィールドの実体は、個々のインスタンスの1部である。その後のプログラムは、主席番号、英語、数学、物理の得点を、クラス型変数とインスタンスメソッドを使って出力している。
その次のt2.saiten(s1);はsaitenメソッドに引数s1,つまりnew Student(80,70,60);を代入して各教科の得点に応じてS~Dまでの評価を与え、3教科のGPAを出すプログラムだ。詳しく見ていくと、まず仮引数s=クラス型変数s1=new Student(80,70,60)でnew Student(s)は、コピーコンストラクタで、ここで引数1~3つコンストラクタとコピーコンストラクタが呼ばれる。もしthis.s1=s1を書いてしまうと、先ほど述べた通り書き換えられてしまう可能性がある為、this.s1=s1と書いてはいけない。このままsaitenメソッドを見ていくと、次に3教科の得点をそれぞれa,b,cに代入する。そして、a,b,cをhanteiメソッドの引数ととり、hanteiメソッドで得点に応じてS~Dの評価を出力し、インスタンス変数gpasに評価ごとのGPAを与える。そして、3教科ごとにgpasをgpa1,gpa2,gpa3に代入する。そして、インスタンス変数GPAをGPA=(gpa1+gpa2+gpa3)/3;と計算し、出力してsaitenメソッドは終了だ。
プログラムはmain文に戻り、「2人目の生徒の点数」も「1人目の生徒の点数」とほぼ同じプログラムだが、唯一違うとこは主席番号を出すint x=s2.getId()-1; のとこである。こうしないとプログラムのコメント文に書いた通り1人目の生徒の出席番号の次の番号が出てこないからだ。なぜこうなったかというと、1人目の3教科の評価とGPAを出すt2.saiten(s1);のsaitenメソッドの中のthis.s=new Student(s);でStudentクラスのコンストラクタが呼び出されたらインスタンス初期化子が呼ばれ、id=++counter;となったからだ。「3人目の生徒の点数」も同じでちゃんと出席番号が続くようにint y=s3.getId()-2;とした。後の処理も、他と同様で他と同じである。
まとめ
パッケージは、クラスやインタフェースなどの型を集めたものである。パッケージは階層構造をもつことが出来、その配置はディレクトリと対応する。パッケージ名は小文字とする。広く公開するパッケージには、インターネットアドレスを逆順に並べることによって一意なパッケージ名を与える。publicクラスは、パッケージの中からも外からもアクセスできる。publicでないクラスは、パッケージの中でのみアクセスできる。1個のソースプログラム内で宣言できるpublicクラスは、最大1個である。
コメントを残す