章VIIの 多重クラス(上)
コードの再利用がある Javaの多くの魅力的な機能の一つが、非常に革新的な言語になりたい、コードをコピーし、それだけで変更することができます十分ではありません、彼はまた、より多くのことを行うことができなければなりません。
7.1 組み合わせた構文
今まで私たちは、単に例のために新しいクラスへのオブジェクト参照を、置く、カウント数の組み合わせを使用しています
クラスWaterSource { プライベートのString; WaterSource(){ システム。アウト .println(" WaterSource()" ); sが = " 建設します" ; } パブリック文字列のtoString(){ リターンS。 } } パブリック クラスSprinklerSystem { プライベート文字列値1、値2、VALUE3、value4。 プライベート WaterSourceソース= 新しいWaterSource(); プライベート int型I; プライベート フロートF; パブリック文字列のtoString(){ 戻り " VALUE1 = " +値1 + " " + " 値2 = " + VALUE2 + " " + " 値3 = " +値3 + " " + " value4 = " + value4 + " \ n " + " 私は= " + I + " " + " F = " + F + " " + "ソース= " + ソース。 } パブリック 静的 ボイドメイン(文字列[]引数){ スプリンクラーのスプリンクラー = 新しいスプリンクラー()。 システム。アウト.println(スプリンクラーシステム)。 } }
出力は次のようになります。
水源()
VALUE1 =ゼロ値2 =ゼロVALUE3 =ゼロVALUE4 =ゼロ
I = 0、F = 0.0ソース=構築
:上記で定義された2つのクラスメソッドでは、非常に特別な存在であるのtoString()は、各非基本クラスオブジェクトが持つのtoString()コンパイラが必要方法、文字列を、あなたが方法そして、1つのオブジェクトのみを、持っていますそれが呼び出されます。この式には:
「ソース=」+ソース。
コンパイラは、あなたが入れたいことを知っているだろうのString オブジェクトが同じであるWaterSource 一つだけあるため、オブジェクトの追加のString オブジェクトと、別の文字列オブジェクトを追加し、コンパイラが呼び出すのtoString()は、ソースが変換された文字列、 2つの文字列が一緒にリンクされ、その結果が渡されるのSystem.out.println()あなたがそのような行動を持つクラスを作成する場合、のみ記述する必要があるのtoString()メソッドをすることができます。
コンパイラは、単にあなたがこれらの参照を初期化したい場合は、行動規範の以下の場所ができ、各参照のデフォルトのオブジェクトを作成できません。
1 、オブジェクトの場所を定義します
2 、クラスの構成
3 これらのオブジェクトを使用する前に、(遅延初期化)
4 、初期化例
7.2 継承の構文
継承は、すべてのです OOPの言語とのJava 言語の異なるクラスから継承する、またはから暗黙的でない限り、クラスを作成することは、常に、継承されている不可欠な部分のオブジェクトを継承。連続プロセスでは、新しいクラスを宣言する必要性は、この文は、クラスの本体の前に左の中括弧であり、キーワードに続いて、基本クラス名が、旧クラスに似て延びているので、行っているとき、それは自動的にベースを取得します、実現しますクラスのすべてのフィールドとメソッド。例えば:
クラスクレンザー{ プライベートのString = 「クレンザー」。 公共 ボイドアペンド(列A){S + = A。} 公共 ボイド希釈(){(付加" )(希釈を" )。} 公共 ボイドは(){(追加適用" )(適用します" )。} 公共 ボイドスクラブ(){(追加" スクラブ()" )。} パブリック文字列のtoString(){ リターンS。} パブリック 静的 ボイドメイン(文字列[]引数){ クレンザーX= 新しいクレンザー()。 x.dilute(); x.apply(); x.scrub()。 システム。アウト.println(X)。 } } パブリック クラス洗剤はクレンザー{延び 公共 ボイドスクラブ(){ (追加" Detergent.scrubを()" )。 super.scrub(); } 公共 ボイドフォームは、(){(追加" フォーム()" )。} パブリック 静的 ボイドメイン(文字列[]引数){ 洗剤X = 新しい洗剤()。 x.dilute(); x.apply(); x.scrub(); x.foam()。 システム。アウト.println(X)。 システム。アウト .println(" テストの基本クラス:" ); Cleanser.main(引数)。 } }
出力は次のようになります。
クレンザー希()(適用)Detergent.scrub()スクラブ()泡()
基本クラスのテスト:
クレンザー希()(適用)スクラブ()
洗剤及び洗浄剤は含まれてmain()の各クラスに配置されている方法で、メイン()技術の方法は、各クラスのユニットが容易になるテスト、及び試験ユニットの完了後となしてもよいが削除メイン() 、あなたは、次のテストにそれを残すことができます。
クレンザーすべてのメソッドがでなければならない公共の、一般的なルールは、すべてのデータメンバーの量は次のように指定されていることである継承するために、プライベート、すべてのメソッドは次のように指定されている公共、もちろん、例外的な状況で調整を行うことができますが、この方法これは非常に便利なルールです。
7.2.1 初期化基底クラス
その派生クラスのオブジェクトが生成された結果を想像しようとする今、基底クラスと2つのクラスを含む派生クラスという。外部から、彼は、新しいクラスが基底クラスと同じインタフェースを持って、おそらくいくつかの追加のメソッドやフィールドが存在しますが、派生クラスのオブジェクトが作成されたときに、基本クラスのコピーではなく、インターフェイスを継承していますオブジェクトがサブオブジェクトの基本クラスが含まれている、あなたは直接、同じ基本クラスのサブオブジェクトに今回作成したオブジェクトと、その差は、外部からの後者は、ベース・サブオブジェクトは、オブジェクトの派生クラス内にパッケージ化されていることです。
適切に、基本クラスのサブオブジェクトのために初期化初期化を実行するために、コンストラクタで基底クラスのコンストラクタを呼び出すことも重要であり、基底クラスのコンストラクタは、すべての知識や初期化に必要な基底クラスを実行する能力を持って、 Javaは自動的にクラスをエクスポートしますコンストラクタ基底クラスのコンストラクタへの呼び出しを挿入します。例えば:
class Art { Art() { System.out.println("Art constructor"); } } class Drawing extends Art { Drawing() { System.out.println("Drawing constructor"); } } public class Cartoon extends Drawing{ public Cartoon() { System.out.println("Cartoon constructor"); } public static void main(String[] args) { Cartoon x = new Cartoon(); } }
输出结果为:
Art constructor
Drawing constructor
Cartoon constructor
我们发现,构件过程是从基类向外扩散的,所以基类在导出类构造器可以访问它之前,就已经完成了初始化,即使你不为Cartoon()创建构造器,编译器也会为你合成一个默认的构造器,这里的构造器是调用基类的构造器。
带参数的构造器
上面这些例子各个类都含有默认的构造器,即这些构造器都不带参数,编译器可以轻松调用他们是因为不必考虑传递什么样的参数问题,但是如果没有默认的基类构造器,或者想调用一个带参数的基类构造器,就必须使用关键字super显式编写调用基类构造器的语句,并且配以适当的参数列表:
class Game { Game(int i) { System.out.println("Game constructor"); } } class BoardGame extends Game { BoardGame(int i) { super(i); System.out.println("Board constructor"); } } public class Chess extends BoardGame{ Chess() { super(1); System.out.println("Chess constructor"); } public static void main(String[] args) { Chess x = new Chess(); } }
输出结果如下:
Game constructor
Board constructor
Chess constructor
如果不是在BoardGame()中调用了基类构造器,编译器将无法找到符合Game()形式的构造器,电泳基类构造器必须是你在导出类构造器中要做的第一件事。
7.3 代理
第三种关系称为代理,Java没有提供对它的直接支持,这是继承于组合之间的中庸之道,我们将一个成员对象置于所要构造的类中,但与此同时我们在新类中暴露了该成员对象的所有方法。
7.4 组合使用组合和继承
同时使用组合和继承是很常见的事,下面这个例子就同时使用了这两项技术,并配以必要的构造器初始化来创建更加复杂的类:
class Plate { Plate(int i) { System.out.println("Plate constructor"); } } class DinnerPlate extends Plate { DinnerPlate(int i) { super(1); System.out.println("Dinner constructor"); } } class Utensil { Utensil(int i) { System.out.println("Utensil constructor"); } } class Spoon extends Utensil { Spoon(int i) { super(1); System.out.println("Spoon constuctor"); } } class Fork extends Utensil { Fork(int i) { super(1); System.out.println("Fork constructor"); } } class Knife extends Utensil { Knife(int i) { super(1); System.out.println("Knife constructor"); } } class Custom { Custom(int i) { System.out.println("Custom constructor"); } } public class PlaceSetting extends Custom{ private Spoon spoon; private Fork fork; private Knife knife; private DinnerPlate dinnerPlate; public PlaceSetting(int i) { super(i + 1); spoon = new Spoon(i+2); fork = new Fork(i+3); knife = new Knife(i+4); dinnerPlate = new DinnerPlate(i+5); System.out.println("PlaceSetting constructor"); } public static void main(String[] args){ PlaceSetting x = new PlaceSetting(9); } }
输出结果为:
Custom constructor
Utensil constructor
Spoon constuctor
Utensil constructor
Fork constructor
Utensil constructor
Knife constructor
Plate constructor
Dinner constructor
PlaceSetting constructor
7.4.1 确保正确清理
Java中没有C++中析构函数的概念,析构函数是一种在对象被销毁时可以自动调用的函数,其原因可能是因为在Java中我们的习惯只是忘掉而不是销毁对象,并且让垃圾回收器在必要时释放其内存。
通常这样是好事,但有时类可能要在其生命周期内执行一些必须执行的清理活动,你可能并不知道垃圾回收器什么时候会被调用,因此我们想要清理一些东西就必须显式地编写一个特殊方法来做这件事。
7.4.2 名称屏蔽
如果Java的基类拥有某个已经被多次重载的方法名称,那么在导出类中重新定义该方法名称并不会屏蔽其在基类中的任何版本,因此无论是在该层或者它的基类中对方法进行定义,重载机制都可以正常工作。
Java SE5新增加了@Override注解,它并不是关键字,但可以把它当成关键字使用,当你想要腹泻某个方法时,可以选择添加这个注解。
7.5 在组合与继承之间选择
组合和继承都允许在新的类中放置子对象,组合是显式地这样做,而继承则是隐式地做。组合技术通常用于想在新类中使用现有类的功能而非它的接口这种情形,也就是在新类中嵌入某个对象,让其实现所需要的功能,但新类的用户看到的只是为新类所定义的接口,而非嵌入对象的接口。有时,允许类的用户直接访问新类中的组合成分是极具意义的,也就是说将对象声明为public,如果对象自身都隐藏了具体实现,那么这种做法是安全的。
在继承的时候,使用某个现有类,并开发一个它的特殊版本,通常这意味着你在使用一个通用类,并为了某种特殊需要而将其特殊化。