Lambda表达式
- 匿名内部类可以省去定义实现类,但是语法太复杂
- lambda表达式不关注面向对象,返璞归真,仅仅关注内容本身。
1
2
|
//一些参数,一个箭头,一段重写代码
() -> System.out.println("lambda");
|
- 将Runnable接口作为参数进行传递,可以使用两种方法:

- 使用lambda必须满足接口中有且仅有一个抽象方法
- 有且仅有一个抽象方法的接口称为 “函数式接口”
- Lambda的省略写法有如下要求:
- 括号中参数列表的数据类型可以省略
- 括号中的参数只有一个,那么类型和()都可以省略
- 如果{}的代码只有一行,都可以省略{} return 分号,且必须要一起省略,不能只省略部分
FIle类
- 文件和目录 的抽象表示,用于文件&目录的创建、查找和删除等操作
1
2
3
4
5
6
7
8
|
//File类的4个成员变量
//路径分隔符(windows分号,linux冒号)
static String pathSeparator;
static char pathSeparatorChar;
//默认名称分隔符(windows反斜杠,linux正斜杠)
static String separator;
static char separator;
//注意:操作路径的时候,路径不能写死了,根据不同系统更换
|
1
2
3
4
5
6
7
8
9
10
11
12
13
|
/**
File(String pathname);
File(String parent,String child);
File(File parent,String child)
路径可以是文件名,也可以是文件夹名
可以相对路径,也可以绝对路径
路径可以是存在的,也可以是不存在的
创建File对象,只是把字符串封装为File对象,不考虑路径的真假情况
**/
public static void main(){
File f1 = new File("/home/jack1024/桌面/a.txt");
File f2 = new File("/home/jack1024/桌面/a");
}
|
1
2
3
4
|
public String getAbsolutePath();//文件/目录绝对路径名字符串(无论构造方法中传递相对还是绝对,都返回绝对路径)
public String getPath();//文件/目录路径名字符串(构造方法中传递的字符串)
public String getName();//文件/目录名字
public long length();//文件字节大小(目录没有大小概念)
|
1
2
3
|
public boolean exists();//该文件/目录是否存在
public boolean isDirectory();//是否为目录
public boolean isFile();//是否为文件
|
1
2
3
4
|
public boolean createNewFile();//当该名字的文件不存在时,创建一个新的空文件
public boolean delete();//删除由此File表示的文件或目录,不走回收站
public boolean mkdir();//创建由此File表示的目录
public boolean mkdirs();//创建由此File表示的目录,包括任何必须但不存在的父目录
|
1
2
|
public String[] list();//返回一个String数组,表示该File目录中所有子文件或目录
public File[] listFiles();//返回一个File数组,表示该File目录中所有子文件或目录
|
递归
- 直接递归 & 间接递归
- 递归一定要有停止的条件约束,否则会出现栈内存溢出StackOverFlowError
- 递归的次数也不能太多,否则也可能发生栈内存溢出
- 构造方法中禁止递归
递归图示:

文件过滤器
1
2
|
public File[] listFiles(FileFilter filter);
public File[] listFiles(FilenameFilter filter);
|
通过重写accept()方法来实现
IO流

字节流
字节输出流
- OutputStream:所有输出字节流的超类,接下来主要探讨其子类FileOutputStream:
- close():释放资源
- flush():刷新此输出流并强制写出所有缓冲的输出字节
- void write(byte[] b):往文件写入多个字节
- void write(int b):往文件写入一个字节
- void write(byte[] b,int off,int len):往文件写入指定位置指定长度的放入的数组中的字节
- FileOutputStream构造方法:
- FileOutputStream(File file)
- FileOutputStream(String name)
- FileOutputStream(File file, boolean append)
- FileOutputStream(String name, boolean append)
字节输入流
- InputStream:表示字节输入流的所有子类超类。接下来主要探讨其子类FileInputStream:
- close():释放资源
- int read(byte[] b):从文件读取一定数量的字节,并将其存在缓冲区数组b中
- int read():从文件读取一个字节
- FileInputStream构造方法:
- FileInputStream(File file)
- FileInputStream(String name)
1
2
3
4
5
6
7
8
9
|
int len = 0;
while((len=fis.read())!=-1){
System.out.println((char)len);
}
byte[] bytes = new byte[1024];
while((len = bis.read(bytes)) != -1){
bos.write(bytes,0,len);
}
|
字符流
字符输入流
-
Reader:是一个抽象类
- int read()
- int read(char[] cbuf)
- void close()
-
FileReader:文件字符输入流
构造方法:
- FileReader(File file)
- FileReader(String name)
字符输出流
1
2
3
4
5
6
7
8
9
|
try{
//可能产生异常的代码
}
catch(异常类参数对象){
//异常的处理逻辑
}
finally{
//资源释放
}
|
1
2
3
4
5
6
|
try(定义流对象){//流对象使用完毕自动释放
//可能产生异常的代码
}
catch(){
//异常的处理逻辑
}
|
属性集Properties
- 唯一一个和io流结合的集合
- store:集合中数据持久化到硬盘存储
- load:硬盘中的键值对读取到集合使用
- Properties是一个双列集合,key和value都默认是字符串
- setProperty方法相当于map中的put方法
- getProperty方法相当于map中的set方法
- stringPropertyNames方法相当于map中的keySet方法
1
2
3
4
5
6
7
8
9
10
11
12
13
|
FileWriter fw = new FileWriter("/home/jack1024/a.txt");
Proterties prop = new Properties();
prop.setProperty("aa","11");
prop.setProperty("bb","22");
prop.store(fw,"save data");
fw.close;
prop.load(fw);
Set<String> set = prop.stringPropertyNames();
for(String key : set){
String value = prop.getProperty(key+"="+value);
System.out.println(key);
}
|
- properties文件中可以使用等号或者空格来分隔键值对
- 对中文进行处理的时候必须使用字符输入/出流
缓冲流
-
增强基本流(普通流都是一个一个地传,效率低下)
-
给基本字节输入输出流增加一个缓冲区,提高传输速率
-
BufferedInputStream(InputStream fis)
BufferedInputStream(InputStream fis, int size)
-
BufferedOutputStream(OutputStream fos)
BufferedOutputStream(OutputStream fos, int size)
-
BufferedWriter(Writer fw)
BufferedWriter(Writer fw, int size)
-
BufferedReader(Reader fr)
BufferedReader(Reader fr, int size)
-
写和读的方法和基本流一致
- BufferedWriter有一个特有的成员方法void newLine:写入一个行分隔符(不同操作系统统一一个方法)
- BufferReader有一个特有的成员方法String readLine:读取一行数据(不同操作系统统一一个方法)
测试复制文件的效率:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
public class Test {
public static void main(String[] args) throws IOException {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入需要复制的文件路径:");
String sourcePath = scanner.next();
System.out.println("请输入粘贴的路径:");
String targetPath = scanner.next();
long t1 = System.currentTimeMillis();
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(sourcePath));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(targetPath));
int len = 0;
while((len = bis.read()) != -1){
bos.write(len);
}
// byte[] bytes = new byte[1024];
// while((len = bis.read(bytes)) != -1){
// bos.write(bytes,0,len);
// }
bis.close();
bos.close();
long t2 = System.currentTimeMillis();
long sumT = t2-t1;
System.out.println("复制过程总共耗时:"+sumT+"毫秒");
}
|
转换流
- 编码:字符——>字节
- 解码:字节——>字符
- 制定了编码,字节文件对应的字符集也就确定了,每个软件也就能根据软件功能转化字节为对应的字符供我们查看了
- 常见的字符集有ASCII字符集,GBK字符集,Unicode字符集
- FileReader只能读取系统默认编码utf-8格式的文件,如果读取GBK格式的文件,就会产生乱码
- 引入转换流来解决:InputStreamReader和OutputStreamWriter是字节流和字符流的桥梁,可以指定编码表

-
InputStreamReader(InputStream fis) //默认编码
InputStreamReader(InputStream fis, String charsetName) //指定编码
-
OutputStreamWriter(OutputStream fos)
OutputStreamWriter(OutputStream fos, String charsetName)
1
2
3
4
5
6
7
8
9
10
|
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("/home/jack1024/a.txt","GBK"));
osw.write("你好");
osw.close();
InputStreamReader isr = new InputStreamReader(new FileInputStream("/home/jack1024/a.txt","GBK"));
int len = 0;
while((len = isr.read())!=-1){
System.out.print((char)len);
}
isr.close();
|
序列化 & 反序列化流
把对象以字节的形式写入到文件中存储 并 从文件中读取对象到内存中继续使用,即是对象持久化
-
ObjectOutputStream(OutputStream os):对象的序列化流
- void writeObject(Object obj)
-
ObjectInputStream(IutputStream is):对象的反序列化流
-
要对对象进行序列化 or 反序列化,必须在类中实现Serializable接口
-
序列化的内容都是对象的内容,所以static的变量不能被序列化。如果想让一个成员变量不被序列化但是又没有static的含义,可以使用transient关键字声明变量
-
如果一个类实现了Serializable接口,编译后的.class文件中会带有一个序列号。序列化时,会将这个序号一同写入文件;当反序列化时,会将文件的序列号和.class文件中的序列号作对比,如果不同会抛InvalidClassException异常。(每次对类修改后都会重新生成一个序列号,为了避免异常,我们可以手动给Serializable实现类加上一个序列号)
1
|
static final long serivalVersionUID = 42L;
|
打印流
- PrintStream:为其他输出流添加功能,使他们能方便打印各种数据值的表示形式
- print()
- pringln()
- 继承自OutputStream的方法(如果使用write方法会查询编码表97->a;如果使用println or print方法会原样输出)
- 构造方法:
- PrintStream(File file)
- PrintStream(OutputStream out)
- PrintStream(String fileName)
1
2
3
4
|
PrintStream ps = new PrintStream("/home/jack1024/a.txt");
ps.write(97);//转化为字节
ps.println(97);//原样打印到文件
ps.close;
|
1
2
|
System.setOut(ps);//改变输出语句的目的地为ps的目的地
System.out.println("haha");//会将haha字符串写到/home/jack1024/a.txt中
|
Java网络编程
TCP/IP协议是Internet中最基本,最广泛的协议,他定义了计算机计算机如何进因特网,数据如何在计算机间传输数据。它的内部包含一系列用于处理数据通信的协议
- UDP协议:无连接,效率更高,不安全
- TCP协议:三次握手建立连接,四次挥手解除连接,效率较低,安全
网络编程三要素:
- 协议
- IP地址:ipv4(32位:84),ipv6(128位:168),连接两台主机
- 端口:两个字节(0—1024—65535),连接两台主机的两个进程
TCP通信程序
- 客户端主动请求服务端建立连接,连接中包含一个IO对象,这个对象只能是字节流对象

代码实现:
客户端

服务器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
//客户端
public class TcpClient {
public static void main(String[] args) throws IOException {
Socket socket = new Socket("127.0.0.1", 8888);
//往服务器写
OutputStream os = socket.getOutputStream();
os.write("你好".getBytes());
//从服务端拿
InputStream is = socket.getInputStream();
byte[] bytes = new byte[1024];
int len = is.read(bytes);
System.out.println(new String(bytes,0,len));
socket.close();
}
}
//服务端
public class TcpServer {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(8888);
Socket accept = serverSocket.accept();
InputStream is = accept.getInputStream();
OutputStream os = accept.getOutputStream();
byte[] bytes = new byte[1024];
int len = is.read(bytes);
System.out.println(new String(bytes,0,len));
os.write("收到了".getBytes());
accept.close();
serverSocket.close();
}
}
|
TCP文件上传案例

模拟B/S通信案例
函数式编程
- 函数式接口:有且仅有一个抽象方法的接口,但是可以包含其他方法(默认,静态,私有)
1
2
3
4
5
6
|
@FunctionalInterface
修饰符 interface 接口名称{
public abstract 返回值类型 方法名称(可选参数信息){
}
}
|
- lambda是延迟加载的,可以提升部分代码的性能。也就是说只有满足执行lambda中方法的条件,才会执行方法中的内容,否则不会执行。
- 函数式接口作为方法的参数,我们可以用lambda表达式 或者 匿名内部类 作为实参
- 函数式接口作为方法的返回值,我们可以返回这个接口的匿名内部类 或者 lambda表达式
常用的函数式接口
1
2
3
4
5
6
7
8
9
10
|
//java.util.funciton.Supplier<T>
public static getString(Supplier<String> sup){
return sup.get();//用来返回一个指定类型的数据对象
}
public static void main(String[] args){
String s = getString(()->{
return "Sam Smith";
});
}
|
1
2
3
4
5
6
7
8
9
10
11
12
|
//java.util.function.Consumer<T>
public static void method(String name,Consumer<String> con){
con.accept(name);//用来消费name字符串
}
public static void main(String[] args){
method("Mariah Carey",(String name)->{
//对传递的字符串进行处理消费
System.out.println(name);
})
}
//andThen()默认方法,连续组合多个Consumer的accept方法
|
1
2
3
4
5
6
7
8
9
10
11
12
13
|
//java.util.function.Predicate<T>
public static boolean checkString(String s,Predicate<String> pre){
return pre.test(s);//对传入的参数进行判断,返回真假
}
public static void main(String[] args){
String s = "abcde";
boolean b = checkString(s,()->{
return s.length()>5;
});
}
//Predicate的and(),or()默认方法可以用来连接两个判断条件
//negate()默认方法可以对一个判断条件取反
|
1
2
3
4
5
6
7
8
9
10
11
12
|
//java.util.function.Function<T,R>
public static void change(String s,Function<String,Integer> fun){
Integer in = fun.apply(s);
}
public staic void main(String[] args){
String s = "123";
change(s,(String s)->{//将一个值从原类型转化为另一个类型
return Ingeter.parseInt(s);
});
}
//andThen()默认方法,连续组合多个Function的apply方法
|
Stream流式编程
- Stream和io流 不能说差不多,只能说是毫无关系
- 极大地简化了对集合进行过滤 & 遍历的操作
1
2
3
4
|
list.stream()
.filter(name->name.startWith("杨"))
.filter(name->name.length==3)
.foreach(name->System.out.println(name));
|
- 流式操作过程:
- 获取数据源(集合,数组)
- 数据转换(把数据源转化为Stream流)
- 执行操作获取想要的结果
获得Stream流对象
- 所有collection集合可通过stream()方法获得
- Stream接口有一个static方法of(),可以把数组转化为stream流
Stream对象的方法
- stream属于管道流,只能使用一次,第一个Stream流调用完毕,数据会流到下一个Stream流身上,此时第一个Stream流就不能再调用方法了
延迟方法
- 返回值类型仍然是Stream接口自身类型的方法,支持链式调用
- 每个流 流到下一步,上一步的流就关闭了,这个要注意,这也是为什么叫流,它是一个动态的过程
filter方法
1
2
3
4
5
|
//Stream<T> filter(Predicate<? super T> predicate)
Stream<String> stream = Stream.of("Jack","Sam","Tom");
Stream<String> stream2 = stream.filter(name->{
reuturn name.startWith("J");
});
|
map方法
1
2
3
4
5
|
//Stream<R> map(Function<? super T, ? extends R> mapper)
Stream<String> stream = Stream.of("1","2","3");
Stream<Integer> stream2 = stream.map(String s->{
reuturn Integer.parse(s);
});
|
limit方法
1
2
3
4
|
//Stream<T> limit(long maxSize)
Stream<String> stream = Stream.of("哈哈","嘿嘿","咚咚");
//只取前两个元素
stream.limit(2).forEach(name->System.out.println(name));
|
skip方法
1
2
3
4
|
//Stream<T> skip(long n):跳过前n个元素
Stream<String> stream = Stream.of("哈哈","嘿嘿","咚咚");
//只取前两个元素
stream.skip(2).forEach(name->System.out.println(name));
|
concat方法
1
2
3
4
5
6
|
//static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b)
//两个流合并为一个新的流
Stream<String> stream1 = Stream.of("1","2","3");
Stream<String> stream2 = Stream.of("a","b","c");
Stream<String> concat = Stream.concat(stream1,stream2);
concat.forEach(name->System.out.println(name));
|
终结方法
- 返回值类型不再是Stream接口自身类型的方法,不再支持链式调用
count方法
1
2
3
4
5
6
7
8
9
|
//long count():统计个数
ArrayList<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
Stream<Ingeter> stream = list.stream();
long count = stream.count();
System.out.println(count);
|
forEach方法
1
2
3
|
//void forEach(Consumer<? super T> con)
Stream<String> stream = Stream.of("Jack","Sam","Tom");
stream.forEach(name->System.out.println(name));
|
方法引用
- 虽然lambda已经很简单了,但是方法引用还可以堆lambda表达式继续简化
1
2
|
name->System.out.println(name);//lambda表达式
System.out::println;//方法引用
|
几种情况:
- 通过对象名引用成员方法
- 通过类名引用静态方法
- 通过super引用父类成员方法
- 通过this引用本类成员方法
- 构造方法的方法引用
- 数组的构造器引用
Junit单元测试
- 黑盒测试:不需要关注代码,只需要看输入是否能得到输出
- 白盒测试:通过代码来测试程序的流程正确性(Junit属于白盒测试)
1
2
3
4
5
6
7
8
|
//定义测试类
/**几个建议
1.类名:被测试类名Test
2.包名:XXX.XXX.test
3.方法名:test被测试的方法名
4.返回值:void
5.参数列表:空参
**/
|
1
2
3
4
5
6
7
|
//之前需要导入junit依赖
public CaculateTest{
public void testAdd(){
int result = 1+2;
Assert.assertEquals(3,result)
}
}
|
@Before和@After注解
1
2
3
4
5
6
7
8
9
|
//所有测试方法前和后都会执行的两个注解@Before和@After
@Before
public void init(){
}
@After
public void close(){
}
|
反射
查看之前写的一片博客《java-反射》
注解
查看之前写的一片博客《java-注解》