文件上传优化:
1.文件名称优化需要优化
服务端,在保存文件的名称如果名称固定,那么最终会导致服务器硬盘,只会保留一个文件,对上传的文件名称优化,
//文件名称定义规则:
System.currenTimeMillis(); + new Random().nextInt(1000000);
2.服务端接收文件的优化
服务器端,接受一个文件之后就关闭了,后面的其他客户端无法继续上传文件,使用循环进行改进,可以不段的接收不同客户端传输过来的文件
//使用循环
while(true){
Socket socket serverSocket.accept();
…
…
}
3.服务器端接收客户端文件的效率优化
服务器端在接收文件的时候,假如某个客户端给你传输一个大文件,此时就不能在接受其他用户文件,所以可以使用多线程技术优化接收效率
while(true){
Socket serverSocket = 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.currenTimeMillis(); + new Random().nextInt(1000000)+".jpg";
//构建一个本地文件字节输出流对象
new FileOutputStream(file + filename;)
//一读一写文件的上传,最终把文件写入到服务器短的硬盘当中
……
}
}).start();
}
//服务器端不关闭
serSocket.close();
函数式接口
概念:
函数式接口在Java指的是:有且仅有一个抽象方法的接口就称为函数式接口.(默认方法,私有方法不管)
函数式接口,适用于函数式编程的,在Java当中的函数式编程体现在Lambda表达式,所以函数式接口就是用来服务Lambda表达式.
只有确保接口当中有且仅有一个抽象方法,Java中的Lambda才能顺利的进行推导.Lambda就是匿名内部类的缩写
备注:"语法糖",是指使用更加便利方便但是原理不变的代码语法.就比如遍历集合的时候使用for-each语法,其实地层使用的是迭代器,这便是"语法糖".
格式:
只有确保接口当中有且仅有一个抽象方法即可:
修饰符 interface IterfaceName{
//只能定义一个抽象方法
public abstract 返回值 方法名称(参数列表);
//还可以定义其他的非抽象方法
}
public interface FunctionInterfaceOne {
public abstract void show01();
public default void show02(){
//…
}
//void show03();//不是函数接口了 有且仅有一个抽象方法,才称为函数式接口
}
@FunctionalInterface注解
与@Override注解作用类似,Java8中专门为函数式接口引入一个新注解@FunctionalInterface,该注解主要定义在接口上,一旦接口使用该注解,
编译器将会强制检查该接口是不是一个函数接口,该接口中是不是有且仅有一个抽象方法,如果不是编译报错.
@FunctionalInterface
public interface FunctionInterfaceOne {
//定义一个抽象方法
void method();
//void show();
default void show02(){
}
}
自定义函数式接口的用途
对于自定义的函数接口,一般用于方法的参数和返回值上,
函数式编程:
能够兼顾Java的面向对象特性基础上,通过Lambda表达式与后面的方法引用,为开发者打开函数式编程的大门.
Lambda的延迟加载
有些场景的代码运行执行后,结果不一定会被使用到,而造成性能的浪费.而Lambda表达式是延迟执行,正好可以解决此问题,提升性能
代码如下:
public class Demo02Lambda {
//定义一个显示日志的方法,方法的参数传递日志的等级和BuildLogMessage接口
public static void showLog(int level, BuildLogMessage log){
//对日志信息等级判断,如果3以下的就进行输出日志信息
if (level <= 3){
System.out.println(log.buildLogMessage());
}
}
public static void main(String[] args) {
//定义一些日志信息
String message1 = "执行mysqld.exe操作";
String message2 = "执行Java.exe操作";
String message3 = "执行tomcat.ext操作";
//调用showLog方法,参数是一个函数式接口BuildLogMessage接口,所以可以使用Lambda表达式
/*showLog(2, ()->{
//返回一个拼接好的字符串
return message1+message2+message3;
});*/
//简化Lambda表达式
/*
使用Lambda表达式作为参数传递
只有满足这个条件,日志的等级小于等于3
才会调用此接口BuildLogMessage当中的方法buildLogMessage
才会进行字符串拼接的动作
如果条件不满足日志的等级大于3
那么BuildLogMessage接口中的方法buildLogMessage也不会执行
所以拼接字符串的动作也不会执行
所以不会存在性能上的浪费
*/
showLog(4, () -> {
System.out.println("前面的日志大于3,此处不执行");
return message1+message2+message3;
});
}
}
备注:实际上使用内部类也可以达到这样的效果,只是将代码操作延迟到另一个对象中通过调用方法来完成
后面代码的执行取决于前面的条件的判断结果
/*
使用Lambda表达式作为参数传递
只有满足这个条件,日志的等级小于等于3
才会调用此接口BuildLogMessage当中的方法buildLogMessage
才会进行字符串拼接的动作
如果条件不满足日志的等级大于3
那么BuildLogMessage接口中的方法buildLogMessage也不会执行
所以拼接字符串的动作也不会执行
所以不会存在性能上的浪费
*/
使用Lambda表达式作为参数传递和返回值
在java当中,Lambda表达式是作为匿名内部类的替代品,如果一个方法的参数是一个函数式接口类型的那么可以使用Lambda表达式进行替代
java.lang.Runnable接口就是一个函数式接口
代码如下:
public class Demo01Lambda{
//定义一个方法,开启线程的方法
public static void startThread(Runnable r){
new Thread?.start();
}
public static void main(String[] args){
startThread(() -> {
System.out.println("开启一个新线程,线程任务被执行了!");
});
//优化Lambda
startThread(() -> System.out.println("开启一个新线程,线程任务被执行了!"))
}
}
如果一个方法的返回值类型是一个函数式接口,那么我们可以直接使用一个Lambda表达式.
java.util.Comparator接口是一个函数式接口
代码如下:
public class Demo02Lambda{
//定义一个方法,.方法返回值类型是一个函数式接口类型Comparator
public static Comparator<String> createComparator(){
//返回值就是一个函数式接口
return new Comparator(){
@Override
public int compare(String o1,String o2){
//自定义比较规则 升序/降序
//字符串的长度
return o1.length()-o2.length();//升序
}
}
//使用Lambda表达式字符串长度升序
return (o1,o2) -> o1.lenght() - o2.length();
}
public static void main(String[] args){
String[] strs = {"aaa","a","abcdefg","ggggg"};
Arrays.sort(strs,createComparator());
System.out.println(Arrays.toString(strs));
}
}
常用的函数式接口
JDK官方提供了大量的常用的函数式接口,丰富Lambda表达式的使用场景.他们主要在java.util.function包中被提供
Supplier接口
java.util.function.Supplier<T>接口,该接口中有且仅有一个无参的方法:T.get().用来获取一个泛型参数指定的类型的对象数据,
由于该接口是一个函数式接口,所以我们可以使用Lambda表达式来操作它
Supplier<T>接口被称之为生产性接口,指定接口的泛型时什么类型,那么接口中的get方法就会生产什么类型的数据
Consumer接口
java.util.functionConsumer<T>接口刚好和Supplier接口相反,他不是用来生产一个数据,二十消费一个数据.数据类型由泛型来确定的
accept方法
意思是消费一个指定泛型数据.
代码示例
public class Demo02Consumer {
//定义一个方法,方法的参数传递一个Consumer<String>接口,传递一个字符串变量
public static void consumer(String str,Consumer<String> con){
//使用消费性接口对象,消费传递字符串值
con.accept(str);
}
public static void main(String[] args) {
//来调用消费方法Consumer<String>接口是一个函数类型接口,所以说可以使用Lambda表达式
consumer("abcdefg", name->{
//把里卖弄abcdefg字符串改为大写输出 消费的规则自定义
String s = name.toUpperCase();
String string = new StringBuilder(s).reverse().toString();
System.out.println(string);
});
}
}
默认方法andThen
如果一个方法的参数和返回值全都是Consumer类型,那么就可以实现这样的效果;消费数据的时候,首先做一个消费的操作,在做体格消费的操作实现组合
可以通过Cunsumer接口当中的默认方法andTen来实现
代码如下:
public class Demo03ConsumerAndThen {
//定义一个方法,方法参数传递一个字符串和两个Consumer接口 Consumer接口的泛型指定为字符串
public static void consumer(String str, Consumer<String> con1,Consumer<String> con2){
/* con1.accept(str);
con2.accept(str);*/
//andThen 连续消费 default Consumer<String> andThen
//先执行左边的Consumer--->con1,andThen--->再次执行con2的动作
con1.andThen(con2).accept(str);
//规则con1连接con2,先执行con1消费数据,在执行con2消费数据
}
public static void main(String[] args) {
//由于Consumers方法的参数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));//{"java31","...","..."}
});
}
}
通过查看源码得知:andThen方法不允许传入一个null对象,传了会抛出空指针异常
要想把两次消费的动作连接起来,需要传入两个Consumer接口,通过andThen方法实现一步一步执行消费动作.
练习:
定义一个字符串数组,存储每一个人的信息如"张三,20,河南省郑州市",存储五个人的信息
使用Consumer接口按照指定的格式进行打印输出:姓名:张三;年龄:20;地址:郑州市
要求将打印姓名的动作作为第一个consumer接口的规则
将打印年龄的动作作为第二个Consumer接口的规则
蒋打印地址的动作作为第三个Consumer接口的规则
public class Demo {
public static void show01(String[] arr, Consumer<String> con1,Consumer<String> con2,Consumer<String> con3){
for (String str : arr) {
con1.andThen(con2.andThen(con3)).accept(str);
}
}
public static void main(String[] args) {
//操作arr数组中的每一个元素
String[] arr = {"张三,20,郑州市","李四,18,三门峡市","王五,24,洛阳市",};
show01(arr, (name)->{
String[] split = name.split(",");
System.out.print("姓名:"+split[0]+";");
}, (age)->{
String[] split = age.split(",");
System.out.print(" 年龄:"+split[1]+";");
}, (address)->{
String[] split = address.split(",");
System.out.println(" 地址:"+split[2]+";");
});
}
}
Stream流:
在JDK1.8中,由于Lambda表达式这种函数式编程 JDK引入了一个全新的概念 Stream流.用于解决已有集合类库的一些弊端的.
给定一些集合数据
public class Demo01Stream{
public static void main(String[] args){
//集合
List<String> list = new Arrays<>();
list.add("abc123");
list.add("aaa22");
list.add("bcd225");
list.add("abcd120");
list.add("bbb330");
//需要字符串中包含数字1的元素取出来
List<String> list2 = new ArrayList<>();//abc123 bcd125 abcd120
for(String str:list){
if(str.contains("1")){
list2.add(str);
}
}
//需要集合当中字符串长度不能超过6个的元素取出来
List<String> list3 = new Arrays<>();
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 Arrays<>();
list.add("abc123");
list.add("aaa22");
list.add("bcd225");
list.add("abcd120");
list.add("bbb330");
//需要字符串中包含数字1的元素取出来
//需要集合当中字符串长度不能超过6个的元素取出来
//遍历查看最终想要的元素集合
list.stream()
.filter(str -> str.contains("1"))
.filter(str -> str.length() <=6)
.forEach(str -> System.out.println(str))
}
}
一般我们把流式思想称之为"生产流水线"
java 函数接口 自定义函数接口 函数式编程 Stream流
原文:https://www.cnblogs.com/shangjinshuai/p/14160844.html