- 学习和使用IO流的步骤:
- 第一步,首先,明确IO流中有两个主要的体系,即字节输入流(InputStream)、字节输出流(OutputStream)和字符输入流(Reader)、字节输出流(Writer)。其次,明确数据的来源和数据将要到达的目的地。
- 第二步,明确将要操作的数据是否是纯文本数据。如果数据源是纯文本数据选Reader;数据源不是纯文本数据选择InputStream。如果数据目的地是纯文本数据就选择Writer;如果不是则选择OutputStream。
- 第三步,明确具体的设备。即数据源是从哪个设备来的:是硬盘就加File;是键盘用System.in(是一个InputStream对象);是内存用数组;是网络用Socket流。同样目的是哪个设备:是硬盘就加File;是键盘用System.out(是一个OutoutStream对象);是内存用数组;是网络用Socket流。
- 第四步,明确是否还需要其他额外功能呢,例如①是否需要较高的效率,即是否需要使用缓冲区,是就加上Buffered;②是否需要转换,是,就使用转换流,InputStreamReader 和OutputStreamWriter。
- 通过上面的四步走基本就可以确定该用哪个类了。如果小伙们看了上面讲解,还是有点似懂非懂,那么,接下来大家可以看看下面的例子。把上面的四步走带入走一遍,应该就可以相当清楚了。例子如图所示。
2. 与流有关的类
- 流的概念:流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象。即数据在两设备间的传输称为流,流的本质是数据传输,根据数据传输特性将流抽象为各种类,方便更直观的进行数据操作。
2.1 与流有关的类、字节流与字符流的区别
(1)File类
- File类是IO包中唯一能代表磁盘文件的对象,File类定义了一些与平台无关的方法来操作文件,通过调用File类提供的各种方法,我们能够创建、删除文件,重命名文件,判断文件的读写权限是否存在,设置和查询文件的最近修改时间。
(2) RandomAccessFile类
(3) InputStream抽象类、OutputStream抽象类
- IO流主要用于硬板、内存、键盘等处理设备上得数据操作,根据处理数据的数据类型的不同可以分为:字节流(抽象基类为InPutStream和OutPutStream)和字符流(抽象基类为Reader和Writer)。
- 程序可以从连续读取字节的对象叫输入流,用InputStream类完成。程序能向其中连续写入字节的对象叫输出流,用OutputStream类完成。
(4) Reader抽象类、Writer抽象类
- 专门用来处理字符流的抽象类。
2.2 字节流与字符流的区别
- (1)字节流读取的时候,读到一个字节就返回一个字节; 字符流使用字节流读到一个或多个字节(中文对应的字节数是两个,在UTF-8码表中是3个字节)时;其本质是基于字节流读取时,去查了指定的码表。先去查指定的编码表,将查到的字符返回。
- (2)字节流可以处理所有类型数据,如:图片,MP3,AVI视频文件,而字符流只能处理字符数据。只要是处理纯文本数据,就要优先考虑使用字符流,除此之外都用字节流。
3. Java流的结构
4. 字节流
4.1 字节输入流InputStream
- InputStream 是所有的输入字节流的父类,它是一个抽象类。继承自InputStream的流都是用于向程序中输入数据,且数据的单位为字节(8bit)。
- ByteArrayInputStream、StringBufferInputStream、FileInputStream 是三种基本的介质流,它们分别从Byte 数组、StringBuffer、和本地文件中读取数据。
- ObjectInputStream和所有FilterInputStream 的子类都是处理流。
- PipedInputStream是从与其它线程共用的管道中读取数据。
- SequenceInputStream的功能:将两个或多个InputStream 对象转换成单一InputStream 。
- FilterInputStream抽象类的子类有四个:DataInputStream, PushbackInputStream, BufferedInputStream, LineNumberInputStream。
- InputStream的基本方法:
InputStream的基本方法:
(1)int read() throws IOException //从输入流中读取一个字节并以整数的形式返回(返回的是下一个输入字节的整数表示(0~255)),如果返回-1表示已到输入流的末尾。该方法抛出了IO异常。
(2)int read(byte[] b) throws IOException //读入b.length个字节放到b中并返回实际读入的字节数。
(3)void close() throws IOException //在操作完一个流后,要使用此方法将其关闭,系统就会释放与这个流相关的资源。
(4)... ...[还有很多方法,可参考API文档阅读]
- 例子:利用FileInputStream读出某一指定文件的内容,并打印输出。注意:打印输出的英文字符能够正常显示,但是中文字符被显示为问号,要解决这个问题就是后面会讲到的FileReader类。
import java.io.*;
public class TestFileInputStream
{
public static void main(String[] args)
{
FileInputStream fis = null;
long num = 0;
int b = 0;
try
{
fis = new FileInputStream("D:\\JavaProject\\demo13\\TestFileInputStream.java");
}
catch (FileNotFoundException e)
{
System.out.println("找不到指定文件");
System.exit(-1);
}
try
{
while((b=fis.read()) != -1)
{
System.out.print((char)b);
num++;
}
fis.close();
System.out.println();
System.out.println("共读取了 "+num+" 个字节");
}
catch (IOException e)
{
System.out.println("文件读取错误");
System.exit(-1);
}
}
}
4.2 字节输出流OutputStream
- OutputStream 是所有的输出字节流的父类,它是一个抽象类。
- ByteArrayOutputStream、FileOutputStream 是两种基本的介质流,它们分别向Byte 数组和本地文件中写入数据。
- PipedOutputStream 是向与其它线程共用的管道中写入数据。
- ObjectOutputStream 和所有FilterOutputStream 的子类都是处理流。
- 例子:把指定的某一文件进行复制到另一个位置的指定文件中去。分析:这时就首先需要FileInputStream类来读出该文件的内容,然后通过FileOutputStream类向文件写入。
import java.io.*;
public class TestFileOutputStream
{
public static void main(String[] args)
{
int b = 0;
FileInputStream in = null;
FileOutputStream out = null;
try
{
in = new FileInputStream("D:\\JavaProject\\demo13\\TestFileOutputStream.java");
out = new FileOutputStream("D:\\JavaProject\\demo13\\CopyTestFileOutputStream.java");
while((b=in.read()) != -1)
{
out.write(b);
}
in.close();
out.close();
}
catch (FileNotFoundException e)
{
System.out.println("找不到指定文件");
System.exit(-1);
}
catch (IOException e)
{
System.out.println("文件复制错误");
System.exit(-1);
}
System.out.println("文件已成功复制");
}
}
- OutputStream的基本方法:
4.3 字节输出流与输入流对应的关系
5. 字符流
5.1 Reader
- Reader 是所有的输入字符流的父类,它是一个抽象类。
- CharReader、StringReader 是两种基本的介质流,它们分别将Char 数组、String中读取数据。PipedReader 是从与其它线程共用的管道中读取数据。
- BufferedReader 很明显就是一个装饰器,它和其子类负责装饰其它Reader 对象。
- FilterReader 是所有自定义具体装饰流的父类,其子类PushbackReader 对Reader 对象进行装饰,会增加一个行号。
- InputStreamReader 是一个连接字节流和字符流的桥梁,它将字节流转变为字符流。FileReader 可以说是一个达到此功能、常用的工具类,在其源代码中明显使用了将FileInputStream 转变为Reader 的方法。我们可以从这个类中得到一定的技巧。Reader 中各个类的用途和使用方法基本和InputStream 中的类使用一致。后面会有Reader 与InputStream 的对应关系。
- 基本方法:
- 示例:
import java.io.*;
public class TestFileReader
{
public static void main(String[] args)
{
FileReader fis = null;
long num = 0;
int b = 0;
try
{
fis = new FileReader("D:\\JavaProject\\demo13\\TestFileInputStream.java");
}
catch (IOException e)
{
System.out.println("找不到指定文件");
System.exit(-1);
}
try
{
while((b=fis.read()) != -1)
{
System.out.print((char)b);
num++;
}
fis.close();
System.out.println();
System.out.println("共读取了 "+num+" 个字节");
}
catch (IOException e)
{
System.out.println("文件读取错误");
System.exit(-1);
}
}
}
5.2 Writer
- Writer 是所有的输出字符流的父类,它是一个抽象类。
- CharArrayWriter、StringWriter 是两种基本的介质流,它们分别向Char 数组、String 中写入数据。PipedWriter 是向与其它线程共用的管道中写入数据,
- BufferedWriter 是一个装饰器为Writer 提供缓冲功能。
- PrintWriter 和PrintStream 极其类似,功能和使用也非常相似。
- OutputStreamWriter 是OutputStream 到Writer 转换的桥梁,它的子类FileWriter 其实就是一个实现此功能的具体类(具体可以研究一SourceCode)。功能和使用和OutputStream 极其类似,后面会有它们的对应图。
- 基本方法:
- 示例:
import java.io.*;
public class TestFileWriter
{
public static void main(String[] args)
{
FileWriter fw = null;
try
{
fw = new FileWriter("D:\\JavaProject\\demo13\\WriteFilewRiter.java");
//如果文件不存在,会自动的创建一个。
for(int c=0;c<5000;c++)
{
fw.write(c);
}
fw.close();
//fw.flush();
/*如果把fw.close();语句注释掉以后编译运行,在WriterFilewRiter.java文件没有
写入的字符串;而将fw.close();语句替换成fw.flush();会发现WriterFilewRiter.java文件
写入了字符串,这就是缓冲区的原因。
*/
}
catch (IOException e)
{
e.printStackTrace();
System.out.println("文件写入错误");
System.exit(-1);
}
System.out.println("文件写入成功!");
}
}
5.3 字符输出流与输入流对应的关系