转换流(在字节流中想使用字符流的方法时使用)
转换流是字节流和字符流之间的桥梁,转换流本身其实就是字符流所以可以使用字符流里的相关方法,通过InputStreamReader字符转换输入流能将字节流转化为字符流输入到内存中,而在内存中之后能使用OutputStreamWriter字符转换输出流将字符流转化为字节流输出,如下:
使用字符流对相应的文件进行相应的解码方式解码,例如一个GBK解码方式的文件数据,我想在idea中看到他的文件数据,但是idea是默认的采用UTF-8解码方式的,所以在idea中看到该文件的数据是一堆乱码,此时我就需要字符流中的FileReader 也就是字符输入流进行解码,格式如下:
FileReader fr = new FileReader("D:\\11Codingtext\\MyIO\\gbkfile.txt", Charset.forName("GBK"));
FileReader的解码用法
在创建FileReader的时候不仅可以输入需要读取的文件地址,还可以传入相关的解码方式,例如GBK,UTF-8,这样的话就能对该文件中的数据按照该解码方式进行读取到程序中,代码如下:
package com.itazhang.Demo1;import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.Charset;public class ConvertStreamDemo1 {public static void main(String[] args) throws IOException {
// InputStreamReader isr = new InputStreamReader(new FileInputStream("D:\\11Codingtext\\MyIO\\gbkfile.txt"),"GBK");
// int temp;
// while(true){
// temp = isr.read();
// if(temp == -1){
// break;
// }
// System.out.print((char)temp);
// }
// isr.close();//以下为jdk11之后的替代方案FileReader fr = new FileReader("D:\\11Codingtext\\MyIO\\gbkfile.txt", Charset.forName("GBK"));int temp;while(true){temp = fr.read();if(temp == -1){break;}System.out.print((char) temp);}fr.colse();}
}
FileWriter的解码用法
在创建FileWriter的时候不但可以输入需要传入的文件位置,还可以在后面输入需要传入数据所对应的解码方式,这样就能将相关解码方式的数据存储到相应的文件中,例如:
FileWriter fw =new FileWriter("D:\\11Codingtext\\MyIO\\temp.txt",Charset.forName("GBK"));
具体创建代码如下:这样就能将GBK解码方式的数据放入到temp.txt文件中,如果不是使用GBK解码方式对该文件进行查看的话,只会看到一堆乱码。
//通过FileWriter写出相关编码方式的数据FileWriter fw =new FileWriter("D:\\11Codingtext\\MyIO\\temp.txt",Charset.forName("GBK"));fw.write("这是一个测试语句");fw.close();
练习(一)
利用FileReader和FileWriter中的read和write方法将一个文件中的GBK解码方式的数据读取到程序中,且将其用UTF-8的解码方式写入到指定的其他文件中,具体实现代码如下:
package com.itazhang.Demo1;import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.charset.Charset;public class CoverntStreamDemo2 {public static void main(String[] args) throws IOException {FileReader fr = new FileReader("D:\\11Codingtext\\MyIO\\temp.txt", Charset.forName("GBK"));FileWriter fw = new FileWriter("D:\\11Codingtext\\MyIO\\temp2.txt",Charset.forName("UTF-8"));int temp;while(true){temp = fr.read();if(temp == -1){break;}fw.write((char)temp);}fw.close();fr.close();}
}
源文件在Idea中的显示,因为采用的是GBK解码方式,idea是采用的UTF-8解码方式,所以看到的数据是以乱码的形式展现
在实现上诉代码并将其写入到temp2文件之后,如下正常显示出来了
练习(二)
将一个文件用字节流取出,且读取到程序中的时候一次读取一行,且在写入新文件的时候,写入一行就换行。
需求分析:上诉功能因为每次读取一行的功能是BufferedReader所特有的,且写入一行换行也是BufferedWriter所特有的,但是题目要求我们用字节流取出,所以这个时候就会用到转换流的方法,这个时候就会先创建字节流FileInputStream将数据读取出来,再用转换流将该字节流转化为字符流,再用该字符流创建字符流的高级流BufferedReader,这样就能实现每次读取一行的功能,同样的输出的时候每输出一行换行操作也是字符流的高级流BufferedWriter所特有的功能,根据上诉的功能要求,通过不断的创建转化从而实现上诉题目的需求,具体代码如下:
package com.itazhang.Demo2;import java.io.*;public class ConvertStreamExercise3 {public static void main(String[] args) throws IOException {FileInputStream fi = new FileInputStream("D:\\11Codingtext\\MyIO\\a.txt");InputStreamReader isr = new InputStreamReader(fi);BufferedReader br =new BufferedReader(isr);FileOutputStream fo = new FileOutputStream("D:\\11Codingtext\\MyIO\\temp3.txt");OutputStreamWriter osw = new OutputStreamWriter(fo);BufferedWriter bw = new BufferedWriter(osw);String temp;while(true){temp = br.readLine();if(temp == null){break;}bw.write(temp);bw.newLine();}bw.close();br.close();}
}
当然在常见字符流和转换流和字符流的高级流BufferedReader这三步可以嵌套成一步
FileInputStream fi = new FileInputStream("D:\\11Codingtext\\MyIO\\a.txt"); InputStreamReader isr = new InputStreamReader(fi); BufferedReader br =new BufferedReader(isr);
改为:
BufferedReader br =new BufferedReader(new InputStreamReader(new FileInputStream("D:\\11Codingtext\\MyIO\\a.txt")));
转换流总结:
序列化流
序列化流能将Java中的对象写入到本地文件中,其实质为字节流的高级流,所以在创建的时候需要关联字节流中的基本流
创建之后,因为传递对象到本地文件,这个对象的类需要实现一个接口Serializable,但是这是一个标记型接口,哪个类实现了该接口说明该类创建出的对象可以被序列化,里面是没有方法的,这样之后,就能将对象写入到本地文件中了。
需要添加的对象学生类如下:
package com.itazhang.Demo3;import java.io.Serializable;public class Student implements Serializable {private String name;private int age;public Student() {}public Student(String name, int age) {this.name = name;this.age = age;}/*** 获取* @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;}public String toString() {return "Student{name = " + name + ", age = " + age + "}";}
}
具体实现代码如下:
package com.itazhang.Demo3;import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;public class ObjecyStreamDemo1 {public static void main(String[] args) throws IOException {Student stu = new Student("azhang",22);//创建序列化流,因为时字节流的高级流,所以需要先创建字节流基础流再包装成序列化流ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:\\11Codingtext\\MyIO\\c.txt"));//用Write方法将对象写入到本地文件oos.writeObject(stu);//释放资源oos.close();}
}
反序列化流
反序列化流也是字节流的高级流,他也是再字符输入流FileInputStream的基础上包装而成,所以在创建该高级流之前,需要关联一个字符输出流,该高级流的作用其实就是将通过序列化流存储到文件中的对象通过该类的readObject方法读取到程序中,具体代码实现如下:
//创建反序列化流,读取文件中的数据并返回到程序中ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D:\\11Codingtext\\MyIO\\c.txt"));//用readObject方法将数据读取出来并返回到程序中Object o = ois.readObject();System.out.println(o);//释放资源ois.close();
序列化和反序列化的问题
在将一个对象序列化放入到本地文件之后,后面可能我将该对象的类里进行一系列的修改,例如增添成员遍历,这时候,将该文件的数据反序列化读取到文件的时候就会报错,因为在我们将该对象序列化并输出到文件的时候,Java在底层就会生成一个该类的版本号,这个版本号会固定该类的格式,而在我们修改了类里面的数据例如成员变量的时候,这个时候Java里面的该类版本号就会发生改变,但是存储到文件里的数据版本号还是之前的,所以在反序列化的时候,Java将这两个版本号进行比对就会失败,最终导致报错
那么怎么解决上述问题呢,我们就可以直接将需要序列化的对象的类的版本号固定死,这样无论之后对该类进行怎样的修改,之前序列化保持的文件数据版本号就会始终与类里的版本号一致。使用idea自带的uid生成,这样可以自动根据我们当前类里面 的方法属性等生成一个固定的版本号。
如下为idea自动生成的版本号
但是如果我类里的一些数据不想被序列化到本地文件怎么办呢,这个时候可以在相关属性前面加上transient关键字,这个关键字的意思是瞬态关键字,他所修饰的属性不会被序列化到本地文件
序列化流与反序列化流总结
注意:一次序列化只能向文件写入一个对象,一次反序列化也只能从文件中读取一个对象。如果需要读取或写入多个对象的话,则需要多次使用序列化流和反序列化流。但是一般不会多次重复写入和读取对象,一般采用将需要序列化存入文件的对象用Arraylist集合存储起来,直接序列化这个集合,将集合里所有对象序列化存入到该文件中。同理,需要读取一个文件存储的对象使用反序列化时,我们一般采用Arraylist集合去接收这些对象,这样的话方便我们序列化和反序列化多个对象
具体实现代码如下:
package com.itazhang.Demo3;import java.io.*;
import java.util.ArrayList;
import java.util.Collections;public class ObjecyStreamDemo1 {public static void main(String[] args) throws IOException, ClassNotFoundException {Student stu= new Student("azhang",22,"001");Student stu1= new Student("azhan",22,"001");Student stu2= new Student("azha",22,"001");Student stu3= new Student("azh",22,"001");//用集合存储需要序列化的对象ArrayList<Student> list1 =new ArrayList<>();Collections.addAll(list1,stu,stu1,stu2,stu3);//创建序列化流,因为时字节流的高级流,所以需要先创建字节流基础流再包装成序列化流ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:\\11Codingtext\\MyIO\\c.txt"));//用Write方法将对象写入到本地文件oos.writeObject(list1);//释放资源oos.close();//创建反序列化流,读取文件中的数据并返回到程序中ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D:\\11Codingtext\\MyIO\\c.txt"));//用readObject方法将数据读取出来并返回到程序中ArrayList<Student> list2 =new ArrayList<>();list2 = (ArrayList<Student>) ois.readObject();System.out.print(list2+" ");//释放资源ois.close();}
}
打印流
打印流只能输出,不能读取,打印流分为printStream和printWriter,printStream是字节打印流,printWriter是字符打印流,打印流能将数据打印到指定文件里
字节打印流printStream
字符打印流printWriter
压缩和解压缩流
压缩和解压缩流,顾名思义就是用来压缩文件和解压文件的,他们都是属于字节流的高级流,压缩流是用来将程序数据写入到文件中,解压缩流是用来将文件中的数据读取到程序中
解压缩流
ZipInputStream是一个解压缩流,他的作用是将压缩包里面的数据读取到程序中,也就是执行读取的功能,他的底层是基于FileInputStream,他是该字节流的高级流,所以再创建他的时候跟之前的高级流创建一样,都是需要先创建底层的流,再用高级流方法将其包装,具体创建代码如下:
ZipInputStream zip = new ZipInputStream(new FileInputStream(src));
具体解压代码如下:
/*
* 解压缩流
*
* */
public class ZipStreamDemo1 {public static void main(String[] args) throws IOException {//1.创建一个File表示要解压的压缩包File src = new File("D:\\aaa.zip");//2.创建一个File表示解压的目的地File dest = new File("D:\\");//调用方法unzip(src,dest);}//定义一个方法用来解压public static void unzip(File src,File dest) throws IOException {//解压的本质:把压缩包里面的每一个文件或者文件夹读取出来,按照层级拷贝到目的地当中//创建一个解压缩流用来读取压缩包中的数据ZipInputStream zip = new ZipInputStream(new FileInputStream(src));//要先获取到压缩包里面的每一个zipentry对象//表示当前在压缩包中获取到的文件或者文件夹ZipEntry entry;while((entry = zip.getNextEntry()) != null){System.out.println(entry);if(entry.isDirectory()){//文件夹:需要在目的地dest处创建一个同样的文件夹File file = new File(dest,entry.toString());file.mkdirs();}else{//文件:需要读取到压缩包中的文件,并把他存放到目的地dest文件夹中(按照层级目录进行存放)FileOutputStream fos = new FileOutputStream(new File(dest,entry.toString()));int b;while((b = zip.read()) != -1){//写到目的地fos.write(b);}fos.close();//表示在压缩包中的一个文件处理完毕了。zip.closeEntry();}}zip.close();}
}
压缩流
压缩一个文件的代码具体如下:
public class ZipStreamDemo2 {public static void main(String[] args) throws IOException {/** 压缩流* 需求:* 把D:\\a.txt打包成一个压缩包* *///1.创建File对象表示要压缩的文件File src = new File("D:\\a.txt");//2.创建File对象表示压缩包的位置File dest = new File("D:\\");//3.调用方法用来压缩toZip(src,dest);}/** 作用:压缩* 参数一:表示要压缩的文件* 参数二:表示压缩包的位置* */public static void toZip(File src,File dest) throws IOException {//1.创建压缩流关联压缩包ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(new File(dest,"a.zip")));//2.创建ZipEntry对象,表示压缩包里面的每一个文件和文件夹//参数:压缩包里面的路径ZipEntry entry = new ZipEntry("aaa\\bbb\\a.txt");//3.把ZipEntry对象放到压缩包当中zos.putNextEntry(entry);//4.把src文件中的数据写到压缩包当中FileInputStream fis = new FileInputStream(src);int b;while((b = fis.read()) != -1){zos.write(b);}zos.closeEntry();zos.close();}
}
压缩一个文件夹的代码具体如下:
public class ZipStreamDemo3 {public static void main(String[] args) throws IOException {/** 压缩流* 需求:* 把D:\\aaa文件夹压缩成一个压缩包* *///1.创建File对象表示要压缩的文件夹File src = new File("D:\\aaa");//2.创建File对象表示压缩包放在哪里(压缩包的父级路径)File destParent = src.getParentFile();//D:\\//3.创建File对象表示压缩包的路径File dest = new File(destParent,src.getName() + ".zip");//4.创建压缩流关联压缩包ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(dest));//5.获取src里面的每一个文件,变成ZipEntry对象,放入到压缩包当中toZip(src,zos,src.getName());//aaa//6.释放资源zos.close();}/** 作用:获取src里面的每一个文件,变成ZipEntry对象,放入到压缩包当中* 参数一:数据源* 参数二:压缩流* 参数三:压缩包内部的路径* */public static void toZip(File src,ZipOutputStream zos,String name) throws IOException {//1.进入src文件夹File[] files = src.listFiles();//2.遍历数组for (File file : files) {if(file.isFile()){//3.判断-文件,变成ZipEntry对象,放入到压缩包当中ZipEntry entry = new ZipEntry(name + "\\" + file.getName());//aaa\\no1\\a.txtzos.putNextEntry(entry);//读取文件中的数据,写到压缩包FileInputStream fis = new FileInputStream(file);int b;while((b = fis.read()) != -1){zos.write(b);}fis.close();zos.closeEntry();}else{//4.判断-文件夹,递归toZip(file,zos,name + "\\" + file.getName());// no1 aaa \\ no1}}}
}
Commons工具包
该包里提供了许多的工具类,需要调用直接用工具类调用即可,特别是IO操作,如之前的复制文件,复制文件夹,删除文件,删除文件夹都能通过调用里面的工具类的相关方法实现
例如相关拷贝方法:
copy方法有多个重载方法,满足不同的输入输出流
IOUtils.copy(InputStream input, OutputStream output)
IOUtils.copy(InputStream input, OutputStream output, int bufferSize)//可指定缓冲区大小
IOUtils.copy(InputStream input, Writer output, String inputEncoding)//可指定输入流的编码表
IOUtils.copy(Reader input, Writer output)
IOUtils.copy(Reader input, OutputStream output, String outputEncoding)//可指定输出流的编码表