在数学中,函数就是有输入量、输出量的一套计算方案,也就是拿什么东西做什么事”。相对而言,面向对象过分强调“必须通过对象的形式来做事情”,而函数式思想则尽量忽略面向对象的复杂语法,强调做什么,而不是以什么形式做,这样能够大大简化代码。例子如下,为了设置一个线程任务,我们需要创建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)代码块(方法体)就是该方案的具体执行步骤了;
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)一些参数;
2)一个箭头;
3)一段代码;
格式:
(参数列表)-> {一些重写方法的代码}
解释说明格式:
():接口中抽象方法的参数列表,没有参数就空着;有参数就写,多个参数用逗号分割;
->:”传递“的意思,把参数传递给方法体{ };
{}:重写接口的抽象方法的方法体,这个方法体默认就是等等要执行的方法体。
注意:
函数式接口,指的是接口里只有一个抽象方法的时候。
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);
}
}
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作为该接口的实例。
java.io.File类是文件和目录路径名的抽象表示,主要用于文件和目录的创建、查找和删除等操作。
java把电脑中的文件和文件夹(目录),封装为了一个File类,我们可以使用File类对文件和文件夹进行操作。File类是和系统无关的类,任何的操作系统都可以使用这个类的方法。我们可以使用File类的方法:
1)创建一个文件/文件夹;
2)删除文件/文件夹
3)获取文件/文件夹;
4)判断文件/文件夹是否存在;
5)对文件夹进行遍历;
6)获取文件的大小;
重点:记住这三个单词:file文件、directory文件夹/目录、path路径。
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下使用反斜杠,但反斜杠是转义字符,因此用两个反斜杠代表一个普通的反斜杠。
/*
(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进行操作。
*/
/*
public String getAbsolutePath() 返回此File的绝对路径名字符串,无论File传入的是相对还是绝对,都是输出绝对。
public String getPath() 将此File转换为路径名字符串,相对返回相对,绝对返回绝对,相当于toString方法。
public String getName() 返回由此File表示的文件或目录的名称,不返回整个路径
public long length() 返回由此File表示的文件的长度,即文件的大小,字节为单位;不能获取文件夹大小,为0。
*/
public boolean exists()
//此类File表示的文件或者目录是否存在
public boolean isDirectory()
//此File表示的是否为目录
public boolean isFile()
//此File表示的是否为文件
注意:isFile()和isDirectory()两个方法是互斥的,因为电脑硬盘中只有文件和文件夹。但是路径必须存在,否则都为false。
public boolean createNewFile()
//当且仅当具有该名称的文件尚不存在时,创建一个新的空文件
//只能创建文件,不能创建文件夹;若路径不存在,抛出异常
//createNewFile声明抛出了IOException,我们调用这个方法,就必须处理这个异常,要么throws,要么trycatch。
public boolean delete()
//删除由此File表示的文件或目录
public boolean mkdir()
//创建由此File表示的目录
public boolean mkdirs()
//创建由此File表示的目录,包括任何必须但不存在的父目录
public String[] list()
//返回一个String数组,表示该File目录中的所有子文件或目录;
public File[] listFiles()
//返回一个File数组,表示该File目录中的所有的子文件或目录;
注意:
list方法和listFiles方法遍历的是构造方法中给出的目录;
如果构造方法中给出的目录的路径不存在,就会抛出空指针异常;
如果构造方法中给出的路径不是一个目录,也会抛出空指针异常。
递归的分类:
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);
}
}
}
例如,只搜索.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);
}
}
}
}
/*
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);
}
}
}
}
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);
}
}
}