zoukankan      html  css  js  c++  java
  • 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的代理机制。

  • 相关阅读:
    (转)通过注册表修改VC6.0的字体
    ubuntu安装好后第三步:安装JDK
    Hiring Managers: Vetting Game Programmers
    (转)Ubuntu下安装Qt环境及Qt Creator开发工具
    Ubuntu下关于Warning DBuserror.org.freedesktop.DBUS.Error.NoReply的解决方法
    vmwaer虚拟机部署ubuntu piix4_smbus: Host SMBus controller not enabled!
    Ernest Adams总结50个最伟大的游戏创意
    (转)Delphi 语言基础
    Eclipse/myEclipse 代码提示/自动提示/自动完成设置
    (转)Eclipse快捷键 10个最有用的快捷键
  • 原文地址:https://www.cnblogs.com/jiangheng/p/3750979.html
Copyright © 2011-2022 走看看