一.分类
1.字节流
(1).InputStream(字节输入流)
定义:操作本地文件的字节输入流,可以把本地文件中的数据读取到程序中
书写步骤:1.创建字节输入流对象,2.读数据,3.释放资源
java
import java.io.FileInputStream;
import java.io.IOException;
public class IO {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("C:\\Users\\21566\\IdeaProjects\\untitled6\\c.txt");
//只读取第一个数据
int i = fis.read();
System.out.println(i);
fis.close();
}
}
循环读取数据:
java
import java.io.FileInputStream;
import java.io.IOException;
public class IO {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("C:\\Users\\21566\\IdeaProjects\\untitled6\\c.txt");
int i;
//循环读取其中的所有数据
while ((i = fis.read()) != -1) {
System.out.print((char) i);
}
fis.close();
}
}
注意点:
1.在使用read方法读取数据时,它会一个一个往后读取,当读不到时会返回-1
2.在创建对象时,如果文件不存在则直接报错(读取一个根本不存在的文件这样的操作本身是完全没有意义的,所以直接杜绝了这样的操作)
3.在读取数据时一次读取一个字节,并且读取出来的数据是在ASCII表上所对应的数字
4.在我们每次使用完流之后都必须要释放资源
在FileInputStream中一次读多个字节
public int read()一次读一个字节的数据
public int read(byte[] b)一次读一个字节数组的数据(在一次读取一个字节数组的数据时,每次读取都会保证会尽可能把数组装满)
java
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Arrays;
public class IO {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("C:\\Users\\21566\\IdeaProjects\\untitled6\\c.txt");
FileOutputStream fos = new FileOutputStream("C:\\Users\\21566\\IdeaProjects\\untitled6\\bbb\\copy.txt");
//创建了一个长度为2的byte数组,那么一次就会读取两个字节的数据
byte[] bytes = new byte[2];
int len = fis.read(bytes);
System.out.println(len);
System.out.println(Arrays.toString(bytes));
//注意点:先开的流后关闭
fos.close();
fis.close();
}
}
注意点:在读取数据时,每一次都将数据读入了byte数组当中,在第二次读入数据时则是将原本存入byte数组中的数据一次覆盖掉,那么如果最后一次读取的数据不足以覆盖掉全部的上一次数据,会出现什么情况呢?答案是:仍有未被覆盖的数据保存在byte数组中
比如:我存入了abcde五个数据,byte数组的大小为2,那么最后一次读取的byte数组的长度应该为1(只读取了e一个数据),但是输出这时的byte数组中的数据则会输出ed两个数据,这是因为后面的d没有被新的数据覆盖,仍被保存在byte数组中
(2).OutputStream(字节输出流)
书写步骤:1.创建字节输出流对象,2.写入数据,3.释放资源
例:文件的字节输出流
java
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class IO {
public static void main(String[] args) throws IOException {
FileOutputStream fos = new FileOutputStream("C:\\Users\\21566\\IdeaProjects\\untitled6\\c.txt");
fos.write(97);
fos.close();
}
}
原理:
在我们书写FileOutputStream fos = new FileOutputStream("");这一行代码时与FIle对象创建了一个数据传输的通道,然后再调用write方法使用该条通道将数据传输到File对象当中,最后使用的close方法则是用于释放资源(毁坏了这条通道)
字节输出流的一些注意点:
1.在创建对象时使用的参数不管是File对象还是字符串表示的路径都可以
2.在创建对象时如果当前要输出的文件对象不存在,那么会创建这样一个文件并将数据输入进去,但是前提是保证父级路径是存在的
3.在创建对象时如果对象已经存在,那么字节输出流会先清空文件,然后再将数据写入文件中
4.在写入数据时使用的是write方法,虽然在write方法中传入的参数是整数,但是实际上写到对象中的数据是该整数在ASCII码表上所对应的字符
5.释放资源(close方法)的作用是解除对资源的占用,否则在字节输出流使用该对象期间任何程序都不得对该对象进行修改,所以每次我们在使用完流之后都要释放资源
FileOutputStream写数据的三种方式:
void write(int i)一次写一个字节的数据
java
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class IO {
public static void main(String[] args) throws IOException {
FileOutputStream fos = new FileOutputStream("C:\\Users\\21566\\IdeaProjects\\untitled6\\c.txt");
byte[] b = {97, 98, 99, 100, 101, 102, 103, 104, 105};
fos.write(b);
fos.close();
}
}
void write(byte[] b)一次写一个字节数组中的所有数据
java
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class IO {
public static void main(String[] args) throws IOException {
FileOutputStream fos = new FileOutputStream("C:\\Users\\21566\\IdeaProjects\\untitled6\\c.txt");
byte[] b = {97, 98, 99, 100, 101, 102, 103, 104, 105};
fos.write(b, 1, 4);
fos.close();
}
}
void write(byte[] b, int off, int len)一次写一个字节数组中的部分数据(b:byte类型的数组,off:起始索引,len:从起始索引开始写入的数据个数)
FileOutputStream写数据的两个问题
换行输出数据:
java
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class IO {
public static void main(String[] args) throws IOException {
FileOutputStream fos = new FileOutputStream("C:\\Users\\21566\\IdeaProjects\\untitled6\\c.txt");
String str1 = "abcdefghijk";
byte[] bytes1 = str1.getBytes();
fos.write(bytes1);
//把第二组数据再次输入File对象当中,但是单纯的再次输入数据并不会换行,而是需要我们手动输入一个代表换行的byte数组
String line = "\r\n";//windows系统中的为\r\n,Linux操作系统中的则是\n,需要大家注意的是在不同系统中的换行符并不相同
byte[] change = line.getBytes();
fos.write(change);
String str2 = "lmnop";
byte[] bytes2 = str2.getBytes();
fos.write(bytes2);
fos.close();
}
}
续写数据:
如果想要续写数据则需要打开续写开关,而续写开关位于创建对象的第二个参数位置,默认为false
java
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class IO {
public static void main(String[] args) throws IOException {
FileOutputStream fos = new FileOutputStream("C:\\Users\\21566\\IdeaProjects\\untitled6\\c.txt",true);
String str = "abcdefghijk";
byte[] bytes = str.getBytes();
fos.write(bytes);
fos.close();
}
}
2.字符流
字符流=字节流+字符集
特点:
输入流:一次读一个字节,遇到中文时一次读多个字节
输出流:在底层把数据按照指定的编码方式编码,变成字节之后再写到文件中
使用场景:
对于纯文本文件进行读写操作
(1).Reader(字符输入流)
FileReader的书写步骤:
1.创建字符输入流对象
public FileReader(File file)
public FileReader(String pathname)
两者都是创建字符输入流与本地文件建立连接,但不管是传递一个File对象还是一个String类型的数据来表示文件路径都是可以的
注意点:如果文件不存在则直接报错
2.读取数据
public int read()一次读取一个数据,到末尾返回-1
java
import java.io.FileReader;
import java.io.IOException;
public class IO {
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("C:\\Users\\21566\\IdeaProjects\\untitled6\\c.txt");
int c;
while ((c = fr.read()) != -1) {
System.out.print((char) c);
}
fr.close();
}
}
public int read(char[] c)一次读取多个数据,到末尾返回-1
java
import java.io.FileReader;
import java.io.IOException;
public class IO {
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("C:\\Users\\21566\\IdeaProjects\\untitled6\\c.txt");
char[] c = new char[2];
int len;
while ((len = fr.read(c)) != -1) {
System.out.print((new String(c, 0, len)));
}
fr.close();
}
}
注意点:
1.按字节进行读取,遇到中文时会一次读取多个字节,之后解码,返回该中文所对应的整数
2.只要读取到了末尾就会返回-1
3.释放资源
public int close()
(2).Writer(字符输出流)
构造方法:
第一类:
创建字符输出流与本地文件建立连接(不续写)
public FileWriter(File file)
public FileWriter(String pathname)
第二类:
(要续写)
public FileWriter(File file, boolean append)
public FileWriter(String pathname, boolean append)
成员方法:
void write(int i)写出一个字符
java
import java.io.FileWriter;
import java.io.IOException;
public class IO {
public static void main(String[] args) throws IOException {
FileWriter fw = new FileWriter("C:\\Users\\21566\\IdeaProjects\\untitled6\\c.txt");
fw.write(23781);//先按照当前字符集的编码方式进行编码,编码之后找到对应的数据写出
fw.close();
}
}
void write(String str)写出一个字符串
java
import java.io.FileWriter;
import java.io.IOException;
public class IO {
public static void main(String[] args) throws IOException {
FileWriter fw = new FileWriter("C:\\Users\\21566\\IdeaProjects\\untitled6\\c.txt");
fw.write("爱自己是终生浪漫的开始");//先按照当前字符集的编码方式进行编码,编码之后找到对应的数据写出
fw.close();
}
}
void write(String str, int off, int len)写出一个字符串的一部分(从off位置开始的len长度的那一部分)
java
import java.io.FileWriter;
import java.io.IOException;
public class IO {
public static void main(String[] args) throws IOException {
FileWriter fw = new FileWriter("C:\\Users\\21566\\IdeaProjects\\untitled6\\c.txt");
fw.write("爱自己是终生浪漫的开始",4,7);//先按照当前字符集的编码方式进行编码,编码之后找到对应的数据写出
fw.close();
}
}
void write(char[] c)写出一个字符数组
java
import java.io.FileWriter;
import java.io.IOException;
public class IO {
public static void main(String[] args) throws IOException {
FileWriter fw = new FileWriter("C:\\Users\\21566\\IdeaProjects\\untitled6\\c.txt");
char[] c = {'爱', '自', '己', '是', '终', '生', '浪', '漫', '的', '开', '始'};
fw.write(c);//先按照当前字符集的编码方式进行编码,编码之后找到对应的数据写出
fw.close();
}
}
void write(char[] c,int off, int len)写出一个字符数组的一部分
java
import java.io.FileWriter;
import java.io.IOException;
public class IO {
public static void main(String[] args) throws IOException {
FileWriter fw = new FileWriter("C:\\Users\\21566\\IdeaProjects\\untitled6\\c.txt");
char[] c = {'爱', '自', '己', '是', '终', '生', '浪', '漫', '的', '开', '始'};
fw.write(c,4,7);//先按照当前字符集的编码方式进行编码,编码之后找到对应的数据写出
fw.close();
}
}
FileWriter的书写步骤:
1.创建字符输出流对象
注意点:
1.参数是字符串表示的路径或者File对象都可以
2.如果文件不存在则会创建一个新的文件,但是要保证父级路径是存在的
3.如果文件已经存在,则会在清空文件内的内容之后再写入数据(构造方法中的第二个默认为false的参数是是否续写的选项)
2.写入数据
注意点:虽然write方法的参数是整数,但是实际上写到本地文件中的是整数在字符集上所对应的字符
3.释放资源
注意点:使用完流之后记得释放资源
二.文件拷贝
1.小文件的拷贝
java
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class IO {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("C:\\Users\\21566\\IdeaProjects\\untitled6\\c.txt");
FileOutputStream fos = new FileOutputStream("C:\\Users\\21566\\IdeaProjects\\untitled6\\bbb\\copy.txt");
int i;
while ((i = fis.read()) != -1) {
fos.write(i);
}
//注意点:先开的流后关闭
fos.close();
fis.close();
}
}
2.大文件的拷贝
在大文件的拷贝中不能使用上面的方式,因为在上面的方式中是一次读取一个字节,速度相当慢,所以我们在大文件的拷贝中需要使用到FileInputStream中一次读取多个字节的方法
java
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class IO {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("");
FileOutputStream fos = new FileOutputStream("");
int len;
byte[] bytes = new byte[1024 * 1024 * 5];//五兆字节
while ((len = fis.read(bytes)) != -1) {
//如果不使用len对最后一次的拷贝进行限制,那么就很可能会出现额外拷贝了未被覆盖的数据的情况
fos.write(bytes, 0, len);
}
//注意点:先开的流后关闭
fos.close();
fis.close();
}
}
三.IO流中不同JDK版本捕获异常的方式
注意点:在try...catch异常处理中我们需要在这里使用finally的特性(除非虚拟机停止运行,则finally中的代码一定会被执行),防止字节输出流和字节输入流最终没有关闭,即,将fos.close();和fis.close();写到finally中
java
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class IO {
public static void main(String[] args) {
//如果在try中定义则会变为局部变量,导致在之后使用fos和fis时报错,所以放到上面来
//并且在初始定义时一定要定义为null,否则会导致在最后关闭fos和fis时报错为:fos和fis可能未定义
FileInputStream fis = null;
FileOutputStream fos = null;
try {
fis = new FileInputStream("C:\\Users\\21566\\IdeaProjects\\untitled6\\c.txt");
fos = new FileOutputStream("C:\\Users\\21566\\IdeaProjects\\untitled6\\bbb\\copy.txt");
int len;
byte[] bytes = new byte[2];
while ((len = fis.read(bytes)) != -1) {
fos.write(bytes, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
简化:
我们在简化时需要使用AutoCloseable接口,它的特点是在特定的情况下可以自动释放资源
JDK7的写法:
java
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class IO {
public static void main(String[] args) {
try (FileInputStream fis = new FileInputStream("C:\\Users\\21566\\IdeaProjects\\untitled6\\c.txt"); FileOutputStream fos = new FileOutputStream("C:\\Users\\21566\\IdeaProjects\\untitled6\\bbb\\copy.txt")) {
int len;
byte[] bytes = new byte[2];
while ((len = fis.read(bytes)) != -1) {
fos.write(bytes, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
JDK9的写法:
java
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class IO {
public static void main(String[] args) throws FileNotFoundException {
FileInputStream fis = new FileInputStream("C:\\Users\\21566\\IdeaProjects\\untitled6\\c.txt");
FileOutputStream fos = new FileOutputStream("C:\\Users\\21566\\IdeaProjects\\untitled6\\bbb\\copy.txt");
try (fis; fos) {
int len;
byte[] bytes = new byte[2];
while ((len = fis.read(bytes)) != -1) {
fos.write(bytes, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}