多态性(Polymorphism)
- 从另一个角度将接口从具体的实施细节中分离出来,及实现了"是什么"与"怎么做"的分离。
- 多态:同一个类型,表现出不同的行为。
1.向上转型
取得一个对象句柄,将其作为一个父类句柄来使用的行为叫做向上转型
//Music.java
//Inheritance & upcasting
class Note{
private int value;
Note(int val) {
value = val;
}
public static final Note
middleC=new Note(0),
cSharp=new Note(1),
cFlat = new Note(2);
} //etc
class Instrument{
public void play(Note n){
System.out.println("Instrument.play");
}
}
//Wind object are instrument because they have same interface
class Wind extends Instrument{
//Redefine interface method:
public void play(Note n) {
System.out.println("Wind.play()");
}
}
public class Music {
public static void tune(Instrument i){
//....
i.play(Note.middleC);
}
public static void main(String[] args) {
Wind flute=new Wind();
tune(flute); //upcasting
}
}
Music.tune()接收一个Instrument句柄,同时也接收从Instrument衍生出来的所有东西
·为什么要向上转型
- 向上转型可以节省大量代码
//Music2.java
//Overloading instead of upcasting
class Note2 {
public static final Note2
middleC = new Note2(0),
cSharp = new Note2(1),
cFlat = new Note2(2);
private int value;
private Note2(int val) {
value = val;
}
} //Etc.
class Instrument2{
public void play(Note2 n) {
System.out.println("Instrument2.play()");
}
}
class Wind2 extends Instrument2 {
@Override
public void play(Note2 n) {
System.out.println("Wind2.play()");
}
}
class Stringed2 extends Instrument2 {
@Override
public void play(Note2 n) {
System.out.println("Stringed.play()");
}
}
class Brass2 extends Instrument2 {
@Override
public void play(Note2 n) {
System.out.println("Brass2.play()");
}
}
public class Music2 {
public static void tune(Wind2 i) {
i.play(Note2.middleC);
}
public static void tune(Stringed2 i) {
i.play(Note2.middleC);
}
public static void tune(Brass2 i) {
i.play(Note2.middleC);
}
public static void main(String[] args) {
Wind2 flute =new Wind2();
Stringed2 violin = new Stringed2();
Brass2 frenchHorn = new Brass2();
tune(flute); //No upcasting
tune(violin);
tune(frenchHorn);
}
}
如上述代码一样可以不采用向上转型,但增加大量代码,需要给每一个类型重写play()和tune()方法,同时如果忘记给某个方法进行重写设置编译器也不会报错,这样一来,类型的整个操作过程就显得极难管理,有失控的危险。
但假如只写一个方法,将基础类作为自变量或参数使用,而不是使用那些特定的衍生类,岂不是会简单得多?
也就是说,如果我们能不顾衍生类,只让自己的代码与基础类打交道,那么省下的工作量将是难以估计
的。
2.深入理解(绑定)
观察Music.java 中tune():
public static void tune(Instrument i){
//...
i.play(Note.middleC);
}
它接受接收一个Instrument句柄,所以在这种情况下编译器才知道Instrument句柄指向的是一个Wind,而不是一个Brass或Stringed,编译器无从得知,所以接下来有必要探讨一下绑定(Binding)
方法调用的绑定(Binding)
- 绑定:将一个方法调用和一个方法主体连接到一起
- 静态绑定(static binding):在程序运行前执行绑定,也叫做早期绑定(Early Binding)
- 动态绑定(Dynamic binding):在运行期间执行绑定,以对象的类型为基础,也叫做后期绑定。
若一种语言实现了动态绑定,同时必须提供一些机制,可在运行期间判断对象的类型,并分别调用适当的方法,也就是说,编译器此时依旧不知道对象的类型,但方法调用机制能自己去调查,找到正确的方法主体
java绑定的所有方法都采用动态绑定,除非方法被声明成final。这意味着我们不必决定是否进行动态绑定——他是自动发生的。
final方法能防止别人覆盖那个方法,但更重要的一点是,它可以有效的“关闭”动态绑定,这样一来,编译器就可以为final方法调用生成效率更高的代码。
如何产生正确的行为
Java里的所有方法都通过动态绑定具有多态性
例:父类Shape(形状),有大量子类:Circle(圆形),Square(矩形),Triangle(三角形)等等
向上转型可通过Shape s=new Cirbcle();
简单表现出来
这里我们创建了一个Cirbcle对象,并把它赋给Shape句柄(表面看起来似乎是错误的)但实际是可行的–因为继承关系Cirbcle是属于Shape的一种。
s.draw();同样的大家以为会调用Shape的draw(),因为这毕竟是一个Shape()句柄。但实际调用的是Cirbcle的Shape(),因为动态绑定已经介入(多态性)
3.过载与覆盖
- 过载(Overloading,重载):同一样东西在不同地方有不同的含义
- 覆盖(Overrideing,重写,改写):随时随地都只有一种含义,只是原先的含义被后来的含义取代了。(允许参数类型不同,且不具备多态性)
4.抽象类和方法
- 抽象方法:
abstract void X();
- 需要在子类中重写,或者将子类也声明为abstract
- 只有方法声明,没有方法实现(在抽象类中创建)
- 不能是使用static,private修饰
- 抽象类:
abstract class J{}
:- 包含一个或多个抽象方法的类,必须声明为abstract
- 只声明方法的存在,而不去实现的类
- 不能被实例化(不能被创建对象)
创建抽象类和抽象方法有时对我们非常有用,它可是一个方法的抽象变成明显的事实,明确的告诉用户和编译器自己打算如何用它
可以看出一下代码除了外,实际并没有进行什么改变
// Music4.java
//Abstract classes and methods
abstract class Instrument4{
int i; //Storage allocated for each
public abstract void play(); //abstract methods
public String what(){
return "Instrument4.play()";
}
public abstract void adjust();
}
class Wind4 extends Instrument4 {
@Override
public void play() {
System.out.println("Wind4.play()");
}
@Override
public String what() {
return "Wind4";
}
@Override
public void adjust() {}
}
class Percussion4 extends Instrument4{
@Override
public void play() {
System.out.println("Percussion4.play()");
}
@Override
public String what() {
return "Percussion4";
}
@Override
public void adjust() {}
}
class Stringed4 extends Instrument4{
@Override
public void play() {
System.out.println("Stringed4.play()");
}
@Override
public String what() {
return "Stringed4";
}
@Override
public void adjust() {}
}
class Woodwind4 extends Wind4{
@Override
public void play() {
System.out.println("Woodwind4.play()");
}
@Override
public String what() {
return "Woodwind4";
}
}
class Brass4 extends Wind4{
@Override
public void play() {
System.out.println("Brass4.play()");
}
@Override
public void adjust() {
System.out.println("Brass4.adjust()");
}
}
public class Music4 {
//Doesn't care about type, so new types added to the system still work right:
static void tune(Instrument4 i){
i.play();
}
static void tuneAll(Instrument4[] e){
for (int i = 0; i < e.length; i++) {
tune(e[i]);
}
}
public static void main(String[] args) {
Instrument4[] orchetra=new Instrument4[5];
int i=0;
//Upcasting during addition to the array:
orchetra[i++]=new Wind4();
orchetra[i++]=new Brass4();
orchetra[i++]=new Woodwind4();
orchetra[i++]=new Stringed4();
orchetra[i++]=new Percussion4();
tuneAll(orchetra);
}
}
5.接口(interface)
- 可将其看成一个“纯”抽象类
- 包含基本数据类型的数据成员,默认为static和final
- 对于实现我的所有类,看起来都应该象我现在这个样子
- 只提供一种形式,并不提供实施细节
- 接口内的方法默认为public
- 接口
interface jj{//主体}
;实现类class aa implements jj{//主体}
- 使用接口的原因:
- 向上转型至多个父类
- 防止客户程序员创建这个类的一个对象
- 以及规定它仅仅是一个接口
接口与抽象类该如何选择?
- 如果想创建的基础类任何方法定义和成员变量,就选择接口
- 如果事先知道某个东西成为基础类,第一选择就是接口
- 只有必须使用方法定义或者成员变量时,才使用抽象类
java的多重继承
- 多重继承:X属于a,也属于b,也属于c,将多个类合并到一起的动作称为多重继承。
- 一个类可以实现多个接口
- 语法:
class X extends a implements b,c{//主体}
也可以不继承
下面这个例子展示了一个“具体”类同几个接口合并的情况,它最终生成了一个新类:
//Adventure.java
//Multiple interfaces
interface CanFight {
void fight();
}
interface CanSwim {
void swim();
}
interface CnaFly {
void fly();
}
class ActionCharacter {
public void fight() {
System.out.println("ActionCharacter.fight");
}
}
class Hero extends ActionCharacter implements CanFight, CanSwim, CnaFly {
@Override
public void swim() {
System.out.println("HERO.SWIM");
}
@Override
public void fly() {
System.out.println("HERO.FLY");
}
}
public class Adventure {
static void t(CanFight x){x.fight();}
static void u(CanSwim x){x.swim();}
static void v(CnaFly x){x.fly();}
static void w(ActionCharacter x){x.fight();}
public static void main(String[] args) {
Hero i=new Hero(); //Hero向上转型为 具体类:ActionCharacter,接口:CanFight, CanSwim, CnaFly 对象
t(i); //Treat it as a CanFight
v(i); //Treat it as a CnaFly
w(i); //Treat it as a ActionCharacter
u(i); //Treat it as a CanSwim
}
}
通过继承扩展接口
- 接口也可以被继承(或者说扩展)并且可以继承多个
- 语法:
interface aa extends interface1,interface2{//主体}
- 一个类也可以实现多个接口
class aa implements interface1,interface2{//主体}
//HorrorShow.java
//Extending an interface with inheritance
interface Monster {
void menace();
}
interface DangerousMonster extends Monster {
void destroy();
}
interface Lethal {
void kill();
}
interface Vampire extends DangerousMonster, Lethal {
void drinkBlood();
}
class DragonZilla implements DangerousMonster,Vampire{
@Override
public void menace() {
}
@Override
public void destroy() {
}
@Override
public void kill() {
}
@Override
public void drinkBlood() {
}
}
public class HorrorShow {
static void u(Monster b) {
b.menace();
}
static void v( DangerousMonster d) {
d.menace();
d.destroy();
}
public static void main(String[] args) {
DragonZilla if2=new DragonZilla();
u(if2);
v(if2);
if2.kill();
}
}
常数分组
由于置入接口的所有字段都自动具有static和final属性,所以接口是一个多常数值进行分组的好工具,它具有个C或C++的enum非常相似的效果:
//:Months.java
//Using interface to create groups of constants
public interface Months {
int JANUARY=1,
FEBRUARY=2,
MARCH=3,
APRIL=4,
MAY=5,
JUNE=6,
JULY=7,
AUGUST=8,
SEPTEMBER=9,
OCTOBER=10,
NOVEMBER=11,
DECEMBER=12;
} ///:~
// Month2.java
//A more robust enumeration system
public final class Month2 {
private String name;
private Month2(String nm) {
name = nm;
}
public String otString() {
return name;
}
public final static Month2
JAN = new Month2("January"),
FEB = new Month2("February"),
MAR = new Month2("March"),
APR = new Month2("April"),
MAY = new Month2("May"),
JUN = new Month2("June"),
JUL = new Month2("July"),
AUG = new Month2("August"),
SEP = new Month2("September"),
OCT = new Month2("October"),
NOV = new Month2("November"),
DEC = new Month2("December");
public final static Month2[] month ={
JAN, JAN, FEB, MAR, APR, MAY, JUN,
JUL, AUG, SEP, OCT, NOV, DEC
};
public static void main(String[] args) {
Month2 m= Month2.JAN;
System.out.println(m.otString());
m=Month2.month[12];
System.out.println(m.otString());
System.out.println(m == Month2.DEC);
System.out.println(m.equals(Month2.DEC));
}
}
初始化接口中的字段
接口中定义的字段会自动具有 static 和final 属性。它们不能是“空白 final”,但可初始化成非常数表达
式。例如:
//RandVla.java
//Initializing interface fields with
//non-constant initializers
public interface RandVal {
int rint=(int) (Math.random()*10);
long rlong = (long) (Math.random()*10);
float rfloat=(float) (Math.random()*10);
double rdouble=Math.random()*10;
}
由于字段是 static 的,所以它们会在首次装载类之后、以及首次访问任何字段之前获得初始化。下面是一个
简单的测试:
//TestRandVals.java
public class TestRandVals {
public static void main(String[] args) {
System.out.println(RandVal.rfloat);
System.out.println(RandVal.rlong);
System.out.println(RandVal.rfloat);
System.out.println(RandVal.rdouble);
}
}
当然,字段并不是接口的一部分,而是保存于那个接口的 static 存储区域中。
6.内部类
- 内部类:将一个类置入另一个类中,这就叫做内部类。
import ln.Const;
import java.security.DomainCombiner;
//Parcel1.java
//Creating inner classes
public class Parcel {
class Contents {
private int i = 11;
public int value() {
return i;
}
}
class Destination {
private String label;
Destination(String whereTO) {
label = whereTO;
}
String readLabel(){
return label;
}
//Using inner classes looks just like
//using any other class,within Parcel1:
}
public void ship(String dest){
Contents c=new Contents();
Destination d=new Destination(dest);
}
public static void main(String[] args) {
Parcel parcel=new Parcel();
parcel.ship("Tanzania");
}
}
内部类的使用看起来和其他任何类都没什么分别。在这里,唯一明显的区别就是它的
名字嵌套在 Parcel1 里面。但大家不久就会知道,这其实并非唯一的区别。
更典型的一种情况是,一个外部类拥有一个特殊的方法,它会返回指向一个内部类的句柄。就象下面这样:
//Parcel2.java
//Returning a handle to an inner class
public class Parcel2 {
class Content2{
private int i=11;
public int value(){
return i;
}
}
class Destination2{
private String label;
Destination2(String whereTo){
label=whereTo;
}
String readLabel(){
return label;
}
}
public Destination2 to(String s){
return new Destination2(s);
}
public Content2 cont(){
return new Content2();
}
public void ship(String dest){
Content2 c=cont();
Destination2 d=to(dest);
}
public static void main(String[] args) {
Parcel2 p=new Parcel2();
p.ship("Tanzania");
Parcel2 q=new Parcel2();
//Defining handles to inner classes:
Parcel2.Content2 c=q.cont();
Parcel2.Destination2 d=p.to("Borneo");
}
}///:~
内部类和向上转型
普通(非内部)类不可设为private 或 protected——只允许 public 或者“友好的”。
通过内部类来隐藏实现的细节
import ln.Const;
//Parcel3.java
//Returning a handle to an inner class
abstract class Contents{
abstract public int value();
}
interface Destination{
String readLabel();
}
public class Parcel3 {
private class PContents extends Contents{
private int i=11;
public int value() {
return i;
}
}
protected class PDestination implements Destination{
private String label;
private PDestination(String whereTo){
label=whereTo;
}
public String readLabel(){return label;}
}
public Destination dest(String s) {
return new PDestination(s);
}
public Contents cont() {
return new PContents();
}
}
class test{
public static void main(String[] args) {
Parcel3 p=new Parcel3();
Contents c=p.cont();
Destination d=p.dest("Tanzania");
//Illegal -- can't access private class:
//! Parcel3.PContents c = p.new PContents();
}
}
方法和作用域中的内部类
可以方法或者作用域中创建内部类,这样做一般有如下两种原因:
- 实现某种形式的接口,自己能够创建和返回一个句柄。
- 要解决一个复杂的问题,并希望创建一个类,用来辅助自己的程序方案,同时不愿意把它公开。
下面是几个例子:
首先是三个接口:
//:Contents.java
package Package.innerscopes;
public interface Contents {
int value();
}
//:Destination.java
package Package.innerscopes;
public interface Destination {
String readLabel();
}///:~
//:Wrapping.java
package Package.innerscopes;
public class Wrapping {
private int i;
public Wrapping(int c) {
i=c;
}
public int value(){
return i;
}
}
例1 方法的作用域里嵌套内部类
//:Parcel4.java
//Nesting a class within a method
package Package.innerscopes;
public class Parcel4 {
public Destination dest(String s) {
class PDestination implements Destination {
private String label;
private String whereTO;
private PDestination(String whereTO) {
label = whereTO;
}
@Override
public String readLabel() {
return label;
}
}
return new PDestination(s);
}
public static void main(String[] args){
Parcel4 p=new Parcel4();
Destination d=p.dest("Tanzania");
}
}///:`
例2 在任意作用域里嵌套内部类
//:Parcel5.java
//Nesting a class within a scope
package Package.innerscopes;
public class Parcel5 {
private void internalTracking(boolean b) {
if (b) {
class TrackingSlip{
private String id;
TrackingSlip(String s){
id=s;
}
String getSlip(){return id;}
}
TrackingSlip ts=new TrackingSlip("slip");
String s =ts.getSlip();
}
//Can’use it here! out of scope:
//!TrackingSlip ts=new TrackingSlip("s");
}
public void track(){internalTracking(true);}
public static void main(String[] args) {
Parcel5 p=new Parcel5();
p.track();
}
}
例3 一个返回值为匿名内部类的方法
package Package.innerscopes;
//Parcel6.java
//A method that returns an anonymous inner class
public class Parcel6 {
public Contents cont(){
return new Contents() { //匿名内部
private int i=11;
@Override
public int value() {
return i;
}
}; //Semicolon required in this case
}
public static void main(String[] args) {
Parcel6 p=new Parcel6();
Contents c=p.cont();
}
}
该匿名内部类等价于
class MyContents extends Contents {
private int i = 11;
public int value() { return i; }
}
return new MyContents();
例4 调用base-class构造方法的匿名内部类
//Parcel7.java
package Package.innerscopes;
//An anonymous inner class that calls the
//base-class constructor
public class Parcel7 {
//Base constructor call:
public Wrapping wrap(int x){
//Base constructor call:
return new Wrapping(x){
public int value(){
return super.value()*47;
}
}; //Semicolon required
}
public static void main(String[] args){
Parcel7 p=new Parcel7();
Wrapping w=p.wrap(10);
System.out.println(w.value());
}
}
例5 在匿名内部类初始化字段
//:Parcel8,java
//An anonymous inner class that performs
//initialization.A briefer version
package Package.innerscopes;
public class Parcel8 {
//Argument must be final to use inside
//anonymous inner class:
public Destination dest(final String dest){
return new Destination() {
private String label=dest;
@Override
public String readLabel() {
return label;
}
};
}
public static void main(String[] args) {
Parcel8 p =new Parcel8();
Destination d=p.dest("tanzania");
System.out.println(d.readLabel());
}
}
例6 一个匿名类,通过实例初始化进行构建(匿名内部类不可拥有构建器)
//Parcel9.java
//Using "instance initialization" to perform
//construction on an anonymous inner class
package Package.innerscopes;
public class Parcel9 {
public Destination dest(final String dest,final float price){
return new Destination() {
private int cost;
//instance initialization for each object:
{ //实例初始化模块————匿名内部类构建器
cost=Math.round(price);
if (cost>100)
System.out.println("Over budget!"+cost);
}
private String label=dest;
@Override
public String readLabel() {
return label;
}
};
}
public static void main(String[] args) {
Parcel9 p=new Parcel9();
Destination d=p.dest("Tanzania",101.485F);
}
}
链接到外部类
- 在创建一个内部类时,这个内部类的对象同时拥有指向封装对象的一个链接,所以它能访问那个封装对象的成员-无需取得任何资格
- 内部类拥有对封装类(外部类)所有元素的访问权限
//Sequence.java
//Holds a sequence of Object
interface Selector{
boolean end ();
Object current();
void next();
}
public class Sequence {
private Object[] o;
private int next =0;
public Sequence(int size){
o=new Object[size];
}
public void add(Object x){
if (next <o.length){
o[next]=x;
next++;
}
}
private class SSelector implements Selector{
int i =0;
public boolean end(){
return i==o.length;
}
public Object current(){
return o[i];
}
public void next(){
if (i < o.length)
i++;
}
}
public Selector getSelector(){
return new SSelector();
}
public static void main(String[] args) {
Sequence s=new Sequence(10);
for(int i =0; i<10;i++){
s.add(Integer.toString(i));
}
Selector sl=s.getSelector();
while (!sl.end()) {
System.out.println((String)sl.current());
sl.next();
}
}
}
static内部类
内部类的对象默认持有创建它的那个封装类的一个对象的句柄
而static内部类意味着:
- 为创建一个static内部类的对象,我们不需要一个外部类对象
- 不能从static内部类的一个对象中访问一个外部类对象
但在存在一些限制:由于static 成员只能位于一个类的外部级别,所以内部类不可拥有static 数据或
static 内部类。
//Parcel10.java
//static inner classes
abstract class Contents1{
abstract public int value();
}
interface Destination1{
String readLabel();
}
public class Parcel10 {
private static class PContents
extends Contents1 {
private int i=11;
public int value(){return i;}
}
protected static class PDestination implements Destination1 {
private String label;
private PDestination(String whereTo){
label =whereTo;
}
public String readLabel(){return label;}
}
public static Destination1 dest(String s){
return new PDestination(s);
}
public static Contents1 cont(){
return new PContents();
}
public static void main(String[] args) {
Contents1 c = cont();
/*
Parcel10 p=new Parcel10();
PDestination ddd=new PDestination("SS");
*/
Destination1 d = dest("Tanzania");
}
}
通常,我们不在一个接口里设置任何代码,但 static 内部类可以成为接口的一部分。由于类是“静态”的,
所以它不会违反接口的规则——static 内部类只位于接口的命名空间内部:
//:IInterface.java
//Static inner class inside interface
interface IInterface {
static class Inner{
int i,j,k;
public Inner(){
System.out.println("ss");
}
void f(){}
}
}
引用外部类对象
//:Creating inner classes
public class Parcel11 {
class Contents{
private int i=11;
public int value(){return i;}
}
class Destination{
private String label;
Destination(String whereTo) {
label=whereTo;
}
String readLabel(){return label;}
}
public static void main(String[] args) {
Parcel11 p=new Parcel11();
//Must use instance of outer class
//to create an instances of inner class
Parcel11.Contents c =p.new Contents(); //引用外部类
Parcel11.Destination d=p.new Destination("Tanzania");
}
}
从内部类继承
从中可以看到,InheritInner 只对内部类进行了扩展,没有扩展外部类。但在需要创建一个构建器的时候,
默认对象已经没有意义,我们不能只是传递封装对象的一个句柄。此外,必须在构建器中采用下述语法:
enclosingClassHandle.super();
它提供了必要的句柄,以便程序正确编译。
//: InheritInner.java
//Inheriting an inner class
class WithInner{
class Inner{}
}
public class InheritInner
extends WithInner.Inner {
//!InheritInner(){} //Won't compile
InheritInner(WithInner wi) {
wi.super();
}
public static void main(String[] args) {
WithInner wi=new WithInner();
InheritInner inheritInner=new InheritInner(wi);
}
}
内部类可以覆盖吗?
若创建一个内部类,然后从封装类继承,并重新定义内部类,那么会出现什么情况呢?也就是说,我们有可
能覆盖一个内部类吗?这看起来似乎是一个非常有用的概念,但“覆盖”一个内部类——好象它是外部类的
另一个方法——这一概念实际不能做任何事情:
内部类不能像一个方法一样被override
//:BigEgg.java
//An inner class cannot be overriden like a method
class Egg{
protected class Yolk{
public Yolk(){
System.out.println("Egg.Yolk()");
}
}
private Yolk y;
public Egg(){
System.out.println("new Egg()");
y=new Yolk();
}
}
public class BigEgg extends Egg{
public class Yolk{
public Yolk(){
System.out.println("BigEgg.Yolk()");
}
}
public static void main(String[] args){
new BigEgg();
}
}
正确的覆盖一个内部类
import com.fr.web.core.A.Y;
//:BigEgg.java
//Proper inheritance of an inner class
class Egg2 {
protected class Yolk{
public Yolk(){
System.out.println("Egg2.Yolk()");
}
public void f(){
System.out.println("Egg2.f()");
}
}
private Yolk y=new Yolk();
public Egg2(){
System.out.println("new Egg()");
}
public void insertYolk(Yolk yy){y=yy;}
public void g(){y.f();}
}
public class BigEgg2 extends Egg2{
public class Yolk extends Egg2.Yolk{
public Yolk(){
System.out.println("BigEdd2.Yolk()");
}
public void f(){
System.out.println("BigEgg2.Yolk.f()");
}
}
public BigEgg2(){
insertYolk(new Yolk());
}
public static void main(String[] args) {
Egg2 e2=new BigEgg2();
e2.g();
}
}
内部类标识符
每个类都会生成一个.class文件,用于容纳与如何创建这个类型的对象有关的所有信息(这种信息产生一个名为Class对象的元类),所以内部类也必须生成相应的.class文件。这个文件的名字遵守一下格式:先是封装类的名字+ + 内 部 类 名 字 。 例 如 : W i t h I n n e r +内部类名字。 例如: WithInner +内部类名字。例如:WithInnerInner.class
为什么要用内部类? ——控制框架
7.构建器和多态
构建器不具有多态性,但仍然非常有必要理解构建器如何在复杂的分级结构中以及随同多态性使用。
构建器的调用顺序
构建器的特殊任务:检查对象是否得到了正确的构建。
构建器的调用顺序:
- 在采取其他任何操作之前,为对象分配的存储空间初始化成二进制零。
- 调用基础类构建器。这个步骤会不断重复下去,首先得到构建的是分级结构的根部,然后是下一个衍生类,等等。直到最盛一层的衍生类。
- 接着按照声明顺序调用成员初始化模块
- 调用衍生构建器的主体。
白话:首先调用祖宗类构建器然后是爷爷类、父类、孙子类等等的构建器,完成后在按照声明顺序调用初始化模块,最后在调用衍生构建器的主体(就是该类的构建器)
为什么要这样做?
因为在继承时,衍生类能访问基础类的任何public和protected成员,着意味着但我们在衍生类的时候,基础类的所有成员都是有效的,在构建器内部,必须保证
使用的所有成员都已构建。为达到这个要求,唯一的办法就是首先调用基础类构建器。然后在进入衍生类构
建器以后,我们在基础类能够访问的所有成员都已得到初始化。此外,所有成员对象(亦即通过合成方法置
于类内的对象)在类内进行定义的时候(比如上例中的b,c 和l),由于我们应尽可能地对它们进行初始
化,所以也应保证构建器内部的所有成员均为有效。若坚持按这一规则行事,会有助于我们确定所有基础类
成员以及当前对象的成员对象均已获得正确的初始化。但不幸的是,这种做法并不适用于所有情况,这将在
下一节具体说明。
//:Sandwich.java
//Order of constructor calls
class Meal{
Meal(){
System.out.println("Meal()");
}
}
class Bread{
Bread(){
System.out.println("Brad()");
}
}
class Cheese {
Cheese() {
System.out.println("Cheese()");
}
}
class Lettuce{
Lettuce(){
System.out.println("Lettuce");
}
}
class Lunch extends Meal{
Lunch(){
System.out.println("Lunch()");
}
}
class PortableLunch extends Lunch{
PortableLunch(){
System.out.println("PortableLunch()");
}
}
public class Sandwich extends PortableLunch {
Sandwich(){
System.out.println("Sandwich()");
}
Bread b=new Bread();
Cheese c=new Cheese();
Lettuce l=new Lettuce();
public static void main(String[] args) {
new Sandwich();
}
}
继承和finalize()(跳过,不理解)
构建器内部的多态性方法的行为(跳过,不理解)
如果可能,避免在构建器里调用任何方法
8.通过继承进行设计
用继承表达行为间的差异,并用成员变量表达状态的变化
/**
* :Transmogrify.java
* Dynamically changing the behavior of
* an object via composition
*/
interface Actor1{
void act();
}
class HappyActor1 implements Actor{
@Override
public void act() {
System.out.println("HappyActor");
}
}
class SadActor1 implements Actor {
@Override
public void act() {
System.out.println("SadAction");
}
}
class Stage1{
Actor a= new HappyActor1();
/**
* SadActor1指向句柄A
*/
void change(){
a=new SadActor1();
}
void go(){
a.act();
}
}
public class Transmogrify1 {
public static void main(String[] args) {
Stage1 s=new Stage1();
s.go(); //Prints "HappyActor"
s.change();
s.go();//Print "SadActor1"
}
}
纯继承与扩展(extends)
基础类Useful中有两个方法 void f() void g() 在它的衍生类中覆盖了这两个方法又添加了u,v,w三个方法。这就像是扩展Useful接口,尽管这是个明智之举,但是它有个缺点:==在接口中扩展的那一部分,不能在基础类中使用,所以一旦向上转型,就不能调用扩展的那一部分新方法。==若在此时不使用向上转型,则不会出现此类问题。
向下转型与运行期类型标识
/**
* :RTTI.java
* DownCasting & Run_Time Type Identification(RTTI)
* 向下转型和运行期间标识
*/
class useful {
public void f() {
}
public void g() {
}
}
class MoreUseful extends useful {
@Override
public void f() {
}
@Override
public void g() {
}
public void u() {
}
public void v() {
}
public void w() {
}
}
public class RTTI {
public static void main(String[] args) {
useful[] x = {
new useful(),
new MoreUseful()
};
x[0].f();
x[1].g();
//Compile-time:method not found in useful:
//!x[1].u();
((MoreUseful)x[1]).u(); // Downcast和RTTI
((MoreUseful)x[0]).u(); //Exception thrown
useful usef=new useful();
((MoreUseful)usef).u(); //基础类useful向下转型到衍生类MoreUseful
}
}