前面依次介绍了简单接口和扩展接口,给出的范例都是自定义的接口代码,其实Java系统自己就自带了若干行为接口,为了更好地理解系统接口的详细用法,接下来仍是从一个基础的例子出发,抽丝剥茧地逐步说明接口的几种调用方式。
早在阐述如何使用数组的时候,就提到Java提供了Arrays工具可用于数组变量的常见处理,例如该工具的copyOf方法用来复制数组、sort方法用来给数组排序等等。当时特别指出,对数组运用sort方法的排序结果是升序排列,那么若想对数组进行降序操做的话,只有一个输入参数的sort方法便无能为力了。好在Arrays工具重载了其它几种sort方法,有个sort方法容许在第二个参数中传入比较器对象,则编译器会自动把排序规则替换为指定的比较器。这个数组元素的比较器就是系统自带的接口,名称叫作Comparator,该接口只定义了一个抽象方法compare,该方法的两个数组参数分别是待比较的两个数组元素,返回-1表示前一个元素要排在后一个元素的前面,返回1表示前一个元素要排在后一个元素的后面。故而只要本身写个新的比较器,并调整compare方法的返回数值,便可命令编译器按照指定规则排序。
为了理清排序方法及其比较器的因缘脉络,下面将从最基本、只有一个输入参数的sort方法讲起。首先对一个整型数组初始化赋值,再调用Arrays工具的sort方法对该数组进行排序,相应的操做代码以下所示:html
// 演示数组工具的默认升序排列 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); }
运行如上的测试代码,获得如下的日志信息:java
整型数组的升序结果为: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); }
再次运行数组排序的测试代码,此时输出了如下的日志信息:ide
整型数组的降序结果为: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); }
运行以上的排序代码,观察获得的日志的确输出了排序好的字符串数组:htm
字符串数组比较字符串长度的升序结果为:同学, 东道主, 亡羊补牢, 无巧不成书, 风马牛不相及, 说曹操曹操就到, 青出于蓝而胜于蓝, 冰冻三尺非一日之寒,
更多Java技术文章参见《Java开发笔记(序)章节目录》对象