而在JAVA中,对数据的操作是通过流的方式来实现的,
在JAVA中按类型分,可以分为字节流和字符流,
按流向分,可以分为输入流和输出流.
其中最根本的就是字节流,因为计算机中的数据就是以字节为单位来保存的,
其中的字符流就是先把字节通过编码表转换成字符之后,方便我们理解和在内存中操作的,其实操作还根本还是byte(字节),或者更根本的0和1
因为数据的量大和繁杂,因此用一个形象的状态来描述,"流",就好像水流一样,从一个地方到另一个地方,
程序需要数据的时候要使用输入流读取数据,就好像从硬盘把数据流出到内存一样,而当程序需要将一些数据保存起来的时候,
就要使用输出流完成,就好像把内存中的数据倒到硬盘保存一样。但实际上流中保存的实际上全都是字节数据,或者叫二进制的0,1。
只要明确数据的基本单位是字节,是一个0-255之间的整数,其他的事情就好理解了,因为其他的东西都是以这个byte为核心而增强或转换出来的东西,
java中用来操作流的类基本都在包Java.io,其中有四个最重要的基类,
InputStream,OutputStream,
Reader,Writer
其中是字节流有两个InputStream,OutputStream,
是字符流的有两个Reader,Writer
其中是输入流的是InputStream,Reader
是输出流的是OutputStream,Writer
流的操作流程
在Java中IO操作也是有相应步骤的,主要的操作流程如下:
1 使用流对象关联一个源,或者关联一个目的地
2 通过字节流或字符流的子类,读取数据到内存中,或者在内存中准备好数据
3 进行读/写操作
4 关闭输入/输出
IO操作属于资源操作,一定要记得关闭,并且由于流数据是在不同的设备之后转移,会在转移过程中发生各种异常,所以一般都要做好异常处理
流的异常处理
主要步骤如下:
1 在try代码块外面定义流对象引用,
2 在try代码块中实现对流对象的初始化,数据的读写,数据的刷新
3 在catch代码块中实现对异常的捕捉
4 在finally代码块中实现对流对象的最终关闭,由于的关闭的过程中也会出现异常,所以在finally中也要进行流对象关闭的异常处理
下面对常用的几个流对象进行介绍,主要是介绍四个基类中的方法,
OutputStream
此类是一个抽象类,是输出字节流的所有类的超类。输出流接受输出字节并将这些字节发送到某个地方,如硬盘,控制台,打印机等。
由于此类是一个抽象类,如果想要使用此类的话,则首先必须通过子类实例化对象,那么如果现在要操作的是一个文件,则可以使用:FileOutputStream类。
其中的继承层次如下:
java.lang.Object
|---java.io.OutputStream
|---java.io.FileOutputStream
FileOutputStream类的常用构造方法如下:
FileOutputStream(String name)
创建一个向具有指定名称的文件中写入数据的输出文件流。
FileOutputStream(String name, boolean append)
创建一个向具有指定 name 的文件中写入数据的输出文件流。
输出流主要是用来保存数据,写数据的:
import java.io.*;//流对象所在的包
public class OutputStreamDemo
{
public static void main(String[] args)
{
//定义引用
OutputStream os = null;
try
{
//用子类FileOutputStream来初始化OutputStream对象
os = new FileOutputStream("OutPutStream.txt");
//准备字节数据
byte[] data = "Hello OutPutStream".getBytes();
//OutputStream常用的方法之一,写入单个字节,字节的范围是0-255,
os.write(65);
//OutputStream常用的方法之二,写入批量的字节数据
os.write(data);
//刷新流对象方法
os.flush();
}
//在catch对异常进行捕捉
catch (IOException ex)
{
ex.printStackTrace();
}
//在finally中关闭流对象
finally
{
if (os != null)
{
try
{
//流的关闭方法
os.close();
} catch (IOException e)
{
e.printStackTrace();
}
}
}
}
}
//
InputStream
此抽象类是表示字节输入流的所有类的超类。需要定义 InputStream 子类的应用程序必须总是提供返回下一个输入字节的方法。
可以从键盘,硬盘,网络等取得数据,然后在内存中对数据进行处理,
因为InputStream也是抽象类,所以用它的子类来演示其功能,主要功能就是读取数据
其中的主要方法就是read()
abstract int read()
从输入流中读取数据的下一个字节。
int read(byte[] b)
从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中。
int read(byte[] b, int off, int len)
将输入流中最多 len 个数据字节读入 byte 数组。
如果因为已经到达文件末尾而没有更多的数据,则返回 -1。即如果read方法如果返回-1,说明已经读到了数据的末尾了.
类 FileInputStream,是InputStream类的一个子类
FileInputStream 从文件系统中的某个文件中获得输入字节。哪些文件可用取决于主机环境。
FileInputStream 用于读取诸如图像数据之类的原始字节流。
其继承层次如下:
java.lang.Object
|---java.io.InputStream
|---java.io.FileInputStream
其构造函数
FileInputStream(String name)
通过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的路径名 name 指定。
字节输出流主要是来用读取字节数据的,大体用法和主要API如代码所示:
//
import java.io.*;
import java.util.Arrays;
public class InputStreamDemo
{
//为了代码美观,不做异常处理,直接抛出
public static void main(String[] args) throws Exception
{
//通过子类实例化对象
InputStream is = new FileInputStream("test.txt");
//读取单个字节
byte data1 = (byte) is.read();
//读取指字节到数组中
byte[] data2 = new byte[1024];
is.read(data2);
//通过循环来读取数据到数组中
byte[] data3 = new byte[1024];
for (int length = 0; (length = is.read(data3)) != -1;)
{
System.out.println(Arrays.toString(data3));
}
//关闭流对象
is.close();
}
}
//
字符流
在计算机中一个字符是由字节来代替的,有可能是一个,也有可能是一个以上,java提供了Reader、Writer两个专门操作字符流的类。
字符流的具体用法和字节流差不多,但是操作的单位不再是以字节为单位的数据,而是以字符为单位的数据,如写入的是字符,读取数据时也是字符
由于字符是由字节代替的,所以其中就要用于转化的功能了,
主要用于转换字符和字节的有两个类
OutputStreamWriter
OutputStreamWriter是字符流通向字节流的桥梁:可使用指定的charset将要写入流中的字符编码成字节。它使用的字符集可以由名称指定或显式给定,否则将接受平台默认的字符集。
每次调用 write() 方法都会导致在给定字符(或字符集)上调用编码转换器。在写入底层输出流之前,得到的这些字节将在缓冲区中累积。可以指定此缓冲区的大小,不过,默认的缓冲区对多数用途来说已足够大。注意,传递给 write() 方法的字符没有缓冲。
InputStreamReader
InputStreamReader 是字节流通向字符流的桥梁:它使用指定的 charset 读取字节并将其解码为字符。它使用的字符集可以由名称指定或显式给定,或者可以接受平台默认的字符集。
每次调用 InputStreamReader 中的一个 read() 方法都会导致从底层输入流读取一个或多个字节。要启用从字节到字符的有效转换,可以提前从底层流读取更多的字节,使其超过满足当前读取操作所需的字节。
Writer
写入字符流的抽象类。子类必须实现的方法仅有 write(char[], int, int)、flush() 和 close()。但是,多数子类将重写此处定义的一些方法,以提供更高的效率和/或其他功能。
此类本身也是一个抽象类,如果要使用此类,则肯定要使用其子类,此时如果是向文件中写入内容,所以应该使用FileWriter的子类。
继承层次关联:
java.lang.Object
|---- java.io.Writer
|---- java.io.OutputStreamWriter
|---- java.io.FileWriter
FileWriter类的构造方法定义如下:
FileWriter(String fileName)
根据给定的文件名构造一个 FileWriter 对象。
FileWriter(String fileName, boolean append)
根据给定的文件名以及指示是否附加写入数据的 boolean 值来构造 FileWriter 对象。
字符流的操作比字节流操作好在一点,就是可以直接输出字符串了,不用再像之前那样进行转换操作了。但是其实功能是差不多的,
Reader
用于读取字符流的抽象类。子类必须实现的方法只有 read(char[], int, int) 和 close()。但是,多数子类将重写此处定义的一些方法,以提供更高的效率和/或其他功能。
是使用字符的方式从文件中取出数据,读取的方法和InputStream差不多一样,只是变成的读取字符的形式了
Reader本身也是抽象类,如果现在要从文件中读取内容,则可以直接使用FileReader子类。
继承层次关联:
java.lang.Object
|---- java.io.Reader
|---- java.io.InputStreamReader
|---- java.io.FileReader
FileReader的构造方法定义如下:
FileReader(String fileName)
在给定从中读取数据的文件名的情况下创建一个新 FileReader。以字符数组的形式读取出数据:
Writer和Reader的使用方法其实和OutputStream与InputStream的方法差不多一样,这里把它们的功能合并在一起演示了
演示代码如下:
import java.io.*;
public class WriterDemo
{
public static void main(String[] args) throws Exception
{
//初始化流对象
Writer writer = new FileWriter("writer.txt");
Reader reader = new FileReader("reader.txt");
//写入单个字符和写入批量字符
writer.write(500);
writer.write("Hello world".toCharArray());
//读取单个字符和读取批量字符
char data1 = (char) reader.read();
char[] data2 = new char[1024];
reader.read(data2);
//循环读取字符和写入字符数据
char[] data3 = new char[1024];
for (int length = 0; (reader.read(data3)) != -1;)
{
writer.write(data3, 0, length);
}
//关闭流对象
writer.close();
reader.close();
}
}
//
字节流与字符流的区别
字节流和字符流使用是非常相似的,那么除了操作代码的不同之外,还有哪些不同呢?
字节流在操作的时候本身是不会用到缓冲区(内存)的,是与文件本身直接操作的,但是是可以缓冲到内存的,如使用数组,而字符流在操作的时候是使用到缓冲区的
字节流在操作文件时,即使不关闭资源(close方法),文件也能输出,但是如果字符流不使用close方法的话,则不会输出任何内容,说明字符流用的是缓冲区,并且可以使用flush方法强制进行刷新缓冲区,这时才能在不close的情况下输出内容
那开发中究竟用字节流好还是用字符流好呢?
在所有的硬盘上保存文件或进行传输的时候都是以字节的方法进行的,包括图片也是按字节完成,而字符是只有在内存中才会形成的,所以使用字节的操作是最多的。
如果要java程序实现一个拷贝功能,应该选用字节流进行操作,因为无论是字符流还是字节流,最后保存的数据都是字节