抽象类
在继承的层次结构中,随着每个新子类的出现,类会变得越来越来明确和具体.如果从一个子类追溯到父类,类就会变得更通用,更加不明确.类的设计应该确保父类包含他的子类的共同特征.有时候,一个父类设计得非常抽象,以至于他都没有任何具体的实例.这样的类称为抽象类
GeometricObject类定义成Circle和Rectangle类的父类.GeometricObject类模拟了几何对象的共同特征.Circle类和Rectangle类都包含分别计算圆和矩形的面积和周长的方法getArea()和getPerimeter().因为可以计算所有几何对象的面积和周长,所以最好在GeometricObject类中定义getArea()和getPerimeter()方法.但是,这些方法不能在GeometricObject类中实现,因为它们的实现取决于几何对象的具体类型.这样的方法称为抽象方法(abstract method),在方法头中使用abstract修饰符表示.在GeometricObject类中定义了这些方法后,GeometricObject就成为一个抽象类.在类头使用abstract修饰符表示该类为抽象类.
GeometricObject.java代码:
package chapter14;
public abstract class GeometricObject
{
private String color = "white";
private boolean filled;
private java.util.Date dateCreated;
/*Construct a default geometric object*/
protected GeometricObject()
{
dateCreated = new java.util.Date();
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public boolean isFilled() {
return filled;
}
public void setFilled(boolean filled) {
this.filled = filled;
}
public java.util.Date getDateCreated() {
return dateCreated;
}
// public void setDateCreated(java.util.Date dateCreated) {
// this.dateCreated = dateCreated;
// }
//Construct a geometric object with color and filled value
protected GeometricObject(String color,boolean filled)
{
dateCreated = new java.util.Date();
this.color = color;
this.filled = filled;
}
//Return a string representation of this object
public String toString()
{
return "created on " +dateCreated + "\ncolor:" + color + " and filled: " + filled;
}
/*Abstract method getArea*/
public abstract double getArea();
/*Abstract method getPerimeter*/
public abstract double getPerimeter();
}
抽象类和常规类很像,但是不能使用new操作符创建它的实例.抽象方法只有定义而没有实现.它的实现由子类提供.一个包含抽象方法的类必须声明为抽象类
抽象类的构造方法定义为protected,因为它只被子类使用.创建一个具体子类的实例时,他的父类的构造方法被调用以初始化父类定义的数据域.
抽象类GeometricObject为几何对象定义了共同特征(数据和方法),并且提供了正确的构造方法.因为不知道如何计算几何对象的面积和周长,所以,getArea和getPerimeter定义为抽象方法.这些方法在子类中实现.
Circle.java代码:
package chapter14;
public class Circle extends GeometricObject {
private double radius;
public Circle(){
}
public Circle(double radius){
this.radius = radius;
}
public Circle(double radius,String color,boolean filled){
this.radius = radius;
setColor(color);
setFilled(filled);
}
public double getRadius(){
return radius;
}
public void setRadius(double radius){
this.radius = radius;
}
public double getArea(){
return radius * radius * Math.PI;
}
public double getDiameter(){
return 2 * radius;
}
public double getPerimeter(){
return 2 * radius * Math.PI;
}
public void printCircle(){
System.out.println("The circle is created " + getDateCreated() + " and the radius is " + radius);
}
}
Rectangle.java代码:
package chapter14;
public class Rectangle extends GeometricObject{
private double width;
private double height;
public Rectangle(){
}
public Rectangle(double width,double height){
this.width = width;
this.height = height;
}
public Rectangle(double width,double height,String color,boolean filled){
this.width = width;
this.height = height;
setColor(color);
setFilled(filled);
}
public double getWidth(){
return width;
}
public void setWidth(double width){
this.width = width;
}
public double getHeight(){
return height;
}
public void setHeight(double height){
this.height = height;
}
public double getArea(){
return width * height;
}
public double getPerimeter(){
return 2 * (width + height);
}
}
为什么要用抽象方法
你可能会疑惑在GeometricObject类中定义方法getArea和getPerimeter为抽象的而不是在每个子类中定义它们会有什么好处.下面的例子就能看出在GeometricObject中定义它们的好处.
例子创建了两个几何对象:一个圆和一个矩形,调用equalArea方法来检查它们的面积是否相同,然后调用displayGeometricObject方法来显示它们.
TestGeometricObject.java代码:
package chapter14;
public class TestGeometricObject
{
public static void main(String[] args)
{
//Create two geometric objects
GeometricObject geoObject1 = new Circle(5);
GeometricObject geoObject2 = new Rectangle(5,3);
System.out.println("The two objects have the same area? " + equalArea(geoObject1,geoObject2));
//Display circle
displayGeometricObject(geoObject1);
//Display rectangle
displayGeometricObject(geoObject2);
}
/*A method for comparing the areas of two geometric objects*/
public static boolean equalArea(GeometricObject object1,GeometricObject object2)
{
return object1.getArea() == object2.getArea();
}
/*A method for displaying a geometric object*/
public static void displayGeometricObject(GeometricObject object)
{
System.out.println();
System.out.println("The area is " + object.getArea());
System.out.println("The perimeter is " + object.getPerimeter());
}
}
Circle类和Rectangle类中覆盖了定义在GeometricObject类中的getArea()和getPerimeter()方法.
GeometricObject geoObject1 = new Circle(5);
GeometricObject geoObject2 = new Rectangle(5,3);
创建了一个 新圆和一个新矩形,并把它们赋值给变量geoObject1和geoObject2.这两个变量都是GeometricObject类型的
当调用equalArea(geoObject1,geoObject2)时,由于geoObject1是一个圆,所以object1.getArea()使用的是Circle类定义的getArea()方法,而geoObject2是一个矩形,所以object2.getArea()使用的是Rectangle类的getArea()方法.
类似的,当调用displayGeometricObject(geoObject1)时,使用Circle类中定义的getArea和getPerimeter方法,而当调用displayGeometricObject(geoObject2)时,使用的是在Rectangle类中定义的getArea和getPerimeter方法.JVM在运行时根据对象的类型动态的决定调用那一个方法.
关于抽象类的几个关注点
1.抽象方法不能包含在非抽象类中.如果抽象父类的子类不能实现所有的抽象方法,那么子类也必须定义为抽象的.换句话说,在抽象类扩展的非抽象子类中,必须实现所有的抽象方法.还要注意到,抽象方法是非静态的.
2.抽象类是不能使用new操作符来初始化.但是,仍然可以定义它的构造方法,这个构造方法在它的子类的构造方法中调用.例如.GeometricObject类的构造方法在Circle类和Rectangle类中调用
3.包含抽象对象的类必须是抽象的.但是,可以定义一个不包含抽象方法的抽象类.在这种情况下.不能使用new操作符创建该类的实例.这种类是用来定义新子类的基类的.
4.即使子类的父类是具体的,这个子类也可以是抽象的.例如.Object类是具体的,但是它的子类如GeometricObject可以是抽象的
5.子类可以覆盖父类的方法并将它定义为abstract.这是很少见的,但是它在当父类的方法实现在子类中变得不合法时是很有用的.在这种情况下,子类必须定义为abstract.
6.不能使用new操作符从一个抽象类创建一个实例.但是抽象类可以用作一种数据类型.因此,下面的语句是创建一个GeometricObjetc实例的数组,这个语句是正确的:
GeometricObject[] objects = new GeometricObject[10];
然后可以创建一个GeometricObject的实例,并将它的引用赋值给数组,如下所示:
objects[0] = new Circle();
举例:日历类Calendar和公历类GregorianCalender
一个java.util.Date的实例表示以毫秒为单位的特定时间段.java.util.Calendar是一个抽象的基类,可以提取出详细的日历信息,例如,年,月,日,小时,分钟和秒.Calendar类的子类可以实现特定
日历系统.例如,公历(Gregorian历),阴历和犹太历.目前,Java支持公历类java.util.GregorianCalendar.
Calendar类的域常量
下面的例子显示当前时间的日期和时间信息
TestCalendar.java代码:
package chapter14;
import java.util.*;
public class TestCalendar {
public static void main(String[] args) {
// Construct a Gregorian calendar for the current date and time
Calendar calendar = new GregorianCalendar();
System.out.println("Current time is " + new Date());
System.out.println("YEAR:\t" + calendar.get(Calendar.YEAR));
System.out.println("MONTH:\t" + calendar.get(Calendar.MONTH));
System.out.println("DATE:\t" + calendar.get(Calendar.DATE));
System.out.println("HOUR:\t" + calendar.get(Calendar.HOUR));
System.out.println("HOUR_OF_DAY:\t" +
calendar.get(Calendar.HOUR_OF_DAY));
System.out.println("MINUTE:\t" + calendar.get(Calendar.MINUTE));
System.out.println("SECOND:\t" + calendar.get(Calendar.SECOND));
System.out.println("DAY_OF_WEEK:\t" +
calendar.get(Calendar.DAY_OF_WEEK));
System.out.println("DAY_OF_MONTH:\t" +
calendar.get(Calendar.DAY_OF_MONTH));
System.out.println("DAY_OF_YEAR: " +
calendar.get(Calendar.DAY_OF_YEAR));
System.out.println("WEEK_OF_MONTH: " +
calendar.get(Calendar.WEEK_OF_MONTH));
System.out.println("WEEK_OF_YEAR: " +
calendar.get(Calendar.WEEK_OF_YEAR));
System.out.println("AM_PM: " + calendar.get(Calendar.AM_PM));
// Construct a calendar for September 11, 2001
Calendar calendar1 = new GregorianCalendar(2001, 8, 11);
System.out.println("September 11, 2001 is a " +
dayNameOfWeek(calendar1.get(Calendar.DAY_OF_WEEK)));
}
public static String dayNameOfWeek(int dayOfWeek) {
switch (dayOfWeek) {
case 1: return "Sunday";
case 2: return "Monday";
case 3: return "Tuesday";
case 4: return "Wednesday";
case 5: return "Thursday";
case 6: return "Friday";
case 7: return "Saturday";
default: return null;
}
}
}
接口
接口(interface)是一种与类相似的结构,只包含常量与抽象方法.接口在许多方面都与抽象类很相似,但是它的目的是指明多个对象的共同行为.例如,使用正确的接口,可以指明这些对象是可比较的,可食用的或可克隆的.
下面是一个接口的例子:
public interface Edible
{
/*Describe how to eat*/
public abstract String howToEat();
}
在JAVA中,接口被看做是一种特殊的类.就像常规类一样,每个接口都被编译为独立的字节码文件.与抽象类相似,不能使用new操作符创建接口的实例,但是在大多数情况下,使用接口或多或少有点像使用抽象类.例如,可以使用接口作为引用变量的数据类型或类型转换的结果等.
现在,可以使用Edible接口来明确一个对象是否是可食用的.这需要使用implements关键字让对像的类实现这个接口来完成.类和接口之间的关系称为接口继承(interface inheritance)
TestEdible.java代码:
package chapter14;
public class TestEdible
{
public static void main(String[] args)
{
Object[] objects = {new Tiger(),new Chicken(),new Apple()};
for(int i = 0; i < objects.length; i++)
if(objects[i] instanceof Edible)
System.out.println(((Edible)objects[i]).howToEat());
}
}
class Animal
{
//Data fields,construction,and methods omitted here
}
class Chicken extends Animal implements Edible
{
public String howToEat()
{
return "Chicken: Fry it";
}
}
class Tiger extends Animal
{
}
abstract class Fruit implements Edible
{
//Data fields,constructs,and methods omitted here
}
class Apple extends Fruit
{
public String howToEat()
{
return "Apple: Make apple cider";
}
}
class Orange extends Fruit
{
public String howToEat()
{
return "Orange: Make orange juice";
}
}
Chicken类扩展子Animal类,并实现Edible以表明小鸡是可食用的.当一个类实现接口时,该类实现了定义在接口中的所有带确切签名和返回类型的方法.Chicken类实现了howToEat方法.
Fruit类实现Edible。因为它不实现howToEat方法,所以Fruit必须表示为abstract.Fruit的具体子类必须实现howToEat方法.Apple类和Orange类实现howToEat方法.
main方法创建由Tiger,Chicken和Apple的三个对象构成的数组,如果这个元素是可食用的,调用howToEat方法.
Comparable接口
comparable接口的定义如下所示:
//Interface for comparing objects,defined in java.lang
package java.lang;
public interface Comparable
{
public int compareTo(Object o);
}
compareTo方法判断这个对象相对于给定对象o的顺序,并且当这个对象小于,等于或大于给定对象o时,分别返回负整数,0和正整数.
Java类库中的许多类(例如,String和Date类)实现了Comparable接口以定义对象的自然顺序,如果你检查这些类的源代码,就会发现这些类中使用的关键字implements,如下所示:
从两个对象中找出最大者的通用方法max可以定义为如图a或图b所示:
ComparableRectangle.java代码:
package chapter14;
public class ComparableRectangle extends Rectangle implements Comparable<Object>
{
/*Construct a ComparableRectangle with specified properties*/
public ComparableRectangle(double width,double height)
{
super(width,height);
}
/*Implement the compareTo method defined in Comparable*/
public int compareTo(Object o)
{
if(getArea() > ((ComparableRectangle)o).getArea())
return 1;
else if(getArea() < ((ComparableRectangle)o).getArea())
return -1;
else
return 0;
}
}
ComparableRectangle类扩展自Rectangle类并实现Comparable方法,关键字implements表示ComparableRectangle类继承Comparable接口的所有常量,并实现该接口的方法.CompareTo方法比较两个矩形的面积.ComparableRectangle类的一个实例也是Rectangle,GeometricObject,Object和Comparable的实例.
现在,可以使用max方法找出两个ComparableRectangle对象中较大的一个,请看下面的例子:
ComparableRectangle rectangle1 = new ComparableRectangle(4,5);
ComparableRectangle rectangle2 = new ComparableRectangle(3,6);
System.out.println(Max.max(rectangle1,rectangle2));
ActionListener接口
按钮就是动作来源的源对象(source object).需要创建一个对象能够处理按钮上的动作事件.这个对象称为监听器(listener),
不是所有的对象都能成为动作事件的监听器.一个对象要成为源对象上动作事件的监听器,需要满足两个条件:
1.这个对象必须是ActionListener(事件监听器)接口的一个实例.该接口定义了所有动作监听器共有的动作.
2.ActionListener对象listener必须使用方法source.addActionListener(listener)注册给源对象.
ActionListener接口包含处理事件的actionPerformed方法.监听器必须覆盖该方法来响应事件.
HandleEvent.java代码:
package chapter14;
import javax.swing.*;
import java.awt.event.*;
public class HandleEvent extends JFrame
{
private static final long serialVersionUID = 1L;
public HandleEvent()
{
//Create two buttons
JButton jbtOK = new JButton("OK");
JButton jbtCancel = new JButton("Canel");
//Create a panel to hold buttons
JPanel panel = new JPanel();
panel.add(jbtOK);
panel.add(jbtCancel);
add(panel);//Add panel to the frame
//Register listeners
OKListenerClass listener1 = new OKListenerClass();
CancelListenerClass listener2 = new CancelListenerClass();
jbtOK.addActionListener(listener1);
jbtCancel.addActionListener(listener2);
}
public static void main(String[] args)
{
JFrame frame = new HandleEvent();
frame.setTitle("Handle Event");
frame.setSize(200,150);
frame.setLocation(200, 100);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
class OKListenerClass implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
System.out.println("OK button clicked");
}
}
class CancelListenerClass implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
System.out.println("Cancel button clicked");
}
}
Cloneable接口
接口包括常量和抽象方法,但是Cloneable接口是一个特殊情况.在java.lang包中的Cloneable接口的定义如下所示:
package java.lange;
public interface Cloneable
{
{
这个接口是空的.一个带空体的接口称为标记接口(marker interface).一个标记接口即不包括常量也不包括方法.它用来表示一个类拥有某些特定的属性.实现Cloneable接口的类标记为可克隆的,而且他的对象可以使用在Object类中定义的clone()方法克隆.
Java库中的很多类(例如,Date,Calendar和ArrayList)实现Cloneable.这样,这些类的实例可以被克隆.例如,下面的代码:
Calendar calendar = new GregorianCalendar(2003,2,1);
Calendar calendar1 = calendar;
Calendar calendar2 = (Calendar)calendar.clone();
System.out.println("calendar == calendar1 is " + (calendar == calendar1));
System.out.println("calendar == calendar2 is " + (calendar == calendar2));
System.out.println("calendar.equals(calendar2) is " + calendar.equals(calendar2));
//显示
calendar == calendar1 is true
calendar == calendar2 is
false
calendar.equals(calendar2) is true
可以使用clone方法克隆一个数组.例如,下面的代码
int[] list1 = {1,2};
int[] list2 = list1.clone();
list1[0] = 7;list[1] = 8;
System.out.println("list1 is " + list1[0] + ", " + list1[1]);
System.out.println("list2 is " + list2[0] + ", " + list2[1]);
//显示
list1 is 7,8
list2 is 1,2
为了定义一个自定制类来实现Cloneable接口,这个类必须覆盖Object类中的clone()方法.下面代码定义了一个实现Cloneable和Comparable的名为House的类
House.java代码:
package chapter14;
public class House implements Cloneable,Comparable<Object>
{
private int id;
private double area;
private java.util.Date whenBuilt;
public House(int id,double area)
{
this.id = id;
this.area = area;
whenBuilt = new java.util.Date();
}
public int getId() {
return id;
}
// public void setId(int id) {
// this.id = id;
// }
public double getArea() {
return area;
}
// public void setArea(double area) {
// this.area = area;
// }
public java.util.Date getWhenBuilt() {
return whenBuilt;
}
// public void setWhenBuilt(java.util.Date whenBuilt) {
// this.whenBuilt = whenBuilt;
// }
//Override the protected clone method defined in the object
//class,and strength its accessibility
public Object clone() throws CloneNotSupportedException
{
return super.clone();
}
//Implement the compareTo method defined in Comparable
public int compareTo(Object o)
{
if(area > ((House)o).area)
return 1;
else if(area < ((House)o).area)
return -1;
else
return 0;
}
}
House类实现在Object类中定义的clone方法.方法头是:
protected native Object clone() throws CloneNoSupportedException;
关键字native表明这个方法不适用java写的,但它是JVM针对自身平台实现的,关键字protected限定方法只能在同一包内或其他子类中访问.由于这个原因,House类必须覆盖该方法并将它的可视性修饰符改为public,这样,该方法就可以在任何一个包中使用。因为Object类中针对自身平台实现的clone方法完成了克隆对象的任务,所以,在House类中 的clone方法只要调用super.clone()即可.在Object类中定义的clone方法会抛出CloneNotSupportedException异常
House类实现定义在Comparable接口中的compareTo方法.该方法比较两个房子的面积.
现在,可以创建一个House类的对象,然后从这个对象创建一个完全一样的拷贝,如下所示:
House house1 = new House(1,1750,50);
House house2 = (House)house1.clone();
house1和house2是两个内容相同的不同对象.Object类中的clone方法将原始对象的每个数据域复制给目标对象。如果一个数据域是基本类型,复制的就是它的值。例如area(double类型)的值从house1复制到house2.如果一个数据域是对象,复制的就是该域的引用.例如,域whenBuilt是Date类,所以,它的引用被复制给house2.因此,尽管house1==house2为假,但是house1.whenBuilt==house2.whenBuilt为真。这称为浅复制(shallow copy)而不是深复制(deep copy)这意味着如果数据域是对象类型,那么复制的是对象的引用,而不是它的内容.
抽象类与接口的比较
参考链接:
http://www.importnew.com/12399.html
http://www.cnblogs.com/dolphin0520/p/3811437.html
http://yinny.iteye.com/blog/1152430