[Java IO]02_字节流

概要

字节流有两个核心抽象类:InputStream 和 OutputStream。所有的字节流类都继承自这两个抽象类。

InputStream 负责输入,OutputStream 负责输出。

字节流主要操作byte类型数据。

以下为 JDK8 版本中字节流的族谱图:

具体详情可查看Java API文档。

由上图可以看出,InputStream 和 OutputStream对于数据源的操作往往是成对出现的。

InputStream

InputStream的作用是用来表示哪些从不同数据源产生输入的类。

InputStream类型表

功能

构造器

ByteArrayInputStream

允许将内存的缓冲区当做InputStream使用

缓冲区,字节将从中取出

StringBufferInputStream

将String转换成InputStream

字符串。底层实现实际使用StringBuffer

FileInputStream

从文件中读取信息

字符串,表示文件名、文件或FileDescriptor对象

PipedInputStream

产生用于写入相关PipedOutputStream的数据。实现“管道化”概念

PipedOutputStream

SequenceInputStream

将多个InputStream对象合并为一个InputStream

两个InputStream对象或一个容纳InputStream对象的容器Enumeration

FilterInputStream

抽象类,作为“装饰器”的接口。其中“装饰器”为其他InputStream类提供有用功能

OutputStream

OutputStream决定了数据的输出形式。

OutputStream类型表

功能

构造器

ByteArrayOutputStream

允许将内存的缓冲区当做InputStream使用

缓冲区初始化尺寸(可选的)

FileOutputStream

从文件中读取信息

字符串,表示文件名、文件或FileDescriptor对象

PipedOutputStream

产生用于写入相关PipedOutputStream的数据。实现“管道化”概念

PipedInputStream

FilterOutputStream

抽象类,作为“装饰器”的接口。其中“装饰器”为其他OutputStream类提供有用功能

文件字节流

文件字节流有两个类:FileOutputStream 和 FileInputStream。

它们提供了方法将字节写入到文件和将数据以字节形式从文件中读取出来。

一般情形下,文件字节流操作遵循以下几个步骤:

(1)使用File类绑定一个文件。

(2)把File对象绑定到流对象上。

(3)进行读、写操作。

(4)关闭输入、输出。

FileOutputStream

向文件写入数据

import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.OutputStream;
 
 public class FileOutputStreamDemo {
 public static void write1(OutputStream out, byte[] b) throws IOException {
         out.write(b); // 将内容输出,保存文件
     }
 
 public static void write2(OutputStream out, byte[] b) throws IOException {
 for (int i = 0; i < b.length; i++) { // 采用循环方式写入
             out.write(b[i]); // 每次只写入一个内容
         }
     }
 
 public static void main(String args[]) throws Exception {
 // 第1步、使用File类找到一个文件
         File f = new File("d:" + File.separator + "test.txt"); // 声明File对象
 
 // 第2步、通过子类实例化父类对象
         OutputStream out = new FileOutputStream(f); // 通过对象多态性,进行实例化
 // 实例化时,默认为覆盖原文件内容方式;如果添加true参数,则变为对原文件追加内容的方式。
 // OutputStream out = new FileOutputStream(f, true);
 
 // 第3步、进行写操作
         String str = "Hello World\r\n"; // 准备一个字符串
  byte b[] = str.getBytes(); // 只能输出byte数组,所以将字符串变为byte数组
         write1(out, b);
 // write2(out, b);
 
 // 第4步、关闭输出流
         out.close();
     }
 };

FileInputStream

从文件中读取数据

import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 
 public class FileInputStreamDemo {
 public static void read1(InputStream input, byte[] b) throws IOException {
 int len = input.read(b); // 读取内容
         System.out.println("读入数据的长度:" + len);
     }
 
 public static void read2(InputStream input, byte[] b) throws IOException {
 for (int i = 0; i < b.length; i++) {
             b[i] = (byte) input.read(); // 读取内容
         }
     }
 
 public static void read3(InputStream input, byte[] b) throws IOException {
 int len = 0;
 int temp = 0; // 接收每一个读取进来的数据
  while ((temp = input.read()) != -1) {
 // 表示还有内容,文件没有读完
             b[len] = (byte) temp;
             len++;
         }
     }
 
 public static void main(String args[]) throws Exception { // 异常抛出,不处理
 // 第1步、使用File类找到一个文件
         File f = new File("d:" + File.separator + "test.txt"); // 声明File对象
 
 // 第2步、通过子类实例化父类对象
         InputStream input = new FileInputStream(f); // 准备好一个输入的对象
 
 // 第3步、进行读操作
 // 有三种读取方式,体会其差异
  byte[] b = new byte[(int) f.length()];
         read1(input, b);
 // read2(input, b);
 // read3(input, b);
 
 // 第4步、关闭输入流
         input.close();
         System.out.println("内容为:\n" + new String(b)); // 把byte数组变为字符串输出
     }
 };

内存操作流

ByteArrayInputStream 和 ByteArrayOutputStream是用来完成内存的输入和输出功能。

内存操作流一般在生成一些临时信息时才使用。  如果临时信息保存在文件中,还需要在有效期过后删除文件,这样比较麻烦。

使用内存操作流完成一个转换为小写字母的程序

import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 
 public class ByteArrayDemo {
 public static void main(String args[]) {
         String str = "HELLOWORLD";
 
         ByteArrayInputStream bis = new ByteArrayInputStream(str.getBytes()); // 内存输入流
         ByteArrayOutputStream bos = new ByteArrayOutputStream(); // 内存输出流
 
 // 用ByteArrayInputStream读取字符串内容,
 // 全转为小写后,写入ByteArrayOutputStream
  int temp = 0;
 while((temp = bis.read()) != -1) {
 char c = (char) temp; // 读取的数字变为字符
             bos.write(Character.toLowerCase(c)); // 将字符变为小写
         }
 
 // 所有的数据就全部都在ByteArrayOutputStream中
         String newStr = bos.toString();    // 取出内容
  try {
             bis.close();
             bos.close();
         } catch(IOException e) {
             e.printStackTrace();
         }
         System.out.println(newStr);
     }
 };

管道流

管道流的主要作用是可以进行两个线程间的通信。

如果要进行管道通信,则必须把PipedOutputStream连接在PipedInputStream上。

为此,PipedOutputStream中提供了connect()方法。

import java.io.IOException;
 import java.io.PipedInputStream;
 import java.io.PipedOutputStream;
 
 class Send implements Runnable {
 private PipedOutputStream pos = null; // 管道输出流
 
 public Send() {
 this.pos = new PipedOutputStream(); // 实例化输出流
     }
 
 public void run() {
         String str = "Hello World!!!"; // 要输出的内容
  try {
 this.pos.write(str.getBytes());
         } catch (IOException e) {
             e.printStackTrace();
         }
 try {
 this.pos.close();
         } catch (IOException e) {
             e.printStackTrace();
         }
     }
 
 public PipedOutputStream getPos() { // 得到此线程的管道输出流
  return this.pos;
     }
 };
 
 class Receive implements Runnable {
 private PipedInputStream pis = null; // 管道输入流
 
 public Receive() {
 this.pis = new PipedInputStream(); // 实例化输入流
     }
 
 public void run() {
 byte b[] = new byte[1024]; // 接收内容
  int len = 0;
 try {
             len = this.pis.read(b); // 读取内容
         } catch (IOException e) {
             e.printStackTrace();
         }
 try {
 this.pis.close(); // 关闭
         } catch (IOException e) {
             e.printStackTrace();
         }
         System.out.println("接收的内容为:" + new String(b, 0, len));
     }
 
 public PipedInputStream getPis() {
 return this.pis;
     }
 };
 
 public class PipedDemo {
 public static void main(String args[]) {
         Send s = new Send();
         Receive r = new Receive();
 try {
             s.getPos().connect(r.getPis()); // 连接管道
         } catch (IOException e) {
             e.printStackTrace();
         }
 new Thread(s).start(); // 启动线程
  new Thread(r).start(); // 启动线程
     }
 };

数据操作流

数据操作流提供了格式化读入和输出数据的方法,分别为DataInputStream 和 DataOutputStream。

DataOutputStream

将格式化的订单数据写入到文件

import java.io.DataOutputStream;
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
 
 public class DataOutputStreamDemo {
 public static void main(String args[]) throws IOException {
 // 第1步、使用File类找到一个文件
         File f = new File("d:" + File.separator + "order.txt");
 
 // 第2步、通过子类实例化父类对象
         DataOutputStream dos = new DataOutputStream(new FileOutputStream(f)); // 实例化数据输出流对象
 
 // 第3步、进行写操作
         String names[] = { "衬衣", "手套", "围巾" }; // 商品名称
  float prices[] = { 98.3f, 30.3f, 50.5f }; // 商品价格
  int nums[] = { 3, 2, 1 }; // 商品数量
 // 循环输出
  for (int i = 0; i < names.length; i++) {
             dos.writeChars(names[i]);
             dos.writeChar('\t');
             dos.writeFloat(prices[i]);
             dos.writeChar('\t');
             dos.writeInt(nums[i]);
             dos.writeChar('\n');
         }
 
 // 第4步、关闭输出流
         dos.close();
     }
 };

DataInputStream

将订单文件中的格式化数据读取出来

import java.io.DataInputStream;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
 
 
 public class DataInputStreamDemo {
 public static void main(String args[]) throws IOException { // 所有异常抛出
 // 第1步、使用File类找到一个文件
         File f = new File("d:" + File.separator + "order.txt");
 
 // 第2步、通过子类实例化父类对象
         DataInputStream dis = new DataInputStream(new FileInputStream(f)); // 实例化数据输入流对象
 
 // 第3步、进行读操作
         String name = null; // 接收名称
  float price = 0.0f; // 接收价格
  int num = 0; // 接收数量
  char temp[] = null; // 接收商品名称
  int len = 0; // 保存读取数据的个数
  char c = 0; // '\u0000'
  try {
 while (true) {
                 temp = new char[200]; // 开辟空间
                 len = 0;
 while ((c = dis.readChar()) != '\t') { // 接收内容
                     temp[len] = c;
                     len++; // 读取长度加1
                 }
                 name = new String(temp, 0, len); // 将字符数组变为String
                 price = dis.readFloat(); // 读取价格
                 dis.readChar(); // 读取\t
                 num = dis.readInt(); // 读取int
                 dis.readChar(); // 读取\n
                 System.out.printf("名称:%s;价格:%5.2f;数量:%d\n", name, price, num);
             }
         } catch (Exception e) {
         }
 
 // 第4步、关闭输入流
         dis.close();
     }
 };

合并流

合并流的主要功能是将多个InputStream合并为一个InputStream流。

合并流的功能由SequenceInputStream完成。

需要稍微留意的是,由前面的字节流族谱图,可以得知并没有对应的OutputStream。

将两个文件内容合并为一个文件

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.SequenceInputStream;

public class SequenceDemo {
 public static void main(String args[]) throws IOException {
 // 第1步,绑定要操作的文件
        InputStream is1 = new FileInputStream("d:" + File.separator + "a.txt"); // 输入流1
        InputStream is2 = new FileInputStream("d:" + File.separator + "b.txt"); // 输入流2    
        OutputStream os = new FileOutputStream("d:" + File.separator + "ab.txt"); // 输出流
 
 // 第2步,绑定要合并的InputStream对象到SequenceInputStream
        SequenceInputStream sis = new SequenceInputStream(is1, is2); // 实例化合并流

 // 读取两个InputStream流的数据,然后合并输出到OutputStream
 int temp = 0; // 接收内容
 while ((temp = sis.read()) != -1) { // 循环输出
            os.write(temp); // 保存内容
        }
 
 // 第4步,关闭相关流
        sis.close();
        os.close();    
        is1.close();
        is2.close();
    }
};

参考资料

《Java编程思想》

《Java开发实战经典》

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏pangguoming

Java编程最差实践(常见编程错误典范)

转载自  http://macrochen.iteye.com/blog/1393502

442
来自专栏Java Edge

深入理解JDK动态代理机制

2996
来自专栏web前端教室

javascript 红皮高程(19)-- 逻辑与

每天只学一点点啊,多学我也不乐意哈,, 今天来看看“逻辑与”,它是由二个(&&)符号组成的,有二个操作数,一左一右的,就这样: var aa = bb && c...

1748
来自专栏ShaoYL

OC语言Block 续

32312
来自专栏专注 Java 基础分享

Java 字节流操作

     在java中我们使用输入流来向一个字节序列对象中写入,使用输出流来向输出其内容。C语言中只使用一个File包处理一切文件操作,而在java中却有着...

2099
来自专栏落影的专栏

Swift学习笔记

这是一篇学习swift的笔记 Objective-C是很好的语言,Runtime机制、消息机制等也是爱不释手。 Swift一直在更新,闲暇时间学一遍。学习的B...

4097
来自专栏大前端_Web

jsvascript—谜之this?

版权声明:本文为吴孔云博客原创文章,转载请注明出处并带上链接,谢谢。 https://blog.csdn.net/wkyseo/articl...

1074
来自专栏马洪彪

Java设计模式(五)Prototype原型模式

一、场景描述 创建型模式中,从工厂方法模式,抽象工厂模式,到建造者模式,再到原型模式,我的理解是,创建对象的方式逐步从编码实现转向内存对象处理。 例如,在“仪器...

2667
来自专栏noteless

[二十六]JavaIO之再回首恍然(如梦? 大悟?)

我们上面列出来了ByteArray  File   Piped    Object  String  CharArray 这几种常用的数据源形式

823
来自专栏屈定‘s Blog

设计模式--装饰者模式思考

装饰者模式实际上是一直提倡的组合代替继承的实践方式,个人认为要理解装饰者模式首先需要理解为什么需要组合代替继承,继承又是为什么让人深恶痛绝.

1312

扫码关注云+社区