一、InputStream和Reader
InputStream和Reader是所有输入流的基类,它们都是两个抽象类,本身并不能创建实例来执行输入,但它们将所谓所有输入流的模板,所以它们的方法是所有输入流都可使用的方法。它们包含如下三个方法:
1.1 InputStream里包含的方法
★int read():从输入流中读取单个字节(相当于从图15.5所示水管中取出一滴水),返回所读取的字节数据(字节数据可直接转换为int类型)。
★int read(byte[] b):从输入流中读取最多b.length个字节的数据,并将其存储在字节数组b中,返回实际读取的字节数。
★int read(byte[] b, int off, int len):从输入流中读取最多len字节的数据,并将其存储在数组 b 中,放入b数组中时,并不是从数组起点开始,而是从off位置开始,返回实际读取的字节数。
1.2 Reader里包含的方法
★int read():从输入流中读取单个字符(2个字节)(相当于从图15.5所示水管中取出一滴水),返回所读取的字节数据(字节数据可直接转换为int类型)。
★int read(char[] cbuf):从输入流中读取最多cbuf个字节的数据,并将其存储在字节数组b中,返回实际读取的字节数。
★int read(char[] cbuf, int off, int len):从输入流中读取最多len字节的数据,并将其存储在字符数组cbuf中,放入cbuf数组中时,并不是从数组起点开始,而是从off位置开始,返回实际读取的字符数。
1.3 实例演示:
InputStream和Reader所提供的方法,功能基本一致。InputStream和Reader都是将输入数据抽象成水管,程序通过read()方法来读取多个水滴。当使用数组时作为read()方法的参数时,可以理解为使用一个“竹筒”到如图所示的水管中取水。程序从数据流中取出水后,记录指针自动后移,多次重复这个取水过程,直到最后,read()方法返回-1,即表示到达数据流的结束点。
InputStream和Reader是所有输入流的基类,它们都是两个抽象类,本身并不能创建实例来执行输入,它们分别有一个用于读取文件的输入流:FileInputSream和FileReader,它们都是节点流——会直接和指定文件相关联。
下面示范使用了FileInputStream流来读取自身的效果:
package section3;
import java.io.FileInputStream;
import java.io.IOException;
public class FileInputStreamTest
{
public static void main(String[] args)
throws IOException
{
//创建字节输入流
var fis=new FileInputStream("src\section3\FileInputStreamTest.java");
//创建一个长度为1024的“竹筒”
byte[] bbuf=new byte[1024];
//用于保存实际读到的字节数
var hasRead=0;
//使用循环重复“取水”的过程
while((hasRead=fis.read(bbuf))>0)
{
//从竹筒中取出“水滴”(字节),将字节数组转换为字符串输出
System.out.println(new String(bbuf,0,hasRead));
}
//关闭文件输入流,放在finally块里更安全
fis.close();
}
}
下面演示使用FileReader来读取文件自身
package section3;
import java.io.FileReader;
import java.io.IOException;
public class FileReaderTest
{
public static void main(String[] args) {
try (
//创建字符输入流
var fr = new FileReader("src\section3\FileReaderTest.java")
) {
//创建一个长度为32的“竹筒”
var cbuf = new char[32];
//用于保存实际读到的字节数
var hasRead = 0;
//使用循环重复“取水”的过程
while ((hasRead = fr.read(cbuf)) > 0) {
//从竹筒中取出“水滴”(字节),将字节数组转换为字符串输出
System.out.println(new String(cbuf, 0, hasRead));
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
1.4 InputStream和FileReader提供的移动指针的方法
(1)void mark(int readAheadLimit):在记录指针的当前位置记录一个标记。
(2)boolean markSupported():判断此输入流是否指出mark()操作,即是否支持记录标记。
(3)void reset():将记录指针重新定位到上一次记录标记的位置。
(4)long skip(long n):记录指针向前移动n个字节/字符。
二、OutputStream和Writer
OutputStream和Writer也非常相似,两个流都提供了如下三个方法:
★void write(int c):将指定的字节/字符输出到输出流中,其中c既可以代表字节,也可以代表字符。
★void write(byte[]/char[] buf):将字节数组/字符数组中的数据输出到指定输出流中。
★void write(char[] cbuf, 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来执行输出,用以实现FileOutputStreamTest.java文件的功能。
package section3;
import java.io.FileReader;
import java.io.IOException;
public class FileReaderTest
{
public static void main(String[] args) {
try (
//创建字符输入流
var fr = new FileReader("src\section3\FileReaderTest.java")
) {
//创建一个长度为32的“竹筒”
var cbuf = new char[32];
//用于保存实际读到的字节数
var hasRead = 0;
//使用循环重复“取水”的过程
while ((hasRead = fr.read(cbuf)) > 0) {
//从竹筒中取出“水滴”(字节),将字节数组转换为字符串输出
System.out.println(new String(cbuf, 0, hasRead));
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
运行上面程序,即看到在当前路径下多了一个文件:newFile.txt,该文件的内容和FileOutputStreamTest.java文件内容相同。
如果希望直接输出字符串内容,即使用writer会由更好的效果,如下程序所示:
package section3;
import java.io.FileWriter;
import java.io.IOException;
public class FileWriterTest
{
public static void main(String[] args)
{
try(
var fw=new FileWriter("src\section3\poem.txt")
)
{
fw.write("锦瑟 - 李商隐
");
fw.write("锦瑟无端五十弦,一弦一柱思华年。
");
fw.write("庄生晓梦迷蝴蝶,望帝春心托杜鹃。
");
fw.write("沧海月明珠有泪,蓝田日暖玉生烟。
");
fw.write("此情可待成追忆,只是当时已惘然。
");
}
catch (IOException e) {
e.printStackTrace();
}
}
}
注意:widows平台的换行符————