前面依次介绍了简单接口和扩展接口,给出的范例都是自定义的接口代码,其实Java系统本身就自带了若干行为接口,为了更好地理解系统接口的详细用法,接下来还是从一个基础的例子出发,抽丝剥茧地逐步说明接口的几种调用方式。
早在阐述如何使用数组的时候,就提到Java提供了Arrays工具可用于数组变量的常见处理,例如该工具的copyOf方法用来复制数组、sort方法用来给数组排序等等。当时特别指出,对数组运用sort方法的排序结果是升序排列,那么若想对数组进行降序操作的话,只有一个输入参数的sort方法便无能为力了。好在Arrays工具重载了其它几种sort方法,有个sort方法允许在第二个参数中传入比较器对象,则编译器会自动把排序规则替换为指定的比较器。这个数组元素的比较器就是系统自带的接口,名称叫做Comparator,该接口只定义了一个抽象方法compare,该方法的两个数组参数分别是待比较的两个数组元素,返回-1表示前一个元素要排在后一个元素的前面,返回1表示前一个元素要排在后一个元素的后面。故而只要自己写个新的比较器,并调整compare方法的返回数值,即可命令编译器按照指定规则排序。
为了理清排序方法及其比较器的因缘脉络,下面将从最基本、只有一个输入参数的sort方法讲起。首先对一个整型数组初始化赋值,再调用Arrays工具的sort方法对该数组进行排序,相应的操作代码如下所示:

	// 演示数组工具的默认升序排列
private static void sortIntArrayAsc() {
Integer[] intArray = { 89, 3, 67, 12, 45 };
Arrays.sort(intArray); // Arrays的sort方法默认为升序
String ascDesc = "intArray的升序结果为:";
for (Integer item : intArray) {
ascDesc = ascDesc + item + ", ";
}
System.out.println(ascDesc);
}

运行如上的测试代码,得到以下的日志信息:

整型数组的升序结果为:3, 12, 45, 67, 89,

可见sort方法果然是默认按照升序排列。
接着尝试自己定义一个数组比较器,主要是实现Comparator接口里面的compare方法,调整一下输入参数对应的返回值,使之按照降序方式排列。新定义的比较器代码示例如下:

//定义一个整型数组的降序比较器
public class SortDescend implements Comparator<Integer> { @Override
public int compare(Integer o1, Integer o2) {
//return Integer.compare(o1, o2); // 默认的参数顺序是升序
return Integer.compare(o2, o1); // 倒过来的参数顺序变成了降序
}
}

然后往比较方法sort中塞入第二个参数,取值为刚定义的比较器实例“new SortDescend()”,修改之后的数组排序代码见下:

	// 利用新定义的降序比较器实现对数组的降序排列
private static void sortIntArrayDesc() {
Integer[] intArray = { 89, 3, 67, 12, 45 };
// sort方法支持按照指定的排序器进行排列判断
// 新定义的SortDescend类实现了降序排列
Arrays.sort(intArray, new SortDescend());
String descDesc = "intArray的降序结果为:";
for (Integer item : intArray) {
descDesc = descDesc + item + ", ";
}
System.out.println(descDesc);
}

再次运行数组排序的测试代码,此时输出了以下的日志信息:

整型数组的降序结果为:89, 67, 45, 12, 3,

从日志结果发现,利用比较器SortDescend果真实现了整型数组的降序处理。
通过书写全新的数组比较器,固然能够实现指定的排序操作,可是也有几个不便之处:
第一,简简单单的几行compare代码,就得专门开个代码文件保存,着实耗费不小。
第二,即使不另外开辟代码文件,仅仅在原代码中增加一个内部类,也会把排序方法与比较器隔开一段距离,尽管说距离产生美,但距离也会产生隔阂呀。
第三,要是比较器的判断逻辑依赖于sort方法之前的某个局部变量,难不成比较器还得弄个构造方法传入这个局部变量的数值?
上述的几个问题虽然总有办法解决,不过若有便捷的方案显然更受欢迎。为此Java创造了一种名叫“匿名内部类”的概念,这个“匿名内部类”本质上属于内部类,但它表面看来没有名字,因而被称作“匿名”。其实就算开发者没给它命名,编译器也要自动给它取个代号,比如路人甲、路人乙、类A、类B等等,之所以省略了内部类的名称,是因为该方式为一种简化的写法。只要程序员给足了必要的信息,内部类的形态不完整没有关系,编译器会根据上下文自行推断此处的代码逻辑。并且匿名内部类的方法定义与实例创建操作合二为一,代码写起来更加流利,看到这里是不是跃跃欲试了呢?下面马上给出“匿名内部类”的实例创建格式:

		new 接口名称() {
// 这里要实现该接口声明的抽象方法
}

观察上面的匿名代码格式,可以看到两个重要信息,一个是new表示创建实例对象,另一个是接口名称表示该对象实现了指定接口,剩下起名字这种例行公事就交给编译器代劳了。既然见过了匿名内部类的使用格式,接着就把它应用到数组排序器当中,对于sort方法而言,相当于原来“new SortDescend()”的位置换成了匿名内部类的实例创建代码,替换之后的排序代码如下所示:

	// 通过匿名内部类完成自定义的排序操作
private static void sortIntArrayDescAnonymous() {
Integer[] intArray = { 89, 3, 67, 12, 45 };
// 匿名内部类无需专门定义形态完整的类,只需指明新创建的实例从哪个接口扩展而来
Arrays.sort(intArray, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return Integer.compare(o2, o1); // 倒过来的参数顺序变成了降序
}
});
String descDesc = "intArray采取匿名内部类的降序结果为:";
for (Integer item : intArray) {
descDesc = descDesc + item + ", ";
}
System.out.println(descDesc);
}

可见有了匿名内部类,从此无需额外定义专门的内部类,乃至单独的代码文件了。
数组比较器不仅适用于整型数组,还能用于其它类型比如字符串数组。当Arrays工具的sort方法给字符串数组排序的时候,默认是根据首字母的拼写顺序来升序排列,但是字符串时常需要按照长度排序,如此一来得重新实现一个按照字符串长度进行排序的比较器。现在利用匿名内部类,比较两个字符串长度的代码也能紧跟着sort方法了,具体的字符串排序代码如下所示:

	// 通过匿名内部类对字符串数组按照字符串长度进行排序
private static void sortStrArrayByLength() {
String[] strArray = { "说曹操曹操就到", "东道主", "风马牛不相及", "亡羊补牢", "无巧不成书",
"冰冻三尺非一日之寒", "同窗", "青出于蓝而胜于蓝" };
// 字符串数组的默认排序方式为根据首字母的拼写顺序,
// 下面的匿名内部类把排序方式改成了按照字符串长度进行排序
Arrays.sort(strArray, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
// 比较前后两个数组元素的字符串长度大小
return o1.length() < o2.length() ? -1 : 1;
}
});
String desc = "strArray比较字符串长度的升序结果为:";
for (String item : strArray) {
desc = desc + item + ", ";
}
System.out.println(desc);
}

运行以上的排序代码,观察得到的日志的确输出了排序好的字符串数组:

字符串数组比较字符串长度的升序结果为:同窗, 东道主, 亡羊补牢, 无巧不成书, 风马牛不相及, 说曹操曹操就到, 青出于蓝而胜于蓝, 冰冻三尺非一日之寒,

  

更多Java技术文章参见《Java开发笔记(序)章节目录

Java开发笔记(六十)匿名内部类的优势的更多相关文章

  1. Java开发笔记(十五)短路逻辑运算的优势

    前面提到逻辑运算只能操作布尔变量,这其实是不严谨的,因为经过Java编程实现,会发现“&”.“|”.“^”这几个逻辑符号竟然可以对数字进行运算.譬如下面的代码就直接对数字分别开展了“与”.“或 ...

  2. Java开发笔记(十六)非此即彼的条件分支

    前面花了大量篇幅介绍布尔类型及相应的关系运算和逻辑运算,那可不仅仅是为了求真值或假值,更是为了通过布尔值控制流程的走向.在现实生活中,常常需要在岔路口抉择走去何方,往南还是往北,向东还是向西?在Jav ...

  3. Java开发笔记(十九)规律变化的for循环

    前面介绍while循环时,有个名叫year的整型变量频繁出现,并且它是控制循环进出的关键要素.不管哪一种while写法,都存在三处与year有关的操作,分别是“year = 0”.“year<l ...

  4. Java开发笔记(十)一元运算符的技巧

    前面讲到赋值运算符的时候,提到“x = x+7”可以被“x += 7”所取代,当然Java编程中给某个变量自加7并不常见,常见的是给某变量自加1,就像走台阶,一般都是一级一级台阶地走,犯不着一下子跳上 ...

  5. Java开发笔记(十二)布尔变量论道与或非

    在编程语言的设计之初,它们除了可以进行数学计算,还常常用于逻辑推理和条件判断.为了实现逻辑判断的功能,Java引入了一种布尔类型boolean,用来表示“真”和“假”.该类型的变量只允许两个取值,即t ...

  6. Java开发笔记(十四)几种运算符的优先级顺序

    到目前为止,我们已经学习了Java语言的好几种运算符,包括算术运算符.赋值运算符.逻辑运算符.关系运算符等基础运算符,并且在书写赋值语句时都没添加圆括号,显然是默认了先完成算术.逻辑.关系等运算,最后 ...

  7. Java开发笔记(十八)上下求索的while循环

    循环是流程控制的又一重要结构,“白天-黑夜-白天-黑夜”属于时间上的循环,古人“年复一年.日复一日”的“日出而作.日落而息”便是每天周而复始的生活.计算机程序处理循环结构时,给定一段每次都要执行的代码 ...

  8. Java开发笔记(序)章节目录

    现将本博客的Java学习文章整理成以下笔记目录,方便查阅. 第一章 初识JavaJava开发笔记(一)第一个Java程序Java开发笔记(二)Java工程的帝国区划Java开发笔记(三)Java帝国的 ...

  9. Java开发笔记(五十六)利用枚举类型实现高级常量

    前面介绍了联合利用final和static可实现常量的定义,该方式用于简单的常量倒还凑合,要是用于复杂的.安全性高的常量,那就力不从心了.例如以下几种情况,final结合static的方式便缺乏应对之 ...

随机推荐

  1. Flutter 获取服务器数据

    文档 文档版本有些老 使用 dio 来获取数据 demo import 'dart:io'; import 'dart:convert'; import 'package:flutter/materi ...

  2. 使用datagrip链接mysql数据库的报错问题.

    1. datagrip刚打开时候,选择风格是白是黑后, 会有一个选择什么数据库,有oracle...一大堆,别选错了.我的是mysql,不要选成了windows sql 和sql. 2 基本设置写完, ...

  3. vue学习笔记:在vue项目里面使用引入公共方法

    首先新建一个文件夹:commonFunction ,然后在里面建立 一个文件common.js 建立好之后,在main.js里面引入这个公共方法 最后是调用这个公共方法 测试一下,我在公共方法里面写了 ...

  4. 认识scrapy

    1.写一个爬虫,需要做很多事情.比如:发送网络请求,数据解析,数据存储,反扒虫虫机制(更换IP代理,设置请求头等),异步请求等.这些工作如果每次都要从零开始写的话,比较浪费时间.因此scrapy吧一些 ...

  5. freemarker导出带图片的word文档

    最近做一个关于文档导出功能, 顺便学习了下freemarker,做了个关于导出带图片的word文档,模板并没有写全,只是验证代码的正确性 这只是做一个小功能,故只做了后台代码关于导出的代码,并未与前台 ...

  6. QEMU KVM Libvirt手册(11): Managing Storage

    When managing a VM Guest on the VM Host Server itself, it is possible to access the complete file sy ...

  7. NP-Completeness理解

    今天大年初一,哪里也没去,在家里重新看了下IOA的NP问题.感觉看明白了. 首先定义下: 所谓P问题是指所有能在多项式复杂度解决的问题,比如排序算法,n*n复杂度解决问题. 有些问题目前没有多项式复杂 ...

  8. HashMap和HashTable简介和区别

    一.HashMap简介 HashMap是基于哈希表实现的,每一个元素是一个key-value对,其内部通过单链表解决冲突问题,容量不足(超过了阀值)时,同样会自动增长. HashMap是非线程安全的, ...

  9. VsCode 使用专用编程字体FiraCode

    FiraCode资料:https://github.com/tonsky/FiraCode PHP代码效果如下: VsCode 配置中添加: "editor.fontFamily" ...

  10. Android 音视频开发(三):使用 AudioTrack 播放PCM音频

    一.AudioTrack 基本使用 AudioTrack 类可以完成Android平台上音频数据的输出任务.AudioTrack有两种数据加载模式(MODE_STREAM和MODE_STATIC),对 ...