IO流(十八) IO流 :存储和读取数据的解决方案。
IO流的作用 :用于读写数据(本地文件,网络)
按流向分类 :
输出流 :写出,从程序到文件
输入流 :读取,从文件到程序
按操作文件类型分类 :
字节流 :可操作所有类型的文件
字符流 :只可操作纯文本文件(用Windows系统自带的记事本打开且能读懂的文件,如:txt,md,xml,lrc)
体系 IO流体系
字节流 :可以拷贝任意类型的文件
InputStream :字节输入流,抽象类
基本流 :FileInputStream :操作本地文件的字节输入流
缓冲流 :BufferedInputStream :字节缓冲输入流
反序列化流 :ObjectInputStream
解压缩流 :ZipInputStream
OutputStream :字节输出流,抽象类
基本流 :FileOutputStream :操作本地文件的字节输出流
缓冲流 :BufferedOutputStream :字节缓冲输出流
序列化流 :ObjectOutputStream
字节打印流 :PrintStream
压缩流 :ZipOutputStream
字符流 :读取纯文本文件中的数据,向纯文本文件写出数据
Reader :字符输入流,抽象类
基本流 :FileReader :操作本地文件的字符输入流
缓冲流 :BufferedReader :字符缓冲输入流
转换流 :InputStreamReader :转换输入流
Writer :字符输出流,抽象类
基本流 :FileWriter :操作本地文件的字符输出流
缓冲流 :BufferedWriter :字符缓冲输出流
转换流 :OutputStreamWriter :转换输出流
字符打印流 :PrintWriter
字节流 字节输出流OutputStream FileOutputStream 操作本地文件的字节输出流,可把程序中的数据写到本地文件。
步骤 :
创建字节输出流对象,就像在两个地点之间修一条公路
参数可以是字符串表示的路径或File对象
若文件不存在,则会创建新文件,但要保证父级路径存在
若文件存在,则会清空文件
写数据,就像在公路上传输
write方法的参数是整数,则会写入整数对应的ASCII字符
释放资源,就像把公路拆掉
每次用完流后,均要释放资源
范例
1 2 3 4 5 6 7 8 9 10 import java.io.FileOutputStream; import java.io.IOException; public class Test{ public static void main(String[] args) throws IOException { FileOutputStream fos=new FileOutputStream("b.txt"); fos.write(97); fos.close(); } }
写数据的三种方式 方式一 :
void write(int b)
说明 :一次写一个字节数据
范例 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 import java.io.FileOutputStream; import java.io.IOException; public class Test{ public static void main(String[] args) throws IOException { FileOutputStream fos=new FileOutputStream("a.txt"); fos.write(97); fos.close(); } } /*a.txt的内容 a */
方式二 :
void write(byte[] b)
说明 :一次写一个字节数组数据
范例 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import java.io.FileOutputStream; import java.io.IOException; public class Test{ public static void main(String[] args) throws IOException { FileOutputStream fos=new FileOutputStream("a.txt"); byte[] arr={97,98,99,100}; fos.write(arr); fos.close(); } } /*a.txt的内容 abcd */
方式三 :
void write(byte[] b,int off,int len)
说明 :一次写一个字节数组的部分数据,off表示起始索引,len表示长度
范例 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 import java.io.FileOutputStream; import java.io.IOException; public class Test{ public static void main(String[] args) throws IOException { FileOutputStream fos=new FileOutputStream("a.txt"); byte[] arr={97,98,99,100}; fos.write(arr,1,2); fos.close(); } } /*a.txt的内容 bc */
换行和续写 换行 :再次写出换行符即可。
不同系统下的换行符不同:window是\r\n,linux是\n,Mac是\r。
在Windows操作系统中,java对回车换行进行优化,虽然完整的是\r\n,但写其中一个\n或\r,java在底层便会自动补全,也可实现换行,不过还是建议不要省略,直接写全。
续写 :
若不想清空文件,想续写,打开续写开关即可,开关位置在创建对象的第二个参数,默认为false,手动传递true便会打开续写。
范例 :
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 import java.io.FileOutputStream; import java.io.IOException; public class Test{ public static void main(String[] args) throws IOException { //换行 FileOutputStream fos1=new FileOutputStream("a.txt"); String string="ljsblog"; byte[] bytes=string.getBytes(); fos1.write(bytes); String strLine="\r\n"; byte[] line=strLine.getBytes(); fos1.write(line); fos1.write(bytes); fos1.close(); //续写 FileOutputStream fos2=new FileOutputStream("a.txt",true); String str="666"; byte[] bytes1=str.getBytes(); fos2.write(bytes1); fos2.close(); } } /*a.txt内容 ljsblog ljsblog666 */
操作本地文件的字节输入流,可把本地文件中的数据读取到程序中来。
步骤 :
创建对象
若文件不存在,则直接报错
读取数据
一次读一个字节,读出来的是数据在ASCII上对应的数字
若读到问价末尾,read方法返回-1
释放资源
每次使用完流必须要释放资源
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 /*a.txt的内容 ab */ import java.io.FileInputStream; import java.io.IOException; public class Test{ public static void main(String[] args) throws IOException { FileInputStream fis=new FileInputStream("a.txt"); int c1=fis.read(); int c2=fis.read(); int c3=fis.read(); System.out.println(c1+" "+c2+" "+c3); System.out.println(""+(char)c1+(char)c2+(char)c3); fis.close(); } } /* 97 98 -1 ab */
循环读取 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 /*a.txt内容 absafsaf */ import java.io.FileInputStream; import java.io.IOException; public class Test{ public static void main(String[] args) throws IOException { FileInputStream fis=new FileInputStream("a.txt"); int c; while((c=fis.read())!=-1){ System.out.print((char)c); } fis.close(); } } /* absafsaf */
一次读多个字节 格式 :
public int read()
说明 :一次读一个字节数据。
格式 :
public int read(byte[] buffer)
说明 :一次读一个字节数组数据,每次读取的数量和字节数组长度有关,读取的内容会存入字节数组,返回值是本次读出数据的字节长度,字节数组长度尽量是1024的倍数。
范例 :
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 /*a.txt内容 abcde */ import java.io.FileInputStream; import java.io.IOException; public class Test{ public static void main(String[] args) throws IOException { FileInputStream fis=new FileInputStream("a.txt"); byte[] bytes=new byte[2]; int len1=fis.read(bytes); System.out.println(len1); System.out.println(new String(bytes,0,len1)); int len2=fis.read(bytes); System.out.println(len2); System.out.println(new String(bytes,0,len2)); int len3=fis.read(bytes); System.out.println(len3); System.out.println(new String(bytes,0,len3)); fis.close(); } } /* 2 ab 2 cd 1 e */
拷贝文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 //将source.mp4拷贝,拷贝文件名copy.mp4 import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; public class Test{ public static void main(String[] args) throws IOException { FileInputStream fis=new FileInputStream("source.mp4"); FileOutputStream fos=new FileOutputStream("copy.mp4"); byte[] bytes=new byte[1024*1024*10];//10M int len=0; while((len= fis.read(bytes))!=-1){ fos.write(bytes,0,len); } fos.close(); fis.close(); } }
异常处理 AutoCloseable AutoCloseable 是接口。
特点 :特定情况下,可自动释放资源
基本做法 1 2 3 4 5 6 7 8 //手动释放资源 try{ 可能出现问题的代码 }catch(异常类名 变量名){ 异常的处理代码 }finally{ 执行所有资源释放操作 }
JDK7方案 1 2 3 4 5 6 //资源用完最终自动释放 try(创建流对象1;创建流对象2){//只有实现AutoCloseable接口的类才能在在此创建对象 可能出现问题的代码 }catch(异常类名 变量名){ 异常的处理代码 }
JDK9方案 1 2 3 4 5 6 7 8 //资源用完最终自动释放 创建流对象1; 创建流对象2; try(流1;流2){//只有实现AutoCloseable接口的类才能在在此创建对象 可能出现问题的代码 }catch(异常类名 变量名){ 异常的处理代码 }
字符集 ASCII 一个英文占一个字符。
ASCII编码规则 :前面补0,补全8位。
ASCII解码规则 :直接转换成十进制。
GBK 简体中文版Windows,默认使用GBK 字符集。
GBK字符集 完全兼容ASCII字符集 。
一个英文占一个字节,二进制第一位是0。
一个中文占两个字节,二进制高位字节的第一位是1。
Unicode Unicode 万国码。
Unicode 字符集的UTF-8编码格式:
一个英文占一个字节,二进制第一位是0,转成十进制是正数
一个中文占三个字节,二进制第一位是1,装成十进制数是负数
乱码 产生乱码的原因 :
读取数据时未读完整个汉字
编码和解码时的方式不统一
如何不产生乱码 :
不要用字节流读取文本文件
编码解码时,使用同一个码表,同一个编码方式
编码和解码方法 编码 格式1 :
public byte[] getBytes()
说明 :使用默认方式编码,IDEA为UTF-8。
格式2 :
public byte[] getBytes(String charsetName)
说明 :使用指定方式编码。
解码 格式1 :
String(byte[] bytes)
说明 :使用默认方式解码。
格式2 :
String(byte[] bytes,String charsetName)
说明 :使用指定方式解码。
范例 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 34 35 36 37 import java.io.UnsupportedEncodingException; import java.util.Arrays; public class Test{ public static void main(String[] args) throws UnsupportedEncodingException { String str1="ni是憨批"; //默认utf-8 byte[] bytes1=str1.getBytes(); System.out.println(Arrays.toString(bytes1)); //指定GBK byte[] bytes2=str1.getBytes("GBK"); System.out.println(Arrays.toString(bytes2)); //bytes1编码方式为utf-8 //以utf-8方式解码bytes1 String strUtf1=new String(bytes1); System.out.println(strUtf1); //以gbk方式解码bytes1,结果为乱码 String strGbk1=new String(bytes1,"GBK"); System.out.println(strGbk1); //bytes2编码方式为GBK //以utf-8方式解码bytes2,结果为乱码 String strUtf2=new String(bytes2); System.out.println(strUtf2); //以gbk方式解码bytes2 String strGbk2=new String(bytes2,"GBK"); System.out.println(strGbk2); } } /* [110, 105, -26, -104, -81, -26, -122, -88, -26, -119, -71] [110, 105, -54, -57, -70, -87, -59, -6] ni是憨批 ni鏄啫鎵� ni�Ǻ��� ni是憨批 */
字符流 字符流底层就是字节流,适用于对纯文本文件进行操作。
字符流=字节流+字符集。
输入流 :一次读一个字节,遇到中文,一次读取多个字节。
输出流 :底层会将数据按照指定的编码方式进行编码,变成字节再写到文件中。
字符输入流Reader FileReader 步骤 :
创建字符输入流对象,若文件不存在会报错
构造方法 :FileReader(File file)
构造方法 :FileReader(String pathname)
读取数据:按字节进行读取,遇到中文,一次读取多个字节,读取后编码,返回一个整数
方法 :public int read()
读取数据,读到末尾返回-1
默认是一个字节一个字节读取,若遇到中文,会一次读取多个
读取之后,方法的底层还会进行解码,并转换为十进制,并将该十进制作为返回值
方法 :public int read(char[] buffer)
读取多个数据,读到末尾返回-1
返回数组长度
空参的read+强制类型转换,无需再手动转换
释放资源
成员方法 :public int close()
说明 :释放资源/关闭
范例 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 //read() import java.io.FileReader; import java.io.IOException; public class Test{ public static void main(String[] args) throws IOException { FileReader fr=new FileReader("a.txt"); int c; while((c=fr.read())!=-1){ System.out.print((char)c); } fr.close(); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 //read(char[] buffer) import java.io.FileReader; import java.io.IOException; public class Test{ public static void main(String[] args) throws IOException { FileReader fr=new FileReader("a.txt"); char[] chars=new char[5]; int len; while((len=fr.read(chars))!=-1){ System.out.print(new String(chars,0,len)); } fr.close(); } }
FileWriter 步骤 :
创建字符输出流对象
参数可以是字符串表示的路径或File对象
若文件不存在则会创建新文件,但要保证父级路径存在
若文件已经存在则会清空文件,不想清空可以打开续写开关,即第二个参数写true
构造方法
public FileWriter(File file) 创建字符输出流并关联本地文件
public FileWriter(String pathname) 创建字符输出流并关联本地文件
public FileWriter(File file,boolean append) 创建字符输出流并关联本地文件,续写
public FileWriter(String pathname,boolean append) 创建字符输出流并关联本地文件,续写
写数据:若write方法是整数,写到本地文件的是整数在字符集上对应的字符
成员方法
void write(int c) 写出一个字符
void write(String str) 写出一个字符串
void write(String str,int off,int len) 写出一个字符串的一部分
void write(char[] cbuf) 写出一个字符数组
void write(char[] cbuf,int off,int len) 写出字符数组的一部分
释放资源:每次用完流后都要释放资源
范例 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import java.io.FileWriter; import java.io.IOException; public class Test{ public static void main(String[] args) throws IOException { FileWriter fw1=new FileWriter("a.txt"); fw1.write(25105); FileWriter fw2=new FileWriter("a.txt",true); fw1.write("是博主"); fw1.write("abcde",0,2); char[] chars={'你','是','观','众'}; fw1.write(chars); fw1.write(chars,0,2); fw1.close(); } } /*a.txt 我是博主ab你是观众你是 */
字符输入流底层原理
创建字符输入流对象,底层关联文件,并创建缓冲区(长度为8192的字节数组)
读取数据,
判断缓冲区中是否有数据可以读取
缓冲区没有数据:从文件获取数据,装到缓冲区,每次尽可能填满缓冲区,若文件也没有数据,返回-1
缓冲区有数据:从缓冲区读取数据
空参的read方法,一次读取一个字节,遇到中文一次读多个字节,把字节解码并转成十进制返回
有参的read方法,把读取字节,解码,强转三步合并,强转之后的字符放到数组中。
字符输出流底层原理
创建字符输出流对象,底层关联文件,并创建缓冲区(长度为8192的字节数组)
当数据缓冲区,手动刷新(flush)或关流(close)时,会将缓冲区中的数据存储到文件中。
flush刷新:刷新之后,依旧可向文件写出数据
close关流:断开通道,不可忘文件写出数据。
范例 例1 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 //需求:拷贝一个文件夹,考虑子文件夹 import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; public class Test{ public static void main(String[] args) throws IOException { File src=new File("d:\\javafiletest\\a"); File det=new File("d:\\javafiletest\\copy"); copyFile(src,det); } public static void copyFile(File src,File det) throws IOException { det.mkdirs(); File[] files=src.listFiles(); for(File file:files) { if (file.isFile()) { FileInputStream fis = new FileInputStream(file); FileOutputStream fos = new FileOutputStream(new File(det, file.getName())); byte[] bytes = new byte[1024 * 5]; int len; while ((len = fis.read(bytes)) != -1) { fos.write(bytes, 0, len); } fis.close(); fos.close(); } else { copyFile(file,new File(det,file.getName()) ); } } } }
例2 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 /* 为保证文件的安全性,需对原始文件进行加密存储,在使用时进行解密 加密原理: 对原始文件的每个字节进行更改,将更改的数据存到新文件中 解密原理: 读取加密后的文件,按加密规则反向操作,变成原始文件 */ import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; public class Test{ public static void main(String[] args) throws IOException { FileInputStream fis1=new FileInputStream("src.PNG"); FileOutputStream fos1=new FileOutputStream("jiami.PNG"); int c; while ((c=fis1.read())!=-1){ fos1.write(c^100); } fos1.close(); fis1.close(); //一个数字异或另一个数字两次,得到的结果还是数字本身 FileInputStream fis2=new FileInputStream("jiami.PNG"); FileOutputStream fos2=new FileOutputStream("jiemi.PNG"); while((c=fis2.read())!=-1){ fos2.write(c^100); } fos2.close(); fis2.close(); } }
例3 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 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 /* 文本文件中有以下的数据: 6-8-4-2-1 将文件中的数据进行排序,变成以下数据 1-2-4-6-8 */ //方式1 import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.util.Arrays; import java.util.StringJoiner; public class Test{ public static void main(String[] args) throws IOException { FileInputStream fis=new FileInputStream("src.txt"); StringBuilder sb=new StringBuilder(); int c; while((c=fis.read())!=-1){ sb.append((char)c); } String[] s=sb.toString().split("-"); int[] arr=new int[s.length]; for (int i=0;i<arr.length;i++){ arr[i]=Integer.parseInt(s[i]); } Arrays.sort(arr); System.out.println(Arrays.toString(arr)); StringJoiner sj=new StringJoiner("-"); for(int a:arr){ sj.add(""+a); } FileOutputStream fos=new FileOutputStream("src.txt"); byte[] bytes=sj.toString().getBytes(); fos.write(bytes); fos.close(); fis.close(); } } //方式2 import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.util.Arrays; public class Test{ public static void main(String[] args) throws IOException { FileReader fis=new FileReader("src.txt"); StringBuilder sb=new StringBuilder(); int c; while((c=fis.read())!=-1){ sb.append((char)c); } String[] sarr=sb.toString().split("-"); Integer[] iarr=Arrays.stream(sarr). map(Integer::parseInt). sorted(). toArray(Integer[]::new); System.out.println(Arrays.toString(iarr)); String s=Arrays.toString(iarr). replace(", ","-"); String str=s.substring(1,s.length()-1); FileWriter fos=new FileWriter("src.txt"); fos.write(str); fos.close(); fis.close(); } }
缓冲流 原理 :底层自带长度为8192的缓冲区提高性能,字节缓冲流8192个byte,字符缓冲流8192个char。
字节缓冲流,字节流本身并无缓冲区,但可加入缓冲流加快效率,即字节缓冲流
BufferedInputStream :字节缓冲输入流
BufferedOutputStream :字节缓冲输出流
字符缓冲流,字符缓冲流对效率提升不大,但字符缓冲流中特有方法比较重要
BufferedReader :字符缓冲输入流
BufferedWriter :字符缓冲输出流
字节缓冲流 构造方法 格式 :
public BufferedInputStream(InputStream is)
说明 :把基本流包装成高级流,提高读取数据的性能
BufferedOutputSream 格式 :
public BufferedOutputStream(OutputStream os)
说明 :把基本流包装成高级流,提高写出数据的性能
范例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 /* 需求: 利用字节缓冲流拷贝文件 */ import java.io.*; public class Test{ public static void main(String[] args) throws IOException { BufferedInputStream bis=new BufferedInputStream(new FileInputStream("src.txt")); BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream("copy.txt")); byte[] bytes=new byte[1024]; int len; while((len=bis.read(bytes))!=-1){ bos.write(bytes,0,len); } bos.close(); bis.close(); } }
字符缓冲流 构造方法 BufferedReader 格式 :
public BufferedReader(Reader r)
说明 :把基本流变成高级流
BufferedWriter 格式 :
public BufferedWriter(Writer r)
说明 :把基本流变成高级流
特有方法 readLine 格式 :
public String readLine()
说明 :读取一行数据,遇到回车换行结束,读取的数据不包括回车换行,若没有数据可读,返回null
newline 格式 :
public void newLine()
说明 :跨平台的换行
范例 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 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 /*src.txt内容 君不见黄河之水天上来,奔流到海不复回。 君不见高堂明镜悲白发,朝如青丝暮成雪。 人生得意须尽欢,莫使金樽空对月。 天生我材必有用,千金散尽还复来。 烹羊宰牛且为乐,会须一饮三百杯。 岑夫子,丹丘生,将进酒,杯莫停。 与君歌一曲,请君为我倾耳听。 钟鼓馔玉不足贵,但愿长醉不复醒。 古来圣贤皆寂寞,惟有饮者留其名。 陈王昔时宴平乐,斗酒十千恣欢谑。 */ import java.io.*; public class Test{ public static void main(String[] args) throws IOException { BufferedReader br=new BufferedReader(new FileReader("src.txt")); BufferedWriter bw=new BufferedWriter(new FileWriter("src.txt",true)); String s; while((s=br.readLine())!=null){ System.out.println(s); } bw.newLine(); bw.write("主人何为言少钱,径须沽取对君酌。"); bw.newLine(); bw.write("五花马、千金裘,呼儿将出换美酒,与尔同销万古愁。"); bw.close(); br.close(); } } /*打印 君不见黄河之水天上来,奔流到海不复回。 君不见高堂明镜悲白发,朝如青丝暮成雪。 人生得意须尽欢,莫使金樽空对月。 天生我材必有用,千金散尽还复来。 烹羊宰牛且为乐,会须一饮三百杯。 岑夫子,丹丘生,将进酒,杯莫停。 与君歌一曲,请君为我倾耳听。 钟鼓馔玉不足贵,但愿长醉不复醒。 古来圣贤皆寂寞,惟有饮者留其名。 陈王昔时宴平乐,斗酒十千恣欢谑。 */ /*src.txt内容 君不见黄河之水天上来,奔流到海不复回。 君不见高堂明镜悲白发,朝如青丝暮成雪。 人生得意须尽欢,莫使金樽空对月。 天生我材必有用,千金散尽还复来。 烹羊宰牛且为乐,会须一饮三百杯。 岑夫子,丹丘生,将进酒,杯莫停。 与君歌一曲,请君为我倾耳听。 钟鼓馔玉不足贵,但愿长醉不复醒。 古来圣贤皆寂寞,惟有饮者留其名。 陈王昔时宴平乐,斗酒十千恣欢谑。 主人何为言少钱,径须沽取对君酌。 五花马、千金裘,呼儿将出换美酒,与尔同销万古愁。 */
范例 例1 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 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 //四种方式拷贝文件,并统计各自用时 import java.io.*; public class Test{ public static void main(String[] args) throws IOException { method1(); method2(); method3(); method4(); } public static void method1() throws IOException { System.out.println("方式1:字节流的基本流:一次读写一个字节"); FileInputStream fis1=new FileInputStream("a.mp4"); FileOutputStream fos1=new FileOutputStream("copy1.mp4"); int c; long start=System.currentTimeMillis(); System.out.println("start:"+start); while((c=fis1.read())!=-1){ fos1.write(c); } long end=System.currentTimeMillis(); System.out.println("end:"+end); fos1.close(); fis1.close(); System.out.println((end-start)/1000.0+"秒"); } public static void method2() throws IOException { System.out.println("方式2:字节流的基本流,一次读写一个字节数组"); FileInputStream fis2=new FileInputStream("a.mp4"); FileOutputStream fos2=new FileOutputStream("copy2.mp4"); byte[] bytes2=new byte[1024*1024*10]; long start=System.currentTimeMillis(); System.out.println("start:"+start); int len; while((len=fis2.read(bytes2))!=-1){ fos2.write(bytes2,0,len); } long end=System.currentTimeMillis(); System.out.println("end:"+end); fos2.close(); fis2.close(); System.out.println((end-start)/1000.0+"秒"); } public static void method3() throws IOException { System.out.println("方式3:字节缓冲流:一次读写一个字节"); BufferedInputStream bis1=new BufferedInputStream(new FileInputStream("a.mp4")); BufferedOutputStream bos1=new BufferedOutputStream(new FileOutputStream("copy3.mp4")); int bc; long start=System.currentTimeMillis(); System.out.println("start:"+start); while((bc=bis1.read())!=-1){ bos1.write(bc); } long end=System.currentTimeMillis(); System.out.println("end:"+end); bos1.close(); bis1.close(); System.out.println((end-start)/1000.0+"秒"); } public static void method4() throws IOException { System.out.println("方式4:字节缓冲流,一次读写一个字节数组"); BufferedInputStream bis2=new BufferedInputStream(new FileInputStream("a.mp4")); BufferedOutputStream bos2=new BufferedOutputStream(new FileOutputStream("copy4.mp4")); int blen; byte[] bbytes=new byte[1024*1024*10]; long start=System.currentTimeMillis(); System.out.println("start:"+start); while((blen=bis2.read(bbytes))!=-1){ bos2.write(bbytes,0,blen); } long end=System.currentTimeMillis(); System.out.println("end:"+end); bos2.close(); bis2.close(); System.out.println((end-start)/1000.0+"秒"); } } /* 方式1:字节流的基本流:一次读写一个字节 start:1680080313285 end:1680081118655 805.37秒 方式2:字节流的基本流,一次读写一个字节数组 start:1680081118661 end:1680081118804 0.143秒 方式3:字节缓冲流:一次读写一个字节 start:1680081118806 end:1680081119696 0.89秒 方式4:字节缓冲流,一次读写一个字节数组 start:1680081119701 end:1680081119846 0.145秒 */
例2 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 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 //给《将进酒》的文章顺序进行恢复到一个新文件夹中 /*src.txt 2.君不见高堂明镜悲白发,朝如青丝暮成雪。 3.人生得意须尽欢,莫使金樽空对月。 1.君不见黄河之水天上来,奔流到海不复回。 12.五花马、千金裘,呼儿将出换美酒,与尔同销万古愁。 4.天生我材必有用,千金散尽还复来。 5.烹羊宰牛且为乐,会须一饮三百杯。 7.与君歌一曲,请君为我倾耳听。 10.陈王昔时宴平乐,斗酒十千恣欢谑。 11.主人何为言少钱,径须沽取对君酌。 9.古来圣贤皆寂寞,惟有饮者留其名。 6.岑夫子,丹丘生,将进酒,杯莫停。 8.钟鼓馔玉不足贵,但愿长醉不复醒。 */ import java.io.*; import java.util.TreeSet; public class Test{ public static void main(String[] args) throws IOException { BufferedReader bis=new BufferedReader(new FileReader("src.txt")); BufferedWriter bos=new BufferedWriter(new FileWriter("det.txt")); TreeSet<String> ts=new TreeSet<>((o1,o2) ->Integer.parseInt(o1.split("\\.")[0])-Integer.parseInt(o2.split("\\.")[0])); String s; while((s=bis.readLine())!=null){ ts.add(s); } for(String str:ts){ bos.write(str); bos.newLine(); } bos.close(); bis.close(); } } /*det文件 1.君不见黄河之水天上来,奔流到海不复回。 2.君不见高堂明镜悲白发,朝如青丝暮成雪。 3.人生得意须尽欢,莫使金樽空对月。 4.天生我材必有用,千金散尽还复来。 5.烹羊宰牛且为乐,会须一饮三百杯。 6.岑夫子,丹丘生,将进酒,杯莫停。 7.与君歌一曲,请君为我倾耳听。 8.钟鼓馔玉不足贵,但愿长醉不复醒。 9.古来圣贤皆寂寞,惟有饮者留其名。 10.陈王昔时宴平乐,斗酒十千恣欢谑。 11.主人何为言少钱,径须沽取对君酌。 12.五花马、千金裘,呼儿将出换美酒,与尔同销万古愁。 */
例3 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 /*实现一个验证程序运行次数的小程序,要求如下: 1.当程序运行次数超过3次,给出提示:运行次数超过3次,请充值会员后使用 2.程序运行演示如下: 第一次:第1次运行此文件 第二次:第2次运行此文件 第三次:第3次运行此文件 第四次及以后:运行次数超过3次,请充值会员后使用 */ import java.io.*; public class Test{ public static void main(String[] args) throws IOException { BufferedReader br=new BufferedReader(new FileReader("src.txt")); int c=Integer.parseInt(br.readLine()); c++; if(c<=3){ System.out.println("第"+c+"次运行此文件"); }else{ System.out.println("运行次数超过3次,请充值会员后使用"); } BufferedWriter bw=new BufferedWriter(new FileWriter("src.txt")); bw.write(c+""); bw.close(); br.close(); } }
转换流 转换流 是字符流和字节流之间的桥梁。
作用:
指定字符集读写(在jdk11被淘汰)
字节流想要使用字符流中的方法
将字节流 转换成字符流 ,可以根据字符集一次读取多个字节,读取数据不会乱码
OutputStreamWriter 将字符流 转换成字节流 。
范例 例1 1 2 3 4 5 6 7 8 9 10 11 12 13 //利用转换流按照指定字符编码读取 //jdk8 import java.io.*; public class Test{ public static void main(String[] args) throws IOException { InputStreamReader isr=new InputStreamReader(new FileInputStream("src.txt"),"GBK"); int ch; while((ch=isr.read())!=-1){ System.out.print((char)ch); } isr.close(); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 //jdk11 import java.io.*; import java.nio.charset.Charset; public class Test{ public static void main(String[] args) throws IOException { FileReader fr=new FileReader("src.txt",Charset.forName("GBK")); int ch; while((ch=fr.read())!=-1){ System.out.print((char)ch); } fr.close(); } }
例2 1 2 3 4 5 6 7 8 9 10 //利用转换流按照指定字符编码写出 //jdk8 import java.io.*; public class Test{ public static void main(String[] args) throws IOException { OutputStreamWriter osr=new OutputStreamWriter(new FileOutputStream("src.txt"),"GBK"); osr.write("你好!"); osr.close(); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 //jdk11 import java.io.FileWriter; import java.io.IOException; import java.nio.charset.Charset; public class Test{ public static void main(String[] args) throws IOException { FileWriter fw=new FileWriter("src.txt", Charset.forName("GBK")); fw.write("你好啊"); fw.close(); } }
例3 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 //将本地文件的GBK文件,转成UTF-8文件 //jdk8 import java.io.*; public class Test{ public static void main(String[] args) throws IOException { InputStreamReader isr=new InputStreamReader(new FileInputStream("src.txt"),"GBK"); OutputStreamWriter osw=new OutputStreamWriter(new FileOutputStream("det.txt"),"UTF-8"); int ch; while((ch=isr.read())!=-1){ osw.write(ch); } osw.close(); isr.close(); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 //jdk11 import java.io.*; import java.nio.charset.Charset; public class Test{ public static void main(String[] args) throws IOException { FileReader fr=new FileReader("src.txt", Charset.forName("GBK")); FileWriter fw=new FileWriter("det.txt",Charset.forName("UTF-8")); int ch; while((ch=fr.read())!=-1){ fw.write(ch); } fw.close(); fr.close(); } }
例4 1 2 3 4 5 6 7 8 9 10 11 12 13 //利用字节流读取文件中的数据,每次读一整行,而且不能出现乱码 import java.io.*; public class Test{ public static void main(String[] args) throws IOException { InputStreamReader isr=new InputStreamReader(new FileInputStream("src.txt"),"GBK"); BufferedReader br=new BufferedReader(isr); String s; while((s=br.readLine())!=null){ System.out.println(s); } isr.close(); } }
序列化流和反序列化流 细节 :
使用序列化流将对象写到文件时,需使javabean类实现Serializable 接口,否则就会出现NotSerializableException异常
序列化流写到文件中的数据不可修改,一旦修改就无法再读回来
序列化对象后,修改Javabean类,再次序列化,便会抛出InvalidClassException异常
解决方案 :可以通过给javabean类添加serialVersionUID(版本号,序列号)
若对象中的某成员变量不想被序列化,则给该变量添加transient关键字修饰,该关键字表示的成员变量不参与序列化过程。
序列化流 又称对象操作输出流 ,可以把java中的对象写到本地文件中。
对象需要实现Serializable 接口,写到文件中的内容是看不懂的。
Serializable 接口里面没有抽象方法,标记性接口。
实现了该接口,就表示当前的类可以被序列化。
构造方法 public ObjectOutputStream(OutputStream out)
说明 :把基本流包装成高级流
成员方法 public final void writeObject(Object obj)
说明 :把对象序列化(写出)到文件夹中去
范例 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 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 //Student.java import java.io.Serializable; public class Student implements Serializable { private int age; private String name; public Student() { } public Student(int age, String name) { this.age = age; this.name = name; } /** * 获取 * @return age */ public int getAge() { return age; } /** * 设置 * @param age */ public void setAge(int age) { this.age = age; } /** * 获取 * @return name */ public String getName() { return name; } /** * 设置 * @param name */ public void setName(String name) { this.name = name; } public String toString() { return "Student{age = " + age + ", name = " + name + "}"; } } //Test.java import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectOutputStream; public class Test{ public static void main(String[] args) throws IOException { Student student=new Student(19,"张三"); ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("src.txt")); oos.writeObject(student); oos.close(); } }
反序列流 又称对象操作输入流 ,可以把序列化到本地文件中的对象,读取到程序中来。
构造方法 public ObjectInputStream(OutputStream out)
说明 :把基本流包装成高级流
成员方法 public final Object readObject()
说明 :把序列化到本地文件中的对象,读取到程序中来。
范例 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 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 //Student.java import java.io.Serializable; public class Student implements Serializable { private int age; private String name; public Student() { } public Student(int age, String name) { this.age = age; this.name = name; } /** * 获取 * @return age */ public int getAge() { return age; } /** * 设置 * @param age */ public void setAge(int age) { this.age = age; } /** * 获取 * @return name */ public String getName() { return name; } /** * 设置 * @param name */ public void setName(String name) { this.name = name; } public String toString() { return "Student{age = " + age + ", name = " + name + "}"; } } //Test.java import java.io.FileInputStream; import java.io.IOException; import java.io.ObjectInputStream; public class Test{ public static void main(String[] args) throws IOException, ClassNotFoundException { ObjectInputStream oim=new ObjectInputStream(new FileInputStream("src.txt")); Student student=(Student)oim.readObject(); System.out.println(student); } }
范例 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 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 //将多个自定义对象序列化到文件中,由于对象个数不确定,反序列化该如何读取? //Student.java import java.io.Serializable; public class Student implements Serializable{ private static final long serialVersionUID = -4683704568207367494L; private int age; private String name; public Student() { } public Student(int age, String name) { this.age = age; this.name = name; } /** * 获取 * @return age */ public int getAge() { return age; } /** * 设置 * @param age */ public void setAge(int age) { this.age = age; } /** * 获取 * @return name */ public String getName() { return name; } /** * 设置 * @param name */ public void setName(String name) { this.name = name; } public String toString() { return "Student{age = " + age + ", name = " + name + "}"; } } //Test.java import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectOutputStream; import java.util.ArrayList; public class Test{ public static void main(String[] args) throws IOException, ClassNotFoundException { ArrayList<Student> list=new ArrayList<>(); Student s1=new Student(19,"张三"); Student s2=new Student(18,"李四"); Student s3=new Student(20,"王五"); list.add(s1); list.add(s2); list.add(s3); ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("src.txt")); oos.writeObject(list); oos.close(); } } //Test1.java import java.io.FileInputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.util.ArrayList; public class Test1 { public static void main(String[] args) throws IOException, ClassNotFoundException { ObjectInputStream ois=new ObjectInputStream(new FileInputStream("src.txt")); ArrayList<Student> list=(ArrayList<Student>) ois.readObject(); for (Student student:list){ System.out.println(student); } ois.close(); } } /* Student{age = 19, name = 张三} Student{age = 18, name = 李四} Student{age = 20, name = 王五} */
打印流 打印流 一般是指:PrintStream,PrintWriter两个类。
特点 :
打印流只操作文件目的地,不操作数据源
特有的写出方法可以实现,数据原样打出
特有的写出方法。可以实现自动刷新。自动换行,打印一次数据=写出+换行+刷新
注 :字节流底层没有缓冲区,开不开自动刷新都一样
字节打印流 构造方法 格式1 :
public PrintStream(OutputStream/File/String)
说明 :关联字节输出流/文件/文件路径
格式2 :
public PrintStream(String fileName,Charset charset)
说明 :指定字节编码
格式3 :
public PrintStream(OutputStream out,boolean autoFlush)
说明 :自动刷新
格式4 :
public PrintStream(OutputStream out,boolean autoFlush,String encoding)
说明 :指定字符编码且自动刷新
成员方法 write 格式 :
public void write(int b)
说明 :常规方法,规则跟之前一样,将指定的字节写出。
println 格式 :
public void println(Xxx xx)
说明 :特有方法,打印任意数据,自动刷新,自动换行,数据原样写出
print 格式 :
public void print(Xxx xx)
说明 :特有方法,打印任意数据,不换行,数据原样写出
printf 格式 :
public void printf(String format,Object…args)
说明 :特有方法,带有占位符的打印语句,不换行,数据原样写出
范例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.PrintStream; import java.io.UnsupportedEncodingException; public class Test{ public static void main(String[] args) throws FileNotFoundException, UnsupportedEncodingException { PrintStream ps=new PrintStream(new FileOutputStream("src.txt"),true,"GBK"); ps.println(97); ps.print(true); ps.println(); ps.printf("学生姓名:%s,年龄%d","张三",18); ps.close(); } } /*src.txt内容 97 true 学生姓名:张三,年龄18 */
字符打印流 字符流底层有缓冲区,要自动刷新需要开启。
构造方法 格式1 :
public PrintWriter(Write/File/String)
说明 :关联字符输出流/文件/文件路径
格式2 :
public PrintWriter(String fileName,Charset charset)
说明 :指定字符编码
格式3 :
public PrintWriter(Write out,boolean autoFlush)
说明 :自动刷新
格式4 :
public PrintWriter(OutputStream,boolean autoFlush,String encoding)
说明 :指定字符编码且自动刷新
成员方法 write 格式 :
public void write(int b)
说明 :常规方法,规则跟之前一样,写出字节或字符串。
println 格式 :
public void println(Xxx xx)
说明 :特有方法,打印任意数据且换行,数据原样写出
print 格式 :
public void print(Xxx xx)
说明 :特有方法,打印任意数据,不换行,数据原样写出
printf 格式 :
public void printf(String format,Object…args)
说明 :特有方法,带有占位符的打印语句,不换行,数据原样写出
范例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; public class Test{ public static void main(String[] args) throws IOException { PrintWriter pw=new PrintWriter(new FileWriter("src.txt"),true); pw.println(97); pw.print(true); pw.println(); pw.printf("学生姓名:%s,年龄%d","张三",18); pw.close(); } }
压缩和解压缩 压缩包里的每一个文件。在Java中都是一个ZipEntry对象。
在java中只能识别zip格式的压缩包
解压缩 解压本质 :把每一个ZipEntry按照层级拷贝到本地另一个文件夹中。
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 34 35 36 37 38 39 40 41 42 import java.io.*; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; public class Test{ public static void main(String[] args) throws IOException { //创建一个File表示要解压的文件 File src=new File("d:\\javafiletest.zip"); //创建一个File表示解压的目的地 File det=new File("d:\\"); unzip(src,det); } public static void unzip(File src,File det) throws IOException { //创建一个解压缩流用来读取压缩包中的数据 ZipInputStream zip=new ZipInputStream(new FileInputStream(src)); //要先获取到压缩包里的每个zipEntry对象 //表示当前在压缩包中获取到的文件或文件夹 ZipEntry entry; while((entry=zip.getNextEntry())!=null){ System.out.println(entry); if(entry.isDirectory()){ //若entry是文件夹,则创建对应的文件夹 File file=new File(det,entry.toString()); file.mkdirs(); }else { //entry是文件,读取该文件,并将其放到指定位置 FileOutputStream fos=new FileOutputStream(new File(det,entry.toString())); int ch; while ((ch=zip.read())!=-1){ //写到目的地 fos.write((char)ch); } fos.close(); //表示在压缩包中一个文件处理完毕 zip.closeEntry(); } } zip.close(); } }
压缩 压缩本质 :把每一个(文件/文件夹)看成ZipEntry对象放到压缩包中。
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 34 35 //压缩单个文件 import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; public class Test{ public static void main(String[] args) throws IOException { //创建一个File表示要压缩的文件 File src=new File("d:\\javafiletest\\b.txt"); //创建一个File表示压缩包的位置 File det=new File("d:\\javafiletest\\b.zip"); zip(src,det); } public static void zip(File src,File det) throws IOException { //创建关联压缩包 ZipOutputStream zos=new ZipOutputStream(new FileOutputStream(det)); //创建ZipEntry对象,表示压缩包里面的每个文件和文件夹 ZipEntry zipEntry=new ZipEntry("b.txt"); //把ZipEntry对象放到压缩包中 zos.putNextEntry(zipEntry); //把src文件的数据写到压缩包中 FileInputStream fis=new FileInputStream(src); int c; while((c=fis.read())!=-1){ zos.write(c); } fis.close(); zos.closeEntry(); zos.close(); } }
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 34 35 36 37 //压缩文件夹 import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; public class Test{ public static void main(String[] args) throws IOException { File src=new File("d:\\javafiletest"); File destParent=src.getParentFile(); File det=new File(destParent,src.getName()+".zip"); ZipOutputStream zos=new ZipOutputStream(new FileOutputStream(det)); directoryZip(src,zos,src.getName()); zos.close(); } public static void directoryZip(File src,ZipOutputStream zos,String name) throws IOException { File[] files=src.listFiles(); for(File file:files){ if(file.isFile()){ ZipEntry zipEntry=new ZipEntry(name+"\\"+file.getName()); zos.putNextEntry(zipEntry); FileInputStream fis=new FileInputStream(file); int c; while ((c=fis.read())!=-1){ zos.write(c); } fis.close(); zos.closeEntry(); }else { directoryZip(file,zos,name+"\\"+file.getName()); } } } }
Common-io Common-io是apache开源基金组织提供的一组有关IO操作的开源工具包。
作用 :提高IO流的开发效率
使用步骤 IDEA下的使用步骤:
在项目中创建文件lib
将jar包复制粘贴到lib文件夹
右键点击jar包,选择Add as Library->点击OK
在类中导包使用
常见方法 FileUtils类 FileUtils类,文件/文件夹相关。
copyFile static void copyFile(File srcFile,File destFile)
说明 :复制文件
copyDirectory static void copyDirectory(File srcDir,File destDir)
说明 :复制文件夹
copDirectoryToDirectory static void copDirectoryToDirectory(File srcDir,File destDir)
说明 :复制文件夹,包括最顶层的文件夹
deleteDirectory static void deleteDirectory(File Directory)
说明 :删除文件夹
clearDirectory static void clearDirectory()
说明 :清空文件夹
IOUtils类 IOUtils类(流相关相关)
copy public static int copy(InputStream input,OutStream output)
说明 :复制文件
copyLarge public static int copyLarge(Reader input,Writer output)
说明 :复制大文件
readlines public static String readlines(Reader input)
说明 :读取数据
write public static void write(String data,OutputStream output)
说明 :写出数据
常见方法 FileUtil类 file 说明 :根据参数创建一个File对象,参数可以是多个字符串
touch 说明 :根据参数创建对象,若父级路径不存在,则会自动创建父级路径
writeLines 说明 :把集合中的数据写出到文件中,覆盖模式
appendLines 说明 :把集合中的数据写出到文件中,续写模式
readLines 说明 :指定字符编码,把文件中的数据,读到集合中
readUtf8Lines 说明 :按照Utf-8的形式,把文件中的数据,读到集合中
copy 说明 :拷贝文件或文件夹