応用プログラミング 第2回


この授業の目的

オブジェクトの生成方法と,メソッドの呼び出し方法を修得します.


出席メール

出席メールで「授業中の課題の実施」を確認します.メールを出した時刻,PCの場所(アドレス)を確認します.

to:jhoshino@esys.tsukuba.ac.jp
subject: 応用プログラミング 第2回.氏名,学籍番号
本文: なし(書かなくて結構です)


理論

オブジェクトとクラスの関係

クラスはデータの持つべき性質を定義したものです。 たとえば「学生」を表すクラスを設計するとしましょう。 クラスの内部のメンバーとして、 「学年」、「所属学部・学科」、「学籍番号」、「氏名」「年齢」など 多くの情報が定義されるでしょう。 しかし、 クラスそのものは1人1人の学生の実体に対応しているわけではありません。 クラスの情報そのものだけではなく、 個別の情報を記憶した存在が必要となります。 これらの個々のデータのことを、 クラスとは区別して「オブジェクト」と呼ぶことにしましょう。 (ここで言う「オブジェクト」の代わりに、 「インスタンス(instance)」という用語もしばしば用いられます。 以下の説明では、「オブジェクト」は「インスタンス」と同じ意味で用います。 同様に「クラス」は「クラスの定義」と同じ意味だと考えてください。)

クラスとオブジェクトの違いをはっきり意識することは非常に大切です。 工業製品を比喩に用いるならば、クラスは製品の仕様を定めた設計図であり、 オブジェクトが1個1個の製品と言えるでしょう。 一般にプログラム内では、 1つのクラスを元にして複数のオブジェクトが生成され、 利用されるのが普通です。

Javaのプログラムの中では、 次のようにクラスは型としてオブジェクトは変数として表現されます。

 

 

class Person{
String name; //名前
int age; //年齢

Person(String person_name,int person_age){
name = person_name;
age = person_age;
}
}
public class ClassSample{
public static void main(String args[]){
Person person_1 = new Person("John",22);
Person person_2 = new Person("Lisa",32);
System.out.println("Name: "+person_1.name+ " Age: "+person_1.age);
System.out.println("Name: "+person_2.name+ " Age: "+person_2.age);
}
}

データフィールド
←クラスの定義の中で直接宣言されたデータを「クラスのフィールド(field)」 もしくは単に「フィールド」と呼びます.この例では,ストリング型のnameという変数と,int型のageという変数が定義されています.

 

オブジェクトの生成
←オブジェクトの生成は,newコマンドを使って行われます.ここでは,Personというクラスのオブジェクトを生成しています.また,person_1person_2は生成されたオブジェクトです.

 

 


class Person{
String name; //名前
int age; //年齢
Person(String person_name,int person_age){
name = person_name;
age = person_age;
}
}
public class ClassSample{
public static void main(String args[]){
Person person_1 = new Person("John",22);
Person person_2 = new Person("Lisa",32);
System.out.println("Name: "+person_1.name+ " Age: "+person_1.age);
System.out.println("Name: "+person_2.name+ " Age: "+person_2.age);
}
}

コンストラクタ

オブジェクトをするときには,コンストラクタを呼び出します.コンストラクタはクラスの名前と同じです.

<----------
       |
       |
       |
       |
       |
       |
       |
-----------

この例では, Person(String person_name,int person_age)を呼び出して,文字列と数値を代入しています.



class Person{
String name; //名前
int age; //年齢
Person(String person_name,int person_age){
name = person_name;
age = person_age;
}
}
public class ClassSample{
public static void main(String args[]){
Person person_1 = new Person("John",22);
Person person_2 = new Person("Lisa",32);
System.out.println("Name: "+person_1.name+ " Age: "+person_1.age);
System.out.println("Name: "+person_2.name+ " Age: "+person_2.age);
}
}

 

 

 

 

 

変数の呼び出し

変数へアクセスするときには「オブジェクト.変数名」で呼び出します.この場合は,person_1.nameと書くことで,person_1オブジェクトのname変数の値を呼び出すことができます.

 


クラスの継承の意味

「クラスの継承(inheritance)」 はクラスやオブジェクトに次いで重要な概念と言えるでしょう。 ほとんどのオブジェクト指向言語はクラスの継承の考え方を導入しています。 もちろん Javaも例外ではありません。 クラスの継承とはどのようなものなのか? そしてオブジェクト指向の中で どんな働きをするのか解説していくことにしましょう。 もっとも、あまり難しく考え込む必要はありません。 この後で述べるようにクラスの継承のアイディア自体は、 きわめて自然で合理的なものだからです。
クラスはその内部にオブジェクトを構成する要素持っています。 Javaの場合ならば、クラス定義内に記述されるメンバー、 フィールドとそれらを操作するためのメソッドおよびコンストラクタ、 innerクラスです。 これらをクラスが所有する「財産」のようなものと考えてみましょう。 人間の場合には子供が親から財産を相続します。 それと同じように、新しいクラスを設計する時に 既存のクラスが持っている情報をそっくり受け継ごうというアイディア、 これがクラスの継承です。 複雑なクラスを記述する場合、まったくゼロの状態から始めるのは大変です。 これに対してクラスの継承を利用すれば、 既に存在する情報に新しい情報をつけ加えるだけで済みます。 この過程は親から子へ、子から孫へと繰り返し行うことも可能です。 ちょうど何代にも渡って少しずつ財産を築いていくことができるわけです。


継承によって内容を少しずつ豊富にしていくことができる

あるいはもう一つの比喩として 「生物の進化」のようなものとイメージしていただいてもいいでしょう。 単純で基本的なクラスから複雑で多様な機能を持つクラスへと、 少しずつ進化させていくわけです。 実際、クラスの継承関係を表現するために、 しばしば生物の進化の「系統樹」のような図が利用されます。


クラスの継承関係の表現

クラスの継承に関連して、 これからしばしば登場することになる重要な用語も確認しておきましょう。 まずクラスの親子の関係に関してです。 あるクラスを元にして、それを継承した新しいクラスを作る場合、 元になる親の方のクラスを「スーパークラス(super class)」と呼びます。 これに対して新しく定義される子のクラスの方を 「サブクラス(sub class)」と呼びます。 またスーパークラス、サブクラスという呼び方は、 直接の親や子に対応するクラスのみに用いられわけではありません。 先祖に当たるクラスはすべてスーパークラス、 子孫として生み出されたクラスはすべてサブクラスと呼ぶのが普通です。 クラスの継承の場合は、親の財産はそっくり全て子供に伝えられます。 したがって一般にサブクラスの方がスーパークラスよりも豊富な機能を備えています。 つまり子供の方が親よりも大きいわけです。 この点は勘違いしやすいので注意してください。

Javaにおける継承の記述

具体的なサンプルを通じて、 Javaではクラスの継承がどのように実現されるかを見てみましょう。例えば,次のようなクラスXがあったとします.

class X{  //スーパークラス
  private int x;
 public void fx(){
  }
}

このクラスXを継承して新しいサブクラスYを作ります.

class Y extends X{  //サブクラス
  private int y;
 public void fy(){
  }
}

このとき,サブクラスYでは,スーパークラスXのデータとメソッドも呼び出すことができます.

class PlayStation{
String CPU="34MHz";
String CD = "CD-ROM";
PlayStation(){}
String getCPU(){ return CPU; }
String getMedia(){ return CD; }
}
class PlayStation2 extends PlayStation{
String MEMORY ="32M";
PlayStation2(){
CPU = "300MHz"; // CPUの値をおきかえる.
}
public String getMemory(){
return MEMORY;
}
}
public class InheritanceSample{
public static void main(String args[]){
PlayStation2 PS2 = new PlayStation2();
System.out.println("PS2 CPU "+PS2.getCPU());
System.out.println("PS2 Memory "+PS2.getMemory());
}
}

 

 

 

 

←PlayStation2クラスはPlayStationクラスを継承しています.

←PlayStation2クラスの中で,PlayStationクラスに定義されている変数 CPUに値を代入することができます.

 

 

←PlayStation2クラスのオブジェクトPS2でも,PlayStationクラスに定義されているgetCPUメソッドを呼び出すことができます.

 



クラス定義のまとめ

クラスはデータメンバとそれを操作するためのメソッドを一体化したもので,次のように定義します.

class クラス名{
  データメンバの宣言

  public メソッド名1(引数){

  }

  public メソッド名2(引数){

  }
}

オブジェクトの宣言

classで定義した「クラス名」とnew演算子を使って,次のようにオブジェクトを宣言します.

クラス名 オブジェクト名=new クラス名(引数);

オブジェクトが生成されるときには,コンストラクタと呼ばれる特別な初期化メソッドを呼び出します.

メソッドの呼び出し

オブジェクトに対してメソッドを呼び出すときには,「オブジェクト名.メソッド」という形で(.)で区切って記述します.例えば,f.show( );と書いた場合,fというオブジェクトに対して,show()というメソッドを適用しています.

クラスの継承

例えば,次のようなクラスXがあったとします.

class X{  //スーパークラス
  private int x;
 public void fx(){
  }
}

このクラスXを継承して新しいサブクラスYを作ります.

class Y extends X{  //サブクラス
  private int y;
 public void fy(){
  }
}

このとき,サブクラスYでは,スーパークラスXのデータとメソッドも呼び出すことができます.

 
実習

オブジェクトの定義と生成

次のプログラムは,クラスの定義とオブジェクトの生成例です.コンストラクタによるオブジェクトの初期化,メソッドの定義,newコマンドによるオブジェクトの生成法などを確認して下さい. また,文字列の取り扱いについても確認して下さい.JAVAでは文字列の加算は"+"演算子で簡単に行うことができます.


/*
  SampleClass.java  クラスの作成
*/
class StrClass{
 //データメンバ
String subject; //主語 String verb; //動詞 String object; //目的語 String str;
//コンストラクタ StrClass(String asub,String averb,String aobject){ subject = asub; verb = averb; object = aobject; }  //メソッド void combine(){   str = subject+verb+object; //文字列の加算 } } class SampleClass{ public static void main(String args[]){ //StrClassのオブジェクトを生成する StrClass sa = new StrClass("They ","arrived at ","the airport."); System.out.println("Subject "+sa.subject); System.out.println("Verb "+sa.verb); System.out.println("Object "+sa.object);   //saオブジェクトにcombineメソッドを適用する sa.combine(); System.out.println("Str "+sa.str); } }

クラスの継承

ここでは,PlayStation2クラスがPlayStationクラスを継承しています.


/*
  InheritanceSample.java  継承のためのサンプルプログラム
*/
class PlayStation{
  String CPU = "34MHz";
  String CD = "CD-ROM";
  PlayStation(){}

  String getCPU(){
    return CPU;
  }
  String getMedia(){
    return CD;
  }
}

class PlayStation2 extends PlayStation{ //PlayStationクラスを継承する
  String DVD = "DVD-ROM";

  PlayStation2(){
    CPU = "300MHz";   // CPUの値をおきかえる.
  }
  String getMedia(){
    return CD+" "+DVD;
    //return super.getMedia()+DVD;
  }
}


class InheritanceSample{
  public static void main(String args[]){

    PlayStation PS = new PlayStation();
    PlayStation2 PS2 = new PlayStation2();

    System.out.println("PS  CPU "+PS.getCPU());
    System.out.println("PS2 CPU "+PS2.getCPU()); //PS2でもgetCPUメソッドが使えることを確認する

    System.out.println("PS  Media "+PS.getMedia());
    System.out.println("PS2 Media "+PS2.getMedia()); //PS2でもgetMediaメソッドが使えることを確認する
  }
}

 

演習問題
  1. a)オブジェクトとクラスの関係,b)クラスの継承,について数行程度で説明して下さい.
  2. 「実習」の項目で示した2つのプログラムを実行して出力結果を確認して下さい.また,その出力結果が得られる理由を考察して下さい.(コンパイルや実行の仕方は第1回目の授業を参照して下さい)
  3. Class PlayStation2を継承して,Class PlayStation3を作って下さい.Class PlayStation3の内部(変数,メソッド)は自由に設計して下さい.

提出先のアドレスは:jhoshino@esys.tsukuba.ac.jpです.Subject欄に「応用プログラミング 第2回課題,氏名,学籍番号」を書いて下さい.本文にソースコード,実行結果,考察を記載して下さい.

締め切りは次の授業の開始時まで.ソースコードはメール本文へのコピーあるいは添付どちらでも結構です.


 


参考資料

違うプログラム例を使ったクラス定義の説明

クラスの宣言
今まで作ったプログラムはすべて public class クラス名 で始まっていたが,Java のプログラムはすべてクラスからできており,クラスを宣言することなしに Java プログラムを組むことはできない.(半分復習を兼ねて)クラス宣言の一般形式を書くと,

[修飾子] class クラス名 {
 フィールドの宣言
 メソッドの宣言
}

である.class は Java の予約語で,クラス宣言の開始を示す.クラス名は大文字で始まる名詞とするのが「慣習」となっている.{ と } の間がクラス宣言の内容であり,そのクラスがもつフィールドとメソッドが書かれる.フィールドとは「情報を保持する場所」でいわば変数のようなものであり,メソッドとは「情報を処理する方法」でいわば関数のようなものである.フィールドとメソッドがこのクラスの性質を決めることは後述する.

フィールド
フィールドは「情報を保持する場所」を表す.いま,「名前」と「年齢」という 2 つの情報を表すクラスを考える.String型の name が「名前」を,int 型 の age が「年齢」を表すとする.この 2 つの情報をもつクラス Person(名簿の中に各人の情報を収めるためのテンプレートを表すクラス)の宣言は下記のようになる.このとき,name や age を Person クラスのフィールド (field) と呼ぶ.

class Person {
 String name;
 int age;
}

いま宣言したのはインスタンスではなくクラスである.つまり具体的なメンバー情報を作ったわけではなく,「名前と年齢をもつ(名簿の中の)メンバー一般」を作ったのである(具体的なメンバー情報であるインスタンスを作る方法を後述する).

メソッド
メソッドは「情報を処理する方法」を表す.上記の Person クラスに,
 * 名前と年齢を設定する.
 * 年齢から生まれ年を計算する.
という 2 つのメソッドを追加してみよう.前者のメソッド名を setPerson,後者のメソッド名を getBirthYear とすると,これらのメソッドの宣言は下記のようになる.最初の 3 行は先ほどと同じである.

class Person {
 String name;
 int age;
 void setPerson(String n, int a) {  −−
    name = n;             | setPerson メソッド
    age = a;              |
  }                 −−
 int getBirthYear() {        −−
  return 2002 - age;          | getBirthYear メソッド
  }                 −−
}

* 4 行目:setPerson メソッドの宣言の開始.戻り値を返さない.括弧 ( ) の中は仮引数列(メソッドへの入力の受け入れ口に相当).
* 5 行目:名前を表す String 型のフィールド name に,String 型の第一引数 n で与えられた値を代入する.
* 6 行目:年齢を表す int 型のフィールド age に,int 型の第二引数 a で与えられた値を代入する.
* 7 行目:setPerson メソッドの宣言の終了.
* 8 行目:getBirthYear メソッドの宣言の開始.戻り値(生まれ年)は int 型.引数はなし(生まれ年の計算に必要な情報はフィールドに保持されているので必要なし).
* 9 行目:生まれ年の計算を行い,その計算結果を戻り値としている.
* 10 行目:getBirthYear メソッドの宣言の終了.
ところで,メソッドを宣言しただけでは何も起こらない.メソッドは「呼び出し」をしなければ働かないし,メソッドを呼び出すには,その前にインスタンスを作っておく必要がある.つまり,setPerson メソッドで名前と年齢を設定するには,事前に設定対象となる具体的なメンバーを用意する必要があるし,getBirthYear メソッドで生まれ年を計算するには,事前に計算対象となる具体的なメンバーを用意する必要がある.そこで次に「インスタンスの作り方」を学ぶ.

インスタンスの作り方

* new 演算子とインスタンスの作成
Person クラスのインスタンスを作るには, new 演算子を使って以下のようにする.すると,Person クラスのインスタンスが新しく 1 個作られる.

new Person()

下記のように,作ったインスタンスを Person 型として宣言した変数に代入しておくと,後で利用するときに便利である(下の式は,配列の確保の式と類似している).

Person member = new Person();

フィールドへのアクセス
新しいインスタンスのフィールドに定数を代入してみよう.それには,

Person member1 = new Person();
member1.name = "Sato";
member1.age = 33;

とする.ここで,member1.name の式を見ると,Person 型の変数 member1 と Person クラスのフィールド名 name の間に「ドット」(.)がある.これは「member1 というインスタンス(に対する Person クラス)の name というフィールド」を意味するもので,ドット演算子と呼ばれる.上記は代入の例だが,参照の場合には単に member1.name と書けば良い.つまりドット演算子は,インスタンスのフィールドを表現する場合に利用できる.

インスタンスメソッドの呼び出し
いまは名前と年齢の 2 つをわざわざ代入したが,先ほどの Person クラス宣言の中で,名前と年齢を設定する setPerson メソッドを既に宣言していた.これを利用すれば,簡単に name フィールドと age フィールドに定数を代入することができる.それには以下のようにする.

Person member1 = new Person();
member1.setPerson("Sato", 33);

このようにメソッドの呼び出しにも先ほどのドット演算子を用いる.すなわち上記の 2 行目を見ると,Person 型の変数 member1 と メソッド名 setPerson の間に「ドット」(.)がある.これは「member1 というインスタンスに対して Person クラスの setPerson メソッドを呼び出す」ことを意味している.このようにドット演算子は,メソッドを呼び出す場合にも利用できる.
上記の 2 行目の括弧 ( ) 内の2つの引数はメソッドへの入力であり,実引数と呼ばれる.この式によって,member1 というインスタンスの name フィールドと age フィールドに,各々 "Sato" という文字列と 33 の整数が代入される.

this
フィールドへのアクセスでは member1.name というように,member1 というインスタンスを表す変数の後に「ドット」を付けてフィールド名を書いたのに対し,Person クラスのメソッド宣言では単にフィールド名だけを書いた.これは一見矛盾するように思えるが,実はメソッド宣言で用いられるフィールド名やメソッド名の前には,「自分」を表すインスタンス thisが省略されている.つまり,setPerson メソッドの宣言を省略なしに書くと以下のようになる.

void setPerson(String n, int a) {
 this.name = n;
 this.age = a;
}

この this は,インスタンス自身を表すものである.例えば,member1.setPerson("Sato", 33) という呼び出しの中では,this は member1 と同じインスタンスを指す.メソッドの宣言の中では,フィールド名やメソッド名の前にインスタンスを指定する必要がないのは,Java コンパイラが自動的に補うからである.

コンストラクタ

ここまででインスタンスの作成と,フィールドへのアクセスおよびメソッドの呼び出しを学んだが,名簿のメンバーの名前と年齢を設定するという処理は,名簿のインスタンスつまりメンバーを作るときにはいつでも必要となる.従って,名簿のインスタンスを作成すると同時に,名前と年齢を設定できれば便利である.つまり,インスタンスを生成すると同時に初期化を行いたい.この初期化を実現するのが コンストラクタである.

* コンストラクタの宣言
名前と年齢を設定する Person のコンストラクタは以下のように記述できる.

class Person {
 … … …
 Person(String n, int a) {  −−
 name = n;           | コンストラクタ
 age = a;            |
 }              −−
 … … …
}

一見するとメソッド宣言に似ているが,
* コンストラクタには戻り値の型がない(void でもない)
* コンストラクタ名はクラス名と同じ
という点がメソッド宣言と異なる.既に setPerson メソッドを宣言しているので,上記のコンストラクタをこの setPerson メソッドを用いて,以下のように書くこともできる.

class Person {
  … … …
  Person(String n, int a) {
   setPerson(n, a);
  }
  … … …
}

* コンストラクタの呼び出し
名前と年齢を設定するコンストラクタを呼び出すには下記のようにすれば良い.このように記述するだけで,名前が "Sato",年齢が 33 である Person のインスタンスが生成され,変数 member1 に代入される.

Person member1 = new Person("Sato", 33);

* 引数なしコンストラクタ
名前と年齢を指定せずに Person のインスタンスを生成したときには,名前を "Tanaka" に,年齢を 22 に自動的に初期化したいと仮定しよう.このような場合には引数なしコンストラクタを用いて,引数を指定せずにインスタンスを初期化する.それには以下のように記述する.

class Person {
 … … …
 Person() {         −−
  setPerson("Tanaka", 22);  | 引数なしコンストラクタ
 }              −−
 … … …
}

つまり仮引数列のないコンストラクタである.なお,引数列が異なれば,1 つのクラスにコンストラクタが複数あっても構わない(つまり,先に宣言したコンストラクタと上記の引数なしコンストラクタが,同じ Person クラスに併記してあっても構わない).

* main メソッドの追加
以上の Person クラスに,実行開始位置を示す main メソッドをつければ,定義したクラスを単なる部品としてではなく,1 つのプログラムとして動かすことが可能である.そこで以下のようなプログラムを作成してみる.

class Person {
  String name;
  int age;
  Person() {           −−
    setPerson("Tanaka", 22);   | 引数なしコンストラクタ
  }                −−
  Person(String n, int a) {    −−
    setPerson(n, a);        | コンストラクタ
  }                −−
  void setPerson(String n, int a) {  −−
    name = n;             | setPerson メソッド
    age = a;             |
  }                  −−
  int getBirthYear() {     −−
    return 2000 - age;     | getBirthYear メソッド
  }               −−
  public static void main(String[] args) {
    Person member1 = new Person("Sato", 33);   ← member1 インスタンスの生成
    System.out.println("member1.name = " + member1.name);
    System.out.println("member1.age = " + member1.age);
    System.out.println("member1.getBirthYear() = " + member1.getBirthYear());


    Person member2 = new Person();  ← member2 インスタンスの生成
    System.out.println("member2.name = " + member2.name);
    System.out.println("member2.age = " + member2.age);
    System.out.println("member2.getBirthYear() = " + member2.getBirthYear());
  }
}

 


継承について

既に存在しているクラスをもとにして,新しいメソッドやフィールドを追加したり,既にあるメソッドに上書きしたりして,新しいクラスを宣言することができる.クラスを拡張してできた新しいクラスを,元のクラスのサブクラス (subclass) と呼び,元のクラスを,拡張して新しくできたクラスの スーパークラス (superclass)と呼ぶ.このようにサブクラスとスーパークラスは相対的な呼称である.あたかも,親子の関係が誰を基準にして考えるかによって変わるように,サブクラス/スーパークラスの関係もどのクラスを基準にして考えるかによって変わる.

別の言い方をすれば,スーパークラスとはより一般的な汎用性のある機能をもったクラスであるのに対し,サブクラスとはより特殊で目的に特化した機能をもったクラスだとも言える.すなわち,既に用意されている一般的なクラスを使って,自分の目的に合う新しいクラスを作るということがクラスの拡張であり,オブジェクト指向プログラミングのキーポイントだと言える.

クラスを拡張するとき,2 つ以上のクラスをスーパークラスとすることはできない.つまり,Java 言語のクラスはたった 1 つのスーパークラスからしか拡張できない.これに対して,サブクラスはいくつでももつことができる.1 つのスーパークラスを拡張してあるサブクラスを作り,そのサブクラスを拡張してそのサブクラスを作り,さらにそのサブクラスを拡張してそのまたそのサブクラスを作り,… … というように多段のサブクラスを階層的に積み上げることも可能である(上図を参照のこと).そしてこの構造全体をクラス階層 (class hierarchy) と呼ぶ.スーパークラスと各サブクラスを線で結ぶと,あらゆるサブクラスのスーパークラスは 1 つしか存在しないので,上図にあるような木構造 (tree structure) を作ることができる.


プログラム例を使った説明

Person クラスを拡張してみよう.
  class Person {
     String name;
     int age;
     void setPerson(String n, int a) {    −−
        name = n;                            | setPerson メソッド
        age = a;                             |
     }                                    −−
     int getBirthYear() {                 −−
        return 2002 - age;                   | getBirthYear メソッド
     }                                    −−
  }
  
ここでは,Person クラスを拡張して HokenPerson クラスを作成してみる.そのためには,extends というキーワードを用いて,
  class HokenPerson extends Person {
     … …
  }
  
とする.extends はその前にあるクラスがそのあとのクラスのサブクラス(拡張クラス)であることを示す.もう少しわかり易く書くと,
  class HokenPerson    // HokenPerson というクラスを宣言する.
     extends Person    // そのスーパークラスは Person である.
  {                    // ここで宣言したクラスの内容は,
     … …             //     … …
  }                    // である.
  
となる.このとき,

である.

継承
(子供が親の遺伝子を受け継ぐように)サブクラスはスーパークラスのフィールドとメソッドを受け継ぐ.このことを継承と呼ぶ.

thisキーワード

thisキーワードを利用して実行中のオブジェクトを参照することができます.先ほどのsample.javaではコンストラクタの宣言のときに,StrClass(String asub,String averb,String aobject)とクラスで定義されている変数とは違う名前を使う必要がありました.thisキーワードを使うと,オブジェクトの変数はthis.subjectのように参照することができるため,同じ変数名を使うことができます.

/*
  SampleClassEx.java  thisキーワードの利用
*/
class StrClass{
  String  subject;  //主語
  String  verb;     //動詞
  String  object;   //目的語
  String  str;


  StrClass(String subject,String verb,String object){  //クラスで定義されている変数と同じ変数名を使うことができます.
    this.subject = subject;
    this.verb    = verb;
    this.object  = object;
  }

  void combine(){
    str = subject+verb+object;
  }
}

class SampleClassEx{
  public static void main(String args[]){
    StrClass sa = new StrClass("They ","arrived at  ","the airport.");

    System.out.println("Subject "+sa.subject);
    System.out.println("Verb    "+sa.verb);
    System.out.println("Object  "+sa.object);

    sa.combine();

    System.out.println("Str     "+sa.str);
  }
}