- 函数式接口
- 自定义函数式接口
- 函数式编程
- 常用的函数接口
- Stream流
- 方法引用
文件上传优化
文件的名称需要优化
服务端,保存文件的名称如果固定,那么最终会导致服务器硬盘只保留一个文件,对上传的文件名称需要进行优化
//文件名称定义规则
"beautiful" + System.currentTimeMillis() + new Random().nextInt(1000000) + ".jpg"
如何保证服务器端一直处于接收文件状态
服务器端接收一个文件之后仍不关闭,使用循环进行实现
//使用循环
while(true){
Socket socket = serverSocket.accept();
...
}
服务器端接收客户端文件的效率问题
服务器端在接收文件时,假如某个客户端传入一个大文件,此时就不能再接收其它客户端的文件,可以使用多线程技术优化提升效率
while(true){
Socket socket = serverSocket.accept();
//使用多线程技术,提高效率
//一个客户端上传文件、开启一个线程完成此任务
new Thread(new Runnable(){
//重写run方法
@Override
public void run(){
InputStream is = socket.getInputStream();//使用网络字节输入流对象
//指定一个目录
File file = new File("D:\upLoad");
if(!file.exist){
file.mkdirs();
}
//防止同名文件被覆盖
String fileName = "beautiful" + System.currentTimeMillis() + new Random().nextInt(1000000) + ".jpg";
//构建一个本地的文件字节输出流对象
FileOutputStram fos = new FileOutputStram(file+"\" + fileName);
//完成读写
int len = 0;
char []chars =new char[1024];
while((len = is.read(chars))!=-1){
fos.write(chars,0,len);
}
}
}).start;
}
//serverSocket.close() 服务器不需要关闭
函数式接口
在Java当中指的是:有且仅有一个抽象方法的接口称为函数式接口。(其他方法不做要求)
函数式接口,适用于函数式编程,在Java当中的函数式编程体现在Lambda表达式。只有确保接口当中有且仅有一个抽象方法,那么在Java中的Lambda才能进行顺利的推导(上下文环境)
备注:"语法糖" 是指使用更加便利、快捷的语法方式(原理不变)例如,Lambda是匿名内部类的语法糖
函数式编程四大核心接口:
Predicate
Function
Supplier
Consumer
格式:只有确保接口当中有且仅有一个抽象方法即可
修饰符 interface 接口名{
//只能定义一个抽象方法
返回值 方法名(参数列表);
}
public interface FuntionInterface{
int sum(int a,int b);
}
@FunctionalInterface注解
作用:检测接口是不是函数式接口,只有有且仅有一个抽象方法与该注解相容,Java8中专门为函数式接口引入的新注解。
自定义函数式接口的用途
对于自定义函数式接口,一般充当方法的返回值和参数。
函数式编程
能够在兼顾Java的面向对象特性基础上,通过Lambda表达式与后面的方法引用,为开发者打开函数式编程的大门
Lambda的延迟加载
有些场景的代码运行执行后,结果不一定会被使用到,从而造成性能的浪费,而Lambda表达式是延迟执行的,正好可以解决此问题,提升性能。
showLog(4,()->{ //前面条件不满足,后面表达式就不加载了。
//重写run方法
});
//使用lambda表达式作为参数传递,只有满足条件,后面的接口方法实现才会被加载
备注:实际上使用匿名内部类也可以达到同样的效果,只是将代码操作延迟到另外一个对象当中通过调用来完成、方法来完成后面的代码执行取决于前面的条件的判断结果
在Java中,Lambda表达式是作为匿名内部类的替代品,如果一个方法的参数是一个函数式接口类型,那么可以使用Lambda表达式替代
java.lang.Runnable
接口就是一个函数式接口
java.util.Comparator
接口也是一个函数式接口
public class LambdaDemo{
//定义一个方法,返回值类型是Comparator接口
public static Comparator<String> creatComparator(){
//常规
return new Comparator(){
@Override
public int compare(String o1,String o2){
return o1.length()-o2.length();
}
}
//Lambda
return (o1,o2) -> o1.length()-o2.length();
}
public static void main(String []args){
String []strs = {"aaa","bbb","ccccc"};
Arrays.sort(strs,creatComparator);// 调用获得的比较器
}
}
常用的函数式接口
JDK提供了大量的常用函数式接口,丰富Lambda表达式的使用场景。他们主要在java.util.function
包中被提供。
Supplier接口,该接口中有且仅有一个无参的方法 T get()
,它是用来获取一个泛型参数指定类型的对象数据。由于该接口是一个函数式接口,可以使用Lambda表达式对其操纵。
Supplier
public static void main(String[] args) {
//调用getString方法,参数传递一个Supplier<T>是一个函数式接口,可以使用Lambda
System.out.println(getString(() ->{
//生产返回一个字符串
return "Nike good boy!";
}));
}
//定义一个方法,方法的参数传递一个Supplier<T>接口类型、泛型指定String,get方法就会返回一个String
public static String getString(Supplier<String> sup){
return sup.get();
}
Consumer接口,被称为消费型接口,该接口刚好和Supplier接口相反,它不是用来生产一个数据,而是消费一个数据。至于数据的类型由泛型来指定。accept方法:消费一个指定泛型的数据
//定义一个方法、方法的参数传递一个Consumer<String>接口,传递一个字符串值
public static void consumer(String s,Consumer<String> con){
con.accept(s); //使用accept消费数据
}
public static void main(String[] args) {
//调用消费方法
consumer("abcdefg",name->{
//把里面的字符串改为大写
System.out.println(new StringBuilder(name.toUpperCase()).reverse());//GFEDCBA
});
}
默认方法:andThen
如果一个方法的参数和返回值全是Consumer类型的,那么就可以实现这样的效果:消费数据的时候
,首先做一个消费的操作,再做一个消费的操作,实现组合。可以通过Consumer接口当中的默认方法andThen来实现
//定义一个方法,方法的参数传递一个字符串和两个Consumer接口
public static void consumer(String str, Consumer<String> con1,Consumer<String> con2){
//con1.accept(str);
//con2.accept(str);
//andThen连续消费操作
con1.andThen(con2).accept(str);
//规则:con1连接con2,先执行con1消费数据,再执行con2消费数据
}
public static void main(String[] args) {
//由于consumer方法的参数Consumer是一个函数式接口,可使用lambda
consumer("Java31-中国最棒-都是业界大佬",(name1)->{
//截取传入的字符串
String sub = name1.substring(0, 6);
System.out.println(sub);
},(name2)->{
String[] strs = name2.split("-");
System.out.println(Arrays.toString(strs));
});
}
andThen方法不允许传入null对象,否则抛出空指针异常。
要想把两次消费的动作连接起来,需要传入两个Consumer接口,通过andThen方法实现消费动作
练习:定义一个字符串数组,存储每一个人的信息,如 "张三,20,北京",存储多人上述信息,使用Consumer接口,按照指定格式进行打印输出:姓名:Xxx ;年龄:xx;地址:xx。要求
-
将打印姓名的动作作为第一个Consumer接口的规则
-
将打印年龄的动作作为第二个Consumer接口的规则
-
将打印地址的动作作为第三个Consumer接口的规则
public static void print(String[] arr, Consumer<String> consumer1, Consumer<String> consumer2, Consumer<String> consumer3) { for (String s1 : arr) { consumer1.andThen(consumer2).andThen(consumer3).accept(s1); //consumer1.accept(s1); //consumer2.accept(s1); //consumer3.accept(s1);//多个accept与使用andThen效果相同 } } public static void main(String args[]) { //精简Lambda String[] arr = {"张三,20,北京", "Lily,21,郑州", "Bob,23,纳尼", "二毛,30,上海", "狗剩,19,东京"}; print(arr, (name) -> System.out.print("姓名:" + name.split(",")[0] + "; ") , (age) -> System.out.print("年龄:" + age.split(",")[1] + "; ") , (address) -> System.out.println("地址:" + address.split(",")[2] + ";")); }
Stream流
在Java1.8中,由于Lambda表达式这种函数式编程,JDK引入了一个全新的概念Stream流
。用于解决已有集合类库的一些弊端。
给定一些集合的数据
public class StreamDemo{
public static void main(String agrs[]){
//构建一个集合
List<String> list = new ArrayList();
list.add("abc123");
list.add("aaa22");
list.add("bcd125");
list.add("abcd120");
list.add("bbb230");
// 将字符串中包含1的元素取出
List<String> list2 = new ArrayList();
for(String str : list){
if(str.contains("1")){
list2.add(str);
}
}
// 并将元素字符串不超过6个的元素取出来
List<String> list3 = new ArrayList();
for(String str : list2){
if(str.length()<=6){
list3.add(str);
}
}
//遍历查看集合元素
for(String str : list){
System.out.println(str);
}
}
}
当我们需要对集合中的元素进行操作的时候,总是需要对集合不停的遍历,遍历再遍历。烦不烦呐?很烦 !then:
Java1.8可以使用Lambda表达式的衍生优化遍历集合的方式。---Stream流
public class StreamDemo{
public static void main(String agrs[]){
//构建一个集合
List<String> list = new ArrayList();
list.add("abc123");
list.add("aaa22");
list.add("bcd125");
list.add("abcd120");
list.add("bbb230");
// 将字符串中包含1的元素取出
// 将元素字符串不超过6个的元素取出来
//遍历查看集合元素
list.stream() //stream来自Collection接口中
.filter(str -> str.contains("1"))
.filter(str -> str.length()<=6)
.forEach(str->System.out.println(str));
}
}
一般我们把流式思想称之为 "生产流水线"