抽象类与接口
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
抽象类与接口
一.抽象类(Abstract Class)与抽象方法
1.抽象方法定义
定义方法时用关键字abstract 修饰的方法,其特征是该方法没有方法体。
public abstract void noAction();
2.抽象类
定义类时用关键字abstract 修饰的类
abstract class 类名{}
例:
abstract class MyClass{
int myInt;
abstract public void noAction(); //抽象方法!
public int getMyInt(){//非抽象方法
return myInt;
}
}
3.说明:
A.抽象方法必须包含在抽象类中,然而,抽象类中的方法不必都是抽象方法。
B.抽象类不能用来创建对象,但可以用来声明变量。
C.在抽象类中定义抽象方法在于建立一种约定,如果没有这种约定,则需要在各个
子类中定义不同名称(不同实现)的方法,将使程序变得十分混乱.一般而言,在抽象类中定义了抽象方法后,就可以在子类中覆盖掉父类的同名方法,然后给予特定的实现。
注意:继承了抽象类的子类一定要覆盖父类的抽象方法,如果不是这样子类也将成为抽象类了。
4.final关键字
用来修饰变量,方法和类
4.1.public final int A=10; //表示变量为常数,程序运行中不能再改变其值,常数一般为对象
所共有,故一般也将常量声明为static final
Public static final double PI=3.1415926; PI 值保持不变,并为所有对象所共有
4.2.Public final void f(){} //表示方法为最终方法,其含义为:继承包含final 方
法的类的子类不能覆盖该最终方法,以达到保护父类方法的行为的目的. 即
是说,最终方法具有保护方法的功能,禁止对该方法的行为作出任何修改。
4.3.final class MyClass{} 表示该类不能被继承,因为这样的类可能与其他类具
有千丝万缕的联系,如果被继承,将导致灾难性后果.与abstract修饰的类相反
例如:
//1。
定义抽象类:
abstract class Shape{
double area;
public double area(){
return area; //返回形状面积
}
public abstract void draw(); //抽象方法
}
//2。
定义派生类
class Triagle extends Shape{
double d,h;
public Triagle(int a,int b){
d=a;
h=b;
area=d*h/2; // area 面积,从父类继承而来的}
public void draw(){//覆盖抽象方法
System.out.println(“画三角形”);
}
}
//3。
定义派生类
class Rectangle extends Shape{
double width,height;
public Rectangle (int a,int b){
width=a;
height=b;
area=width*height; // area 面积,从父类继承而来的}
public void draw(){//覆盖抽象方法
System.out.println(“绘制矩形”);
}
}
//4。
定义测试类
public class Test{
public static void main(String[] args){
Shape tri=new Triangle(10,3);
Shape rect=new Rectangle(5,8);
System.out.pritnln(tri.area());
Tri.draw();
System.out.pritnln(rect.area());
rect.draw();
}
}
//输出结果:
15.0
画三角形
40.0
绘制矩形
二.接口:
接口可以理解为两个对象相互通信的规则或约束。
声明抽象方法的原因在于定义一种子类可以覆盖进而使用它的约束。
故接口本身就是一种约束.
接口将设计与实现分离,可以使编写的代码更加通用,便于维护。
在Java中,接口设计称为”优雅”设计
(一)。
接口的声明
interface接口名[extends] 父接口
{
/* 接口体*/
//常量声明
[public] [static] [final]常量类型常量名=常量值
//抽象方法声明
[public] [abstract]返回类型方法名(参数表列) [throws 异常列表] }
(二)。
接口的特点
1.Java接口的特点:
所有方法都是抽象的
所有变量都是静态常量
public interface MyInter{
public static final int MAX=100;
public int MIN=1; //编译器会自动添加static final 关键字来修饰MIN
public abstract void method();
public void method2(int a); //编译器会自动添加abstract 关键字来修饰method2 }
2.接口继承
一个接口可以继承一个或多个其它接口,这意味着子接口拥有父接口的成员.
接口继承的语法:
interface ChildInterface extends ParentInterface1, ParentInterface2,…
3.接口回调
接口回调指把实现了某一接口的类的实例赋给该接口声明的接口变量中。
那么该接口变量就可以调用被类实现的接口中的方法。
这是非常重要的接口性质。
4.接口做方法的参数
接口变量可以作为方法的参数,这样,传递个这个接口的形参就可以是实现了这个接口的任何类的实例。
5. 说明:
A. 由于接口中的所有方法都是抽象方法,所以,实现接口的类必须实现接口中
所有的抽象方法。
B. 一个类可以实现多个接口.
C. 实现接口语法
implements <Interrface1>,<Interrface2>,…
public class MyClass implements Inter1,Inter2{
//类体
}
D. 可以用接口声明变量
<接口名> var
E. 接口中的方法不能使用private 或protected 修饰.但访问修饰可以缺省,
因为类只能用覆盖的方法实现接口中的抽象方法.
//接口1
interface Interface1{
public void method1();
}
//接口2
interface Interface2{
public void method2();
}
public class TestInterface implements Interface1,Interface2 {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
TestInterface obj=new TestInterface();
obj.method1();
obj.method2();
}
public void method1() {
// TODO Auto-generated method stub
System.out.println("method1 overdide");
}
public void method2() {
// TODO Auto-generated method stub
System.out.println("method2 overdide");
}
}
//接口回调
interface ShowMessage {
void show();
}
class TV implements ShowMessage{
public void show() {
// TODO Auto-generated method stub
System.out.println("电视显示图像");
}
}
class PC implements ShowMessage{
public void show() {
// TODO Auto-generated method stub
System.out.println("计算机显示文本内容");
}
}
public class Test{
public static void main(String[] args){
ShowMessage msg=null;//声明接口变量
msg=new TV();//保存引用到接口变量
msg.show();//接口回调
msg=new PC();//保存引用到接口变量
msg.show();////接口回调
}
}
三.接口与抽象类的区别:
1从编程角度看:
接口中不定义任何与实现相关的内容,在抽象类中可以定义具有具体实现的方法,这个方法是各子类都有的完全相同的行为。
2 从概念角度看
接口与抽象类都是一个抽象体;
抽象类是对实体的抽象,如交通工具就是一个抽象类,它是对各式各样的交通工具的抽象。
抽象类和子类在概念上是一致的,子类和抽象类之间表现为”是”的关系, 如,”汽车”是”交通工具”;
接口是对行为的抽象,例如:“可以飞”是个接口,它是对各种可飞的实体的行为的抽象。
接口与实现它的类在概念上是不一致的,实现类仅仅是实现了接口所定义的协议,使它具有某个功能,例如,”飞机”与“可以飞”,我们可以说”飞机可以飞”
练习:
1.梯形,圆,矩形都必须有计算面积的功能,定义一个接口包含一个计算面积的方法,
并用相应的类来实现这个接口。
2设计一个接口,其内有一个方法welcom(). 用两个类分别实现这个接口,其中一个类用英文表示欢迎,另一个类用中文表示欢迎;再写一个类,该类有一个方法,此方法包含一个接口参数,在方法的实现体中,用接口参数调用接口方法。
最后写一个测试类,完成测试。
3. 设计一个接口:
interface IFlyable {
String fly();
}
然后用两个类Plane和Bird分别实现这个接口,Plane类显示“飞机靠发动机引擎在天空飞行;Bird 类显示”鸟在靠翅膀在天空中飞翔“。
最后写一个测试类,该类有一个方法,在方法中有一个接口参数,其实现体调用fly() 显示结果。
系统常用接口
一.Cloneable接口
这是系统提供的一个接口,该接口产生对象的副本,Java产生对象副本时规定:1.创建该对象的类必须实现Cloneable接口
2.创建该对象的类必须覆盖Object类的Clone()方法
protected native Object clone() throws CloneNotSupportedException;
public class Cloneable2 implements Cloneable{//实现Cloneable接口
String s;
int a;
Public Cloneable2(String s,int a){
this.s=s;
this.a=a;
}
public Object clone(){//覆盖Object类的Clone()方法,返回自身副本
try{
return super.clone();//调用Object类的clone()方法
}
catch(CloneNotSupportedException e2){
return null;
}
}
}
public static void main(String[] args){
Cloneable2 obj1=new Cloneable2(“apple”,12);
Cloneable2 obj2=( Cloneable2)obj1.clone(); //生成ob1的副本
//输出hashCode
System.out.println(“原本:”+obj1.hashCode());
System.out.println(“副本:”+obj2.hashCode());
System.out.println(“原本:”+obj1.s+”“+obj1.a);
System.out.println(“副本:”+obj2.s+”“+obj2.a);
}
//运行结果:
原本:2536009 //两个对象的hsahCode 不一样,说明它们处于不同的存储区。
副本:8581339
原本:apple 12
副本:apple 12
二.Comparator 接口
1.接口定义:
public interface Comparator{
public int compare(Object a,Object b);
}
2.说明:
2.1. compare(Object a,Object b)方法的返回值规定如下:
当对象a<b 对象时,返回–1;
当对象a=b 对象时,返回0;
当对象a>b 对象时,返回1
2.2. 该接口在java.util包中。
实现两个对象之间的比较,比较规则由方法实现
三.Comparable接口
基本数据类型间可以比较大小,但是对象之间如何比较大小呢?对象分为可比较对象和不可比较对象。
如果对象实现了Comparable接口,则称该对象为可比较对象,反之,称该对象为不可比较对象。
1.接口声明如下
public interface Comparable{
public int compareTo(Object obj);
}
2. 说明:
2.1. compareTo(Object obj)方法的返回值规定如下:
当本对象<obj 对象时,返回–1;
当本对象=obj 对象时,返回0;
当本对象>obj 对象时,返回 1
2.2. 该接口在java.util包中。
实现本对象与参数obj的比较,比较规则由方法实现。
练习:
1.设计一个类,用以封装一个矩形Rectangle,并实现Comparable接口,从而使该类的实例之间可以比较大小,比较规则由程序确定。
最后声明一个Rectangle(对象)数组,并对该数组进行排序。
2. 设计一个Person类,该类有两个数据成员:职务,年龄。
要求该类的实例为可比较对象。
假设”职务”取值如下值:军长,师长,旅长,团长,营长,连长,排长。
另外写一个测试程序,对Person数组进行排序。
四.Enumeration 接口
实现了Enumeration 接口的对象内部对于给定的元素需要输出时,提供一种按顺序输出的方式.要使自己的类具有顺序输出功能,可以实现Enumeration 接口。
该接口位于java.util包,它包含两个方法:
public interface Enumeration{
boolean hasMore Elements(); //检查是否有元素,返回true ,表示还有元素.
Object nextElement()返回下一个元素,并自动调整到下一个元素位置}
例如:
package lcm.math;
/**
* <p>Title:家庭成员列表</p>
*
* <p>Description: 实现接口Enumeration</p>
*
* <p>Copyright: Copyright (c) 2006</p>
*
* <p>Company: </p>
*
* @author not attributable
* @version 1.0
*/
public class Family implements java.util.Enumeration{
private String father,mother,child;
private int currentIndex=0;//当前项索引
public Family(String father,String mother,String child) {
this.father="father:"+father;
this.mother="mother:"+mother;
this.child="child:"+child;
}
public boolean hasMoreElements(){
return currentIndex<3;
}
public Object nextElement(){
String member;
switch(currentIndex){
case 0:
member=father;
break;
case 1:
member=mother;
break;
case 2:
member=child;
break;
default:
throw new IndexOutOfBoundsException("索引越界!");
}
currentIndex++; //调整索引
return member;
}
}
public class Foreach implements Enumeration{
Object[] data=null;
Int count=0;
Public Foreach(Object[] data)
this.data=data;
}
public Boolean hasMoreElements(){
return count<data.length;
}
public Object nextElement(){
if(count<data.length)
return data[count++];
return null;
}
public static void main(String[] args){
String[] persons={“dada”,”qwqewq”,”zaq”};
Foreach e=new Foreach(persons);
//遍历对象,获得所有元素
while(e.hasMoreElements()){
System.out.println(e.nextElement());
}
}
}
思考:
修正下列代码中的错误:
class Abstract{
int a;
private void f();
public void g();
public h(){
a=5;
}
}
提示:从抽象类和非抽象类两方面分析和修改。
五.包
包(Package)是类的容器,用于把类名空间分隔开。
Java引入包的主要目的是避免名字冲突。
这样便于对庞大的类进行管理。
通常,一个java源文件可以包含以下4个部分中的任何一个(或全部):
单个包语句(可选)
任意数目的导入语句(可选)
单个公共类声明(必须)
对包来说是私有的任意数目的类(可选)
1.定义一个包
package <包名> //要求该语句必须为源文件的第一条语句!
定义一个包后,意味着这名称与操作系统的一个目录关联起来。
2.查找包和CLASSPATH
java 运行时系统可以通过两种方式之一查找包:默认情况下,java运行时系统
使用当前的工作目录作为它的起始点,如果包在当前工作目录或是其子目录中,
就可以找到包;其次,使用在CLASSPA TH环境变量指定的一个或多个目录路径查找,这种方式可以将包放到任何目录下。
package mypackage;
public class Balance {
String name;
double bal;
Balance(String n,double b){
name=n;
bal=b;
}
void show(){
if(bal<0)
System.out.println("--> ");
System.out.println(name +": $"+bal);
}
}
class AccountBalance{
public static void main(String[] argv){
Balance[] current=new Balance[3];
current[0]=new Balance("张三",154.0);
current[1]=new Balance("汪芜",254.1);
current[2]=new Balance("留洋",194.5);
for(int I=0;I<3;I++){
current[I].show();
}
}
}
编译这个源文件,确保生成的.class文件也在mypackage目录中,然后使用下面的
命令执行AccountBalance类:
java mypackage. AccountBalance
记住,执行该命令时,位置需要在mypackage之上的目录,或者对CLASSPA TH环境变量进行相应的设置。
AccountBalance 现在是包mypackage的一部分,它不能通过自身来执行,即不能用下面的命令行:java AccountBalance
AccountBalance 必须和包名在一起使用。