原文出处: linbingdong
Java中I/O操作主要是指使用Java进行输入,输出操作. Java所有的I/O机制都是基于数据流进行输入输出,这些数据流表示了字符或者字节数据的流动序列。
数据流是一串连续不断的数据的集合,就像水管里的水流,在水管的一端一点一点地供水,而在水管的另一端看到的是一股连续不断的水流。数据写入程序可以是一段、一段地向数据流管道中写入数据,这些数据段会按先后顺序形成一个长的数据流。对数据读取程序来说,看不到数据流在写入时的分段情况,每次可以读取其中的任意长度的数据,但只能先读取前面的数据后,再读取后面的数据(不能随机读取)。不管写入时是将数据分多次写入,还是作为一个整体一次写入,读取时的效果都是完全一样的。
简而言之:数据流是一组有序,有起点和终点的字节的数据序列。包括输入流和输出流。
当程序需要读取数据的时候,就会建立一个通向数据源的连接,这个数据源可以是文件,内存,或是网络连接。类似的,当程序需要写入数据的时候,就会建立一个通向目的地的连接。
数据流分类:
流序列中的数据既可以是未经加工的原始二进制数据,也可以是经一定编码处理后符合某种格式规定的特定数据。 因此Java中的流分为两种:
- 字节流:数据流中最小的数据单元是字节
- 字符流:数据流中最小的数据单元是字符, Java中的字符是Unicode编码,一个字符占用两个字节。
概览
Java.io包中最重要的就是5个类和一个接口。5个类指的是File、OutputStream、InputStream、Writer、Reader;一个接口指的是Serializable。掌握了这些就掌握了Java I/O的精髓了。
Java I/O主要包括如下3层次:
- 流式部分——最主要的部分。如:OutputStream、InputStream、Writer、Reader等
- 非流式部分——如:File类、RandomAccessFile类和FileDescriptor等类
- 其他——文件读取部分的与安全相关的类,如:SerializablePermission类,以及与本地操作系统相关的文件系统的类,如:FileSystem类和Win32FileSystem类和WinNTFileSystem类。
主要类如下:
- File(文件特征与管理):用于文件或者目录的描述信息,例如生成新目录,修改文件名,删除文件,判断文件所在路径等。
- InputStream(字节流,二进制格式操作):抽象类,基于字节的输入操作,是所有输入流的父类。定义了所有输入流都具有的共同特征。
- OutputStream(字节流,二进制格式操作):抽象类。基于字节的输出操作。是所有输出流的父类。定义了所有输出流都具有的共同特征。
- Reader(字符流,文本格式操作):抽象类,基于字符的输入操作。
- Writer(字符流,文本格式操作):抽象类,基于字符的输出操作。
- RandomAccessFile(随机文件操作):它的功能丰富,可以从文件的任意位置进行存取(输入输出)操作。
File类
Java文件类以抽象的方式代表文件名和目录路径名。该类主要用于文件和目录的创建、文件的查找和文件的删除等。 File对象代表磁盘中实际存在的文件和目录。通过以下构造方法创建一个File对象。
构造方法摘要 | |
---|---|
File(File parent, String child) 根据 parent 抽象路径名和 child 路径名字符串创建一个新 File 实例。 | |
File(String pathname) 通过将给定路径名字符串转换为抽象路径名来创建一个新 File 实例。 | |
File(String parent, String child) 根据 parent 路径名字符串和 child 路径名字符串创建一个新 File 实例。 | |
File(URI uri) 通过将给定的 file: URI 转换为一个抽象路径名来创建一个新的 File 实例。 |
创建File对象成功后,可以使用以下列表中的方法操作文件。
功能 | 方法描述 |
---|---|
获取 功能 | public String getName() 返回由此抽象路径名表示的文件或目录的名称。 |
public String getParent() 返回此抽象路径名的父路径名的路径名字符串,如果此路径名没有指定父目录,则返回 null 。 | |
public File getParentFile() 返回此抽象路径名的父路径名的抽象路径名,如果此路径名没有指定父目录,则返回 null 。 | |
public String getPath() 将此抽象路径名转换为一个路径名字符串。 | |
public String getAbsolutePath() 返回抽象路径名的绝对路径名字符串。 | |
public long length() 返回由此抽象路径名表示的文件的长度。 | |
public long lastModified() 返回此抽象路径名表示的文件最后一次被修改的时间。 | |
public String[] list() 返回由此抽象路径名所表示的目录中的文件和目录的名称所组成字符串数组。 | |
public String[] list(FilenameFilter filter) 返回由包含在目录中的文件和目录的名称所组成的字符串数组,这一目录是通过满足指定过滤器的抽象路径名来表示的。 | |
public File[] listFiles() 返回一个抽象路径名数组,这些路径名表示此抽象路径名所表示目录中的文件。 | |
public File[] listFiles(FileFilter filter) 返回表示此抽象路径名所表示目录中的文件和目录的抽象路径名数组,这些路径名满足特定过滤器。 | |
public String toString() 返回此抽象路径名的路径名字符串。 | |
功能
| public boolean isAbsolute() 测试此抽象路径名是否为绝对路径名。 |
public boolean isHidden() 是否隐藏 | |
public boolean canRead() 测试应用程序是否可以读取此抽象路径名表示的文件。 | |
public boolean canWrite() 测试应用程序是否可以修改此抽象路径名表示的文件。 | |
public boolean exists() 测试此抽象路径名表示的文件或目录是否存在。 | |
public boolean isDirectory() 测试此抽象路径名表示的文件是否是一个目录。 | |
public boolean isFile() 测试此抽象路径名表示的文件是否是一个标准文件。 | |
public boolean equals(Object obj) 测试此抽象路径名与给定对象是否相等。 | |
设置 功能 | public boolean setLastModified(long time) 设置由此抽象路径名所指定的文件或目录的最后一次修改时间。 |
public boolean setReadOnly() 标记此抽象路径名指定的文件或目录,以便只可对其进行读操作。 | |
创建 功能 | public static File createTempFile(String prefix, String suffix, File directory) throws IOException 在指定目录中创建一个新的空文件,使用给定的前缀和后缀字符串生成其名称。 |
public static File createTempFile(String prefix, String suffix) throws IOException 在默认临时文件目录中创建一个空文件,使用给定前缀和后缀生成其名称。 | |
public boolean mkdir() 创建此抽象路径名指定的目录 | |
public boolean mkdirs() 创建此抽象路径名指定的目录,包括创建必需但不存在的父目录。 | |
public boolean createNewFile() throws IOException 创建文件,如果存在就不创建 | |
删除 功能 | public boolean delete() 删除此抽象路径名表示的文件或目录。 |
public boolean renameTo(File dest) 重新命名此抽象路径名表示的文件。 | |
比较 功能 | public int compareTo(Object o) 按字母顺序比较抽象路径名与给定对象。 |
public int compareTo(File pathname) 按字母顺序比较两个抽象路径名。 | |
其他 | public void deleteOnExit() 在虚拟机终止时,请求删除此抽象路径名表示的文件或目录 |
示例:
//创建File对象
File file = new File("D:\\1.txt");
//创建文件
System.out.println(file.createNewFile());
File folder = new File("D:\\demo");
//创建单级目录
folder.mkdir();
// File folders = new File("D:\\demo\\new\\test");
// File folders = new File("D:\\demo","new\\test");
File folders = new File(folder,"new\\test");
//创建多级目录
folders.mkdirs();
//删除文件
//file.delete();
//删除非空目录(删除不了非空目录)
folder.delete();
//删除空目录
folders.delete();
File f1 = new File("1.txt");
f1.createNewFile();
/*File f2 = new File("2.txt");
//同一目录下的重命名
f1.renameTo(f2);*/
/*File f3 = new File("D:\\a.txt");
//不同路径下的重命名(剪切并重命名)
f1.renameTo(f3);*/
//绝对路径
System.out.println(f1.getAbsolutePath());
//相对路径
System.out.println(f1.getPath());
//字节数
System.out.println(f1.length());
//名称
System.out.println(f1.getName());
//最后一次修改时间毫秒值
long time = f1.lastModified();
//创建文件的日期
Date date = new Date(time);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String s = sdf.format(date);
System.out.println(s);
//获取目录下所有文件和文件夹的名称
String[] names = folder.list();
for(String name:names){
System.out.println(name);
}
System.out.println("-------------------");
//获取目录下所有文件和文件夹的File对象
File[] files = folder.listFiles();
for (File file : files) {
System.out.println(file.getAbsolutePath());
}
I/O流
java.io包里有4个基本类:InputStream、OutputStream及Reader、Writer类,它们分别处理字节流和字符流。其他各种各样的流都是由这4个派生出来的。
按数据类型分:①字节流 ②字符流
按来源/去向分类:①输入流(读入) ②输出流(写出)
- File(文件): FileInputStream, FileOutputStream, FileReader, FileWriter
- byte[]:ByteArrayInputStream, ByteArrayOutputStream
- Char[]: CharArrayReader, CharArrayWriter
- String: StringBufferInputStream, StringReader, StringWriter
- 网络数据流:InputStream, OutputStream, Reader, Writer
字节流
InputStream
InputStream 为字节输入流,它本身为一个抽象类,必须依靠其子类实现各种功能,此抽象类是表示字节输入流的所有类的超类。 继承自InputStream 的流都是向程序中输入数据的,且数据单位为字节(8bit);
InputStream是输入字节数据用的类,所以InputStream类提供了3种重载的read方法。Inputstream类中的常用方法:
- public abstract int read( ):读取一个byte的数据,返回值是高位补0的int类型值。若返回值=-1说明没有读取到任何字节读取工作结束。
- public int read(byte b[ ]):读取b.length个字节的数据放到b数组中。返回值是读取的字节数。该方法实际上是调用下一个方法实现的。
- public int read(byte b[ ], int off, int len):从输入流中最多读取len个字节的数据,存放到偏移量为off的b数组中。
- public int available( ):返回输入流中可以读取的字节数。注意:若输入阻塞,当前线程将被挂起,如果InputStream对象调用这个方法的话,它只会返回0,这个方法必须由继承InputStream类的子类对象调用才有用。
- public long skip(long n):忽略输入流中的n个字节,返回值是实际忽略的字节数, 跳过一些字节来读取
- public int close( ) :使用完后,必须对我们打开的流进行关闭。
来看看几种不同的InputStream:
- FileInputStream把一个文件作为InputStream,实现对文件的读取操作
- ByteArrayInputStream:把内存中的一个缓冲区作为InputStream使用
- StringBufferInputStream:把一个String对象作为InputStream
- PipedInputStream:实现了pipe的概念,主要在线程中使用
- SequenceInputStream:把多个InputStream合并为一个InputStream
- FilerInputStream:封装其它的输入流,并为它们提供额外的功能。
FileInputStream
文件字节输入流,继承InputStream
1)构造方法
FileInputStream(File file):通过File对象创建文件字节输入流
FileInputStream(String filepath):通过File对象创建文件字节输入流
2)成员方法
int read():一次读一个字节,返回
示例:
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("io\\1.txt");
//读一个字节
/*int by = fis.read();
System.out.println((char)by);*/
/*int by = 0;
while((by=fis.read()) != -1){
System.out.print((char)by);
}*/
//读一个字节数组
byte[] bys = new byte[5];
int len = 0;//读出字节的个数
//没有数据,读出字节个数为-1
while((len=fis.read(bys)) != -1){
System.out.print(new String(bys,0,len));
}
//释放资源
fis.close();
}
ByteArrayInputStream
字节数组输入流在内存中创建一个字节数组缓冲区,从输入流读取的数据保存在该字节数组缓冲区中。
1)构造方法
ByteArrayInputStream(byte[] buf):接收字节数组作为参数创建一个ByteArrayInputStream
ByteArrayInputStream(byte[] buf, int offset, int length):接收一个字节数组和两个整型变量 off、len,off表示第一个读取的字节,len表示 读取字节的长度。
2)成员方法
public int read():从此输入流中读取下一个数据字节。
public int read(byte[] r, int off, int len):将最多 len 个数据字节从此输入流读入字节数组。
public int available():返回可从此输入流读取(或跳过)的剩余字节数。
public void mark(int read):设置流中的当前标记位置。
public long skip(long n):从此输入流中跳过 n 个输入字节。
public void reset():将缓冲区的位置重置为标记位置。
示例:
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
public class ByteArrayInputStreamDemo {
public static void main(String[] args) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream(12);
while(baos.size() != 10){
baos.write(System.in.read());
}
byte b[] = baos.toByteArray();
System.out.println("Print the content");
for(int x = 0 ; x < b.length ; x++){
//打印字符
System.out.print((char)b[x] + "\t");
}
System.out.println();
int c = 0;
ByteArrayInputStream bais = new ByteArrayInputStream(b);
System.out.println("Converting characters to Upper case");
while((c=bais.read())!=-1){
System.out.println(Character.toUpperCase((char)c));
}
bais.reset();
}
}
运行结果如下:
abcdefghijklmn
Print the content
a b c d e f g h i j
Converting characters to Upper case
A
B
C
D
E
F
G
H
I
J
StringBufferInputStream
已过时。 此类未能正确地将字符转换为字节。从 JDK 1.1 开始,从字符串创建流的首选方法是通过 StringReader
类进行创建。
简单示例:
public static void main(String[] args) throws IOException {
InputStream in = new StringBufferInputStream("Has been out of date.Creates a string input stream to read data from the specified string.");
int c;
while((c=in.read())!=-1){
System.out.print((char)c);
}
}
运行输出:
Has been out of date.Creates a string input stream to read data from the specified string.
(已过时。 创建一个字符串输入流,以从指定字符串读取数据。)
PipedInputStream
管道输入流,管道输入流应该连接到管道输出流;管道输入流提供要写入管道输出流的所有数据字节。通常,数据由某个线程从 PipedInputStream
对象读取,并由其他线程将其写入到相应的 PipedOutputStream
。不建议对这两个对象尝试使用单个线程,因为这样可能死锁线程。管道输入流包含一个缓冲区,可在缓冲区限定的范围内将读操作和写操作分离开。如果向连接管道输出流提供数据字节的线程不再存在,则认为该管道已损坏。
1).构造方法
PipedInputStream():创建尚未连接的 PipedInputStream。
PipedInputStream(int pipeSize):创建一个尚未连接的PipedInputStream,并对管道缓冲区使用指定的管道大小。
PipedInputStream(PipedOutputStream src):创建PipedInputStream,使其连接到管道输出流src。
PipedInputStream(PipedOutputStream src, int pipeSize):创建一个PipedInputStream,使其连接到管道输出流 src,并对管道缓冲区使 用指定的管道大小。
2).成员方法
int available():返回可以不受阻塞地从此输入流中读取的字节数。
void close():关闭此管道输入流并释放与该流相关的所有系统资源。
void connect(PipedOutputStream src):使此管道输入流连接到管道输出流 src。
int read():读取此管道输入流中的下一个数据字节。
int read(byte[] b, int off, int len):将最多 len 个数据字节从此管道输入流读入 byte 数组。
protected void receive(int b):接收数据字节。
示例:
import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
/**
* 生产者线程
*/
class Producer extends Thread {
//输出流
private PipedOutputStream out = new PipedOutputStream();
//构造方法
public Producer(PipedOutputStream out) {
this.out = out;
}
@Override
public void run() {
writeMessage();
}
private void writeMessage() {
StringBuilder sb = new StringBuilder("Hello World!!!");
try {
out.write(sb.toString().getBytes());
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 消费线程
*/
class Consumer extends Thread {
//输入流, 默认缓冲区大小为1024
private PipedInputStream in = new PipedInputStream();
//构造方法
public Consumer(PipedInputStream in) {
this.in = in;
}
@Override
public void run() {
readMessage();
}
private void readMessage() {
byte [] buf = new byte[1024];
try {
int len = in.read(buf);
System.out.println("缓冲区的内容为: " + new String(buf, 0, len));
in.close();
} catch (IOException e) {
e.printStackTrace();
} finally { }
}
}
public class PipedDemo {
public static void main(String[] args) {
/*
* 流程
* 1 建立输入输出流
* 2 绑定输入输出流
* 3 向缓冲区写数据
* 4 读取缓冲区数据
*/
PipedOutputStream out = new PipedOutputStream();
PipedInputStream in = new PipedInputStream();
Producer producer = new Producer(out);
Consumer consumer = new Consumer(in);
try {
out.connect(in);
producer.start();
consumer.start();
} catch (IOException e) {
e.printStackTrace();
}
}
}
//运行结果:缓冲区的内容为: Hello World!!!
注意:使用connect方法将输入流和输出流关联起来,调用 pipedInputStream.connect(pipedOutputStream)或者pipedOutputStream.connect(pipedInputStream)效果是一样的,但只能选择其中的一个而不能两个connect同时调用。另一种关联方法是通过构造方法绑定,但需要捕获或抛出异常,如:
PipedOutputStream out = new PipedOutputStream();
PipedInputStream in = new PipedInputStream(out);
或者
PipedInputStream in = new PipedInputStream();
PipedOutputStream out = new PipedOutputStream(in);
SequenceInputStream
SequenceInputStream
表示其他输入流的逻辑串联。它从输入流的有序集合开始,并从第一个输入流开始读取,直到到达文件末尾,接着从第二个输入流读取,依次类推,直到到达包含的最后一个输入流的文件末尾为止。
1)构造方法
SequenceInputStream(Enumeration<? extends InputStream> e): 通过记住参数来初始化新创建的 SequenceInputStream,该参数必须是生成运行时类型为 InputStream 对象的 Enumeration 型参数。
SequenceInputStream(InputStream s1, InputStream s2):通过记住这两个参数来初始化新创建的 SequenceInputStream(将按顺序读取这两个参数,先读取 s1,然后读取 s2),以提供从此 SequenceInputStream 读取的字节。
2)成员方法
int available():返回不受阻塞地从当前底层输入流读取(或跳过)的字节数的估计值,方法是通过下一次调用当前底层输入流的方法。
void close():关闭此输入流并释放与此流关联的所有系统资源。
int read():从此输入流中读取下一个数据字节。
int read(byte[] b, int off, int len):将最多 len 个数据字节从此输入流读入 byte 数组。
示例:
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.SequenceInputStream;
import java.util.Vector;
public class SequenceInputStreamDemo {
public static void main(String[] args) throws Exception {
InputStream input1 = new FileInputStream("src/test/output.xml");
InputStream input2 = new FileInputStream("src/test/hello.java");
InputStream input3 = new FileInputStream("src/test/file.txt");
InputStream input4 = new FileInputStream("src/test/Main.java");
InputStream input5 = new FileInputStream("src/test/person.xml");
//组合二个InputStream
SequenceInputStream sequenceInputStream1 = new SequenceInputStream(input1,input2);
/*int data = sequenceInputStream1.read();
while(data != -1){
System.out.print((char)data);
data = sequenceInputStream1.read();
}*/
//组合两个以上的InputStream
Vector<InputStream> streams = new Vector<InputStream>();
streams.add(input3);
streams.add(input4);
streams.add(input5);
SequenceInputStream sequenceInputStream2 = new SequenceInputStream(streams.elements());
/*int data2 = sequenceInputStream2.read();
while(data2 != -1){
System.out.print((char)data2);
data2 = sequenceInputStream2.read();
}
sequenceInputStream2.close();*/
//组合两个的SequenceInputStream
SequenceInputStream sequenceInputStream3 = new SequenceInputStream(
sequenceInputStream1,sequenceInputStream2);
int data3 = sequenceInputStream3.read();
while(data3 != -1){
System.out.print((char)data3);
data3 = sequenceInputStream3.read();
}
sequenceInputStream3.close();
}
}
FilterInputStream
包含其他一些输入流,它将这些流用作其基本数据源,它可以直接传输数据或提供一些额外的功能。FilterInputStream
类本身只是简单地重写那些将所有请求传递给所包含输入流的 InputStream
的所有方法。FilterInputStream
的子类可进一步重写这些方法中的一些方法,并且还可以提供一些额外的方法和字段。
直接已知子类:
BufferedInputStream, CheckedInputStream, CipherInputStream, DataInputStream, DeflaterInputStream, DigestInputStream, InflaterInputStream, LineNumberInputStream, ProgressMonitorInputStream, PushbackInputStream
1)构造方法摘要
protected FilterInputStream(InputStream in)
将参数 in 分配给字段 this.in,以便记住它供以后使用,通过这种方式创建一个 FilterInputStream。
2)方法摘要
int available()
返回下一次对此输入流调用的方法可以不受阻塞地从此输入流读取(或跳过)的估计剩余字节数。
void close()
关闭此输入流并释放与此流关联的所有系统资源。
void mark(int readlimit)
在输入流中的当前位置上作标记。
boolean markSupported()
测试此输入流是否支持 mark 和 reset 方法。
int read()
从此输入流中读取下一个数据字节。
int read(byte[] b)
从此输入流中将 byte.length 个字节的数据读入一个 byte 数组中。
int read(byte[] b, int off, int len)
从此输入流中将 len 个字节的数据读入一个 byte 数组中。
void reset()
将此流重新定位到对此输入流最后调用 mark 方法时的位置。
long skip(long n)
跳过和丢弃此输入流中数据的 n 个字节。
- BufferedInputStream
BufferedInputStream
为另一个输入流添加一些功能,即缓冲输入以及支持 mark
和 reset
方法的能力。在创建 BufferedInputStream
时,会创建一个内部缓冲区数组。在读取或跳过流中的字节时,可根据需要从包含的输入流再次填充该内部缓冲区,一次填充多个字节。mark
操作记录输入流中的某个点,reset
操作使得在从包含的输入流中获取新字节之前,再次读取自最后一次 mark
操作后读取的所有字节。
1)构造方法摘要
BufferedInputStream(InputStream in)
创建一个 BufferedInputStream 并保存其参数,即输入流 in,以便将来使用。
BufferedInputStream(InputStream in, int size)
创建具有指定缓冲区大小的 BufferedInputStream 并保存其参数,即输入流 in,以便将来使用。
2)方法摘要
int available():返回可以从此输入流读取(或跳过)、且不受此输入流接下来的方法调用阻塞的估计字节数。
void close():关闭此输入流并释放与该流关联的所有系统资源。
void mark(int readlimit):在此输入流中标记当前的位置。
boolean markSupported():测试此输入流是否支持 mark 和 reset 方法。
int read():从输入流中读取数据的下一个字节。
int read(byte[] b, int off, int len):从此字节输入流中给定偏移量处开始将各字节读取到指定的 byte 数组中。
void reset():将此流重新定位到最后一次对此输入流调用 mark 方法时的位置。
long skip(long n): 跳过和丢弃此输入流中数据的 n 个字节。
示例:
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
//比较字节流和缓冲字节流的读写速度
public class StreamDemo01 {
public static void main(String[] args) throws IOException {
// System.out.println(getTime());//448
System.out.println(getTime2());//142
}
//字节流读写速度
public static long getTime() throws IOException{
//字节流
InputStream is = new FileInputStream("C:\\Users\\Administrator\\Desktop\\1.txt");
OutputStream os = new FileOutputStream(".\\io\\Java1.txt");
//一次读写一个字节
/*int by = 0;
while((by=is.read()) != -1){
os.write(by);
}*/
long t1 = System.currentTimeMillis();
//一次读写一个字节数组
//开发中字节数组长度一般为1024或者1024的倍数
byte[] bys = new byte[1024];
int len = 0;
while((len = is.read(bys)) != -1){
os.write(bys);
}
long t2 = System.currentTimeMillis();
return t2-t1;
}
//缓冲字节流读写速度
public static long getTime2() throws IOException{
//缓冲字节流对象
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("C:\\Users\\Administrator\\Desktop\\1.txt"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(".\\io\\Java.txt"));
long t1 = System.currentTimeMillis();
//一次读取一个字节
/*int by = 0;
while((by=bis.read()) != -1){
bos.write(by);
}*/
//一次读写一个字节数组
//开发中字节数组长度一般为1024或者1024的倍数
byte[] bys = new byte[1024];
int len = 0;
while((len = bis.read(bys)) != -1){
bos.write(bys);
}
long t2 = System.currentTimeMillis();
return t2-t1;
}
}
- DataInputStream
数据输入流允许应用程序以与机器无关方式从底层输入流中读取基本 Java 数据类型。应用程序可以使用数据输出流写入稍后由数据输入流读取的数据。
DataInputStream 对于多线程访问不一定是安全的。 线程安全是可选的,它由此类方法的使用者负责。
1)构造方法摘要
DataInputStream(InputStream in):使用指定的底层 InputStream 创建一个 DataInputStream。
2)方法摘要
int read(byte[] b):从包含的输入流中读取一定数量的字节,并将它们存储到缓冲区数组 b 中。
int read(byte[] b, int off, int len):从包含的输入流中将最多 len 个字节读入一个 byte 数组中。
boolean readBoolean():读取一个输入字节,如果该字节不是零,则返回 true,如果是零,则返回 false。
byte readByte():读取并返回一个输入字节。
char readChar():读取两个输入字节并返回一个 char 值。
double readDouble():读取八个输入字节并返回一个 double 值。
float readFloat(): 读取四个输入字节并返回一个 float 值。
void readFully(byte[] b): 从输入流中读取一些字节,并将它们存储在缓冲区数组 b 中。
void readFully(byte[] b, int off, int len): 从输入流中读取 len 个字节。
int readInt(): 读取四个输入字节并返回一个 int 值。
String readLine():已过时。该方法无法将字节正确转换为字符。
从 JDK 1.1 开始,读取文本行的首选方法是使用 BufferedReader.readLine() 方法。
使用 DataInputStream 类读取文本行的程序可以改为使用 BufferedReader 类,只要将以下形式的代码:
DataInputStream d = new DataInputStream(in);
替换为:
BufferedReader d = new BufferedReader(new InputStreamReader(in));
long readLong():读取八个输入字节并返回一个 long 值。
short readShort(): 读取两个输入字节并返回一个 short 值。
int readUnsignedByte():读取一个输入字节,将它左侧补零 (zero-extend) 转变为 int 类型,并返回结果,所以结果的范围是0到 255。
int readUnsignedShort():读取两个输入字节,并返回 0 到 65535 范围内的一个 int 值。
String readUTF():读入一个已使用 UTF-8 修改版格式编码的字符串。
static String readUTF(DataInput in):从流 in 中读取用 UTF-8 修改版格式编码的 Unicode 字符格式的字符串;然后以 String 形式返回此字符串。
int skipBytes(int n):试图在输入流中跳过数据的 n 个字节,并丢弃跳过的字节。
示例见下DataOutputStream。
注: 要用DataInputStream 读取一个文件,这个文件必须是由DataOutputStream 写出的,否则会出现EOFException,因为DataOutputStream 在写出的时候会做一些特殊标记,只有DataInputStream 才能成功的读取。
- PushbackInputStream
PushbackInputStream
为另一个输入流添加性能,即“推回(push back)”或“取消读取 (unread)”一个字节的能力。在代码片段可以很方便地读取由特定字节值分隔的不定数量的数据字节时,这很有用;在读取终止字节后,代码片段可以“取消读取”该字节,这样,输入流上的下一个读取操作将会重新读取被推回的字节。例如,表示构成标识符字符的字节可能由表示操作符字符的字节终止;用于读取一个标识符的方法可以读取到遇到操作符为止,然后将该操作符推回以进行重读。通俗来讲,它允许将读取的字节推回到流中,这样就像流没有被动过,下次调用 read() 时,将再次重新读取。就像男人对女人(Stream)说:我只看看,不动手。
1)构造方法摘要
PushbackInputStream(InputStream in):创建 PushbackInputStream 并保存其参数(即输入流 in),以供将来使用。
PushbackInputStream(InputStream in, int size):使用指定size的推回缓冲区创建PushbackInputStream,并保存其参数(即输入流 in),以供将来使用。
2)方法摘要
int available():返回可以不受下一次调用此输入流的方法阻塞地从此输入流读取(或跳过)的估计字节数。
void close():关闭此输入流并释放与该流关联的所有系统资源。
void mark(int readlimit):在此输入流中标记当前的位置。
boolean markSupported():测试此输入流是否支持 mark 和 reset 方法,此输入流不支持这两个方法。
int read():从此输入流中读取下一个数据字节。
int read(byte[] b, int off, int len):从此输入流将最多 len 个数据字节读入 byte 数组。
void reset():将此流重新定位到最后一次对此输入流调用 mark 方法时的位置。
long skip(long n):从此输入流中跳过并丢弃 n 个数据字节。
void unread(int b): 推回一个字节:将其复制到推回缓冲区之前。
void unread(byte[] b, int off, int len):推回 byte 数组的某一部分:将其复制到推回缓冲区之前。
void unread(byte[] b): 推回一个 byte 数组:将其复制到推回缓冲区之前。
示例:
import java.io.ByteArrayInputStream;
import java.io.PushbackInputStream;
public class PushbackInputStreamDemo {
public static void main(String[] args) throws Exception {
String s = "abcdefg";
/*
* PushbackInputStream pbin = new PushbackInputStream(in)
* 这个构造函数创建的对象一次只能回推一个字节
*/
ByteArrayInputStream in = new ByteArrayInputStream(s.getBytes());
PushbackInputStream pbin = new PushbackInputStream(in);
int n;
while ((n = pbin.read()) != -1) {
System.out.print((char) n);
if('b' == n) pbin.unread('U');
}
System.out.println();
pbin.close();
in.close();
/*
输出结果:
abUcdefg
*/
/*
* PushbackInputStream pbin = new PushbackInputStream(in)
* 这个构造函数创建的对象一次可以回推一个缓存
*/
n=0;
ByteArrayInputStream in2 = new ByteArrayInputStream(s.getBytes());
PushbackInputStream pbin2 = new PushbackInputStream(in2,3);
byte[] buffer = new byte[3];
while ((n = pbin2.read(buffer)) != -1) {
System.out.println(new String(buffer));
if(new String(buffer).equals("abc"))pbin2.unread(new byte[]{'M','N','O'});
buffer = new byte[3];
}
/*
输出:
abc
MNO
def
g
*/
ByteArrayInputStream in3 = new ByteArrayInputStream(s.getBytes());
PushbackInputStream pbin3 = new PushbackInputStream(in3, 4);
n = 0;
byte[] buffer2 = new byte[4];
while ((n = pbin3.read(buffer2)) != -1) {
System.out.println(new String(buffer2));
//取回推缓存中的一部分数据
if(new String(buffer2).equals("abcd"))pbin3.unread(buffer2,2,2);
buffer2 = new byte[4];
}
/*
输出结果:
abcd
cdef
g
*/
}
}
参考:[十三]JavaIO之PushBackInputStream
JAVA基础—PushbackInputStream类简单介绍
OutputStream
OutputStream提供了3个write方法来做数据的输出,这个是和InputStream是相对应的。
- public void write(byte b[ ]):将参数b中的字节写到输出流。
- public void write(byte b[ ], int off, int len) :将参数b的从偏移量off开始的len个字节写到输出流。
- public abstract void write(int b) :先将int转换为byte类型,把低字节写入到输出流中。
- public void flush( ) : 将数据缓冲区中数据全部输出,并清空缓冲区。
- public void close( ) : 关闭输出流并释放与流相关的系统资源。
几种不同的OutputStream:
- ByteArrayOutputStream:把信息存入内存中的一个缓冲区中
- FileOutputStream:把信息存入文件中
- PipedOutputStream:实现了pipe的概念,主要在线程中使用
- FilterInputStream:过滤输出流的所有类的超类
ByteArrayOutputStream
字节数组输出流在内存中创建一个字节数组缓冲区,所有发送到输出流的数据保存在该字节数组缓冲区中。
1).构造方法
ByteArrayOutputStream():创建一个32字节(默认大小)的缓冲区.
ByteArrayOutputStream(int a):创建一个大小为n字节的缓冲区
2).成员方法
public void reset():将此字节数组输出流的 count 字段重置为零,从而丢弃输出流中目前已累积的所有数据输出。
public byte[] toByteArray():创建一个新分配的字节数组。数组的大小和当前输出流的大小,内容是当前输出流的拷贝。
public String toString():将缓冲区的内容转换为字符串,根据平台的默认字符编码将字节转换成字符。
public void write(int w):将指定的字节写入此字节数组输出流。
public void write(byte []b, int off, int len):将指定字节数组中从偏移量 off 开始的 len 个字节写入此字节数组输出流。
public void writeTo(OutputStream outSt):将此字节数组输出流的全部内容写入到指定的输出流参数中。
示例:见上ByteArrayInputStream
FileOutputStream
文件字节输出流,继承OutputStream
1)构造方法
FileOutputStream(File file):通过File对象创建文件字节输出流
FileOutputStream(File file,boolean append):追加写入
FileOutputSteam(String filePath):通过文件名路径创建文件字节输出流
FileOutputSteam(String filePath,boolean append):追加写入
2)成员方法
write(int by):写入一个字节
write(byte[] bys):写入字节数组
write(byte[] bys,int offset,int len):写入字节数组中的一部分
close():关闭流对象,释放资源
说明:
在创建FileOutputStream对象的时候,如果没有写入文件,系统默认会创建文件;如果没有文件夹,系统会报FileNotFoundException异常
示例:
//文件字节输出流
public class FileOutputStreamDemo {
public static void main(String[] args) {
FileOutputStream fos = null;
try {
fos = new FileOutputStream("io\\1.txt");
//写入字节
fos.write(97);
fos.write('a');
//写入字节数组
byte[] bys = {65,66,67,68,69};
fos.write(bys);
//写入字节数组中的一部分
fos.write(bys,2,2);
} catch (FileNotFoundException e) {
System.out.println("文件找不到");
} catch (IOException e) {
System.out.println("字节不能写入");
}finally{
try {
//释放资源代码
fos.close();
} catch (IOException e) {
System.out.println("流对象不能关闭");
}
}
}
}
PipedOutputStream
管道输出流,可以将管道输出流连接到管道输入流来创建通信管道。管道输出流是管道的发送端。通常,数据由某个线程写入 PipedOutputStream
对象,并由其他线程从连接的 PipedInputStream
读取。不建议对这两个对象尝试使用单个线程,因为这样可能会造成该线程死锁。如果某个线程正从连接的管道输入流中读取数据字节,但该线程不再处于活动状态,则该管道被视为处于 毁坏 状态。
1)构造方法
PipedOutputStream():创建尚未连接到管道输入流的管道输出流。
PipedOutputStream(PipedInputStream snk):创建连接到指定管道输入流的管道输出流。
2)方法摘要
void close():关闭此管道输出流并释放与此流有关的所有系统资源。
void connect(PipedInputStream snk):将此管道输出流连接到接收者。
void flush():刷新此输出流并强制写出所有缓冲的输出字节。
void write(byte[] b, int off, int len):将 len 字节从初始偏移量为 off 的指定 byte 数组写入该管道输出流。
void write(int b): 将指定 byte 写入传送的输出流。
示例见PiedInputStream。
FilterInputStream
此类是过滤输出流的所有类的超类。这些流位于已存在的输出流(基础 输出流)之上,它们将已存在的输出流作为其基本数据接收器,但可能直接传输数据或提供一些额外的功能。
FilterOutputStream
类本身只是简单地重写那些将所有请求传递给所包含输出流的 OutputStream
的所有方法。FilterOutputStream
的子类可进一步地重写这些方法中的一些方法,并且还可以提供一些额外的方法和字段。
1).构造方法
FilterOutputStream(OutputStream out):创建一个构建在指定基础输出流之上的输出流过滤器。
2).方法摘要
void close():关闭此输出流并释放与此流有关的所有系统资源。
void flush():刷新此输出流,并强制将所有已缓冲的输出字节写入该流中。
void write(byte[] b):将 b.length 个字节写入此输出流。
void write(byte[] b, int off, int len):将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此输出流。
void write(int b):将指定 byte 写入此输出流。
直接已知子类:
BufferedOutputStream, CheckedOutputStream, CipherOutputStream, DataOutputStream, DeflaterOutputStream, DigestOutputStream, InflaterOutputStream, PrintStream
- BufferedOutputStream
该类实现缓冲的输出流。通过设置这种输出流,应用程序就可以将各个字节写入底层输出流中,而不必针对每次字节写入调用底层系统。即:输出缓冲流,先将要输出的内容放入到内存中,再一次性全都输出。
1)构造方法摘要
BufferedOutputStream(OutputStream out):创建一个新的缓冲输出流,以将数据写入指定的底层输出流。
BufferedOutputStream(OutputStream out, int size):创建一个新的缓冲输出流,以将具有指定缓冲区大小的数据写入指定的底层输出流。
2)方法摘要
void flush():刷新此缓冲的输出流。
void write(byte[] b, int off, int len): 将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此缓冲的输出流。
oid write(int b):将指定的字节写入此缓冲的输出流。
示例:
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
//将桌面的demo目录复制到io目录下
public class StreamTest02 {
public static void main(String[] args) throws IOException {
File from = new File("C:\\Users\\Administrator\\Desktop\\demo");
File dest = new File(".\\io\\demo");
copyFolders(from,dest);
}
public static void copyFolders(File fromFile,File destFile) throws IOException{
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
if(!destFile.exists()){
destFile.mkdirs();
}else{
deleteFolders(destFile);
destFile.mkdir();
}
File[] foldersFiles = fromFile.listFiles();
for (File file : foldersFiles) {
String fileName = file.getName();
File dest = new File(destFile,fileName);
if(file.isFile()){
//文件类型不知道采用字节流
bis = new BufferedInputStream(new FileInputStream(file));
bos = new BufferedOutputStream(new FileOutputStream(dest));
byte[] bys = new byte[1024];
int len = 0;
while((len = bis.read(bys)) != -1){
bos.write(bys,0,len);
}
bos.close();
bis.close();
}else{
//目录
copyFolders(file,dest);
}
}
}
public static void deleteFolders(File destFile) {
File[] folders = destFile.listFiles();
if(destFile == null){
destFile.delete();
}else{
for (File file : folders) {
if(file.isFile()){
file.delete();
}else{
deleteFolders(file);
}
}
//删除当前目录
destFile.delete();
}
}
}
注意:
i、缓冲流需要调用flush()方法才能将内容输入或输出出来,或者调用close()方法时,也会调用flush()方法。
ii、new FileOutputStream("***"); 如果参数中的路径有目录不存在,并不会自动创建目录。
iii、只需将缓冲流关闭,无需将节点流关闭,因为缓冲流内部会自动调用节点流关闭。
- DataOutputStream
数据输出流允许应用程序以适当方式将基本 Java 数据类型写入输出流中。然后,应用程序可以使用数据输入流将数据读入。
1).构造方法摘要
DataOutputStream(OutputStream out):创建一个新的数据输出流,将数据写入指定基础输出流。
2)方法摘要
void flush():清空此数据输出流。
int size():返回计数器 written 的当前值,即到目前为止写入此数据输出流的字节数。
void write(byte[] b, int off, int len):将指定 byte 数组中从偏移量 off 开始的 len 个字节写入基础输出流。
void write(int b):将指定字节(参数 b 的八个低位)写入基础输出流。
void writeBoolean(boolean v):将一个 boolean 值以 1-byte 值形式写入基础输出流。
void writeByte(int v):将一个 byte 值以 1-byte 值形式写出到基础输出流中。
void writeBytes(String s):将字符串按字节顺序写出到基础输出流中。
void writeChar(int v):将一个 char 值以 2-byte 值形式写入基础输出流中,先写入高字节。
void writeChars(String s):将字符串按字符顺序写入基础输出流。
void writeDouble(double v):使用Double类中的doubleToLongBits方法将double参数转换为一个long值,然后将该long值以8-byte值形式写入基础输出流中,先写入高字节。
void writeFloat(float v):使用 Float 类中的 floatToIntBits 方法将 float 参数转换为一个 int 值,然后将该 int 值以 4-byte 值形式写入基础输出流中,先写入高字节。
void writeInt(int v):将一个 int 值以 4-byte 值形式写入基础输出流中,先写入高字节。
void writeLong(long v):将一个 long 值以 8-byte 值形式写入基础输出流中,先写入高字节。
void writeShort(int v):将一个 short 值以 2-byte 值形式写入基础输出流中,先写入高字节。
void writeUTF(String str):以与机器无关方式使用 UTF-8 修改版编码将一个字符串写入基础输出流
示例:模拟一张表一条数据写入并读取数据
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class DataOutputStreamDemo {
public static void main(String[] args) throws IOException {
DataInputStream dis = new DataInputStream(new FileInputStream("io/1.txt"));
DataOutputStream dos = new DataOutputStream(new FileOutputStream("io/1.txt"));
dos.writeUTF("stuno"+"\t"+"stuname"+"\t"+"stubirth"+"\t"+"stusex"+"\t"+"stuaddr"+"\t"+"stutel"+"\n");
dos.writeInt(5001);
dos.writeUTF("李四");
dos.writeUTF("1998-12-12");
dos.writeByte(0);
dos.writeUTF("南京");
dos.writeLong(123456789);
dos.writeChars("\n");
String header = dis.readUTF();
int id = dis.readInt();
String name = dis.readUTF();
String birth = dis.readUTF();
byte sex = dis.readByte();
String addr = dis.readUTF();
Long tel = dis.readLong();
System.out.print(header);
System.out.printf("%d\t%s\t%s\t%d\t%s\t%d",id,name,birth,sex,addr,tel);
}
}
输出结果:
stuno stuname stubirth stusex stuaddr stutel
5001 李四 1998-12-12 0 南京 123456789
1.txt是utf-8编码(试过其他编码格式也未能正确显示,原因可能是DataInputStream读取与DataOutputStream写入时会有一定的格式)
- PrintStream
PrintStream 为其他输出流添加了功能,使它们能够方便地打印各种数据值表示形式。它还提供其他两项功能。与其他输出流不同,PrintStream 永远不会抛出 IOException;它产生的IOException会被自身的函数所捕获并设置错误标记, 用户可以通过 checkError() 返回错误标记,从而查看PrintStream内部是否产生了IOException。另外,为了自动刷新,可以创建一个 PrintStream;这意味着可在写入 byte 数组之后自动调用flush方法,可调用其中一个println方法,或写入一个换行符或字节 ('\n')。
PrintStream打印的所有字符都使用平台的默认字符编码转换为字节。在需要写入字符而不是写入字节的情况下,应该使用PrintWriter类。
1)构造方法摘要
PrintStream(File file):创建具有指定文件且不带自动行刷新的新打印流。
PrintStream(File file, String csn) : 创建具有指定文件名称和字符集且不带自动行刷新的新打印流。
PrintStream(OutputStream out):创建新的打印流。
PrintStream(OutputStream out, boolean autoFlush):创建新的打印流。
PrintStream(OutputStream out, boolean autoFlush, String encoding):创建新的打印流。
PrintStream(String fileName):创建具有指定文件名称且不带自动行刷新的新打印流。
PrintStream(String fileName, String csn):创建具有指定文件名称和字符集且不带自动行刷新的新打印流。
2)方法摘要
PrintStream append(char c):将指定字符添加到此输出流。
PrintStream append(CharSequence csq):将指定字符序列添加到此输出流。
PrintStream append(CharSequence csq, int start, int end):将指定字符序列的子序列添加到此输出流。
boolean checkError():刷新流并检查其错误状态。
protected void clearError():清除此流的内部错误状态。
void close():关闭流。
void flush():刷新该流的缓冲。
PrintStream format(Locale l, String format, Object... args):使用指定格式字符串和参数将格式化字符串写入此输出流中。
PrintStream format(String format, Object... args):使用指定格式字符串和参数将格式化字符串写入此输出流中。
void print(boolean b):打印 boolean 值。
void print(char c):打印字符。
void print(char[] s):打印字符数组。
void print(double d):打印双精度浮点数。
void print(float f):打印浮点数。
void print(int i):打印整数。
void print(long l):打印 long 整数。
void print(Object obj):打印对象。
void print(String s):打印字符串。
PrintStream printf(Locale l, String format, Object... args):使用指定格式字符串和参数将格式化的字符串写入此输出流的便捷方法。
PrintStream printf(String format, Object... args):使用指定格式字符串和参数将格式化的字符串写入此输出流的便捷方法。
void println():通过写入行分隔符字符串终止当前行。
void println(boolean x):打印 boolean 值,然后终止行。
void println(char x):打印字符,然后终止该行。
void println(char[] x):打印字符数组,然后终止该行。
void println(double x):打印 double,然后终止该行。
void println(float x):打印 float,然后终止该行。
void println(int x):打印整数,然后终止该行。
void println(long x):打印 long,然后终止该行。
void println(Object x):打印 Object,然后终止该行。
void println(String x):打印 String,然后终止该行。
protected void setError():将该流的错误状态设置为 true。
void write(byte[] buf, int off, int len):将 len 字节从指定的初始偏移量为 off 的 byte 数组写入此流。
void write(int b):将指定的字节写入此流
PrintStream和DataOutputStream异同点
相同点:都是继承与FileOutputStream,用于包装其它输出流。
不同点:
(01) PrintStream和DataOutputStream 都可以将数据格式化输出;但它们在“输出字符串”时的编码不同。
PrintStream是输出时采用的是用户指定的编码(创建PrintStream时指定的),若没有指定,则采用系统默认的字符编码。而DataOutputStream则采用的是UTF-8。 关于UTF-8的字符编码可以参考“字符编码(ASCII,Unicode和UTF-8) 和 大小端”
(02) 它们的写入数据时的异常处理机制不同。
DataOutputStream在通过write()向“输出流”中写入数据时,若产生IOException,会抛出。而PrintStream在通过write()向“输出流”中写入数据时,若产生IOException,则会在write()中进行捕获处理;并设置trouble标记(用于表示产生了异常)为true。用户可以通过checkError()返回trouble值,从而检查输出流中是否产生了异常。
(03) 构造函数不同
DataOutputStream的构造函数只有一个:DataOutputStream(OutputStream out)。即它只支持以输出流out作为“DataOutputStream的输出流”。而PrintStream的构造函数有许多:和DataOutputStream一样,支持以输出流out作为“PrintStream输出流”的构造函数;还支持以“File对象”或者“String类型的文件名对象”的构造函数。而且,在PrintStream的构造函数中,能“指定字符集”和“是否支持自动flush()操作”。
04) 目的不同
DataOutputStream的作用是装饰其它的输出流,它和DataInputStream配合使用:允许应用程序以与机器无关的方式从底层输入流中读写java数据类型。而PrintStream的作用虽然也是装饰其他输出流,但是它的目的不是以与机器无关的方式从底层读写java数据类型;而是为其它输出流提供打印各种数据值表示形式,使其它输出流能方便的通过print(), println()或printf()等输出各种格式的数据。
示例:
package test;
import java.io.PrintStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
/*
* PrintStream 的示例程序
*/
public class PrintStreamTest {
public static void main(String[] args) {
// 下面3个函数的作用都是一样:都是将字母“abcde”写入到文件“file.txt”中。
// 任选一个执行即可!
testPrintStreamConstrutor1() ;
//testPrintStreamConstrutor2() ;
//testPrintStreamConstrutor3() ;
// 测试write(), print(), println(), printf()等接口。
testPrintStreamAPIS() ;
}
/*
* PrintStream(OutputStream out) 的测试函数
* 函数的作用,就是将字母“abcde”写入到文件“file.txt”中
*/
private static void testPrintStreamConstrutor1() {
// 0x61对应ASCII码的字母'a',0x62对应ASCII码的字母'b', ...
final byte[] arr={0x61, 0x62, 0x63, 0x64, 0x65 }; // abced
try {
// 创建文件“file.txt”的File对象
File file = new File("file.txt");
// 创建文件对应FileOutputStream
PrintStream out = new PrintStream(new FileOutputStream(file));
// 将“字节数组arr”全部写入到输出流中
out.write(arr);
// 关闭输出流
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/*
* PrintStream(File file) 的测试函数
* 函数的作用,就是将字母“abcde”写入到文件“file.txt”中
*/
private static void testPrintStreamConstrutor2() {
final byte[] arr={0x61, 0x62, 0x63, 0x64, 0x65 };
try {
File file = new File("file.txt");
PrintStream out = new PrintStream(file);
out.write(arr);
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/*
* PrintStream(String fileName) 的测试函数
* 函数的作用,就是将字母“abcde”写入到文件“file.txt”中
*/
private static void testPrintStreamConstrutor3() {
final byte[] arr={0x61, 0x62, 0x63, 0x64, 0x65 };
try {
PrintStream out = new PrintStream("file.txt");
out.write(arr);
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/*
* 测试write(), print(), println(), printf()等接口。
*/
private static void testPrintStreamAPIS() {
// 0x61对应ASCII码的字母'a',0x62对应ASCII码的字母'b', ...
final byte[] arr={0x61, 0x62, 0x63, 0x64, 0x65 }; // abced
try {
// 创建文件对应FileOutputStream
PrintStream out = new PrintStream("other.txt");
// 将字符串“hello PrintStream”+回车符,写入到输出流中
out.println("hello PrintStream");
// 将0x41写入到输出流中
// 0x41对应ASCII码的字母'A',也就是写入字符'A'
out.write(0x41);
// 将字符串"65"写入到输出流中。
// out.print(0x41); 等价于 out.write(String.valueOf(0x41));
out.print(0x41);
// 将字符'B'追加到输出流中
out.append('B');
// 将"CDE is 5" + 回车 写入到输出流中
String str = "CDE";
int num = 5;
out.printf("%s is %d\n", str, num);
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
运行结果:
file.txt内容为:abcde
other.txt内容为:
hello PrintStream
A65BCDE is 5
PrintStream与PrintWriter区别:
①构造函数不同
PrintWriter类可接受Writer类型的参数,即:PrintWriter(Writer out[, boolean autoFlush])
②自动刷新方面
PrintStream在遇到换行符的时候就会自动刷新,即在调用了println()方法,或者文本中出现“\n”,就会自动flush
PrintWriter则不会,要在构造方法中设置自动刷新,或者手动flush。
③编码问题
PrintStream在输出字符,将字符转换为字节时采用的是系统默认的编码格式,这样当数据传输另一个平台,而另一个平台使用另外一个编码格式解码时就会出现问题,存在不可控因素。
而PrintWriter可以在传入Writer时 可由程序员指定字符转换为字节时的编码格式,这样兼容性和可控性会更好。
④用途不同
PrintStream主要操作字节流,也就是说打印出来所有字符按照平台的编码转换为字节,所以一般用于二进制文件。(文本文件也是可以的)
PrintWriter主要操作字符,可以创建指定的字符集,一般用来读取文本文件,因为默认用unicode编码,所以PrintWriter的跨平台性要比PrintStream好,也就是说PrintWriter可用于操作中文。
注意:
- PrintStream能做的PrintWriter也都能实现,并且PrintWriter的功能更为强大。但是由于PrintWriter出现的比较晚,较早的System.out、err使用的是PrintStream来实现的,所以为了兼容就没有废弃PrintStream。
- PrintStream与PrintWriter构建时会在内部new一个BufferedWriter,所有print方法都在内部调用这个Writer的write方法(write(String)或write(char[]))——对于print(char[]),直接调用write(char[]);对于其他的print方法,先用String.valueOf获得参数的字符串表示,然后调用write(String)。 但是,PrintStream是字节流,它有处理raw byte的方法,write(int)和write(byte[],int,int);PrintWriter是字符流,它没有处理raw byte的方法。
字符流
Reader和InputStream类似;Writer和OutputStream类似。
有两个需要注意的:
- InputStreamReader : 从输入流读取字节,在将它们转换成字符。
- BufferReader :接受Reader对象作为参数,并对其添加字符缓冲器,使用readline()方法可以读取一行。
如何选择I/O流
- 确定是输入还是输出
输入:输入流 InputStream Reader
输出:输出流 OutputStream Writer - 明确操作的数据对象是否是纯文本
是:字符流 Reader,Writer
否:字节流 InputStream,OutputStream - 明确具体的设备。
- 文件:
读:FileInputStream,, FileReader,
写:FileOutputStream,FileWriter - 数组:
byte[ ]:ByteArrayInputStream, ByteArrayOutputStream
char[ ]:CharArrayReader, CharArrayWriter - String:
StringBufferInputStream(已过时,因为其只能用于String的每个字符都是8位的字符串), StringReader, StringWriter - Socket流
键盘:用System.in(是一个InputStream对象)读取,用System.out(是一个OutoutStream对象)打印
- 文件:
- 是否需要转换流
是,就使用转换流,从Stream转化为Reader、Writer:InputStreamReader,OutputStreamWriter - 是否需要缓冲提高效率
是就加上Buffered:BufferedInputStream, BufferedOuputStream, BufferedReader, BufferedWriter - 是否需要格式化输出