Day15
Lambda表达式
使用Lambda表达式可以更专注于逻辑代码,不必再赘述实现的接口与实现的方法
去除方法名、返回值、修饰符、参数类型
注:若大括号去除,则return关键字也要去掉
表达式格式
(参数列表) -> {
方法体
}
示例
//匿名内部类写法
Comparator<String> comparator = new Comparator<String>(){
@Override
public int compare(String o1, String o2) {
return o1.length() - o2.length();
}
};
//Lambda写法
Comparator<String> comparator1 = (String o1, String o2) -> {
return o1.length() - o2.length();
};
//Lambda简化写法
Comparator<String> comparator2 = (o1, o2) -> o1.length()-o2.length();
File
java.io.File
File的每个实例用于表示硬盘上的一个文件或目录(实际仅保存一个抽象路径)
File的作用
- 访问文件或目录的属性(名字、大小、权限)
- 创建/删除文件或目录
- 访问一个目录中的子项
注:File无法对文件进行读写
常用方法
创建File对象
./表示当前项目目录
File file = new File("./demo.txt");//使用相对路径
获取文件名称
String name = file.getName();
System.out.println("文件名:" + name);
获取文件大小(以字节为单位)
long length = file.length();
System.out.println("文件大小:" + length + " 字节");
查看文件权限
boolean read = file.canRead();
boolean write = file.canWrite();
boolean execute = file.canExecute();
System.out.println("文件权限:" + (execute ? "可执行" : "不可执行"));
System.out.println("文件权限:" + (read ? "可读" : "不可读") + "," + (write ? "可写" : "不可写"));
查看文件是否隐藏
查看文件是否隐藏
注意:Windows系统中隐藏文件是以"."开头的, 而在Linux系统中隐藏文件则没有这个前缀
boolean hidden = file.isHidden();
System.out.println("文件是否隐藏:" + (hidden ? "是" : "否"));
查看文件是否已存在
boolean existed = file.exists();
创建删除文件
// 创建10000个文件
for (int i = 1; i <= 10000; i++) {
File file = new File("test" + i + ".txt");
if (file.exists()) {
System.out.println("text" + i + ".txt文件存在");
} else {
file.createNewFile();
System.out.println("已创建test" + i + ".txt");
}
}
// 删除文件
for (int i = 1; i <= 10000; i++) {
File file = new File("test" + i + ".txt");
if (file.exists()){
file.delete();
System.out.println("已删除test" + i + ".txt");
} else {
System.out.println("test" + i + ".txt文件不存在");
}
}
创建文件目录
- 创建单级目录
File dir = new File("./testDir1/testDir2");//若创建的目录路径不存在时不会创建
if (dir.exists()){
System.out.println("目录已存在");
}else {
dir.mkdir();//创建单级目录
System.out.println("目录创建成功");
}
- 创建多级目录
File dir = new File("./testDir1/testDir2");//可以同时创建多级目录
if (dir.exists()){
System.out.println("目录已存在");
}else {
dir.mkdirs();//创建多级目录
System.out.println("目录创建成功");
}
删除单级目录
File dir = new File("./testDir1/testDir2");//多级目录时,只删除最内层的目录
if (dir.exists()){
dir.delete();
System.out.println("目录已删除");
}else {
System.out.println("目录不存在");
}
获取目录子项
-
判断当前File表示的是否为一个实际存在的文件
boolean isFile()
-
判断当前File表示的是否为一个实际存在的目录
boolean isDirectory()
-
获取指定目录下的下一层的所有目录
File dir = new File(".");//.表示获取项目根目录下的所有文件目录 if (dir.isDirectory()){ File[] subs = dir.listFiles();//获取到的目录存入File[]数组中 for (File sub : subs) { System.out.println(sub.getName()); } }
-
文件过滤器
重载listFiles方法,传入一个文件过滤器参数(需要自定义规则)
File[] listFiles(FileFilter filter)
示例:获取指定目录下以.开头的文件和目录
File dir = new File("."); FileFilter fileFilter = (file -> file.getName().startsWith("."));//过滤目录中以.开头的目录文件 for (File d : dir.listFiles(fileFilter)) { System.out.println(d.getName()); }
Day16
IO流
流的概念
流是单向的
读操作和写操作是参照计算机而言的
Java中的输入输出流
- 所有字节输入流的超类 java.io.InputStream
- 所有字节输出流的超类 java.io.OutputStream
文件输出流
java.io.FileOutputStream,继承自OutputStream
从程序(内存中)输出到文件(硬盘中)
- 文件输出流会创建一个输出文件的对象,并使用OutputStream的相关方法来对文件进行输出操作。
- 文件输出流是连接在程序与文件之间的管道,负责将程序中的字节数据输送到文件中。
文件输出流常用的构造器:
-
FileOutputStream(File file):创建文件输出流连接到File表示的文件上
FileOutputStream fos = new FileOutputStream("./fos.dat");
-
FileOutputStream(String name):创建文件输出流连接到name路径指定的文件上
文件可以不存在,但是目录路径必须存在,否则创建失败
File file = new File("./fos.dat"); FileOutputStream fileOutputStream = new FileOutputStream(file);
单字节写入操作
重复执行以下代码不会继续添加文本,而是直接覆盖原内容
FileOutputStream fos = new FileOutputStream("./fos.dat");
fos.write(1);
fos.write(2);
fos.close();//文件输出流执行完后一定要关闭资源
块写操作
方法格式
- data:这是一个字节数组(byte[]),包含了要写入文件的数据。
- off:这是data数组中的起始偏移量,表示从数组的哪个位置开始读取数据。
- len:这是要写入的字节数。它指定了从data数组的起始偏移量开始,要写入的字节数量。
void write(byte b[], int off, int len) throws IOException
示例
fileOutputStream.write(data, 0, len) 方法将从data数组的起始位置(偏移量为0)开始,连续读取len个字节,并将这些字节写入到fileOutputStream所代表的文件中。
FileInputStream fileInputStream = new FileInputStream("bingimg_20220821_UHD.jpg");
FileOutputStream fileOutputStream = new FileOutputStream("bingimg_20220821_UHD_copy_001.jpeg");
byte[] data = new byte[1024 * 10];
int len = 0;
while ((len = fileInputStream.read(data)) != -1){
fileOutputStream.write(data,0,len);
}
System.out.println("复制成功");
fileInputStream.close();
fileOutputStream.close();
文件输入流
从文件(硬盘中)输入到程序(内存中)
速度慢,但是没有数据偏差,没有脏数据
- 从文件头逐个字符读取并返回读到的字符
- 当到达文件末尾后返回值变为-1,此时若继续往后读返回值不变仍为-1
单字节读取操作
int d = fileInputStream.read();
循环多个读取
- 循环读取方式一
int d = fileInputStream.read();
System.out.print((char) d);
while (d != -1){
d = fileInputStream.read();
if (d != -1){
System.out.print((char) d);
}
}
- 循环读取方式二
while (true) {
int d = fileInputStream.read();
if (d == -1) {
break;
}
System.out.print((char) d);
}
- 循环读取方式三
int d = 0;
while ((d = fileInputStream.read())!= -1){
System.out.print((char) d);
}
块读操作
超类java.io.InputStream上定义了块读字节的方法
可能会出现脏数据
- 从流中最多读取b.length个字节数据并存入数组b中(以文件实际大小为准)
- 返回值为实际读取到的字节数
- 若返回值为-1则表示流读取到了末尾
方法格式
返回值为本次读取字节个数,返回-1时表示到达文件末尾
int read(byte b[]) throws IOException
示例
//Create a new File object for the song.txt file
File file = new File("./song.txt");
//Create a byte array to store the data from the file
byte[] data = new byte[(int) file.length()];
//Create a FileInputStream object to read the file
FileInputStream fileInputStream = new FileInputStream(file);
//Read the data from the file into the byte array
fileInputStream.read(data);
//Create a String object from the byte array using UTF-8 encoding
String msg = new String(data, StandardCharsets.UTF_8);
//Print the message
System.out.println("msg = " + msg);
//Close the FileInputStream
fileInputStream.close();
字节流
节点流和处理流
节点流(低级流)
- 数据源明确,可以从或向一个特定的地方(节点)读或写数据
- 真实负责搬运数据的流
- 读写一定是建立在节点流的基础上进行的
处理流(高级流)
- 不独立存在,无意义
- 必须对其他流进行处理,处理的既可以是节点流也可以是其他处理流
- 使用处理流可以简化我们对读写数据的加工处理,简化读写操作
流连接
开发中常连接一组高级流到某个低级流上,使得读写数据以流水线式的加工完成被成为是流的连接
缓冲流
在流连接中通常直接连接在低级流上,缓冲流在读写时可以保证读写效率
注:文件输入流和输出流要和缓冲输入流和输出流一一对应,否则无法使用
原理
输入流BufferedInputStream从硬盘中取数据时会取出很多块数据到内存中,当cpu向内存索取时,BufferedInputStream可以很快反应给cpu所需的数据,直到存储的数据被cpu取完之后再去硬盘中取
输出流BufferedOutputStream亦然
两种构造器
- 实例化一个缓冲字节输出流并链接在指定的字节输出流上,默认缓冲区大小为8kb(内部维护的byte[] 数组长度为8192)
BufferedInputStream(InputStream in)
- 实例化一个指定缓冲区大小的缓冲字节输出流并链接在只当的字节输出流上
BufferedInputStream(InputStream in, int size)
冲洗操作
flush方法存在于OutputStream父类中,所以所有的输出流中均有flush方法
- 缓冲流中flush作用:强制向硬盘中存入数据
- 其他输出流中flush作用:调用下层流的flush,即传递flush
原理:bufferedOutputStream写入时会先存入内存byte[]中,当byte[]满时才会写入到文件,此时若没有flush,数据会随着程序关闭而丢失,不会写入到文件中
缓冲流flush注意:
-
bufferedOutputStream写完后要flush一下,不然数据不会写入文件,
-
关闭流close方法中会自动调用一次flush方法
public void close() throws IOException { try (OutputStream ostream = out) { flush(); } }
-
flush后数据会写入到文件中
-
flush后数据会从内存中清除
-
flush后数据会从内存中写入到磁盘中
方法格式
synchronized void flush() throws IOException
示例
// 创建文件输入流,并连接缓冲流
FileInputStream fileInputStream = new FileInputStream("image.jpeg");
BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream);
// 创建文件输出流,并连接缓冲流
FileOutputStream fileOutputStream = new FileOutputStream("image_copy3.jpeg");
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
// 开始复制,使用缓冲流读取数据,并写入到新文件中
int data = 0;
while ((data = bufferedInputStream.read())!= -1){
bufferedOutputStream.write(data);
}
System.out.println("复制完成");
bufferedOutputStream.flush();//冲洗数据
// 关闭流
bufferedInputStream.close();
bufferedOutputStream.close();
对象流
对象输出流:java.io.ObjectOutputStream
一个高级流,用于进行对象的序列化
序列化:将对象转换为字节序列的过程
反序列化:将字节序列恢复为对象的过程
应用场景:将对象写入文件,或者通过网络发送对象对象存在于内存中,当需要将对象存储在硬盘中时可以使用对象流进行序列化后进行流存储
注意:
- 只有实现了Serializable接口的对象才能被序列化
- 静态变量不会被序列化
- 序列化对象时,对象的所有 transient 成员变量都不会被序列化
- 序列化对象时,对象的所有 static 成员变量都不会被序列化
原理
将对象直接交给对象流的writeObject方法,对象流就可以将序列化后的对象存入文件
示例
// Declare a FileInputStream to read from a file named "person.obj"
FileInputStream fileInputStream = new FileInputStream("./person.obj");
// Declare an ObjectInputStream to read Java objects from the FileInputStream
ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
// Read the serialized object and deserialize it into a Java object
Person msg = (Person) objectInputStream.readObject();
// Print the Person object to the console
System.out.println("msg = " + msg.toString());
// Close the ObjectInputStream to release the resources it uses
objectInputStream.close();
在对象的属性前添加transient关键字,该对象在序列化时转换出来的字节中不包含该属性的值,可以缩小对象序列化的大小
private transient String[] otherInfo;
注:将对象写入文件后就不要在对该对象类进行修改操作了,否则在读取该文件中的对象时会报类失效异常InvalidClassException
Day17
字符流
只能用于读取文本数据,图片数据等不可读
以char为单位读写数据,一次处理一个uniode
字符流都是高级流,即无法独立工作,需要套在低级流上
字符流父类:
- java.io.Reader:所有字符输入流的超类
- java.io.Writer:所有字符输出流的超类
转换流
转换流的意义
- 字符流不能直接连接在字节流上
- 节点流通常为字节流
- 本质原因时字符流读写字符,字节流读写字节。读写单位不同
转换输出流
转换输出流可以将上层字符流写出的文本数据转换为字节,在通过其链接的字节流写出
转化流输出:链接到FileOutputStream上使用
OutputStreamWriter(FileOutputStream)
示例
String line = "Hello, World!哈哈哈哈哈哈";
FileOutputStream fileOutputStream = new FileOutputStream("osw.txt");
// 创建转换流将其链接到字符流上,并设置编码格式为UTF-8
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream, StandardCharsets.UTF_8);
//调用转换流写入方法
outputStreamWriter.write(line);
outputStreamWriter.write("嘻嘻嘻嘻嘻嘻嘻嘻嘻");
System.out.println("写入成功");
outputStreamWriter.close();
转换输入流
转换输入流可以从字节流读取相应的字节并转换为字符再交给上层字符流做进一步处理
在字符输入流超类中定义了读取一个字符的方法
int read()
读取一个字符,将char的2个字符放回到int值的低16为中,若int返回的值为-1,则说明到了文件末尾
示例
FileInputStream fileInputStream = new FileInputStream("./osw.txt");
//创建转换输入流对象,将文件字节输入流转换为字符流
InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream, StandardCharsets.UTF_8);
int d = 0;
while ((d = inputStreamReader.read()) != -1) {
System.out.print((char) d);
}
缓冲字符流PrintWriter
PrintWriter时具有自动行刷新功能的缓冲字符输出流
特点
- 缓冲字符流java.io.PrintWriter
- 实际开发中常用PrintWriter,因为它的内容总是链接着BufferWriter的缓冲区,所以效率很高。
- 具有自动行刷新功能
- 默认编码UTF-8
底层原理
PrintWriter构造
PrintWriter printWriter = new PrintWriter("pw.txt");
构造器源码
public PrintWriter(String fileName) throws FileNotFoundException {
this(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(fileName))),
false);
}
底层实现
//创建文件输出流
FileOutputStream fileOutputStream = new FileOutputStream("pw.txt");
//创建字符转换输出流并文件输出流链接,指定字符编码为UTF-8
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream, StandardCharsets.UTF_8);
//创建缓冲字符流并字符流链接
BufferedWriter bufferedWriter = new BufferedWriter(outputStreamWriter);
//创建PrintWriter字符流对象并缓冲流链接
PrintWriter printWriter1 = new PrintWriter(bufferedWriter);
构造方法:
PrintWriter(Writer out) //所有字符输出流的父类
PrintWriter(Writer out, boolean autoFlush)
PrintWriter(OutputStream out)
PrintWriter(OutputStream out, boolean autoFlush)
PrintWriter(OutputStream out, String encoding)
PrintWriter(OutputStream out, String encoding, boolean autoFlush)
写入字符串示例
//新建PrintWriter字符流对象,指定文件名和编码格式
PrintWriter printWriter = new PrintWriter("pw.txt", String.valueOf(StandardCharsets.UTF_8));
//调用PrintWriter的println方法按行写入字符串
printWriter.println("呵呵呵呵呵呵呵呵呵呵");
printWriter.println("惺惺惜惺惺嘻嘻嘻嘻嘻嘻嘻嘻");
System.out.println("写入成功");
printWriter.close();
写入字符串原理
扩展——UTF_8原理
/**
* 从文件中读取文本数据
*
* char c = 'a'; 00000000 01100001 char在java内部固定2字节.英文而言,前面补8个0
* char c = '向'; 10100110 01111110
*
* 但写入文件时,需要几个字节就写几个字节
* 01100001 10100110 01111110
* a |-----向--------|
*
*
* UTF-8 英文占1个字节,中文占3个字节
* 01100001 英文
*
* 10100110 01111110
* ^^^^---- --||||||
* 11101010 10011001 10111110
* ^^^^ ------ ||||||
*
* 文件内容:
* a向
* 文件内容2进制示意:
* 01100001 11101010 10011001 10111110
*/
BufferReader缓冲字符输入流
java.io.BufferReader缓冲字符输入流
内部维护一个8192长的char数组,总是以块读取文本数据保证读取效率
提供了按行读取字符串的操作
若某一行为空行,则返回为空字符串,而不是null
bufferReader.readLine();
Java异常处理机制
java中异常的父类为Throwable,下派生两个子类Exception、Error
- Exception:相对较轻,例如网络故障、文件孙环、输入非法等
- Error:严重异常,无法挽回,虚拟机直接停止,例如内存溢出
try-catch
- try—catch用于对其包裹的代码块进行异常捕获,并进行异常处理
- 捕获到异常之后会直接跳转到对应的catch块执行异常处理代码,原代码块剩余的代码不会再执行
- catch可以有多个,用于捕获一段代码块中不同的异常
- 前面的catch捕获到异常后,后面的catch就不会再执行了
System.out.println("开始");
try{
String str = null;
System.out.println("str.length() = " + str.length());
System.out.println("结束");
} catch (Exception e){ //捕获异常
System.out.println("捕获到异常");
}
System.out.println("结束");
开始
捕获到异常
结束
finally
- finally包裹的代码不管有没有获取到异常、不管是否return,都会执行
- finally用在catch捕获异常之后
- finally必须再异常捕获的最后一块
- 常用于释放资源,例如IO流释放资源
try{
String str = null;
String str2 = "abc";
System.out.println("str.length() = " + str2.length());
System.out.println("结束");
} catch (NullPointerException e) {
System.out.println("捕获到NullPointerException异常");
} finally {
System.out.println("finally");
}
System.out.println("结束");