文章目录
- 1. File 类
- 2. 字符集
- 3. IO流
- 3-1. FileInputStream(文件字节输入流)
- 3-2. FileOutputStream(文件字节输出流)
- 3-3. 通过FileInputoutStream、FileOutputStream实现文件的复制
- 3-3-1. 通过try-catch-finally来释放资源
- 3-3-2. 通过try-catch-resource来释放资源
- 4. FileReader(文件字符输入流)
- 5. FileWriter(文件字符输出流)
- 6. 缓冲流
- 6-1. 字节缓冲流
- 6-2. 字符缓冲输入流
- 6-3. 字符缓冲输出流
- 7. 测试复制文件代码的性能
- 8. 转换流
- 8-1. 字符输入转换流(InputStreamReader)
- 8-2. 字符输出转换流(OutputStreamWriter)
- 9. 打印流(PrintStream/PrintWriter)
- 9-1.PrintStream
- 9-2. PrintWriter
- 9-3. 关于打印流的高效
- 9-4. 把系统默认的打印流修改为自定义的打印流
- 10. 数据流
- 10-1. 数据输出流(DataOutputStream)
- 10-2. 数据输入流(DataInputStream)
- 11. 序列化流
- 11-1. 对象字节输出流 (ObjectOutputStream)
- 11-2. 对象字节输入流 (ObjectInputStream)
1. File 类
File 是Java.io包下的类,File类的对象,用于代表当前操作系统的文件(可以是文件或文件夹)。
File类只能对文件本身进行操作(比如获取文件大小、文件名、修改时间,或者创建文件/文件夹,删除文件/文件夹),不能读写文件里面存储的数据。如果要读写数据,需要使用到Java IO流。
2. 字符集
- ascii编码中字符a的ascii编码为97,标准的ASCII使用1个字节存储一个字符;
- gbk编码(汉字编码字符集,gbk中一个中文字符编码成两个字节的形式存储,GBK兼容ASCII字符集);
- utf-8编码(unicode字符集的一种编码方式,采取可变长编码方案,英文、数字字符只占用一个字节,汉字字符占用3个字节);
使用gbk编码方式,只占用7个字节。
package File_study;import java.util.Arrays;public class File_study {public static void main(String[] args) throws Exception {// 编码String _str = "我a你";byte[] bytes = _str.getBytes();byte[] bytes1 = _str.getBytes("gbk");System.out.println(Arrays.toString(bytes));// utf-8 编码中上述字符串占用7个字节System.out.println(Arrays.toString(bytes1));// gbk 编码中上述字符串占用5个字节// 解码String string = new String(bytes);System.out.println(string);String s = new String(bytes1);System.out.println(s);// 编码使用的是utf-8,而解码使用的是gbk,因此出现了乱码}
}
运行结果:
3. IO流
- 按流的方向分为输入流、输出流;
- 按流中数据的最小单位,分为字节流、字符流,其中字节流适合操作所有类型的文件,而字符流只适合操作纯文本文件;
因此常用的有字符输入流(Reader)、字符输出流(Writer)、字节输入流(InputStream)、字节输出流(OutputStream),但上述指定的都是抽象类,它们的具体实现类分别为FileReader、FileWriter、FileInputStream、FileOutputStream。
3-1. FileInputStream(文件字节输入流)
用于从文件中读取字节,文件字节输入流常用的构造方法有如下图中的前两种,可以是文件路径或者File对象都可以。
通过文件字节输入流的read方法读取文件数据。
- read 无参数表示每次读取一个字节,返回值类型为int,读取到最后返回值为-1;
- read 一个参数时表示把读取的字节数组传给参数,它的返回值表示此次读取的字节长度,读取到最后返回值为-1。
package File_study;import java.io.*;public class File_study2 {public static void main(String[] args) throws IOException {File file = new File("1/1.txt");// 文件里边的数据为:我a你,使用的编码方式为utf-8InputStream inputStream = new FileInputStream(file);int a;while((a = inputStream.read())!=-1){System.out.println(a);}inputStream.close();}
}
运行结果:
package File_study;import java.io.*;public class File_study2 {public static void main(String[] args) throws IOException {File file = new File("1/1.txt");// 文件里边的数据为:我a你,使用的编码方式为utf-8InputStream inputStream = new FileInputStream(file);byte[] bytes1 = new byte[3];int len;while((len = inputStream.read(bytes1))!=-1){System.out.println(new String(bytes1,0,len));}inputStream.close();}
}
为了避免读取的汉字不乱码,可以定义一个长度和文件大小一致的字节数组,这样就可以读取所有的字节了。
package File_study;import java.io.*;public class File_study2 {public static void main(String[] args) throws IOException {File file = new File("1/1.txt");long len = file.length();// 文件里边的数据为:我a你,使用的编码方式为utf-8InputStream inputStream = new FileInputStream(file);byte[] bytes1 = new byte[(int) len];int read = inputStream.read(bytes1);System.out.println(read+" "+new String(bytes1));}
}
3-2. FileOutputStream(文件字节输出流)
用于把字节数据写入到文件中去。常用的构造方法也有两种,一种是直接写文件名,另外一种则是写File对象。
package File_study;import java.io.*;public class File_study2 {public static void main(String[] args) throws IOException {FileOutputStream os = new FileOutputStream("1.txt");os.write(97);os.write("我a你".getBytes());os.close();}
}
写入方式是会把原来的数据覆盖掉,如果想在文件后面追加数据,可以直接在文件字节输出流后面加上参数true即可,如下:
FileOutputStream os = new FileOutputStream("1.txt",true);
3-3. 通过FileInputoutStream、FileOutputStream实现文件的复制
package File_study;import java.io.*;public class File_study2 {public static void main(String[] args) throws IOException {// 实现文件的复制InputStream is = new FileInputStream("C:\\Users\\Administrator\\Pictures\\5.jpg");OutputStream os = new FileOutputStream("1.jpg",true);byte[] bytes = new byte[1024*1024];int len;while((len=is.read(bytes))!=-1){os.write(bytes,0,len);}os.close();is.close();}
}
3-3-1. 通过try-catch-finally来释放资源
package File_study;import java.io.*;public class File_study2 {public static void main(String[] args) {InputStream is = null;OutputStream os = null;// 实现文件的复制try{is = new FileInputStream("C:\\Users\\Administrator\\Pictures\\5.jpg");os = new FileOutputStream("2.jpg",true);byte[] bytes = new byte[1024*1024];int len;while((len=is.read(bytes))!=-1){os.write(bytes,0,len);}}catch (IOException e){e.printStackTrace();}finally {try {if(os != null)os.close();} catch (IOException e) {e.printStackTrace();}try {if(is != null)is.close();} catch (IOException e) {e.printStackTrace();}}}
}
3-3-2. 通过try-catch-resource来释放资源
通过这种方式,把需要释放的资源放到try()小括号里边,这样代码运行完之后就会自动释放。
package File_study;import java.io.*;public class File_study2 {public static void main(String[] args) {// 实现文件的复制try(InputStream is = new FileInputStream("C:\\Users\\Administrator\\Pictures\\5.jpg");OutputStream os = new FileOutputStream("3.jpg",true);){byte[] bytes = new byte[1024*1024];int len;while((len=is.read(bytes))!=-1){os.write(bytes,0,len);}}catch (IOException e){e.printStackTrace();}}
}
这里根本看不出它是否释放了资源,通过查询FileInputStream继承关系看看,发现最终它的close方法最终来自于接口AutoCloseable下的close方法。
为此我们可以定义一个类,让其实现AutoCloseable接口下的close方法来进行演示,看在小括号中这个自定义的类对象是否执行了close方法,如下:
package File_study;public class CloseTest implements AutoCloseable{@Overridepublic void close() throws Exception {System.out.println("在这里释放资源");}
}
package File_study;import java.io.*;public class File_study2 {public static void main(String[] args) {// 实现文件的复制try(InputStream is = new FileInputStream("C:\\Users\\Administrator\\Pictures\\5.jpg");OutputStream os = new FileOutputStream("3.jpg",true);CloseTest closeTest = new CloseTest();){byte[] bytes = new byte[1024*1024];int len;while((len=is.read(bytes))!=-1){os.write(bytes,0,len);}System.out.println(closeTest);}catch (Exception e){e.printStackTrace();}}
}
运行结果:
4. FileReader(文件字符输入流)
从文件中读取字符出来,常用的构造方法两种,一种写文件存储路径,第二种参数为File对象。
package File_study;import java.io.*;public class File_study3 {public static void main(String[] args) {try(Reader r = new FileReader("src/File_study/File_study2.java");){int c;while((c= r.read())!=-1){System.out.print((char) c);}}catch (IOException e){e.printStackTrace();}}
}
关于读取方法read,常用的有如下两种:
上述读取效率较低,为此,可以使用字符数组来存储读取到的字符。
package File_study;import java.io.*;public class File_study3 {public static void main(String[] args) {try(Reader r = new FileReader("src/File_study/File_study2.java");){int len;char[] chars = new char[10];while((len= r.read(chars))!=-1){System.out.print(new String(chars,0,len));// 读取多少,输出多少}}catch (IOException e){e.printStackTrace();}}
}
5. FileWriter(文件字符输出流)
把字符写入到文件中去。
常用的写入字符到文件中去的方法writer有如下几种:
package File_study;import java.io.*;public class File_study3 {public static void main(String[] args) {try(Writer w = new FileWriter("src/File_study/1.txt");){w.write('我');// 写入单个字符w.write("\r\n");w.write("我a你");// 写入字符串w.write("\r\n");char[] chars = {'我','a','你'};w.write(chars);// 写入字符数组w.write("\r\n");w.write("我a你",1,1);// 写入字符串 “a”w.write("\r\n");w.write(chars,1,1);// 写入字符数组 'a'}catch (IOException e){e.printStackTrace();}}
}
需要注意的是:字符输出流写出数据后,必须刷新流,或者关闭流,否则不能生效。 这是因为文件字符输入流它执行writer方法后,会先把数据先写入到缓冲区中去,然后再经过系统调用写入到文件中。所以在没有执行close方法或flush方法时,数据是没有写入到文件中去的,在执行close方法时会调用flush方法。
6. 缓冲流
对上述中的一些的原始流(文件字节输入流、文件字节输出流、文件字符输入流、文件字符输出流)进行了包装,以提高原始流的读写数据的性能。
6-1. 字节缓冲流
提高字节流读写的性能。原理:字节缓冲输入流自带8kb缓冲池;字节缓冲输出流也自带了8kb的缓冲池。把上述那个复制文件的代码修改为使用字节缓冲流的形式,如下:
package File_study;import java.io.*;public class File_study4 {public static void main(String[] args) {// 实现文件的复制try(InputStream is = new FileInputStream("C:\\Users\\Administrator\\Pictures\\5.jpg");InputStream bis = new BufferedInputStream(is);OutputStream os = new FileOutputStream("5.jpg",true);OutputStream bos = new BufferedOutputStream(os);){byte[] bytes = new byte[1024];int len;while((len=bis.read(bytes))!=-1){bos.write(bytes,0,len);}}catch (Exception e){e.printStackTrace();}}
}
查看字节缓冲流的源码可以看到的确有8kb的缓冲池,如下:
如果想设置缓冲池的大小,直接在字符缓冲流后加上参数即可。
InputStream bis = new BufferedInputStream(is,8192*2);
OutputStream bos = new BufferedOutputStream(os,8192*2);
6-2. 字符缓冲输入流
自带8kb(8192)的字符缓冲池,可以提高字符输入流读取字符数据的性能。
package File_study;import java.io.*;public class File_study5 {public static void main(String[] args) {try(Reader r = new FileReader("src/File_study/File_study3.java");BufferedReader br = new BufferedReader(r)){int len;char[] chars = new char[100];while((len=br.read(chars))!=-1){System.out.println(new String(chars,0,len));}}catch (IOException e){e.printStackTrace();}}
}
按照行来读取数据,使用方法readLine,如下:
package File_study;import java.io.*;public class File_study5 {public static void main(String[] args) {try(Reader r = new FileReader("src/File_study/File_study3.java");BufferedReader br = new BufferedReader(r)){String outcome;while((outcome=br.readLine())!=null){System.out.println(outcome);}}catch (IOException e){e.printStackTrace();}}
}
6-3. 字符缓冲输出流
自带8kb(8192)的字符缓冲池,可以提高字符输出流写字符数据的性能。
package File_study;import java.io.*;public class File_study6 {public static void main(String[] args) {try(Writer os = new FileWriter("3.txt");BufferedWriter bos = new BufferedWriter(os);){bos.write("爱我中华。。");bos.newLine();// 换行bos.write('爱');}catch(IOException e){e.printStackTrace();}}
}
7. 测试复制文件代码的性能
这里通过四种方式来测试,分别为使用一个一个字节来实现文件的复制、使用字节数组来实现文件的复制、使用缓冲流并一个一个字节来实现文件的复制、使用缓冲流并字节数组来实现文件的复制,参考代码如下:
package File_study;import java.io.*;public class File_study7 {private static final String FILE_PATH = "E:/Download/2.png";private static final String DIR = "E:/Download/";public static void main(String[] args) {copy1(FILE_PATH,DIR);copy2(FILE_PATH,DIR);copy3(FILE_PATH,DIR);copy4(FILE_PATH,DIR);}public static void copy1(String file_path,String DIR){long start = System.currentTimeMillis();try(InputStream is = new FileInputStream(file_path);OutputStream os = new FileOutputStream(DIR+"3.png");){int c;while((c=is.read())!=-1){os.write(c);}}catch (IOException e){e.printStackTrace();}long end = System.currentTimeMillis();System.out.println("通过一个一个字节来复制文件花费的时间为:"+(end-start)/1000.0+"s");}public static void copy2(String file_path,String DIR){long start = System.currentTimeMillis();try(InputStream is = new FileInputStream(file_path);OutputStream os = new FileOutputStream(DIR+"4.png");){byte[] bytes = new byte[1024];int len;while((len=is.read(bytes))!=-1){os.write(bytes,0,len);}}catch (IOException e){e.printStackTrace();}long end = System.currentTimeMillis();System.out.println("通过字节数组来复制文件花费的时间为:"+(end-start)/1000.0+"s");}public static void copy3(String file_path,String DIR){long start = System.currentTimeMillis();try(InputStream is = new FileInputStream(file_path);BufferedInputStream bis = new BufferedInputStream(is);OutputStream os = new FileOutputStream(DIR+"5.png");BufferedOutputStream bos = new BufferedOutputStream(os);){int c;while((c=bis.read())!=-1){bos.write(c);}}catch (IOException e){e.printStackTrace();}long end = System.currentTimeMillis();System.out.println("通过缓冲字符流、一个一个字节来复制文件花费的时间为:"+(end-start)/1000.0+"s");}public static void copy4(String file_path,String DIR){long start = System.currentTimeMillis();try(InputStream is = new FileInputStream(file_path);BufferedInputStream bis = new BufferedInputStream(is);OutputStream os = new FileOutputStream(DIR+"6.png");BufferedOutputStream bos = new BufferedOutputStream(os);){byte[] bytes = new byte[1024];int len;while((len=bis.read(bytes))!=-1){bos.write(bytes,0,len);}}catch (IOException e){e.printStackTrace();}long end = System.currentTimeMillis();System.out.println("通过缓冲流和字节数组来复制文件花费的时间为:"+(end-start)/1000.0+"s");}
}
运行结果如下:
这里没有运行copy1,效率太低了。如果把copy2中字节数组大小修改为1024*8(或者更大吧!),其他的不变,只运行copy2和copy4,可以发现它们两者的运行效率基本上差不了多少。
8. 转换流
用于解决不同编码读写时的乱码情况,这里有一段代码如下:
package File_study;import java.io.* ;public class File_study8 {public static void main(String[] args) {try(Reader r = new FileReader("3.txt");BufferedReader br = new BufferedReader(r)){String line;while((line=br.readLine())!=null){System.out.println(line);}}catch (IOException e){e.printStackTrace();}}
}
此时出现了乱码,因为这个文件的编码为gbk,而代码运行文件编码为utf-8。
8-1. 字符输入转换流(InputStreamReader)
解决思路:先获取文件的原始字节流,再将其按真实的字符集编码转换成字符输入流,这样字符输入流中的字符就不乱码了。
package File_study;import java.io.* ;public class File_study8 {public static void main(String[] args) {try(InputStream fis = new FileInputStream("3.txt");Reader r = new InputStreamReader(fis,"gbk");BufferedReader bfr = new BufferedReader(r);){String line;while((line= bfr.readLine())!=null){System.out.println(line);}}catch (IOException e){e.printStackTrace();}}
}
8-2. 字符输出转换流(OutputStreamWriter)
控制写出去的字符使用特定的字符集编码。
解决思路:先获取字节输出流,再将其按指定的字符集编码转换成字符输出流,之后写出去的字符就会使用该字符集编码了。
package File_study;import java.io.*;public class File_study9 {public static void main(String[] args) {try (OutputStream fos = new FileOutputStream("4.txt");Writer osw = new OutputStreamWriter(fos,"gbk");BufferedWriter bw = new BufferedWriter(osw);){bw.write("爱我中华");bw.write("我爱中国。。");} catch (IOException e) {e.printStackTrace();}}
}
9. 打印流(PrintStream/PrintWriter)
作用:更方便、更高效的把数据打印出去,能够实现打印啥出去就是啥出去
9-1.PrintStream
package File_study;import java.io.*;public class File_study10 {public static void main(String[] args) {try(PrintStream ps = new PrintStream("printStream.txt", "gbk");){ps.println("爱我中华");ps.print("1");ps.println();ps.print("我爱中国");}catch (IOException e){e.printStackTrace();}}
}
9-2. PrintWriter
package File_study;import java.io.*;public class File_study10 {public static void main(String[] args) {try(PrintWriter pw = new PrintWriter("printWriter.txt");){pw.println("爱我中华");pw.println("1");}catch (IOException e){e.printStackTrace();}}
}
9-3. 关于打印流的高效
直接查看源码,可以发现在它们的下面使用了缓冲流。
PrintStream
PrintWriter
9-4. 把系统默认的打印流修改为自定义的打印流
系统运行结果都会打印到控制台上,如果想把打印的数据写入的文件中去,可以使用打印流,如下:
package File_study;import java.io.*;public class File_study10 {public static void main(String[] args) {System.out.println("我爱中国!");try(PrintStream ps = new PrintStream("printstream1.txt");){System.setOut(ps);System.out.println("爱我中华");}catch (IOException e){e.printStackTrace();}}
}
10. 数据流
允许把数据和其类型一并写出去。
10-1. 数据输出流(DataOutputStream)
package File_study;import java.io.*;public class File_study11 {public static void main(String[] args) {try(OutputStream os = new FileOutputStream("dataoutputstream.txt");DataOutputStream dos = new DataOutputStream(os);){dos.write(1);dos.writeFloat(0.1f);dos.writeBoolean(false);dos.writeUTF("爱我中华2");}catch (IOException e){e.printStackTrace();}}
}
10-2. 数据输入流(DataInputStream)
package File_study;import java.io.*;public class File_study11 {public static void main(String[] args) {try(InputStream is = new FileInputStream("dataoutputstream.txt");DataInputStream dis = new DataInputStream(is);){int i = dis.read();float v = dis.readFloat();boolean b = dis.readBoolean();String s = dis.readUTF();System.out.println(i+" "+v+" "+b+" "+s);}catch (IOException e){e.printStackTrace();}}
}
数据是怎样写入的,就怎么读取出来。
11. 序列化流
11-1. 对象字节输出流 (ObjectOutputStream)
Java对象进行序列化:把Java对象存入到文件中去。
把对象序列化,需要把对象对应的类实现Serializable这个接口。
package File_study.enity;import java.io.Serializable;public class User implements Serializable {private String name;private Integer age;private char sex;public User(String name, Integer age, char sex) {this.name = name;this.age = age;this.sex = sex;}@Overridepublic String toString() {return "User{" +"name='" + name + '\'' +", age=" + age +", sex=" + sex +'}';}
}
package File_study;import File_study.enity.User;import java.io.*;public class File_study12 {public static void main(String[] args) {try(OutputStream fos = new FileOutputStream("object.txt");ObjectOutputStream oos = new ObjectOutputStream(fos);){User user = new User("张三", 20, 'm');oos.writeObject(user);}catch (IOException e){e.printStackTrace();}}
}
11-2. 对象字节输入流 (ObjectInputStream)
Java对象反序列化:从文件中读取Java对象。
package File_study;import File_study.enity.User;
import java.io.*;public class File_study12 {public static void main(String[] args) {try(InputStream fis = new FileInputStream("object.txt");ObjectInputStream ois = new ObjectInputStream(fis);){User o = (User) ois.readObject();System.out.println(o);}catch (Exception e){e.printStackTrace();}}
}