[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 待补充

内部类、代理 的示例代码

时间: 2024-10-24 02:06:39

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

Java核心技术 第六章 接口和内部类

Java核心技术  第六章  接口与内部类 接口: 任何实现Comparable接口的类都需要包含compareTo方法,并且这个方法的参数必须是一个Object对象,返回一个整数数值. 在Java SE 5.0中,Comparable接口已经改进为泛型类型. 接口中所有的方法自动的属于public.因此,在接口中声明方法时,不必提供关键字public. 接口中决不能含有实例域,也不能在接口中实现方法. 要让一个类使用排序服务,必须让它实现compareTo方法,因此必须实现Comparable

[Java学习笔记] Java核心技术 卷1 第四章

第4章 对象与类 4.1 类和对象的基本概念 描述了类和对象的基本概念,以及类之间的关系介绍. 程序中的很多对象来自于标准库,还有一些自定义的. 结构化程序设计:通过设计一系列的过程(算法),选择合适的存储方式来解决问题. 算法+数据结构 4.1.1 类/封装/继承 类是构造对象的模板,由类构造对象的过程称为创建类的实例. 封装:也称为数据隐藏.从形式上看,封装不过是将数据和行为组合在一个包中,并对对象的使用者隐藏了数据的实现方式.优点4.2.3 实例域:对象中的数据. 方法:操纵数据的过程.

Java学习笔记----Java基础11 对象引用

作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 我们之前一直在使用“对象”这个概念,但没有探讨对象在内存中的具体存储方式.这方面的讨论将引出“对象引用”(object reference)这一重要概念. 对象引用 我们沿用之前定义的Human类,并有一个Test类: public class Test { public static void main(String[] args) { Human aPerson = new

[Java学习笔记] Java核心技术 卷1 第五章 继承

第5章 继承 利用继承,可以基于已存在的类构造一个新类.继承已存在的类就是复用(继承)这些类的方法和域.还可以在此基础上添加新的方法和域. 反射. 5.1 超类子类 使用extends构造一个派生类 class Manager extends Employee { 添加方法和域 覆盖:重写一些基类中不适合派生类的方法 } 所有的继承都是公有继承.即所有的公有成员和保护成员都保持原来的状态,基类的私有成员仍然是私有的.不能被派生类的成员访问. (C++中私有继承或保护继承会将 公有成员和保护成员都

[Java学习笔记]-Java对象和类

Java是完全面向对象的高级语言,其基本的操作基本都是针对相应的对象和类.面向对象的程序是由对象组成的,每个对象包含对用户公开的特定功能部分和隐藏的实现部分.对应面向对象的语言,还有一种面向过程的语言,如C语言.面向对象的语言是在面向过程语言的基础上发展而来的.面向对象(OOP,全称为Object-Oriented-Programer,下文简称为OOP)相对于面向过程的语言而言,其优势在于很多问题的解决方法被封装在对象里,有时只需要创建这样的对象就可以解决我们的问题,而不必关心其具体实现细节,这

[java学习笔记]java语言基础概述之数组的定义&常见操作(遍历、排序、查找)&二维数组

1.数组基础 1.什么是数组:           同一类型数据的集合,就是一个容器. 2.数组的好处:           可以自动为数组中的元素从零开始编号,方便操作这些数据. 3.格式:  (一旦创建,必须明确长度)          格式1:              元素类型   [ ]  数组名  =  new  元素类型  [元素个数即数组的长度]:              示例:int[] array = new int[5];          格式2:           

[java学习笔记]java语言基础概述之内存的划分&堆和栈

1.内存的划分 1.寄存器         cpu处理 2.本地方法区        和所在系统相关 3.方法区 4.栈内存 5.堆内存 2.栈和堆 1.栈:      存储的都是局部变量.而且变量所属的作用域一旦结束,就释放该变量.      栈中的变量生命周期都很短,更新速度会很快. 局部代码块:限定局部变量的生命周期.局部代码块一旦执行结束,里面的变量就会被释放. 2.堆      存储的是数组和对象.凡是new建立的,都会存在堆里.      特点: 每一个实体都有首地址值. 堆内存中的

[java学习笔记]java语言基础概述之函数的定义和使用&函数传值问题

1.函数 1.什么是函数? 定义在类中的具有特定功能的一段独立小程序. 函数也叫做方法 2.函数的格式 修饰符   返回值类型    函数名(参数类型  形式参数1, 参数类型  形式参数2-) {           执行语句(函数体);           return 返回值;       } 返回值类型:函数运行完成后输出的数据类型. 参数类型:是形式参数的数据类型. 形式参数:是一个变量,用于储存调用函数时传递给函数的实际参数. 实际参数:传递给形参的实际数据. return:结束函数

java学习笔记——Java中HashMap和TreeMap的区别深入理解

本文转载自Java中HashMap和TreeMap的区别深入理解 首先介绍一下什么是Map.在数组中我们是通过数组下标来对其内容索引的,而在Map中我们通过对象来对对象进行索引,用来索引的对象叫做key,其对应的对象叫做value.这就是我们平时说的键值对. HashMap通过hashcode对其内容进行快速查找,而 TreeMap中所有的元素都保持着某种固定的顺序,如果你需要得到一个有序的结果你就应该使用TreeMap(HashMap中元素的排列顺序是不固定的). HashMap 非线程安全