Java核心技术卷一基础知识-第6章-接口与内部类-读书笔记
第6章 接口与内部类
本章内容:
* 接口
* 对象克隆
* 接口与回调
* 内部类
* 代理
- 接口技术主要用来描述类具有什么功能,而并不给出每个功能的具体实现。一个类可以实现(implement)一个或多个功能,并在需要接口的地方,随时使用实现了相应接口的对象。
- 对象的克隆是指创建一个新对象,且新对象的状态和原始对象的状态相同。当对克隆的新对象进行修改时,不会影响原始对象的状态。
- 内部类定义在另外一个类的内部,其中的方法可以访问包含它们的外部类的域。内部类技术主要用于设计具有相互协作关系的类集合。
- 代理是一种实现任意接口的对象。代理是一种非常专业的构造工具,他可以用来构建系统级的工具。
6.1 接口
- 在Java程序设计语言中,接口不是类,而是对类的一组需求描述,这些类要遵从接口描述的统一格式进行定义。
- 接口中的所有方法自动地属于public。因此,在接口中声明方法时,不必提供关键字public。
- 接口绝不能含有实例域,也不能在接口中实现方法。提供实例域和方法实现的任务应该由实现接口的那个类来完成。因此,可以将接口看成是没有实例域的抽象类。
- 为了让类实现一个接口,通常需要下面两个步骤:
(1)将类声明为实现给定的接口。
(2)对接口中的所有方法进行定义。 - 在实现接口时,必须把方法声明为public;否则,编译器将认为这个方法的访问属性是包可见性,即类的默认访问属性,之后编译器就会给出试图提供更弱的访问权限的警告信息。
- Java语言是一种强类型(strongly typed)语言。在调用方法的时候,编译器将会检查这个方法是否存在。
- java.lang.Comparable 1.0
- java.lang.Comparable 1.0
用这个对象与other进行比较。如果这个对象小于other则返回负值;如果相等则返回0;否则返回正值。
- java.lang.Comparable 1.0
- java.util.Arrays 1.2
- static void sort(Object[] a)
使用mergesort算法对数组a中的元素进行排序。要求数组中的元素必须属于实现了Comparable接口的类,并且元素之间必须是可比较的。
- static void sort(Object[] a)
- java.lang.Integer 7
- static int compare(int x,int y)
如果x<y返回一个负整数;如果x和y相等,则返回0;否则返回一个负整数。
- static int compare(int x,int y)
- 语言标准规定:对于任意的x和y,实现必须能够保证sgn(x.compareTo(x))=-sgn(y.compareTo(x))。这里的“sgn”是一个数值的符号:如果n是负值,sgn(n)等于-1;如果n是0,sgn(n)等于0;如果n是正值,sgn(n)等于1。简单地讲,如果调换compareTo的参数,结果的符号也应该调换(而不是实际值)。
6.1.1 接口的特性
- 接口不是类,尤其不能使用new运算符实例化一个接口。然而,尽管不能构造接口的对象,却能声明接口的变量。接口变量必须引用实现了接口的对象。可以使用instance检查一个对象是否实现了某个特定的接口。接口也可以被扩展。允许存在多条从具有较高通用性的接口到较高专用性的接口的链。
- 接口中不能包含实例域或静态方法,但却可以包含常量。
- 接口中的方法都自动地被设置为public,接口中的域将被自动设为public static final。
- 有些接口只定义了常量,而没有定义方法。这种应用接口似乎有点偏离了接口概念的初衷,最好不要这样使用它。
6.1.2 接口与抽象类
- 使用抽象类表示通用属性存在一个问题:每个类只能扩展于一个类。
- 接口可以提供多重继承的大多数好处,同时还能避免多重继承的复杂性和低效性。
6.2 对象克隆
- 当拷贝一个变量时,原始变量与拷贝变量引用同一个对象。也就是说,改变一个变量所引用的对象将会对另一个变量产生影响。
- 如果创建一个对象的新的copy,它的最初状态与original一样,但以后将可以各自改变各自的状态,那就需要使用clone方法。
- 默认的克隆操作是浅拷贝,它并没有克隆包含在对象中的内部对象。
如果进行浅拷贝会发生什么呢?这要根据具体情况而定。如果原始对象与浅克隆对象共享的子对象是不可变的,将不会产生什么任何问题。例如,子对象属于像String类这样的不允许改变的类;也可能子对象在其生命周期内不会发生改变,既没有更改它们的方法,也没有创建对它引用的方法。然而,更常见的情况是子对象可变,因此必须重新定义clone方法,以便实现克隆子对象的深拷贝。 - 对于每一个类,都需要做出下列判断:
(1)默认的clone方法是否满足要求。
(2)默认的clone方法是否能够通过调用可变子对象的clone得到修补。
(3)是否不应该使用clone。
实际上,选项3是默认的。如果选择1或2,类必须:
(1)实现Cloneable接口。
(2)使用public访问修饰符重新定义clone方法。
在这里,Cloneable接口的出现于接口的正常使用没有任何关系,尤其是,它并没有指定clone方法,这个方法是从Object类继承而来的。接口在这里只是作为一个标记,表明类设计者知道用进行克隆处理。如果一个对象需要克隆,而没有实现Cloneable接口,就会产生一个已检验异常(checked exception)。 - 即使clone的默认实现(浅拷贝)能够满足需求,也应该实现Cloneable接口,将clone重定义为public,并调用susper.clone()。
- 在Java SE 5.0以前的版本中,clone方法总是返回Object类型,而现在,协变返回类型特性允许克隆方法指定正确的返回类型。
6.3 接口与回调
- 回调(callback)是一种常见的程序设计模式。在这种模式中,可以指出某个特定事件发生时应该采用的动作。
- 传递一个对象要比传递一个函数灵活的多。
- Java有函数指针的对应物-Method对象。然而,使用起来却比较困难,速度也稍慢一些,并且在编译时不能提供类型的安全性检查。
- javax.swing.JOptionPane 1.2
- static void showMessageDialog(Component parent,Object message)
显示一个包含一条消息和OK按钮的对话框。这个对话框将位于其parent组件的中央。如果parent为null,对话框将显示在屏幕的中央。
- static void showMessageDialog(Component parent,Object message)
- javax.swing.Timer 1.2
- Timer(int interval, ActionListener listener)
构造一个定时器,每隔interval毫秒钟通告listener一次。 - void start()
启动定时器。一旦启动成功,定时器将调用监听器的actionPerformed。 - void stop()
停止定时器。一旦停止成功,定时器将不再调用监听器的actionPerformed。
- Timer(int interval, ActionListener listener)
- java.awt.Toolkit 1.0
- static Toolkit getDefaultToolkit()
获得默认的工具箱。工具箱包含有关GUI环境的信息。 - void beep()
发出一声铃响。
- static Toolkit getDefaultToolkit()
6.4 内部类
- 内部类(inner class)是定义在另一个类中的类。为什么需要使用内部类呢?其主要原因有以下三点:
- 内部类方法可以访问该类定义所在的作用域中的数据,包括私有的数据。
- 内部类可以对同一个包中的其他类隐藏起来。
- 当想要定义一个回调函数且不想编写大量代码时,使用匿名(anonymous)内部类比较便捷。
6.4.1 使用内部类访问对象状态
- 从传统的意义上讲,一个方法可以引用调用这个方法的对象数据域。内部类既可以访问自身的数据域,也可以访问创建它的外围类对象的数据域。
- 为了能够运行这个程序,内部类的对象总有一个隐式引用,它指向了创建它的外部类对象。
- 外围类的引用在构造器中设置。编译器修改了所有的内部类的构造器,添加一个外围类引用的参数。
6.4.2 内部类的特殊语法规则
- 通过,this限定词是多余的。不过,可以通过显式地命名将外围类引用设置为其他的对象。
6.4.3 内部类是否有用、必要和安全
- 如果内部类访问了私有数据域,就有可能通过附加在外围所在包中的其他类访问它们,但做这些事情需要高超的技巧和极大的决心。
6.4.4 局部内部类
- 局部类不用用public或private访问说明符进行声明。它的作用域被限定在声明这个局部类的块中。
- 局部类有一个优势,即对外部世界可以完全地隐藏起来。
6.4.5 由外部方法访问final变量
- 局部类还有一个优点。它们不仅能够访问它们的外部类,还可以访问布局变量。不过,那些局部变量必须被声明为final。
- final关键字可以应用于局部变量‘实例变量和静态变量。在所有这些情况下,它们的含义都是:在创建这个变量之后,只能够为之赋值一次。此后,再也不能修改它的值了,这就是final。
- 在定义final变量的时候,不必进行初始化。定义时没有初始化的final变量通常被称为空final(blank final)变量。
6.4.6 匿名内部类
- 假设只创建这个类的一个对象,就不必命名了。这种类被称为匿名内部类(anonymous inner class)。
- 由于构造器的名字必须与类名相同,而匿名类没有类名,所以,匿名类不能有构造器。取而代之的是,将构造器参数传递给超类(superclass)构造器。尤其是内部类实现接口的时候,不能有任何构造参数。
- 调用getClass时调用的是this.getClasss(),而静态方法没有this。所以应该使用以下表达式:
new Object(){}.getClass().getEnclosingClass()//get class of static method
,在这里,new Object(){}会建立Object的一个匿名子类的一个匿名对象,getEnclosingClass则得到其外围类,也就是包含这个静态方法的类。
6.4.7 静态内部类
- 有时候,使用内部类只是为了把一个类隐藏在另一个类的内部,并不需要内部类引用外围类对象。为此,可以将内部类声明为static,以便消除产生的引用。
- 只有内部类可以声明为static。静态内部类的对象除了没有对生成它的外围类对象的引用特权外,与其他所有内部类完全一样。
- 声明在接口中的内部类自动成为static和public类。
6.5 代理
- 利用代理可以在运行时创建一个实现了一组给定接口的新类。这种功能只有在编译时无法确定需要实现哪个接口才有必要使用。
- 代理类可以在运行时创建全新的类。这样的代理类能够实现指定的接口。尤其是,它具有下列方法:
- 指定接口所需要的全部方法。
- Object类中的全部方法,例如,toString、equals等。
然后,不能在运行时定义这些方法的新代码。而是要提供一个调用处理器(invocaton handler)。调用处理器是实现了InvocationHandler接口的类对象。在这个接口中只有一个方法:Object invoke(Object proxy,Method method,Object[] args)
。
无论何时调用代理对象的方法,调用处理器的invoke方法都会被调用,并向其传递Method对象和原始的调用参数。调用处理器必须给出处理调用的方式。
- 要想创建一个代理对象,需要使用Proxy类的newProxyInstance方法。这个方法有三个参数:
- 一个类加载器(class loader)。作为Java安全模型的一部分,对于系统类和从因特网上下载下来的类,可以使用不同的类加载器。目前,用null表示使用默认的类加载器。
- 一个Class对象数组,每个元素都是需要实现的接口。
- 一个调用处理器。
- 使用代理可能处于很多原因,例如:
- 路由对远程服务器的方法调用。
- 在程序运行期间,将用户接口事件与动作关联起来。
- 为调试,跟踪方法调用。
- 代理类是在程序运行过程中创建的,然而,一旦被创建,就变成了常规类,与虚拟机中的任何其他类没有什么区别。
- 所有的砝码类都扩展于Proxy类。一个代理类只有一个实例域—调用处理器,它定义在roxy的超类中。为了履行代理对象的职责,所需要的任何附加数据都必须存储在调用处理器中。
- 所有的代理来都覆盖了Object类中的方法toString、equals和hashCode。如同所有的代理方法一样,这些方法仅仅调用了调用处理器的invoke。Object类中的其他方法(如clone和getClass)没有被重新定义。
- 没有定义代理类的名字,Sun虚拟机中的Proxy类将生成一个以字符串$Proxy开头的类名。
- 没有特定的类加载器和预设的一组接口来说,只能有一个代理类。也就是说,如果使用同一个类加载器和接口数组调用两次newProxyInstance方法的话,那么只能够得到同一个类的两个对象。也可以利用getProxyClass方法获得这个类:
Class proxyClass = Proxy.getProxyClass(null,interfaces);
。 - 代理类一定是public和final。如果代理类实现的所有接口都是public,代理类就不属于某个特定的包;否则,所有非公有的接口都必须属于同一个包,同时,代理类也属于这个包。
- 可以通过调用Proxy类中的isProxyClass方法检测一个特定的Class对象是否代表一个代理类。
- java.lang.reflect.InvocationHandler 1.3
- Object invoke(Object proxy,Method method,Object[] args)
定义了代理对象调用方法时希望执行的动作。
- Object invoke(Object proxy,Method method,Object[] args)
- java.lang.reflect.Proxy 1.3
- static Class getProxyClass(ClassLoader loader,Class[] interfaces)
返回实现指定接口的代理类。 - static Object newProxyInstance(ClassLoader loader,Class[] interfaces,InvocationHandler handler)
构造一个实现指定接口的代理类的实例。所有方法都将调用给定处理器对象的invoke方法。 - static boolean isProxyClass(Class c)
如果c是一个代理类返回true。
- static Class getProxyClass(ClassLoader loader,Class[] interfaces)
Java核心技术卷一基础知识-第6章-接口与内部类-读书笔记的更多相关文章
- Java核心技术卷一基础知识-第9章-Swing用户界面组件-读书笔记
第9章 Swing用户界面组件 本章内容: * Swing与模型-视图-控制器设计模式 * 布局管理概述 * 文本输入 * 选择组件 * 菜单 * 复杂的布局管理 * 对话框 本章将介绍构造功能更加齐 ...
- Java核心技术卷一基础知识-第5章-继承-读书笔记
第5章 继承 本章内容: * 类.超类和子类 * Object:所有类的超类 * 泛型数组列表 * 对象包装器和自动装箱 * 参数数量可变的方法 * 枚举类 * 反射 * 继承设计的技巧 利用继承,人 ...
- Java核心技术卷一基础知识-第14章-多线程-读书笔记
第 14 章 多线程 本章内容: * 什么是线程 * 中断线程 * 线程状态 * 线程属性 * 同步 * 阻塞队列 * 线程安全的集合 * Collable与Future * 执行器 * 同步器 * ...
- Java核心技术卷一基础知识-第11章-异常、断言、日志和调试-读书笔记
第11章 异常.断言.日志和调试 本章内容: * 处理错误 * 捕获异常 * 使用异常机制的技巧 * 使用断言 * 日志 * 调试技巧 * GUI程序排错技巧 * 使用调试器 11.1 处理错误 如果 ...
- Java核心技术卷一基础知识-第8章-事件处理-读书笔记
第8章 事件处理 本章内容: * 事件处理基础 * 动作 * 鼠标事件 * AWT事件继承层次 8.1 事件处理基础 在AWT所知的事件范围内,完全可以控制事件从事件源(event source)例如 ...
- Java核心技术卷一基础知识-第12章-泛型程序设计-读书笔记
第12章 泛型程序设计 本章内容: * 为什么要使用泛型程序设计 * 定义简单泛型类 * 泛型方法 * 类型变量的限定 * 泛型代码和虚拟机 * 约束与局限性 * 泛型类型的继承规则 * 通配符类型 ...
- Java核心技术卷一基础知识-第10章-部署应用程序和applet-读书笔记
第10章 部署应用程序和applet 本章内容: * JAR文件 * Java Web Start * applet * 应用程序首选项存储 10.1 JAR文件 一个JAR文件既可以包含类文件,也可 ...
- Java核心技术卷一基础知识-第7章-图形程序设计-读书笔记
第7章 图形程序设计 本章内容: * Swing概述 * 创建框架 * 框架定位 * 在组件中显示信息 * 处理2D图形 * 使用颜色 * 文本使用特殊字体 * 显示图像 本章主要讲述如何编写定义屏幕 ...
- Java核心技术卷一基础知识-第3章-Java的基本程序设计结构-读书笔记
第3章 Java的基本程序设计结构 本章内容: 一个简单的Java应用程序 字符串 注释 输入输出 数据类型 控制流 变量 大数值 运算符 数组 本章主要讲述程序设计相关的基本概念(如数据类型.分支以 ...
随机推荐
- kubernetes 常见问题整理
使用kubectl 命令是报错 报错: [root@k8s-master ~]# kubectl get pod The connection to the server localhost: was ...
- weex h5开发区别-实践初级篇
html标签 weex中没有标签的概念,html中标签对应于weex中的Components weex 无<span> .<p> ,用<text>替代.但是< ...
- boost asio死锁一例
socket close -> sendmessage -> io_service stop 先关闭socket句柄,再给windows窗口发送消息,然后io_service停止. 当se ...
- awk命令小结
先在此至敬朱双印老师,博客写得很详细:http://www.zsythink.net/archives/tag/awk/ 这是朱双印老师关于awk博客的链接,强力推荐给大家 AWK一般在网上说是一 ...
- mysql sql mode
/usr/local/mysql/bin/mysqld --verbose --help | grep -A 1 'Default options' (1)关于配置文件路径 有时候,我发现虽然尝试修改 ...
- 【NIFI】 Apache NiFI 使用技巧
本章介绍NIFI组件的使用. 主要有:Nginx反向代理NIFI,配置SSLContextService Nginx反向代理NIFI 使用nginx反向代理NIFI配置如下 upstream nifi ...
- Java--druidAPI查询
maven依赖<dependency> <groupId>in.zapr.druid</groupId> <artifactId>druidry< ...
- C++ 使用命名规范
刚开始正式学习C++, 之前写了一个C++ 的小程序,但是并没有注意命名规范之类的.这一次重写一个类似的程序,再加上这几天学习 c++Prime(发现好喜欢这本书.虽然看的很慢,每一小节都感是满满的干 ...
- 解决使用Mybatis 传入多参数使用map封装遇到的 “坑”问题
好久没来写些东西了,今天 我分享一下自己遇到的一个“小 坑”,这也许对您来说不是个问题,但是我还是希望对没有遇到过这类问题的朋友给个小小的帮助吧 是这样的,需求:需要实现根据多条件 且分页展示数据 1 ...
- jQuery子页面获取父页面元素并绑定事件
父页面HTML文件: <ul id="faul"> <li class="sonli">子页面列表1</li> <li ...