摘要:本文学习了如何使用输入输出流对文件进行操作。
环境
Windows 10 企业版 LTSC 21H2
Java 1.8
1 文件
1.1 概念
File类是数据源的一种,可以表示一个文件,也可以表示一个文件目录。
File类只能对文件和目录进行创建和删除等操作,可以查看文件和目录的属性,不能读取或修改内容。如果需要读取或修改文件的内容,需要使用输入输出流。
常常将File类的对象作为参数传递到输入输出流的类的构造器中。
绝对路径和相对路径:
- 相对路径:相对路径名必须使用取自其他路径名的信息进行解释。
- 绝对路径:绝对路径名是完整的路径名,不需要任何其他信息就可以定位它所表示的文件或目录。
相对路径创建的实例不等于绝对路径创建的实例。
1.2 常用方法
查看文件和目录:
java1 2 3 4 5 6
| String getName();
String getPath();
String getAbsolutePath();
|
创建和删除文件和目录:
java1 2 3 4 5 6 7 8
| boolean createNewFile();
boolean mkdir();
boolean mkdirs();
boolean delete();
|
常用的判断方法:
java1 2 3 4 5 6
| boolean exists();
boolean isDirectory();
boolean isFile();
|
1.3 常量
1.3.1 名称分隔符
使用File.separator
获取与系统有关的名称分隔符字符串,此字符串只包含separatorChar字符,用于分隔路径中的文件和目录。
separatorChar被初始化为包含系统属性file.separator值的第一个字符。在UNIX系统上对应/
符号,在Windows系统上对应\
符号。
1.3.2 路径分隔符
使用File.sepapathSeparatorrator
获取与系统有关的路径分隔符字符串,此字符串只包含pathSeparatorChar字符,用于分隔以路径列表中的路径。
pathSeparatorChar被初始为包含系统属性path.separator值的第一个字符。在UNIX系统上对应:
符号,在Windows系统上对应;
符号。
2 输入输出流
2.1 概念
流是数据在数据源(文件)和程序(内存)之间经历的路径。
输入输出流可以称为IO流,I即输入流,O即输出流。流的方向以内存为参照,如果数据流向内存流动则是输入流,反之则是输出流。
文件和目录在程序中是以流的形式来操作的,凡是与输入输出相关的都定义在java.io
包下。
打开的资源不属于内存里的资源,垃圾回收机制无法回收该资源,所以应该显式关闭。关闭流时只需要考虑关闭最外层的流即可,如果要强制关闭所有流,必须先关闭外层的流。
在JDK1.7之后,可以在try-catch
代码块中打开流,最后程序会自动关闭流对象。
2.2 分类
按数据流的流向:输入流,输出流。
按操作数据单位:字节流(InputStream、OutputStream),字符流(Reader、Writer)。
按流的角色:节点流,处理流。
3 文件流
文件流主要有:FileInputStream、FileOutputStream、FileReader、FileWriter。
这四个类用于操作文件流,用法高度相似,前面两个操作字节流,后面两个操作字符流。
文件流直接与操作系统底层交互,因此也被称为节点流,节点流需要关流。
构造方法:
java1 2
| FileInputStream(File file); FileInputStream(String name);
|
常用方法:
java1 2 3 4 5 6 7 8
| int read();
int read(byte[] b);
int read(byte[] b, int off, int len);
void close();
|
读入文件到输入流:
java1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| public void test() { FileInputStream fis = null; try { fis = new FileInputStream(new File("D:" + File.separator + "hello.txt")); byte[] buffer = new byte[1024]; int len = 0; String txt = ""; while ((len = fis.read(buffer)) != -1) { txt += new String(buffer, 0, len); } System.out.println(txt); } catch (IOException e) { e.printStackTrace(); } finally { if (fis != null) { try { fis.close(); } catch (IOException e) { e.printStackTrace(); } } } }
|
3.2 FileOutputStream
构造方法:
java1 2 3 4
| FileOutputStream(File file); FileOutputStream(File file, boolean append); FileOutputStream(String name); FileOutputStream(String name, boolean append);
|
常用方法:
java1 2 3 4 5 6 7 8 9 10
| void write(int b);
void write(byte[] b);
void write(byte[] b, int off, int len);
void flush();
void close();
|
读出文件到输出流:
java1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public void test() { FileOutputStream fos = null; try { fos = new FileOutputStream(new File("D:" + File.separator + "hello.txt")); fos.write("hello".getBytes()); } catch (IOException e) { e.printStackTrace(); } finally { if (fos != null) { try { fos.close(); } catch (IOException e) { e.printStackTrace(); } } } }
|
3.3 FileReader
构造方法:
java1 2
| FileReader(File file); FileReader(String fileName);
|
常用方法:
java1 2 3 4 5 6 7 8 9 10
| String getEncoding();
int read();
int read(char[] cbuf);
int read(char[] cbuf, int off, int len);
void close();
|
读入文本到输入流:
java1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| public void test() { FileReader fr = null; try { fr = new FileReader(new File("D:" + File.separator + "hello.txt")); char[] buffer = new char[1024]; int len = 0; String txt = ""; while ((len = fr.read(buffer)) != -1) { txt = new String(buffer, 0, len); } System.out.print(txt); } catch (IOException e) { e.printStackTrace(); } finally { if (fr != null) { try { fr.close(); } catch (IOException e) { e.printStackTrace(); } } } }
|
3.4 FileWriter
构造方法:
java1 2 3 4
| FileWriter(File file); FileWriter(File file, boolean append); FileWriter(String fileName); FileWriter(String fileName, boolean append);
|
常用方法:
java1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| String getEncoding();
void write(int b);
void write(byte[] b);
void write(byte[] b, int off, int len);
void write(String str);
void write(String str, int off, int len);
void flush();
void close();
|
读出文本到输出流:
java1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public void test() { FileWriter fw = null; try { fw = new FileWriter(new File("D:" + File.separator + "hello.txt")); fw.write("hello"); } catch (IOException e) { e.printStackTrace(); } finally { if (fw != null) { try { fw.close(); } catch (IOException e) { e.printStackTrace(); } } } }
|
3.5 复制文件
使用字节流复制文件:
java1 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
| public void test() { FileInputStream fis = null; FileOutputStream fos = null; try { fis = new FileInputStream("D:" + File.separator + "old.jpeg"); fos = new FileOutputStream("D:" + File.separator + "new.jpeg"); byte[] buffer = new byte[1024]; int len = 0; while ((len = fis.read(buffer)) != -1) { fos.write(buffer, 0, len); } } catch (IOException e) { e.printStackTrace(); } finally { if (fis != null) { try { fis.close(); } catch (IOException e) { e.printStackTrace(); } } if (fos != null) { try { fos.close(); } catch (IOException e) { e.printStackTrace(); } } } }
|
使用字符流复制文件:
java1 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
| public void test() { FileReader fr = null; FileWriter fw = null; try { fr = new FileReader("D:" + File.separator + "old.txt"); fw = new FileWriter("D:" + File.separator + "new.txt"); char[] buffer = new char[1024]; int len = 0; while ((len = fr.read(buffer)) != -1) { fw.write(buffer, 0, len); } } catch (IOException e) { e.printStackTrace(); } finally { if (fr != null) { try { fr.close(); } catch (IOException e) { e.printStackTrace(); } } if (fw != null) { try { fw.close(); } catch (IOException e) { e.printStackTrace(); } } } }
|
4 缓冲流
缓冲流主要有:BufferedInputStream、BufferedOutputStream、BufferedReader、BufferedWriter。
这四个类可以封装现有的节点流,实现对数据传输的效率的提升。
缓冲流比文件流多了一个缓冲区,读取时先从缓冲区读取,当缓冲区数据读完时再把数据写入到缓冲区。因此,当每次读取的数据量很小时,文件流从硬盘读入,缓冲流从缓冲区读入。读取内存速度比读取硬盘速度快得多,因此缓冲流效率高。
缓冲流的默认缓冲区大小是8192字节,当每次读取数据量接近或远超这个值时,两者效率就没有明显差别了。
缓冲流属于处理流,处理流需要关流。
构造方法:
java1 2
| BufferedInputStream(InputStream in); BufferedInputStream(InputStream in, int size);
|
常用方法:
java1 2 3 4 5 6 7 8
| int read();
int read(byte[] b);
int read(byte[] b, int off, int len);
void close();
|
4.2 BufferedOutputStream
构造方法:
java1 2
| BufferedOutputStream(OutputStream out); BufferedOutputStream(OutputStream out, int size);
|
常用方法:
java1 2 3 4 5 6 7 8 9 10
| void write(int b);
void write(byte[] b);
void write(byte[] b, int off, int len);
void flush();
void close();
|
4.3 BufferedReader
构造方法:
java1 2
| BufferedReader(Reader in); BufferedReader(Reader in, int size);
|
常用方法:
java1 2 3 4 5 6 7 8
| int read();
int read(char[] cbuf);
int read(char[] cbuf, int off, int len);
void close();
|
4.4 BufferedWriter
构造方法:
java1 2
| BufferedWriter(Writer out); BufferedWriter(Writer out, int size);
|
常用方法:
java1 2 3 4 5 6 7 8 9 10 11 12 13 14
| void write(int c);
void write(char[] cbuf);
void write(char[] cbuf, int off, int len);
void write(String str);
void write(String str, int off, int len);
void flush();
void close();
|
4.5 复制文件
使用字节流复制文件:
java1 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
| public void test() { BufferedInputStream bis = null; BufferedOutputStream bos = null; try { bis = new BufferedInputStream(new FileInputStream("D:" + File.separator + "old.mp3")); bos = new BufferedOutputStream(new FileOutputStream("D:" + File.separator + "new.mp3")); byte[] buffer = new byte[1024]; int len = 0; while ((len = bis.read(buffer)) != -1) { bos.write(buffer, 0, len); } } catch (IOException e) { e.printStackTrace(); } finally { if (bis != null) { try { bis.close(); } catch (IOException e) { e.printStackTrace(); } } if (bos != null) { try { bos.close(); } catch (IOException e) { e.printStackTrace(); } } } }
|
使用字符流复制文件:
java1 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
| public void test() { BufferedReader br = null; BufferedWriter bw = null; try { br = new BufferedReader(new FileReader("D:" + File.separator + "old.txt")); bw = new BufferedWriter(new FileWriter("D:" + File.separator + "new.txt")); char[] buffer = new char[1024]; int len = 0; while ((len = br.read(buffer)) != -1) { bw.write(buffer, 0, len); } } catch (IOException e) { e.printStackTrace(); } finally { if (br != null) { try { br.close(); } catch (IOException e) { e.printStackTrace(); } } if (bw != null) { try { bw.close(); } catch (IOException e) { e.printStackTrace(); } } } }
|
5 转换流
转换流主要有:InputStreamReader、OutputStreamWriter。
这两个流可以将文本在字节流和字符流之间进行转换,但只能处理文本文件。
转换流需要关流。
构造方法:
java1 2
| InputStreamReader(InputStream in); InputStreamReader(InputStream in, String charsetName);
|
常用方法:
java1 2 3 4 5 6 7 8 9 10
| String getEncoding();
int read();
int read(char[] cbuf);
int read(char[] cbuf, int off, int len);
void close();
|
5.2 OutputStreamWriter
构造方法:
java1 2
| OutputStreamWriter(OutputStream out); OutputStreamWriter(OutputStream out, String charsetName);
|
常用方法:
java1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| String getEncoding();
void write(int c);
void write(char[] cbuf);
void write(char[] cbuf, int off, int len);
void write(String str);
void write(String str, int off, int len);
void flush();
void close();
|
5.3 复制文件
使用转换流复制文件:
java1 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
| public void test() { InputStreamReader isr = null; OutputStreamWriter osw = null; try { isr = new InputStreamReader(new FileInputStream("D:" + File.separator + "old.txt"), "UTF-8"); osw = new OutputStreamWriter(new FileOutputStream("D:" + File.separator + "new.txt"), "UTF-8"); char[] buffer = new char[20]; int len = 0; while ((len = isr.read(buffer)) != -1) { osw.write(buffer, 0, len); } } catch (IOException e) { e.printStackTrace(); } finally { if (isr != null) { try { isr.close(); } catch (IOException e) { e.printStackTrace(); } } if (osw != null) { try { osw.close(); } catch (IOException e) { e.printStackTrace(); } } } }
|
6 内存读写流
内存读写流主要有:ByteArrayOutputStream、ByteArrayInputStream。
内存读写流将数组当作流输入输出对象的类。
不同于指向硬盘的流,它内部是使用字节数组读内存的,这个字节数组是它的成员变量,当这个数组不再使用变成垃圾时会被回收,所以内存读写流不需要关流。
构造方法:
java1 2
| ByteArrayInputStream(byte[] b); ByteArrayInputStream(byte[] b, int off, int len);
|
常用方法:
java1 2 3 4
| int read();
int read(byte[] b, int off, int len);
|
6.2 ByteArrayIOutputStream
构造方法:
java1 2
| ByteArrayIOutputStream(); ByteArrayIOutputStream(int size);
|
常用方法:
java1 2 3 4 5 6 7 8 9 10 11 12
| void write(int b);
void write(byte[] b, int off, int len);
byte toByteArray();
String toString();
String toString(String charsetName);
void writeTo(OutputStream out);
|
6.3 解决乱码
当用字节数组读取字符串时,受数组长度的影响,导致产生乱码。
如果用String类型接收,则不能完全解析出正常的文字,需要使用字节数组输出流将字节数组的内容输出到缓冲区,待读取完成后再转换为String类型的字符串。
示例:
java1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| public void test() { ByteArrayInputStream bais = null; ByteArrayOutputStream baos = null; try { bais = new ByteArrayInputStream("测试写入内容".getBytes()); baos = new ByteArrayOutputStream(); byte[] buffer = new byte[1]; int len = -1; String txt = ""; while ((len = bais.read(buffer)) != -1) { baos.write(buffer, 0, len); txt += new String(buffer, 0, len); } System.out.println("正常:" + baos.toString()); System.out.println("乱码:" + txt); } catch (IOException e) { e.printStackTrace(); } }
|
7 标准流
7.1 System.in
可以获取键盘输入的值,属于字节流。
获取方式:
java
7.2 System.out
可以将文本从控制台输出,属于字节流。
PrintStream是FileOutputStream下的子类,而FileOutputStream是OutputStream下的子类。
获取方式:
java
7.3 读取输入内容
从控制台中读取输入内容:
java1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public void test() { BufferedReader br = null; try { br = new BufferedReader(new InputStreamReader(System.in)); System.out.println("请输入字符串:"); System.out.println("输入的字符串为:" + br.readLine()); } catch (IOException e) { e.printStackTrace(); } finally { if (br != null) { try { br.close(); } catch (IOException e) { e.printStackTrace(); } } } }
|
7.4 读取文件内容
从文件中读取输入内容:
java1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public void test() { try { System.setIn(new FileInputStream("D:" + File.separator + "output.txt")); byte[] buffer = new byte[1024]; int len = 0; String str = ""; while ((len = System.in.read(buffer)) != -1) { str += new String(buffer, 0, len); } System.out.println(str); } catch (IOException e) { e.printStackTrace(); } }
|
7.5 输出内容
将内容输出到控制台和文件:
java1 2 3 4 5 6 7 8 9
| public void test() { try { System.out.println("这是输出到控制台的文字。"); System.setOut(new PrintStream("D:" + File.separator + "input.txt")); System.out.println("这是输出到文件里的文字。"); } catch (IOException e) { e.printStackTrace(); } }
|
8 对象流
对象流主要有:ObjectInputStream、ObjectOutputStream。
这两个类型都是字节流,可以处理所有文件,可以将内存中的对象保存到本地,也可以将本地的对象还原到内存中。
8.1 序列化
8.1.1 概念
对象序列化机制允许把内存中的对象转换成平台无关的二进制流,当其它程序获取了这种二进制流,就可以恢复成原来的对象。
如果想将一个对象进行网络传输,要求是该对象必须是可序列化的,该类必须实现Serializable接口或者Externalizable接口。
不能序列化static和transient修饰的属性。
8.1.2 好处
序列化的好处在于可将任何实现了Serializable接口的对象转化为字节数据,使其在保存和传输时可被还原。
序列化是远程方法调用(Remote Method Invoke)过程的参数和返回值都必须实现的机制,而RMI是系统调用的基础,因此序列化机制是系统调用的基础。
8.1.3 实现
序列化是用ObjectOutputStream类保存基本类型数据或对象的机制,因为是输出到文件里,所以是输出流。
反序列化是用ObjectInputStream类读取基本类型数据或对象的机制,因为将数据输入到内存里,所以是输入流。
8.1.4 自定义
在进行序列化和反序列化时,虚拟机会首先试图调用对象里的writeObject和readObject方法,进行用户自定义的序列化和反序列化。
如果没有这样的方法,那么默认调用的是ObjectOutputStream的defaultWriteObject以及ObjectInputStream的defaultReadObject方法。
8.1.5 验证版本
序列化机制是通过判断类的serialVersionUID来验证版本一致性的。
在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体类的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现InvalidCastException序列化版本不一致的异常。
serialVersionUID有两种生成方式:生成默认的1L,或者生成一个64位的Hash字段。
构造方法:
java1
| ObjectInputStream(InputStream in);
|
常用方法:
java1 2 3 4 5 6 7 8 9 10 11 12 13 14
| int read();
int read(byte[] buf);
int read(byte[] buf, int off, int len);
String readUTF();
Object readObject();
void defaultReadObject();
void close();
|
示例:
java1 2 3 4 5 6 7 8 9 10 11
| public void deserialize() { try { ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D:" + File.separator + "object.obj")); System.out.println(ois.readInt()); System.out.println(ois.readObject()); System.out.println((Box) ois.readObject()); ois.close(); } catch (IOException e) { e.printStackTrace(); } }
|
8.3 ObjectOutputStream
构造方法:
java1
| DataOutputStream(OutputStream out);
|
常用方法:
java1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| void write(int b);
void write(byte[] b);
void write(byte[] b, int off, int len);
void writeUTF(String str);
void writeObject(Object obj);
void defaultWriteObject();
void flush();
void close();
|
示例:
java1 2 3 4 5 6 7 8 9 10 11
| public void serialize() { try { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:" + File.separator + "object.obj")); oos.writeInt(100); oos.writeObject("String"); oos.writeObject(new Box("box", 6, 8)); oos.close(); } catch (IOException e) { e.printStackTrace(); } }
|
9 随机存取文件流
随机存取文件流可以处理所有文件,既可以充当输入流,又可以充当输出流。
如果将要输出文件不存在则尝试自动创建,并将内容输出到此文件中。如果将要输出的文件存在,则将对文件内容进行覆盖。
9.1 RandomAccessFile
构造方法:
java1 2
| RandomAccessFile(File file, String mode); RandomAccessFile(String name, String mode);
|
使用mode指定文件的访问模式:
- r:以只读方式打开。
- rw:打开以便读取和写入。
- rws:还要求对文件的内容或元数据的更新都同步到底层存储设备。
- rwd:还要求对文件内容的更新都同步到底层存储设备。
常用方法:
java1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| void write(byte[] b);
void write(int b);
void write(byte[] b, int off, int len);
int read();
int read(byte[] b);
int read(byte[] b, int off, int len);
long getFilePointer();
void seek(long pos);
void close();
|
9.2 复制文件
使用随机存取文件流复制文件:
java1 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
| public void test() { RandomAccessFile r = null; RandomAccessFile w = null; try { r = new RandomAccessFile(new File("D:" + File.separator + "read.txt"), "r"); w = new RandomAccessFile(new File("D:" + File.separator + "write.txt"), "rw"); byte[] buffer = new byte[1024]; int len = 0; while ((len = r.read(buffer)) != -1) { w.write(buffer, 0, len); } } catch (IOException e) { e.printStackTrace(); } finally { if (r != null) { try { r.close(); } catch (IOException e) { e.printStackTrace(); } } if (w != null) { try { w.close(); } catch (IOException e) { e.printStackTrace(); } } } }
|
9.3 插入内容
使用随机存取文件流插入内容:
java1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| public void test() { RandomAccessFile raf = null; try { raf = new RandomAccessFile("D:" + File.separator + "read.txt", "rw"); raf.seek(3); byte[] buffer = new byte[1024]; int len = 0; String str = ""; while ((len = raf.read(buffer)) != -1) { str += new String(buffer, 0, len); } raf.seek(3); raf.write("read".getBytes()); raf.write(str.getBytes()); } catch (IOException e) { e.printStackTrace(); } finally { try { raf.close(); } catch (IOException e) { e.printStackTrace(); } } }
|
条