黑马程序员-- 十、IO
来源:互联网 发布:西工大编程 编辑:程序博客网 时间:2024/05/21 08:49
一、 概述
Java对数据的操作是通过流的方式,IO流用来处理设备之间的数据传输。
IO流按操作数据类型可分为:字节流和字符流。
按流向可分为:输入流和输出流。
按流的角色分为:节点流和处理流。可以从一个特定的IO设备读写的数据流,称为节点流,节点流也被称为低级流;处理流则用于对一个已经存在的流进行封装或链接,通过封装后的流来实现数据读写功能。处理流也被称为高级流。
字节流的抽象基类有:InputStream、OutputStream。
字符流的抽象基类有:Reader、Writer
Tips:
Java使用处理流来包装节点流是一种典型的装饰者设计模式,通过使用处理流来包装不同的节点流,可以消除不同节点流的实现差异,也可以提高更方便的方法来完成输入输出功能。因此处理流也被称为包装流。
处理流的功能主要体现在以下两个方面:
——性能的提高:主要以增加缓冲的方式来提高输入输出效率。
——操作的便捷:处理流可能提供了一系列便捷的方法来一次输入\输出大批量的内容,而不是每次只操作一点点内容。
二、字节流和字符流
字节流和字符流的操作方式几乎完全一样,区别只是操作的数据单元不同而已——字节流操作的数据单元是字节,字符流操作的是字符。
1、InputStream和Reader
在InputStream里有如下三个方法:
——int read():从输入流中读取单个字节,返回读取的字节数据(字节数据可直接转换为int类型)。
——int read(byte[] b):从输入流中最多读取b.length个字节数据,并将其存储在字节数组b中,返回实际读取的字节数。
——int read(byte[] b, int off,int len):从输入流中读取最多len个字节数,并将其存储在数组b中,放入数组b时,从off位置开始放,返回实际读取的字节数。
与InputStream对应,Reader里包含如下三个方法:
int read()、int read(char[] cbuf)、int read(char[] cbuf, int off, int len),它们的使用方式与InputStream一样,只是操作的数据是以字符为单位。
InputStream和Reader都是抽象类,它们各有一个用于读取文件的实现类:FileInputStream和FileReader,他们都是节点流,会直接和指定文件关联。
下面的程序示范了如何使用FileInputStream来读取自身的效果:
import java.io.*;
public class FileInputStreamTest {
public static void main(String[] args) throws IOException
{
//第一步:关联源文件
FileInputStream fis = new FileInputStream
("H:\\WorkSpace\\StudyNote\\src\\io\\FileInputStreamTest.java");
//第二步:创建一个字节数组,用来读取数据
byte[] bbuf = new byte[1024];
//定义一个int型变量,用来存储实际读取到的字节
int hashread;
//第三步:读取文件数据
while ((hashread = fis.read(bbuf)) > 0)
{
System.out.println(new String(bbuf, 0, hashread));
}
//第四步:关闭流对象,放在finally块中更安全
fis.close();
}
}
Tips:
1、 上面创建了一个长度为1024的字节数组来读取该文件,实际Java源文件的长度不足1024字节,也就是说,程序只需执行一次read方法即可读取全部内容。但如果创建了较小的字节数组,程序在输出中文时就可能显示乱码——这是因为本文件保存时采用的是GBK编码方式,每个字符占两个字节,如果read()方法读取时只读取到了半个字节,将导致乱码。
2、 Java7以前,程序需手动关闭流对象,释放系统资源,Java7改写了所有IO资源类,它们都实现了AutoCloseable接口,因此都可通过自动关闭资源的try语句来关闭这些IO流。
3、 java7实现的AutoCloseable接口关闭流对象,好像不会在关闭之前刷新流。——待验证
下面程序使用FileReader来读取文件本身:
import java.io.*;
public class FileReaderTest {
public static void main(String[] args) throws IOException
{
try{
//第一步:关联源文件
FileReader fr = new FileReader
("H:\\WorkSpace\\StudyNote\\src\\io\\FileInputStreamTest.java");
//第二步:创建一个字符数组,用来读取数据
char[] cbuf = new char[32];
//定义一个int型变量,用来存储实际读取到的字符
int hashread;
//第三步:读取文件数据
while ((hashread = fr.read(cbuf)) > 0)
{
System.out.println(new String(cbuf, 0, hashread));
}
}
catch(IOException e)
{
e.printStackTrace();
}
}
}
除此之外,InputStream和Reader还支持如下几个方法来移动记录指针:
——void mark(int readAheadLimit):在记录指针当前位置记录一个标记(mark)
——boolean markSupported():判断此输入流是否支持mark操作。
——void reset():将此流的记录指针位置重新定位到上一次记录标记(mark)位置。
——long skip(long n):记录指针向前移动n个字节/字符。
2、OutputStream和Writer
这两个流都提供了如下三种方法:
——void write(int c):将指定的字节/字符输出到输出流中,其中c既可以为字节又可以为字符。
——void write(byte[]/char[] buf):将指定字节数组/字符数组中的数据输出到指定输出流中。
——void write(byte[]/char[] buf, int off, int len):将字节数组/字符数组中从off位置开始,长度为len的字节/字符输出到输出流中。
此外,字符流直接以字符作为操作单位,所以Writer可以用字符串来代替字符数组,即以String对象作为参数。Writer里还包含了如下两个方法:
——void write(String str):将str字符串里包含的字符串输出到输出流中。
——void write(String str, int off, int len):将str字符串里从off位置开始,长度为len的字符串输出到输出流中。
下面程序使用FileInputStream来执行输入,使用FileOutputStream来执行输出,用以实现文件复制功能:
import java.io.*;
public class FileOutputStreamTest {
public static void main(String[] args)
{
try{
//创建字节输入流
FileInputStream fis = new FileInputStream("H:\\WorkSpace\\StudyNote\\src\\io\\FileOutputStreamTest.java");
//创建字节输出流
FileOutputStream fos = new FileOutputStream("C:\\Users\\MyPC\\Desktop\\FileOutputStreamTest.java");
byte[] bbuf = new byte[32];
int hasread = 0;
//从输入流中读取数据
while((hasread = fis.read(bbuf)) > 0)
{
//每读取一次,即写入文件输出流
fos.write(bbuf, 0, hasread);
}
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
Tips:
关闭输出流有两个作用:
1、 释放物理资源
2、 将输出流缓冲区的数据flush到物理节点里。
如果希望直接输出字符串内容,则使用Writer会有更好的效果:
import java.io.*;
public class FileWriterTest {
public static void main(String[] args)
{
try {
FileWriter fw = new FileWriter("C:\\Users\\MyPC\\Desktop\\锦瑟.txt");
fw.write("锦瑟 - 李商隐\r\n");
fw.write("锦瑟无端五十弦,一弦一柱思华年。\r\n");
fw.write("庄生晓梦迷蝴蝶,望帝春心托杜鹃。\r\n");
fw.write("沧海月明珠有泪,蓝田日暖玉生烟。\r\n");
fw.write("此情可待成追忆,只是当时已惘然。\r\n");
fw.flush();
} catch (IOException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
}
Tips:
Java7添加AutoCloseable接口,并让所有IO类实现此接口,使用try语句会自动关闭流对象,无需手动调用close方法,但自动关闭流之前不会调用flush方法,需手动调用。
三、IO流体系
1、处理流的用法
Tips:
识别处理流十分简单,只要流的构造函数参数不是一个物理节点,而是已经存在的流,那么这种流就一定是处理流;所有节点流都是以物理IO节点作为构造函数参数的。
下面程序使用PrintStream处理流来包装OutputStream,使用处理流后的输出流在输出是将更加方便:
import java.io.*;
public class PrintStreamTest {
public static void main (String[] args)
{
try {
FileOutputStream fos = new FileOutputStream("test.txt");
PrintStream ps = new PrintStream(fos);
//使用PrintStream执行输出
ps.println("测试字符串");
//使用PrintStream打印对象
ps.println(new PrintStreamTest());
} catch (FileNotFoundException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
}
2、输入输出流体系
Java的输入/输出流体系提供了近40个类,下表显示了Java输入输出流体系中常见的流分类:
分类
字节输入流
字节输出流
字符输入流
字符输出流
抽象基类
InputStream
OutputStream
Reader
Writer
访问文件
FileInputStream
FileOutputStream
FileReader
FileWriter
访问数组
ByteArrayInputStream
ByteArrayOutputStream
CharArrayReader
CharArrayWriter
访问管道
PipedInputStream
PipedOutputStream
PipedReader
PipedWriter
访问字符串
StringReader
StringWriter
缓冲流
BufferedInputStream
BufferedOutputStream
BufferedReader
BufferWriter
转换流
InputStreamReader
OutputStreamWriter
对象流
ObjectInputStream
ObjectOutputStream
抽象基类
FilterInputStream
FilterOutputStream
FilterReader
FilterWriter
打印流
PrintInputStream
PrintOutoutStream
PrintReader
PrintWriter
推回输入流
PushbackInputStream
PushbackReader
特殊流
DataInputStream
DataOutputStream
Tips:粗体标出的类代表节点流,八个抽象基类无法创建对象
3、转换流
转换流:InputStreamReader和OutputStreamWriter是Reader 和Writer的实现类,用于将字节输入/输出流转换为字符输入/输出流。例如下面程序:
import java.io.*;
public class KeyinTest {
public static void main(String[] args)
{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String buffer = null;
try {
while((buffer = br.readLine()) != null)
{
//如果读取的字符串为“exit”,则程序退出
if(buffer.equals("exit"))
System.exit(1);
//打印读取的内容
System.out.println("输入内容为:"+ buffer);
}
} catch (IOException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
}
Tips:
转换流除了可将字节流转换为字符流外,更便捷的一个功能是可以指定编码表。例:new OutputStreamWriter(OutputStream out, String charsetName)。即创建了一个使用指定字符集(charsetName,可以是GBK或UTF-8等字符集名称)的输出转换流对象。
4、推回输入流
推回输入流:PushbackInputStream和PushbackReader,他们有如下三个方法:
——void unread(byte[]/char[] buf):将一个字节/字符数组内容推回到推回缓冲区里,从而允许重复读取刚刚读取过的内容。
——void unread(byte[]/char[] buf, int off, int len):将一个字节/字符数组从off开始,长度为len的字节/字符内容推回到推回缓冲区里。
——void unread(int b):将一个字节/字符推回到推回缓冲区里。
Tips:
当程序调用这两个推回输入流的read()方法时总是先从推回缓冲区读取,只有完全读取了推回缓冲区的内容后,但还没装满read方法所需的数组时才会从原输入流中读取。
5、重定向标准输入/输出
在System类提供了如下三个方法重定向标准输入输出的方法:
——static void setErr(PrintStream err):重定向标准错误输出流。
——static void setIn(InputStream in):重定向标准输入流。
——static void setOut(OutputStream):重定向标准输出流。
下面的例子通过重定向标准输入输出流到指定文件,实现了文件的复制:
import java.io.*;
public class RedirectTest {
public static void main(String[] args)
{
try {
//创建输入流
FileInputStream fis = new FileInputStream(".\\src\\io\\RedirectTest.java");
//重定向标准输入流为fis流
System.setIn(fis);
PrintStream ps = new PrintStream(new FileOutputStream("副本.java"));
System.setOut(ps);
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
String line = null;
while((line = br.readLine()) != null)
{
bw.write(line);
bw.newLine();
bw.flush();
}
} catch (Exception e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
}
6、RandomAcessFile
RandomAcessFile是Java输入输出流体系中功能最丰富的文件内容访问类,它既可以读取文件内容,又可以向文件输出数据。与普通输入输出流不同的是,它支持“随机访问”的方式,程序可以跳到文件的任意地方来读写数据。
RandomAcessFile对象包含一个记录指针,用以表示当前读写处的位置,当程序新创建了一个RondomAcessFile对象时,该对象的记录指针位于文件头,当读写了n个字节后,文件记录指针将向后移动n个字节。除此之外,RandomAcessFile可以自由移动记录指针,提供了如下两个方法来操作文件记录指针:
——long getFilePointer():返回记录指针的当前位置。
——void seek(long pos):将文件记录指针定位到pod位置。
RondomAcessFile既包含了类似于InoutStream的三个read()方法,也包含了类似于OutputStream的三个write()方法。此外,它还包含了一系列的readXxx()和writeXxx()方法来完成输入输出。
Tips:
创建RondomAcessFile对象时还需指定一个mode参数,该参数指定RandomAcessFile的访问模式,该参数有四个值:r(只读),rw(读写),rws(相对rw模式,要求每个更新都同步写入底层设备),rwd(相对rw模式,要求每个更新都同步写入底层设备)。
7、流操作规律
如何选择操作流对象:
1、明确源和目的
——源:即输入流,有InputStream和Reader的实现类。
——目的:即输出流,有OutputStream和Writer的实现类。
2、操作的数据是否是纯文本
——是:选字符流
——否:选字节流
3、当体系明确后,再明确使用哪个具体对象
可以通过设备来区分:
——源设备:内存,硬盘,键盘
——目的设备:内存,键盘,控制台
Tips:
——标准的键盘输入:System.in。最常见写法:BufferReader bufr = new BufferReader(new InputStreatReader(System.in));
——标准的屏幕输出:System.out。最常见写法:BufferWriter bufw = new BufferWriter(new OutputStreamWriter(System.out));
---------------------- ASP.Net+Android+IOS开发、.Net培训、期待与您交流! ----------------------
- 黑马程序员-- 十、IO
- 黑马程序员----二十-IO流一
- 黑马程序员 Java基础<十>---> IO流<1>
- 黑马程序员--第二十一天:io流的第四天
- 黑马程序员学习(十) IO流学习总结
- 黑马程序员 十、IO 文件操作(2)
- 黑马程序员(十)
- 黑马程序员 IO
- 黑马程序员之IO
- 黑马程序员 IO流
- 黑马程序员之IO
- 黑马程序员--IO
- 黑马程序员IO流
- 黑马程序员-io
- 黑马程序员-IO流
- 黑马程序员-IO
- 黑马程序员---IO流
- 黑马程序员-----IO流
- QT实现天气预报软件
- 一道面试题:三次称量判断十二个球中一个劣质球的解法
- ACM HDOJ 1232 (畅通工程 )
- zoj2301Color the Ball(线段树,离散化,成段更新)
- jsp:useBean 中type、class和beanName
- 黑马程序员-- 十、IO
- Linux中变量$#,$@,$0,$1,$2,$*,$$,$?的含义
- Excel中Match()函数使用方法
- qemu-kvm学习资料
- Qt模块化笔记之Qt Widgets——抽象旋转框及其继承类
- 正向动力学与反向动力学
- 【Angular】Controller用来初始化作用域Scope === ng-init
- float:center
- android 语音识别