概念
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(); } }
序列流
整合两个输入流
public class Test { public static void main(String[] args) throws IOException { FileInputStream fis1 = new FileInputStream("xxx.txt"); FileInputStream fis2 = new FileInputStream("yyy.txt"); SequenceInputStream sis = new SequenceInputStream(fis1, fis2); FileOutputStream fos = new FileOutputStream("zzz.txt"); int b; while((b = sis.read()) != -1) { fos.write(b); } sis.close(); fos.close(); } }
整合多个输入流
public class Test { public static void main(String[] args) throws IOException { FileInputStream fis1 = new FileInputStream("xxx.txt"); FileInputStream fis2 = new FileInputStream("yyy.txt"); FileInputStream fis3 = new FileInputStream("zzz.txt"); Vector<FileInputStream> v = new Vector<FileInputStream>(); v.add(fis1); v.add(fis2); v.add(fis3); Enumeration<FileInputStream> en = v.elements(); SequenceInputStream sis = new SequenceInputStream(en); FileOutputStream fos = new FileOutputStream("out.txt"); int b; while((b = sis.read()) != -1) { fos.write(b); } sis.close(); fos.close(); } }
内存输出流ByteArrayOutputStream
public class Test { public static void main(String[] args) throws IOException { FileInputStream fis = new FileInputStream("xxx.txt"); ByteArrayOutputStream baos = new ByteArrayOutputStream(); int b; while((b = fis.read()) != -1) { baos.write(b); } byte[] arr = baos.toByteArray(); System.out.println(new String(arr)); fis.close(); baos.close(); //内存用完了会自动释放,所以可以不用关闭 } }
也可以直接toString,但这样就用的是默认编码,
如果转换要用指定编码,需要先toByteArray再使用String类创建字符串
随机访问流RandomAccessFile
public class Test { public static void main(String[] args) throws IOException { RandomAccessFile raf = new RandomAccessFile("xxx.txt", "rw"); //读 raf.read(); //写 raf.write(97); //定位 raf.seek(0); //关闭 raf.close(); } }
对象操作流ObjectOutputStream和ObjectInputStream
可以将对象写入文件,或者读取一个对象到程序中
也就是执行了序列化和反序列哈的功能
对象需要可以被序列化,也就是需要实现Serializabel接口
对象写入,序列化
public class Test { public static void main(String[] args) throws IOException { XXX m1 = new XXX(); XXX m2 = new XXX(); ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("test.txt")); //写入对象到文件中 oos.writeObject(m1); oos.writeObject(m2); oos.close(); } } class XXX implements Serializable { }
读取方法,反序列化
public class Test { public static void main(String[] args) throws IOException, ClassNotFoundException { ObjectInputStream ois = new ObjectInputStream(new FileInputStream("test.txt")); XXX m1 = (XXX)ois.readObject(); XXX m2 = (XXX)ois.readObject(); ois.close(); } } class XXX implements Serializable { }
批量写入和读取只要将对象存入集合,然后将集合进行操作即可
public class Test { public static void main(String[] args) throws IOException { XXX m1 = new XXX(); XXX m2 = new XXX(); ArrayList<XXX> list = new ArrayList<XXX>(); list.add(m1); list.add(m2); ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("test.txt")); oos.writeObject(list); oos.close(); } } class XXX implements Serializable { }
在进行对象的序列化操作时,对象的类中可以加一个id号,
可以有利于类的结构被修改时引发的版本不一致的错误
数据输入输出流DataInputStream和DataOutputStream
数据写入
public class Test { public static void main(String[] args) throws IOException { DataOutputStream dos = new DataOutputStream(new FileOutputStream("test.txt")); dos.writeInt(999); dos.close(); } }
数据读取
public class Test { public static void main(String[] args) throws IOException { DataInputStream dis = new DataInputStream(new FileInputStream("test.txt")); int x = dis.readInt(); dis.close(); } }
打印流PrintStream(字节)和PrintWriter(字符)
可以将对象的toString结果输出,并且可以使用自动刷出的模式
System.io就是一个打印流
public class Test { public static void main(String[] args) throws IOException { PrintStream ps = System.out; ps.println("aaa"); ps.write(98); } }
PrintWriter的自动刷出功能
public class Test { public static void main(String[] args) throws IOException { //第二个参数加上了true,代表使用了自动刷出功能,但是只是针对println方法才会触发 PrintWriter pw = new PrintWriter(new FileOutputStream("test.txt"), true); pw.println("xxx"); pw.write("a"); pw.close(); } }
标准输入输出流System.in和System.out
略。。。。。
public class Test { public static void main(String[] args) throws IOException { InputStream is = System.in; OutputStream os = System.out; int x = is.read(); System.out.println(x); os.write(98); //标准输入输出流可以不用关闭 } }
修改标准输入流和标准输出流
public class Test { public static void main(String[] args) throws IOException { System.setIn(new FileInputStream("test.txt")); System.setOut(new PrintStream("xxx.txt")); InputStream is = System.in; OutputStream os = System.out; int b; while((b = is.read()) != -1) { os.write(b); } } }
Properties和IO流的交互
这个类是Hashtable的子类,是一个双列集合
Properties 类表示了一个持久的属性集
Properties 可保存在流中或从流中加载
public class Test { public static void main(String[] args) throws IOException { Properties prop = new Properties(); prop.put("abs", 123); prop.put("sada", 123123); System.out.println(prop); } }
还可以使用,setProperty设置属性和属性值
和IO流的交互
首先要有一个配置文件,比如xxx.properties
name=asd tel=123123123 qq=345345345
接着是读取的方法
public class Test { public static void main(String[] args) throws IOException { Properties prop = new Properties(); prop.load(new FileInputStream("xxx.properties")); System.out.println(prop); } }
在程序中对属性进行修改,并将结果写入回配置文件