本章内容
- 文件上传优化
- 函数式接口
- 自定义函数接口
- 函数式编程
- 常用的函数式接口
文件上传优化
- 文件名称需要优化
服务器端在保存文件的时候,名字如果固定,最终会导致服务器硬盘只会保留一个文件,对上传的文件名称需要进行优化。
//文件名称的优化
System.currentTimeMills() + new Random().nextInt(100000)+".jpg" //或者是其他
- 服务器端接收文件的优化
服务器端接收一个文件之后就关闭了,之后的其他客户端无法继续上传文件,我们使用循环进行改进,可以不断的接收不同的客户端传输过来的文件。
//使用循环
while(true){
Socket socket=serverSocket.accept();
...
...
}
- 服务器端接收客户端文件的效率问题的优化
服务器端在接收文件的时候,加入的某个客户端给你传输了一个大文件,此时就不能再接收其他用户文件,所以可以使用多线程技术优化接收效率
while(true){
Scoket socket = serverSocket.accept();
//使用多线程技术提高程序的效率
//有一个客户端上传文件就开启一个线程完成文件的上传
new Thread(new Runnable(){
//重写run方法
@Override
public void run(){
//使用网络字节输入流对象
InputStream is = socket.getInputStream();
//指定一个目录
File file= new File("D:\\upload")
if(!file.exits){
file.mkdirs();
}
// 防止同名的文件被覆盖
String fileName = System.currentTimeMills() + new Random().nextInt(1000000)+".jpg";
//构建一个本地的文件输出流对象
new FileOutputStream(file + "\\"+ fileName);
//完成文件的上传,最终把文件写入到服务器端的硬盘当中
}
}).start();
}
函数式接口
概念:
在Java中指的是,有且仅有一个抽象方法的接口就称为函数式接口。
函数式接口适用于函数式编程。在Java当中的函数式编程指的是Lambda表达式。所以函数式编程接口就是用来服务Lambda表达式的,只有保证接口中有且仅有一个抽象方法那么在Java中才能顺利的进行推导。
备注:“语法糖”是指适用语法更加便利,但是原理不变。
格式:
只有确保接口当中有且仅有一个抽象方法。
修饰符 interface InterfaceNam{
//只能有一个抽象方法
public abstract 返回值类型 方法名称(参数列表);
//还可以定义其他的非抽象方法
}
示例:
public interface FuntionInterFaceName{
public abstract void show01();
public default void show02(){
//..
}
//void show03(); 有且仅有一个抽象方法,才是函数式接口
}
@FunctionalInterface注解
与@Override注解类似,Java 8中专门为函数式接口引入的一个新注解FunctionalInterface
该注解主要是定义在接口上,一旦接口上使用该注解,编译器将会强制检查该接口是否是一个函数式接口,该接口中是不是有且仅有一个抽象方法
如果不是,编译报错。
@FunctionalInterface
//函数式接口的注解
public interface FunctionInterfaceOne {
//定义一个抽象方法
public abstract void method();
//void show();
default void show02(){
}
}
自定义函数式接口的用途
一般用于方法的参数和返回值上
函数式编程
能够兼顾Java面向对象特性基础上,通过Lambda表达式与方法引用,为开发者打开函数式编程的大门
Lambda表达式的延迟加载
有些场景代码运行,执行后结果不一定会被使用到,从而造成一定性能的浪费。而Lambda表达式的效果是延迟执行,正好可以解决此问题,提升性能。
/*
使用Lambda优化刚才的日志输出案例
Lambda具有延迟加载效果
Lambda的使用前提是需要提供一个函数式接口
*/
public class Demo02Lambda {
//定义一个显式日志的方法,方法的参数传递日志的等级和BuildLogMessage
public static void showLog(int levle,BuildLogMessage log){
if(levle<=3){
System.out.println(log.buildMessage());
}
}
public static void main(String[] args) {
String message1="执行mysql";
String message2="执行java.exe";
String message3="执行tomcat.exe操作";
// 调用showLog方法,参数是一个函数式接口————BuildLogMessage ,可以使用Lambda表达式
/* showLog(2, ()->{
//返回一个拼接好的字符串
return message1+message2+message3;
});*/
/* //简化Lambda表达式
showLog(2, ()->message1+message2+message3);*/
/*
使用Lambda表达式作为参数传递
只有满足条件,日志的等级<=3才会调用BuildLogMessage当中的方法buildMessage
才会进行字符串的拼接
如果条件不满足,日志的等级大于3
BuildLogMessage接口当中的方法buildMessage也不会执行
所以拼接字符串的动作也不会执行
*/
showLog(6, ()->{
System.out.println("不满足,不输出");
return message1+message2+message3;
});
}
}
备注:实际上使用内部类也可以达到这样的效果,只是将代码操作延迟到另外一个对象当中,通过调用方法来完成,
后面代码的执行取决于前面条件的判断结果
使用Lambda作为方法的参数和返回值
在Java当中,Lambda表达式是作为匿名内部类的替代品,如果一个方法参数是一个函数式接口,那么可以使用Lambda表达式进行替代
java.lang.Runnable
接口就是一个函数式接口
代码:
public class Demo01Lambda{
//定义一个方法,开启线程
public static void startThread(Runnable r){
new Thread(r).start();
}
public static void main(String []args){
startThread(()->{
System.out.println("开启一个新线程,线程任务被执行!");
});
//优化
startThread(()->System.out.println("开启一个新线程,线程任务被执行!"));
}
}
如果一个方法的返回值是一个函数式接口,那么我们可以直接使用一个Lambda表达式。
java.util.Comparator
接口是一个函数式接口
public class Demo02{
//定义一个方法,让方法的返回值是一个函数式接口
public static Comprator<String> createComprator(){
//此时返回值就是一个函数式接口
return new Comprator(){
@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","a","asadq","adsa","hieow"};
Arrays.sort(strs,createComprator());
System.out.println(Arrays.toString(strs));
}
}
常用的函数式接口
JDK提供了大量常用的函数式接口,用来丰富Lambda表达式的使用场景。他们主要在java.util.function
包中被提供
Supplier接口
java.util.function.Supplier<T>
接口,该接口中有且仅有以个无参方法: T get()
用来获取一个泛型参数指定类型的对象数据,由于该接口是一个函数式接口,所以我们可以使用Lambda表达式来操作。
Supplier 接口一般被称为生产型接口,指定接口的泛型是什么类型,那么接口中的get方法就会生产什么类型的数据
import java.util.function.Supplier;
//Supplier<T> 接口一般被称为生产型接口,
// 指定接口的泛型是什么类型,那么接口中的get方法就会生产什么类型的数据
public class Demo01Supplier {
//定义一个方法,方法的参数传递一个Supplier<T> 接口,泛型指定位String,get方法就会返回一个String
public static String getString(Supplier<String> sup){
return sup.get();
}
//定义一个方法,方法的参数传递一个Supplier<T> 接口,泛型指定Integer
public static int getNum(Supplier<Integer> sup){
return sup.get();
}
public static void main(String[] args) {
//调用getString方法,方法的参数传递一个Supplier<T> 接口,函数式接口,使用Lambda
String s= getString(()->{
return new String("你好Java" );
});
System.out.println(s);
//求一个int数组中的最值
int [] arr={10,50,41,15};
int num = getNum(() -> {
//求出最大值
int max = arr[0];
for (int i : arr) {
if (max < i) {
max = i;
}
}
return max;
});
System.out.println(num);
}
}
Consumer接口
java.util.funvtion.Consumer<T>
接口刚好和Supplier接口相反,不是用来生产一个数据,而是用来消费一个数据
数据的类型有泛型来指定。
accept方法
意思就是消费一个指定泛型的数据
代码如下:
package com.zhiyou100.funvtiopnInterface.demo03;
import java.util.function.Consumer;
/*
accept()方法,消费一个指定类型的数据
Consumer是一个消费型接口,泛型指定什么类型数据,就使用accept消费什么类型数据
至于怎么消费,需要自己定义(统计,求和,输出...)
*/
public class Demo02Consumer {
//定义一个方法,方法的参数传递一个Consumer<String>接口
public static void consumer(String str,Consumer<String> con){
//通过accept方法 消费字符串
con.accept(str);
}
public static void main(String[] args) {
//来调用消费者的consumer
consumer("abcdefg", name ->{
//把abcdefg转为大写
String s = name.toUpperCase();
String string = new StringBuffer(s).reverse().toString();//反转
System.out.println(string);//GFEDCBA
});
}
}
默认方法: andThen
如果一个方法的参数和返回值都是Consumer类型的,那么久可以实现这样的效果:消费数据的时候,首先做一个消费的操作,再做一次消费的操作。实现组合。 可以通过Consumer中 的默认方法andThen
方法来实现
import java.util.Arrays;
import java.util.function.Consumer;
public class Demo03ConsumerAndThen {
//定义一个方法,参数传递一个字符串和两个Consumer接口,Consumer的泛型指定String
public static void consumer(String str, Consumer<String> con1,Consumer<String> con2){
/* con1.accept(str);
con2.accept(str);*/
con1.andThen(con2).accept(str);
//先执行con1消费数据,在执行con2消费数据
}
public static void main(String[] args) {
//由于consumer方法的参数Consumer接口是一个函数式接口,使用Lambda表达式
consumer("Java31-天下-第一", (name1)->{
String sub = name1.substring(0, 6);
System.out.println(sub); //Java31
},(name2)->{
String[] strs = name2.split("-");
System.out.println(Arrays.toString(strs)); //[Java31, 天下, 第一]
} );
}
}
通过查看源码,andThen方法不允许传入null对象,否则就会抛出空指针异常
想要把两次消费动作连接起来,需要传入两个Consumer接口。通过andThen方法实现一步一步执行消费动作
Stream
在Java1.8中,由于Lambda表达式这种函数式编程,JDK引入了一个全新的概念Stream流
.用来解决已有集合类库的一些弊端的。
给定你一些集合的数据
public class Demo01Stream{
public static void main(String []args){
//先构建一个集合
List<String> list=new ArrayList<String>();
list.add("abc123");
list.add("aaa12");
list.add("bcd125");
list.add("abcd120");
list.add("bbb130");
//需要字符串中包含数字1的元素,取出
List<String> list2=new ArrayList<String>();
for(String str:list){
if(str.contains("1")){
list2.add(str);
}
}
//需要元素中字符串长度不超过6个的元素
List<String> list3=new ArrayList<Stirng>();
for(String str: list2){
if(str.length<=6){
list3.add(str);
}
}
//遍历查看想要的元素集合
for(String str: list3){
System.out.println(str);
}
}
}
当我们需要对集合当中的元素进行操作的时候,总是需要对集合不停的遍历…一定需要这样做吗?不一定
他只是你用来找到你需要的元素的一种方式,并不是目的。目的就是想要取出想要的元素并打印输出,以往的方式都是每次循环都要从头开始遍历,下一次还是需要从头开始
Java1.8可以使用Lambda表达式的衍生物Stream流来优化你遍历集合的方式
代码如下
public class Demo01Stream{
public static void main(String []args){
//先构建一个集合
List<String> list=new ArrayList<String>();
list.add("abc123");
list.add("aaa12");
list.add("bcd125");
list.add("abcd120");
list.add("bbb130");
//需要字符串中包含数字1的元素,取出
//需要元素中字符串长度不超过6个的元素
//遍历查看想要的元素集合
list.stream()
.filter( str -> str.contains("1"))
.filter( str -> str.length()<=6)
.forEach(System.out.println(str)) ;
}
}