JAVA学习8——Lambda表达式、File类、文件过滤器

1.Lambda表达式

1.1面向对象编程思想的不足

在数学中,函数就是有输入量、输出量的一套计算方案,也就是拿什么东西做什么事”。相对而言,面向对象过分强调“必须通过对象的形式来做事情”,而函数式思想则尽量忽略面向对象的复杂语法,强调做什么,而不是以什么形式做,这样能够大大简化代码。例子如下,为了设置一个线程任务,我们需要创建Runnable接口的实现类,重写run方法。

public class RunnableImpl implements Runnable {
	@Override
	public void run() {
		System.out.println(Thread.currentThread().getName()+"新线程创建了");
	}
}

public class Demo01Runnable {
	public static void main(String[] args) {
		RunnableImpl run = new RunnableImpl();
		Thread t = new Thread(run);
		t.start();

		//简化代码
		Runnable r = new Runnbale() {
			@Override
			public void run() {
				System.out.println(Thread.currentThread().getName() + "新线程创建了");
			}
		}
		new Thread(r).start();

		//再简化
		new Thread(Runnable r = new Runnbale() {
			@Override
			public void run() {
				System.out.println(Thread.currentThread().getName() + "新线程创建了");
			}
		}).start();
	}
}

以上代码的冗余分析:
1)Thread类需要Runnable接口作为参数,其中的抽象run方法是用来指定线程任务内容的核心;
2)为了指定run的方法题,不得不需要Runnable接口的实现类;
3)为了省去定义一个RunnableImpl实现类的麻烦,不得不使用匿名内部类;
4)必须覆盖重写抽象run方法,所以方法名称、方法参数、方法返回值不得不再写一遍,且不能错;
5)实际上,只有方法体才是关键所在。

通过观察我们可以发现,Runnable接口只有一个run方法的定义,其实就是一个函数,它有以下三个特点:
1)无参数:不需要任何条件即可执行该方案;
2)无返回值:该方案不产生任何结果;
3)代码块(方法体)就是该方案的具体执行步骤了;

1.2 函数式编程方法——Lambda方法

JDK1.8中的重大更新,就是加入了Lambda表达式。通过Lambda表达式,上述Runnable接口的匿名内部类写法可以通过更加简单的Lambda表达式来实现。

public class Demo02LambdaRunnable {
	public static void main(String[] args) {
		new Thread(() -> System.out.println("多线程任务执行!")).start();//启动线程
	}
}

相对于Runnable接口,Lambda语法中同样的语义会更加简单。
1)前面的一对小括号即run方法的参数(无),代表不需要任何条件;
2)中间的一个箭头代表将前面的参数传递给后面的代码;
3)后面的输出语句即业务逻辑代码。

1.3 Lambda的标准格式

由三部分组成:
1)一些参数;
2)一个箭头;
3)一段代码;

格式:
(参数列表)-> {一些重写方法的代码}
解释说明格式:
():接口中抽象方法的参数列表,没有参数就空着;有参数就写,多个参数用逗号分割;
->:”传递“的意思,把参数传递给方法体{ };
{}:重写接口的抽象方法的方法体,这个方法体默认就是等等要执行的方法体。

注意:
函数式接口,指的是接口里只有一个抽象方法的时候。
Lambda表达式只能用于函数式接口!

1.4 Lambda表达式实例

//实例1
/*
	需求:给定一个厨师cook接口,内含唯一抽象方法makeFood,无参无返回值。
	使用Lambda的标准格式调用invokeCook方法,打印输出”吃饭啦!“字样。
*/
public Interface cook {
	void makeFood();
}

public class Demo01cook {
	public static void main(String[] args) }
		invokeCook(new Cook() {
			@Override
			public void makeFood() {
				System.out.println("吃饭了");
			}
		});

		//使用Lambda表达式简化匿名内部类的书写
		invokeCook(()->{
			System.out.println("吃饭了");
		});
	}
	//定义一个方法,参数传递Cook接口,方法内部调用Cook接口中的方法makefood
	public static void invokeCook(Cook cook) {
		cook.makeFood();
	}
}
//实例2
public class Person {
	private String name;
	private int age;
	public Person() {
	}
	public Person(String name, int age) {
		this.name=name;
		this.age=age;
	}
	@Override
	public String toString() {
		return "Person{" +
					"name='" + name + '\'' +
					:,age=" + age +
					'}'; 
	}
	/*
		省略get和set函数
	*/
}

/*
需求:
	使用数组存储多个Person对象
	对数组的Person对象使用Arrays的sort方法通过年龄进行升序排序
*/
public class Demo01Arrays {
	public static void main(String[] args) {
		Person[] arr = {
					new Person("lisa",33),
					new Person("nancy",18),
					new Person("lulu",23)
		};
		Arrays.sort(arr,new Compare(Person o1, Person o2) {
			@Override
			public int compare(Person o1, Person o2) {
				return o1.getAge() - o2.getAge();
			}
		});
		
		//使用lambda表达式,表示上面语句
		Arrays.sort(arr,(Person o1, Person o2) -> {
			return o1.getAge()-o2.getAge(); 
		});

		for (Person p : arr) {
			System.out.println(p);
		}
	}
}
/*
实例3——有参数有返回的Lambda表达式
  
*/
public interface Calculator {
	int calc(int a, int b);
	//只有一个抽象函数的接口,即为函数式接口
}

public class Demo {
	public static void main(String[] args) {
		//
		int a = 2;
		int b = 3;
		invokeCalc(a, b, new Calculator() {
			@Override
			int calc(int a, int b) {
				return a+b;
			}
		});

		invokeCalc(a, b, (int a, int b) -> {
			return a+b;
		});

		invokeClac
	}

	priavte static void invokeCalc(int a, int b, Calculator calculator) {
		int result = calculator.calc(a,b);
		System.out.println("结果是:" + result);
	}
}

1.5 Lambda省略格式

lambda表达式:是可推导可省略的;
凡是根据上下文推导出来的内容,都可以省略书写;
可以省略的内容:
1)(参数列表):括号中,参数列表的数据类型,可以省略不写
2)(参数列表):括号中的参数如果只有一个,那么类型和()都可以省略;
3)(一些代码):如果{}中的代码只有一行 ,无论是否有返回值,都可以省略({},return,分号)
注意:要省略{},则return和分号必须一起省略。

这一点在其他方面也有体现,例如下面这个例子:

public class Demo01ArrayList {
	public static void main(String[] args) {
		ArrayList list01 = new ArrayList();

		//JDK1.7之后,=号后面的泛型可以省略,后面的泛型可以根据前边的泛型推导出来
		ArrayList list02 = new ArrayList<>();
	}
}

接下来将之前的lambda表达式进行省略:


//原来
new Thread(()->{
		System.out.println(Thread.currentThread().getName()+"新线程");
}).start();
//优化
new Thread(()->System.out.println(Thread.currentThread().getName()+"新线程")).start();
/*===========================================*/

//未优化前
invokeCook(()->{
		System.out.println("ss");
});
//优化后
invokeCook(()->System.out.println("ss"));

/*=======================================*/
//前
Arrays.sort(arr,(Person o1, Person o2)-> {
	return o1.getAge() - o2.getAge();
});
//后
Arrays.sort(arr, (o1,o2)->o1.getAge() - o2.getAge());

/*==========================================*/
//前
invokeCalc(120, 130, (int a, int b)->{
	return a+b;
});
//后
invokeCalc(120,130,(a,b)->a+b);

Lambda的使用前提
1)使用Lambda必须具有接口,且要求接口中有且仅有一个抽象方法。
2)使用Lambda必须具有上下文推断,也就是方法的参数或者局部变量类型必须为Lambda对应的接口类型,才能使用Lambda作为该接口的实例。

2. File类

java.io.File类是文件和目录路径名的抽象表示,主要用于文件和目录的创建、查找和删除等操作。

java把电脑中的文件和文件夹(目录),封装为了一个File类,我们可以使用File类对文件和文件夹进行操作。File类是和系统无关的类,任何的操作系统都可以使用这个类的方法。我们可以使用File类的方法:
1)创建一个文件/文件夹;
2)删除文件/文件夹
3)获取文件/文件夹;
4)判断文件/文件夹是否存在;
5)对文件夹进行遍历;
6)获取文件的大小;

重点:记住这三个单词:file文件、directory文件夹/目录、path路径。

2.1 File类的静态成员变量

1)static String pathSeparator 与系统有关的路径分隔符,为了方便,它被表示为一个字符串;windows下是分号,Linux下是冒号。

2)static char pathSeparatorChar 与系统有关的路径分隔符;

3)static String separator 与系统有关的默认名称分隔符,为了方便,它被表示为一个字符串;
文件名称分隔符,windows是反斜杠,linux是正斜杆。
因为这个关系,操作路径不能写死了:
C:\develop\a\a.txt windows
C:/develop/a/a.txt linux
“C:”+File.separator+“develop”+File.separator+“a”+File.separator+“a.txt”" 这样在windows和linux中都能正常显示。

4)static char separatorChar 与系统有关的默认名称分隔符。

关于路径:
(1)绝对路径
(2)相对路径
注意:路径不区分大小写;路径中的文件名称分隔符,windows下使用反斜杠,但反斜杠是转义字符,因此用两个反斜杠代表一个普通的反斜杠。

2.2 File类的构建

/*
(1)File(String pathname) 通过给定路径名称字符串转换未抽象路径名来创建一个新的File实例。
参数:
	String pathname:字符串的路径名称;
	路径可以是以文件结尾,也可以是以文件夹结尾;
	路径可以是相对或绝对路径;
	路径可以存在也可以不存在;
	创建File对象,只是把字符串路径封装为File对象,不考虑路径的真假情况
*/

priavte static void show01() {
	File f1 = new File("c:\\Users\\a.txt");
}

/*
	(2)File(String parent, String child) 根据parent路径名字字符串和child路径名字符串创建一个新File实例
	参数:把路径分为两部分
		String parent:父路径
	这样父路径子路径单独书写,用起来灵活
*/

/*
	(3)File(File parent, String child)
	这里父路径是File类型,这样方便使用File类中方法对File进行操作。
*/

2.3 File获取功能的常用方法

/*
public String getAbsolutePath()		返回此File的绝对路径名字符串,无论File传入的是相对还是绝对,都是输出绝对。

public String getPath()				将此File转换为路径名字符串,相对返回相对,绝对返回绝对,相当于toString方法。

public String getName()				返回由此File表示的文件或目录的名称,不返回整个路径
 
public long length()					返回由此File表示的文件的长度,即文件的大小,字节为单位;不能获取文件夹大小,为0。
*/

2.4 判断功能的方法

public boolean exists()
//此类File表示的文件或者目录是否存在

public boolean isDirectory()
//此File表示的是否为目录

public boolean isFile()
//此File表示的是否为文件 

注意:isFile()和isDirectory()两个方法是互斥的,因为电脑硬盘中只有文件和文件夹。但是路径必须存在,否则都为false。

2.5 创建删除功能的方法

public boolean createNewFile()
//当且仅当具有该名称的文件尚不存在时,创建一个新的空文件
//只能创建文件,不能创建文件夹;若路径不存在,抛出异常
 //createNewFile声明抛出了IOException,我们调用这个方法,就必须处理这个异常,要么throws,要么trycatch。

public boolean delete()
//删除由此File表示的文件或目录

public boolean mkdir()
//创建由此File表示的目录

public boolean mkdirs()
//创建由此File表示的目录,包括任何必须但不存在的父目录 

2.6 目录的遍历

public String[] list()
//返回一个String数组,表示该File目录中的所有子文件或目录;

public File[] listFiles()
//返回一个File数组,表示该File目录中的所有的子文件或目录;  

注意:	
	list方法和listFiles方法遍历的是构造方法中给出的目录;
	如果构造方法中给出的目录的路径不存在,就会抛出空指针异常;
	如果构造方法中给出的路径不是一个目录,也会抛出空指针异常。

2.7. 递归

递归的分类:
1)递归分为两种,直接递归和间接递归;
2)直接递归称为方法自身调用自己;
3)间接递归可以A方法调用B方法,B方法调用C方法,C方法调用A方法;

注意事项:
(1)递归一定要有条件限定,保证递归能够停下来,否则发送栈内存溢出;
(2)在递归中虽然有限定条件,但递归次数不能太多,否则也会发送栈内存溢出;
(3)构造方法,禁止递归;

/*
定义一个方法,参数传递File类型的目录
方法中对目录进行遍历
*/
public static void getAllFile(File dir) {
	System.out.println(dir);
	File[] files = dir.listFiles();
	for(File f: files) {
		if(f.isDirectory()) {
			getAllFile(f);
		} else {
			System.out.println(f);
		}
	}
}

2.8 综合案例——文件搜索

例如,只搜索.java结尾的文件

public static void getAllFile(File dir) {
	File[] files = dir.listFiles();
	for(File f: files) {
		if(f.isDirectory()) {
			getAllFile(f);
		} else {
			if(f.getName().toLowerCase().endsWith(".java")){
				System.out.println(f);
			}
		}
	}
}

2.9 文件过滤器

/*
java.io.FileFilter接口:用于抽象路径名(File对象)的过滤器。
	作用:用来过滤文件(File对象)
	抽象方法:用来过滤文件的方法
		boolean accept(File pathname) 测试指定抽象路径名是否应该包含在某个路径名列表中。
			参数:
				File pathname:使用ListFiles方法遍历目录,得到的每一个文件对象

java.io.FilenameFilter接口:实现此接口的类实例,可以用于过滤文件名。
	抽象方法:用来过滤文件的方法
		boolean accept(File dir, String name)  测试指定文件是否应该包含在某一文件列表中。
		参数:
			File dir:构造方法中传递的被遍历的目录;
			String name:使用ListFilter方法遍历目录,获取每一个文件/文件夹的名称。

注意,两个过滤器接口是没有实现类的,需要我们自己写实现类,重写过滤的方法accept,在方法中自己定义过滤器的规则。 
*/

/*
	因为FilenameFilter接口和FileFilter接口没有实现类,因此需要创建过滤器FileFilter的实现类,重写过滤方法accept,定义过滤规则。
*/
public class FileFilterImpl implements FileFilter {
	@Override
	public boolean accept(File pathname) {
		//return true;
		if(pathname.isDirectory()) {
			return true;//如果pathname是一个文件夹,返回true,继续遍历这个文件夹
		}
		return pathname.getName().toLowerCase().endsWith(".java");
	}
	//accept方法返回值是一个布尔值,如果是true,就会把传递过去的File对象保存下来;false就不会保存,具体保存方法见该接口实现列的调用方法中。
}

public class Demo {
	public static void main(String[] args) {
		File file = new File("c:\\abc");
		getAllFile(file);
	}
	public static void getAllFile(File dir) {
		File[] file = dir.listFiles(new FileFilterImpl());//传递过滤对象
		/*
			这里listFiles方法一共做了3件事情:
			1)listFiles方法对构造方法中传递的目录进行遍历,获取了目录中的每一个文件/文件夹,并封装为File对象
			2)listFiles方法会调用参数传递的过滤器中的accept方法;
			3)listFiles方法会把遍历得到的每一个File对象,传递到accept方法的参数pathname中;
		*/
		for (File f: files) {
			if(f.isDirectory()) {
				getAllFile(f);
			} else{
				System.out.println(f);
			}
		}
	}
}

2.10 将文件过滤器和匿名类和Lambda表达式结合起来


public static void getAllFile(File dir) {
		//File[] file = dir.listFiles(new FileFilterImpl());//传递过滤对象
		//改成匿名内部类
		File[] file = dir.listFiles(new FileFilter() {
			@Override
			public boolean accept(File pathname) {
				return pathname.isDirectory() || pathname.getName().toLowerCase().endsWith(".java");
			}
		}); 
		//其lambda表达式为
		File[] files = dir.listFiles((File d, String name)->{
			return new File(d,name).isDirectory() || name.toLowerCase().endsWith(".java");
		});
		
		//同样可以试一试FilenameFileter()
		File[] files = dir.listFiles(new FilenameFilter() {
			@Override
			public boolean accept(File dir, String name) {
				return new File(dir,name).isDirectory() || name.toLowerCase().endsWith(".java");
			}
		});

		//lambda
		File[] file = dir.listFiles((File dir, String name)->{
			return new File(dir,name).isDirectory() || name.toLowerCase().endsWith(".java");
		});


		for (File f: files) {
			if(f.isDirectory()) {
				getAllFile(f);
			} else{
				System.out.println(f);
			}
		}
	}

你可能感兴趣的:(JAVA,SE,学习,java)