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的代理机制。