IO流概述
IO流用来处理设备之间的数据传输
上传和下载文件
Java对数据的操作是通过流的方式
Java用于操作流的对象在IO包中
Java流分类
流向:输入流 输出流
数据类型:字节流 字符流
操作的是文本数据,就用字符流(你要操作的文件用记事本打开,你可以读懂的就可以使用字符流)
如果不能读懂,就用字节流。(如果啥都不知道,就用字节流。)
输入流 读取数据
输出流 写出数据
字节流
字节输入流
字节输出流
字符流
字符输入流
字符输出流
注:一般在探讨IO流的时候,如果按明确说明按那种分类来说,默认情况下按数据类型来分的。
字节流的抽象基类
InputStream, OutputStream
字符流的抽象基类
Reader, Writer
注:由这四个类派生出来的子类名称都是以其父类名作为子类名的后缀
如:InputStream的子类FileInputStream
Reader的子类FileReader
import java.io.FileOutputStream; import java.io.IOException; public class OutpusTtreamDemo { public static void main(String[] args) throws IOException { /* * 这里一共执行了三步: * 1、创建了FileOutputStream对象 * 2、调用系统功能创建了文件,注意:这个并不能创建文件夹,只能创建文件 * 3、把FileOutputStream指向了文件 * 如果文件夹不存在,需要先创建文件夹,再创建文件。如果没有文件夹不存在,会报错。 * */ FileOutputStream fos = new FileOutputStream("D:\\zed.txt"); String s = "helloworld"; //把字符串改为字节数组 byte[] b = s.getBytes(); //写入字节数组 fos.write(b); fos.write("zed".getBytes()); //关闭资源,一定要关闭资源。
fos.close(); } }
关闭资源:让流对象变成垃圾,这样就可以被垃圾回收器回收了;通知系统释放创建文件是调用的系统资源。
// void write(byte[] b) 将 b.length 个字节从指定 byte 数组写入此文件输出流中。 // void write(byte[] b, int off, int len) 将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此文件输出流。 // void write(int b) 将指定字节写入此文件输出流。 fos.write(b); //这里是直接写一个字节,122代表Z? fos.write(122);
public static void main(String[] args) throws IOException { FileOutputStream fos = new FileOutputStream("fos.txt"); //输出的是小写字母a //底层二进制,打开对应小写字母a fos.write(97); String s = "12545346745y63525346egsdvscvsdagvbas"; //代表从位置0开始写3个 fos.write(s.getBytes(), 1, 3); fos.close() }
换行
import java.io.IOException; /* * 如何换行? * * * */ public class FileOutputStreamDemo { public static void main(String[] args) throws IOException { FileOutputStream fos = new FileOutputStream("fos.txt"); for(int i = 0; i < 10; i++){ fos.write(("hello" + i ).getBytes()); //fos.write("hello" + i + + "\n").getBytes())这样也是可以的,就不需要另起一行写换行 //对换行符进行转义 //fos.write("\n".getBytes()); } fos.close(); } }
但是直接用win自带记事本打开不能换行?因为不能系统对换行符的识别是不一样的,
win的的换行符是:\r\n,
linux是:\n;
MAC是:\r;
而有些记事本软件是识别任意换行符的。
FileOutputStream fos = new FileOutputStream("fos.txt"); for(int i = 0; i < 10; i++){ fos.write(("hello" + i ).getBytes()); //win下的换行 fos.write("\r\n".getBytes()); } fos.close();
追加文件
import java.io.FileOutputStream; import java.io.IOException; /* * 追加? * FileOutputStream(String name, boolean append) 创建一个向具有指定 name 的文件中写入数据的输出文件流。 * append - 如果为 true,则将字节写入文件末尾处,而不是写入文件开始处 * * */ public class FileOutputStreamDemo { public static void main(String[] args) throws IOException { FileOutputStream fos = new FileOutputStream("fos.txt",true); for(int i = 0; i < 10; i++){ fos.write(("hello" + i ).getBytes()); //win下的换行 fos.write("\r\n".getBytes()); } fos.close(); } }
字节流写数据加入异常处理
import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; public class FileOutputStreamDemo { public static void main(String[] args){ //在try块外面定义 FileOutputStream fos = null; try{ fos = new FileOutputStream("fos.txt"); fos.write("hello".getBytes()); //异常从低到高抛出
FileOutputStream fos = new FileOutputStream("fos.txt");
这个对象定义在外面,并且赋初值为null。 }catch(FileNotFoundException e){ e.printStackTrace(); }catch(IOException e){ e.printStackTrace(); }catch(Exception e){ e.printStackTrace(); }finally{ //但是会保证,fos在try块中定义,finally看不到的,所以为了让finally看得到 //fos的定义必须放在try块外面,还必须给初始值 //close()方法也需要处理异常 try { fos.close(); } catch (IOException e) { e.printStackTrace(); } } } }
import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; public class FileOutputStreamDemo { public static void main(String[] args) { FileOutputStream fos = null; try { // 如果fos = new FileOutputStream("Z:fos.txt"); // 并没有Z盘,fos创建失败,就不需要close(),而finally中用来close() // 这样就不好了,所以finally块先判断是否为空,再处理。 fos = new FileOutputStream("fos.txt"); fos.write("hello".getBytes()); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } finally { //判断fos不能为空,再处理关闭流
//如果没有创建成功,而直接使用close()方法,会报空指针异常
//所以添加判断对象是否为空 if (fos != null) { try { fos.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
字节输入流
创建字节输入流对象
调用read()方法读取数据,并把数据显示在控制台
释放资源
public class FileInputStreamDemo { public static void main(String[] args) throws IOException { // TODO 自动生成的方法存根 FileInputStream fis = new FileInputStream("D:\\copy异常处理.txt"); //一次读取一个字节 //第一次读取 int by = fis.read(); System.out.println((char)by); //第二次读取 by = fis.read(); System.out.println((char)by); fis.close(); } }
这样太蛋疼了
public class FileInputStreamDemo { public static void main(String[] args) throws IOException { // TODO 自动生成的方法存根 FileInputStream fis = new FileInputStream("D:\\J2EE学习路线.doc"); //如果你读取到的数据是-1,那就说明读取到末尾了 int by = fis.read(); //只要不返回-1就一直度 while(by != -1){ System.out.print((char)by); by = fis.read(); } fis.close(); }
这样更简单一些:
public class FileInputStreamDemo { public static void main(String[] args) throws IOException { // TODO 自动生成的方法存根 FileInputStream fis = new FileInputStream("D:\\J2EE学习路线.doc"); //如果你读取到的数据是-1,那就说明读取到末尾了 int by = 0; //只要不返回-1就一直读,读取放在判断上就可以,只需要读取,判断,赋值就可以。
//这样如果文件中有中文是会乱码的,因为你把所有的读取都转换成char了。 while((by = fis.read()) != -1){ System.out.print((char)by); } fis.close(); } }
读写数据
import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; public class ReadWriteDemo { public static void main(String[] args) throws IOException { //数据源 读数据 FileInputStream fis = new FileInputStream("D:\\美服.txt"); //目的地 写数据 FileOutputStream fos = new FileOutputStream("D:\\美服账号.txt"); int by = 0; //从数据源读数据 while((by = fis.read()) != -1){ //把读到的数据写到目的地去 fos.write(by); } fis.close(); fos.close(); } }
上面程序运行完打开,中文并没有出现乱码。因为之前我们读取的数据写到控制台,都转换成char类型。现在是读取到的字节全部写到目标文件中,没有做转换,所以你可以打开,并且中文没有出现乱码。
复制视频
import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; public class ReadWriteDemo { //复制视频 public static void main(String[] args) throws IOException { //数据源 FileInputStream fis = new FileInputStream("D:\\你猜.mp4"); //目的地 FileOutputStream fos = new FileOutputStream("D:\\copy你猜.mp4"); int by = 0; //从数据源读数据 while((by = fis.read()) != -1){ //把读到的数据写到目的地去 fos.write(by); } fis.close(); fos.close(); } }
每次读取一个字节数组
import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; public class ReadWriteDemo { //复制视频 public static void main(String[] args) throws IOException { //数据源 FileInputStream fis = new FileInputStream("D:\\你猜.mp4"); //目的地 FileOutputStream fos = new FileOutputStream("D:\\copy你猜2.mp4"); //如果数据很大,一次读一个字节,就很慢,可以每次读取一个字节数组
//定义一个字节数组 byte[] bArray = new byte[1024]; int by = 0; while((by = fis.read(bArray)) != -1){ fos.write(by); } fis.close(); fos.close(); } }
但是上面程序,如果字节数组小于你定义的字节数组,输出的时候就会有问题。所以,建议写成下面这样:
import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; public class ReadWriteDemo { //复制视频 public static void main(String[] args) throws IOException { //数据源 FileInputStream fis = new FileInputStream("D:\\你猜.mp4"); //目的地 FileOutputStream fos = new FileOutputStream("D:\\copy你猜2.mp4"); //如果数据很大,一次读一个字节,就很慢,可以每次读取一个字节数组 //数组的大小一般是1024,或者1024的整数倍 byte[] bArray = new byte[1024]; int by = 0; /* * int read(byte[] b, int off, int len) 从此输入流中将最多 len 个字节的数据读入一个 byte 数组中。 * * int void write(byte[] b, int off, int len) 将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此文件输出流。 */ while((by = fis.read(bArray)) != -1){ //一定要这样,每次读出写入大小为bArrayz的字节数组 //但是每次最多写入by个,到文件尾的时候,返回-1,也就没得写了。 //by是读取的时候返回的数值 fos.write(bArray, 0, by); } fis.close(); fos.close(); } }
读取写入文件一定要用上面这种方法
字节缓冲流
字节流一次读写一个数组的速度明显比一次读写一个字节的速度快很多,这是加入了数组这样的缓冲区效果,java本身在设计的时候,也考虑到了这样的设计思想(装饰设计模式后面讲解,所以提供了字节缓冲流)
字节缓冲流
BufferedOutputStream
BufferedInputStre
构造方法可以指定缓冲区大小,一般默认的就够用了,不用再设置。
字节缓冲区流仅仅提供缓冲区,为高效设计的。但是基本的读取操作是基础的操作流设置的。
/* BufferedOutputStream(OutputStream out) 创建一个新的缓冲输出流,以将数据写入指定的底层输出流。 BufferedOutputStream(OutputStream out, int size) 创建一个新的缓冲输出流,以将具有指定缓冲区大小的数据写入指定的底层输出流。 BufferedInputStream(InputStream in) 创建一个 BufferedInputStream 并保存其参数,即输入流 in,以便将来使用。 BufferedInputStream(InputStream in, int size) 创建具有指定缓冲区大小的 BufferedInputStream 并保存其参数,即输入流 in,以便将来使用。 一般不需要设置缓冲区大小,默认的就够用了。 */
import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; public class BufferedDemo { public static void main(String[] args) throws IOException { FileOutputStream fos = new FileOutputStream("D:\\你guess猜.mp4"); FileInputStream fis = new FileInputStream("D:\\你猜.mp4"); //包装进去 BufferedOutputStream bos = new BufferedOutputStream(fos); BufferedInputStream bis = new BufferedInputStream(fis); //上面可以直接写成下面这样 //BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("D:\\你guess猜.mp4")); //BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D:\\你猜.mp4"));
//如果是用这种方法关闭资源只需要关闭bos和bis的资源,更简单一些,建议这么写 byte[] b = new byte[1024]; int len = 0; while((len = bis.read(b)) != -1){ bos.write(b, 0, len); }
bos.close();
bis.close();
fos.close();
fis.close(); } }
编码问题
由于字节流操作中文不是特别方便,所以java提供了转换流 字符流 = 字节流 + 编码表 编码表 由字符及对应的数值组成的一张表 常见编码表 ASCII/Unicode字符集:最高位是符号位,其余为数值位,Java用Unicode ISO-8859-1:拉丁码表,8位表示一个数据 GB2312/GBK/GB18030:简体中文码表/ BIG5:繁体字,大五码 UTF-8:最多用三个字节表示一个字符,能用一个的就用一个,一个表示不了的用两个,实在不行用三个。 构造方法:String(byte[] bytes, String charsetName):通过制定字符集解码字节数组,解码。 byte[] getBytes(String chasetName):通过制定的字符集合把字符串编码为字节数组,编码。 编码:把看得懂的编程看不懂 String ——————byte[] 解码:把看不懂的变成看得懂的 byte[] ——————String
转换流
//根据默认编码把字节流数据转换为字符流 OutputStreamWriter(OutputStream out, String charsetName) 创建使用指定字符集的 OutputStreamWriter。 //根据制定编码把字节流数据转换为字符流 OutputStreamWriter(OutputStream out) 创建使用默认字符编码的 OutputStreamWriter
import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; public class OutputStreamWriterDemo { public static void main(String[] args) throws IOException { //字节流转换为字符流 OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("a.txt")); osw.write("影流之主"); osw.close(); } }
注意:写入用的什么编码,看的时候用什么编码。
import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; public class OutputStreamWriterDemo { public static void main(String[] args) throws IOException { //字符流转换为字节流输出 //这里用什么编码输出,读回就要用什么编码 OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("a.txt"),"utf-8"); osw.write("影流之主"); osw.close(); } } //--------------------------------------------------------------------- import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; public class InputStreamReaderDemo { public static void main(String[] args) throws IOException { //写入用什么编码,读回用什么编码
//InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"),"GBK");
//写出去的时候是UTF-8,读回的时候也应该是,不然就无法正确解码
InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"),"utf-8"); //每次读一个字节 //注意:这里是读取的字符流,所以byte[] b = new byte[1024];是没用的 int len = 0; while((len = isr.read()) != -1){ System.out.print((char)len); } isr.close(); } }
OutputStreamWriter的write()方法:
public void write(int c):写一个字符 public void write(char[] cbuf):写一个字符数组 public void write(char[] cbuf, int off, int len):写一个字符数组的一部分 public void write(String str):写一个字符串 public void write(String str, int off, int len):写一个字符串的一部分
刷新缓冲区
import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; public class OutputStreamWriterDemo { public static void main(String[] args) throws IOException { OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("c.txt")); osw.write('a'); osw.write(97); //字符流的时候要记得刷新缓冲区,有时候是可以直接写入的,但是保险起见,close()之前flush()一下。 osw.flush(); osw.close(); } }
close()和flush()的区别?
close():关闭流对象,但是先刷新一次缓冲区,关闭之后,流对象不可以继续再使用了
flush():仅仅刷新缓冲区,刷新之后流对象还可以继续使用。
import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; public class InputStreamReaderDemo { public static void main(String[] args) throws IOException { InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"),"utf-8"); //每次读取一个字符 /*int len = 0; while((len = isr.read()) != -1){ System.out.print((char)len); }*/ char[] ch = new char[1024]; int len = 0; //每次读取一个字符数组 while((len = isr.read(ch)) != -1){ //把读取到的字符数组全部转换为字符串 System.out.println(new String(ch, 0,len)); } isr.close(); } }
用来写入写出字符的便捷类
FileWriter 此类的构造方法假定默认字符编码和默认字节缓冲区大小都是可接受的。要自己指定这些值,可以先在 FileOutputStream 上构造一个 OutputStreamWriter
FileReader 此类的构造方法假定默认字符编码和默认字节缓冲区大小都是适当的。要自己指定这些值,可以先在 FileInputStream 上构造一个 InputStreamReader
FileWriter(File file) 根据给定的 File 对象构造一个 FileWriter 对象。 FileWriter(File file, boolean append) 根据给定的 File 对象构造一个 FileWriter 对象。 FileWriter(FileDescriptor fd) 构造与某个文件描述符相关联的 FileWriter 对象。 FileWriter(String fileName) 根据给定的文件名构造一个 FileWriter 对象。 FileWriter(String fileName, boolean append) 根据给定的文件名以及指示是否附加写入数据的 boolean 值来构造 FileWriter 对象。 FileReader(File file) 在给定从中读取数据的 File 的情况下创建一个新 FileReader。 FileReader(FileDescriptor fd) 在给定从中读取数据的 FileDescriptor 的情况下创建一个新 FileReader。 FileReader(String fileName) 在给定从中读取数据的文件名的情况下创建一个新 FileReader。
字符流为了高效读写,提供了对应的字符缓冲流: BufferedWriter:字符缓冲输出流 将文本写入字符输出流,缓冲各个字符,从而提供单个字符,数组和字符串的高效写入 可以指定缓冲区大小,或者接受默认的大小,在大多数情况下,默认值就够用了。 BufferedWriter(Writer out) 创建一个使用默认大小输出缓冲区的缓冲字符输出流。 BufferedWriter(Writer out, int sz) 创建一个使用给定大小输出缓冲区的新缓冲字符输出流。 //传入一个字符输出流 BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("b.txt"))); 等价于 BufferedWriter bw = new BufferedWriter(new FileWriter("b.txt")); BufferedReader:字符缓冲输入流 BufferedReader(Reader in) 创建一个使用默认大小输入缓冲区的缓冲字符输入流。 BufferedReader(Reader in, int sz) 创建一个使用指定大小输入缓冲区的缓冲字符输入流。 //类似BufferedWriter
BufferedWriter和BufferedReader复制文件,跨平台换行:
import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; /* BufferedWriter void newLine() 写入一个行分隔符。 BufferedReader String readLine() 读取一个文本行,读取的是字符串,不包含换行符。 */ public class BufferedDemo2 { public static void main(String[] args) throws IOException { BufferedWriter bw = new BufferedWriter(new FileWriter("b.txt")); BufferedReader br = new BufferedReader(new FileReader("a.txt")); String len = null; //通过readLine()读取每一行 while((len = br.readLine()) != null){ bw.write(len); //写完读出来的一行之后,插入换行符,这种换行可以跨平台 bw.newLine(); } bw.close(); br.close(); } }
读取文件输出到控制台的时候要注意:println()和print()是不一样的。
char[] cbuf = new char[1024]; int read(char[] cbuf, int offset, int length) 将字符读入数组中的某一部分。 将读到的字符放在缓冲区char[]数组中
字节输入流
FileInputStream
BufferedInputStream
字节输出流
FileOutputStream
BufferedOutputStream
字节输出流有四种方式读取文件,每种流两种:读一个字节和读字节数组
字符输入流
BufferedReader
FileReader
字符输出流
BufferedWriter
FileWriter
字符输出流有五种方式读取文件,每种流两种:读一个字节和读字节数组
还有一个特殊的,BufferedReader和BufferedWriter可以每次读一行readLIne();
转换流
InputStreamReader
OutputStreamWriter
字节输入流 FileInputStream int read() 从此输入流中读取一个数据字节。 int read(byte[] b) 从此输入流中将最多 b.length 个字节的数据读入一个 byte 数组中,把数据放在byte数组中 BufferedInputStream int read() 读取一个字节 int read(byte[] b, int off, int len) 从此字节输入流中给定偏移量处开始将各字节读取到指定的 byte 数组中。 字节输出流 FileOutputStream write(byte[] b):写出数组b的数据到文件中 write(byte[] b, int off, int len):写出数组b的数据到文件中 BufferedOutputStream void write(byte[] b, int off, int len) 将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此缓冲的输出流。 void write(int b) 将指定的字节写入此缓冲的输出流。 read()方法,每次读一个字节,读完就返回-1.没读完,就返回读到的字节。 read(byte[] b)方法,每次读一个字节数组,数据放在数组中,读完了返回-1 int read(byte[] b, int off, int len) 从此输入流中将最多 len 个字节的数据读入一个 byte 数组中。 //读取写出的两种方式: 还有两种加了缓冲区 byte[] bArray = new byte[1024]; int by = 0; while((by = fis.read(bArray)) != -1){ //下面两句得到的结果是一样的,但是注意,如果直接写入一个数组,而文件尾实际没有那么多的字符就会出问题了 //所以建议写入数组的方式改变一下 //fos.write(bArray); //写成下面这样就可以 fos.write(bArray,0,by); } //每次写入一个字节 int by = 0; while((by = fis.read()) != -1){ fos.write(by); } 字节输出流有四种方式读取文件,每种流两种:读一个字节和读字节数组 字符输入流 BufferedReader 读一个字符、一个字符数组、还有一个特殊功能:读一个文本行(这个文本行只是字符串,没有换行符) FileReader 继承InputStream流 字符输出流 BufferedWriter 写字符数组的一部分、字符、字符数组、字符串、字符串的一部分 FileWriter 可以写出一个字符、字符数组的一部分、字符串、字符串的一部分 字符输出流有五种方式读取文件,每种流两种:读一个字符和读字符数组 还有一个特殊的,BufferedReader和BufferedWriter可以每次读一行readLIne(); 转换流 InputStreamReader 可以读取一个字符、一个字符数组 OutputStreamWriter 可以写出一个字符、字符数组的一部分、字符串、字符串的一部分
将ArrayList集合中的元素写入文件中:
import java.io.BufferedWriter; import java.io.FileWriter; import java.io.IOException; import java.util.ArrayList; import CollectionTest.Student; public class ArrayListDemo { public static void main(String[] args) throws IOException { ArrayList<Student> c = new ArrayList<Student>(); Student s1 = new Student("zed1", 1); Student s2 = new Student("zed2", 2); Student s3 = new Student("zed3", 3); Student s4 = new Student("zed4", 4); Student s5 = new Student("zed5", 5); Student s6 = new Student("zed6", 6); c.add(s1); c.add(s2); c.add(s3); c.add(s4); c.add(s5); c.add(s6); BufferedWriter br = new BufferedWriter(new FileWriter("c.txt")); //遍历集合 for(Student s : c){ //把集合拼接成一个字符串 String str = s.getName() + "---" + s.getAge(); //写入字符串 br.write(str); //换行 br.newLine(); //刷新 br.flush(); } br.close(); } }
读取文件并添加到集合中
import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; import java.util.ArrayList; public class duquTest { public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new FileReader("c.txt")); ArrayList<String> ar = new ArrayList<String>(); String len = null; //读取数据并添加到集合中 while((len = br.readLine()) != null){ ar.add(len); } for(String s : ar){ System.out.println(s); } br.close(); } }
DataInputStream和DataOutputStream
import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; public class DataStreamDemo { public static void main(String[] args) throws IOException { //write(); read(); } public static void write() throws IOException{ /* void flush() 清空此数据输出流。 int size() 返回计数器 written 的当前值,即到目前为止写入此数据输出流的字节数。 void write(byte[] b, int off, int len) 将指定 byte 数组中从偏移量 off 开始的 len 个字节写入基础输出流。 void write(int b) 将指定字节(参数 b 的八个低位)写入基础输出流。 void writeBoolean(boolean v) 将一个 boolean 值以 1-byte 值形式写入基础输出流。 void writeByte(int v) 将一个 byte 值以 1-byte 值形式写出到基础输出流中。 void writeBytes(String s) 将字符串按字节顺序写出到基础输出流中。 void writeChar(int v) 将一个 char 值以 2-byte 值形式写入基础输出流中,先写入高字节。 void writeChars(String s) 将字符串按字符顺序写入基础输出流。 void writeDouble(double v) 使用 Double 类中的 doubleToLongBits 方法将 double 参数转换为一个 long 值,然后将该 long 值以 8-byte 值形式写入基础输出流中,先写入高字节。 void writeFloat(float v) 使用 Float 类中的 floatToIntBits 方法将 float 参数转换为一个 int 值,然后将该 int 值以 4-byte 值形式写入基础输出流中,先写入高字节。 void writeInt(int v) 将一个 int 值以 4-byte 值形式写入基础输出流中,先写入高字节。 void writeLong(long v) 将一个 long 值以 8-byte 值形式写入基础输出流中,先写入高字节。 void writeShort(int v) 将一个 short 值以 2-byte 值形式写入基础输出流中,先写入高字节。 void writeUTF(String str) 以与机器无关方式使用 UTF-8 修改版编码将一个字符串写入基础输出流。 */ DataOutputStream dos = new DataOutputStream(new FileOutputStream("e.txt")); /*byte[] b = new byte[]{1 , 3, 4, 6}; dos.write(b, 1, 2);*/ dos.write(13); dos.writeBoolean(true); dos.writeByte(0); dos.writeBytes("输出字符串"); dos.writeChar(97); dos.writeChars("输出字符串2"); dos.writeDouble(13.14); dos.writeFloat(12.645F); dos.writeInt(146); dos.writeLong(24L); dos.writeShort(3905); dos.writeUTF("UTF字符串"); //注意此时打开e.txt文件是看不懂的乱码,用输入流输出就可以 dos.close(); } public static void read() throws IOException{ /* int read(byte[] b) 从包含的输入流中读取一定数量的字节,并将它们存储到缓冲区数组 b 中。 int read(byte[] b, int off, int len)从包含的输入流中将最多 len 个字节读入一个 byte 数组中。 boolean readBoolean() 参见 DataInput 的 readBoolean 方法的常规协定。 byte readByte() 参见 DataInput 的 readByte 方法的常规协定。 char readChar() 参见 DataInput 的 readChar 方法的常规协定。 double readDouble() 参见 DataInput 的 readDouble 方法的常规协定。 float readFloat() 参见 DataInput 的 readFloat 方法的常规协定。 void readFully(byte[] b) 参见 DataInput 的 readFully 方法的常规协定。 void readFully(byte[] b, int off, int len)参见 DataInput 的 readFully 方法的常规协定。 int readInt() 参见 DataInput 的 readInt 方法的常规协定。 long readLong() 参见 DataInput 的 readLong 方法的常规协定。 short readShort() 参见 DataInput 的 readShort 方法的常规协定。 int readUnsignedByte() 参见 DataInput 的 readUnsignedByte 方法的常规协定。 int readUnsignedShort()参见 DataInput 的 readUnsignedShort 方法的常规协定。 String readUTF() 参见 DataInput 的 readUTF 方法的常规协定。 static String readUTF(DataInput in) 从流 in 中读取用 UTF-8 修改版格式编码的 Unicode 字符格式的字符串;然后以 String 形式返回此字符串。 */ DataInputStream dis = new DataInputStream(new FileInputStream("e.txt")); //如果文件中有多个类型的值,而你只想读取其中一种类型,直接读就可以 Boolean booleanTest = dis.readBoolean(); System.out.println(booleanTest); dis.close(); } }
内存操作流 操作字节数组 ByteArrayInputStream ByteArrayOutputStream void close() 关闭 ByteArrayOutputStream 无效。 void close() 关闭 ByteArrayInputStream 无效。 操作字符数组 CharArrayReader CharArrayWriter 操作字符串 StringReader StringWriter
打印流
字节流打印流
PrintStream 为其他输出流添加了功能,使它们能够方便地打印各种数据值表示形式
字符打印流
PrintWriter 向文本输出流打印对象的格式化表示形式
使用该类的print方法,可以操作打印任意类型的数据。
import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; /* PrintWriter(Writer out, boolean autoFlush) 创建新 PrintWriter。 PrintWriter(OutputStream out, boolean autoFlush)通过现有的 OutputStream 创建新的 PrintWriter。*/ public class PrintWriterDemo { public static void main(String[] args) throws IOException { PrintWriter pw = new PrintWriter(new FileWriter("g.txt")); pw.print(140); pw.print(true); pw.print(214.2365); pw.print(false); pw.close(); //true为自动刷新,如果使用自动刷新的话要使用println()方法,使用Print()方法是无法自动刷新的 PrintWriter pw2 = new PrintWriter(new FileWriter("g.txt"),true); //println()方法相当于之前的write() + flush() + newLine()三合一疗程 pw2.println(35); pw2.println(false); pw2.close(); } }
打印流特点
只能操作目的地,不能操作 数据
可以操作任意类型的数据
如果启动自动刷新,能够自动刷新
可以操作文件的流
查看构造方法,如果同时有File类型和String类型的参数,一般来说就是可以操作文件的
打印流复制文本文件
import java.io.BufferedReader; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; public class PrintDemo { public static void main(String[] args) throws IOException { BufferedReader rd = new BufferedReader(new FileReader("D:\\EclipseWorkSpace\\StudyDemo\\src\\ioTest\\Student.java")); PrintWriter pw = new PrintWriter(new FileWriter("CopyStudent.java"), true); String line = null; while((line = rd.readLine()) != null){ pw.println(line); } rd.close(); pw.close(); } }
System类中的字段:in out
它们各代表了系统标准的输入和输出设备
默认输入设备是键盘,输出设备是显示器
System.in的类型是InputStream
System.out的类型是PrintStream是OutputStream的子类FilterOutputStream的子类
标准输入输出流
System类中的两个成员变量
public static final InputStream in “标准”输入流 public static final PtintStream out “标准”输出流 InputStream is = System.in; PrintStream ps = System.out; Sysetm.out.println(); 等于 new PintStream().println()方法 //这个输出语句本质是IO流操作,把数据输出到控制台。
键盘录入的方式:
A:main方法的args接受参数
B:Scanner(JDK5以后的)
Scanner sc = new Scanner(System.in); String s = sc.nextLine(); int x = sc.nextInt();
C:
//字节流 InputStream is = System.in; //转换为字符流 InputStreamReader isr = newInputStreamreader(is); //传入缓冲字符流中读取 BufferedReader br = new BufferedReader(isr); //通过BufferedReader的readLine()方法就可以每次读取一行 //简化版本 BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
输出
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)) bw.write("hello"); //注意,这个是字符流,要记得刷新 bw.flush(); bw.close();
RandomAccessFile
RandomAccessFile类不属于流,是Object类的子类
但它融合了InputStream和OutputStream的功能,支持对随机访问文件的读取和写入
RandomAccessFile(File file, String mode)
创建从中读取和向其中写入(可选)的随机访问文件流,该文件由 File 参数指定。
RandomAccessFile(String name, String mode)
创建从中读取和向其中写入(可选)的随机访问文件流,该文件具有指定名称
mode 参数指定用以打开文件的访问模式。允许的值及其含意为:
值 含意
"r" 以只读方式打开。调用结果对象的任何 write 方法都将导致抛出 IOException。
"rw" 打开以便读取和写入。如果该文件尚不存在,则尝试创建该文件。
"rws" 打开以便读取和写入,对于 "rw",还要求对文件的内容或元数据的每个更新都同步写入到底层存储设备。
"rwd" 打开以便读取和写入,对于 "rw",还要求对文件内容的每个更新都同步写入到底层存储设备。
SequenceInputStream 表示其他输入流的逻辑串联
import java.io.BufferedOutputStream; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.SequenceInputStream; public class SequenceInputStreamDemo { /*SequenceInputStream(InputStream s1, InputStream s2) 通过记住这两个参数来初始化新创建的 SequenceInputStream(将按顺序读取这两个参数,先读取 s1,然后读取 s2),以提供从此 SequenceInputStream 读取的字节*/ public static void main(String[] args) throws IOException { InputStream s1 = new FileInputStream("D:\\EclipseWorkSpace\\StudyDemo\\CopyStudent.java"); InputStream s2 = new FileInputStream("D:\\EclipseWorkSpace\\StudyDemo\\c.txt"); //将s1,s2写入sis,先读s1,再读s2 SequenceInputStream sis = new SequenceInputStream(s1,s2); //复制目的地 BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("BufferedCopy.txt")); byte[] b = new byte[1024]; int len = 0; //注意:读写入字节数组要用下面这种方式,可以避免一些不必要的麻烦 while((len = sis.read(b)) != -1){ bos.write(b, 0, len); } sis.close(); bos.close(); } }
但是这样只能传入两个文件,如果需要传入多个文件使用这个构造
SequenceInputStream(Enumeration<? extends InputStream> e) 通过记住参数来初始化新创建的 SequenceInputStream,该参数必须是生成运行时类型为 InputStream 对象的 Enumeration 型参数。
Vector<InputStream> v = new Vector<InputStream>(); InputStream i1 = new FileInputStream("BUfferedCopy.txt"); InputStream i2 = new FileInputStream("c.txt"); InputStream i3 = new FileInputStream("a.txt"); v.add(i1); v.add(i2); v.add(i3); //调用集合的elements()方法,传入集合中的IO流 SequenceInputStream sis = new SequenceInputStream(v.elements()); BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("BufferedCopy2.txt")); byte[] b = new byte[1024]; int len = 0; while((len = sis.read(b)) != -1){ bos.write(b, 0, len); } sis.close(); bos.close();
Vector: Enumeration<E> elements() 返回此向量的组件的枚举。
序列化流
序列化:(写)把对象按照流一样的方式存入文本文件或在网络中传入
反序列化:(读)把文本文件中的流对象数据或网络中的对象数据还原成对象
ObjectOutputStream
将Java对象写入OutputStream。
反序列化流
ObjectInputStream
//序列化类要实现Serializable接口 public class Student implements Serializable{ }
import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; public class ObjectStreamDemo { public static void main(String[] args) throws IOException, ClassNotFoundException { write(); read(); } private static void read() throws IOException, IOException, ClassNotFoundException { ObjectInputStream ois = new ObjectInputStream(new FileInputStream("SerializableDemo.txt")); //读取返回的是Object类型,用Object接收的,但是如果直接输出接到的对象,输出的是Student类的toString()方法,本质还是Student类 Object obj = ois.readObject(); ois.close(); System.out.println(obj); } private static void write() throws IOException { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("SerializableDemo.txt")); Student s1 = new Student("zed1", 1); //写完打开文件出现的是乱码,读不懂的字符,这个时候要用反序列化去读它 oos.writeObject(s1); oos.close(); } }
序列化数据后,再次修改类文件,读取数据出问题
Student.class -- id = 100
write数据: oos.txt -- id = 100
read数据: oos.txt -- id = 100
现在
Student.class -- id = 200
write数据: oos.txt -- id = 100
read数据: oos.txt -- id = 100
成员变量变化,序列化的值也会变,不匹配了
而我们实际开发中,我们可能需要以前的数据,这个时候我们需要把这个ID值在Java文件中设为固定的值,这样就可以了。
实在开发工具中实现类Serializable接口的类都会有提示,比如Ecplise会有黄色警告线:
private static final long serialVersionUID = 4119635118382250816L;
使用transient关键字声明不需要序列化的成员变量,使用该关键字声明的变量不会被序列化,如果使用反序列化读取这个值,是读不出来的,输出的是默认值。
Properties
Properties 类表示了一个持久的属性集。Properties 可保存在流中或从流中加载。属性列表中每个键及其对应值都是一个字符串。
这里必须是Properties集合 void load(InputStream inStream) 从输入流中读取属性列表(键和元素对)。 void load(Reader reader) 按简单的面向行的格式从输入字符流中读取属性列表(键和元素对)。 void store(OutputStream out, String comments) 以适合使用 load(InputStream) 方法加载到 Properties 表中的格式,将此 Properties 表中的属性列表 (键和元素对)写入输出流。 void store(Writer writer, String comments) 以适合使用 load(Reader) 方法的格式,将此 Properties 表中的属性列表(键和元素对)写入输出字符。