Java面向对象程序设计--接口和内部类

1.接口的定义:

In the Java programming language, an interface is not a class but a set of requirements for classes that want to conform the interface. 

说明: 1) Interface 不是class,虽然interface具有class的一些特点,例如能够继承,能够定义相同类型的变量,而且和C++的abstract class非常像,但Java

的interface和class在本质上已经有所区别!

        2) Interface 是一组要求。类的接口是类的调用者和被调用者之间的一个契约,这个契约定义了interface实现者所要提供的功能和功能提供的方式,也定义了

interface调用者获取 interface实现者服务的方式。

 为了让某个class实现一个接口,只需要以下两个步骤:

1) 声明你所定义的类将实现某个接口;

2) 在你的类中实现接口中所有的方法;

下面的Employee类实现了compareTo接口,那么就可以使用Array的sort方法来对Emplyee对象形成的数组进行排序;


 1  import java.util.Arrays;
 2 
 3 
 4  public  class EmployeeSortTest 
 5 {
 6      public  static  void main(String[] args) 
 7     {
 8         Employee[] staff =  new Employee[3];
 9         
10         staff[0] =  new Employee("Harry Hacker",35000);
11         staff[1] =  new Employee("Carl Cracker",75000);
12         staff[2] =  new Employee("Tony Tester",38000);
13         
14         Arrays.sort(staff);
15         
16          for(Employee e : staff)
17             System.out.println("name = " + e.getName() + ",salary=" + e.getSalary());
18     }
19 }
20 
21  class Employee  implements Comparable<Employee>
22 {
23      public Employee(String n, double s)
24     {
25         name = n;
26         salary = s;
27     }
28     
29      public String getName()
30     {
31          return name;
32     }
33     
34      public  double getSalary()
35     {
36          return salary;
37     }
38     
39      public  void raiseSalary( double byPercent)
40     {
41          double raise = salary * byPercent / 100;
42         salary += raise;
43     }
44     
45      /**
46       * Compares employees by salary
47        */
48      public  int compareTo(Employee other)
49     {
50          if(salary < other.salary)  return -1;
51          if(salary > other.salary)  return 1;
52          return 0;
53     }
54     
55      private String name;
56      private  double salary;
57 }

 接口的一些特性:
1.接口不是类,你无法new出一个接口实例:

2.虽然无法构建出接口实例,但是可以定义接口变量; 并且接口变量只能指向一个实现该接口的类的对象;

Comparable x;

x = new Employee(...);

3.就像使用instanceof判断一个对象是不是一个类的实例一样,你也可以使用instanceof判断一个类的对象是否实现了某个接口。

if(anObject instanceof Comparable) {}

4.就像类的使用一样,接口也可以用来被继承;

5.Java中的类只能继承一个父类,但却可以实现多个接口!这个特性为定义一个类的行为提供的很大的方便。

 

 接口和抽象类:

为什么不使用抽象类来代替接口的概念呢?--Java中不存在多重继承!Java中用接口来实现了C++中复杂的多继承功能!

 

2. 对象克隆(Object Cloning):

对任何一个对象你需要认清下面几点:
a). 默认的clone函数是否已经足够优秀!

b). Clone一个基类对象时要Clone其成员的每个mutable部分;

c).  Clone是否根本无法实现;

对象型变量实际存储的是对象实例的地址,而不是对象实例本身,这是Java设计的一个遗憾! 


  1 import java.util.*;

 2 
 3  public  class CloneTest 
 4 {
 5      public  static  void main(String[] args) 
 6     {
 7          try
 8         {
 9             MyEmployee original =  new MyEmployee("John Q. Public",50000);
10             original.setHireDay(2000, 1, 1);
11             MyEmployee copy = original.clone();
12             copy.raiseSalary(10);
13             copy.setHireDay(2002, 12, 31);
14             System.out.println("original="+original);
15             System.out.println("Copy="+copy);
16         }
17          catch(CloneNotSupportedException e)
18         {
19             e.printStackTrace();
20         }
21     }
22 }
23 
24  class MyEmployee  implements Cloneable
25 {
26      public MyEmployee(String n, double s)
27     {
28         name = n;
29         salary = s;
30         hireDay =  new Date();
31     }
32     
33      public MyEmployee clone()  throws CloneNotSupportedException
34     {
35         MyEmployee cloned = (MyEmployee) super.clone();
36         
37         cloned.hireDay = (Date)hireDay.clone();
38         
39          return cloned;
40     }
41     
42      public  void setHireDay( int year, int month, int day)
43     {
44         Date newHireDay =  new GregorianCalendar(year,month-1,day).getTime();
45         hireDay.setTime(newHireDay.getTime());
46     }
47     
48      public  void raiseSalary( double byPercent)
49     {
50          double raise = salary * byPercent / 100;
51         salary += raise;
52     }
53     
54      public String toString()
55     {
56          return "MyEmployee[name="+name+",salary="+salary+"hireDay="+hireDay+"]";
57     }
58     
59      private String name;
60      private  double salary;
61      private Date hireDay;
62 }

 结果如下:


 original=MyEmployee[name=John Q. Public,salary=50000.0hireDay=Sat Jan 01 00:00:00 CST 2000]

Copy=MyEmployee[name=John Q. Public,salary=55000.0hireDay=Tue Dec 31 00:00:00 CST 2002]

 

3.接口和回调:

一个在编程中常用的模型称为callback(模型),当一个事件发生时要制定处理动作。比如当按钮被按下后执行的特定动作,或者选择了一个

菜单选项之后执行的特定动作!

Java语言利用传递对象来实现这一点,但C++使用传递函数指针来实现这一点的!


  1 /**

 2      @version  1.00 2000-04-13
 3      @author  Cay Horstmann
 4  */
 5 
 6  import java.awt.*;
 7  import java.awt.event.*;
 8  import java.util.*;
 9  import javax.swing.*;
10  import javax.swing.Timer; 
11  //  to resolve conflict with java.util.Timer
12 
13  public  class TimerTest
14 {  
15     public  static  void main(String[] args)
16    {  
17       ActionListener listener =  new TimePrinter();
18 
19        //  construct a timer that calls the listener
20         //  once every 10 seconds
21        Timer t =  new Timer(10000, listener);
22       t.start();
23 
24       JOptionPane.showMessageDialog( null, "Quit program?");
25       System.exit(0);
26    }
27 }
28 
29  class TimePrinter  implements ActionListener
30 {  
31     public  void actionPerformed(ActionEvent event)
32    {  
33       Date now =  new Date();
34       System.out.println("At the tone, the time is " + now);
35       Toolkit.getDefaultToolkit().beep();
36    }
37 }

 

 4. 内部类:

内部类是一个定义在其他类中的类,为何要使用内部类呢?下面是常见的3种理由:

 

  • 内部类中的方法可以访问内部类定义处的数据,即使这些数据是private类型的;
  • 内部类对同一个包中的其他类是隐藏的,即包中的其他类是看不到这个内部类的;
  • 匿名内部类在定义回调函数时非常有用; 

 

       4.1 使用内部类来访问对象的状态:


 1  import java.awt.*;
 2  import java.awt.event.*;
 3  import java.util.*;
 4  import javax.swing.*;
 5  import javax.swing.Timer;
 6 
 7  public  class InnerClassTest
 8 {
 9      public  static  void main(String[] args)
10     {
11              TalkingClock clock =  new TalkingClock(1000, true);
12              clock.start();
13 
14           // keep program running until user selects "OK"
15           JOptionPane.showMessageDialog( null,"Quit program?");
16          System.exit(0);
17     }
18 }
19 
20  /**
21   * A clock that prints the time in regular interval
22    */
23  class TalkingClock
24 {
25      /**
26       * Constucts a taking clock
27       *  @param  interval the interval between message(in milliseconds)
28       *  @param  beep true if the clock should beep
29        */
30      public TalkingClock( int interval, boolean beep)
31     {
32          this.interval = interval;
33          this.beep = beep;
34     }
35 
36      /**
37       * Starts the clock
38        */
39      public  void start()
40     {
41         ActionListener listener =  new TimePrinter();
42         Timer t =  new Timer(interval,listener);
43         t.start();
44     }
45 
46      private  int interval;
47      private  boolean beep;
48 
49     public class TimePrinter implements ActionListener
50     {
51         public void actionPerformed(ActionEvent e)
52         {
53             Date now = new Date();
54             System.out.println("At the tone, the time is "+now);
55             if(beep) 
56                 System.out.println("Beep!");
57         }
58     }

59 }

 上述红色代码部分定义了一个内部监听器类,用来监听事件的发生并采取相应的动作:

 if(beep) 

          System.out.println("Beep!");

表明这个内部类可以直接访问其外部类的private字段!相当于内部类中存在一个对外部类的隐含引用!

这个代码可以改成:

if(TalkingClock. this.beep) 

   System.out.println("Beep!"); 

 

     4.2  内部类的实用性,内部类真的又存在的必要吗? 内部类的安全性如何呢?

Java 引入内部类机制更多的是为了显得优雅有趣,而非实用性!

内部类最好是只和其所在的外部类进行交互: 一方面让内部类只访问其所在的外部类的数据,另一方面,也让内部类只在其所处的外部类的方法中使用。

让内部类只作为其所处的外部类的处理逻辑的一部分。实际开发中尽量少用或不用内部类。

 

     4.3 本地内部类: 将内部类定义在外部类的某个成员函数中,这样,只有这个外部类的成员函数才会去使用这个内部类的对象。


 1  public  void start()
 2 {
 3      class TimePrinter  implements ActionListener
 5     {
 6          public  void actionPerformed(ActionEvent e)
 7         {
 8             Date now =  new Date();
 9             System.out.println("At the tone, the time is "+now);
10              if(TalkingClock. this.beep) 
11                 System.out.println("Beep!");
12         }
13     }
15     ActionListener listener =  new TimePrinter();
16     Timer t =  new Timer(interval,listener);
17     t.start();
18 }

 注意,本地内部类并没有定义一个访问控制符:(public or private),因为本地内部类的有效作用区域只在定义本地内部类的代码块之中。

 

 

 

      4.4 匿名内部类:


 1  public  void start( int iterval, final  boolean beep)
 2     {
 3          ActionListener listener =  new  Actionlistener()
 4         {
 5     
 6                public  void actionPerformed(ActionEvent e)
 7               {
 8                   Date now =  new Date();
 9                   System.out.println("At the tone, the time is "+now);
10                    if(TalkingClock. this.beep) 
11                        System.out.println("Beep!");
12               }
13          }
14         Timer t =  new Timer(interval,listener);
15         t.start();
16     }

 只使用上述内部类的一个对象!

上述这段代码的意思是:

1.定义了一个匿名的类,这个匿名的类实现了Actionlistener接口

2.创建了一个匿名类的实例,在本程序中将其赋给变量listener;

匿名内部类的对象的创建规则是:

new SuperType(construction parameters)

{

       inner class method and data; 

}

在这里,这个SuperType可以是一个接口,比如上面程序中的Actionlistener,或者是一个基类,这样这个内部类就继承了这个基类;

由于匿名内部类是没有名称的,不然也就不叫"匿名"了,所以匿名内部类是没有构造函数的,

a) 当SuperType是一个类的时候,将construction parameters传递给SuperType类的构造函数。

b) 当SuperType是一个接口的时候,new InterfaceType() {methods and data} 

 

     4.5 静态内部类(static):

有些时候,你只是希望在一个类中使用内部类,但并不希望用内部类来访问外部类中的对象。你可以将内部类声明为

static来限制这种内部类对外部类的访问。 

 引入static 内部类的一个重要原因:

static 方法执行时不会有类的实例与之对应,所以,当一个内部类需要在其所在的外部类的static方法中使用时,内部

类就不能获得其所在外部类实例的引用。下面是一个静态内部类的使用实例:


 1  /**
 2      @version  1.00 1998-04-07
 3      @author  Cay Horstmann
 4  */
 5 
 6  public  class StaticInnerClassTest
 7 {  
 8     public  static  void main(String[] args)
 9    {  
10        double[] d =  new  double[20];
11        for ( int i = 0; i < d.length; i++)
12          d[i] = 100 * Math.random();
13       ArrayAlg.Pair p = ArrayAlg.minmax(d);
14       System.out.println("min = " + p.getFirst());
15       System.out.println("max = " + p.getSecond());
16    }
17 }
18 
19  class ArrayAlg
20 {  
21     /**
22        A pair of floating point numbers
23      */
24     public  static  class Pair
25    { 
26        /**  
27            Constructs a pair from two floating point numbers
28             @param  f the first number
29             @param  s the second number
30         */
31        public Pair( double f,  double s)
32       {  
33          first = f;
34          second = s;
35       }
36 
37        /**
38           Returns the first number of the pair
39            @return  the first number
40         */
41        public  double getFirst()
42       {  
43           return first;
44       }
45 
46        /**
47           Returns the second number of the pair
48            @return  the second number
49         */
50        public  double getSecond()
51       {  
52           return second;
53       }
54 
55        private  double first;
56        private  double second;
57    }
58 
59     /**
60        Computes both the minimum and the maximum of an array 
61         @param  a an array of floating point numbers
62         @return  a pair whose first element is the minimum and whose
63        second element is the maximum
64      */
65     public  static  Pair minmax( double [] d)
66     {  
67        if  (d.length == 0)  return  new  Pair(0, 0);
68        double  min = d[0];
69        double  max = d[0];
70        for  ( int  i = 1; i < d.length; i++)
71        {  
72           if  (min > d[i]) min = d[i];
73           if  (max < d[i]) max = d[i];
74        }
75        return  new  Pair(min, max);
76     }
77 }

 

     4.6  代理(Proxies):


  1 import java.lang.reflect.*;

 2  import java.util.*;
 3 
 4  /**
 5   * An invocation handler that prints out the method name and parameters, then
 6   * invokes the original method
 7    */
 8  class TraceHandler  implements InvocationHandler
 9 {
10      /**
11       * Constructs a TraceHandler
12       *  @param  t the implicit parameter of the method call
13        */
14      public TraceHandler(Object t)
15     {
16         target = t;
17     }
18 
19      public Object invoke(Object proxy,Method m,Object[] args)  throws Throwable
20     {
21          // print implicit argument
22          System.out.print(target);
23 
24          // print method name
25          System.out.print("."+m.getName()+"(");
26 
27          // print explicit arguments
28           if(args !=  null)
29         {
30              for( int i = 0; i < args.length; i++)
31             {
32                 System.out.print(args[i]);
33                  if(i < args.length - 1) System.out.print(", ");
34             }
35         }
36 
37         System.out.println(")");
38 
39          // invoke actual method
40           return m.invoke(target,args);
41     }
42 
43      private Object target;
44 }
45 
46  public  class ProxyTest
47 {
48      public  static  void main(String[] args)
49     {
50         Object[] elements =  new Object[1000];
51 
52          // fill elements with proxies for the integers 1...1000
53           for( int i = 0; i < elements.length; i++)
54         {
55             Integer value = i+1;
56             InvocationHandler handler =  new TraceHandler(value);
57             Object proxy = Proxy.newProxyInstance( null, new Class[] {Comparable. class},handler);
58             elements[i] = proxy;
59         }
60 
61          //  construct a random integer
62          Integer key =  new Random().nextInt(elements.length) + 1;
63 
64          //  search for the key
65           int result = Arrays.binarySearch(elements,key);
66 
67          // print match if found
68           if(result >= 0) System.out.println(elements[result]);
69     }
70 }

 代理机制属于模式设计范畴,在以后的博客中会专门开辟一篇探究Java的代理机制。

 

 

 

 

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(java)