java核心技术笔记 接口与内部类

1.       尽管不能构造接口对象,却能声明接口的变量:

Comparable x;//OK

接口变量必须引用实现了接口的类对象

x = new Employee(…); //    OK provided Employee implements Comparable

 

2.       如果希望自己设计的类拥有克隆和比较的能力,只要实现Cloneable和Comparable。

 

3.       如果对象的属性全是基本类型的,那么可以使用浅拷贝(clone),但是如果对象有引用属性,那就要基于具体的需求来选择浅拷贝还是深拷贝。

下面是一个建立深拷贝clone方法的一个示例:

class Employee implements Cloneable {
       …
       public Employeeclone() throws CloneNotSupportException {
              //callobject.clone()
              Employeecloned = (Employee) super.clone();
              //clonemutable fields
              cloned.hireDay= (Date) hireDay.clone();
                     returncloned;
<span style="white-space:pre">	</span>}
}

4.       接口与回调

class aa implementsActionListener {
        @Override
        public void actionPerformed(ActionEvent arg0) {
            Date now = new Date();
            System.out.println("The time is "+ now);
        }
}
 
publicclassMain {
        public static void main(String[] args) {
            ActionListener listener = new aa();
            Timer t = new Timer(1000, listener);
            t.start();
            JOptionPane.showMessageDialog(null, "退出?");
        }
}

5.       内部类:

内部类既可以访问自身的数据域,也可以创建它的外围类对象的数据域。内部类的对象总有一个隐式引用,它指向了创建它的外部类对象。 

public class TalkingClock {
       private intinterval;
       private booleanbeep;
 
       public TalkingClock(int interval, boolean beep) {…..}
       public voidstart() {…..}
      
       public classTimePrinter implements ActionListener {
              ……       // an inner class
}    

如果一个TimePrinter类是一个常规类,它就需要通过TalkingClock类的公有方法访问beep标志,而使用内部类可以不必提供仅用于访问其他类的访问器。

       TimePrinter类声明为私有的,这样只有TalkingClock的方法能够构造TimePrinter对象。只有内部类可以是私有类,而常规类只可以具有包可见性,或公有可见性。

 

6.       可以采用下列语法格式更加明确地编写内部对象的构造器。

outerObject.newInnerClass(construction parameters)

例如:

       ActionListenerlistener = this.new TimePrinter();

如果TimePrinter是一个公有内部类,对于任意的语音时钟都可以构造一个TimePrinter

TalkingClockjabberer = new TalkingClock(1000,true);

TalkingClock.TimePrinterlistener  =  jabberer.new TimePrinter();

需要注意,在外围类的作用域之外,可以这样引用内部类:

 OuterClass.InnerClass


7.       局部内部类:定义在方法内部

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

局部类有一个优势,即对外部世界可以完全隐藏起来。即使TalkingClock类中的其他代码也不能访问它。除start方法外,没有任何方法知道TimePrinter类的存在。

class TalkingClock {
    private int interval;
    private Boolean beep;
 
    public TalkingClock(int interval, Boolean beep) {
        this.interval = interval;
        this.beep = beep;
    }
 
    public void start() {
        class TimePrinter implements ActionListener {
            @Override
            public void actionPerformed(ActionEvent event) {
                Date now = new Date();
                System.out.println("the time is "+ now);
                if (beep)
                   Toolkit.getDefaultToolkit().beep();
            }
        }
        ActionListener listener = new TimePrinter();
        Timer t = new Timer(interval, listener);
        t.start();
    }
}
 
public class Main {
    public static void main(String[] args) {
        TalkingClock clock = new TalkingClock(1000, true);
        clock.start();
        JOptionPane.showMessageDialog(null, "Quit programme?");
        System.exit(0);
    }
}

8.   局部类不仅能够访问包含它们的类,还可以访问局部变量。不过那些局部变量必须被 声明为final。这使得局部变量与在局部类内建立的拷贝一致。下面是一个典型的示例。 这里,TalkingClock构造器的参数intervalbeep移至start方法中。

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

为了能够清楚的看到内部的问题,让我们仔细看一下控制流程。

a)      调用start方法

b)      调用内部类TimePrinter的构造器,以便初始化对象变量listener

c)      将listener引用传递给Timer构造器,定时器开始计时,start方法结束。此时,start方法的beep变量不复存在。

d)      然后,actionPeerformed方法执行if(beep)

编译器必须检测对局部变量的访问,为每一个变量建立相应的数据域,并将局部变量拷贝到构造器中,以便将这些数据域初始化为局部变量的副本。

 

9.  匿名内部类

将局部内部类的使用再深入一步。假如只创建这一个类的对象就不必命名了。

classTalkingClock {
 
        public void start(int interval, final boolean beep) {
 
            ActionListener listener = new ActionListener() {
                @Override
                publicvoidactionPerformed(ActionEvent event) {
                    Date now = new Date();
                    System.out.println("the time is " + now);
                    if (beep)
                        Toolkit.getDefaultToolkit().beep();
                }
            };
 
            Timer t= newTimer(interval,listener);
            t.start();
        }
}

10. 下面的技巧称为“双括号初始化” (double brace initialization)。

ArrayList<String> friends = newArrayList<>();
favorites.add(“harry”);
favorites.add(“Tony”);
invite(friends);

如果不再需要这个数组列表,最好让它作为一个匿名列表。方法如下:

invite(new ArrayList<String>() {{ add(“Harry”);add(“Tony”); }})

注意这里的双括号。外层括号建立了ArrayList的一个匿名子类。内层括号则是一个对象构造块。

 

11.静态内部类

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

/**
 * This programdemonstrates the use of static inner classes.
 *
 * @version 1.01 2004-02-27
 * @author Cay Horstmann
 */
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 {
        private double first;
        private double second;
 
        public Pair(double f, double s) {
            first = f;
            second = s;
        }
 
        public double getFirst() {
            return first;
        }
 
        public double getSecond() {
            return 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);
    }
}

 

12.代理

import java.lang.reflect.*;
import java.util.*;
 
/**
 * This programdemonstrates the use of proxies.
 * @version 1.00 2000-04-13
 * @author Cay Horstmann
 */
public class ProxyTest
{
   public static void main(String[] args)
   {
      Object[] elements = new Object[1000];
 
      // fill elements with proxies for the integers 1 ... 1000
      for (int i = 0; i < elements.length; i++)
      {
         Integer value = i + 1;
         InvocationHandler handler = new TraceHandler(value);
         Object proxy = Proxy.newProxyInstance(null, new Class[] { Comparable.class } , handler);
         elements[i] = proxy;
      }
 
      // construct a random integer
      Integer key = new Random().nextInt(elements.length) + 1;
 
      // search for the key
      int result = Arrays.binarySearch(elements, key);
 
      // print match if found
      if (result >= 0) System.out.println(elements[result]);
   }
}
 
/**
 * An invocationhandler that prints out the method name and parameters, then
 * invokes theoriginal method
 */
class TraceHandler implementsInvocationHandler
{
   private Object target;
 
   /**
    * Constructs aTraceHandler
    * @param t the implicitparameter of the method call
    */
   public TraceHandler(Object t)
   {
      target = t;
   }
 
   public Object invoke(Object proxy, Method m, Object[] args) throws Throwable
   {
      // print implicit argument
      System.out.print(target);
      // print method name
      System.out.print("." + m.getName() + "(");
      // print explicit arguments
      if (args != null)
      {
         for (int i = 0; i < args.length; i++)
         {
            System.out.print(args[i]);
            if (i < args.length - 1) System.out.print(", ");
         }
      }
      System.out.println(")");
 
      // invoke actual method
      return m.invoke(target, args);
   }
}


代理类的特性:

1)      代理类是再程序运行过程中创建的。然而,一旦被创建,就变成了常规类。

2)      所有的代理类都扩展于Proxy类。一个代理只有一个实例域——调用处理器,它定义在Proxy的超类中。为了履行代理对象的职责,所需的任何附加数据都必须存储在调用处理器中。

3)      所有的代理都覆盖了Object类中的toString、equals、和hashCode。如同所有的代理方法一样,这些方法仅仅调用了调用处理器的invoke。Object类中的其他方法(如clone和getClass)没有被重新定义。

4)      没有定义代理类的名字,Sun虚拟机vs的Proxy类将生成一个以字符串$Proxy开头的类名。

5)      代理类一定是public和final。

6)      对于特定的类加载器和预设的一组借口来说,只能有一个代理类。

  • Object invoke(Object proxy,Method method, Object[] args)
    定义了代理对象调用方法时希望执行的动作。

  • static Class getProxyClass(ClassLoaderloader, Class[] interfaces)
    返回实现指定接口的代理类。

  • static OnjectnewProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandlerhandler)
    构造一个实现指定接口的代理类实例。所有方法都将调用给定处理器对象的invoke方法。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值