概念
IO流用来处理设备之间的数据传输
java对数据的操作是通过流的方式
java用来操作流的类都在IO包中
流按流向分为两种:输入流、输出流
流按操作类型分为两种:字节流、字符流
IO流常用父类
字节流的抽象父类:InputStream、OutputStream
字符流的抽象父类:Reader、Writer
IO流的使用注意
使用前:要导入IO包中的类
使用时:要进行IO异常处理
使用后:要释放资源
流对象尽量晚开早关
FileInputStream进行文件读取
public class Test { public static void main(String[] args) throws IOException { FileInputStream fis = new FileInputStream("test.txt"); //从硬盘上读取一个字节 int x = fis.read(); System.out.println(x); //关闭流资源 fis.close(); } }
文件的结束标记是 -1
public class Test { public static void main(String[] args) throws IOException { FileInputStream fis = new FileInputStream("test.txt"); int b; while((b = fis.read()) != -1) { System.out.println(b); } //关闭流资源 fis.close(); } }
为什么read返回的是int类型而不是byte类型?
应为FileInputStream可以读取任意类型的文件,如果用byte类型,中间可能出现11111111,而这就是byte类型的-1
程序就会停止执行,后面的就读不到了,如果用int,前面高位不会变成1,也就变成了255,可以保证整个数据的读写
FileOutputStream向文件中写入
将打开的文件清空再重新写入
FileOutputStream在创建时,如果没有这个文件,则会自动创建一个
如果有这个文件,则会先将这个文件清空
public class Test { public static void main(String[] args) throws IOException { //如果没有此文件则会自动创建一个 FileOutputStream fos = new FileOutputStream("test.txt"); fos.write(97); fos.write(98); fos.write(99); fos.write(100); fos.close(); } }
如果想以追加的方式操作文件,要给第二个参数为true
public class Test { public static void main(String[] args) throws IOException { FileOutputStream fos = new FileOutputStream("test.txt", true); fos.write(97); fos.write(98); fos.write(99); fos.write(100); fos.close(); } }
使用FileInputStream和FileOutputStream进行拷贝
逐个字节拷贝
public class Test { public static void main(String[] args) throws IOException { FileInputStream fis = new FileInputStream("test.jpg"); FileOutputStream fos = new FileOutputStream("test_copy.jpg"); int b; while((b = fis.read()) != -1) { fos.write(b); } fis.close(); fos.close(); } }
FileInputStream里的available方法:
返回下一次对此输入流调用的方法可以不受阻塞地从此输入流读取(或跳过)的估计剩余字节数
使用字节数组将文件一次性读取和写入
public class Test { public static void main(String[] args) throws IOException { FileInputStream fis = new FileInputStream("9.jpg"); FileOutputStream fos = new FileOutputStream("test_copy.jpg"); //获取文件的字节数 int len = fis.available(); //创建和文件一样大小的字节数组 byte[] arr = new byte[len]; //将文件一次性读进来 fis.read(arr); //将文件一次性写入 fos.write(arr); fis.close(); fos.close(); } }
而对于某些大文件,需要创建很大的字节数组,有可能导致内存溢出
所以可以创建合适大小的字节数组,一部分一部分读取
public class Test { public static void main(String[] args) throws IOException { FileInputStream fis = new FileInputStream("test.jpg"); FileOutputStream fos = new FileOutputStream("test_copy.jpg"); //小文件一般定义为16的整数倍,大文件大小一般定义为1024的整数倍 byte[] arr = new byte[1024]; //存放每次读取的字节 int len; //记录每次读取的字节个数 while((len = fis.read(arr)) != -1) { fos.write(arr, 0, len); } fis.close(); fos.close(); } }
使用BufferedInputStream和BufferedOutputStream
这两个是对FileInputStream和FileOutputStream的包装
在进行读取时,会预先将一部分读入内存,然后其实是在内存中读取
读完了在进行下一部分的读取
所以同样是一个字节一个字节读取,但是使用Buffered会更快
因为内存的速度比硬盘快
public class Test { public static void main(String[] args) throws IOException { FileInputStream fis = new FileInputStream("9.jpg"); FileOutputStream fos = new FileOutputStream("test_copy.jpg"); BufferedInputStream bis = new BufferedInputStream(fis); BufferedOutputStream bos = new BufferedOutputStream(fos); int b; while ((b = bis.read()) != -1) { bos.write(b); } //仅需要关闭包装后的对象即可 bis.close(); bos.close(); } }
定义小数组和使用buffer的比较
小数组会比buffer稍微快一点点
flush和close方法的区别
flush用来刷新缓冲区
close用来关闭流
close方法具备刷新的功能,在关闭流之前会进行刷新,刷新之后不能继续写入
flush方法仅仅是刷新缓冲区的功能,刷新完之后可以继续写入
字节流读写中文的问题
字节流读取中文会出现各种乱码,因为不确定每个字符所占的字节数
public class Test { public static void main(String[] args) throws IOException { FileInputStream fis = new FileInputStream("test.txt"); byte[] arr = new byte[2]; int len; while((len = fis.read(arr)) != -1) { System.out.println(new String(arr, 0, len)); } } }
写出中文时要将字符串转换成字节数组
public class Test { public static void main(String[] args) throws IOException { FileOutputStream fos = new FileOutputStream("test.txt"); fos.write("你好啊".getBytes()); fos.close(); } }
流的标准异常处理代码
1.6
public class Test { public static void main(String[] args) throws IOException { FileInputStream fis = null; FileOutputStream fos = null; try { fis = new FileInputStream("9.jpg"); fos = new FileOutputStream("test_copy.jpg"); int b; while((b = fis.read()) != -1) { fos.write(b); } } finally { //关闭时出现异常,则能关一个就关一个 try { if(fis != null) fis.close(); } finally { if(fos != null) fos.close(); } } } }
1.7版本的写法,这种写法可以自动关闭流的功能
public class Test { public static void main(String[] args) throws IOException { try( FileInputStream fis = new FileInputStream("test.jpg"); FileOutputStream fos = new FileOutputStream("test_copy.jpg"); ) { int b; while((b = fis.read()) != -1) { fos.write(b); } } } }
字符流
字符流是能够直接读取字符的IO流
对字节流进行了编码表的转换
使用FileReader读取
public class Test { public static void main(String[] args) throws IOException { FileReader fr = new FileReader("test.txt"); int x; while((x = fr.read()) != -1) { System.out.println((char)x); } fr.close(); } }
使用FileWriter进行文件写入
public class Test { public static void main(String[] args) throws IOException { FileWriter fw = new FileWriter("test.txt"); fw.write("大家好啊"); fw.close(); } }
Writer类中有一个2k的小缓冲区,如果不关闭流,写入的内容最后不会刷新进去,可能写入不完全
使用字符流定义小数组进行拷贝
public class Test { public static void main(String[] args) throws IOException { FileReader fr = new FileReader("xxx.txt"); FileWriter fw = new FileWriter("yyy.txt"); char[] arr = new char[1024]; int len; while((len = fr.read()) != -1) { fw.write(arr); } fr.close(); fw.close(); } }
使用BufferedReader和BufferedWriter进行字符流读写
public class Test { public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new FileReader("xxx.txt")); BufferedWriter bw = new BufferedWriter(new FileWriter("yyy.txt")); int c; while((c = br.read()) != -1) { bw.write(c); } br.close(); bw.close(); } }
带缓冲的字符流的readLine和newLine方法
readLine方法每次读取一行
public class Test { public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new FileReader("xxx.txt")); BufferedWriter bw = new BufferedWriter(new FileWriter("yyy.txt")); String line; while((line = br.readLine()) != null ) { bw.write(line); } br.close(); bw.close(); } }
newLine方法写入一个行分隔符,不加的话,所有读取的会被写在一行
public class Test { public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new FileReader("xxx.txt")); BufferedWriter bw = new BufferedWriter(new FileWriter("yyy.txt")); String line; while((line = br.readLine()) != null ) { bw.write(line); bw.newLine(); } br.close(); bw.close(); } }
newLine与\r\n的区别:
newLine是跨平台的,\r\n只是windows系统的
LineNumberReader跟踪行号的缓冲区输入流
public class Test { public static void main(String[] args) throws IOException { LineNumberReader lnr = new LineNumberReader(new FileReader("test.txt")); String line; lnr.setLineNumber(0); //设置当前行号,默认为零 while((line = lnr.readLine()) != null) { System.out.println(lnr.getLineNumber() + ":" + line); } lnr.close(); } }
InputStreamReader和OutputStreamWriter
使用指定编码表进行读写
public class Test { public static void main(String[] args) throws IOException { InputStreamReader isr = new InputStreamReader(new FileInputStream("test.txt"), "utf-8"); OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("test_x.txt"), "gbk"); int c; while((c = isr.read()) != -1) { osw.write(c); } isr.close(); osw.close(); } }
进一步更高效的包装
public class Test { public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("utf8.txt"), "utf-8")); BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("gbk.txt"), "gbk")); int c; while((c = br.read()) != -1) { bw.write(c); } br.close(); bw.close(); } }