IO流
一、什么是IO流
IO流就是以流的方式进行输入输出
就是存储和读取数据的解决方案
二、IO流体系
字节流:
InputStream
OutputStream
字符流
Reader
Writer
这四个类都是抽象类,我们需要学习他们的子类
字节流的子类:
文件操作:
FileOutputStream的示例:
1.创建对象 2.写出数据 3.释放资源 FileOutputStream fos = new FileOutputStream("a.txt");
try {
fos.write("123123".getBytes());
} catch (IOException e) {
throw new RuntimeException(e);
}
在构造函数中也可以指定续写为开启
FileOutputStream fos = new FileOutputStream("a.txt", true);
细节:
1.参数是字符表示的路径或者File对象
2.文件如果不存在会创建一个新的文件,但要保证父级路径存在
3.如果文件已经存在,则会覆盖文件,而不是追加
4.输出的内容是字节,ASCII码,
5.如果不释放,则在程序运行时会一直占用资源。
6.在java中\n或者\r都可以表示换行,但是Windows系统原生换行符为\r\n,Linux为\n,macOS为\r
FileInputStream的示例:
1.创建对象 2.读取数据 3.释放资源 FileInputStream fis = new FileInputStream("a.txt");
int rd = fis.read();
System.out.println(rd);
细节:
1.如果文件不存在,就直接报错。
2.read方法一次读取一个字节,ASCII码,如果文件结束,返回-1。
3.一般在拷贝大文件时,我们会创建一个5-10MB的数组(1024 * 1024 * 5)
文件拷贝示例:
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("a.txt");
FileOutputStream fos = new FileOutputStream("b.txt");
byte[] array = new byte[1024 * 1024 * 5];
int b = 0;
while ((b = fis.read(array)) != -1) {
fos.write(array,0,b);
System.out.println(Arrays.toString(array));
}
fis.close();
fos.close();
}
}
JDK7方案
public static void main(String[] args) {
try(FileInputStream fis = new FileInputStream("a.txt");
FileOutputStream fos = new FileOutputStream("b.txt")){
int len;
byte[] bytes = new byte[1024 * 1024 * 5];
while ((len = fis.read(bytes)) != -1){
fos.write(bytes, 0, len);
}
}catch (IOException e){
e.printStackTrace();
}
}
JDK9方案
public static void main(String[] args) throws FileNotFoundException {
public static void main(String[] args) {
FileInputStream fis = new FileInputStream("a.txt");
FileOutputStream fos = new FileOutputStream("b.txt");
try(fis;fos){
int len;
byte[] bytes = new byte[1024 * 1024 * 5];
while ((len = fis.read(bytes)) != -1){
fos.write(bytes, 0, len);
}
}catch (IOException e){
e.printStackTrace();
}
}
}
字符集&编码规则
乱码:看到的数据与原始数据不一致,一般是编码格式不同导致的
Unicode
字符流的子类
为了解决不同编码格式的问题,所以有了字符流的接口,其下有抽象类:Reader和Writer
一、FileReader
可以从纯文本文件中读取数据
细节:
一、在读取之后,方法的底层还会进行解码并转换为10进制。
二、这个10进制会作为返回值,同时表示在字符集上的数字。
三、所以如果强转成char类型,则不能正常显示中文!
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("a.txt");
char[] rd = new char[10];
int len = fr.read(rd);
System.out.println(new String(rd,0,len));
}
底层原理:
一、在创建对象的同时,在底层会创建一个8192字节的字节数组,作为缓冲区。
二、在第一次调用read的无参方法时,会先从文件读取到缓冲区,之后如果数据在缓冲区中,则从缓冲区读取,否则重新填充缓冲区,如果文件已经读取完毕,则返回-1。在字节流中没有缓冲区
三、缓冲区只会更新前部分,后面的数据不会被覆盖。
四、如果在缓冲区读取完成之前文件被清空,还能继续读取缓冲区内的数据,但是在缓冲区读取完之后,会返回-1。
二、FileWriter
重点:在创建FileWriter的对象,如果不指定append=true,那么文件会被直接清空
底层原理:
一、在创建完对象之后,就会在底层创建一个长度为8192的字节数组
二、在执行write方法时,数据会先存储到缓冲区,有三种情况下数据会被写入磁盘
①缓冲区满。
②手动调用flush方法,刷新缓冲区。
③调用close方法,释放资源。
我们已经学完了IO流的四种基本流,接下来开始学习高级流
(将基本流封装,添加新功能)
缓冲流
一、字节缓冲流(Buffered-)
原理:在底层自带了长度为8192的缓冲区来提高性能
1.构造方法
利用字节缓冲流来拷贝文件
public static void main(String[] args) throws IOException {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("a.txt"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("b.txt"));
//循环读取,并写入
int b;
while ((b = bis.read()) != -1) {
bos.write(b);
}
bis.close();
bos.close();
}
二、字符缓冲流
相比起字符流,两者都有缓冲区,但是字符缓冲流的方法更加好用。
1.构造方法
2.特有的方法
利用字符缓冲流来拷贝文件
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new FileReader("a.txt"));
BufferedWriter bw = new BufferedWriter(new FileWriter("b.txt"));
String line;
while ((line = br.readLine()) != null) {
bw.write(line);
bw.newLine();
}
br.close();
bw.close();
}
转换流
使用转换流就可以在字节流的基础上使用字符流中的方法
序列化流
一、作用:
序列化流可以把Java中的对象写到本地文件中
反序列化流可以将文件中的数据重新读取为对象
二、方法
注意:在使用自定义对象时需要实现Serializable接口
这个接口没有抽象方法,只是作为标记型接口,一旦实现这个接口,就表示可以被序列化
public static void main(String[] args) throws IOException, ClassNotFoundException {
Student stu = new Student("张三",18);
Student stu1 = new Student("李四",19);
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("a.txt"));
oos.writeObject(stu);
oos.writeObject(stu1);
oos.close();
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("a.txt"));
Student stu2 = (Student)ois.readObject();
System.out.println(stu2);
Student stu3 = (Student)ois.readObject();
System.out.println(stu3);
}
细节:
一、如果JavaBean类被修改,那么先前存储的数据就不匹配了。会无法读入对象而报错,此时可以通过手动指定序列号解决
private static final long serialVersionUID = xxxxxL;
二、如果想要一个属性不被序列化到文件中,可以使用transient(瞬态关键字)修饰变量。
import java.io.Serial;
import java.io.Serializable;
public class Student implements Serializable {
@Serial
private static final long serialVersionUID = -770572251124423830L;
private String name;
private int age;
private transient String address;
public Student() {
}
public Student(String name, int age, String address) {
this.name = name;
this.age = age;
this.address = address;
}
/**
* 获取 * @return name
*/ public String getName() {
return name;
}
/**
* 设置 * @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取 * @return age
*/ public int getAge() {
return age;
}
/**
* 设置 * @param age
*/
public void setAge(int age) {
this.age = age;
}
/**
* 获取 * @return address
*/ public String getAddress() {
return address;
}
/**
* 设置 * @param address
*/
public void setAddress(String address) {
this.address = address;
}
public String toString() {
return "Student{name = " + name + ", age = " + age + ", address = " + address + "}";
}
}
打印流
打印流不能读只能写
一、方法
字节打印流
字符打印流
总结:
压缩流
通过例子了解ZipEntry
:
如下:
new ZipEntry(name):`这个`name`是什么,解压后的文件结构就是什么 我有一个文件是:`D:\CaptureTest\filepath\1.jpg`, 而`name = "Image\01.jpg"` 生成压缩文件后,再解压的文件结构就是`"Image\01.jpg"
/**
* 压缩指定路径的文件
*/
public static void fileToZip(String srcFile, String zipFile) throws IOException {
File file = new File(srcFile);
//取出文件名
String name = file.getName();
//读取文件
FileInputStream inputStream = new FileInputStream(file);
//输出流
ZipOutputStream zipOutputStream = new ZipOutputStream(new FileOutputStream(zipFile));
//ZipEnter:表示压缩文件的条目(文件目录)
zipOutputStream.putNextEntry(new ZipEntry("Image\\01.jpg"));
int temp = 0;
while ((temp = inputStream.read()) != -1) {
zipOutputStream.write(temp);
}
zipOutputStream.close();
inputStream.close();
}
public static void main(String[] args) {
try {
fileToZip("D:\CaptureTest\filepath\1.jpg", "D:\\CaptureTest\\123.zip");
} catch (IOException e) {
e.printStackTrace();
}
}
1234567891011121314151617181920212223242526272829
用解压软件打开123.zip
:路径就是:\Image\01.jpg
将文件夹添加到压缩文件案例
public class ZIPOutputStreamDemo {
public static void main(String[] args) throws IOException {
//1.创建File对象表示要压缩的文件夹
File src = new File("D:\\test");
//2.创建File对象表示压缩包的存放目录、
File destParent = src.getParentFile();
//3.创建File对象表示压缩包的路径
File dest = new File(destParent,src.getName() + ".zip");
//4.创建压缩流关联压缩包
ZipOutputStream zos = new ZipOutputStream((new FileOutputStream(dest)));
//5.获取src里的每一个文件,编程ZipEntry对象,放入压缩包中
toZip(src, zos);
//6.释放资源
zos.close();
}
/*
* 将一个目录添加到压缩包 * 参数一、数据源 * 参数二、压缩流 * 参数三、内部路径 * */ public static void toZip(File src,ZipOutputStream zos) throws IOException {
String name = src.getName();
File[] files = src.listFiles();
for (File file : files) {
if (file.isDirectory()){
toZip(file,zos);
}else{
//判断文件,转为ZipEntry对象
ZipEntry fileEntry = new ZipEntry(name + "\\" + file.getName());
zos.putNextEntry(fileEntry);
//读取文件中的数据,写入到压缩包
FileInputStream fis = new FileInputStream(file);
int b;
while((b = fis.read()) != -1){
zos.write(b);
}
fis.close();
zos.closeEntry();
}
}
}
}