抽象类和接口的学习

来源:互联网 发布:二手正版pk10ub软件 编辑:程序博客网 时间:2024/05/24 06:33

抽象类和接口学习笔记

         已经学习了如何编写简单的程序创建和现实GUI组件,你能编写代码以响应像点击一个按钮一样的用户动作吗?如下图所示,当点击一个按钮时,控制台上就会显示一条消息。



        为了编写这样的代码,必修要很了解接口。借口就是定义多个类(特别是不相关的类)的共同行为。在学习接口之前,我来介绍一下相关主题:抽象类。

抽象类

       和C++一样,抽象类就是包含抽象方法的类。为什么需要抽象方法呢,举一个例子来说。Circle和矩Rectangle都是Shape类的子类,Circle和Rectangle都有getArea()和getPerimeter()方法,但是所有的Shape都有这两种方法,所以最好的方式就是在Shape类中定义getArea()和getPerimeter()方法,但是这两个方法不能再Shape类中实现,因为它们的具体实现方式都取决于具体的形状,所以这样的方法成为抽象方法。在方法头中使用abstract修饰符即可,如:

public abstract class GeometricObject {public abstract double getArea();public abstract double getPerimeter();}

抽象类的构造方法一般被定义为protected,因为它只被子类使用。

关于抽象类的几个注意点:

1.   抽象方法不能包含在非抽象类中。如果抽象父类的子类不能实现所有的抽象方法,那子类也必须定义为抽象的。还要注意到,抽象方法是非静态的。

2.   抽象类是不能使用new操作符来初始化的,但是仍然可以定义它的构造方法,这个构造方法在它的子类的构造方法中调用。

3.   包含抽象对象的类必须是抽象的,但是也可以定义一个不包含抽象方法的抽象类。

4.   即使子类的父类的方法是具体的,子类也可以是抽象的。

5.   子类可以覆盖父类的方法并将它定义为abstract。

6.   不能使用new操作符从一个抽象类创建一个实例,但是抽象类可以作为一种数据类型

为什么要使用抽象类

        举一个例子来说,人,兔子,老虎都属于动物,动物都有一系列的相同属性,比如都有眼睛鼻子耳朵嘴巴还有四肢,但是人,兔子,老虎吃的方式不一样,人要煮熟了吃,兔子慢慢啃,老虎用咬的。于是,我们定义一个动物类(定义为抽象类),人,兔子,老虎为其派生类,其他的属性因为都一样,我们只要为派生类”定制“它们的特征方法就行了,这样书写代码简单而又高效。

接口

        接口是一种与类相似的结构, 只包含常量和抽象方法。接口在很多地方都与抽象类相似,但是它的目的是指明多个对象的共同行为。一个类要继承一个接口的时候需要用关键字implement。

Comparable接口

        接口定义为:

public interface Comparable{    Public int compareTo(Object o);}

       compareTo 方法判断这个对象对于给定对象o的顺序,并且当这个对象小于等于或大于给定对象o,分别返回负整数,0,或正整数。Java类库中的许多类如String和Date,都实现了Comparable接口以定义对象的自然顺序。

自定义继承Comparable 类 comparableRectangle

代码如下:

public class comparableRectangle extends Rectangle implements Comparable {public comparableRectangle(double width, double height) {super(width, height);}public int compareTo(Object o) {if (getArea() > ((comparableRectangle) o).getArea())return 1;else if (getArea() < ((comparableRectangle) o).getArea())return -1;elsereturn 0;}}

        定义了comparableRectangle类之后就可以方便比较原来不方便比较的对象,从而可以实现方法Max以得到对象的最大值。


ActionListener接口

        现在你已经准备好了编写一个小程序来解决文章开头的提出的问题。为了响应一个按钮的点击,需要编写代码来处理点击动作。按钮是动作的源对象。需要创建一个对象能够处理按钮上的动作事件。这个对象称为监听器(listener)。

不是所有的对象都能成为动作事件的监听器。一个对象要成为源对象上的动作事件的监听器,需要满足两个条件:

1.   这个对象必须是ActionListener(事件监听器)接口的一个实例。该接口定义了所有动作监听器的共有动作。

2.   ActionListener对象listener必须使用方法source.addActionListener(listene)注册给源对象。

        ActionListener接口包含处理事件的actionPerformed方法。监听器必须覆盖该方法来响应事件。下面的代码实现了点击OK,就会显示“OK button clicked”,点击Cancel显示“Cancel button clicked”。

HandleEvent 类

import javax.swing.*;import java.awt.*;import java.awt.event.*;public class HandleEvent extends JFrame{       public HandleEvent()       {       JButton jbtOK = new JButton("OK");       JButton jbtCancel = new JButton("Cancel");       JPanel p = new JPanel();       p.add(jbtOK);       p.add(jbtCancel);       add(p);       OKListenerClass listener1 = new OKListenerClass();       CancelListenerClass listener2 = new CancelListenerClass();       jbtOK.addActionListener(listener1);       jbtCancel.addActionListener(listener2);       }}class OKListenerClass implements ActionListener{public void actionPerformed(ActionEvent e){JOptionPane.showMessageDialog(null, "OK!");}}class CancelListenerClass implements ActionListener{public void actionPerformed(ActionEvent e){JOptionPane.showMessageDialog(null, "Cancel!");}}

Main函数

import java.util.*;import javax.swing.*;import java.io.*;public class Main {public static void main(String[] args) {JFrame frame = new HandleEvent();frame.setTitle("Handle Event");frame.setSize(200,150);frame.setLocation(200, 100);frame.setDefaultCloseOperation(HandleEvent.EXIT_ON_CLOSE);frame.setVisible(true);}}

Cloneable接口

        接口包括常量和抽象方法,但是Cloneable接口是一个特殊情况,它的定义如下:

package java.lang;public interface Cloneable{}

        这个接口是空的,它用来标记某些特定的属性,接下来看下面一段代码:

public class Main {public static void main(String[] args) {Calendar date = new GregorianCalendar(2003,2,1);Calendar date1 = date;Calendar date2 = (Calendar)date.clone();System.out.println("date == date1 is "+(date==date1));    //trueSystem.out.println("date == date2 is "+(date==date2));    //falseSystem.out.println("date.equals(date2) is "+date.equals(date2));  //true}}

        代码中,将date的引用复制给date1,所以date和date1都指向相同的date对象,接下来创建了一个新对象,它是date的克隆,然后将这个新对象的引用赋值给date2.date2和date是内容相同的不同对象。这个克隆的意义和人类社会的克隆意义相同。

        当然,也可以用clone方法克隆一个数组。

接口与抽象类



用Comparable接口实现对一个对象数组的排序

import java.util.*;import javax.swing.*;import java.io.*;public class Main {public static void sort(Comparable[] list){Comparable currentMin;int currentMinIndex;for(int i=0;i<list.length;++i){currentMin = list[i];currentMinIndex = i;for(int j = i+1;j<list.length-1;++i){if(currentMin.compareTo(list[j])>0){currentMin = list[j];currentMinIndex = j;}}if(currentMinIndex != i){list[currentMinIndex] = list[i];list[i]=currentMin;}}}public static void printList(Object[] list){for(int i=0;i<list.length;++i){System.out.print(list[i]+" ");}System.out.println();}public static void main(String[] args) {Integer[] intArray = {new Integer(2),new Integer(4),new Integer(3)};Double[]  doubleArray={new Double(3.4),new Double(1.3),new Double(-22.1)};Character[] charArray={new Character('a'),new Character('J'),new Character('r')};String[] stringArray = {"Tom","John","Fred"};sort(intArray);sort(doubleArray);sort(charArray);sort(stringArray);printList(intArray);printList(doubleArray);printList(charArray);printList(stringArray);}}

Biginteger和BigDecimal类

        有时候会碰到计算特别大的数,比如111111111111111111111111*12233333333333,这样的计算需要显然不能用之前的方式来进行。我们不能用任何的数据类型来装下这么大的数,它已经操作了int、float、double的数据类型的范围。那么如何解决这样的计算需求呢?这时候,就需要进行大数操作。

在java.math这个包中有两个进行大数操作的类:java.math.BigInteger和java.math.BigDecimal。从名字上可以知道这两个类的作用了吧。很明显,前者是进行整数的大数操作的,后者是进行小数的大数操作的。下面来看一下实例。

        实例1:

import java.math.BigInteger;public classBigIntegerDemo01 {    public static voidmain(String[] args){       String num1="88379348888888478403839479";       String num2="838777777333333333337";             BigInteger big1=newBigInteger(num1);       BigInteger big2=newBigInteger(num2);       System.out.println(big1.add(big2));//加法操作       System.out.println(big1.subtract(big2));//加法操作       System.out.println(big1.multiply(big2));//乘法操作       System.out.println(big1.divide(big2));//除法操作       BigInteger[] result=big1.divideAndRemainder(big2);       System.out.println(big1.toString()+"/"+big2.toString()+"的商:"+result[0]);       System.out.println(big1.toString()+"/"+big2.toString()+"的余数:"+result[1]);    }}

        构造方法publicBigInteger(String val)是 将 BigInteger 的十进制字符串表示形式转换为BigInteger。大整数操作可以像其它类型的数据一样进行加法、减法、乘法、除法等操作。需要特别说明的是除法操作。publicBigInteger divide(BigInteger val)这个方法只能得到一个“商“,要想的到余数需要用public BigInteger[] divideAndRemainder(BigInteger val)这个方法。divideAndRemainder()这个方法返回的是存储有”商“和”余数“的BigInteger数组。下面看看BigDecimal如何使用。

       实例2:

package cn.tty.math;import java.math.BigDecimal;public classBigDecimalDemo02 {    public static voidmain(String[] args) {       String num1="84995.333333333323";       String num2="894.99";       //保留5位小数       System.out.println(BigDecimalDemo02.round(BigDecimalDemo02.add(num1, num2), 5));       //保留4位小数       System.out.println(BigDecimalDemo02.round(BigDecimalDemo02.subtract(num1, num2), 4));       //保留3位小数       System.out.println(BigDecimalDemo02.round(BigDecimalDemo02.multiply(num1, num2), 3));       //保留2位小数       System.out.println(BigDecimalDemo02.divide(num1, num2,2));    }    public static doubleadd(String num1,String num2){       //将 BigDecimal 的字符串表示形式转换为BigDecimal       BigDecimal b1=newBigDecimal(num1);       BigDecimal b2=newBigDecimal(num2);       returnb1.add(b2).doubleValue();    }    public static doublesubtract(String num1,String num2){       //将 BigDecimal 的字符串表示形式转换为BigDecimal       BigDecimal b1=newBigDecimal(num1);       BigDecimal b2=newBigDecimal(num2);       returnb1.subtract(b2).doubleValue();    }    public static doublemultiply(String num1,String num2){       //将 BigDecimal 的字符串表示形式转换为BigDecimal       BigDecimal b1=newBigDecimal(num1);       BigDecimal b2=newBigDecimal(num2);       returnb1.multiply(b2).doubleValue();    }    public static doubledivide(String num1,String num2,int scale){       BigDecimal b1=newBigDecimal(num1);       BigDecimal b2=newBigDecimal(num2);       //下面的“2”表示需要保留的小数位数,“BigDecimal.ROUND_HALF_UP”常量表示的是四舍五入的模式       returnb1.divide(b2,scale,BigDecimal.ROUND_HALF_UP).doubleValue();          }       public static double round(doublenum1,int scale){       BigDecimal big1=newBigDecimal(num1);       BigDecimal big2=newBigDecimal(1);       returnbig1.divide(big2,scale,BigDecimal.ROUND_HALF_UP).doubleValue();//任何数除于1都等于本身    }}

        上面的例子重新包装了BigDecimal的加减乘除操作,是这些方法的使用更符合本例的需要。加减乘的操作就不用多说了,很直接,很简单,需要说明的还是除法操作。 

BigDecimal的除法重载了很多。其中有一种是publicBigDecimal divide(BigDecimal divisor,int scale,RoundingMode roundingMode)。这种方法指定了保留的小数位数(scale)和四舍五入的模式(roundingMode)。比如,“ROUND_HALF_DOWN“的模式表示向“最接近的”数字舍入,如果与两个相邻数字的距离相等,则为上舍入的舍入模式。

//下面的操作不涉及大数操作的内容,但涉及到保留小数位数       double x=874.748;       double y=893.32;       double z=x*y;       System.out.println("x * Y = "+z);       System.out.println("x * Y = "+(int)(z*10)/10.0);//保留1为小数       System.out.println("x * Y = "+(int)(z*100)/100.0);//保留2为小数       System.out.println("x * Y = "+(int)(z*1000)/1000.0);//保留3为小数

        这种方式并不能完全指定四舍五入的小数位数,可以称之为“伪四舍五入“,因为这某种巧合的情况下,它并不能很好的实现指定小数位数的功能。比如x=874.738,y=893.32那么 z= 781420.9501600001。(int)(z*1000)/1000.0的输出结果仍为”781420.95“,并没有预想的保留3位小数。原因很简单,z*1000=781420950. 1600001,(int)(z*1000)=781420950,那么(int)(z*1000)/1000.0=781420.95。因为末位是0,因此被舍掉了。

        虽然这种方式的保留小数位数的方式不保险,但这种方式简单便捷,在要求并不严苛的情况下可以使用。

        还有个四舍五入的方法,在java.lang.Math类中:

        public staticlong round(double a)

        publicstatic int round(float a)

        显然,这两个方法返回的数将是整型数据,并不会保留任何小数。

package cn.tty.format;import java.text.DecimalFormat;public classDecimalFormatDemo02 {    public static String round(String pattern,double value){       DecimalFormat formatter=new DecimalFormat(pattern);       String rv=formatter.format(value);       return rv;    }    public static void main(String[] args){       //指定模式:保留2为小数       String roundedValue=DecimalFormatDemo02.round("####.00",838.666);//保留两位小数       System.out.println(roundedValue);    }}

本章小结

1.接口是一种与类很类似的结构,只是包含常量和抽象方法。接口在许多方面与抽象类很接近,但是抽象类除了包含常量和抽象方法之外,还可以包含变量和具体方法。

2.在Java中接口被认为是一种特殊的类。(用implement实现)就像常规类一样,每一个接口都被编译为独立的字节码文件。

3.接口Cloneable是一个标记接口(空接口)。实现Cloneable接口的对象是可克隆的。

4.一个类仅能继承一个父类,但是一个类可以实现一个或多个接口。

5.一个类如果实现了一个借口,则要实现该接口的所有方法。

6.一个接口可以扩展为多个接口。(interface a extends b,c,d)

7.许多Java方法要求使用对象作为参数,Java提供了一个便捷的办法,将基本数据类型合并或包装到一个对象中(如,包装int值到Integer类中,包装double到Double类中)。对应的类称作包装类。使用包装对象而不是基本数据类型的变量,将有助于通用程序设计

8.Java可以根据上下文自动将基本类型值转换为包装对象,反之亦然

9.BigInteger类在计算和处理任意大小的正整数方面是很有用的。BigDecimal类可以用于计算和处理待人以精度的浮点数。

10.因为接口的方法默认是public类型的,所以在实现的时候一定要用public来修饰(否则默认为protected类型,缩小了方法的使用范围)。











0 0
原创粉丝点击