Java笔记二十一.深入解析I/O编程之节点流

深入解析I/O编程之节点流

转载请表明出处:http://blog.csdn.net/u012637501(嵌入式_小J的天空)

在上一节我们说到IO流主要分为两个大类:节点流类和过滤流类(也称包装类)。程序用于直接操作目标设备所对应的类叫节点流类;为更加灵活方便地读写各种类型的数据,程序也可用通过一个间接流类去调用节点流类而这个简介流类即为过滤流类(也称为包装类)。

总之,无论是节点流类还是过滤流类,它们都属于字节流或字符流其中的一种。下面我们介绍一下常见的节点流类有哪些。

字****节****流

一、InputStream与OutoutStream

InputStream类为所有字节输入流的父类(是抽象类),通过该类对象实现从目标设备中连续读取字节数据。OutputStream类为所有字节输出流的父类(抽象类),通过该类对象实现向目标设备中连续写入字节数据。由于InputStream和OutputStream对象是两个抽象类,不能表明具体对应哪种IO设备。它们下面有许多子类,包括网络、管道、内存、文件等具体的IO设备,如FileInputStream类对应的是文件输入流,是一个节点流类。我们将这些节点流类所对应的IO源和目标称为流节点(Node)。

1.InputStream类

(1)功能:InputStream定义了Java的输入流模型,通过该类对象(输入流)实现从目标设备中连续读取字节数据。InputStream是一个抽象类,程序中实际使用的是它的各种子类对象。

(2)构造方法

>InputStream()

(3)常用方法

>abstract int read():返回下一个输入字节的整型表示,如果返回-1表示遇到流的末尾;

>int read(byte[] b):从输入流读取b.length个字节存放到字节数组b中并返回实际读入的字节数;

>int read(byte[] b,int off,int len):从输入流的数据读入到字节数组b中从脚标为off开始的len个数组元素中;

>int skip(long n):跳过输入流上的n个字节并返回实际跳过的字节数;

>int available():返回当前输入流中可读的字节数

>void mark(int readlimit):在输入流的当前位置处放上一个标识,允许最多再读取readlimit个字节

>void reset():把输入指针返回到以前所做的标识处;

>boolean markSupported():如果当前流支持mark/reset操作就返回true;

>void close():在操作完一个流后使用该方法关闭该流,释放流对象和与该流相关的系统资源;

2.OutputStream类

(1)功能:通过该类对象(输入流)实现向目标设备中连续写入字节数据。

(2)构造方法

>OutputStream()

(3)常用方法

>void write(int b):将一个自留写到输出流;

>void write(byte[]
b):将整个字节数组写到输出流中;

>void write(byte[]
b,int off,int len):将字节数组b中的从off开始的len个字节写到输出流;

>void flush():将输出流中的数据写入到目标设备中并清空缓冲区;

>void close():关闭输出流

提升:输入输出流类是相对程序而言的,而不是代表文件的。如果我们想要将A文件中内容写入B文件中,我们可以这样做:创建一个输入流来完成对A文件的读取数据操作,创建一个输出类完成对B文件的写数据操作。

二、FileInputStream与FileOutputStream

这两个流节点用来操作磁盘文件(文本、图片、音频等),在创建一个FIleInputStream对象时通过构造函数指定文件的路径和名字(该文件应是存在且可读的)。在创建一个FileOutputStream对象时指定文件,如果存在则覆盖该文件。即通过FileOutputStream类往其连接(对应)的一个文件中写入数据,通过FileInputStream类从其连接(对应)的文件将内容读取出来。

1.FileInputStream类

(1)功能:为一个磁盘文件创建一个输入流,通过FileInputStream流对象实现从文件读取连续字节数据。

(2)构造方法

>FileInputStream(String name):创建一个磁盘文件输入流并将其连接到指定的文件;

>FileInputStream(File file):创建一个磁盘文件输入流并将其连接到File对象指定的文件;

举例:

//第一种构造方法

FileInputStream stream=new FileInputStream("hello.test");

//第二种构造方法(允许在把文件连接到输入流之前进一步操作文件属性)

File file=new File();

FileInputStream stream = new FileInputStream(file);

(3)常用方法

>int read(byte[] b):从输入流读取b.length个字节存放到字节数组b中并返回实际读入的字节数;

>int read(byte[] b,int off,int len):从输入流的数据读入到字节数组b中从脚标为off开始的len个数组元素中;

>int skip(long n):跳过输入流上的n个字节并返回实际跳过的字节数;

>int available():返回当前输入流中可读的字节数

>void close():在操作完一个流后使用该方法关闭该流,释放流对象和与该流相关的系统资源;

2.FileOutputStream类

(1)功能:为一个磁盘文件创建一个输出流,通过FileInputStream流对象实现从文件写入连续字节数据。

(2)构造方法

>FileOutputStream(String name):创建一个磁盘文件输出流并将其连接到指定的文件;

>FileInputStream(File file):创建一个磁盘文件输出流并将其连接到File对象指定的文件;

举例:

//第一种构造方法

FileOutputStream stream=new FileOutputStream("hello.test");

//第二种构造方法(允许在把文件连接到输出流之前进一步操作文件属性)

File file=new File();

FileOutputStream stream = new FileOutputStream(file);

(3)常用方法

>void write(int b):将一个自留写到输出流;

>void write(byte[] b):将整个字节数组写到输出流中;

>void write(byte[] b,int off,int len):将字节数组b中的从off开始的len个字节写到输出流;

>void close():关闭输出流

提升:在创建一个FileOutputStream对象时,可以为其指定还不存在的文件名,但不能是存在的目录名或是已经被其他程序打开了的文件。FileOutputStream先创建输出对象,然后再准备输出。

三、PipedInputStream与PipedOutputStream

一个PipedInputStream对象必须和一个PipedOutputStream对象进行连接而产生一个通信管道,其中PipedOutputStream用于向管道写入数据,PipedInputStream用于从管道中读取PipedOutputStream写入的数据。在流中引入管道的概念,主要用来完成线程之间的通信,即一个线程的PipedInputStream对象能够从另外一个线程的PipedOutputStream对象中读取数据。

1.PipedInputStream类

(1)功能:PipedInputStream用于从管道中读取PipedOutputStream写入的数据;

(2)构造方法

>PipedInputStream() :创建一个管道的输入流对象,但没有与PipedOutputStream进行连接;

>PipedInputStream(int pipeSize):创建一个管道的输入流对象,并指定管道大小作为管道缓存;

>PipedInputStream(PipedOutputStream src) : 创建一个管道的输入流对象,但与指定的PipedOutputStream进行连接;

(3)常用方法

>int available() :判定管道输入流还有多有可读字节数据;

>void close() :关闭管道输入流并释放系统资源

>void connect(PipedOutputStream src) :将该输入流与指定的PipedOutputStream进行连接

>int read() :从该管道输入流读取下一个字节数据;

>int read(byte[] b, int off, int len):将管道输入流中的数据读入到字节数组b中从脚标为off开始的len个数组元素中;

>protected void receive(int b):接收一个字节数据

2.PipedOutputStream类

(1)功能:PipedOutputStream用于向管道写入数据;

(2)构造方法

>PipedOutputStream():创建一个管道输出流,但没有与 PipedInputStream进行连接;

>PipedOutputStream(PipedInputStream snk) :创建一个管道输出流,并与指定的PipedInputStream连接;

(3)常用方法

>void close() :关闭管道输出流并释放相应的系统资源;

>void connect(PipedInputStream snk) :将该管道输出流与指定的PipedInputStream相连接;

>void flush() :彻底写入数据到输出流

>void write(byte[] b, int off, int len) :将指定字节数据的off位置开始的len个字节数据写入到管道输出流中

>void write(int b) :写入一个指定的字节数据到管道输出流

提升:使用管道流类,可以实现各个程序模块之间的松耦通信,我们可以灵活地将多个这样的模块的输出流和输入流相连接,以拼装成满足各种应用的程序,而不用对模块内部进行修改。

3.源码实战

实现:多线程实现管道间通信。

(1)Sender.java

功能:实现一个Thread子类,重写run方法用于向管道输出流写入字节数据

<span style="font-size:18px;">package PipedStream;
import java.io.IOException;
import java.io.PipedOutputStream;
/*PipedOutputStream输入流
 * 功能:向管道输出流写入字节数据*/
class Sender extends Thread{
 private PipedOutputStream out = new PipedOutputStream();	//声明一个PipedOutputStream实例
 //成员方法:返回一个PipedOutputStream对象
 public PipedOutputStream getOutputStream()
 {
  return out;
 }
 //成员方法
 public void run() {
  String s=new String("Hello,receiver.How are you!");
  try
  {
   out.write(s.getBytes());
   out.close();
  }
  catch (IOException e)
  {
   e.printStackTrace();
  }
 }
}</span>

(2)Receiver.java

功能:实现一个Thread子类,重写run方法用于从管道输入流读取字节数据到字节数组中

<span style="font-size:18px;">package PipedStream;
import java.io.IOException;
import java.io.PipedInputStream;
/* PipedInputStream输出流
 * 功能:向管道读取字节数据到字节数组中*/
class Receiver extends Thread{
 //成员变量
 PipedInputStream in = new PipedInputStream();
 //成员方法:返回一个PipedInputStream对象
 public PipedInputStream getInputStream()
 {
  return in;
 }
 //成员方法
 public void run() {
  String s=null;
  byte[] buf = new byte[1024];
  try {
   int len = in.read(buf);	//从管道读取buf.length个字节到byte字节数组中并返回实际读到字节数量
   s=new String(buf,0,len);//将字节数组中的数据转换为字符串
   System.out.println("The following message comes from sender:\n"+s);
  } catch (IOException e) {
   e.printStackTrace();
  }
 }

}</span>

(3)PipedStreamTest.java

功能:创建两个线程并使用PipedOutputStream和PipedInputStream类创建一个管道。

<span style="font-size:18px;">package PipedStream;
import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
public class PipedStreamTest {
 public static void main(String[] args)
 {

  try {
    Thread t1=new Sender();	 //实例化一个线程1
    Thread t2 = new Receiver();	 //实例化一个线程2
    PipedOutputStream out = ((Sender) t1). getOutputStream();
    PipedInputStream in = ((Receiver) t2).getInputStream();
    out.connect(in);	 //连接管道
    t1.start();
    t2.start();
  } catch (IOException e) {
   e.printStackTrace();
  }

 }
}</span>

效果演示:

四、ByteArrayInputStream与ByteArrayOutputStream

一般来说,要在程序分配一个存储数据的内存块,通常都用定义一个字节数组实现。在Java中,提供了ByteArrayInputStream与ByteArrayOutputStream两个流类来实现类似内容虚拟文件的功能。ByteArrayInputStream与ByteArrayOutputStream流作用在于用IO流的方式来完成数组内容的读写,我们以保存数据的数组作为数据源来创建一个ByteArrayInputStream流对象,以数组作为缓冲区来创建一个ByteArrayOutputStream对象,最后从ByteArrayOutputStream对象中的数据存储到数组中。

注:这里使用流而非数组实现内存块,相比数组,我们可以借助IO流的各种方法来操作数组中的内容。

1.ByteArrayInputStream

(1)功能:输入流的一种实现。

(2)构造方法:

>ByteArrayInputStream(byte[] buf):以byte字节数组作为数据源创建一个输入流对象;

>ByteArrayInputStream(byte[] buf,int offset,int
length):使用数组buf中的从offset开始的length个元素作为数据源创建一个输入流对象;

(3)常用方法

>abstract int read():返回下一个输入字节的整型表示,如果返回-1表示遇到流的末尾;

>int read(byte[] b):从输入流读取b.length个字节存放到字节数组b中并返回实际读入的字节数;

>int read(byte[] b,int off,int len):从输入流的数据读入到字节数组b中从脚标为off开始的len个数组元素中;

>int skip(long n):跳过输入流上的n个字节并返回实际跳过的字节数;

>int available():返回当前输入流中可读的字节数

>void mark(int readlimit):在输入流的当前位置处放上一个标识,允许最多再读取readlimit个字节

>void reset():把输入指针返回到以前所做的标识处;

>void close():在操作完一个流后使用该方法关闭该流,释放流对象和与该流相关的系统资源;

2.ByteArrayOutputStream 
    

(1)功能:输出流的一种实现。

(2)构造方法

>ByteArrayOutputStream():创建一个32字节的缓冲区;

>ByteArrayOutputStream(int size):根据参数指定的大小创建缓冲区,缓冲区的大小在数据过多时能够自动增长;

(3)常用方法

>void write(int b):将一个字节数据写到输出流;

>void write(byte[] b,int off,int len):将字节数组b中的从off开始的len个字节写到输出流;

>void close():关闭输出流

>int size():返回buffer缓冲区当前大小;

>byte[] toByteArray():将数据转换为一个字节数组数据并返回该字节数组

>String toString():将缓冲区中的字节数组内容转换成为一个字符串

注:内存虚拟(映像)文件就是把一块内存虚拟成一个硬盘上的文件,原来该写到硬盘文件上的内容会被写到这个内存中,原来该从一个硬盘文件上读取内容改为直接从内存中读取。如果程序在运行过程中要产生一些临时文件,就可以用虚拟文件的方式实现,我们无需访问硬盘,而是直接访问内存,因此能够提供应用程序的效率。

3.源码实战

实现:模拟内存块功能-将小写字母作为输入流的数据源,然后,将该输入流中的所有英文字母变成对应的大写字母写入到输出流中,最后存储到字节数组中并输出。

ByteArraytest.java

<span style="font-size:18px;">import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
public class ByteArraytest {
 public static void main(String[] args)
 {
  String tmp = "abcdefghijklmnopqrstuvwxyt";
  byte[] src=tmp.getBytes();	 //src为转换前的数据块
  ByteArrayInputStream input = new ByteArrayInputStream(src);	 //实例化一个ByteArrayInputStream输入流并以字节数据src为数据源
  ByteArrayOutputStream out = new ByteArrayOutputStream();	 //实例化一个ByteArrayOutputStream输出流
  //调用转换字母功能函数转换字母
  new ByteArraytest().transForm(input, out);
  byte[] result = out.toByteArray();	 //resulte为转换后的内存块,用于存储输出流中的数据
  System.out.println(new String(result));//将字节数组中的字节数据转换为字符串数据打印

 }
 //转换成员方法
 public void transForm(ByteArrayInputStream input,ByteArrayOutputStream out)
 {
  int c;
  while((c=input.read())!=-1)	//循环从输入流读取一个字节,读到末尾返回-1
  {
   int b=(int)Character.toUpperCase((char)c);	 //字节-》字符-》字节(一个字符占1个字节)
   out.write(b);	//将转换后的一个字节数据写入到输出流中
  }
 }
}</span>

源码分析:

(1)byte[] src=tmp.getBytes()语句中的getBytes()方法作用将tmp对象的字符串内容转换成一系列字节数据并存储到新建的byte字节数组中;

(2)为了实现大小写字母的转换,我们通过使用输入流的read()方法(返回读到的字节数据)依次从输入流中读取一个字节,并将字节强制类型转换为字符,然后调用Character.toUpperCase(char)方法实现将小写字母转换为大写的功能,最后再将转换好的大写字母依次强制类型转换为Int(字节)类型写入到ByteArrayOutputStream输出流中。

效果演示:

字****符****流

五、Reader与Writer

Java中的字符为双字节的Unicode编码,而InputStream与OutputStream是用来处理字节的,在处理字符文本时不太方便,需要编写额外的程序代码。为此,Java为字符文本的输入输出专门提供了一套单独的类,即Reader与Writer抽象类。Reader、Writer下面也有许多子类,对具体IO设备进行字符输入输出,如FileReader就是用来读取文件流中的字符。

1.Reader类

(1)功能:用于从输入流读取字符数据,需要注意的是其所有的子类必须实现read(char[], int, int)和close()方法

(2)构造方法

>protected Reader()

>protected Reader(Object lock)

(3)常用方法

与InputStream方法相似

>int read(char[] cbuf):从输入流读取cbuf.length()个字符存储到char字符数组中;

>int read():从输入流读取一个字符并返回读取字符的个数

>boolean ready():判定输入流是否准备能读

2.Writer类

(1)功能:用于写入字符数据到输出流,需要注意的是其所有的子类必须实现write(char[],
int, int)、flush()和close()方法;

(2)构造方法

>protected Writer()

>protected Writer(Object lock)

(3)常用方法

Writer append(char c) :将指定的一个字符追加写入到输出流中;

abstract void close() :调用flush彻底写入数据,然后关闭流

abstract void flush() :彻底写入数据;

void write(char[] cbuf) :将字符数组中的数据写入到输出流;

void write(int c) :将一个字符写入到输出流;

void write(String str) :将字符串数据写入到输出流;

六、FileReader与FileWriter

1.FileReader类

(1)功能:用来读取文件流中的字符

(2)构造方法

>FileReader(File file) :构造一个FileReader对象,并指定要读取的File包含的文件资源

>FileReader(String fileName) :构造一个FileReader对象,并指定路径文件资源

(3)常用方法

与FileInputStream相似,详情:http://docs.oracle.com/javase/8/docs/api/index.html

2.FileWriter类

(1)功能:用来写字符数据到文件输出流

(2)构造方法

>FileWriter(File file)

>FileWriter(String fileName)

(3)常用方法

与FileOutputStream相似,详情:http://docs.oracle.com/javase/8/docs/api/index.html

3.源码实战

实现:使用FileWriter打开(创建)一个文件并向其写入字符串数据;使用FileReader读取该文件的字符串数据到指定的数组或字符串中。

FileChar.java

<span style="font-size:18px;">import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class FileChar {
 public static void main(String[] args )
 {
  /*输出流:写入字符串数据到文件file*/
  File file=new File("helloWorld.txt");	 //实例化一个File对象,该对象可以操作文件的属性
  try
  {
   FileWriter out = new FileWriter(file);	 //实例化一个字符输出流
   out.write("HelloWorld,I am Lihua!--2015.02.06");	//向输出流写入字符串数据
   out.close();
  }
  catch (IOException e)
  {
   e.printStackTrace();
  }
  /*输入流:读取文件file中的字符串数据*/
  try
  {
   FileReader in = new FileReader(file);
  //	char[] buf = new char[1024];        //第一种读取文件方法
  //	int len=in.read(buf);	 //从文件读取buf.length()个字符数据到字符数组buf
  //System.out.println(new String(buf,0,len));
   int c;                                                //第二中读取文件方法
   String str = "";
  while((c=in.read())!=-1)
    {
     str=str+(char)c;
    }
  System.out.println(str);
   }
  catch (IOException e) {
    e.printStackTrace();
   }

  }
}</span>

效果演示:

七、字节流类与字符流类对比图

1.IO输入流对应图

2.IO输入流对应图

时间: 2024-10-13 01:04:55

Java笔记二十一.深入解析I/O编程之节点流的相关文章

Java笔记二十.深入解析I/O编程之文件访问类

深入解析I/O编程之文件访问类 转载请表明出处:http://blog.csdn.net/u012637501(嵌入式_小J的天空) 开始学习字节流类之前,我们来先看下与文件有关的类-File类.File类是IO包中唯一代表磁盘文件本身的对象,File类定义了一些与平台无关的方法来操作文件,通过调用File类提供的各种方法,我们能够创建.删除文件.重命名文件以及判断文件的读写权限及其是否存在,设置和查询文件的最近修改时间等.在Java中,目录也被当作File使用,只是多了一些目录特有的功能---

Java笔记十九.深入解析I/O编程之流的层次结构

深入解析I/O编程之流的层次结构 转载请表明出处:http://blog.csdn.net/u012637501(嵌入式_小J的天空) 大多数应用程序都需要与外部设备进行数据交换,最常见的外部设备包含磁盘和网络.所谓I/O(Input/Output缩写),即指应用程序对这些设备的数据输入与输出.在程序中,键盘被当作输入文件.显示器被当作输出文件使用.Java语言定义了许多专门负责各种方式的输入输出,这些类都被方法Java.io包中. 一.理解流的概念 1. 数据流 数据流是一串连续不断的数据的集

《Programming in Lua 3》读书笔记(二十一)

日期:2014.8.1 PartⅣ The C API 25 An Overview of the C API Lua是一种嵌入式语言.这就意味着Lua不是单独存在的,而是可以通过一系列的标准库将lua的特性嵌入至其他应用模块中. Lua以Lua interpreter(lua的解释器?)来解决了其不是独立程序,我们直到现在却又能独立使用Lua的问题.这个解释器是一个小型的程序(不超过500行代码),使用lua的标准库来实现独立解释程序,这个程序将处理与用户的交互等操作交给lua的标准库,这些库

JAVA笔记二

JAVA笔记二 常量:表示不可改变的值 变量:将不确定的数据进行存储也就是需要在内存中开辟一个空间 如何开辟内存空间? 就是通过明确的的数据类型 变量名称 数据来完成 Integer.toBinaryString(number); JAVA提供转换二进制的方法; number表示转换成二进制的数字 两个变量不通过第三个变量进行两个值得交换 1.int a=3,b=8; a=a+b; a=a-b; b=a-b; 此方法有局限性比如说a+b的大于2^32-1就会出现错误 我们可以通过异或运算来进行交

马哥学习笔记二十一——LVS DR模型

kernel parameter: arp_ignore: 定义接收到ARP请求时的响应级别: 0:只要本地配置的有相应地址,就给予响应: 1:仅在请求的目标地址配置请求到达的接口上的时候,才给予响应: arp_announce:定义将自己地址向外通告时的通告级别: 0:将本地任何接口上的任何地址向外通告: 1:试图仅向目标网络通告与其网络匹配的地址: 2:仅向与本地接口上地址匹配的网络进行通告: curl命令选项: --cacert <file> CA证书 (SSL) --capath &l

菜鸟学Java(二十一)——如何更好的进行单元测试——JUnit

测试在软件声明周期中的重要性,不用我多说想必大家也都非常清楚.软件测试有很多分类,从测试的方法上可分为:黑盒测试.白盒测试.静态测试.动态测试等:从软件开发的过程分为:单元测试.集成测试.确认测试.验收.回归等. 在众多的分类中,与开发人员关系最紧密的莫过于单元测试了.像其他种类的测试基本上都是由专门的测试人员来完成,只有单元测试是完全由开发人员来完成的.那么今天我们就来说说什么是单元测试,为什么要进行单元测试,以及如更好的何进行单元测试. 什么是单元测试? 单元测试(unit testing)

java笔记--String类对象解析与运用

1.String中的equals和==的区别 String是对象而非基本数据类型,不能使用"=="来判断两个字符串是否相当, 判断两个字符串内容是否相同用equals(); 判断两个字符串内存地址是否相同用"==" 2.startsWith(String str): 判断字符串是否以str为前缀 3.endsWith(String str): 判断字符串是否以str为后缀 4.String 字符串的比较: 1).compareTo()和compareToIgnore

Java笔记二十二.深入解析I/O编程之包装类

深入解析I/O编程之包装类 转载请表明出处:http://blog.csdn.net/u012637501(嵌入式_小J的天空) 从上节学到的节点流类我们知道,如Java中提供的FileOutputStream和FileInputStream类实现往文件中写入字节或从文件中读取字节数据.在实际应用中,我们需要往文件中写入或读取各种类型的数据,一般的做法是先将其他类型的数据转换成字节数组后写入文件或是将从文件中读取到的字节数组转换成其他类型.然而,上述方法会给我们的程序增加了代码量和带来一些困难和

Java基础学习笔记二十一 多线程

多线程介绍 学习多线程之前,我们先要了解几个关于多线程有关的概念.进程:进程指正在运行的程序.确切的来说,当一个程序进入内存运行,即变成一个进程,进程是处于运行过程中的程序,并且具有一定独立功能. 线程:线程是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程.一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序. 简而言之:一个程序运行后至少有一个进程,一个进程中可以包含多个线程 什么是多线程呢?即就是一个程序中有多个线程在同时执行.通过下图来区别单线程程序与