Java拾遗系列- io知识
最近看tomcat源码,发现socket的输入输出流处理起来坑挺多的。莫名其妙的就阻塞了。最后决定回来先看看io知识。
- 简介
- io分类及相关实例
3.总结
1.简介
Java IO流对我们来说既熟悉有陌生,首先做Java没有不知道IO流的,但是在工作中经常用的场景不是很多。最常用的是上传图片,操作xlsx文件等功能,自己写Socket通信的很少。
Java程序与外界进行信息交互。都需要用到io流。如文件的操作,控制台的输入输出等,io流可以理解为数据的传输。数据在两设备间的传输称为流,流的本质是数据传输。
2.io分类及相关应用
2.1 io流的分类
io流的分类可以从三方面进行分类,
从流向分类:输入流 输出流
从单位分类:字节流 字符流
从功能分类:节点流 处理流
JavaIO流的详细划分.jpg
在Java这么多IO流中,真正操作资源的只有FileInputStream,FileOutputStream两个流,FileReader,FileWriter是基于FileInputStream,FileOutputStream实现的。而其他的流都是基于内存实现的。像ByteArray,CharArray,Piped下面会详细介绍
2.2 File**流
/**
* FileInputStream FileOutputStream 文件字节输入输出流
* 查看源码可知
* FileInputStream FileOutputStream 的close() 实现了管理资源方法
* FileOutputStream 的flush()方法为空。说明FileOutputStream 没有缓冲区
*
*/
public static void testFileInputStream() {
File file = new File("testFile.txt");
try {
//写入
FileOutputStream fileOutputStream = new FileOutputStream(file);
byte[] outbytes = "testFileInputStream 测试".getBytes("utf-8");
fileOutputStream.write(outbytes);
//fileOutputStream.flush();
fileOutputStream.close();
//读取
FileInputStream fileInputStream = new FileInputStream(file);
byte[] inbytes = new byte[(int) file.length()];
int len = fileInputStream.read(inbytes);
System.out.println(len);
System.out.println(new String(inbytes, "utf-8"));
fileInputStream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 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()方法。
*/
public static void testFileReader() {
File file = new File("testFile.txt");
try {
//写入
FileWriter fileWriter = new FileWriter(file);
fileWriter.write("testFileReader 测试");
fileWriter.flush();
fileWriter.close();
//读取
FileReader fileReader = new FileReader(file);
char[] chars = new char[(int) file.length()];
int len = fileReader.read(chars);
System.out.println(len);
System.out.println(new String(chars));
fileReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
2.3内存流
内存流,其实就是将数据存入到内存中,像
ByteArrayInputStream,ByteArrayOutputStream,
CharArrayReader,CharArrayWriter,StringReader,StringWriter
其实实现都是将数据已数组形式存入到内存中。
使用是注意不要将大的数据存入内存中。否则内存溢出。注意内存泄漏问题。
/**
* 实现类似内存虚拟文件的功能
* ByteArrayInputStream本身操作的是一个数组,并没有打开文件描述之类的,所有不需要关闭流
*/
public static void testByteArrayInputStream() {
try {
//写入
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(1024);
byteArrayOutputStream.write("testByteArrayInputStream 测试".getBytes("utf-8"));
//byteArrayOutputStream.flush();
//不用书写这个因为就没有调用底层资源,所以也就不用释放资源
byteArrayOutputStream.close();
//但使用ByteArrayInputStream的好处是关掉流之后它的数据仍然存在。
byte[] outbytes = byteArrayOutputStream.toByteArray();
//读取
byte[] inbytes = new byte[outbytes.length];
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(outbytes);
int len = byteArrayInputStream.read(inbytes);
System.out.println(len);
System.out.println(new String(inbytes, "utf-8"));
////不用书写这个因为就没有调用底层资源,所以也就不用释放资源
byteArrayInputStream.close();
//使用完毕设置为NULL 让GC回收
byteArrayOutputStream = null;
byteArrayInputStream = null;
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* CharArrayReader 本身操作的是char buf[];数组 并没有打开文件描述之类的,所有不需要关闭流
* 用法与ByteArrayInputStream相同
*/
public static void testCharArrayReader() {
try {
CharArrayWriter charArrayWriter = new CharArrayWriter();
charArrayWriter.write("testCharArrayReader 测试!");
char[] outchars = charArrayWriter.toCharArray();
CharArrayReader charArrayReader = new CharArrayReader(outchars);
char[] inchars = new char[1024];
charArrayReader.read(inchars);
System.out.println(new String(inchars));
charArrayWriter = null;
charArrayReader = null;
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 字符串流 与CharArray 相同 存入内存,不涉及到资源,close()方法 为空实现
* StringWriter 内部是基于StringBufffer实现的
*
* StringReader 内部是基于String 实现的
*/
public static void testStringReader(){
try {
StringWriter stringWriter = new StringWriter();
stringWriter.write("testStringReader 测试");
//空方法
stringWriter.flush();
//空方法
stringWriter.close();
//StringWriter 重写toString方法
StringReader stringReader = new StringReader(stringWriter.toString());
char[] chars = new char[200];
stringReader.read(chars);
System.out.println(new String(chars));
//close() 方法 实现str = null; reader 方法清除内部的str 没有内存泄漏问题
stringReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
2.4管道流
管道流其实也是操作内存中的数组实现的。具体可查看源码。多用于线程之间的通信。
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();
}
}
}
2.5缓冲流
/**
* 用了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();
}
}
2.6 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流有很多类,学习起来很费时间。但是抓住几个常用的类学习,看看源码,把源码理解清楚,思路还是挺清晰的。