文件分为两种:文本文件,非文本文件(二进制文件)。
一般来说,我们的数据存储在两个地方:
程序-内存-变量—》只有程序运行的时候,才存在。
硬盘—》一直存在的,持久的。
把文件从硬盘读取到内存里面-》读取-》读入read-》输入-》Input-》输入流
从内存里面把数据保存到硬盘-》存储-》写入write-》输出-》Output-》输出流

文件和目录操作➝File

Java文件类以抽象的方式代表文件名和目录路径名。该类主要用于文件和目录的创建、文件的查找和文件的删除等。

构造方法

File对象代表磁盘中实际存在的文件和目录。通过以下构造方法创建一个File对象。

通过给定的父抽象路径名和子路径名字符串创建一个新的File实例。

  1. //父目录+子目录
  2. File f = new File(File parent,String child);

通过给定的父抽象路径名和子路径名字符串创建一个新的File实例。

  1. //完整目录
  2. File f = new File(String pathName);

根据 parent 路径名字符串和 child 路径名字符串创建一个新 File 实例。

  1. //父目录+子目录
  2. File f = new File(String parent,String child);

通过将给定的 file: URI 转换成一个抽象路径名来创建一个新的 File 实例。

  1. File f = new File(URI uri);

操作方法

创建File对象成功后,可以使用以下列表中的方法操作文件。

ID 方法名 方法描述
1 public String getName() 返回由此抽象路径名表示的文件或目录的名称。
2 public String getParent() 返回此抽象路径名的父路径名的路径名字符串,如果此路径名没有指定父目录,则返回 null。
3 public File getParentFile() 返回此抽象路径名的父路径名的抽象路径名,如果此路径名没有指定父目录,则返回 null。
4 public String getPath() 将此抽象路径名转换为一个路径名字符串。
5 public boolean isAbsolute() 测试此抽象路径名是否为绝对路径名。
6 public String getAbsolutePath() 返回抽象路径名的绝对路径名字符串。
7 public boolean canRead() 测试应用程序是否可以读取此抽象路径名表示的文件。
8 public boolean canWrite() 测试应用程序是否可以修改此抽象路径名表示的文件。
9 public boolean exists() 测试此抽象路径名表示的文件或目录是否存在。
10 public boolean isDirectory() 测试此抽象路径名表示的文件是否是一个目录。
11 public boolean isFile() 测试此抽象路径名表示的文件是否是一个标准文件。
12 public long lastModified() 返回此抽象路径名表示的文件最后一次被修改的时间。
13 public long length() 返回由此抽象路径名表示的文件的长度。
14 public boolean createNewFile() throws IOException 当且仅当不存在具有此抽象路径名指定的名称的文件时,原子地创建由此抽象路径名指定的一个新的空文件。
15 public boolean delete() 删除此抽象路径名表示的文件或目录。
16 public void deleteOnExit() 在虚拟机终止时,请求删除此抽象路径名表示的文件或目录。
17 public String[] list() 返回由此抽象路径名所表示的目录中的文件和目录的名称所组成字符串数组。
18 public String[] list(FilenameFilter filter) 返回包含在目录中的文件和目录的名称组成的字符串数组,此目录通过满足指定过滤器的抽象路径名来表示。
19 public File[] listFiles() 返回一个抽象路径名数组,这些路径名表示此抽象路径名所表示目录中的文件。
20 public File[] listFiles(FileFilter filter) 返回表示此抽象路径名所表示目录中的文件和目录的抽象路径名数组,这些路径名满足特定过滤器。
21 public boolean mkdir() 创建此抽象路径名指定的目录。
22 public boolean mkdirs() 创建此抽象路径名指定的目录,包括创建必需但不存在的父目录。
23 public boolean renameTo(File dest) 重新命名此抽象路径名表示的文件。
24 public boolean setLastModified(long time) 设置由此抽象路径名所指定的文件或目录的最后一次修改时间。
25 public boolean setReadOnly() 标记此抽象路径名指定的文件或目录,以便只可对其进行读操作。
26 public static File createTempFile(String prefix, String suffix, File directory) throws IOException 在指定目录中创建一个新的空文件,使用给定的前缀和后缀字符串生成其名称。
27 public static File createTempFile(String prefix, String suffix) throws IOException 在默认临时文件目录中创建一个空文件,使用给定前缀和后缀生成其名称。
28 public int compareTo(File pathname) 按字母顺序比较两个抽象路径名。
29 public int compareTo(Object o) 按字母顺序比较抽象路径名与给定对象。
30 public boolean equals(Object obj) 测试此抽象路径名与给定对象是否相等。
31 public String toString() 返回此抽象路径名的路径名字符串。

常用的一些方法:

  1. ------------操作方法------------
  2. boolean createNewFile()//创建文件
  3. boolean mkdir();//创建单个目录(不推荐)
  4. boolean mkdirs();//创建单个或多级目录(推荐)
  5. boolean delete();//删除文件或者文件夹
  6. boolean renameTo(File dest) //重命名文件
  7. ------------判断方法------------
  8. boolean isDirectory();//是否是目录
  9. boolean isFile();//是否是文件
  10. boolean exists();//是否存在
  11. boolean canRead();//是否可读
  12. boolean canWrite();//是否可写
  13. boolean isHidden();//是否隐藏
  14. ------------获取方法------------
  15. String getAbsolutePath();//获取文件在硬盘上的绝对路径名
  16. String getPath();//获取构造File对象时的文件路径名
  17. String getName();//获取路径表示的文件或目录名
  18. long length();//获取路径名长度
  19. long lastModified();//获取此文件最后被修改的视角
  20. String[] list();//返回目录下所有的文件和文件夹的名字
  21. File[] listFile();//返回目录下所有的文件和文件夹并转换成File数组(方便操作)

文件内容操作➝字节流和字符流

基本内容

文件内容的数据分为两种:文本数据, 二进制(字节)数据。
如果要进行文件内容的操作那么必须依靠数据流完成,而数据流分为两种:
  字节流:InputStream(字节输入流)、OutputStream(字节输出流)
  字符流:Reader(字符输入流)、Writer(字符输出流)

字节流理解配图
文件操作(IO流) - 图1
字符流理解配图
文件操作(IO流) - 图2

字节流与字符流操作的本质区别只有一个:字节流是原生的操作,而字符流是经过处理后的操作。

  • 在进行网络数据传输、磁盘数据保存所保存所支持的数据类型只有:字节。
  • 而所有磁盘中的数据必须先读取到内存后才能进行操作,而内存中会帮助我们把字节变为字符。字符更加适合处理中文。

不管使用的是字节流还是字符流,其基本的操作流程几乎是一样的,以文件操作为例。

  1. 创建File类对象 ,主要是指明要操作的文件路径;
  2. 根据字节流或字符流的子类实例化父类对象 ;
  3. 进行数据的读取或写入操作;
  4. 关闭流(close());

对于IO操作属于资源处理,所有的资源处理操作(IO操作、数据库操作、网络)最后必须要进行关闭。

如前所述,一个流被定义为一个数据序列。输入流用于从源读取数据,输出流用于向目标写数据。
下图是一个描述输入流和输出流的类层次图。
文件操作(IO流) - 图3

字节流

计算机中都是二进制数据,一个字节是8个2进制位。字节可以表示所有的数据,比如文本,音频,视频,图片,都是作为字节存在的。也就是说字节流处理的数据非常多。
在文本文件中存储的数据是以我们能读懂的方式表示的。而在二进制文件中存储的数据是用二进制形式表示的。我们是读不懂二进制文件的,因为二进制文件是为了让程序来读取而设计的。例如,Java的源程序(.java源文件)存储在文本文件中,可以使用文本编辑器阅读,但是Java的类(字节码文件)存储在二进制文件中,可以被Java虚拟机阅读。二进制文件的优势在于它的处理效率比文本文件高。
我们已经知道File对象封装的是文件或者路径属性,但是不包含向(从)文件读(写)数据的方法。为了实现对文件的读和写操作需要学会正确的使用Java的IO创建对象。

字节流的抽象基类:
输入流:java.io.InputStream
输出流:java.io.OutputStream

特点:字节流的抽象基类派生出来的子类名称都是以其父类名作为子类名的后缀。如:FileInputStream, ByteArrayInputStream等。
说明:字节流处理的单元是一个字节,用于操作二进制文件(计算机中所有文件都是二进制文件)。

总的来说,字节流主要是操作byte类型数据,以byte数组为准,主要操作类时字节输入流InputStream、字节输出流OutputStream。

字节输入流➝InputStream

InputStream可以实现数据的读取操作,在java中InputStream类的定义如下:

  1. public abstract class InputStream implements Closeable

InputStream类中定义有三个数据的读取操作方法:
1、读取单个字节:public abstract int read() throws IOException;
每次执行此方法将读取当个字节数据,如果已经读取完成了,那么最后返回-1。
文件操作(IO流) - 图4
2、读取数据到字节数组中:public int read(byte b[]) throws IOException;(最常用方法)
每次将数据读取到数组之中,那么会返回一个读取长度的数据,如果没有数据则返回的长度为-1,可是要考虑两种情况:
要读取的内容大于开辟的数组内容:长度就是整个数组的长度。
要读取的内容小于开辟数组的内容:长度就是全部最后的内容长度,数组装不满。

3、读取部分内容到字节数组中:public int read(byte b[], int off,int len) throws IOException;
每次读取内容到部分字节数组,只允许读取满限制的数组的字节个数。此方法依然会返回读取的长度。

InputStream是一个抽象类,所以要进行文件的读取使用FileInputStream子类。

文件输入流➝FileInputStream

该流用于从文件读取数据,它的对象可以用关键字 new 来创建。
有多种构造方法可用来创建对象。

可以使用字符串类型的文件名来创建一个输入流对象来读取文件:

  1. InputStream f = new FileInputStream("C:/java/hello");

也可以使用一个文件对象来创建一个输入流对象来读取文件。我们首先得使用 File() 方法来创建一个文件对象:

  1. File f = new File("C:/java/hello");
  2. InputStream input = new FileInputStream(f);

创建了InputStream对象,就可以使用下面的方法来读取流或者进行其他的流操作。

ID 方法名 方法描述
1 public void close() throws IOException{} 关闭此文件输入流并释放与此流有关的所有系统资源。抛出IOException异常。
2 protected void finalize() throws IOException {} 这个方法清除与该文件的连接。确保在不再引用文件输入流时调用其 close 方法。抛出IOException异常。
3 public int read(int r) throws IOException{} 从 InputStream 对象读取指定字节的数据。返回为整数值。返回下一字节数据,如果已经到结尾则返回-1。
4 public int read(byte[] r) throws IOException{} 这个方法从输入流读取r.length(一般设置1024)长度的字节。返回读取的字节数。如果是文件结尾则返回-1。
5 public int available() throws IOException{} 返回下一次对此输入流调用的方法可以不受阻塞地从此输入流读取的字节数。返回一个整数值。

示例:

  1. @Test
  2. public void testFileInputStream() {
  3. FileInputStream input = null;
  4. try {
  5. input = new FileInputStream("TestIO/demo01.txt");
  6. // //1,读取一个字节
  7. // int a =-1;
  8. // while( (a = input.read())>-1 ) {
  9. // System.out.print((char)a);//中文不止占有一个字节,不能拿一个字节来读,会出现乱码
  10. // }
  11. // // 2,按照字节数组 1024
  12. // byte[] data = new byte[4];
  13. // int length = -1;
  14. // while ((length = input.read(data)) > -1) {
  15. // String str = new String(data, 0, length);
  16. // System.out.print(str);
  17. // }
  18. // // 3,按照字节数组 1024(推荐)
  19. // byte[] data = new byte[1024];
  20. // int length = input.read(data);
  21. // String str = new String(data,0,length);
  22. // System.out.println(str);
  23. } catch (IOException e) {
  24. e.printStackTrace();
  25. } finally {
  26. try {
  27. if (input != null)
  28. input.close();// 释放资源
  29. } catch (IOException e) {
  30. e.printStackTrace();
  31. }
  32. }
  33. }

字节输出流➝OutputStream

字节输出流主要以操作byte数据为主,观察java.io.OutputStream类的定义结构:

  1. public abstract class OutputStream implements Closeable, Flushable

OutputStream类实现了Closeable,Flushable两个接口,这两个接口中的方法:
 1. Closeable: public void close() throws IOException;
 2. Flushable: public void flush() throws IOException;
文件操作(IO流) - 图5
OutputStream类定义有三个重要的输出操作方法:
1、将给定的字节数组内容全部输出:public void write(byte b[]) throws IOException;
2、将部分字节数组内容输出:public void write(byte b[], int off, int len) throws IOException(重点)
3、输出单个字节:public abstract void write(int b) throws IOException;

OutputStream是一个抽象类,按照抽象类的基本原则来讲,如果想要取得OutputStream类的实例化对象那么一定需要子类,如果要进行文件的操作,可以使用FileOutputStream类来处理。

文件输出流➝FileOutputStream

该类用来创建一个文件并向文件中写数据。
如果该流在打开文件进行输出前,目标文件不存在,那么该流会创建该文件。

有两类构造方法可以用来创建 FileOutputStream 对象。
1、接收File类(覆盖):public FileOutputStream(File file) throws FileNotFoundException
2、接收File类(追加):public FileOutputStream(File file, boolean append)

使用字符串类型的文件名来创建一个输出流对象(覆盖):

  1. OutputStream f = new FileOutputStream("C:/java/hello")

也可以使用一个文件对象来创建一个输出流来写文件。我们首先得使用File()方法来创建一个文件对象(覆盖):

  1. File f = new File("C:/java/hello");
  2. OutputStream out = new FileOutputStream(f);

使用字符串类型的文件名来创建一个输出流对象(追加):

  1. OutputStream f = new FileOutputStream("C:/java/hello",true)

也可以使用一个文件对象来创建一个输出流来写文件。我们首先得使用File()方法来创建一个文件对象(追加):

  1. File f = new File("C:/java/hello");
  2. OutputStream out = new FileOutputStream(f,true);

创建OutputStream 对象完成后,就可以使用下面的方法来写入流或者进行其他的流操作。

ID 方法名 方法描述
1 public void close() throws IOException{} 关闭此文件输入流并释放与此流有关的所有系统资源。抛出IOException异常。
2 protected void finalize()throws IOException {} 这个方法清除与该文件的连接。确保在不再引用文件输入流时调用其 close 方法。抛出IOException异常。
3 public void write(int w)throws IOException{} 这个方法把指定的字节写到输出流中。
4 public void write(byte[] w) 把指定数组中w.length(一般设置1024)长度的字节写到OutputStream中。

示例:

  1. @Test
  2. public void testFileOutputStream() {
  3. FileOutputStream output = null;
  4. try {
  5. //output = new FileOutputStream("TestIO/demo02.txt");
  6. output = new FileOutputStream(new File("TestIO/demo02.txt"));
  7. // 按照单个字节写入
  8. // output.write('v');
  9. // output.write('d');
  10. // output.write('u');
  11. // 按照字节数组写入(推荐)
  12. String str = "hello";
  13. output.write(str.getBytes(), 1, 4);
  14. } catch (IOException e) {
  15. e.printStackTrace();
  16. } finally {
  17. try {
  18. if (output != null)
  19. output.close();
  20. } catch (IOException e) {
  21. e.printStackTrace();
  22. }
  23. }
  24. }

字节缓冲区输入/输出流(用来做包装)

上述程序中我们为了提高流的使用效率,自定义了字节数组,作为缓冲区,Java其实提供了专门的字节流缓冲来提高效率。
BufferedInputStream和BufferedOutputStream
BufferedOutputStream和BufferedOutputStream类可以通过减少读写次数来提高输入和输出的速度。它们内部有一个缓冲区,用来提高处理效率。查看API文档,发现可以指定缓冲区的大小。其实内部也是封装了字节数组。没有指定缓冲区大小,默认的字节是8192。
显然缓冲区输入流和缓冲区输出流要配合使用。首先缓冲区输入流会将读取到的数据读入缓冲区,当缓冲区满时,或者调用flush方法,缓冲输出流会将数据写出。
注意:当然使用缓冲流来进行提高效率时,对于小文件可能看不到性能的提升。但是文件稍微大一些的话,就可以看到实质的性能提升了。具体看案例

缓冲区输入流➝BufferedInputStream

使用一个InputStream对象来创建一个缓冲区输入流对象读取文件

  1. InputStream fileInput = new FileInputStream("TestIO\\demo01.txt");
  2. BufferedInputStream input = new BufferedInputStream(fileInput);

使用方法和FileInputStream完全一样。
示例:

  1. @Test
  2. public void testBufferedInputStream() {
  3. //缓冲区输入流 = 输入流 + 缓冲区(数组)
  4. BufferedInputStream input = null;
  5. FileInputStream fileInput;
  6. try {
  7. fileInput = new FileInputStream("TestIO\\demo01.txt");
  8. input = new BufferedInputStream(fileInput);
  9. // //1,读取一个字节
  10. // int a =-1;
  11. // while( (a = input.read())>-1 ) {
  12. // System.out.print((char)a);//中文不止占有一个字节,不能拿一个字节来读,会出现乱码
  13. // }
  14. // // 2,按照字节数组 1024(常用)
  15. // byte[] data = new byte[1024];
  16. // int length = input.read(data);
  17. // String str = new String(data,0,length);
  18. // System.out.println(str);
  19. } catch (IOException e) {
  20. e.printStackTrace();
  21. } finally {
  22. try {
  23. if (input != null)
  24. input.close();// 释放资源
  25. } catch (IOException e) {
  26. e.printStackTrace();
  27. }
  28. }
  29. }

缓冲区输出流➝BufferedOutputStream

使用一个OutputStream对象来创建一个缓冲区输出流对象写文件

  1. OutputStream fileOutput = new FileOutputStream("TestIO\\demo03.txt");
  2. BufferedOutputStream output = new BufferedOutputStream(fileOutput);

使用方法和FileOutputStream完全一样。
示例:

  1. @Test
  2. public void testBufferedOutputStream() {
  3. //缓冲区输出流 = 输出流 + 缓冲区(数组)
  4. BufferedOutputStream output = null;
  5. FileOutputStream fileOutput;
  6. try {
  7. fileOutput = new FileOutputStream("TestIO\\demo03.txt");
  8. output = new BufferedOutputStream(fileOutput);
  9. // 按照单个字节写入
  10. // 缓冲区里面的数据什么时候会写到硬盘上呢
  11. // 1,缓冲区满了
  12. // 2,调用flush
  13. // 3,调用close
  14. // output.write('a');
  15. // output.write('s');
  16. // output.write('i');
  17. // output.write('a');
  18. // output.write('s');
  19. // output.write('z');
  20. // 按照字节数组写入
  21. String str = "hello中国";
  22. output.write(str.getBytes());
  23. output.flush();
  24. } catch (IOException e) {
  25. // TODO Auto-generated catch block
  26. e.printStackTrace();
  27. } finally {
  28. try {
  29. if (output != null)
  30. output.close();
  31. } catch (IOException e) {
  32. // TODO Auto-generated catch block
  33. e.printStackTrace();
  34. }
  35. }
  36. }

案例

使用文件字节输入/输出流复制文件

按照字节数组进行复制的效率远远高于按照单个字节进行复制。

  1. //使用FileInputStream和FileOutputStream
  2. @Test
  3. public void testCopy() {
  4. long start = System.currentTimeMillis();
  5. copyByByte("TestIO\\5m.jpg", "TestIO\\copy01.jpg");
  6. System.out.println("copyByByte所花费的时间:" + (System.currentTimeMillis()-start) + "ms");//copyByByte所花费的时间:64935ms
  7. start = System.currentTimeMillis();
  8. copyByByteArray("TestIO\\5m.jpg", "TestIO\\copy02.jpg");
  9. System.out.println("copyByByteArray所花费的时间:" + (System.currentTimeMillis()-start) + "ms");//copyByByteArray所花费的时间:72ms
  10. }
  11. //按照字节数组进行复制(推荐)
  12. public static void copyByByteArray(String sourceName,String targetName) {
  13. FileInputStream input = null;
  14. FileOutputStream output = null;
  15. try {
  16. input = new FileInputStream(sourceName);
  17. output = new FileOutputStream(targetName);
  18. byte[] data = new byte[1024];
  19. int length = -1;
  20. while( (length = input.read(data))>-1 ) {
  21. output.write(data,0,length);
  22. }
  23. } catch (IOException e) {
  24. e.printStackTrace();
  25. } finally {
  26. try {
  27. if(input!=null)
  28. input.close();
  29. } catch (IOException e) {
  30. e.printStackTrace();
  31. }
  32. try {
  33. if(output!=null)
  34. } catch (IOException e) {
  35. e.printStackTrace();
  36. }
  37. }
  38. }
  39. //按照单个字节进行复制
  40. public static void copyByByte(String sourceName,String targetName) {
  41. FileInputStream input = null;
  42. FileOutputStream output = null;
  43. try {
  44. input = new FileInputStream(sourceName);
  45. output = new FileOutputStream(targetName);
  46. int data = -1;
  47. while( (data = input.read())>-1 ) {
  48. output.write(data);
  49. }
  50. } catch (IOException e) {
  51. e.printStackTrace();
  52. } finally {
  53. try {
  54. if(input!=null)
  55. input.close();
  56. } catch (IOException e) {
  57. e.printStackTrace();
  58. }
  59. try {
  60. if(output!=null)
  61. output.close();
  62. } catch (IOException e) {
  63. e.printStackTrace();
  64. }
  65. }
  66. }

使用缓冲区字节输入/输出流复制文件

两者效率差的不大,但是和文件输入/输出流性能差距还是很明显的,特别是按照单个字节进行复制。

  1. 使用BufferedInputStreamBufferedOutputStream
  2. @Test
  3. public void testCopy() {
  4. long start = System.currentTimeMillis();
  5. copyByByte("TestIO\\5m.jpg", "TestIO\\copy01.jpg");
  6. System.out.println("copyByByte所花费的时间:" + (System.currentTimeMillis()-start) + "ms");//copyByByte所花费的时间:44ms
  7. start = System.currentTimeMillis();
  8. copyByByteArray("TestIO\\5m.jpg", "TestIO\\copy02.jpg");
  9. System.out.println("copyByByteArray所花费的时间:" + (System.currentTimeMillis()-start) + "ms");//copyByByteArray所花费的时间:23ms
  10. }
  11. //按照字节数组进行复制(推荐)
  12. public static void copyByByte(String sourceName,String targetName) {
  13. BufferedInputStream input = null;
  14. BufferedOutputStream output = null;
  15. try {
  16. input = new BufferedInputStream(new FileInputStream(sourceName));
  17. output = new BufferedOutputStream(new FileOutputStream(targetName));
  18. int data = -1;
  19. while( (data = input.read())!=-1 ) {
  20. output.write(data);
  21. }
  22. } catch (IOException e) {
  23. e.printStackTrace();
  24. } finally {
  25. try {
  26. if(input!=null)
  27. input.close();
  28. } catch (IOException e) {
  29. e.printStackTrace();
  30. }
  31. try {
  32. if(output!=null)
  33. output.close();
  34. } catch (IOException e) {
  35. e.printStackTrace();
  36. }
  37. }
  38. }
  39. //按照单个字节进行复制
  40. public static void copyByByteArray(String sourceName,String targetName) {
  41. BufferedInputStream input = null;
  42. BufferedOutputStream output = null;
  43. try {
  44. input = new BufferedInputStream(new FileInputStream(sourceName));
  45. output = new BufferedOutputStream(new FileOutputStream(targetName));
  46. byte[] data = new byte[1024];
  47. int length = -1;
  48. while( (length = input.read(data))!=-1 ) {
  49. output.write(data, 0, length);
  50. }
  51. } catch (IOException e) {
  52. e.printStackTrace();
  53. } finally {
  54. try {
  55. if(input!=null)
  56. input.close();
  57. } catch (IOException e) {
  58. e.printStackTrace();
  59. }
  60. try {
  61. if(output!=null)
  62. output.close();
  63. } catch (IOException e) {
  64. e.printStackTrace();
  65. }
  66. }
  67. }

字符流

字符流:就是在字节流的基础上,加上编码,形成的数据流。
字符流出现的意义:不用编码下,中文字符所占有的字节个数是不一样的。而字节流在操作字符时,可能会有中文导致的乱码,所以由字节流引申出了字符流。

字符流的抽象基类:
输入流:java.io.Reader
输出流:java.io.Writer

总的来说,字符流主要是操作字符类型数据,以char数组为准,主要操作类时字符输入流Reader、字符输出流Writer。

关于编码

编码是用来表示如何把字符使用数字存储起来的。(比如一个字符占几个字节,判断这个字节里面存储的是中文字符还是英文字符)
一个字符对应一个数字。几乎所有由文字和字符的地方,都有编码的存在!
数据一般是转换为字节来存储或者传输,我们拿到字节后,要通过编码来转换成人眼可以识别的字符。
字节和字符之间的转换,需要通过固定的编码来完成。
Java中的编码格式:https://www.cnblogs.com/yuan1164345228/p/6937958.html
常见的有ASCII,GB2312,GBK,UTF-8。Java里面默认的编码为GBK。

在Java的文件操作中,会涉及到三种编码:文件设置的编码,程序读取的编码,程序写入的编码。

字符输入流➝Reader

Reader是进行字符输入操作使用的类,这个类属于抽象类,要进行文件字符流操作,一般使用FileReader。

FileReader

FileReader类从InputStreamReader类继承而来。该类按字符读取流中数据。可以通过以下几种构造方法创建需要的对象。

在给定从中读取数据的 File 的情况下创建一个新 FileReader。

  1. FileReader(File file)

在给定从中读取数据的 FileDescriptor 的情况下创建一个新 FileReader。

  1. FileReader(FileDescriptor fd)

在给定从中读取数据的文件名的情况下创建一个新 FileReader(最常用)。

  1. FileReader(String fileName)

创建FIleReader对象成功后,可以参照以下列表里的方法操作文件。

ID 方法名 方法描述
1 public int read() throws IOException 读取单个字符,返回一个int型变量代表读取到的字符
2 public int read(char [] c, int offset, int len) 读取字符到c数组,返回读取到字符的个数

关于FileReader的父类InputStreamReader

  1. //构造方法
  2. InputStreamReader(InputStream stream);
  3. InputStreamReader(InputStream stream,String charsetName);
  4. //For example-InputStreamReader
  5. FileInputStream fileinput = new FileInputStream("TestIO\\demo04.txt");
  6. InputStreamReader reader = new InputStreamReader(fileinput);
  7. //For example-FileReader
  8. FileReader reader = new FileReader("TestIO\\demo04.txt");

两者使用方式完全一模一样,不过FileReader对InputStreamReader的构造方法做了一层封装,使用起来更方便。


示例:

  1. @Test
  2. public void testFileReader() {
  3. FileReader reader = null;
  4. try {
  5. reader = new FileReader("TestIO\\demo04.txt");
  6. //按照字符数组读 1024(常用)
  7. char[] data = new char[1024];
  8. int length = reader.read(data);
  9. String str = new String(data,0,length);
  10. System.out.println(str);
  11. } catch (IOException e) {
  12. e.printStackTrace();
  13. }finally {
  14. try {
  15. if(reader!=null)
  16. reader.close();
  17. } catch (IOException e) {
  18. e.printStackTrace();
  19. }
  20. }
  21. }

字符输出流➝Writer

Writer是进行字符输出操作使用的类,这个类属于抽象类,要进行文件字符流操作,一般使用FileWriter。

FileWriter

FileWriter 类从 OutputStreamWriter 类继承而来。该类按字符向流中写入数据。可以通过以下几种构造方法创建需要的对象。

在给出 File 对象的情况下构造一个 FileWriter 对象。

  1. FileWriter(File file)

在给出 File 对象的情况下构造一个 FileWriter 对象。

  1. FileWriter(File file, boolean append)

参数:

  • file:要写入数据的 File 对象。
  • append:如果 append 参数为 true,则将字节写入文件末尾处,相当于追加信息。如果 append 参数为 false, 则写入文件开始处。

构造与某个文件描述符相关联的 FileWriter 对象。

  1. FileWriter(FileDescriptor fd)

在给出文件名的情况下构造 FileWriter 对象,它具有指示是否挂起写入数据的 boolean 值(最常用)。

  1. FileWriter(String fileName, boolean append)

创建FileWriter对象成功后,可以参照以下列表里的方法操作文件。

ID 方法名 方法描述
1 public void write(int c) throws IOException 写入单个字符c。
2 public void write(char [] c, int offset, int len) 写入字符数组中开始为offset长度为len的某一部分。
3 public void write(String s, int offset, int len) 写入字符串中开始为offset长度为len的某一部分。

关于FileWriter的父类OutputStreamWriter

  1. //构造方法
  2. OutputStreamWriter(OutputStream stream);
  3. OutputStreamWriter(OutputStream stream,String charsetName);
  4. //For example-OutputStreamWriter
  5. FileOutputStream fileOutput = new FileOutputStream("TestIO\\demo04.txt");
  6. OutputStreamWriter writer = new OutputStreamWriter(fileOutput);
  7. //For example-FileWriter
  8. FileWriter writer = new FileWriter("TestIO\\demo04.txt");

两者使用方式完全一模一样,不过FileWriter对OutputStreamWriter的构造方法做了一层封装,使用起来更方便。


示例:

  1. @Test
  2. public void testFileWriter() {
  3. //FileWriter按照默认编码 GBK来
  4. FileWriter writer = null;
  5. try {
  6. writer = new FileWriter("TestIO\\demo04.txt");
  7. writer.write("Hello中国");
  8. writer.flush();
  9. } catch (IOException e) {
  10. e.printStackTrace();
  11. } finally {
  12. try {
  13. if(writer!=null)
  14. writer.close();
  15. } catch (IOException e) {
  16. e.printStackTrace();
  17. }
  18. }
  19. }

注意:FileWriter 能调用flush方法,说明其实它也使用到了缓冲区。

字符缓冲区输入/输出流(用来做包装)

同字节缓冲区输入/输出流,字符也有自己的缓冲区输入/输出流。

缓冲区输入流➝BufferedReader

使用一个Reader对象来创建一个缓冲区输入流对象读取文件

  1. FileReader fileReader = new FileReader("TestIO\\demo04.txt");
  2. BufferedReader reader = new BufferedReader(fileReader);

使用方法和FileReader一样。
示例:

  1. @Test
  2. public void testBufferedReader() {
  3. FileReader fileReader = null;
  4. BufferedReader reader = null;
  5. try {
  6. fileReader = new FileReader("TestIO\\demo04.txt");
  7. reader = new BufferedReader(fileReader);
  8. //1,读取一个字符
  9. // int a =-1;
  10. // while( (a = reader.read())>-1 ) {
  11. // System.out.print((char)a);//这里读的是字符不是字节,所以能识别中文。但是程序读取的编码必须和文件设置的编码一致!!
  12. // }
  13. // 2,按照字符数组(常用)
  14. // char[] data = new char[1024];
  15. // int length = reader.read(data);
  16. // String str = new String(data,0,length);
  17. // System.out.println(str);
  18. /*
  19. * 3,BufferedReader提供了按行读取方法:
  20. * String readLine()
  21. * 连续读取若干字符,直到读取到换行符为止
  22. * 并将换行符之间读取到的字符以一个字符串返回
  23. * 若返回值为NULL,则表示读取到末尾。
  24. * 注意:该字符串不包含最后的换行符。
  25. *
  26. */
  27. // String line = null;
  28. // while((line = reader.readLine())!=null){
  29. // System.out.println(line);
  30. // }
  31. } catch (IOException e) {
  32. e.printStackTrace();
  33. } finally {
  34. try {
  35. if(reader!=null)
  36. reader.close();
  37. } catch (IOException e) {
  38. e.printStackTrace();
  39. }
  40. }
  41. }

缓冲区输出流➝BufferedWriter

使用一个Writer对象来创建一个缓冲区输出流对象写文件

  1. FileWriter fileWriter = new FileWriter("TestIO\\demo04.txt");
  2. BufferedWriter writer = new BufferedWriter(fileWriter);

使用方法和FileWriter完全一样。
示例:

  1. //字节流的缓冲区流 包装的是 字节流
  2. //字符流的缓冲区流 包装的是 字符流
  3. @Test
  4. public void testBufferedWriter() {
  5. BufferedWriter writer = null;
  6. FileWriter fileWriter = null;
  7. try {
  8. fileWriter = new FileWriter("TestIO\\demo05.txt");
  9. writer = new BufferedWriter(fileWriter);
  10. // 按照单个字符写入
  11. // 缓冲区里面的数据什么时候会写到硬盘上呢
  12. // 1,缓冲区满了
  13. // 2,调用flush
  14. // 3,调用close
  15. // writer.write('a');
  16. // writer.write('s');
  17. // writer.write('i');
  18. // writer.write('a');
  19. // writer.write('s');
  20. // writer.write('z');
  21. // 按照字符串写入
  22. String str = "hello中国";
  23. writer.write(str);
  24. writer.flush();
  25. } catch (IOException e) {
  26. e.printStackTrace();
  27. } finally {
  28. try {
  29. if(writer!=null)
  30. writer.close();
  31. } catch (IOException e) {
  32. e.printStackTrace();
  33. }
  34. }
  35. }

案例

使用文件字符输入/输出流复制文件

按照字符数组进行复制的效率高于按照单个字符进行复制,但没有字节那么夸张。

  1. //使用FileReader和FileWriter
  2. @Test
  3. public void testCopy() {
  4. long start = System.currentTimeMillis();
  5. copyByChar("TestIO\\5m.jpg", "TestIO\\copy01.jpg");
  6. System.out.println("copyByChar所花费的时间:" + (System.currentTimeMillis()-start) + "ms");//copyByChar所花费的时间:421ms
  7. start = System.currentTimeMillis();
  8. copyByCharArray("TestIO\\5m.jpg", "TestIO\\copy02.jpg");
  9. System.out.println("copyByCharArray所花费的时间:" + (System.currentTimeMillis()-start) + "ms");//copyByCharArray所花费的时间:96ms
  10. }
  11. public void copyByChar(String sourceName,String targetName) {
  12. FileWriter writer = null;
  13. FileReader reader = null;
  14. try {
  15. writer = new FileWriter(targetName);
  16. reader = new FileReader(sourceName);
  17. int data = -1;
  18. while( (data=reader.read())!=-1 ) {
  19. writer.write(data);
  20. }
  21. writer.flush();
  22. } catch (IOException e) {
  23. e.printStackTrace();
  24. } finally {
  25. try {
  26. if(writer!=null)
  27. writer.close();
  28. } catch (IOException e) {
  29. e.printStackTrace();
  30. }
  31. try {
  32. if(reader!=null)
  33. reader.close();
  34. } catch (IOException e) {
  35. e.printStackTrace();
  36. }
  37. }
  38. }
  39. public void copyByCharArray(String sourceName,String targetName) {
  40. FileWriter writer = null;
  41. FileReader reader = null;
  42. try {
  43. writer = new FileWriter(targetName);
  44. reader = new FileReader(sourceName);
  45. char[] data = new char[1024];
  46. int length = -1;
  47. while( (length = reader.read(data))!=-1 ) {
  48. writer.write(data, 0, length);
  49. }
  50. writer.flush();
  51. } catch (IOException e) {
  52. e.printStackTrace();
  53. } finally {
  54. try {
  55. if(writer!=null)
  56. writer.close();
  57. } catch (IOException e) {
  58. e.printStackTrace();
  59. }
  60. try {
  61. if(reader!=null)
  62. reader.close();
  63. } catch (IOException e) {
  64. e.printStackTrace();
  65. }
  66. }
  67. }

使用缓冲区字符输入/输出流复制文件

两者效率差不多。

  1. //使用BufferedReader和BufferedWriter
  2. @Test
  3. public void testCopy() {
  4. long start = System.currentTimeMillis();
  5. copyByChar("TestIO\\5m.jpg", "TestIO\\copy01.jpg");
  6. System.out.println("copyByChar所花费的时间:" + (System.currentTimeMillis()-start) + "ms");//copyByChar所花费的时间:221ms
  7. start = System.currentTimeMillis();
  8. copyByCharArray("TestIO\\5m.jpg", "TestIO\\copy02.jpg");
  9. System.out.println("copyByCharArray所花费的时间:" + (System.currentTimeMillis()-start) + "ms");//copyByCharArray所花费的时间:93ms
  10. }
  11. public void copyByChar(String sourceName,String targetName) {
  12. BufferedReader reader=null;
  13. BufferedWriter writer = null;
  14. try {
  15. reader = new BufferedReader(new FileReader(sourceName));
  16. writer = new BufferedWriter(new FileWriter(targetName));
  17. int data = -1;
  18. while( (data = reader.read())!=-1 ) {
  19. writer.write(data);
  20. }
  21. writer.flush();
  22. } catch (IOException e) {
  23. // TODO Auto-generated catch block
  24. e.printStackTrace();
  25. } finally {
  26. try {
  27. if(writer!=null)
  28. writer.close();
  29. } catch (IOException e) {
  30. // TODO Auto-generated catch block
  31. e.printStackTrace();
  32. }
  33. try {
  34. if(reader!=null)
  35. reader.close();
  36. } catch (IOException e) {
  37. // TODO Auto-generated catch block
  38. e.printStackTrace();
  39. }
  40. }
  41. }
  42. public void copyByCharArray(String sourceName,String targetName) {
  43. BufferedReader reader=null;
  44. BufferedWriter writer = null;
  45. try {
  46. reader = new BufferedReader(new FileReader(sourceName));
  47. writer = new BufferedWriter(new FileWriter(targetName));
  48. char[] data = new char[1024];
  49. int length = -1;
  50. while( (length = reader.read(data))!=-1 ) {
  51. writer.write(data, 0, length);
  52. }
  53. writer.flush();
  54. } catch (IOException e) {
  55. // TODO Auto-generated catch block
  56. e.printStackTrace();
  57. } finally {
  58. try {
  59. if(writer!=null)
  60. writer.close();
  61. } catch (IOException e) {
  62. // TODO Auto-generated catch block
  63. e.printStackTrace();
  64. }
  65. try {
  66. if(reader!=null)
  67. reader.close();
  68. } catch (IOException e) {
  69. // TODO Auto-generated catch block
  70. e.printStackTrace();
  71. }
  72. }
  73. }

读取文本文件里面的内容,按照单行内容,放到集合里面。

  1. @Test
  2. public void test03() {
  3. ArrayList<String> list = new ArrayList<String>();
  4. BufferedReader reader = null;
  5. try {
  6. reader = new BufferedReader(new FileReader("TestIO\\demo05.txt"));
  7. String data = null;
  8. while( (data=reader.readLine())!=null ) {
  9. list.add(data);
  10. }
  11. } catch (IOException e) {
  12. e.printStackTrace();
  13. } finally {
  14. try {
  15. if(reader!=null)
  16. reader.close();
  17. } catch (IOException e) {
  18. e.printStackTrace();
  19. }
  20. }
  21. for (String string : list) {
  22. System.out.println(string);
  23. }
  24. }

根据两个文件夹名称,将A文件夹内容全部拷贝到B文件夹(不包含A文件夹里面的子文件夹)

  1. @Test
  2. public void test04() {
  3. File sourceFolder=null;
  4. File targetFolder=null;
  5. sourceFolder = new File("D:\\Test");
  6. targetFolder = new File("D:\\Test2");
  7. File[] files = sourceFolder.listFiles();
  8. for(File file : files) {
  9. if(file.isDirectory())continue;
  10. File newFile = new File(targetFolder,file.getName());
  11. try {
  12. Files.copy(file.toPath(), newFile.toPath());
  13. } catch (IOException e) {
  14. e.printStackTrace();
  15. }
  16. }
  17. }

复制一个文件夹里面的所有内容到另外一个文件夹,包括里面的子文件夹

  1. @Test
  2. public void test05() {
  3. copy(new File("d:\\Test"),new File("Test2"));
  4. }
  5. public void copy(File sourceFolder,File targetFolder) {
  6. if(targetFolder.exists()==false)targetFolder.mkdirs();
  7. File[] files = sourceFolder.listFiles();
  8. for(File file : files) {
  9. if(file.isDirectory()) {
  10. File newFile = new File(targetFolder,file.getName());
  11. copy(file,newFile);
  12. }else {
  13. File newFile = new File(targetFolder,file.getName());
  14. try {
  15. Files.copy(file.toPath(), newFile.toPath());
  16. } catch (IOException e) {
  17. e.printStackTrace();
  18. }
  19. }
  20. }
  21. }

字节流与字符流的区别

字节流在操作的时候本身不用缓存区的,是与文件直接对接的;而字符流操作时需要缓冲区。

文件操作(IO流) - 图6
使用字节流时,即使流没有关闭,最终也可以输出到文件;
使用字符流时,所有的内容实际上都只是输出到了缓冲区中(内存)。在使用close()方法关闭的时候会将我们缓冲区的数据进行输出,如果没有关闭,那么就将无法进行输出,此时可以利用flush()方法进行强制的输出。
如果处理中文使用字符流,其他的任何数据都使用字节流。