首先我们需要明确一点,那就是JAVA中我们常说的输入输出都是相对于内存来说的,写入内存就是我们常说的输入流,写出内存就是我们说的输出流.只有明确了这个概念我们说的输入流和输出流才是有意义的.
下面我们通过一张图来认识一下我们的输入流和输出流:
看了上面的图片,相信我们会对IO的分类有一个详细的认识:字节流和字符流,输入流还是有输出流.下面我们通过一些demo来认识一下这个流的使用吧
//演示InputStream(字节输入流)
public static void inputStreamTest() {
InputStream inputStream = null;
try {
inputStream = new FileInputStream("F://test/inputStream.txt");
byte bt[] = new byte[1024];
int hasCode = 0;
while((hasCode = inputStream.read(bt)) > 0) {
//防止中文乱码
System.out.println(new String(bt, 0, hasCode, "UTF-8"));
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException io) {
io.printStackTrace();
} finally{
try {
//关闭流
if(inputStream != null) inputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
//演示字节输出流
public static void outputStreamTest() {
InputStream inputStream = null;
OutputStream out = null;
try {
inputStream = new FileInputStream("F://test/inputStream.txt");
out = new FileOutputStream(new File("F://test/outputStream.txt"));
byte bt[] = new byte[1024];
int length = 0;
while((length = inputStream.read(bt)) > 0) {
//写入
out.write(bt, 0, length);
}
} catch (IOException io) {
io.printStackTrace();
} finally {
try {
//关闭流
if(out != null) out.close();
if(inputStream != null) inputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
//测试字符输入流
public static void testReader() {
Reader r = null;
try {
r = new FileReader(new File("F://test/reader.txt"));
char ch[] = new char[1024];
int length = 0;
while((length =r.read(ch) ) != -1) {
System.out.println("测试reader:" + new String(ch));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if(r != null) r.close();
} catch (Exception e2) {
e2.printStackTrace();
}
}
}
//测试输出流
public static void testWriter() {
Reader r = null;
Writer wr = null;
try {
r = new FileReader(new File("F://test/reader.txt"));
wr = new FileWriter(new File("F://test/writer.txt"));
int length = 0;
while((length = r.read()) != -1) {
wr.write(length);
}
} catch (Exception e) {
e.printStackTrace();
} finally{
try {
if(wr != null) wr.close();
} catch (Exception e2) {
e2.printStackTrace();
}
}
}
我将上面的方法都写成了静态,方便我的调用测试,有兴趣的同学可以写一些demo做一下自己的测试.
上面的流都是通过OS底层直接处理的,这样就会导致一个问题,每次写入和写出的时候都会耗费大量的资源,导致性能十分低下.因此我们能不能一次读取一行或者一部分文件呢?BufferedReader和BufferedWriter的出现便是为了解决这个问题.他们都是将写入或者写出的数据存入一个缓存中,只有当缓存慢的时候才会写入内存或者写出内存.下面为大家展示一些demo:
//测试缓存字节输入流
public static void testBufferedInputStream() {
InputStream input = null;
BufferedInputStream bufInputStream = null;
try {
input = new FileInputStream(new File("D://打包/test/bufferedInput.txt"));
bufInputStream = new BufferedInputStream(input);
byte bt[] = new byte[1024];
int len = 0;
StringBuilder sbBuilder = new StringBuilder();
while((len = bufInputStream.read(bt)) != -1) {
sbBuilder.append(new String(bt, 0, len, "UTF-8"));
}
System.out.println("输出层:::" + sbBuilder);
} catch (FileNotFoundException e) {
e.printStackTrace();
}catch (IOException e1) {
e1.printStackTrace();
} finally {
try {
if(bufInputStream != null) bufInputStream.close();
if(input != null) input.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
//测试缓存字节输出流
public static void testBufferedOutputStream() {
InputStream input = null;
BufferedInputStream bufInputStream = null;
OutputStream outputStream = null;
BufferedOutputStream buf = null;
try {
input = new FileInputStream(new File("D://打包/test/bufferedInput.txt"));
bufInputStream = new BufferedInputStream(input);
outputStream = new FileOutputStream(new File("D://打包/test/bufferedOutput.txt"));
buf = new BufferedOutputStream(outputStream);
byte bt[] = new byte[1024];
int len = 0;
while((len = bufInputStream.read(bt)) != -1) {
buf.write(bt, 0, len);
}
buf.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
}catch (IOException e1) {
e1.printStackTrace();
}finally{
try {
if(buf != null) buf.close();
if(bufInputStream != null) bufInputStream.close();
if(input != null) input.close();
if(outputStream != null) outputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
//测试缓存字符流输入输出
public static void testReaderAndWriter() {
Reader reader = null;
BufferedReader bufferedReader = null;
Writer writer = null;
BufferedWriter bufferedWriter = null;
try {
reader = new FileReader(new File("D://打包/test/bufferedReader.txt"));
bufferedReader = new BufferedReader(reader);
writer = new FileWriter(new File("D://打包/test/bufferedWriter.txt"));
bufferedWriter = new BufferedWriter(writer);
String line = null;
while((line = bufferedReader.readLine()) != null) {
bufferedWriter.write(line.toUpperCase());
bufferedWriter.newLine();
bufferedWriter.flush();
}
} catch (FileNotFoundException fn) {
fn.printStackTrace();
} catch (IOException ioe) {
ioe.printStackTrace();
} finally {
try {
//BufferedReader装饰一个 Reader使之具有缓冲功能,
//要关闭只需要调用最终被装饰出的对象的 close()方法即可,因为它最终会
//调用真正数据源对象的 close()方法。因此,可以只调用外层流的close方法关闭其装饰的内层流
if(bufferedWriter != null) bufferedWriter.close();
if(bufferedReader != null) bufferedReader.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
//文件的写入写出整合
public static void testReaderWriterWithUnicode() {
File file = new File("D://打包/test/测试.txt");
InputStreamReader reader = null;
BufferedReader bufferedReader = null;
OutputStreamWriter writer = null;
BufferedWriter bufferedWriter = null;
try {
if(!file.exists()) {
file.createNewFile();
}
reader = new InputStreamReader(new FileInputStream("D://海典文件整理/药店加公众号和第三方/微信秘钥/药店加公众号秘钥.txt"), "UTF-8");
bufferedReader = new BufferedReader(reader);
writer = new OutputStreamWriter(new FileOutputStream(file), "UTF-8");
bufferedWriter = new BufferedWriter(writer);
String line = null;
while((line = bufferedReader.readLine()) != null) {
bufferedWriter.write(line);
bufferedWriter.newLine();
}
bufferedWriter.flush();
} catch (IOException io) {
io.printStackTrace();
} finally {
try {
if(bufferedWriter != null) bufferedWriter.close();
if(bufferedReader != null) bufferedReader.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
//图片写入写出
public static void testImageIO() {
//FileImageInputStream fis = null;
//FileImageOutputStream fos = null;
FileInputStream fis = null;
FileOutputStream fos = null;
try {
//fis = new FileImageInputStream(new File("D://海典文件整理/美女.jpg"));
//fos = new FileImageOutputStream(new File("D://打包/test/美女.jpg"));
fis = new FileInputStream(new File("D://海典文件整理/美女.jpg"));
fos = new FileOutputStream(new File("D://打包/test/美女1.jpg"));
byte read[] = new byte[1024];
int len = 0;
while((len = fis.read(read)) != -1) {
fos.write(read, 0, len);
}
fos.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if(fos != null) fos.close();
if(fis != null) fis.close();
} catch (Exception e2) {
e2.printStackTrace();
}
}
}
注意事项:我们在写入和写出流的时候需要在使用完之后,将其关闭,不然使用他们是一个十分耗资源的事情.另外,写出流关闭的时候, 要确认其已经写出完毕,因此一般我们都会在关闭前调用flush()方法来确保.同时,在遇到换行的时候,调用newline()方法.
read和readline的区别:
read是通过判断其返回值是否为-1来确保是否读取完毕的,其是通过字节来来读取的,readline()表示读取的是一段,通常默认值是8192个字节大小,其内部是一个阻塞函数,当没有读取的时候不会返回null,只有发生异常或者另一端被关闭的时候,才会返回null.其在读取效率的时候不如定义一个大小为8192的数组,因为buffered内部是定义两个这样大小的数组.
IO是阻塞的流,NIO则是非阻塞的,本文只是对其进行简单的介绍,详情会在后续的关于socket博文中进行研究:
NIO主要有三大核心部分:Channel(通道),Buffer(缓冲区), Selector。传统IO基于字节流和字符流进行操作,而NIO基于Channel和Buffer(缓冲区)进行操作,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中。Selector(选择区)用于监听多个通道的事件(比如:连接打开,数据到达)。因此,单个线程可以监听多个数据通道。