Java(六)接口与内部类

1、接口

     接口主要用来描述类具有什么功能,并且不给出每个功能的具体实现,接口不是类。

     一个类可以实现(implement)一个或多个接口(interface),并在需要接口的地方,随时使用实现了相应接口的对象。

     接口中的所有方法自动地属于 public。

     接口绝不能含有实例域,也不能在接口中实现方法。提供实例域和方法实现的任务由实现接口的那个类完成。

     可以将接口看成是没有实例域的抽象类。

     实现一个接口,必须:1)将类声明为实现给定的接口

                                         2)对接口中的所有方法进行定义

     实例:Arrays 类中的 sort 方法可以对对象数组进行排序,但对象所属的类必须实现了 Comparable 接口,如下:


public interface Comparable<T>
{
	int compareTo(T other);
}

          

              实现这个接口,必须提供 compareTo(Employee other) 方法。

              实现接口:

class Employee implements Comparable<Employee>
{
	//......
	
	public int compareTo(Employee other)
	{
		if (salary < other.salary) return -1;
		if (salary > other.salary) return 1;
		return 0;
	}
      

        //.....

}

         

             完整程序:


import java.util.*;
public class EmployeeSortTest 
{
	public static void main(String[] args)
	{
		Employee[] staff = new Employee[3];
		
		staff[0] = new Employee("Ai",35000);
		staff[1] = new Employee("Bi",75000);
		staff[2] = new Employee("Ci",38000);
		
		Arrays.sort(staff);
		
		for (Employee e : staff)
			System.out.println("name="+e.getName()+",salary="
					+e.getSalary());
	}
}

class Employee implements Comparable<Employee> //实现接口的类
{
	public Employee(String n,double s)
	{
		name = n;
		salary = s;
	}
	
	public String getName()
	{
		return name;
	}
	
	public double getSalary()
	{
		return salary;
	}
	
	public void raiseSalary(double byPercent)
	{
		double raise = salary * byPercent / 100;
		salary += raise;
	}
	
	public int compareTo(Employee other)
	{
		if (salary < other.salary) return -1;
		if (salary > other.salary) return 1;
		return 0;
	}
	
	private String name;
	private double salary;
}


          运行结果

      


    (1)接口的特性


             接口不是类,尤其不能使用 new 运算符实例化一个接口,但尽管不能构造接口的对象,却能声明接口的变量,接口的变量必须引用实现了接口的类对象:


Comparable x;
x = new Employee(...);

            也可以使用 instance 检查一个对象是否实现了某个特定的接口:if(anObject instanceof Comparable){...}

            接口也可以被扩展(继承)。

            在接口中不能包含实例域或静态方法,但却可以包含常量。

            尽管每个类智能够拥有一个超类,但却可以实现多个接口(使用逗号隔开)。

   (2)接口与抽象类

            为何不直接使用抽象类而使用接口?

                    因为每个类智能扩展于一个类(java不支持C++中的多继承),但每个类可以实现多个接口。

2、对象克隆


     当拷贝一个变量时,原始变量与拷贝变量引用同一个对象,改变引用的对象时,会同步改变两个变量。如想要创建一个

对象的新的 copy 之后,不再相互影响,就需要使用 clone 方法。

     clone 方法是 Object 类的一个 proteced 方法,若子对象可变,则必须重新定义 clone 方法,实现克隆子对象的深拷贝。

     所有的数组类型均包含一个 clone 方法(public),可以利用这个方法创建一个包含所有数据元素拷贝的一个新数组。

     对于每一个类,都需要做出如下判断:

                              1)默认的 clone 方法是否满足要求

                              2)默认的 clone 方法是否能够通过调用可变子对象的 clone 得到修补

                              3)是否不应该使用 clone ,实际上这个选择是默认的,如果要选前两项,类必须:

                                                                           1)实现 Cloneable 接口(一种标记接口)

                                                                           2)使用 public 访问修饰符重新定义 clone 方法。

       实例:

import java.util.*;
public class CloneTest 
{
	public static void main(String[] args)
	{
		try
		{
			Employee original = new Employee("Ai.Public",50000);
			original.setHireDay(1991,1,1);
			Employee copy = original.clone(); //克隆
			copy.raiseSalary(10);
			copy.setHireDay(1993,12,31);
			System.out.println("original="+original);
			System.out.println("copy="+copy);
		}
		catch (CloneNotSupportedException e)
		{
			e.printStackTrace();
		}
	}
}

class Employee implements Cloneable//实现接口
{
	public Employee(String n,double s)
	{
		name = n;
		salary = s;
		hireDay = new Date();
	}
 
	//只要在 clone 中没有实现 Cloneable 接口的对象,Object 类的 clone 方法就会
	//抛出一个 ClontNot-SupportException 异常,实现之后,还需声明异常如下:
	public Employee clone() throws CloneNotSupportedException
	{
		Employee cloned = (Employee) super.clone();//调用 Object.clone()
		
		cloned.hireDay = (Date) hireDay.clone();//克隆可变域
		
		return cloned;
	}
	
	public void setHireDay(int year, int month, int day)
	{
		Date newHireDay = new GregorianCalendar(year,month-1,day).getTime();
		
		hireDay.setTime(newHireDay.getTime());
	}
		
	public void raiseSalary(double byPercent)
	{
		double raise = salary * byPercent / 100;
		salary += raise;
	}
	
	public String toString()
	{
		return "Employee[name="+name+",salary="+salary
				+",hireDay="+hireDay+"]";
	}
	
	private String name;
	private double salary;
	private Date hireDay;
}

                   运行结果:


3、接口与回调


      回调(callback)是一种常见的程序设计模式。在这种模式中,可以指出某个特定事件发生时应该采取的动作。

      在 java.swing 包中有一个 Timer 类(定时器),使用时要求相应的类实现了 java.awt.event 包的 ActionListener 接口:

 

public interface ActionListener
{
    void actionPerformed(ActionEvent event);
}



      java 有函数指针的对应物:Method 对象,但使用比较困难,因此,在任何使用 C++ 函数指针的地方,都应该考虑使用java中的接口

      完整程序:

import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.Timer;

public class TimerTest 
{
	public static void main(String[] args)
	{
		ActionListener listener = new TimePrinter();
		
		//构造一个调用 listener 的定时器,10秒钟每次
		//Time(int interval,AvtionListener listener):构造
		//一个定时器,每隔 interval 毫秒钟通告 listener 一次。
		Timer t = new Timer(10000,listener);
		t.start();//启动定时器
		
		//显示一个包含一条消息和 OK 按钮的对话框,位于屏幕的中央
		JOptionPane.showMessageDialog(null,"Quit program?");
		System.exit(0);
	}
}

class TimePrinter implements ActionListener //实现 ActionListener 接口的类
{
	public void actionPerformed(ActionEvent event)
	{
		Date now = new Date();
		System.out.println("At the tone,the time is "+now);
		Toolkit.getDefaultToolkit().beep();
		//Toolkit.getDefaultToolkit():获得默认工具箱,beep():发出一声铃响
	}
}

   程序启动后,弹出 "Quit program?" 字样对话框,每10秒显示一次定时器消息,按 OK 退出

           运行结果:


4、内部类


      内部类(inner class)是定义在另一个类中的类,使用内部类的原因:

            a)内部类方法可以访问该类定义所在的作用域中的数据,包括私有的数据

             b)内部类可以对同一个包中的其他类隐藏起来

             c)当想要定义一个回调函数且不想编写大量代码时,使用匿名内部类比较便捷

   (1)使用内部类访问对象状态

            内部类的对象总有一个隐式引用,它指向了创建它的外部类对象。这个引用在内部类的定义中是不可见的。为了说明这个概念

我们将外围类对象的引用称为 outer,下面的 if(beep)相当于 if(outer.beep)

               实例:

import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.Timer;
public class InnerClassTest 
{
	public static void main(String[] agrs)
	{
		TalkingClock clock = new TalkingClock(1000,true);
		clock.start();
		
		JOptionPane.showMessageDialog(null,"Quit program?");
		System.exit(0);
	}
}

class TalkingClock
{
	public TalkingClock(int interval,boolean beep)
	{
		this.interval = interval;
		this.beep = beep;
	}
	
	public void start()
	{
		ActionListener listener = new TimePrinter();
		Timer t = new Timer(interval,listener);
		t.start();
	}
	
	private int interval;
	private boolean beep;
	
	public class TimePrinter implements ActionListener //内部类
	{
		public void actionPerformed(ActionEvent event)
		{
			Date now = new Date();
			System.out.println("At the tone,the time is "+now);
			if (beep) Toolkit.getDefaultToolkit().beep(); //这里的 beep 是引用外围类对象的数据域
		}
	}
}


       运行结果:


        (2)局部内部类


                上例中,TimePrinter 这个类名字只在 start 方法中创建这个类型的对象时使用了一次,因此,也可以就在此方法中定义局部类:

public void start()
{
	class TimePrinter implements ActionListener
	{
		public void actionPerformed(ActionEvent event)
		{
			Date now = new Date();
			System.out.println("At the tone,the time is "+now);
			if (beep) Toolkit.getDefaultToolkit().beep();
		}
	}
	
	ActionListener listener = new TimePrinter();
	Timer t = new Timer(interval,listener);
	t.start();
}

              局部类不能用 public 或 private 访问说明符进行声明,它的作用域被限定在声明这个局部类的块中。

              局部类有一个优势,即对外部世界(即使是 TalkingClock 类中的其他代码)可以完全地隐藏起来。

      (3)匿名内部类

               假如只创建这个局部内部类的一个对象,就不必命名了,这种类被称为匿名内部类。

               匿名类不能有构造器,取而代之的是将构造器参数传递给超类构造器。

               下面实例中的匿名内部类的含义是:创建一个实现 ActionListener 接口的类的新对象,需要实现的方法 actionPerformed 定义在括号内。

import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.Timer;
public class AnonymousInnerClassTest 
{
	public static void main(String[] agrs)
	{
		TalkingClock clock = new TalkingClock();
		clock.start(1000,true);
		
		JOptionPane.showMessageDialog(null,"Quit program?");
		System.exit(0);
	}
}

class TalkingClock
{
	public void start(int interval,final boolean beep)
	{
		ActionListener listener = new ActionListener()//匿名内部类
		{
			public void actionPerformed(ActionEvent event)
			{
				Date now = new Date();
				System.out.println("At the tone,the time is "+now);
				if (beep) Toolkit.getDefaultToolkit().beep();
			}
		};
		Timer t = new Timer(interval,listener);
		t.start();
	}
}


      (4)静态内部类

               有时候,使用内部类只是为了把一个类隐藏在另外一个类的内部,并不需要内部类引用外围对象,为此,可以将内部类声明为 static。

public class StaticInnerClassTest
{
   public static void main(String[] args)
   {
      double[] d = new double[20];
      for (int i = 0; i < d.length; i++)
         d[i] = 100 * Math.random();
      ArrayAlg.Pair p = ArrayAlg.minmax(d);
      System.out.println("min = " + p.getFirst());
      System.out.println("max = " + p.getSecond());
   }
}

class ArrayAlg
{
   public static class Pair //包含最大值最小值的类
   {
      public Pair(double f, double s)
      {
         first = f;
         second = s;
      }

      public double getFirst()
      {
         return first;
      }

      public double getSecond()
      {
         return second;
      }

      private double first;
      private double second;
   }

   public static Pair minmax(double[] values)//计算最大最小值的方法
   {
      double min = Double.MAX_VALUE;
      double max = Double.MIN_VALUE;
      for (double v : values)
      {
         if (min > v) min = v;
         if (max < v) max = v;
      }
      return new Pair(min, max);
   }
}

        运行结果:


                              注:声明在接口中的内部类自动成为 static 和 public。

5、代理

       利用代理可以在运行时创建一个实现了一组给定接口的新类。这种功能只有在编译时无法确定要实现哪个接口时才有必要使用。


猜你喜欢

转载自blog.csdn.net/u012561696/article/details/19637985