• [Java学习笔记] Java核心技术 卷1 第六章 接口与内部类


    第6章 接口与内部类

    6.1 接口

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

    在接口声明中,方法自动public,可以不写修饰符。在实现接口时必须把方法声明为public。

    一个接口中可以包含多个方法,还可以定义常量,自动设置public static final

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

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

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

    在调用方法时编译器会检查这个方法是否存在,如果是实现了接口的对象的数组,接口中提供了该方法,那么就可以确定该方法一定存在。每个实现了接口的类都必须提供接口中定义的所有方法。

    为了让类实现一个接口,需要1:将类声明为实现 implements 给定的接口 2:对接口中的所有方法进行定义。

    public interface Comparable
    {
       int compareTo(Object other);
    }
    class Employee implements Comparable
    {
       ...
       public int compareTo(Object other)
       {
           Employee e=(Employee)other;
           return Double.compare(salary,e.salary);
        }
    }

    6.1.1 接口的特性

    l  接口不是类,不能使用new运算符实例化一个接口但是可以声明接口变量,同时接口变量必须引用实现了接口的类对象。

    x = new Comparable(...);//Error
    Comparable x ;// ok
    x = new Employee(...);

    l  使用instanceof检查一个对象是否实现了某个特定的接口

    if( anObject instanceof Comparable){...}

    l  如同可以继承类,也可以扩展接口,扩展接口不用像实现接口那样实现所有方法

    有些接口只定义了常量没有定义方法,任何实现了接口的类都自动继承了这些常量,并且可以直接饮用而不需像静态域那样类名.域名 的形式

    public interface Moveable
    {
       void move(double x , double y );
    }
    public interface Powered extends Moveable
    {
       double milesPerGallon();
       double SPEEDLIMIT = 95; // public static final
    }

    l  尽管每个类只能拥有一个超类,但可以实现多个接口。使用逗号将实现的各个接口分隔开。

    class Employee implements Cloneable,Comparable

    6.1.2 接口与抽象类

    抽象类也可以表示通用属性,但存在一个问题:每个类只能扩展于(继承)一个类。接口却可以实现多个。

    6.1.3 标记接口

    Cloneable接口是Java提供的几个标记接口之一,标记接口没有方法,使用它的唯一目的是可以用instanceof进行类型检查。

    自己编写程序时不要使用这种技术。

    6.2 对象克隆

    拷贝一个变量时,原始变量与拷贝变量引用同一个对象,改变一个变量所引用的对象会对另一个变量产生影响。

    clone方法是Object 类的一个protected方法,在用户编写的代码中不能直接调用它。Object类对具体的类对象一无所知,只能将各个域进行对应的拷贝。对于基本类型或数值拷贝没有问题,但如果对象包含了子对象的引用,拷贝的结果还是两个域引用同一个子对象。

    所有默认的克隆操作是浅拷贝,并没有克隆包含在对象中的内部对象。

    必须重新定义clone方法,以便实现克隆子对象的深拷贝。对每个类都要做出下列判断:

    1:默认的clone方法能否满足要求

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

    3:是否不应该使用clone

    如果要使用clone,必须:

    1:实现Cloneable接口

    2:使用public访问修饰符重新定义clone方法,并声明异常CloneNotSupportedException

    即使clone的默认实现能满足要求,也应该实现Cloneable接口,将clone定义为public,然后调用super.clone();

    Cloneable接口与接口的正常使用没有任何关系。它并没有制定clone方法。clone方法是从Object类继承而来。接口在这里作为一个标记,表明类设计者制定要进行克隆处理。如果一个对象需要克隆而没有实现Cloneable接口,就会产生一个已检查异常。

    class Employee implements Cloneable
    {
       ...
       public Employee clone() throws CloneNotSupportedException
       {
           Employee cloned=(Employee)super.clone();//Object.clone()
           cloned.hireDay=(Date)hireDay.clone();//克隆子对象
           return cloned;
       }
    }
    

    关于是否应该事先clone方法,如果客户需要深拷贝就应该实现它。克隆的应用也不是很普遍,在标准类库中只有不到5%的类实现了clone。

    所有的数组类型都包含一个clone方法,而且被设为了public,可以利用这个方法创建一个包含所有数组元素拷贝的一个新数组。

    6.3 接口与回调

    回调可以指出某个特定事件发生时应该采取的动作。

    以构造定时器为例:

    在很多程序设计语言中,可以提供一个函数名,定时器周期性的调用它。但Java标准类库中的类采用的是面向对象方法。将某个类的对象传递给定时器,然后定时器,定时器调用这个对象的方法。由于对象可以携带一些附加的信息,所有传递一个对象比传递一个函数灵活的多。

    public interface ActionListener
    { 
        void actionPerformed(ActionEvent event); 
    }
    class TimePrinter implenets ActionListener
    {
       public void actionPerformed(ActionEvent event)
       {
           //do something small
        }
    }
    
    //构造这个类的对象传递给Timer构造器
    ActionListener listener = new TimePrinter();
    Timer t = new Timer(10000,listener);
    

    6.4 内部类

    为什么需要使用内部类:

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

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

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

    6.4.1 使用内部类访问对象状态

    内部类的对象总有一个隐式引用,指向了创建它的外部类对象。将外围类的对象的引用称为outer。

    outer不是Java关键字,外围类的引用在构造器中设置,编译器修改了所有内部类的构造器,添加了一个外围类的引用参数。

    只有内部类可以是私有类。常规类只可以具有包可见性,或公有可见性。

    6.4.2 内部类的特殊语法规则

    使用外围类引用:OuterClass.this

    if(TalkingCLock.this.beep) ...

    编写内部类对象的构造器:outerObject.new InnerClass(contruction parameters)

    ActionListener listener = this.new TimerPrinter();

    在外围类的作用域之外引用内部类:

    OuterClass.InnerClass

    6.4.3 内部类安全性

    如果内部类访问了私有数据域,就有可能通过附加在外围类梭子包中的其他类访问它们,但做这些事情需要高超的技巧和极大的决心。不可能无意之中就获得对类的访问权限,必须可以构建或修改类文件才有可能达到这个目的。

    6.4.4 局部内部类

    可以在一个方法中定义局部类,并且不能用public或private访问说明符进行声明,它的作用域被限定在声明这个局部类的块中。

    局部类可以对外部世界完全隐藏起来,即使方法所在类中的其他代码也不能访问。除了定义它的方法外,没有任何方法知道它的存在。

    局部类的另一个优势:不仅可以访问包含它们的外部类,还可以访问局部变量,但那些局部变量必须被声明为final

    public void start(int interval,final boolean beep)
    {
        class TimePrinter implements ActionListener
        {
            public void actionPerformed(ActionEvent event)
            {
                if(beep)//局部类访问局部变量
               Toolkit.getDefaultToolkit().beep();
            }
        }
        ActionListener listener = new TimePrinter();
        Time t = new Timer(interval,listener);
        t.start();
    }

    6.4.5 匿名内部类

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

    匿名类不能有构造器,需要将参数传递给超类构造器;内部类实现接口时不能有任何构造参数。

    构造器与类名同名,没有名字的类无法设置构造器。

    SuperType可以是接口,内部类就要实现这个接口;也可以是一个类,内部类就要扩展它。

    new SuperType(construction parameters)
    {
       inner class methods and data
    }
    new InterfaceType()
    {
       methods and data
    }
    
    public void start(int interval, final boolean beep)
    {
       //创建一个实现AL接口的类的新对象
       ActionListener listener =  new ActionListener()
       {
           ...
       }
       ...
    }

    6.4.6 静态内部类

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

    只有内部类可以声明为static。静态内部类的对象除了没有对生成它的外围类对象的引用特权外,与其他所有内部类完全一样。如果在静态方法中构造内部类,则必须是静态内部类。

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

    6.5 代理

    在编译时无法确定需要实现哪个接口时,利用代理可以在运行时创建一个实现了一组给定接口的新类。

    例如假设有一个表示接口的Class对象,它的确切类型在编译时无法知道。想要构造一个实现这些接口的类,就需要使用newInstance方法或反射找出这个类的构造器。但是不能实例化一个接口,需要在程序处于运行状态时定义一个新类。

    为了解决上述问题,有些程序将会生成代码,然后将这些代码放在一个文件中,调用编译器,再加载结果类文件,很明显这样做的速度比较慢,并且需要将编译器与程序放在一起。

    代理类则可以在运行时创建全新的类,这样代理类能够实现指定的接口,尤其是具有下列方法:

    1:指定接口所需要的全部方法

    2:Object类中的全部方法

    但是不能在运行时定义这些方法的新代码。而要提供一个调用处理器,它是实现了InvocationHandler接口的类对象,在这个接口中只有一个方法

    Object invoke(Object proxy, Method method ,Object[] args)

    无论何时调用代理对象的方法,调用处理器的invoke方法都会被调用,并向其传递Method对象和原始的调用参数。调用处理器必须给出调用处理的方式。

    要创建一个代理对象,需要使用Proxy类的newProxyInstance方法,该方法有三个参数:

    1:类加载器。作为Java安全模型的一部分,对于系统类和从其他类,可以使用不同的类加载器,目前使用null表示使用默认的类加载器

    2:Class对象数组。每个元素都是需要实现的接口

    3:调用处理器。

    以及需要解决两个问题:

    1:如何定义一个处理器

    2:能够使用结果代理对象做些什么

    6.6 待补充

    内部类、代理 的示例代码

  • 相关阅读:
    SQL查询语句 group by后, 字符串合并
    正则表达式对象模型
    C#正则表达式编程(四):正则表达式
    C#正则表达式编程(三):Match类和Group类用法
    C#正则表达式编程(二):Regex类用法
    C#正则表达式编程(一):C#中有关正则的类
    正则表达式中-分组构造
    正则表达式-定位点
    正则表达式-字符类减法
    正则表达式-匹配标点符号
  • 原文地址:https://www.cnblogs.com/djq0/p/5639523.html
Copyright © 2020-2023  润新知