第 8章
继承、多型
– 继承
– 多型扩充( extends)父类别
使用 "extends"作为其扩充父类别的关键词
public class Bird {
private String name;
public Bird() {
}
public Bird(String name) {
this.name = name;
}
public void walk() {
System.out.println("走路 ");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
扩充( extends)父类别
public class Chickenextends Bird { //扩充 Bird类别
private String crest; //新增私有成员,鸡冠描述
public Chicken() {super(); }
//定义建构方法
public Chicken(String name,String crest) {
super(name);
this.crest = crest;
}
//新增方法
public void setCrest(String crest) {
this.crest = crest;
}
public String getCrest() {
return crest;
}
public void wu() {
System.out.println("咕咕叫 …");
}
}
扩充( extends)父类别
Chicken chicken1 = new Chicken("小克 ","红色小鸡冠 ");
Chicken chicken2 = new Chicken();
System.out.printf("小雞 1 -名称 %s,鸡冠是 %s。 \n",
chicken1.getName(),chicken1.getCrest());
chicken1.wu();
System.out.printf("小雞 2 -名称 %s,鸡冠是 %s。 \n",
chicken2.getName(),chicken2.getCrest());
chicken2.wu();
被保护的( protected)成员
保护意思表示存取该成员是有条件限制的
继承的类别就可以直接使用这些成员,但这些成员仍然受到保护
不同套件( package)的对象不可直接呼叫使用 protected成员被保护的( protected)成员
public class Rectangle {
//受保护的 member
protected int x;
protected int y;
protected int width;
protected int height;
…
}
public class CubicextendsRectangle {
…
public int getVolumn() {
//可以直接使用父类别中的 width,height成员
return length*width*height;
}
}
重新定义( Override)方法
如果父类别中的定义并不符合您的需求,
可以在扩充类别的同时重新定义
可以重新定义方法的实作内容、成员的访问权限,或是成员的返回值型态重新定义( Override)方法
public class SimpleArray {
protected int[] array;
public SimpleArray(int i) {
array = new int[i];
}
public void setElement(int i,int data) {
array[i] = data;
}
....
}
public class SafeArrayextends SimpleArray {
…
//重新定义 setElement()
public void setElement(int i,int data) {
if(i < array.length)
super.setElement(i,data);
}
....
}
重新定义( Override)方法
SimpleArray simpleArray = new SafeArray();
simpleArray.setElement();
实际运作的对象是 SafeArray的实例,所以被呼叫执行的会是 SafeArray中重新定义过的 setElement()方法重新定义( Override)方法
在衍生类别中想要呼叫基类的建构方法,
可以使用 super()方法
要在衍生类别中呼叫基类方法,则可以如使用 super.methodName()
条件限制
– 父类别中的方法或建构方法不能是 "private",
也就是不能是私用成员重新定义( Override)方法
您可以增大父类别中的方法权限,但不可以缩小父类别的方法权限
– 若原来成员是 "public"的话,您不可以在父类别中重新定义它为 "private"或 "protected"
public class SafeArray extends SimpleArray {
//不可以缩小父类别中同名方法的权限
private void setElement(int i,int data) {
....
}
}
setElement(int,int) in SafeArray cannot override
setElement(int,in t) in SimpleArray; attempting to assign
weaker accessprivileges; was publicprivate void
setElement(int i,int data) {^1 error
重新定义( Override)方法
从 J2SE5.0开始在重新定义方法时,您可以重新定义返回值的型态
public class Bird {
protected String name;
public Bird(String name) {
this.name = name;
}
public Bird getCopied {
return new Bird(name);
}
}
重新定义( Override)方法
public class Chicken extends Bird {
protected String crest;
public Chicken(String name,String crest) {
super(name);
this.crest = crest;
} //重新定义返回值型态为 Chicken
publicChicken getCopied() {
return new Chicken(name,crest);
}
}
重新定义的返回值型态必须是父类别中同一方法返回型态的子类别
无法重新定义 static方法
Object类别
Object是 Java程序中所有类别的父类别
每个类别都直接或间接继承自 Object类别
public class Foo {
//实作
}
public class Foo extends Object {
//实作
}
toString(),equals(),hashCode()
toString()方法是对对象的文字描述
Object的 toString()预设会传回类别名称及
16进位制的编码
预设的 equals()本身是比较对象的内存地址是否相同
getClass().getName() + '@' + Integer.toHexString(hashCode())
toString(),equals(),hashCode()
可以重新定义 equals()方法,以定义您自己的对象在什么条件下可视为相等的对象
在重新定义 equals()方法时,建议同时重新定义 hashCode()方法
toString(),equals(),hashCode()
public boolean equals(Object other) {
if (this == other)
return true;
if (!(other instanceof Cat))
return false;
final Cat cat = (Cat) other;
if (!getName().equals(cat.getName()))
return false;
if (!getBirthday().equals(cat.getBirthday()))
return false;
return true;
}
public int hashCode() {
int result = getName().hashCode();
result = 29 * result + getBirthday().hashCode();
return result;
}
clone()方法
如何复制对象本身
最基本的作法:实作 java.lang.Cloneable界面( Interface)
public class PointimplementsCloneable { //要实作 Cloneable
…
public Object clone() throws CloneNotSupportedException {
//呼叫父类别的 clone()来进行复制
return super.clone();
}
}
clone()方法
public class TableimplementsCloneable { //要实作 Cloneable
private Point center;
// …
public Object clone ()
throws CloneNotSupportedException {
//呼叫父类的 clone()来复制
Table table = (Table) super.clone();
if(this.center != null) {
//复制 Point类型的数据成员
table.center = (Point) center.clone();
}
return table;
}
}
clone()方法
Table table = new Table();
table.setCenter(new Point(2,3));
Point originalCenter = table.getCenter();
Table clonedTable = (Table) table.clone();
Point clonedCenter = clonedTable.getCenter();
System.out.printf("原来的 Table中心,(%d,%d)\n",
originalCenter.getX(),originalCenter.getY());
System.out.printf("复制的 Table中心,(%d,%d)\n",
clonedCenter.getX(),clonedCenter.getY());
clonedCenter.setX(10);
clonedCenter.setY(10);
//改变复制品的内容,对原来的对象不会有影响
System.out.printf("原来的 Table中心,(%d,%d)\n",
originalCenter.getX(),originalCenter.getY());
System.out.printf("复制的 Table中心,(%d,%d)\n",
clonedCenter.getX(),clonedCenter.getY());
final关键词
,final”关键词使用在变量宣告时,表示该变量设定之后,就不可以再改变该变量的值
如果在定义方法成员时使用 "final",表示该方法成员在无法被子类别重新定义
final double PI = 3.14159;
public class Ball {
private double radius;
publicfinal double getRadius() {
return radius;
}
//,...
}
final关键词
如果您在宣告类别时加上 final关键词,则表示要终止被扩充
publicfinal class Ball {
//,...
}
多型导论
如果使用不正确类别型态转换对象操作接口,会发生 java.lang.ClassCastException
多型导论
定义了两个 execute()方法来分别操作
Class1与 Class2的实例
execute()分别依赖了 Class1与 Class2两个类别
public void execute(Class1 c1) {
c1.doSomething();
}
public void execute(Class2 c2) {
c2.doSomething();
}
多型导论
将程序中的 execute()改成
只依赖 ParentClass,程序对个别对象的依赖程序降低了,日后在修改、维护或调整程序时的弹性也增加了
public void execute(ParentClass c) {
c.doSomething();
}
多型导论
实际上在设计并不依赖于具体类别,而是依赖于抽象
Java中在实现多型时,可以让程序依赖于
「抽象类」 ( Abstractclass)或是「接口」
( Interface)
抽象类( Abstract class)
public class ConcreteCircle {
private double radius;
public void setRedius(int radius) { this.radius = radius; }
public double getRadius() { return radius; }
public void render() {
System.out.printf("画一个半径 %f的实心圆 \n",getRadius());
}
}
public class HollowCircle {
private double radius;
public void setRedius(int radius) { this.radius = radius; }
public double getRadius() { return radius; }
public void render() {
System.out.printf("画一个半径 %f的空心圆 \n",getRadius());
}
}
抽象类( Abstractclass)
要宣告抽象方法与抽象类,您要使用
"abstract"关键词
publicabstract class AbstractCircle {
protected double radius;
public void setRedius(int radius) { this.radius = radius; }
public double getRadius() { return radius; }
public abstract void render();
}
抽象类( Abstract class)
public class ConcreteCircle extends AbstractCircle {
public ConcreteCircle() {}
public ConcreteCircle(double radius) {
this.radius = radius;
}
public void render() {
System.out.printf("画一个半径 %f的实心圆 \n",getRadius());
}
}
public class HollowCircle extends AbstractCircle {
public HollowCircle() {}
public HollowCircle(double radius) {
this.radius = radius;
}
public void render() {
System.out.printf("画一个半径 %f的空心圆 \n",getRadius());
}
}
抽象类( Abstract class)
public class CircleDemo {
public static void main(String[] args) {
renderCircle(new ConcreteCircle(3.33));
renderCircle(new HollowCircle(10.2));
}
public static void renderCircle(AbstractCircle circle) {
circle.render();
}
}
抽象类应用
publicabstract class AbstractGuessGame {
…
public void start() {
showMessage("欢迎 ");
int guess = 0;
do {
guess =getUserInput();
if(guess > number) {
showMessage("输入的数字较大 ");
} else if(guess < number) {
showMessage("输入的数字较小 ");
} else {
showMessage("猜中了 ");
}
} while(guess != number);
}
protectedabstractvoid showMessage(String message);
protectedabstract int getUserInput();
}
抽象类应用
public class TextModeGameextends AbstractGuessGame {
private Scanner scanner;
public TextModeGame() {
scanner = new Scanner(System.in);
}
protected void showMessage(String message) {
for(int i = 0; i < message.length()*2; i++) {
System.out.print("*");
}
System.out.println("\n"+ message);
for(int i = 0; i < message.length()*2; i++) {
System.out.print("*");
}
}
protected int getUserInput() {
System.out.print("\n输入数字,");
return scanner.nextInt();
}
}
抽象类应用
藉由在抽象类中先定义好程序的执行流程,
并将某些相依方法留待子类别中执行
AbstractGuessGame guessGame = new TextModeGame();
guessGame.setNumber(50);
guessGame.start();
界面( Interface)
继承某抽象类的类别必定是该抽象类的一个子类
实作某接口的类别并不被归属于哪一类
– 一个对象上可以实作多个接口
接口的宣告是使用 "interface"关键词
[public] interface接口名称 {
权限设定 传回型态 方法 (参数列 );
权限设定 传回型态 方法 (参数列 );
//,...
}
界面( Interface)
在宣告接口时方法上的权限设定可以省略,
如果省略的话,预设是 "public
public interface IRequest {
public void execute();
}
界面( Interface)
public class HelloRequestimplements IRequest {
private String name;
public HelloRequest(String name) {
this.name = name;
}
public void execute() {
System.out.printf("哈啰 %s! %n",name);
}
}
public class WelcomeRequestimplements IRequest {
private String place;
public WelcomeRequest(String place) {
this.place = place;
}
public void execute() {
System.out.printf("欢迎来到 %s! %n",place);
}
}
界面( Interface)
public static void main(String[] args) {
for(int i = 0; i < 10; i++) {
int n = (int) (Math.random() * 10) % 2; //随机产生
switch (n) {
case 0:
doRequest(new HelloRequest("良葛格 "));
break;
case 1:
doRequest(new WelcomeRequest("Wiki网站 "));
}
}
}
public static voiddoRequest(IRequestrequest) {
request.execute();
}
界面( Interface)
在 Java中您可以一次实作多个接口
必要时必须作「接口转换」,如此程序才知道如何正确的操作对象
public class类别名称 implements界面 1,界面 2,界面 3 {
//界面实作
}
ISomeInterface1 obj1 = (ISomeInterface1) someObject;
obj1.doSomeMethodOfISomeInterface1();
ISomeInterface2 obj2 = (ISomeInterface2)
someObject;obj2.doSomeMethodOfISomeInterface2();
界面( Interface)
接口也可以进行继承的动作,同样也是使用,extends”关键词来继承父接口
一个接口可以同时继承多个父接口,实作子接口的类别必须将所有在父接口和子接口中定义的方法实作出来
public interface名称 extends界面 1,界面 2 {
//,..
}
继承、多型
– 继承
– 多型扩充( extends)父类别
使用 "extends"作为其扩充父类别的关键词
public class Bird {
private String name;
public Bird() {
}
public Bird(String name) {
this.name = name;
}
public void walk() {
System.out.println("走路 ");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
扩充( extends)父类别
public class Chickenextends Bird { //扩充 Bird类别
private String crest; //新增私有成员,鸡冠描述
public Chicken() {super(); }
//定义建构方法
public Chicken(String name,String crest) {
super(name);
this.crest = crest;
}
//新增方法
public void setCrest(String crest) {
this.crest = crest;
}
public String getCrest() {
return crest;
}
public void wu() {
System.out.println("咕咕叫 …");
}
}
扩充( extends)父类别
Chicken chicken1 = new Chicken("小克 ","红色小鸡冠 ");
Chicken chicken2 = new Chicken();
System.out.printf("小雞 1 -名称 %s,鸡冠是 %s。 \n",
chicken1.getName(),chicken1.getCrest());
chicken1.wu();
System.out.printf("小雞 2 -名称 %s,鸡冠是 %s。 \n",
chicken2.getName(),chicken2.getCrest());
chicken2.wu();
被保护的( protected)成员
保护意思表示存取该成员是有条件限制的
继承的类别就可以直接使用这些成员,但这些成员仍然受到保护
不同套件( package)的对象不可直接呼叫使用 protected成员被保护的( protected)成员
public class Rectangle {
//受保护的 member
protected int x;
protected int y;
protected int width;
protected int height;
…
}
public class CubicextendsRectangle {
…
public int getVolumn() {
//可以直接使用父类别中的 width,height成员
return length*width*height;
}
}
重新定义( Override)方法
如果父类别中的定义并不符合您的需求,
可以在扩充类别的同时重新定义
可以重新定义方法的实作内容、成员的访问权限,或是成员的返回值型态重新定义( Override)方法
public class SimpleArray {
protected int[] array;
public SimpleArray(int i) {
array = new int[i];
}
public void setElement(int i,int data) {
array[i] = data;
}
....
}
public class SafeArrayextends SimpleArray {
…
//重新定义 setElement()
public void setElement(int i,int data) {
if(i < array.length)
super.setElement(i,data);
}
....
}
重新定义( Override)方法
SimpleArray simpleArray = new SafeArray();
simpleArray.setElement();
实际运作的对象是 SafeArray的实例,所以被呼叫执行的会是 SafeArray中重新定义过的 setElement()方法重新定义( Override)方法
在衍生类别中想要呼叫基类的建构方法,
可以使用 super()方法
要在衍生类别中呼叫基类方法,则可以如使用 super.methodName()
条件限制
– 父类别中的方法或建构方法不能是 "private",
也就是不能是私用成员重新定义( Override)方法
您可以增大父类别中的方法权限,但不可以缩小父类别的方法权限
– 若原来成员是 "public"的话,您不可以在父类别中重新定义它为 "private"或 "protected"
public class SafeArray extends SimpleArray {
//不可以缩小父类别中同名方法的权限
private void setElement(int i,int data) {
....
}
}
setElement(int,int) in SafeArray cannot override
setElement(int,in t) in SimpleArray; attempting to assign
weaker accessprivileges; was publicprivate void
setElement(int i,int data) {^1 error
重新定义( Override)方法
从 J2SE5.0开始在重新定义方法时,您可以重新定义返回值的型态
public class Bird {
protected String name;
public Bird(String name) {
this.name = name;
}
public Bird getCopied {
return new Bird(name);
}
}
重新定义( Override)方法
public class Chicken extends Bird {
protected String crest;
public Chicken(String name,String crest) {
super(name);
this.crest = crest;
} //重新定义返回值型态为 Chicken
publicChicken getCopied() {
return new Chicken(name,crest);
}
}
重新定义的返回值型态必须是父类别中同一方法返回型态的子类别
无法重新定义 static方法
Object类别
Object是 Java程序中所有类别的父类别
每个类别都直接或间接继承自 Object类别
public class Foo {
//实作
}
public class Foo extends Object {
//实作
}
toString(),equals(),hashCode()
toString()方法是对对象的文字描述
Object的 toString()预设会传回类别名称及
16进位制的编码
预设的 equals()本身是比较对象的内存地址是否相同
getClass().getName() + '@' + Integer.toHexString(hashCode())
toString(),equals(),hashCode()
可以重新定义 equals()方法,以定义您自己的对象在什么条件下可视为相等的对象
在重新定义 equals()方法时,建议同时重新定义 hashCode()方法
toString(),equals(),hashCode()
public boolean equals(Object other) {
if (this == other)
return true;
if (!(other instanceof Cat))
return false;
final Cat cat = (Cat) other;
if (!getName().equals(cat.getName()))
return false;
if (!getBirthday().equals(cat.getBirthday()))
return false;
return true;
}
public int hashCode() {
int result = getName().hashCode();
result = 29 * result + getBirthday().hashCode();
return result;
}
clone()方法
如何复制对象本身
最基本的作法:实作 java.lang.Cloneable界面( Interface)
public class PointimplementsCloneable { //要实作 Cloneable
…
public Object clone() throws CloneNotSupportedException {
//呼叫父类别的 clone()来进行复制
return super.clone();
}
}
clone()方法
public class TableimplementsCloneable { //要实作 Cloneable
private Point center;
// …
public Object clone ()
throws CloneNotSupportedException {
//呼叫父类的 clone()来复制
Table table = (Table) super.clone();
if(this.center != null) {
//复制 Point类型的数据成员
table.center = (Point) center.clone();
}
return table;
}
}
clone()方法
Table table = new Table();
table.setCenter(new Point(2,3));
Point originalCenter = table.getCenter();
Table clonedTable = (Table) table.clone();
Point clonedCenter = clonedTable.getCenter();
System.out.printf("原来的 Table中心,(%d,%d)\n",
originalCenter.getX(),originalCenter.getY());
System.out.printf("复制的 Table中心,(%d,%d)\n",
clonedCenter.getX(),clonedCenter.getY());
clonedCenter.setX(10);
clonedCenter.setY(10);
//改变复制品的内容,对原来的对象不会有影响
System.out.printf("原来的 Table中心,(%d,%d)\n",
originalCenter.getX(),originalCenter.getY());
System.out.printf("复制的 Table中心,(%d,%d)\n",
clonedCenter.getX(),clonedCenter.getY());
final关键词
,final”关键词使用在变量宣告时,表示该变量设定之后,就不可以再改变该变量的值
如果在定义方法成员时使用 "final",表示该方法成员在无法被子类别重新定义
final double PI = 3.14159;
public class Ball {
private double radius;
publicfinal double getRadius() {
return radius;
}
//,...
}
final关键词
如果您在宣告类别时加上 final关键词,则表示要终止被扩充
publicfinal class Ball {
//,...
}
多型导论
如果使用不正确类别型态转换对象操作接口,会发生 java.lang.ClassCastException
多型导论
定义了两个 execute()方法来分别操作
Class1与 Class2的实例
execute()分别依赖了 Class1与 Class2两个类别
public void execute(Class1 c1) {
c1.doSomething();
}
public void execute(Class2 c2) {
c2.doSomething();
}
多型导论
将程序中的 execute()改成
只依赖 ParentClass,程序对个别对象的依赖程序降低了,日后在修改、维护或调整程序时的弹性也增加了
public void execute(ParentClass c) {
c.doSomething();
}
多型导论
实际上在设计并不依赖于具体类别,而是依赖于抽象
Java中在实现多型时,可以让程序依赖于
「抽象类」 ( Abstractclass)或是「接口」
( Interface)
抽象类( Abstract class)
public class ConcreteCircle {
private double radius;
public void setRedius(int radius) { this.radius = radius; }
public double getRadius() { return radius; }
public void render() {
System.out.printf("画一个半径 %f的实心圆 \n",getRadius());
}
}
public class HollowCircle {
private double radius;
public void setRedius(int radius) { this.radius = radius; }
public double getRadius() { return radius; }
public void render() {
System.out.printf("画一个半径 %f的空心圆 \n",getRadius());
}
}
抽象类( Abstractclass)
要宣告抽象方法与抽象类,您要使用
"abstract"关键词
publicabstract class AbstractCircle {
protected double radius;
public void setRedius(int radius) { this.radius = radius; }
public double getRadius() { return radius; }
public abstract void render();
}
抽象类( Abstract class)
public class ConcreteCircle extends AbstractCircle {
public ConcreteCircle() {}
public ConcreteCircle(double radius) {
this.radius = radius;
}
public void render() {
System.out.printf("画一个半径 %f的实心圆 \n",getRadius());
}
}
public class HollowCircle extends AbstractCircle {
public HollowCircle() {}
public HollowCircle(double radius) {
this.radius = radius;
}
public void render() {
System.out.printf("画一个半径 %f的空心圆 \n",getRadius());
}
}
抽象类( Abstract class)
public class CircleDemo {
public static void main(String[] args) {
renderCircle(new ConcreteCircle(3.33));
renderCircle(new HollowCircle(10.2));
}
public static void renderCircle(AbstractCircle circle) {
circle.render();
}
}
抽象类应用
publicabstract class AbstractGuessGame {
…
public void start() {
showMessage("欢迎 ");
int guess = 0;
do {
guess =getUserInput();
if(guess > number) {
showMessage("输入的数字较大 ");
} else if(guess < number) {
showMessage("输入的数字较小 ");
} else {
showMessage("猜中了 ");
}
} while(guess != number);
}
protectedabstractvoid showMessage(String message);
protectedabstract int getUserInput();
}
抽象类应用
public class TextModeGameextends AbstractGuessGame {
private Scanner scanner;
public TextModeGame() {
scanner = new Scanner(System.in);
}
protected void showMessage(String message) {
for(int i = 0; i < message.length()*2; i++) {
System.out.print("*");
}
System.out.println("\n"+ message);
for(int i = 0; i < message.length()*2; i++) {
System.out.print("*");
}
}
protected int getUserInput() {
System.out.print("\n输入数字,");
return scanner.nextInt();
}
}
抽象类应用
藉由在抽象类中先定义好程序的执行流程,
并将某些相依方法留待子类别中执行
AbstractGuessGame guessGame = new TextModeGame();
guessGame.setNumber(50);
guessGame.start();
界面( Interface)
继承某抽象类的类别必定是该抽象类的一个子类
实作某接口的类别并不被归属于哪一类
– 一个对象上可以实作多个接口
接口的宣告是使用 "interface"关键词
[public] interface接口名称 {
权限设定 传回型态 方法 (参数列 );
权限设定 传回型态 方法 (参数列 );
//,...
}
界面( Interface)
在宣告接口时方法上的权限设定可以省略,
如果省略的话,预设是 "public
public interface IRequest {
public void execute();
}
界面( Interface)
public class HelloRequestimplements IRequest {
private String name;
public HelloRequest(String name) {
this.name = name;
}
public void execute() {
System.out.printf("哈啰 %s! %n",name);
}
}
public class WelcomeRequestimplements IRequest {
private String place;
public WelcomeRequest(String place) {
this.place = place;
}
public void execute() {
System.out.printf("欢迎来到 %s! %n",place);
}
}
界面( Interface)
public static void main(String[] args) {
for(int i = 0; i < 10; i++) {
int n = (int) (Math.random() * 10) % 2; //随机产生
switch (n) {
case 0:
doRequest(new HelloRequest("良葛格 "));
break;
case 1:
doRequest(new WelcomeRequest("Wiki网站 "));
}
}
}
public static voiddoRequest(IRequestrequest) {
request.execute();
}
界面( Interface)
在 Java中您可以一次实作多个接口
必要时必须作「接口转换」,如此程序才知道如何正确的操作对象
public class类别名称 implements界面 1,界面 2,界面 3 {
//界面实作
}
ISomeInterface1 obj1 = (ISomeInterface1) someObject;
obj1.doSomeMethodOfISomeInterface1();
ISomeInterface2 obj2 = (ISomeInterface2)
someObject;obj2.doSomeMethodOfISomeInterface2();
界面( Interface)
接口也可以进行继承的动作,同样也是使用,extends”关键词来继承父接口
一个接口可以同时继承多个父接口,实作子接口的类别必须将所有在父接口和子接口中定义的方法实作出来
public interface名称 extends界面 1,界面 2 {
//,..
}