一、概述
IO根据要操作对象的类型不同(操作的媒介不同),可以分为多种。Writer/Reader,InputStream/OutputStream为抽象类,定义了IO所通用的方法,包括write(),flush(),close()等。Writer/Reader对应为字符流,可以用来操作中文字符,InputStream/OutputStream为字节流,可以传输图片等。
IO类结构图:
图片来源于网络,侵删。
二、IO
·File媒介
File媒介IO包括FileWriter/FileReader,FileOutputStream/FileInputStream,是针对文件进行读写。
File类对象代表一个文件实例,File类实现Synchorizable接口和Comparable接口,提供方法来对文件进行操作。
在File源码中有一个abstarct pathname,有些费解,个人的理解是其实就是在new File(String pathname)时传递的参数,这样理解的原因是:构建一个file后,调用file.isAbsolute(),判断给出的abstract pathname是否为绝对路径,由这个方法的说明可以知道:传递的pathname参数就是abstract pathname。
-File类操作
public class FileTest {
public static void main(String[] args) {
// 对象file代表test.txt文件,""内为pathname
// File file = new File("H:\\workspace3\\io\\test.txt");
// 不把分割符写死,便于在win/unix上移植
File file = new File(
"H:" + File.separator + "workspace3" + File.separator + "io" + File.separator + "test.txt");
System.out.println("H:" + File.separator + "workspace3" + File.separator + "io" + File.separator + "test.txt");
try {
// 创建test.txt
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
// 文件是否存在
System.out.println(file.exists());
// 是否可读
System.out.println(file.canRead());
// 文件所在目录
System.out.println(file.getParent());
// 文件绝对路径,absolute name
System.out.println(file.getAbsolutePath());
// 文件所在目录,通俗理解,返回File()构造方法中的路径
System.out.println(file.getPath());
// 判断abstract pathname是否为绝对路径
System.out.println(file.isAbsolute());
// 列出同级文件,如果abstract pathname不是目录,返回null
file.list();
// 获取filesystem roots:C:\ D:\ E:\
file.listRoots();
// 创建目录
file.mkdir();
// 创建目录,包括不存在的父目录
file.mkdirs();
// 删除文件
// file.delete();
System.out.println(File.separator);
System.out.println(File.pathSeparator);
System.out.println(File.separatorChar);
}
}
-FileWriter/FileReader
public class FileRW {
public static void TestFileWriter(File file) throws IOException {
//如果文件不存在,创建之
if (!file.exists()) {
file.createNewFile();
}else {
FileWriter fw = new FileWriter(file,true);
System.out.println(fw.getEncoding());
fw.write("张柏芝\n"+"陈冠希\n"+"谢霆锋\n");
//
fw.close();
}
}
public static void TestFileReader(File file) throws IOException {
if (!file.exists()) {
file.createNewFile();
}
FileReader fr = new FileReader(file);
//读取单个字符,如果读完返回-1
//fr.read();
char[] content = new char[100];
fr.read(content);
System.out.println(new String(content));
fr.close();
}
public static void main(String[] args) {
File file = new File("H:\\workspace3\\io\\test2.txt");
//写入
try {
FileRW.TestFileWriter(file);
} catch (IOException e) {
e.printStackTrace();
}
//读取
try {
FileRW.TestFileReader(file);
} catch (IOException e) {
e.printStackTrace();
}
}
}
-FileOutputStream/FileInputStream
public class FileIO {
//FileInputStream,读出
public static void testFIS(File file) throws IOException {
FileInputStream fis = new FileInputStream(file);
// 字符数组,存放数据
byte[] content = new byte[1024];
// 如果文件存在,读取到字符数组中
if (file.exists()) {
fis.read(content);
}
System.out.println(new String(content));
//read()读取下一个byte,如果到达最后返回-1
fis.read();
//关闭流,释放资源
fis.close();
}
//FileOutputStream,写入
public static void testOPS(File file) throws IOException {
if (file.exists()) {
//boolean true,true从末尾写入
FileOutputStream fos = new FileOutputStream(file,true);
String content = "haha--hehe--heihei--xixi";
fos.write(content.getBytes());
//
//关闭流,释放资源
fos.close();
}
}
public static void main(String[] args) throws IOException {
// 获取文件
File file = new File("H:" + File.separator + "workspace3" + File.separator + "io" + File.separator + "test.txt");
//FileOutputStream,写入文件
FileIO.testOPS(file);
//FileInputStream,读取文件
FileIO.testFIS(file);
}
}
-随机访问文件
随机读写文件的方法是设定一个pointer,相当于一个指针,可以设定从pointer的位置开始读/写。
RandomAccessFile.write()可以向文件中随机写入文件,使用FileIO,或者是FileWR进行写入时,可以在构造方法中传入true参数,这样可以从文件末尾写入,不会覆盖文件原来内容。如果要在随机访问文件时,不覆盖原内容,可以设置pointer为文件长度,这样即可从末尾开始写入。
public class RandomAccess {
public static void randomAccessFile() throws IOException {
File file = new File("H:" + File.separator + "workspace3" + File.separator + "io" + File.separator + "test4.txt");
//创建文件,rw读写,r只读,rws,rwd
RandomAccessFile raf = new RandomAccessFile(file, "rw");
String str = "haha hehe heihei xixi 张柏芝 陈冠希 谢霆锋 1 2 3";
//写入,写入byte数组不会中文乱码,写入string出现乱码
raf.write(str.getBytes());
//文件长度
System.out.println("file-length::"+raf.length());
//设置pointer
raf.seek(10);
//获取pointer
System.out.println("point ::"+raf.getFilePointer());
byte[] content = new byte[1024];
//从pointer位置开始读取
raf.read(content);
System.out.println("content:::"+new String(content));
//读取文件后的pointer位置
System.out.println(raf.getFilePointer());
}
public static void main(String[] args) throws IOException {
RandomAccess.randomAccessFile();
}
}
·转换流
FileWriter/FileReader并非直接继承自Writer/Reader,而是继承自InputerStreamReader/OutputStreamWriter。
InputerStreamReader/OutputStreamWriter为转换流,可以将字节流转换为字符流。
在二者源码中的说明是:OutputStreamWriter is bridge from character stream to byte stream.(是从字符流到字节流的桥梁)
这里的类结构设计还是有些巧妙的:因为InputStreamReader继承了Reader,所以在InputStreamReader的构造方法中直接super(),就可调用Reader的构造方法,创建一个Reader出来,这样就完成了字节流到字符流的转换。这样的类关系也决定了只能由字节流转换为字符流。
//字节流转换为字符流
public class TransIO {
public static void transInput(File file) throws IOException {
InputStream fis = new FileInputStream(file);
//outputStream --> Writer:
Reader fr = new InputStreamReader(fis);
}
public static void transOutput(File file) throws IOException {
FileOutputStream fos = new FileOutputStream(file);
//outputStream --> Writer
Writer fw = new OutputStreamWriter(fos);
}
public static void main(String[] args) throws IOException {
File file = new File("d:/test.txt");
TransIO.transInput(file);
TransIO.transOutput(file);
}
}
·String媒介
提供对string的IO操作,感觉有点鸡肋。
-StringWriter/StringReader
public class StringRW {
public static void testStringWriter() {
//StringWriter对象sw存放内容
StringWriter sw = new StringWriter();
//写入
sw.write("hahaha");
sw.append("c");
//将sw中内容以StringBuffer输出
System.out.println(sw.getBuffer());
//将sw中内容以String输出
System.out.println(sw.toString());
}
public static void TestStringReader() throws IOException {
String str = "abcd";
StringReader sr = new StringReader(str);
char[] strValue = new char[100];
//将字符串内容读取到char[]
sr.read(strValue);
System.out.println(strValue[0]);
//将字符串内容读取到stringBuffer
//省略.....
}
public static void main(String[] args) {
StringRW.testStringWriter();
try {
StringRW.TestStringReader();
} catch (IOException e) {
e.printStackTrace();
}
}
}
·管道媒介
pipe管道是用于线程之间的读写。在线程之间进行读写时,需要建立input和output流的连接(connection),否则报异常:
java.io.IOException: Pipe not connected
注意:在PipedOutStream调用write()方法进行写入时,应当选择write(byte[] b)方法,其他方法可能会造成中文乱码问题。
public class PipedIO {
public static void main(String[] args) throws IOException {
PipedInputStream pis = new PipedInputStream();
PipedOutputStream pos = new PipedOutputStream();
//input/output管道建立连接
pis.connect(pos);
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
try {
pos.write("hello".getBytes());
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
pos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
int data;
try {
data = pis.read();
while (data != -1) {
System.out.print((char) data);
data = pis.read();
}
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
pis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
});
t1.start();
t2.start();
}
}
·Buffered流
在对流进行写入时提供一个buffer来提高IO效率。在进行磁盘或网络IO时,原始的InputStream对数据读取的过程都是一个字节一个字节操作的,而BufferedInputStream在其内部提供了一个buffer,在读数据时,会一次读取一大块数据到buffer中,这样比单字节的操作效率要高的多,特别是进程磁盘IO和对大量数据进行读写的时候。
不同媒介的流都可以通过Buffered流转换为使用Buffer的流。
另外BufferReader额外提供readline()方法读取一行。
//File字符流转换
BufferedReader bd = new BufferedReader(new FileReader(file));
bd.readline(); //读取一行,返回string
BufferedWriter bw = new BufferedWriter(new FileWriter(file));
//File字节流转换
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
BufferedOutputStream bos = new BufferedOutputStream (new FileOutputStream(file));
在close()之前调用flush()方法:
flush()在源码中的说明:
Flushes the stream. If the stream has saved any characters from the various write() methods in a buffer, write them immediately to their intended destination.
flush流,如果流已经通过多个write()保存了内容在buffer中,立即将内容写入到目标中
flush()意思是把缓冲区的内容强制的写出。主要用在IO中,即清空缓冲区数据,一般在读写流(stream)的时候,数据是先被读到了内存(buffer)中,再把数据写到文件中,当你数据读完的时候不代表你的数据已经写完了,因为还有一部分有可能会留在内存这个缓冲区中。这时候如果你调用了close()方法关闭了读写流,那么这部分数据就会丢失,所以应该在关闭读写流之前先flush()。
flush()也只有在buffered流中使用才有意义。