Java IO
Java IO即Java 输入输出系统。不管我们编写何种应用,都难免和各种输入输出相关的媒介打交道,其实和媒介进行IO的过程是十分复杂的,这要考虑的因素特别多,比如我们要考虑和哪种媒介进行IO(文件、控制台、网络),我们还要考虑具体和它们的通信方式(顺序、随机、二进制、按字符、按字、按行等等)。Java类库的设计者通过设计大量的类来攻克这些难题,这些类就位于java.io包中。
1.流
在Java IO中,流是一个核心的概念。流从概念上来说是一个连续的数据流。你既可以从流中读取数据,也可以往流中写数据。流与数据源或者数据流向的媒介相关联。在Java IO中流既可以是字节流(以字节为单位进行读写),也可以是字符流(以字符为单位进行读写)。
2.IO分类和用途
IO流的分类
IO流的分类可以从三个维度进行描述
流向:输入流和输出流
单位:字节流和字符流
功能:节点流和处理流
IO流的用途
文件访问
网络访问
内存缓存访问
管道
3.Java IO流的基本用法
3.1 文件流
FileInputStream/FileOutputStream代码
/**
* FileInputStream FileOutputStream 文件字节输入输出流
* 查看源码可知
* FileInputStream FileOutputStream 的close() 实现了管理资源方法
* FileOutputStream 的flush()方法为空。说明FileOutputStream 没有缓冲区
* Created by lyyz on 2018/5/24.
*/
public class FileByteEg {
/**
* 字节流形式读取文件内容
* @return
*/
public byte[] readFileWithByte(){
byte[] result = null;
try {
File file = new File("D:\\filetest.txt");
InputStream fileInputStream = new FileInputStream(file);
result = new byte[(int)file.length()];
fileInputStream.read(result);
fileInputStream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return result;
}
/**
* 字节流形式写入文件
* @param input
*/
public void writeFIleWithByte(String input){
try {
byte[] bytes = input.getBytes();
File file = new File("D:\\filetest.txt");
OutputStream fileOutputStream = new FileOutputStream(file);
fileOutputStream.write(bytes);
fileOutputStream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
String input = "读写文件 用字节流的形式 FileOutputStream/FileInputStream";
FileByteEg fileByteEg = new FileByteEg();
fileByteEg.writeFIleWithByte(input);
byte[] result = fileByteEg.readFileWithByte();
System.out.println(new String(result));
}
}
FileReader/FileWriter代码
/**
* FileReader FileWriter 文件字符输入输出流
* FileReader FileWriter 是通过StreamDecoder StreamEncoder 字符流字节流编码解码转换器实现的。
* 其实本质还是操作FileInputStream FileOutputStream 只不过是通过StreamDecoder StreamEncoder 在中间处理下数据。将字节变成字符
* 从源码可以看出
* FileReader 继承InputStreamReader 而 InputStreamReader 继承 Reader
* FileWriter 继承OutputStreamWriter 而 OutputStreamWriter 继承 Writer
* FileReader FileWriter 类没有实现read(),writer()等方法 只有构造函数
* 1,FileReader的构造函数是将传入的文件new FileInputStream() 并将文件字节流作为参数调用父类InputStreamReader的构造函数
* 2,InputStreamReader的构造函数 将文件字节流 传递给StreamDecoder.forInputStreamReader()方法构建 字节流解码转换器
*,3,而我们调用read()方法实际是调用的StreamDecoder.read()方法。
* Created by lyyz on 2018/5/24.
*/
public class FileStringEg {
/**
* 字符流形式写入文件
* @param input
*/
public void writeFileWithString(String input){
try {
File file = new File("D://filetest.txt");
Writer fileWriter = new FileWriter(file);
fileWriter.write(input);
fileWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 字节流形式读取文件
* @return
*/
public char[] readFileWithString(){
char[] result = null;
try {
File file = new File("D://filetest.txt");
Reader fileReader = new FileReader(file);
result = new char[(int)file.length()];
fileReader.read(result);
fileReader.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return result;
}
public static void main(String[] args) {
FileStringEg fileStringEg = new FileStringEg();
fileStringEg.writeFileWithString("读写文件 用字符流的形式 FileWriter/FileReader");
char[] result = fileStringEg.readFileWithString();
System.out.println(new String(result));
}
}
3.2内存流
内存流,其实就是将数据存入到内存中,像
ByteArrayInputStream,ByteArrayOutputStream,
CharArrayReader,CharArrayWriter,StringReader,StringWriter
其实实现都是将数据以数组形式存入到内存中。
使用是注意不要将大的数据存入内存中。否则内存溢出。注意内存泄漏问题。
ByteArrayInputStream/ByteArrayOutputStream代码
/**
* 实现类似内存虚拟文件的功能
* ByteArrayInputStream本身操作的是一个数组,并没有打开文件描述之类的,所有不需要关闭流
* ByteArrayInputStream 内部有一个字节数组存放写入内存中的数据(protected byte buf[];)
* 通过ByteArrayInputStream 构造方法传入数组的大小,默认为1024
* Created by lyyz on 2018/5/24.
*/
public class MemoryByteEg {
/**
* 写入数据到内存中 返回写入的ByteArrayOutputStream 等读取时候要从此对象中取数据
* @param input
* @return
*/
public ByteArrayOutputStream writeMemoryWithByte(byte[] input){
ByteArrayOutputStream result = null;
try {
//初始化buf[] 容量
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(1024);
result = byteArrayOutputStream;
byteArrayOutputStream.write(input);
//ByteArrayOutputStream 中close()方法为空
//不用写close()这个因为就没有调用底层资源,所以也就不用释放资源
//byteArrayOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
return result;
}
/**
* 从内存读取数据
* @param byteArrayOutputStream 写数据的 ByteArrayOutputStream
* @return
*/
public byte[] readMemoryWithByte(ByteArrayOutputStream byteArrayOutputStream ){
byte[] result = null;
try {
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
byteArrayInputStream.read(result=new byte[byteArrayOutputStream.size()]);
//ByteArrayInputStream 中close()方法为空
//不用写close()这个因为就没有调用底层资源,所以也就不用释放资源
//byteArrayInputStream.close();
//使用完毕设置为NULL 让GC回收
byteArrayOutputStream = null;
byteArrayInputStream = null;
} catch (IOException e) {
e.printStackTrace();
}
return result;
}
public static void main(String[] args) {
MemoryByteEg memoryByteEg = new MemoryByteEg();
String input = "读写内存数据 通过ByteArrayInputStream/ByteArrayOutputStream";
ByteArrayOutputStream byteArrayOutputStream = memoryByteEg.writeMemoryWithByte(input.getBytes());
byte[] result = memoryByteEg.readMemoryWithByte(byteArrayOutputStream);
System.out.println(new String(result));
}
}
CharArrayReader/CharArrayWriter代码
/**
* CharArrayReader 本身操作的是char buf[];数组 并没有打开文件描述之类的,所有不需要关闭流
* 用法与ByteArrayInputStream相同
* Created by lyyz on 2018/5/24.
*/
public class MemoryCharEg {
/**
* 写入数据到内存中 返回写入的CharArrayWriter 等读取时候要从此对象中取数据
* @param input
* @return
*/
public CharArrayWriter writeMemoryWithChar(char[] input){
CharArrayWriter result = null;
//初始化 protected char buf[]; 容量
try {
CharArrayWriter charArrayWriter = new CharArrayWriter(1024);
charArrayWriter.write(input);
result = charArrayWriter;
//CharArrayWriter 中close()方法为空
//不用写close()这个因为就没有调用底层资源,所以也就不用释放资源
//charArrayWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
return result;
}
/**
* 从内存读取数据
* @param charArrayWriter 写数据的 charArrayWriter 对象
* @return
*/
public char[] readMemoryWithChar(CharArrayWriter charArrayWriter){
char[] result = null;
try {
CharArrayReader charArrayReader = new CharArrayReader(charArrayWriter.toCharArray());
charArrayReader.read(result = new char[charArrayWriter.size()]);
//CharArrayReader 中close()方法为空
//不用写close()这个因为就没有调用底层资源,所以也就不用释放资源
//charArrayReader.close();
//使用完毕设置为NULL 让GC回收
charArrayReader = null;
charArrayWriter = null;
} catch (IOException e) {
e.printStackTrace();
}
return result;
}
public static void main(String[] args) {
MemoryCharEg memoryCharEg = new MemoryCharEg();
String input = "读写内存数据 通过CharArrayReader/CharArrayWriter";
CharArrayWriter charArrayWriter = memoryCharEg.writeMemoryWithChar(input.toCharArray());
char[] result = memoryCharEg.readMemoryWithChar(charArrayWriter);
System.out.println(new String(result));
}
}
StringReader/StringWriter代码
/**
* 字符串流 与CharArray 相同 存入内存,不涉及到资源,close()方法 为空实现
* private StringBuffer buf;
* StringWriter 内部是基于StringBufffer实现的
* Created by lyyz on 2018/5/24.
*/
public class MemoryStringEg {
/**
* 写入内存
* @param input
* @return
*/
public StringWriter writerMemoryWithString(String input){
StringWriter result = null;
// private StringBuffer buf; 初始化大小
StringWriter stringWriter = new StringWriter(1024);
stringWriter.write(input);
result = stringWriter;
return result;
}
/**
* 从内存中读取数据
* @param stringWriter
* @return
*/
public String readMemoryWithString(StringWriter stringWriter){
String result = null;
//内存StringWriter是通过StringBuffer实现的所以 直接从StringWriter中拿去StringBuffer就可以实现 读取数据
//result = stringWriter.getBuffer().toString();
// StringWriter 重写了toString()方法, 调用StringBufferd的toString()方法
//result = stringWriter.toString();
char[] chars = new char[stringWriter.toString().length()];
try {
StringReader stringReader = new StringReader(stringWriter.toString());
stringReader.read(chars);
result = new String(chars);
// 使用完毕设置为NULL 让GC回收
stringWriter = null;
//close() 方法 实现str = null; reader 方法清除内部的str 没有内存泄漏问题
stringReader.close();
} catch (IOException e) {
e.printStackTrace();
}
return result;
}
public static void main(String[] args) {
MemoryStringEg memoryStringEg = new MemoryStringEg();
String input = "读写内存数据 通过StringReader/StringWriter";
StringWriter stringWriter = memoryStringEg.writerMemoryWithString(input);
String result = memoryStringEg.readMemoryWithString(stringWriter);
System.out.println(result);
}
}
3.3管道流
管道流其实也是操作内存中的数组实现的。具体可查看源码。多用于线程之间的通信。
/**
* 管道流其实也是操作内存中的数组实现的。具体可查看源码。多用于线程之间的通信。
* Created by lyyz on 2018/5/24.
*/
public class PipedEg {
public static void main(String[] args) throws IOException {
//管道流可以实现两个线程之间,二进制数据的传输。
TestPipedInputStream testPipedInputStream = new TestPipedInputStream();
TestPipedOutputStream testPipedOutputStream = new TestPipedOutputStream();
//将管道连接
testPipedOutputStream.getPipedOutputStream().connect(testPipedInputStream.getPipedInputStream());
Thread th1 = new Thread(testPipedInputStream);
Thread th2 = new Thread(testPipedOutputStream);
th1.start();
th2.start();
}
}
class TestPipedInputStream implements Runnable {
private PipedInputStream pipedInputStream;
public TestPipedInputStream() {
pipedInputStream = new PipedInputStream();
}
public PipedInputStream getPipedInputStream() {
return pipedInputStream;
}
@Override
public void run() {
try {
byte[] bytes = new byte[1024];
int len = pipedInputStream.read(bytes);
System.out.println(len);
System.out.println(new String(bytes, "utf-8"));
pipedInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
class TestPipedOutputStream implements Runnable {
private PipedOutputStream pipedOutputStream;
public TestPipedOutputStream() {
pipedOutputStream = new PipedOutputStream();
}
public PipedOutputStream getPipedOutputStream() {
return pipedOutputStream;
}
@Override
public void run() {
try {
pipedOutputStream.write("testPipedOutputStream 管道测试".getBytes("utf-8"));
pipedOutputStream.flush();
pipedOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
3.4缓冲流
/**
* 用了BufferedInputStream后你每次读取都是从缓冲区里拷贝数据,在后你再读,缓冲区没东西了就调IO从数据源读到缓冲区,然后你再从缓冲区读。
* 如果你自己建立的数组大小和缓冲区大小一样,根本就没起到缓冲作用。
* 当你程序的数组小于缓冲区的大小的时候才会起到缓冲作用。比如是byte[] b=new byte[2048];,
* 你要读的数据是1G,那么你要调512次IO,假设一次1s,就512s,但如果用BufferedInputStream,
* 你每从缓冲区读取4(8192/2048=4)次才调用一次IO(假设访问内存一次0.1s),总共要128次
* ,就128s,加上总的从缓冲区拷贝数据的时间(512*0.1=51.2s),128+51.2=179.2。
* 这里用0.1s和1s来体现IO很耗时
*/
public static void testBufferedInputStream(){
try {
File file = new File("testfile.txt");
FileInputStream fileInputStream = new FileInputStream(file);
BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream);
//每次读取时1024 而默认缓冲区的大小是1024*8 缓冲起到作用
byte[] inbytes = new byte[1024];
System.out.println(file.length());
int count = 1;
while(true){
System.out.println("count====================================="+count);
int len=bufferedInputStream.read(inbytes);
System.out.println("len = ======================================================="+len);
System.out.println(new String(inbytes,"utf-8"));
++count;
if(len==-1)
break;
}
fileInputStream.close();
bufferedInputStream.close();
FileOutputStream fileOutputStream = new FileOutputStream(file);
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
bufferedOutputStream.write("testBufferedInputStream 测试".getBytes("utf-8"));
bufferedOutputStream.flush();
bufferedOutputStream.close();
fileOutputStream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
3.5 ObjectInputStream
ObjectInputStream,OjbectOutputStream 通常用做序列化反序列化应用。它是处理流。
public static void testObjectInputStream(){
try {
Cat cat = new Cat("12","波斯猫");
File file = new File("object.txt");
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(file));
objectOutputStream.writeObject(cat);
objectOutputStream.flush();
objectOutputStream.close();
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(file));
Object object= objectInputStream.readObject();
Cat inCat = (Cat)object;
System.out.println(inCat.getId()+"=="+inCat.getName());
objectInputStream.close();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
class Cat implements Serializable{
// 可序列化对象的版本
private static final long serialVersionUID = 1L;
private String id;
private String name;
public Cat(String id, String name) {
this.id = id;
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
3.总结
Java IO流有很多类,学习起来很费时间。但是抓住几个常用的类学习,看看源码,把源码理解清楚,思路还是挺清晰的。