发表时间:2022-03-25来源:网络
流,表示任何有能力产生数据的数据源对象或者是有能力接收数据的接收端对象,它屏蔽了实际的I/O设备中处理数据的细节。
java1.0版本中,I/O库中与输入有关的所有类都将继承InputStream,与输出有关的所有类继承OutputStream,用以操作二进制数据。
java1.1版本对I/O库进行了修改:
在原先的库中新增了新类,如ObjectInputStream和ObjectOutputStream。 增加了Reader和Writer,提供了兼容Unicode与面向字符的I/O功能。 在Reader和Writer类层次结构中,提供了使字符与字节相互转化的类,OutputStreamWriter和InputStreamReader。两个不同的继承层次结构拥有相似的行为,它们都提供了读(read)和写(write)的方法,针对不同的情况,提供的方法也是类似的。
java1.4版本的java.nio.*包中引入新的I/O类库,这部分以后再做学习。
在实际应用中,异常处理的方式都需要按照下面的结构进行,本篇为了节约篇幅,之后都将采用向上抛出的方式处理异常。
//将流对象放在try之外声明,并附为null,保证编译,可以调用close FileWriter writer = null; try { //将流对象放在里面初始化 writer = new FileWriter("D:\\b.txt"); writer.write("abc"); //防止关流失败,没有自动冲刷,导致数据丢失 writer.flush(); } catch (IOException e) { e.printStackTrace(); } finally { //判断writer对象是否成功初始化 if(writer!=null) { //关流,无论成功与否 try { writer.close(); } catch (IOException e) { e.printStackTrace(); }finally { //无论关流成功与否,都是有意义的:标为垃圾对象,强制回收 writer = null; } } } 并不会直接将数据写入文件中,而是先写入缓冲区,待缓冲区满了之后才将缓冲区的数据写入文件。 假设数据写入缓冲区时且缓冲区还没满,数据还没能够写入文件时,程序就已经结束,会导致数据惨死缓冲区,这时需要手动冲刷缓冲区,将缓冲区内的数据冲刷进文件中。writer.flush();。 数据写入完毕,释放文件以允许别的流来操作该文件。关闭流可以调用close()方法,值得注意的是,在close执行之前,流会自动进行一次flush的操作以避免数据还残存在缓冲区中,但这并不意味着flush操作是多余的。JDK1.7提出了对流进行异常处理的新方式,任何AutoCloseable类型的对象都可以用于try-with-resourses语法,实现自动关闭。
要求处理的对象的声明过程必须在try后跟的()中,在try代码块之外。
try(FileWriter writer = new FileWriter("D:\\c.txt")){ writer.write("abc"); }catch (IOException e){ e.printStackTrace(); }处理异常的正确方式在输入的结构中已说明,这边就不进行繁杂的异常处理,执行直接向上抛出的不负责任的操作。
public static void main(String[] args) throws IOException { FileReader reader = new FileReader("D:\\b.txt"); //定义一个变量记录每次读取的字符 int m; //读取到末尾为-1 while((m = reader.read())!=-1){ System.out.print(m); } reader.close(); } 文件字符输入流没有缓冲区。 read方法用来从文件中读取字符,每次只读取一个。 定义变量m记录读取的字符,以达到末尾为终止条件。m!=-1时,终止循环。 读取结束,执行关流操作。当然,每次读取一个字符,怪麻烦的,我们可以改进一下:
//定义数组作为缓冲区 char[] cs = new char[5]; //定义变量记录读取字符的个数 int len; while ((len = reader.read(cs)) != -1) { System.out.println(new String(cs, 0, len)); } reader.close();运用文件字符输入与输出的小小案例:
public static void copyFile(FileReader reader, FileWriter writer) throws IOException { //利用字符数组作为缓冲区 char[] cs = new char[5]; //定义变量记录读取到的字符个数 int m; while((m = reader.read(cs))!=-1){ //将读取到的内容写入新的文件中 writer.write(cs,0,m); } reader.close(); writer.close(); }缓冲流基于装饰设计模式,即利用同类对象构建本类对象,在本类中进行功能的改变或者增强。
例如,BufferedReader本身就是Reader对象,它接收了一个Reader对象构建自身,自身提供缓冲区和其他新增方法,通过减少磁盘读写次数来提高输入和输出的速度。

除此之外,字节流同样也存在缓冲流,分别是BufferedInputStream和BufferedOutputStream。
利用转换流可以实现字符流和字节流之间的转换。
OutputStreamWriter public static void main(String[] args) throws Exception { //在构建转换流时需要传入一个OutputStream 字节流 OutputStreamWriter ow = new OutputStreamWriter( new FileOutputStream("D:\\b.txt"),"utf-8"); //给定字符--> OutputStreamWriter转化为字节-->以字节流形式传入文件FileOutputStream //如果没有指定编码,默认使用当前工程的编码 ow.write("天乔巴夏"); ow.close(); }最终与文件接触的是字节流,意味着将传入的字符转换为字节。
InputStreamReader public static void main(String[] args) throws IOException { //以字节形式FileInputStream读取,经过转换InputStreamReader -->字符 //如果没有指定编码。使用的是默认的工程的编码 InputStreamReader ir = new InputStreamReader( new FileInputStream("D:\\b.txt")); char[] cs = new char[5]; int len; while((len=ir.read(cs))!=-1){ System.out.println(new String(cs,0,len)); } ir.close(); }最初与文件接触的是字节流,意味着将读取的字节转化为字符。
缓冲流基于适配器设计模式,将某个类的接口转换另一个用户所希望的类的接口,让原本由于接口不兼容而不能在一起工作的类可以在一起进行工作。
以OutputStreamWriter为例,构建该转换流时需要传入一个字节流,而写入的数据最开始是由字符形式给定的,也就是说该转换流实现了从字符向字节的转换,让两个不同的类在一起共同办事。

程序的所有输入都可以来自于标准输入,所有输出都可以发送到标准输出,所有错误信息都可以发送到标准错误。
可以直接使用System.out和System.err,但是在读取System.in之前必须对其进行封装,例如我们之前经常会使用的读取输入:Scanner sc = new Scanner(System.in);实际上就封装了System.in对象。
标准流都是字节流。 标准流对应的不是类而是对象。 标准流在使用的时候不用关闭。 /** * 从控制台获取一行数据 * @throws IOException readLine 可能会抛出异常 */ public static void getLine() throws IOException { //获取一行字符数据 -- BufferedReader //从控制台获取数据 -- System.in //System是字节流,BufferedReader在构建的时候需要传入字符流 //将字节流转换为字符流 BufferedReader br = new BufferedReader( new InputStreamReader(System.in)); //接收标准输入并转换为大写 String str = br.readLine().toUpperCase(); //发送到标准输出 System.out.println(str); }通过转换流,将System.in读取的标准输入字节流转化为字符流,发送到标准输出,打印显示。
打印流只有输出流没有输入流
PrintStream: 打印字节流 public static void main(String[] args) throws IOException { //创建PrintStream对象 PrintStream p = new PrintStream("D:\\b.txt"); p.write("abc".getBytes()); p.write("def".getBytes()); p.println("abc"); p.println("def"); //如果打印对象,默认调用对象身上的toString方法 p.println(new Object()); p.close(); } PrintWriter:打印字符流 //将System.out转换为PrintStream public static void main(String[] args) { //第二个参数autoFlash设置为true,否则看不到结果 PrintWriter p = new PrintWriter(System.out,true); p.println("hello,world!"); }以第一种构建方式为例,我们之前说过,Enumeration可以通过Vector容器的elements方法创建。
public static void main(String[] args) throws IOException { FileInputStream in1 = new FileInputStream("D:\\1.txt"); FileInputStream in2 = new FileInputStream("D:\\a.txt"); FileInputStream in3 = new FileInputStream("D:\\b.txt"); FileInputStream in4 = new FileInputStream("D:\\m.txt"); FileOutputStream out = new FileOutputStream("D:\\union.txt"); //准备一个Vector存储输入流 Vector v = new Vector(); v.add(in1); v.add(in2); v.add(in3); v.add(in4); //利用Vector产生Enumeration对象 Enumeration e = v.elements(); //利用迭代器构建合并流 Se s = new Se(e); //读取 byte[] bs = new byte[10]; int len; while((len = s.read(bs))!=-1){ out.write(bs,0,len); } out.close(); s.close(); }创建一个Person类。
//必须实现Serializable接口 class Person implements Serializable { //序列化ID serialVersionUID private static final long serialVersionUID = 6402392549803169300L; private String name; private int age; public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } }创建序列化流,将对象转化为字节,并写入"D:\1.data"。
public class ObjectOutputStreamDemo { public static void main(String[] args) throws IOException { Person p = new Person(); p.setAge(18); p.setName("Niu"); //创建序列化流 //真正将数据写出的流是FileOutputStream //ObjectOutputStream将对象转化为字节 ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("D:\\1.data")); out.writeObject(p); out.close(); } }创建反序列化流,将从"D:\1.data"中读取的字节转化为对象。
public static void main(String[] args) throws IOException, ClassNotFoundException { //创建反序列化流 //真正读取文件的是FileInputStream //ObjectInputStream将读取的字节转化为对象 ObjectInputStream in = new ObjectInputStream(new FileInputStream("D:\\1.data")); //读取数据必须进行数据类型的强制转换 Person p = (Person)in.readObject(); in.close(); System.out.println(p.getName());//Niu System.out.println(p.getAge());//18 }需要注意的是:
如果一个对象要想被序列化,那么对应的类必须实现接口serializable,该接口没有任何方法,仅仅作为标记使用。 被static或transient修饰的属性不会进行序列化。如果属性的类型没有实现serializable接口但是也没有用这两者修饰,会抛出NotSerializableException。 在对象序列化的时候,版本号会随着对象一起序列化出去,在反序列化的时候,对象中的版本号和类中的版本号进行比较,如果版本号一致,则允许反序列化。如果不一致,则抛出InvalidClassException。 集合允许被整体序列化 集合及其中元素会一起序列化出去。关于版本号:
一个类如果允许被序列化,那么这个类中会产生一个版本号 serialVersionUID。
如果没有手动指定版本号,那么在编译的时候自动根据当前类中的属性和方法计算一个版本号,也就意味着一旦类中的属性发生改变,就会重新计算新的,导致前后不一致。 但是,手动指定版本号的好处就是,不需要再计算版本号。版本号的意义在于防止类产生改动导致已经序列化出去的对象无法反序列化回来。版本号必须用static final修饰,本身必须是long类型。
写在最后:如果本文有叙述错误之处,还望评论区批评指正,共同进步。
参考资料:《Java 编程思想》、《Java语言程序设计》、《大话设计模式》
皓盘云建最新版下载v9.0 安卓版
53.38MB |商务办公
ris云客移动销售系统最新版下载v1.1.25 安卓手机版
42.71M |商务办公
粤语翻译帮app下载v1.1.1 安卓版
60.01MB |生活服务
人生笔记app官方版下载v1.19.4 安卓版
125.88MB |系统工具
萝卜笔记app下载v1.1.6 安卓版
46.29MB |生活服务
贯联商户端app下载v6.1.8 安卓版
12.54MB |商务办公
jotmo笔记app下载v2.30.0 安卓版
50.06MB |系统工具
鑫钜出行共享汽车app下载v1.5.2
44.7M |生活服务
2022-03-26
2022-03-26
2022-03-26
2022-03-26
2022-03-26
2022-03-26
2022-03-26
2022-03-26
2022-02-15
2022-02-14