熟悉接口的匿名内部类写法
匿名内部类回顾
格式:
new 父类名或者接口名(){
//方法重写
@Override
public void method(){
//执行语句
}
};
接口名称 对象名 = new 接口名称() {
//覆盖重写所有抽象方法
};
Runable接口
创建线程普通方式一;
/*
匿名内部类方式实现线程的创建
匿名:没有名字
内部类:写在其他类内部的类
匿名内部类作用:简化代码
把子类继承父类,重写父类的方法,创建子类对象合一步完成
把实现类实现类接口,重写接口中的方法,创建实现类对象合成一步完成
匿名内部类的最终产物:子类/实现类对象,而这个类没有名字
格式:
new 父类/接口(){
重复父类/接口中的方法
};
*/
public class Demo01InnerClassThread {
public static void main(String[] args) {
//线程的父类是Thread
// new MyThread().start();
new Thread(){
//重写run方法,设置线程任务
@Override
public void run() {
for (int i = 0; i <20 ; i++) {
System.out.println(Thread.currentThread().getName()+"-->"+"黑马");
}
}
}.start();
//线程的接口Runnable
//Runnable r = new RunnableImpl();//多态
Runnable r = new Runnable(){
//重写run方法,设置线程任务
@Override
public void run() {
for (int i = 0; i <20 ; i++) {
System.out.println(Thread.currentThread().getName()+"-->"+"程序员");
}
}
};
new Thread(r).start();
//简化接口的方式
new Thread(new Runnable(){
//重写run方法,设置线程任务
@Override
public void run() {
for (int i = 0; i <20 ; i++) {
System.out.println(Thread.currentThread().getName()+"-->"+"传智播客");
}
}
}).start();
}
}
匿名内部类实现线程创建
/*
匿名内部类方式实现线程的创建
匿名:没有名字
内部类:写在其他类内部的类
匿名内部类作用:简化代码
把子类继承父类,重写父类的方法,创建子类对象合一步完成
把实现类实现类接口,重写接口中的方法,创建实现类对象合成一步完成
匿名内部类的最终产物:子类/实现类对象,而这个类没有名字
格式:
new 父类/接口(){
重复父类/接口中的方法
};
*/
public class Demo01InnerClassThread {
public static void main(String[] args) {
//线程的父类是Thread
// new MyThread().start();
new Thread(){
//重写run方法,设置线程任务
@Override
public void run() {
for (int i = 0; i <20 ; i++) {
System.out.println(Thread.currentThread().getName()+"-->"+"黑马");
}
}
}.start();
//线程的接口Runnable
//Runnable r = new RunnableImpl();//多态
Runnable r = new Runnable(){
//重写run方法,设置线程任务
@Override
public void run() {
for (int i = 0; i <20 ; i++) {
System.out.println(Thread.currentThread().getName()+"-->"+"程序员");
}
}
};
new Thread(r).start();
//简化接口的方式
new Thread(new Runnable(){
//重写run方法,设置线程任务
@Override
public void run() {
for (int i = 0; i <20 ; i++) {
System.out.println(Thread.currentThread().getName()+"-->"+"传智播客");
}
}
}).start();
}
}
3.2 文件过滤器优化
java.io.FileFilter
是一个接口,是File的过滤器。 该接口的对象可以传递给File类的listFiles(FileFilter)
作为参数, 接口中只有一个方法。
boolean accept(File pathname)
:测试pathname是否应该包含在当前File目录中,符合则返回true。
分析:
- 接口作为参数,需要传递子类对象,重写其中方法。我们选择匿名内部类方式,比较简单。
accept
方法,参数为File,表示当前File下所有的子文件和子目录。保留住则返回true,过滤掉则返回false。保留规则:- 要么是.java文件。
- 要么是目录,用于继续遍历。
- 通过过滤器的作用,
listFiles(FileFilter)
返回的数组元素中,子文件对象都是符合条件的,可以直接打印。
代码实现:
public class DiGuiDemo4 {
public static void main(String[] args) {
File dir = new File("D:\\aaa");
printDir2(dir);
}
public static void printDir2(File dir) {
// 匿名内部类方式,创建过滤器子类对象
File[] files = dir.listFiles(new FileFilter() {
@Override
public boolean accept(File pathname) {
return pathname.getName().endsWith(".java")||pathname.isDirectory();
}
});
// 循环打印
for (File file : files) {
if (file.isFile()) {
System.out.println("文件名:" + file.getAbsolutePath());
} else {
printDir2(file);
}
}
}
}
3.3 Lambda优化
分析:FileFilter
是只有一个方法的接口,因此可以用lambda表达式简写。
lambda格式:
()->{ }
代码实现:
public static void printDir3(File dir) {
// lambda的改写
File[] files = dir.listFiles(f ->{
return f.getName().endsWith(".java") || f.isDirectory();
});
// 循环打印
for (File file : files) {
if (file.isFile()) {
System.out.println("文件名:" + file.getAbsolutePath());
} else {
printDir3(file);
}
}
}
3.4过滤器原理
`java.io.FileFilter`是一个接口,是File的过滤器。 该接口的对象可以传递给File类的`listFiles(FileFilter)` 作为参数, 接口中只有一个方法。
`boolean accept(File pathname) ` :测试pathname是否应该包含在当前File目录中,符合则返回true。
**分析**:
1. 接口作为参数,需要传递子类对象,重写其中方法。我们选择匿名内部类方式,比较简单。
2. `accept`方法,参数为File,表示当前File下所有的子文件和子目录。保留住则返回true,过滤掉则返回false。保留规则:
1. 要么是.java文件。
2. 要么是目录,用于继续遍历。
3. 通过过滤器的作用,`listFiles(FileFilter)`返回的数组元素中,子文件对象都是符合条件的,可以直接打印。
File类下的listFiles()方法一共做了3件事:
1. listFile方法会对构造方法中传递的目录进行遍历,获取目录中的每一个文件/文件夹-->并将文件/文件夹 封装为File对象。
2. listFile方法会调用参数传递的过滤器中的方法
3. listFile方法会把遍历得到的每一个File对象,传递给accept方法的参数pathname
例如:
listFile对目录文件进行遍历。假设遍历到c:\\abc\\abc.java 。遍历到 c:\\abc\\abc.java 就会调用accpet方法 并且将
c:\\abc\\abc.java封装成File对象 传递给accept方法的参数pathname 。accept方法返回一个true
accept方法返回的是一个布尔值:
true 就会把传递过去的File对象保存到File数组当中 例如把c:\\abc\\abc.java 保存到数组当中
false 就不会把传递过去的File对象保存到File数组当中
我们可以对accept方法过滤条件自行定义:
例如判断文件是不是以.java 结尾 是就返回 true 就把.java结尾的文件保存到File数组当中
接口里面有方法,通过实现类重写接口里面的方法
可以通过匿名内部类来实战接口里面的方法进行优化v--> ( new 接口 名称 重写接口里的方法)
可以用lambda表达式再进一步优化 -->(使用Lambda表达式优化匿名内部类(接口中只有一个抽象方法))
总结,有接口--> 想到接口的方法 -->想到接口的实现类 -->想到匿名内部类 --> 想到lambda表达式
import java.io.File;
/*
需求:
遍历c:\\abc文件夹,及abc文件夹的子文件夹
只要.java结尾的文件
c:\\abc
c:\\abc\\abc.txt
c:\\abc\\abc.java
c:\\abc\\a
c:\\abc\\a\\a.jpg
c:\\abc\\a\\a.java
c:\\abc\\b
c:\\abc\\b\\b.java
c:\\abc\\b\\b.txt
我们可以使用过滤器来实现
在File类中有两个和ListFiles重载的方法,方法的参数传递的就是过滤器
File[] listFiles(FileFilter filter)
java.io.FileFilter接口:用于抽象路径名(File对象)的过滤器。
作用:用来过滤文件(File对象)
抽象方法:用来过滤文件的方法
boolean accept(File pathname) 测试指定抽象路径名是否应该包含在某个路径名列表中。
参数:
File pathname:使用ListFiles方法遍历目录,得到的每一个文件对象
File[] listFiles(FilenameFilter filter)
java.io.FilenameFilter接口:实现此接口的类实例可用于过滤器文件名。
作用:用于过滤文件名称
抽象方法:用来过滤文件的方法
boolean accept(File dir, String name) 测试指定文件是否应该包含在某一文件列表中。
参数:
File dir:构造方法中传递的被遍历的目录
String name:使用ListFiles方法遍历目录,获取的每一个文件/文件夹的名称
注意:
两个过滤器接口是没有实现类的,需要我们自己写实现类,重写过滤的方法accept,在方法中自己定义过滤的规则
*/
public class Demo01Filter {
public static void main(String[] args) {
File file = new File("c:\\abc");
getAllFile(file);
}
/*
定义一个方法,参数传递File类型的目录
方法中对目录进行遍历
*/
public static void getAllFile(File dir){
File[] files = dir.listFiles(new FileFilterImpl());//传递过滤器对象
for (File f : files) {
//对遍历得到的File对象f进行判断,判断是否是文件夹
if(f.isDirectory()){
//f是一个文件夹,则继续遍历这个文件夹
//我们发现getAllFile方法就是传递文件夹,遍历文件夹的方法
//所以直接调用getAllFile方法即可:递归(自己调用自己)
getAllFile(f);
}else{
//f是一个文件,直接打印即可
System.out.println(f);
}
}
}
}
******************************************************************
过滤器实现类:
import java.io.File;
import java.io.FileFilter;
/*
创建过滤器FileFilter的实现类,重写过滤方法accept,定义过滤规则
*/
public class FileFilterImpl implements FileFilter{
@Override
public boolean accept(File pathname) {
/*
过滤的规则:
在accept方法中,判断File对象是否是以.java结尾
是就返回true
不是就返回false
*/
//如果pathname是一个文件夹,返回true,继续遍历这个文件夹
if(pathname.isDirectory()){
return true;
}
return pathname.getName().toLowerCase().endsWith(".java");
}
}
***********************************************************
过滤器 Lambda 优化
*
需求:
遍历c:\\abc文件夹,及abc文件夹的子文件夹
只要.java结尾的文件
c:\\abc
c:\\abc\\abc.txt
c:\\abc\\abc.java
c:\\abc\\a
c:\\abc\\a\\a.jpg
c:\\abc\\a\\a.java
c:\\abc\\b
c:\\abc\\b\\b.java
c:\\abc\\b\\b.txt
我们可以使用过滤器来实现
在File类中有两个和ListFiles重载的方法,方法的参数传递的就是过滤器
File[] listFiles(FileFilter filter)
java.io.FileFilter接口:用于抽象路径名(File对象)的过滤器。
作用:用来过滤文件(File对象)
抽象方法:用来过滤文件的方法
boolean accept(File pathname) 测试指定抽象路径名是否应该包含在某个路径名列表中。
参数:
File pathname:使用ListFiles方法遍历目录,得到的每一个文件对象
File[] listFiles(FilenameFilter filter)
java.io.FilenameFilter接口:实现此接口的类实例可用于过滤器文件名。
作用:用于过滤文件名称
抽象方法:用来过滤文件的方法
boolean accept(File dir, String name) 测试指定文件是否应该包含在某一文件列表中。
参数:
File dir:构造方法中传递的被遍历的目录
String name:使用ListFiles方法遍历目录,获取的每一个文件/文件夹的名称
注意:
两个过滤器接口是没有实现类的,需要我们自己写实现类,重写过滤的方法accept,在方法中自己定义过滤的规则
*/
class Demo02Filter {
public static void main(String[] args) {
File file = new File("c:\\abc");
getAllFile(file);
}
/*
定义一个方法,参数传递File类型的目录
方法中对目录进行遍历
*/
public static void getAllFile(File dir){
//传递过滤器对象 使用匿名内部类
/*File[] files = dir.listFiles(new FileFilter() { //new FileFilter接口 省去实现类 重写FileFilter接口理的accept方法
@Override
public boolean accept(File pathname) {
//过滤规则,pathname是文件夹或者是.java结尾的文件返回true
return pathname.isDirectory() || pathname.getName().toLowerCase().endsWith(".java");
}
});*/
//使用Lambda表达式优化匿名内部类(接口中只有一个抽象方法)
/*File[] files = dir.listFiles((File pathname)->{
return pathname.isDirectory() || pathname.getName().toLowerCase().endsWith(".java");
});*/
File[] files = dir.listFiles(pathname->pathname.isDirectory() || pathname.getName().toLowerCase().endsWith(".java"));
/*File[] files = dir.listFiles(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
//过滤规则,pathname是文件夹或者是.java结尾的文件返回true
return new File(dir,name).isDirectory() || name.toLowerCase().endsWith(".java");
}
});*/
//使用Lambda表达式优化匿名内部类(接口中只有一个抽象方法)
/*File[] files = dir.listFiles((File d, String name)->{
//过滤规则,pathname是文件夹或者是.java结尾的文件返回true
return new File(d,name).isDirectory() || name.toLowerCase().endsWith(".java");
});*/
//File[] files = dir.listFiles((d,name)->new File(d,name).isDirectory() || name.toLowerCase().endsWith(".java"));
for (File f : files) {
//对遍历得到的File对象f进行判断,判断是否是文件夹
if(f.isDirectory()){
//f是一个文件夹,则继续遍历这个文件夹
//我们发现getAllFile方法就是传递文件夹,遍历文件夹的方法
//所以直接调用getAllFile方法即可:递归(自己调用自己)
getAllFile(f);
}else{
//f是一个文件,直接打印即可
System.out.println(f);
}
}
}
}
第五章 Collections
2.1 常用功能
java.utils.Collections
是集合工具类,用来对集合进行操作。部分方法如下:
public static <T> boolean addAll(Collection<T> c, T... elements)
:往集合中添加一些元素。public static void shuffle(List<?> list) 打乱顺序
:打乱集合顺序。public static <T> void sort(List<T> list)
:将集合中元素按照默认规则排序。public static <T> void sort(List<T> list,Comparator<? super T> )
:将集合中元素按照指定规则排序。
代码演示:
public class CollectionsDemo {
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<Integer>();
//原来写法
//list.add(12);
//list.add(14);
//list.add(15);
//list.add(1000);
//采用工具类 完成 往集合中添加元素
Collections.addAll(list, 5, 222, 1,2);
System.out.println(list);
//排序方法
Collections.sort(list);
System.out.println(list);
}
}
结果:
[5, 222, 1, 2]
[1, 2, 5, 222]
代码演示之后 ,发现我们的集合按照顺序进行了排列,可是这样的顺序是采用默认的顺序,如果想要指定顺序那该怎么办呢?
我们发现还有个方法没有讲,public static <T> void sort(List<T> list,Comparator<? super T> )
:将集合中元素按照指定规则排序。接下来讲解一下指定规则的排列。
2.2 Comparator比较器
我们还是先研究这个方法
public static <T> void sort(List<T> list)
:将集合中元素按照默认规则排序。
不过这次存储的是字符串类型。
public class CollectionsDemo2 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<String>();
list.add("cba");
list.add("aba");
list.add("sba");
list.add("nba");
//排序方法
Collections.sort(list);
System.out.println(list);
}
}
结果:
[aba, cba, nba, sba]
我们使用的是默认的规则完成字符串的排序,那么默认规则是怎么定义出来的呢?
说到排序了,简单的说就是两个对象之间比较大小,那么在JAVA中提供了两种比较实现的方式,一种是比较死板的采用java.lang.Comparable
接口去实现,一种是灵活的当我需要做排序的时候在去选择的java.util.Comparator
接口完成。
那么我们采用的public static <T> void sort(List<T> list)
这个方法完成的排序,实际上要求了被排序的类型需要实现Comparable接口完成比较的功能,在String类型上如下:
public final class String implements java.io.Serializable, Comparable<String>, CharSequence {
String类实现了这个接口,并完成了比较规则的定义,但是这样就把这种规则写死了,那比如我想要字符串按照第一个字符降序排列,那么这样就要修改String的源代码,这是不可能的了,那么这个时候我们可以使用
public static <T> void sort(List<T> list,Comparator<? super T> )
方法灵活的完成,这个里面就涉及到了Comparator这个接口,位于位于java.util包下,排序是comparator能实现的功能之一,该接口代表一个比较器,比较器具有可比性!顾名思义就是做排序的,通俗地讲需要比较两个对象谁排在前谁排在后,那么比较的方法就是:
-
public int compare(String o1, String o2)
:比较其两个参数的顺序。两个对象比较的结果有三种:大于,等于,小于。
如果要按照升序排序,
则o1 小于o2,返回(负数),相等返回0,01大于02返回(正数)
如果要按照降序排序
则o1 小于o2,返回(正数),相等返回0,01大于02返回(负数)
操作如下:
Comparator是一个接口
匿名内部类 实现接口方法
对方法重写
public class CollectionsDemo3 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<String>();
list.add("cba");
list.add("aba");
list.add("sba");
list.add("nba");
//排序方法 按照第一个单词的降序
Collections.sort(list, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o2.charAt(0) - o1.charAt(0);
}
});
System.out.println(list);
}
}
结果如下:
[sba, nba, cba, aba]
2.3 简述Comparable和Comparator两个接口的区别。
Comparable:强行对实现它的每个类的对象进行整体排序。这种排序被称为类的自然排序,类的compareTo方法被称为它的自然比较方法。只能在类中实现compareTo()一次,不能经常修改类的代码实现自己想要的排序。实现此接口的对象列表(和数组)可以通过Collections.sort(和Arrays.sort)进行自动排序,对象可以用作有序映射中的键或有序集合中的元素,无需指定比较器。
Comparator强行对某个对象进行整体排序。可以将Comparator 传递给sort方法(如Collections.sort或 Arrays.sort),从而允许在排序顺序上实现精确控制。还可以使用Comparator来控制某些数据结构(如有序set或有序映射)的顺序,或者为那些没有自然顺序的对象collection提供排序。
2.4 练习
创建一个学生类,存储到ArrayList集合中完成指定排序操作。
Student 初始类
public class Student{
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
测试类:
public class Demo {
public static void main(String[] args) {
// 创建四个学生对象 存储到集合中
ArrayList<Student> list = new ArrayList<Student>();
list.add(new Student("rose",18));
list.add(new Student("jack",16));
list.add(new Student("abc",16));
list.add(new Student("ace",17));
list.add(new Student("mark",16));
/*
让学生 按照年龄排序 升序
*/
// Collections.sort(list);//要求 该list中元素类型 必须实现比较器Comparable接口
for (Student student : list) {
System.out.println(student);
}
}
}
发现,当我们调用Collections.sort()方法的时候 程序报错了。
原因:如果想要集合中的元素完成排序,那么必须要实现比较器Comparable接口。
于是我们就完成了Student类的一个实现,如下:
public class Student implements Comparable<Student>{
....
@Override
public int compareTo(Student o) {
return this.age-o.age;//升序
}
}
再次测试,代码就OK 了效果如下:
Student{name='jack', age=16}
Student{name='abc', age=16}
Student{name='mark', age=16}
Student{name='ace', age=17}
Student{name='rose', age=18}
2.5 扩展
如果在使用的时候,想要独立的定义规则去使用 可以采用Collections.sort(List list,Comparetor c)方式,自己定义规则:
Collections.sort(list, new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
return o2.getAge()-o1.getAge();//以学生的年龄降序
}
});
效果:
Student{name='rose', age=18}
Student{name='ace', age=17}
Student{name='jack', age=16}
Student{name='abc', age=16}
Student{name='mark', age=16}
如果想要规则更多一些,可以参考下面代码:
Collections.sort(list, new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
// 年龄降序
int result = o2.getAge()-o1.getAge();//年龄降序
if(result==0){//第一个规则判断完了 下一个规则 姓名的首字母 升序
result = o1.getName().charAt(0)-o2.getName().charAt(0);
}
return result;
}
});
效果如下:
Student{name='rose', age=18}
Student{name='ace', age=17}
Student{name='abc', age=16}
Student{name='jack', age=16}
Student{name='mark', age=16}