Java(六)接口与内部类
2014-02-23 20:29
253 查看
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、代理
利用代理可以在运行时创建一个实现了一组给定接口的新类。这种功能只有在编译时无法确定要实现哪个接口时才有必要使用。相关文章推荐
- 【java基础】内部类,局部内部类,匿名内部类、静态内部类、接口中的内部类
- 抽象类_接口_内部类JAVA048-051
- Java核心技术(第8版)学习笔记_接口与内部类
- java学习 接口派生 内部类实现接口 暑假第七天
- 牛刀小试 - 详解Java中的接口与内部类的使用
- Java接口interface,匿名内部类
- 黑马程序员Java笔记——抽象类、接口、内部类(拾遗补录)
- 黑马程序员——Java基础---面向对象(继承、多态、抽象类、接口、内部类)
- Java中:接口,抽象类,内部类
- Java学习笔记四——接口与内部类
- NO8.java笔记【面向对象、抽象类、abstract、接口、interface、多态 、内部类、匿名内部类、异常、Object类】
- thinking in java 多态,接口,内部类,异常小结
- IT十八掌作业_java基础第六天_接口与适配器模式、多态、内部类
- java核心卷1 第6章接口lambda 内部类 心得
- java 面向对象内部类和接口
- 黑马程序员__ 6java基础 接口 多态 内部类
- Java核心技术——接口和内部类
- [学习笔记] Java核心技术 卷一:基础知识 接口、lambda表达式与内部类(三)
- 【JAVA】内部类,内部接口
- java基础(2)-基础类型和语法(static、内部类、final、抽象类、接口、封装)(并将这些基础知识与java的三大特征关联(继承、多态、封装))