java中北大学ppt总结+课后习题第五章(小宇特详解)
第05章 抽象类与接口
什么是抽象类与抽象方法
用关键字abstract修饰的类称为抽象类。例如:
abstract class A {
…
}
抽象类是一种特殊的类,其最大特点是可以包含抽象方法。
用关键字abstract修饰的方法称为抽象方法。对于抽象方法,只允许声明,不允许实现,即抽象方法只有方法声明,而没有方法体。例如:
abstract int min(int x,int y);
抽象类的特点
1.抽象类可以包含抽象方法。
抽象类可以包含普通类能够包含的任何东西,例如非抽象方法、成员变量等,此外还可以有抽象方法。例如:
abstract class A {
abstract int min(int x, int y); // 抽象方法
int max(int x, int y) { // 非抽象方法,即普通方法
return x>y?x:y;
}
}
2.抽象类也可以没有抽象方法。
包含抽象方法的类一定是抽象类(必须用abstract修饰),即只有抽象类才能具有抽象方法;
不能在非抽象类中声明抽象方法。
3.抽象类不能用new运算符创建对象。
即使没有抽象方法,抽象类也不能直接实例化。
4.抽象类必须有子类,一个子类只能继承一个抽象类(使用extends关键字)。
如果一个类是某个抽象类的非抽象子类,那么它必须重写父类的全部抽象方法(给出方法体);
如果一个类是某个抽象类的抽象子类,它可以重写父类的抽象方法,也可以继承这个抽象方法;
因此,抽象类不能使用final修饰,也不允许使用final和abstract同时修饰一个方法。
5.抽象类中可以包含构造方法。
抽象类之中除了抽象方法之外,还包含了普通方法和属性,而属性一定要在构造方法执行完毕后才能进行初始化操作,抽象类中的构造方法是供非抽象子类实例化时调用的。
抽象类的作用
设计程序时,经常会使用抽象类。
可以通过在抽象类中声明若干个抽象方法,表明这些方法在整个系统设计中的重要性;
要求它的非抽象子类必须通过重写这些抽象方法,给出这些方法的具体实现细节(即方法体的内容细节)。
抽象类的设计者只需要关注提供什么样的操作(即只关心方法的名字、类型以及参数),而不需要关心这些操作具体实现的细节(即不关心方法体),这样可以设计者把主要精力放在程序的设计上,而不必拘泥于细节的实现上。
抽象类的定义与使用
抽象类的定义
抽象类的定义与普通类的定义类似。区别在于:
抽象类必须使用abstract修饰;
抽象类可以有抽象方法(用abstract修饰)。
例如:
abstract class A {
abstract int sum(int x,int y); // 抽象方法
int sub(int x,int y) { // 非抽象方法
return x-y;
}
}
抽象类的使用
抽象类不能像普通类那样直接实例化对象。
抽象类必须有子类,而且:
如果一个类是某个抽象类的非抽象子类,那么它必须重写父类的全部抽象方法(给出方法体)。
可以实例化并使用一个抽象类的非抽象子类对象。
abstract class A {
abstract int sum(int x,int y); // 抽象方法
int sub(int x,int y) { // 非抽象方法
return x-y;
}
}
class B extends A {
int sum(int x, int y) { //子类必须重写父类的sum方法
return x+y;
}
}
public class TestExample {
public static void main(String args[]) {
B b=new B(); //生成一个B新的对象b
int sum=b.sum(30,20); //调用重写的方法
int sub=b.sub(30,20); //调用继承的方法
System.out.println("sum="+sum);
System.out.println("sum="+sub);
}
}
例一
有一个抽象类—机动车,它有3个抽象方法,即启动、加速和刹车,表示机动车将启动、加速和刹车功能视为一些重要的功能,机动车类的非抽象子类必须给出启动、加速和刹车的实现细节。
本程序有4个类,即MotorVehicle类(抽象类)、 ManuCar类和AutoCar类(MotorVehicle类的两个非抽象子类)、 TestAbstract5_1类(测试主类)。
abstract class MotorVehicle { // 机动车
abstract void start(); // 启动
abstract void speed_up(); // 加速
abstract void speed_down(); // 刹车
}
class ManuCar extends MotorVehicle { // 手动档轿车
void start() {
System.out.println("踏下离合器,换到一挡");
System.out.println("然后慢慢抬起离合器");
}
void speed_up() { System.out.println("踩油门"); }
void speed_down() {
System.out.println("踏下离合器,踏下刹车板");
System.out.println("然后将挡位换到一挡");
}
}
public class TestAbstract5_1 {
public static void main(String args[]) {
ManuCar mCar=new ManuCar();
System.out.println("手动档轿车的操作:");
mCar.start();
mCar.speed_up();
mCar. speed_down();
AutoCar aCar=new AutoCar();
System.out.println("自动档轿车的操作:");
aCar.start();
aCar.speed_up();
aCar.speed_down();
}
}
程序运行结果:
手动档轿车的操作:
踏下离合器,换到一挡
然后慢慢抬起离合器
踩油门
踏下离合器,踏下刹车板
然后将挡位换到一挡
自动档轿车的操作:
使用前进挡
然后轻踩油门
踩油门
踏下刹车板
抽象类与多态
抽象类是实现多态的重要途径之一。
尽管不能通过new运算符创建抽象类对象,但可以声明一个抽象类对象(定义一个抽象类的引用),将它作为其非抽象子类的上转型对象;
这个上转型对象就可以调用子类重写的方法。
抽象类可以在不同的地方,被不同的非抽象子类所继承,这些非抽象子类对于抽象类中的抽象方法可以有不同的重写,从而可实现不同的行为或功能。
可以利用上转型对象对例一进行改进
声明一个抽象类对象,将它作为其非抽象子类的上转型对象;通过上转型对象就可以调用子类重写的方法,从而可以实现不同的行为或功能。
增加一个类—Simulator类(模拟器),该类有一个playOperation(MotorVehicle car)方法,该方法的参数是MotorVehicle类型。显然,参数MotorVehicle可以是抽象类MotorVehicle的任何一个子类对象的上转型对象,即参数MotorVehicle可以调用MotorVehicle的子类重写的那3个方法。
为了使输出结果能够分辨手动挡和自动挡,在抽象类MotorVehicle中增加一个抽象方法getCarName(),即
abstract String getCarName();
同时,在该抽象类的两个非抽象子类中各自实现该抽象方法。例如:在ManuCar类中增加:
String getCarName(){ return (“自动档轿车");}
public class TestAbstract5_1_2 {
public static void main(String args[]) {
MotorVehicle car = new ManuCar();
System.out.println("手动档轿车的操作:");
car.start(); car.speed_up(); car. speed_down();
car = new AutoCar();
System.out.println("自动档轿车的操作:");
car.start(); car.speed_up(); car.speed_down();
}
}
class Simulator {
void playOpration(MotorVehicle car) {
System.out.println(car.getCarName() + “的操作:");
car.start(); car.speed_up(); car. speed_down();
}
}
public class TestAbstract5_1_3 {
public static void main(String args[]) {
Simulator simulator = new Simulator();
similutor.playOperation(new ManuCar());
similutor.playOperation(new AutoCar());
}
}
抽象类小结
抽象类
抽象类是一种特殊的类,其最大特点是包含了抽象方法(只有方法声明,没有方法实现),抽象类和抽象方法都使用abstract修饰;
必须有子类,如果一个类是某个抽象类的非抽象子类,那么它必须重写父类的全部抽象方法(给出方法体);
设计程序时,经常会使用抽象类,其原因在于:抽象类只关心操作,而不关心这些操作具体实现的细节,这样可以使程序的设计者把主要精力放在程序的设计上,而不必拘泥于细节的实现上。
可以通过上转型对象调用非抽象子类按不同方式重写的方法,从而可实现不同的行为或功能(多态)。
接口
什么是接口
如果一个类定义时全部由抽象方法和全局常量组成,那么这种类就称为接口。
使用关键字interface(而不是class)定义一个接口。
接口使抽象的概念更深入了一层,可将其想象为一个“纯”抽象类。
接口的特点
(1)不能对接口直接进行对象的实例化操作;
(2)接口中只有全局常量,没有成员变量,所以接口中不能有构造方法;
(3)每一个接口必须定义子类,子类使用implements关键字实现接口;
(4)接口的实现类(如果不是抽象类)必须重写接口中所定义的全部抽象方法;
(5)一个接口可以继承扩展多个父接口;
(6)一个类可以实现多个接口。
Java中的接口类似于日常生活中接口的作用。
通过在接口中声明若干个抽象方法,表明这些方法的重要性,方法体的内容细节由实现接口的类去完成。
接口设计者只关心操作,并不关心操作的具体实现,设计者可以把主要精力放在接口的设计上,而不必拘泥于细节的实现。
不同的类(不一定有继承关系)可以实现相同的接口, 这样可以使用接口规定很多类都需要具有的功能。
一个接口可以继承自多个父接口,一个类可以实现多个接口,接口可以实现与类的多重继承同样强大的功能。
在程序设计中,接口应用非常广泛。
接口的定义
接口的定义和类的定义很相似,分为接口的声明和接口体。
接口必须使用interface来声明,格式:
interface 接口名
接口的定义和类的定义很相似,分为接口的声明和接口体。
接口体包含常量定义(可以没有)和方法定义两部分:
所有常量的访问权限一定都是public,而且允许省略public、final和static修饰符;
所有方法都是抽象方法,没有普通方法,其访问权限一定都是public,而且允许省略public、abstract修饰符。 例如:
interface Printable{
public static final int MAX = 100;
public abstract float add(float x, float y);
public abstract float sub(float x, float y);
}
interface Printable{
int MAX = 100;
float add(float x, float y);
float sub(float x, float y);
}
interface Printable{
public int MAX = 100;
public float add(float x, float y);
public float sub(float x, float y);
}
接口支持多重继承
interface 接口名 extends 接口1, 接口2,…… {
…
}
接口继承也是使用extends关键字。
继承的接口方法亦然是public与abstract的。
如果父接口中的接口方法有重复,则进行归并。
接口的使用
一个类通过使用关键字implements声明自己实现一个或多个接口。 如果实现多个接口,则用逗号将接口名隔开。语法格式:
class 类名 implements 接口1,接口2,……{
…
}
如果一个类实现了一个或多个接口,则该类必须实现该接口或所有接口的所有抽象方法。
Class A implements Printable, Addable{
…
} //A类实现Printable和Addable两个接口
在重写接口方法时不仅要去掉abstract修饰符,给出方法体;而且遵循类中关于继承的语法规则,即重写后打的方法的访问权限一定要明显地使用public来修饰,否则会降低访问权限(不允许)。
一个类可以在继承某个类(只能一个,而且可以是抽象类)的同时实现一个或多个接口。例如:
class Dog extends Animal implements Eatable, Sleepable{
…
} //Dog类在继承父类Animal的同时实现Eatable和Sleepable两个接口
注意:implemets如果存在的话必须在最后,不能在extends前。
通过import语句引入包中的接口
通过import语句不仅可以引入JDK包中的类,也可以引入包中的接口。例如:
import java.io.*;
例二
本程序有1个接口和3个类,即Computable接口、China类和Japan类( Computable接口的两个实现类)、Example5_14类(测试主类)。
public interface Computable {
int MAX=100; // 省略了public、static 、final修饰符
int f(int x); // 省略了public、abstract修饰符
}
//与下面的代码等价:
public interface Computable {
public static final int MAX=100;
abstract public int f(int x);
}
public class TestInterface_1 {
public static void main(String args[]) {
China zhang; Japan henlu;
zhang=new China(); henlu=new Japan();
zhang.number=991898+Computable.MAX;
// 接口中的常量通常使用接口名来调用,即:Computable.MAX
henlu.number=941448+Computable.MAX;
// 接口中的常量也可以用实现类的类名来调用,例如:Japan.MAX;
System.out.println("number:"+zhang.number+"求和"
+zhang.f(100));
System.out.println("number:"+henlu.number+"求和"
+henlu.f(100));
}
}
程序运行结果:
number:991998求和5050
number:941548求和166
需要注意以下几点:
类重写的接口方法以及接口中的常量可以被类的对象调用,而且常量也可以用类名或接口名直接调用;
声明接口时,如果在关键字interface前面加上public关键字,就称这样的接口是一个public接口,它可以被任何一个类声明实现;否则,如果没有加上public,它只能被与该接口在同一个包中的类声明实现;
如果父类实现了某个接口,那么其子类也就自然实现了该接口;
接口也可以被继承(仍通过关键字extends声明),子接口将继承父接口中的所有方法和常量。
如果一个类声明实现某个接口,但没有重写接口中的所有方法,则该类必须是抽象类。
由于类实现接口,必须实现所有的接口方法,这对程序员很不方便,Java语言中提出了适配器的概念来解决。
所谓适配器就是实现接口,并对接口中的接口方法提供空实现。
空实现就是有方法体,但方法体中基本上不提供任何有效代码。
适配器类
interface IBiz { //接口
void charge();
boolean withdraw(double amount);
}
class BizAdapter implements Ibiz{ //适配器类
public void charge() { }
public boolean withdraw(double amount){
return false;
}
}
class BizImpl extends BizAdapter{
//通过适配器只需要实现需要的接口方法
public void charge(){
…… //接口方法的真正实现。
}
}
例三
有2个接口和4个类,即Charging接口、Attemperation接口、Bus类(实现了Charging接口)、Taxi类和MovieTheater类(实现了两个)、Example5_15类(测试主类)。
interface Charging{ // 收费
public void charge(); // 收取费用
}
interface Attemperation { // 调节温度
public void controlTemperature();
}
class Bus implements Charging { // 公共汽车
public void charge() {
System.out.println("公共汽车:一元/张,不计算公里数");
}
}
class Taxi implements Charging, Attemperation { //出租车
public void charge() {
System.out.println("出租车:3.20元/公里,起价3公里");
}
public void controlTemperature() {
System.out.println("安装了Hair空调");
}
}
class MovieTheater implements Charging, Attemperation { // 电影院
public void charge() {
System.out.println("电影院:门票,十元/张");
}
public void controlTemperature() {
System.out.println("安装了中央空调");
}
}
public class TestInterface_2 {
public static void main(String args[]) {
Bus busNo7 = new Bus(); Taxi taxiTianyu = new Taxi();
MovieTheater mTheaterHongxing = new MovieTheater();
busNo7.charge(); taxiTianyu.charge();
mTheaterHongxing. charge();
taxiTianyu.controlTemperature();
mTheaterHongxing.controlTemperature();
}
}
程序运行结果:
公共汽车:一元/张,不计算公里数
出租车:3.20元/公里,起价3公里
电影院:门票,十元/张
安装了Hair空调
安装了中央空调
接口与多态
接口回调
接口回调是通过接口实现多态性的核心机制。
接口回调,是指把实现某一接口的类创建的对象的引用赋给该接口声明的接口变量中,那么该接口变量就可以调用被类重写的接口方法。
接口回调非常类似前面介绍的上转型对象调用子类的重写方法
例四
有1个接口和3个类,即ShowMessage接口、TV类和PC类(实现ShowMessage接口)、Example5_16类(测试主类)。
interface ShowMessage {
void show(String s);
}
class TV implements ShowMessage {
public void show(String s) { System.out.println(“TV商标:” + s); }
}
class PC implements ShowMessage {
public void show(String s) { System.out.println("PC商标:" + s ); }
}
public class TestInterface_3 {
public static void main(String args[]) {
ShowMessage sm; //声明接口变量
sm=new TV(); //接口变量中存放对象的引用
sm.show("长城牌电视机"); //接口回调。
sm=new PC(); //接口变量中存放对象的引用
sm.show("联想奔月5008PC机"); //接口回调
}
}
程序运行结果:
TV商标:长城牌电视机
PC商标:联想奔月5008PC机
接口是实现多态的重要途径之一。
由接口产生的多态就是不同的类在实现同一个接口时可能具有不同的实现方式,那么接口变量在回调接口方法时就可能具有多种形态。
一个接口只有方法的特征,没有方法的实现,所以这样的方法可以在不同的地方,被不同的类实现,因而这些实现可以具有不同的行为或功能。
利用接口也可以体现程序设计的所谓“开-闭”原则(Open-Closed Principle),即强调一个程序应当对扩展开放,对修改关闭,增强代码的可维护性。
例五
有1个接口和4个类,即Advertisement接口、AdvertisementBoard类、PhilipsCorp类和LenovoCorp类(实现Advertisement接口)、Example5_17类(测试主类)。
public interface Advertisement {
public void showAdvertisement();
public String getCorpName();
}
public class AdvertisementBoard {
public void show(Advertisement adver) {
System.out.println("广告牌显示“ + adver.getCorpName()
+ "公司的广告词:");
adver.showAdvertisement();
}
}
public class PhilipsCorp implements Advertisement {
//PhilipsCorp实现Avertisement接口
public void showAdvertisement(){
System.out.println("@@@@@@@@@@");
System.out.println("没有最好,只有更好");
System.out.println("@@@@@@@@@@");
}
public String getCorpName() {
return "飞利普" ;
}
}
public class LenovoCorp implements Advertisement {
//LenovoCorp实现Avertisement接口
public void showAdvertisement(){
System.out.println("**************");
System.out.println("让世界变得很小");
System.out.println("**************");
}
public String getCorpName() {
return "联想集团" ;
}
}
public class TestInterface4 {
public static void main(String args[]) {
AdvertisementBoard board = new AdvertisementBoard();
board.show(new PhilipsCorp());
board.show(new LenovoCorp());
}
}
程序运行结果:
广告牌显示飞利普公司的广告词:
@@@@@@@@@@
没有最好,只有更好
@@@@@@@@@@
广告牌显示联想集团公司的广告词:
**************
让世界变得很小
**************
例六
编程模拟USB设备通过USB接口连接到电脑上使用。假设:每一个USB设备只有两个功能(由USB接口决定):安装驱动程序和工作。
定义一个接口USB,规定USB设备都应遵循的操作标准。
interface USB{ //定义一个USB的操作标准
public void install();
public void work();
}
//定义USB设备—手机,实现USB接口。
class Phone implements USB{
public void install(){
System.out.println("安装手机驱动程序。");
}
public void work(){
System.out.println("手机与电脑进行工作。");
}
}
//定义USB设备—打印机,实现USB接口。
class Print implements USB{
public void install(){
System.out.println("安装打印机驱动程序。");
}
public void work(){
System.out.println("进行文件打印。");
}
}
//定义一个电脑类。在电脑上应用USB接口,可以连接任意USB设备。
class Computer{
public void plugin(USB usb){ // 接收USB接口实例
usb.install(); // 调用接口方法
usb.work(); // 调用接口方法
}
}
//定义测试主类。
public class TestInterface5{
public static void main(String args[]){
Computer c = new Computer();
c.plugin(new Phone());
c.plugin(new Print());
}
}
程序运行结果:
安装手机驱动程序。
手机与电脑进行工作。
安装打印机驱动程序。
进行文件打印。
接口小结
接口
如果一个类定义时全部由抽象方法和全局常量(可以没有)组成,那么这种类就称为接口;
接口使用interface定义,分为接口的声明和接口体,接口体则包含常量定义(可以没有)和方法定义两部分;
接口中的所有常量的访问权限一定都是public,而且允许省略public、final和static修饰符;
接口中的所有方法都是抽象方法,没有普通方法,其访问权限一定都是public,而且允许省略public、abstract修饰符;
接口支持多重继承,一个接口可以继承自多个父接口;
一个类通过使用关键字implements声明自己实现一个或多个接口;
一个类可以在继承某个类(只能一个,而且可以是抽象类)的同时实现一个或多个接口;
接口可以实现与类的多重继承同样强大的功能;
接口回调,是指把实现某一接口的类创建的对象的引用赋给该接口声明的接口变量中,那么该接口变量就可以调用被类重写的接口方法;
由接口产生的多态就是不同的类在实现同一个接口时可能具有不同的实现方式,那么接口变量在回调接口方法时就可能具有多种形态。
接口只关心操作,并不关心操作的具体实现 ,我们可以把主要精力放在程序的设计上,而不必拘泥于细节的实现。
接口与抽象类的比较
接口与抽象类的异同
共同点:
都有抽象的行为,约束了它们的实现者(类)或者子类必须实现的业务功能。
接口中的所有行为都是抽象行为;
抽象类通常都有抽象行为。
不同点:
抽象类与其子类从大的范畴来讲它们属于同一类型(或同一种事物),抽象行为也决定了它的所有子类应该具有的行为。因此,抽象类是对属于同一种类型的不同事物的共同行为的一种抽象或表达;
接口中的抽象行为可以被任何类实现,实现这个接口的类可以是不同的类(这些类之间毫无关系)。因此,接口是对不同种类型的事物的共同行为的一种抽象。
接口与抽象类的联系
实现接口的类可以是抽象类,即该实现类并未实现接口中的全部抽象方法。
interface IBiz { //接口
void charge();
boolean withdraw(double amount);
}
abstract class ClsA implements IBiz{
//只实现接口中某些方法。
public boolean withdraw(double amount){
}
}
一个非抽象类可以通过继承实现接口的某个抽象类,来方便地实现接口的功能。
抽象类除了提供重要的需要子类去实现的抽象方法外,还提供了子类可以继承的变量和非抽象方法。如果某个问题需要使用继承才能更好地解决,就可以考虑用抽象类。如果某个问题的解决不需要继承,只是需要若干个类给出某些重要的抽象方法的实现细节,就可以考虑使用接口。
在实际开发中,为了避免单继承的局限,当遇到抽象类和接口都可以使用的情况下要优先考虑使用接口。
JDK常用接口
JDK中定义了很多抽象类和接口。例如:
与图形用户界面(GUI)有关的有:
各类事件监听器接口:EventListener接口及其子接口ActionListener、ItemListener、 KeyListener、 MouseListener、WindowListener等。
与输入输出(IO)流有关的有:
InputStream抽象类、OutputStream抽象类、Reader抽象类、Writer抽象类、 Serializable接口等
与集合框架有关的有:
Collection接口、List接口、Set接口、Map接口等
这里介绍两个常用的接口:
Comparable接口
Cloneable接口
Comparable接口
当一个类实现了Comparable接口,就认为这个类的对象之间是可比较的。
Comparable接口中只有一个抽象方法compareTo,用于同类对象之间进行“大小”比较操作。
这里的“大小”比较,对于不同的类,可能有不同的语义。例如,对于Student类,可以比较两个学生对象的score(分数);对于Circle类,可以比较两个圆对象的半径或面积;等等。
Comparable接口的定义如下:
package java.lang;
public interface Comparable{
public int compareTo(Object o);
}
抽象方法compareTo(Object o),用来判断当前对象相对于给定对象o的顺序,且规定:
当当前对象大于给定对象时,返回值为正整数;
当当前对象等于给定对象时,返回值为0;
当当前对象小于给定对象时,返回值为负整数。
例七
定义学生类Student,使用Comparable接口比较两个学生对象,并按照成绩从高到低输出,若成绩相同,则按照学号从大到小排列。
class Student implements Comparable{
private int num;
private String name;
private float score;
public Student(int num, String name, float score){
this.num = num; this.name = name; this.score = score;
}
public String toString(){
return "学号:" + num + " ; 姓名:" + name + " ; 成绩:" + score;
}
public int compareTo(Object o){
Student stu = (Student)o;
if(this.score > stu.score){
return 1;
}else if(this.score < stu.score){
return -1;
}else{
if(this.num > stu.num){ return 1;
}else if(this.num < stu.num){ return -1;
}else{ return 0;
}
}
}
}
public class TestStudentComparable{
public static void main(String[] args){
Student stu1 = new Student(17001,"张大江",90.0f);
Student stu2 = new Student(17002,"佟小菲",99.5f);
// 通过“比较”将“大”对象排在“小”对象前面
if(stu1.compareTo(stu2)>=0){
System.out.println(stu1+"\n"+stu2);
}else{
System.out.println(stu2+"\n"+stu1);
}
}
}
public int compareTo(Object o){
Student stu = (Student)o;
if(this.score > stu.score){
return 1;
}else if(this.score < stu.score){
return -1;
}else{
if(this.num > stu.num){ return 1;
}else if(this.num < stu.num){ return -1;
}else{ return 0;
}
}
}
}
if(this.num > stu.num){ return -1;
}else if(this.num < stu.num){ return 1;
}else{ return 0;
}
在定义好某个类的对象大小比较规则(如前所述,通过重写compareTo方法来定义比较规则)以后,我们就可以利用Arrays数组的sort(排序)方法对该类的多个对象进行大小排序。
学生对象排序
import java.util.Arrays;
public class TestStudentComparable{
public static void main(String[] args){
Student[] students = { new Student(17001,"张大江", 86.0f),
new Student(17002,"佟小菲", 80.0f),
new Student(17003,"江天河", 98.5f),
new Student(17004,"彭百成", 80.0f),
new Student(17005,"佟小菲", 100.0f) };
Arrays.sort(students);
for(Student stu: students){ System.out.println(stu); }
}
}
总结:如果想利用Arrays数组的sort(排序)方法对某个类(用户自定义类)的多个对象进行排序,排序前必须在实现Comparable接口中的抽象方法compareTo( )时定义好排序规则(见前面的代码)。
但是,Java类库中的许多类(例如String和Date)已经通过实现Comparable接口定义好了排序规则(即按对象的自然顺序),可以直接利用Arrays数组的sort(排序)方法对它们的实例化对象进行排序。
例八
字符串排序
import java.util.Arrays;
public class TestStringSort{
public static void main(String[] args){
String[] strings = new String []{"ruler", "rubber", "pen",
"pencil", "book", "ink", "globe"};
// 其中: new String []可以省略。
Arrays.sort(strings); //按字典序排序
for(String str: strings){
System.out.println(str);
}
}
}
在软件开发中,经常需要创建一个对象的拷贝,这需要用到clone()方法和Cloneable接口。
Cloneable接口在java.lang包中的定义:
public interface Cloneable {
}
它是一个空接口,既没有常量,也没有方法,称为标记接口,仅仅用来表示一个类拥有某些特定的属性。
一个类,一旦实现了Cloneable接口,那么就被标记为它的实例化对象是可克隆的,而且可以使用Object类中定义的clone()方法进行克隆操作。
在Object类中定义的clone( )方法:
protected native Object clone()
throws CloneNotSupportedException;
关键字native表明这个方法是用Java以外的语言编写的,但它是JVM针对自己的平台实现的。
关键字protected 表明该方法只能在同一个包或在其子类中调用。因此,一个实现Cloneable接口的类必须重写该方法并将它的访问修饰符改为public,以便在任何地方都可以调用clone方法。重写时只要调用super.clone( )即可。
若一个类没有实现Cloneable接口,则该类的实例化对象在调用clone方法时会抛出CloneSupportedException异常。
浅度拷贝:如果成员变量是基本类型的话,拷贝的就是它的值;如果是引用类型(如一个对象),拷贝的只是它的引用(而不是它的内容),使得拷贝前后的引用是指向同一个数据空间。
深度拷贝:必须在调用super.clone( )方法之后使用自定义的克隆操作来重写clone方法。
重写clone方法的语法格式:
public Object clone()
throws CloneNotSupportedException{
return super.clone();
}
注意:Object类的clone方法使用了protected修饰,重写时应修改为public修饰。
例九
让学生类Student实现Cloneable接口,并实现学生对象的克隆操作。
//定义Student类,实现Cloneable接口,重写clone() 方法
class Student implements Cloneable{
private int num;
private String name;
private float score;
public Student(int num,String name,float score){
this.num = num; this.name = name; this.score = score;
}
public String toString(){
return "学号:" + num + " ; 姓名:" + name + " ; 成绩:" + score;
}
public void speakAfterTest(){
System.out.println("机考终于结束,我叫" + name
+ ",学号" + num + ",答题很顺利,得了" + score + "分!");
}
public Object clone() throws CloneNotSupportedException{
return super.clone();
}
}
//编写测试主类,调用clone方法实现克隆操作。
public class TestStudentCloneable {
public static void main(String[] args){
Student stu = new Student(17001,"张大江",98.0f);
Student stuByClone = null;
try{
stuByClone = (Student)stu.clone();
} catch(CloneNotSupportedException e){
e.printStackTrace();
}
stu.speakAfterTest(); stuByClone.speakAfterTest();
System.out.print("这两个对象");
if(stu == stuByClone){
System.out.println("是同一个对象!");
}else{
System.out.println("不是同一个对象,后者是前者的克隆!");
}
}
}
小结
JDK常用接口
Comparable接口用于实现同类对象之间的“大小”比较操作,当一个类实现了Comparable接口,就认为这个类的对象之间是可比较的;
该接口只有一个抽象方法compareTo(Object o),用来判断当前对象相对于给定对象o的顺序,即“大小”关系;
在对象数组、集合框架中的排序依赖于对象比较操作。
Cloneable接口是一个空接口,没有接口方法,仅起标记作用;
一个类实现Cloneable接口后,就可以使用来自Object的clone方法(对象克隆方法);
Object的clone方法提供对象的字段到字段的拷贝,属于浅度克隆:若想深度克隆,则必须按照自己的需要定制和重写clone方法。
接口设计与实现
项目分析
项目功能模块分析
项目书籍处理模块中定义了BookDao接口,通过抽象方法给出了如何查询书籍详细信息的标准操作:
public Book queryBook(String id);
public Book[] queryBook();
BookDaoImpl类是书籍处理业务类,实现了BookDao接口。
项目登录验证模块中定义了UserDao接口,判断用户名与密码是否存在。
public User login(String uid, String pwd);
UserDaoTesting是UserDao的实现类。
本章小结
抽象类是一种特殊的类,最大特点是包含了抽象方法(抽象类和抽象方法都用abstract修饰) ;
如果一个类是某个抽象类的非抽象子类,那么它必须重写父类的全部抽象方法(给出方法体) ;
可以通过上转型对象调用非抽象子类按不同方式重写的方法,从而可实现不同的行为或功能(多态)。
如果一个类定义时全部由抽象方法和全局常量(可以没有)组成,那么这种类就称为接口,接口使用interface定义;
一个接口可以继承自多个父接口,一个类可以实现一个或多个接口;
由接口产生的多态就是不同的类在实现同一个接口时可能具有不同的实现方式,那么接口变量在回调接口方法时就可能具有多种形态;
设计程序时,经常会使用抽象类(或接口),其原因在于:抽象类(或接口)只关心操作,而不关心这些操作具体实现的细节,这样可以使程序的设计者把主要精力放在程序的设计上,而不必拘泥于细节的实现上;基于抽象类与接口,都可以实现多态性;多态设计程序的好处:可以体现程序设计的所谓“开-闭”原则;但是,抽象类与接口在很多方面又是不同的,要注意区分;
Comparable接口用于实现同类对象之间的“大小”比较操作,当一个类实现了Comparable接口,就认为这个类的对象之间是可比较的;
一个类实现Cloneable接口后,就可以使用来自Object的clone方法(对象克隆方法) 。
课后习题
选择题
1.对于下列代码,哪个叙述是正确的?
interface Com {
int MAX;
public void stop();
void start();
abstract void loop();}
- A. void start();会导致编译出错。
- B. public void stop();会导致编译出错。
- C. abstract void loop();会导致编译出错。
- D. int MAX;会导致编译出错。
正确答案: D
(1)不能对接口直接进行对象的实例化操作;
(2)接口中只有全局常量,没有成员变量,所以接口中不能有构造方法;
2.下列(A,B,C,D)哪个代码替换源文件Com.java中的【代码】不会导致编译错误。
public interface Com {
int M = 200;
int f();
}
class ImpCom implements Com {
【代码】}
-
A.
public int f() {
return 100+M;
}
-
B.
int f() {
return 100;
}
-
C.
public double f(){
return 2.6;
}
-
D. public abstract int f();
正确答案: A
每一个接口必须定义子类,子类使用implements关键字实现接口;
而且这里的100和M的类型是一样的,这样返回的数据类型也是一样的
3.下列哪个叙述是正确的?
- A. final 类不可以有父类。
- B. 可以用new运算符和abstract类的构造方法创建对象。
- C. 如果一个类中有abstract方法,这个类必须是abstract类。
- D. static方法也可以同时是abstract方法。
正确答案: C
这里可以看我上面写得抽象类的方法
4.下列哪个叙述是正确的?
- A. final 类不可以有子类。
- B. abstract类中只可以有abstract方法。
- C. abstract类中可以有非abstract方法,但该方法不可以用final修饰。
- D. bird instanceof C的值是true。
instanceof 是 Java 的保留关键字。它的作用是测试它左边的对象是否是它右边的类的实例,返回 boolean 的数据类型。无法判断
final类可以有子类
abstract类中可以有抽象方法和非抽象方法
不能用final和abstract修饰同一个方法
5.ABCD注释标注的哪行代码有错误?
abstract class AAA {
final static void speakHello() {} //A
final abstract void cry(); //B
static final int f(){ return 0 ;} //C
abstract float g(); //D}
- A. final static void speakHello() {}
- B. final abstract void cry();
- C. static final int f(){ return 0 ;}
- D. abstract float g();
正确答案: B
不能用final和abstract修饰同一个方法
6.ABCD注释标注的哪行代码有错误?
abstract class Animal {
int m =100;
}
class Dog extends Animal{
double m;
}
public class E {
public static void main(String args[]){
Animal animal = null; //A
Dog dog = new Dog();
animal = dog; //B
dog.m = 3.14; //C
animal.m = 3.14; //D }}
- A. Animal animal = null;
- B. animal = dog;
- C. dog.m = 3.14;
- D. animal.m = 3.14;
正确答案: D
这里的类型是int型所以发生了错误
7.ABCD注释标注的哪行代码有错误?
public interface Com {
abstract void cry(); //A
public int x = 0; //B
static int y = 0; //C}
abstract class Cat implements Com {
abstract void cry(); //D}
- A. abstract void cry();
- B. public int x = 0;
- C. static int y = 0;
- D. abstract void cry();
正确答案: D
在重写接口方法时不仅要去掉abstract修饰符,给出方法体;而且遵循类中关于继承的语法规则,即重写后打的方法的访问权限一定要明显地使用public来修饰,否则会降低访问权限(不允许)。
8.ABCD注释标注的哪行代码有错误?
interface Com {
int MAX = 100; //A
void f();}
abstract class Animal implements Com {
int MIN ; //B}
class Dog extends Animal{
public void f() {
MIN = 10; //C
MAX = 200; //D } }
- A. int MAX = 100;
- B. int MIN ;
- C. MIN = 10;
- D. MAX = 200;
正确答案: D
在重写接口方法时不仅要去掉abstract修饰符,给出方法体;而且遵循类中关于继承的语法规则,即重写后打的方法的访问权限一定要明显地使用public来修饰,否则会降低访问权限(不允许)。
填空题
阅读程序题(给出【代码】注释标注的代码的输出结果)
interface Com {
int add(int a,int b);
}
abstract class People {
abstract int add(int a,int b);
}
class Student extends People implements Com{
public int add(int a,int b) {
return a+b; }}
public class E {
public static void main(String args[]) {
Student stu = new Student();
Com com = stu;
int m = com.add(12,6);
People p = stu;
int n = p.add(12,6);
System.out.printf("%d:%d",m,n); //【代码】 }}
正确答案:
(1) 18:18
这里就是使用了抽象类和接口
阅读程序题(给出【代码】注释标注的代码的输出结果)
interface Com {
int add(int a,int b);
public static int get(int n){
return n; }
public default int see(int n){
return n; }
public default int look(int n){
return n; }}
class A implements Com{
public int add(int a,int b) {
return a+b; }
public int see(int n){
return n+1; }}
public class E {
public static void main(String args[]) {
A a = new A();
int m = a.add(12,6);
int n =Com.get(12);
int t = a.see(6);
int q = a.look(6);
System.out.printf("%d:%d:%d:%d",m,n,t,q); //【代码】 }}
正确答案:
(1) 18:12:7:6
判断题
可以同时用final和abstract修饰同一个方法。
- A. 对
- B. 错
正确答案: 错
abstract类中只可以有abstract方法。
- A. 对
- B. 错
正确答案: 错
接口中的方法也可以用private或protected修饰。
- A. 对
- B. 错
ople implements Com{
public int add(int a,int b) {
return a+b; }}
public class E {
public static void main(String args[]) {
Student stu = new Student();
Com com = stu;
int m = com.add(12,6);
People p = stu;
int n = p.add(12,6);
System.out.printf("%d:%d",m,n); //【代码】 }}
正确答案:
(1) 18:18
这里就是使用了抽象类和接口
阅读程序题(给出【代码】注释标注的代码的输出结果)
interface Com {
int add(int a,int b);
public static int get(int n){
return n; }
public default int see(int n){
return n; }
public default int look(int n){
return n; }}
class A implements Com{
public int add(int a,int b) {
return a+b; }
public int see(int n){
return n+1; }}
public class E {
public static void main(String args[]) {
A a = new A();
int m = a.add(12,6);
int n =Com.get(12);
int t = a.see(6);
int q = a.look(6);
System.out.printf("%d:%d:%d:%d",m,n,t,q); //【代码】 }}
正确答案:
(1) 18:12:7:6
判断题
可以同时用final和abstract修饰同一个方法。
- A. 对
- B. 错
正确答案: 错
abstract类中只可以有abstract方法。
- A. 对
- B. 错
正确答案: 错
接口中的方法也可以用private或protected修饰。
- A. 对
- B. 错
正确答案: 错