Java---抽象类和接口
抽象类:
抽象类的基本概念:
在面向对象的概念中,所有的对象都是通过类来描述并创建的,但是有一种特殊的类,并不能用完整的信息来描述一个具体的对象,这样的类就是抽象类,不能完整描述对象相关信息的方法,通常称为抽象方法,也就是说,抽象类中需要包含抽象方法。
例如:
几何图形中有三角形,圆形,矩形,多边形的我们通常称为形状,形状就是对各种具体几何图形的模糊化,抽象化的通称,但形状本身又不能归结于某一个具体的几何图形,假如说形状就是三角形,这样的定义显然与现实不符,形状都具有面积,但是不同具体形状的面积有不同的计算方法,如果形状不能具体化,那么面积就无法计算,如果将形状表现为抽象,那么面积就是其中的抽象方法。
而抽象类一般作为父类,在抽象类中定义抽象方法,并交给抽象类的子类去实现抽象方法,也就是说在抽象类的各个子类中去定义完善某个具体对象的描述。
抽象方法和抽象类的定义:
抽象方法
是一种“特殊”的方法,该方法只有方法的定义部分(只有方法头)
而没有方法的实现部分(没有方法体
),在访问权限和返回类型之间需要用abstract关键字进行修饰。
定义格式如下:
权限修饰符 abstract returntype 方法名([参数列表]);
注意:
1:抽象方法只有方法头部并以“;”结束,没有方法体,所以后面不能有"{}"
2:构造方法不能被定义为抽象方法。
3:抽象类在定义时需要在class前添加abstract关键字修饰,类体中至少包含一个抽象方法,其余内容和普通方法一样,也可以定义普通常量,普通方法等。
定义格式如下:
[权限修饰符] abstract class 类名称{
成员属性;
常量;
构造方法([参数列表])//构造方法
{..........}
[权限修饰符] returntype 方法名([参数列表])//普通方法
{..........}
abstract returntype 方法名([参数列表])//抽象方法
{..........}
}
注意:
1:由于抽象类是需要被子类继承的,所以抽象类不能使用final关键字修饰。
2:抽象类中定义的抽象方法是要被子类覆盖的,所以抽象方法不能用final关键字修饰,而且抽象类的访问权限不能是private。
3:抽象方法不能被static关键字修饰,抽象方法必须是实例方法。
4:抽象类不能用new运算符创建它的对象。
抽象类本身不能实例化,抽象类必须要有子类来继承它,否则抽象类就失去了存在的意义。
由于抽象类的子类实现了抽象类的抽象方法,从多态的角度讲,一般通常都是定义抽象类的引用指向抽象类子类的实例,自动完成上转型,因此,使用抽象类是对象多态性的一个很好的体现。
举例:
//Animal是抽象类[父类] Sheep是抽象类的子类[父类]
//通过上转型方式定义抽象类的子类对象
Aninmal sheep1=new Sheep();
抽象类的应用:
关于抽象类的应用,这里我使用我们的实验中的一个进行作为例子:
-----顾客就餐实验:
首先我们先对实验内容进行分析,根据上图,很清楚的表明了Restaurant为继承类,也就是所谓的父类,其中它包含了3个抽象方法和一个普通方法,但这些方法的数量并不是一成不变的,我们可根据自己的理解进行调整。
对于问候或者加工这种无论是什么餐厅都是相同的,那么我们就将其写在父类中,而对于菜品的种类和点菜过程这种应写在不同的子类中。
代码如下所示;
package Restuarant;
abstract class restuarant {
//实验内容要求的抽象类
public abstract void pay();
public abstract void order();
public abstract void repast();
public void message(){//新增普通方法
System.out.println("顾客用餐完毕,请通知服务人员尽快打扫对应的餐桌!");
}
public void process(){//实验内容要求的普通类
System.out.println("您所点的菜品正在准备,请耐心等待!");
System.out.println("----------------");
System.out.println("菜品制作完成");
}
}
上文我们说过,抽象类本身不能实例化,抽象类必须要有子类来继承它,否则抽象类就失去了存在的意义,那么接下来我们就创建它的子类。
首先子类必须重写父类所有的
抽象方法,当然子类也可以创建属于自己的变量和方法。
package Restuarant;
import java.util.Scanner;
public class Pizza extends restuarant{
double A=29.9;
double B=19.7;
double C=49.8;
double D=9.9;
double sum=0.0;
public double getSum() {
return sum;
}
public void setSum(double sum) {
this.sum = sum;
}
public double getD() {
return D;
}
public void setD(double d) {
D = d;
}
public double getC() {
return C;
}
public void setC(double c) {
C = c;
}
public double getB() {
return B;
}
public void setB(double b) {
B = b;
}
public double getA() {
return A;
}
public void setA(double a) {
A = a;
}
public void show_pizza() {//子类特有的方法
System.out.println("本店菜品如下:");
System.out.println("1-双人套餐:"+A+",2-单人套餐:"+B+",3-全家桶:"+C+",4-儿童套餐:"+D);
}
//重写父类的
@Override
public void repast() {
System.out.println("----------------");
System.out.println("顾客正在2号餐厅吃饭......");
}
@Override
public void pay() {
System.out.println("请点击你需要的菜品号:结束点单请输入0!");
Scanner in = new Scanner(System.in);
int[] Q=new int[3];
for(int i=0;i<4;i++){
Q[i]=in.nextInt();
if(Q[i]==0)
break;
if(Q[i]==1)
sum+=A;
else if(Q[i]==2)
sum+=B;
else if(Q[i]==3)
sum+=C;
else if(Q[i]==4)
sum+=D;
}
}
@Override
public void order() {
System.out.println("您需要支付的金额为:"+sum);
System.out.println("等待用户支付......");
System.out.println("----------------");
System.out.println("支付成功,祝您用餐愉快!");
}
}
其他两个子类均是如此,根据子类不同的特点将其就行改写和完善,这里就不过多赘述了。
package Restuarant;
import java.util.Scanner;
public class KFC extends restuarant{
double a=15.9;
double b=7.9;
double c=4.9;
double sum=0.0;
public double getSum() {
return sum;
}
public void setSum(double sum) {
this.sum = sum;
}
public double getA() {
return a;
}
public void setA(double a) {
this.a = a;
}
public double getB() {
return b;
}
public void setB(double b) {
this.b = b;
}
public double getC() {
return c;
}
public void setC(double c) {
this.c = c;
}
public void show_KFC() {
System.out.println("本店菜品如下:");
System.out.println("1-炸鸡:"+a+",2-爆米花:"+b+",3-可乐:"+c);
}
@Override
public void order() {
System.out.println("请点击你需要的菜品号:结束点单请输入0!");
Scanner in = new Scanner(System.in);
int[] B=new int[3];
for(int i=0;i<3;i++){
B[i]=in.nextInt();
if(B[i]==0)
break;
if(B[i]==1)
sum+=a;
else if(B[i]==2)
sum+=b;
else if(B[i]==3)
sum+=c;
}
}
@Override
public void pay() {
System.out.println("您需要支付的金额为:"+sum);
System.out.println("等待用户支付......");
System.out.println("----------------");
System.out.println("支付成功,祝您用餐愉快!");
}
@Override
public void repast() {
System.out.println("----------------");
System.out.println("顾客正在1号餐厅吃饭......");
}
}
package Restuarant;
import java.util.Scanner;
public class breakfast extends restuarant{
double noodles=8;
double dumplings=7.9;
double rices=4.9;
double Casserole=13;
double sum=0.0;
public double getNoodles() {
return noodles;
}
public void setNoodles(double noodles) {
this.noodles = noodles;
}
public double getDumplings() {
return dumplings;
}
public void setDumplings(double dumplings) {
this.dumplings = dumplings;
}
public double getRices() {
return rices;
}
public void setRices(double rices) {
this.rices = rices;
}
public double getCasserole() {
return Casserole;
}
public void setCasserole(double casserole) {
Casserole = casserole;
}
public double getSum() {
return sum;
}
public void setSum(double sum) {
this.sum = sum;
}
public void show_breakfast() {
System.out.println("本店菜品如下:");
System.out.println("1-面条:"+noodles+",2-饺子:"+dumplings+",3-米饭:"+rices+",4-砂锅"+Casserole);
}
@Override
public void pay() {
System.out.println("请点击你需要的菜品号:结束点单请输入0!");
Scanner in = new Scanner(System.in);
int[] Q=new int[3];
for(int i=0;i<4;i++){
Q[i]=in.nextInt();
if(Q[i]==0)
break;
if(Q[i]==1)
sum+=noodles;
else if(Q[i]==2)
sum+=dumplings;
else if(Q[i]==3)
sum+=rices;
else if(Q[i]==4)
sum+=Casserole;
}
}
@Override
public void order() {
System.out.println("您需要支付的金额为:"+sum);
System.out.println("等待用户支付......");
System.out.println("----------------");
System.out.println("支付成功,祝您用餐愉快!");
}
@Override
public void repast() {
System.out.println("----------------");
System.out.println("顾客正在3号餐厅吃饭......");
}
}
package Restuarant;
import java.util.Scanner;
public class Test {
public static void main(String[]args){
System.out.println("请输入你想去的餐厅:");
Scanner in = new Scanner(System.in);
int select=in.nextInt();
if(select==1){
KFC kfcs=new KFC();
kfcs.show_KFC();
kfcs.order();
kfcs.pay();
kfcs.process();
kfcs.repast();
kfcs.message();
}
if(select==2){
Pizza pizza=new Pizza();
pizza.show_pizza();
pizza.order();
pizza.pay();
pizza.process();
pizza.repast();
pizza.message();
}
if(select==3){
breakfast breakfasts=new breakfast();
breakfasts.show_breakfast();
breakfasts.order();
breakfasts.pay();
breakfasts.process();
breakfasts.repast();
breakfasts.message();
}
}
}
输出:
请输入你想去的餐厅:
3
本店菜品如下:
1-面条:8.0,2-饺子:7.9,3-米饭:4.9,4-砂锅13.0
您需要支付的金额为:0.0
等待用户支付......
----------------
支付成功,祝您用餐愉快!
请点击你需要的菜品号:结束点单请输入0!
3
0
您所点的菜品正在准备,请耐心等待!
----------------
菜品制作完成
----------------
顾客正在3号餐厅吃饭......
顾客用餐完毕,请通知服务人员尽快打扫对应的餐桌!
接口:
接口的基本概念:
现实世界的接口通常是指两个不同物体之间相互交互所必须通过的一个中介,没有,这个中介,两者就无法进行交互,我们把这个中介,称为,接口,例如门窗户,楼道插孔电梯间等,在软件世界中所提到的接口有狭义和广义之分的,狭义的接口是指某个程序设计语言所提供的API,广义的接口是指人与软件交互的图形界面,通过这个图形界面,人们才能使用此软件,JAVA中的接口是一种特殊的类
,接口中只能包含常量和抽象方法
,属于符合数据类型,是狭义的接口
接口的作用:
通过之前的学习,我们了解到,JAVA是纯面向对象的程序设计语言,为了避免多继承产生的二义性,在java中类的继承只能实现单继承,不能实现多继承,但多继承也有许多优点,她允许一个子类可以继承多个父类的属性和方法,使得子类能够具有多个父类的特性,但是java并没有因为只允许单继承而抛弃多继承带来的便利,为了结合多继承的优点,又不与单继承冲突
,就提供了接口这个新技术来实现多继承的效果
,通过使用接口可以体现面向对象中的多态机制。
接口的定义和实现:
Java使用关键字interface定义接口
,接口的定义和类很相似,分为接口声明和接口体两个部分。
定义格式:
[public] interface 接口名[extends interface1,interface2, interface3,interface4........]{//interface1,interface2, interface3,interface4为接口名
double E=2.178;//常量的定义
void dosomething(int i,double x);//抽象方法的定义
}
接口中能够定义的成员只有常量和抽象方法两种(或者两选一),不能包含构造方法**,接口中的常量默认是public访问权限,并且是静态的,所以常量定义前的public static final关键字可以省略不写,接口中抽象方法的访问权限默认也是public,所以抽象方法前面的public abstract关键字也可以省略不写,接口不存在类的单继承限制,一个接口可以继承多个接口,多个接口名之间用逗号隔开。**
例如:
interface myinterface extends interface1,interface2, interface3,interface4........{//interface1,interface2, interface3,interface4为接口名
..............
}
注意:
<1>接口的继承不存在最高层,也就是不存在最顶层的接口,而类的继承存在最高层,最顶层是java.lang.Object.
<2>接口的实现需要交给类来完成,实现接口的类继承了接口中的常量并且要重写接口中所有的抽象方法,一个类需要在声明中使用关键字implements表示要实现的一个或多个接口,如果一个类要实现多个接口,多个接口名字之间需要通过逗号隔开。
例如:
class A implements Interface1,Interface2
实现类要给出接口中抽象方法的具体行为,如果一个类只重写了接口中的部分抽象方法,相当于这个类还继承有接口中的抽象方法,那么这个类就不能称为普通类,需要定义为抽象类。
假如上述例子中的A类并没有完全实现接口 Interface1,Interface2和中的所有抽象方法,那么它就必须定义为abstract类:
如下所示:
abstract class A implements Interface1,Interface2
注意:
<1>接口的实现类重写接口中定义的抽象方法时,需要显式添加public访问权限(否则无法实现覆盖),去掉abstract关键字,同时给出方法体部分。
<2>如果父类实现了某个接口,那么子类也就自然实现了这个接口,子类不必再显式地使用关键字implements声明这个接口。
抽象类和接口的应用:
关于抽象类和接口结合的应用,这里我还是使用我们的实验中的一个进行作为例子:
-----模拟物流的过程
首先我们还是对实验内容进行分析,Transportion为抽象类,也就是所谓的父类,其中包含实验所需的变量,物流号,具体的交通工具名称等,还包含一个抽象方法----运输方法,那么子类需要实现将这个抽象的运输方法具体化,也就是说运输方法可以是汽车,飞机,卡车等。
代码如下:
package transport1;
abstract class Transportion {
String model;
String code;
String admin;
String number;
public abstract void transportion();
public String getModel() {
return model;
}
public void setModel(String model) {
this.model = model;
}
public String getNumber() {
return code;
}
public void setNumber(String number) {
this.code = number;
}
public String getAdmin() {
return admin;
}
public void setAdmin(String admin) {
this.admin = admin;
}
}
—创建子类
和上述餐厅点餐实例的子类基本相同,唯一不同的地方就是该子类不仅继承了一个父类还实现了一个接口carable,那么该子类也需要对接口中所有的抽象方法进行重写。
package transport1;
public class Car extends Transportion implements carable{
@Override
public void transportion() {
// TODO Auto-generated method stub
System.out.println(admin+"开着"+model+"在拉货物");
}
public Car(String admin,String model,String number) {
this.admin=admin;
this.model=model;
this.number=number;
}
@Override
public void upkeep() {
System.out.println("汽车保养完成!");
}
}
–货物运输类
和上述餐厅点餐实例的子类基本相同,这里不过多赘述!
package transport1;
public class send_task extends Transportion {
String number;
double goodweight;
String admin;
public send_task(String number,double goodweight,String admin) {
this.number = number;
this.goodweight=goodweight;
this.admin=admin;
}
@Override
public void transportion() {
System.out.println("编号为:"+number+"的货物即将开始运输!");
}
public void sendBefor(){//送货前
System.out.println("订单开始处理,仓库验货中......");
System.out.println("货物重量为"+goodweight);
System.out.println("货物检验完毕!");
System.out.println("货物填装完毕 !");
System.out.println("已通知运货人");
System.out.println("快递单号:"+number);
System.out.println("=======================");
}
public void sendAfter(String name,String model){//送货后
System.out.println(admin+"已将编号为"+number+"的"+model+"货车归还");
}
public void send(String name,String model){//送货中
System.out.println(admin+"正在驾驶编号为"+number+"的"+model+"发送货物");
System.out.println("货物运输中....");
}
}
—定义类Martphone实现接口GPS,也就是实现货物的定位功能!
package transport1;
public class Martphone implements GPS{
@Override
public void showcoordinate() {
System.out.println("当前货物状态为:北京----->石家庄");
System.out.println("=======================");
}
}
package transport1;
public interface GPS {
public void showcoordinate();
}
—定义保养功能,由于在现实生活中某些交通工具需要定期进行保养,而有的则不需要。
package transport1;
public interface carable {
public void upkeep();
}
–测试类
package transport1;
public class text {
public static void main(String[]args) {
Car car=new Car("张三","汽车","5");
car.transportion();
send_task tasks=new send_task("EMS1234",23.5,"张三");
tasks.sendBefor();
tasks.transportion();
tasks.send("张三","汽车");
Martphone phone=new Martphone();
phone.showcoordinate();
tasks.sendAfter("张三","汽车");
}
}
输出:
张三开着汽车在拉货物
订单开始处理,仓库验货中......
货物重量为23.5
货物检验完毕!
货物填装完毕 !
已通知运货人
快递单号:EMS1234
=======================
编号为:EMS1234的货物即将开始运输!
张三正在驾驶编号为EMS1234的汽车发送货物
货物运输中....
当前货物状态为:北京----->石家庄
=======================
张三已将编号为EMS1234的汽车货车归还
接口和抽象类的比较:
相同点:
<1>抽象类和接口都包含抽象方法,抽象类需要被子类继承,子类去实现抽象类中定义的抽象方法,接口需要被类去实现,实现接口的类需要实现接口中定义的所有抽象方法。
<2>抽象类和接口都不能通过new关键字来创建自己的对象。
<3>抽象类和接口都是引用数据类型,可以声明抽象类和接口类型的引用变量,并将子类的对象实例赋给抽象类和接口变量,也就是说抽象类和接口都可以按照上转型方式去创建对象。
不同点:
那么它们究竟如何进行选择呢?
如果说某个问题需要使用继承才能更好的解决,例如,子类除了需要重写父类的抽象方法,还需要从父类继承一些变量或者一些中的普通方法,那么就可以使用抽象类,但是如果某个问题不需要继承,只是需要若干个类给出某些重要的抽象方法的实现逻辑,此时便可以考虑接口。