1. 写在前面的话

I/O ,I 是 Input (输入)的缩写,O是Output (输出) 的缩写,众所周知,人与人之间想要沟通交流,就需要讲彼此都能听懂的语言,比如大家都统一说英语。

人类如果想和计算机交流,也需要共同的语言,而计算机只懂二进制0101代码,然而人类发现很难理解和学懂这门语言,于是乎有了一个“翻译机“——高级编程语言,比如C#,Java 等,高级编程语言通过一种解释器的程序可以将人类容易学习的高级语言代码解释翻译成二进制代码,于是乎计算机便能听懂我们的命令,做我们想让它做的事情了。

同样,程序和程序之间也有交流方式,那就是I/O.

I/O 其实在某种意义上是一个应用程序与文件,网络连接,控制台 通信交流的一套API.

在Java.io包中最重要的就是5个类和一个接口。5个类指的是File、OutputStream、InputStream、Writer、Reader;一个接口指的是Serializable。掌握了这些就掌握了Java I/O的精髓了。

正如上面所提到,如果我们想对文件操作,可以使用File相关的类,来进行文件新建,复制,移动,遍历,删除等操作。

如果想对文件的内容进行操作,我们则可以考虑两种方式,一种是读取字节流的方式,一种是读取字符的方式。

也许你会困惑什么时候用哪一种方式会更好呢?

我的建议:

如果操作的文件内容中没有中文,那么一般用字节流就行(InputStream和OutputStream),当然用字符流(Reader 和Writer)也没啥问题。

但是如果操作的文件中有中文,那么就不得不用字符流来操作了,使用的时候要注意字符编码。

这是为什么呢?

因为一个中文文字至少占用2个字节,一个英文文字一般至少占据一个字节即可。

Java中I/O操作主要是指使用Java进行输入,输出操作. Java所有的I/O机制都是基于数据流进行输入输出,这些数据流表示了字符或者字节数据的流动序列。

  •  字节流:数据流中最小的数据单元是字节 
  •  字符流:数据流中最小的数据单元是字符, Java中的字符是Unicode编码,一个字符占用两个字节。

想要正确显示中文汉字,则至少需要两个字节的组合来读写,如果用字节流来读写,可能看到的就是乱码。

当不同数量的字节组合就产生了不通的字符编码。比如GB2312能完美的支持简体中文和英文,一个中文文字占用2个字节,一个英文字母同样占用2个字节。

关于字符编码的讨论细节,当年大一那时候偶然发现写过一篇有趣的博文,当执行下面这条命令,C语言打印出来是 “我爱你“”三个字

printf("%c%c%c%c%c%c\n",,,,,,);

显示打印结果:

有兴趣可以看下 C语言中两位ASCLL码可以表示汉字

JDK6 在线中文文档:http://tool.oschina.net/apidocs/apidoc?api=jdk-zh

JDK7: http://tool.oschina.net/apidocs/apidoc?api=jdk_7u4

2. Java 的 I/O 的基本分类

I/O 问题是任何编程语言都无法回避的问题,可以说 I/O 问题是整个人机交互的核心问题,因为 I/O 是机器获取和交换信息的主要渠道。

我们通过高级语言编写程序代码来进行I/O操作, 从而实现人机交互。

在Java.io包中最重要的就是5个类和一个接口。

5个类指的是File、OutputStream、InputStream、Writer、Reader;一个接口指的是Serializable。

掌握了这些就掌握了Java I/O的精髓了。

Java 的 I/O 操作类在包 java.io 下,大概有将近 80 个类,但是这些类大概可以分成四组,分别是:

  1. 基于字节操作的 I/O 接口:InputStream 和 OutputStream
  2. 基于字符操作的 I/O 接口:Writer 和 Reader
  3. 基于磁盘操作的 I/O 接口:File
  4. 基于网络操作的 I/O 接口:Socket

前两组主要是根据传输数据的数据格式,后两组主要是根据传输数据的方式,虽然 Socket 类并不在 java.io 包下,但是我仍然把它们划分在一起,因为我个人认为 I/O 的核心问题要么是数据格式影响 I/O 操作,要么是传输方式影响 I/O 操作,也就是将什么样的数据写到什么地方的问题,I/O 只是人与机器或者机器与机器交互的手段,除了在它们能够完成这个交互功能外,我们关注的就是如何提高它的运行效率了,而数据格式和传输方式是影响效率最关键的因素了。我们后面的分析也是基于这两个因素来展开的。————摘自IBM 学习文档

java.io包里有4个基本类:InputStream、OutputStream及Reader、Writer类,它们分别处理字节流和字符流。

其他各种各样的流都是由这4个派生出来的。

在JDK 1.4 之后引入了 NIO,NIO 相比传统的IO流做了改进,其中一个亮点就是引入了缓冲区的概念。

2.1 File 类

关于 File 这个对象我们需要注意的是,它不仅可以用来表示一个文件也可以表示一个文件夹。

File 类可以写一个实用的工具类。

现在假如我们需要查询某一个文件夹下所有的文件中是.java 类型的文件列表

那么我们需要两个参数,一个是查询文件夹路径名称,第二个参数是文件的类型

如果不递归的话,可以这样调用

List<File> fileList=SmartFileUtils.lookFollder("C:\\Users\\fairy\\Pictures\\国家地理馆",".jpg");//只查询图片文件 不递归
//List<File> fileList=SmartFileUtils.lookFollder("C:\\Users\\fairy\\Pictures\\国家地理馆");//查询所有文件夹和文件 不递归
for (File file : fileList) { System.out.println(file.getAbsolutePath()); }

递归查询当前文件夹包括子文件夹下所有的java 文件列表,我们可以这样调用:

public static void main(String[] args) {

        FileTreeInfo fileTreeInfo=SmartFileUtils.watchFolder(".",".java");//只查询java文件 递归
       //FileTreeInfo fileTreeInfo=SmartFileUtils.watchFolder(".");//查询所有文件夹和文件 递归
        System.out.println(fileTreeInfo.toString());
}

如果想单独获取查询结果的文件夹和文件集合列表,这样调用即可

List<File> fileList=fileTreeInfo.fileList;
List<File> folderList=fileTreeInfo.folderList;

SmartFileUtils.java

import java.io.File;
import java.io.FilenameFilter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Pattern; /**
* @author fairy 查询文件夹下的文件
***/
public class SmartFileUtils {
/**
* 查看某个文件夹下所有文件和文件夹列表
* 无递归
* */
public final static List<File> lookFollder(String startDir) {
return lookFollderInfo(new File(startDir), ".*");
}
/**
* 查看某个文件夹下指定类型的文件和文件夹列表
* 无递归
* */
public final static List<File> lookFollder(String startDir, String regex) {
return lookFollderInfo(new File(startDir), ".*\\" + regex);
}
private final static List<File> lookFollderInfo(File rootFolder, final String regex) {
return Arrays.asList(
rootFolder.listFiles(new FilenameFilter() {
private Pattern pattern = Pattern.compile(regex);
@Override
public boolean accept(File dir, String name) {
// TODO Auto-generated method stub
return pattern.matcher(name).matches();
}
}));
} /**
* 查看某个文件夹下所有的文件和文件夹列表
* 递归遍历
* */
public final static FileTreeInfo watchFolder(String startDir) {
return watchDirs(new File(startDir), ".*");
}
/**
* 查看某个文件夹下指定类型的文件和文件夹列表
* 递归遍历
* */
public final static FileTreeInfo watchFolder(String startDir, String regex) {
return watchDirs(new File(startDir), ".*" + regex);
}
private final static FileTreeInfo watchDirs(File startDir, String regex) {
FileTreeInfo resultInfo = new FileTreeInfo(); for (File item : startDir.listFiles()) {
if (item.isDirectory()) {
resultInfo.folderList.add(item);
resultInfo.addAll(watchDirs(item, regex));
} else {
if (item.getName().matches(regex)) {
resultInfo.fileList.add(item);
}
}
}
return resultInfo;
}
/**
* TreeInfo
***/
public static class FileTreeInfo implements Iterable<File> {
public List<File> fileList = new ArrayList<File>();
public List<File> folderList = new ArrayList<File>(); public Iterator<File> iterator() {
return fileList.iterator();
} void addAll(FileTreeInfo other) {
fileList.addAll(other.fileList);
folderList.addAll(other.folderList);
} public String toString() {
return "dirs:" + PPrint.pFormat(folderList) + "\n\nfiles:" + PPrint.pFormat(fileList);
}
}
}

上面代码中我们引用了一个格式化打印集合工具类:

import java.util.Collection;

public class PPrint {

      public static String pFormat(Collection<?> c) {
if(c.size()==0) return "[]";
StringBuilder result=new StringBuilder();
for (Object elem:c) {
if(c.size()!=1) {
result.append("\n ");
}
result.append(elem);
}
if(c.size()!=1) {
result.append("\n");
}
result.append("]");
return result.toString();
}
}

字节和字符之间的转化

另外数据持久化或网络传输都是以字节进行的,所以必须要有字符到字节或字节到字符的转化。

字节到字符需要转化,其中读的转化过程如下图所示:

字节流<----读----->字符流

InputStreamReader 类是字节到字符的转化桥梁,StreamDecoder 正是完成字节到字符的解码的实现类

InputStream 到 Reader 的过程要指定编码字符集,否则将采用操作系统默认字符集,很可能会出现乱码问题。

写入也是类似的过程如下图所示:

图 : 字节流<----写----->字符流

通过 OutputStreamWriter 类完成,字符到字节的编码过程,由 StreamEncoder 完成编码过程。

5. Java IO 流的基本方法

5.1 InputStream 的基本方法

5.2 OutputStream 的基本方法

5.3 Reader的基本方法

5.4 Writer 的基本方法

6. Java 常用流的使用

6.1 节点流类型

FileInputStreamTest.java

package com.xingyun.io;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URISyntaxException; public class FileInputStreamTest { public final static String getCurrentFilePath =new File("").getAbsolutePath()+"\\src\\com\\xingyun\\io\\"+FileInputStreamTest.class.getSimpleName()+".java";
//getCurrentFilePath: C:\Users\fairy\Documents\EclipseWorkspace\JavaSEBaseSample\src\com\xingyun\io\InputStreamTest.java public static void main(String[] args) throws IOException, URISyntaxException {
// TODO Auto-generated method
int b=0;//read byte count
FileInputStream fileInputStream=null;
try {
fileInputStream=new FileInputStream(getCurrentFilePath);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
System.out.println("File not found");
System.exit(-1);
}
try {
long num=0;
while((b=fileInputStream.read())!=-1) {
System.out.print((char)b);
num++;
}
System.out.println("--read over------");
System.out.println("read "+num+" byte");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
fileInputStream.close();
}
}
}

FileOutputStreamTest.java

package com.xingyun.io;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException; public class FileOutputStreamTest { public final static String getCurrentFilePath =new File("").getAbsolutePath()+"\\src\\com\\xingyun\\io\\"+FileOutputStreamTest.class.getSimpleName()+".java";
public final static String writeNewFilePath=new File("").getAbsolutePath()+"\\src\\com\\xingyun\\io\\"+FileOutputStreamTest.class.getSimpleName()+"New.txt"; public static void main(String[] args) {
// TODO Auto-generated method stub
int b=0;
FileInputStream fileInputStream=null;
FileOutputStream fileOutputStream=null; try {
fileInputStream=new FileInputStream(getCurrentFilePath);
fileOutputStream=new FileOutputStream(writeNewFilePath);
while ((b=fileInputStream.read())!=-1) {
fileOutputStream.write(b);
} } catch (FileNotFoundException e) {
// TODO Auto-generated catch block
System.out.println("can't find the file");
} catch (IOException e) {
// TODO Auto-generated catch block
System.out.println("read or write error");
}finally {
try {
System.out.println("read and write success");
fileInputStream.close();
fileOutputStream.close();
} catch (IOException e) {
// TODO Auto-generated catch block
System.out.println("close error");
System.exit(-1);
}
} } }

FileReaderTest.java

package com.xingyun.io;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException; public class FileReaderTest { private final static String getCurrentFilePath =new File("").getAbsolutePath()+"\\src\\com\\xingyun\\io\\"+FileReaderTest.class.getSimpleName()+".java"; public static void main(String[] args) {
// TODO Auto-generated method stub
FileReader fileReader=null;
int c=0;
try {
fileReader=new FileReader(getCurrentFilePath);
while((c=fileReader.read())!=-1) {
System.out.print((char)c);
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
try {
fileReader.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}

FileWriterTest.java

package com.xingyun.io;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException; public class FileWriterTest { public final static String getCurrentFilePath =new File("").getAbsolutePath()+"\\src\\com\\xingyun\\io\\"+FileWriterTest.class.getSimpleName()+".java";
public final static String writeNewFilePath=new File("").getAbsolutePath()+"\\src\\com\\xingyun\\io\\"+FileWriterTest.class.getSimpleName()+"New.txt";
public static void main(String[] args) {
// TODO Auto-generated method stub
int b=0;
FileReader fileReader=null;
FileWriter fileWriter=null; try {
fileReader=new FileReader(getCurrentFilePath);
fileWriter=new FileWriter(writeNewFilePath);
while ((b=fileReader.read())!=-1) {
fileWriter.write(b);
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
System.out.println("can't find the file");
} catch (IOException e) {
// TODO Auto-generated catch block
System.out.println("read or write error");
}finally {
try {
System.out.println("read and write success");
fileReader.close();
fileWriter.close();
} catch (IOException e) {
// TODO Auto-generated catch block
System.out.println("close error");
System.exit(-1);
}
} } }

6.2 处理流类型

6.2.1 缓冲流

BufferedInputStreamTest.java

package com.xingyun.io;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException; public class BufferedInputStreamTest { public final static String getCurrentFilePath =new File("").getAbsolutePath()+"\\src\\com\\xingyun\\io\\"+BufferedInputStreamTest.class.getSimpleName()+".java"; public static void main(String[] args) {
// TODO Auto-generated method stub
FileInputStream fileInputStream=null;
BufferedInputStream bufferedInputStream=null;
try {
fileInputStream=new FileInputStream(getCurrentFilePath);
bufferedInputStream=new BufferedInputStream(fileInputStream,100);
int c=0;
System.out.println(bufferedInputStream.read());
System.out.println(bufferedInputStream.read());
bufferedInputStream.mark(100);//jump to 100 location
for (int i = 0; i <10&&((c=bufferedInputStream.read())!=-1); i++) {
System.out.print((char)c+" ");
}
System.out.println("");
System.out.println("------------------");
bufferedInputStream.reset();//return to 100 location
for (int i = 0; i <10&&((c=bufferedInputStream.read())!=-1); i++) {
System.out.print((char)c+" ");
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
try { fileInputStream.close();
bufferedInputStream.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
} }

BufferedWriterAndBufferedReaderTest.java

package com.xingyun.io;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException; public class BufferedWriterAndBufferedReaderTest { public final static String getCurrentFilePath =new File("").getAbsolutePath()+"\\src\\com\\xingyun\\io\\"+BufferedWriterAndBufferedReaderTest.class.getSimpleName()+"New.txt"; public static void main(String[] args) {
// TODO Auto-generated method stub
BufferedWriter bufferedWriter=null;
BufferedReader bufferedReader=null;
try {
bufferedWriter=new BufferedWriter(new FileWriter(getCurrentFilePath),100);
bufferedReader=new BufferedReader(new FileReader(getCurrentFilePath),100);
String string=null;
for(int i=0;i<100;i++) {
string=String.valueOf(Math.random());
bufferedWriter.write(string);
bufferedWriter.newLine();
}
bufferedWriter.flush();
while ((string=bufferedReader.readLine())!=null) {
System.out.println(string);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
try {
bufferedWriter.close();
bufferedReader.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} } }

6.2.2 转换流

OutputStreamWriterTest.java

package com.xingyun.io;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader; public class InputStreamReaderTest { public static void main(String[] args) {
// TODO Auto-generated method stub
InputStreamReader inputStreamReader = new InputStreamReader(System.in); BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String string = null;
try {
string = bufferedReader.readLine();
while (string != null) {
if (string.equals("exit"))
break;
else {
System.out.println(string.toUpperCase());
string = bufferedReader.readLine();
} }
System.out.println(bufferedReader.readLine());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
try {
inputStreamReader.close();
bufferedReader.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
} }

InputStreamReaderTest.java

package com.xingyun.io;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader; public class InputStreamReaderTest { public static void main(String[] args) {
// TODO Auto-generated method stub
InputStreamReader inputStreamReader = new InputStreamReader(System.in);
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String string = null;
try {
string = bufferedReader.readLine();
while (string != null) {
if (string.equals("exit"))
break;
else {
System.out.println(string.toUpperCase());
string = bufferedReader.readLine();
} }
System.out.println(bufferedReader.readLine());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
try {
inputStreamReader.close();
bufferedReader.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
} }

6.2.3 打印流

PrintStreamTest.java

package com.xingyun.io;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream; public class PrintStreamTest { public final static String getCurrentFilePath = new File("").getAbsolutePath() + "\\src\\com\\xingyun\\io\\"
+ PrintStreamTest.class.getSimpleName() + "New.txt"; public static void main(String[] args) {
// TODO Auto-generated method stub
PrintStream printStream = null;
FileOutputStream fileOutputStream=null;
try {
fileOutputStream = new FileOutputStream(getCurrentFilePath);
printStream = new PrintStream(fileOutputStream);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} if (printStream != null) {
System.setOut(printStream);
}
//it will write into a file but not console screen
int line = 0;
for (char c = 0; c <= 60000; c++) {
System.out.print(c + "");
if (line++ >= 100) {
System.out.println();
line = 0;
}
}
//it will write into a file but not console screen
System.out.println("write into file success");
try {
fileOutputStream.close();
printStream.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

PrintStreamTest2.java

package com.xingyun.io;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintStream; public class PrintStreamTest2 { public static void main(String[] args) {
// TODO Auto-generated method stub
String filename = args[0];
//you must type the file path as args
//C:\Users\fairy\Documents\EclipseWorkspace\JavaSEBaseSample\src\com\xingyun\io\PrintStreamTest2.java
System.out.println("please type file path which one file you need to print");
if (filename != null) {
list(filename, System.out);
}
} public static void list(String f, PrintStream printStream) {
BufferedReader bufferedReader = null;
try {
bufferedReader = new BufferedReader(new FileReader(f));
String string = null;
while ((string = bufferedReader.readLine()) != null) {
printStream.println(string);
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
try {
bufferedReader.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
} }

PrintStreamTest3.java

package com.xingyun.io;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.Date; public class PrintStreamTest3 { public final static String getCurrentFilePath =new File("").getAbsolutePath()+"\\src\\com\\xingyun\\io\\"+PrintStreamTest3.class.getSimpleName()+"New.log"; public static void main(String[] args) {
// TODO Auto-generated method stub
String string=null;
System.out.println("Please type message:");
BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(System.in),100);
FileWriter fileWriter=null;
PrintWriter printWriter=null; try {
fileWriter=new FileWriter(getCurrentFilePath,true);
printWriter=new PrintWriter(fileWriter); while((string=bufferedReader.readLine())!=null) {
if(string.equals("exit"))break;
System.out.println(string.toUpperCase());
printWriter.println("-------------------");
printWriter.println(string.toUpperCase());
printWriter.flush();
}
printWriter.println("-------------"+new Date()+"--------");
printWriter.flush();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
printWriter.close();
}
}
}

6.2.4  Object 流

Tips:

transient 序列化时不处理这个关键字修饰的变量

serializable 使得自己的实体类支持序列化和反序列化

externalizable 接口,自己处理序列化和反序列化

ObjectOutputStreamTest.java

package com.xingyun.io;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable; public class ObjectOutputStreamTest { public final static String getCurrentFilePath =new File("").getAbsolutePath()+"\\src\\com\\xingyun\\io\\"+ObjectOutputStreamTest.class.getSimpleName()+"New.log"; public static void main(String[] args) throws IOException, ClassNotFoundException {
// TODO Auto-generated method stub
TestObject testObject=new TestObject();
testObject.k=8; //write
FileOutputStream fileOutputStream=new FileOutputStream(getCurrentFilePath);
ObjectOutputStream objectOutputStream=new ObjectOutputStream(fileOutputStream);
objectOutputStream.writeObject(testObject);
objectOutputStream.flush();
objectOutputStream.close();
fileOutputStream.close(); //read
FileInputStream fileInputStream=new FileInputStream(getCurrentFilePath);
ObjectInputStream objectInputStream=new ObjectInputStream(fileInputStream);
TestObject tReaderObject= (TestObject)objectInputStream.readObject();
System.out.println("------"+tReaderObject.i+"----------"+tReaderObject.j+"------"+tReaderObject.d+"---------"+tReaderObject.k);
objectInputStream.close();
fileInputStream.close(); } static class TestObject implements Serializable{
/**
*
*/
private static final long serialVersionUID = -5736467218763256621L;
int i=10;
int j=9;
double d=2.30;
// int k=18;
transient int k=18;//don't deal with the value
}
}

7. IO调优

下面就磁盘 I/O 和网络 I/O 的一些常用的优化技巧进行总结如下:

磁盘 I/O 优化

性能检测

我们的应用程序通常都需要访问磁盘读取数据,而磁盘 I/O 通常都很耗时,我们要判断 I/O 是否是一个瓶颈,我们有一些参数指标可以参考:

如我们可以压力测试应用程序看系统的 I/O wait 指标是否正常,例如测试机器有 4 个 CPU,那么理想的 I/O wait 参数不应该超过 25%,如果超过 25% 的话,I/O 很可能成为应用程序的性能瓶颈。Linux 操作系统下可以通过 iostat 命令查看。

通常我们在判断 I/O 性能时还会看另外一个参数就是 IOPS,我们应用程序需要最低的 IOPS 是多少,而我们的磁盘的 IOPS 能不能达到我们的要求。每个磁盘的 IOPS 通常是在一个范围内,这和存储在磁盘的数据块的大小和访问方式也有关。但是主要是由磁盘的转速决定的,磁盘的转速越高磁盘的 IOPS 也越高。

现在为了提高磁盘 I/O 的性能,通常采用一种叫 RAID 的技术,就是将不同的磁盘组合起来来提高 I/O 性能,目前有多种 RAID 技术,每种 RAID 技术对 I/O 性能提升会有不同,可以用一个 RAID 因子来代表,磁盘的读写吞吐量可以通过 iostat 命令来获取,于是我们可以计算出一个理论的 IOPS 值,计算公式如下所以:

( 磁盘数 * 每块磁盘的 IOPS)/( 磁盘读的吞吐量 +RAID 因子 * 磁盘写的吞吐量 )=IOPS

这个公式的详细信息请查阅参考资料 Understanding Disk I/O

提升 I/O 性能

提升磁盘 I/O 性能通常的方法有:

  1. 增加缓存,减少磁盘访问次数
  2. 优化磁盘的管理系统,设计最优的磁盘访问策略,以及磁盘的寻址策略,这里是在底层操作系统层面考虑的。
  3. 设计合理的磁盘存储数据块,以及访问这些数据块的策略,这里是在应用层面考虑的。如我们可以给存放的数据设计索引,通过寻址索引来加快和减少磁盘的访问,还有可以采用异步和非阻塞的方式加快磁盘的访问效率。
  4. 应用合理的 RAID 策略提升磁盘 IO,每种 RAID 的区别我们可以用下表所示:
表 2.RAID 策略
磁盘阵列 说明
RAID 0 数据被平均写到多个磁盘阵列中,写数据和读数据都是并行的,所以磁盘的 IOPS 可以提高一倍。
RAID 1 RAID 1 的主要作用是能够提高数据的安全性,它将一份数据分别复制到多个磁盘阵列中。并不能提升 IOPS 但是相同的数据有多个备份。通常用于对数据安全性较高的场合中。
RAID 5 这中设计方式是前两种的折中方式,它将数据平均写到所有磁盘阵列总数减一的磁盘中,往另外一个磁盘中写入这份数据的奇偶校验信息。如果其中一个磁盘损坏,可以通过其它磁盘的数据和这个数据的奇偶校验信息来恢复这份数据。
RAID 0+1 如名字一样,就是根据数据的备份情况进行分组,一份数据同时写到多个备份磁盘分组中,同时多个分组也会并行读写。

网络 I/O 优化

网络 I/O 优化通常有一些基本处理原则:

  1. 一个是减少网络交互的次数:要减少网络交互的次数通常我们在需要网络交互的两端会设置缓存,比如 Oracle 的 JDBC 驱动程序,就提供了对查询的 SQL 结果的缓存,在客户端和数据库端都有,可以有效的减少对数据库的访问。关于 Oracle JDBC 的内存管理可以参考《 Oracle JDBC 内存管理》。除了设置缓存还有一个办法是,合并访问请求:如在查询数据库时,我们要查 10 个 id,我可以每次查一个 id,也可以一次查 10 个 id。再比如在访问一个页面时通过会有多个 js 或 css 的文件,我们可以将多个 js 文件合并在一个 HTTP 链接中,每个文件用逗号隔开,然后发送到后端 Web 服务器根据这个 URL 链接,再拆分出各个文件,然后打包再一并发回给前端浏览器。这些都是常用的减少网络 I/O 的办法。
  2. 减少网络传输数据量的大小:减少网络数据量的办法通常是将数据压缩后再传输,如 HTTP 请求中,通常 Web 服务器将请求的 Web 页面 gzip 压缩后在传输给浏览器。还有就是通过设计简单的协议,尽量通过读取协议头来获取有用的价值信息。比如在代理程序设计时,有 4 层代理和 7 层代理都是来尽量避免要读取整个通信数据来取得需要的信息。
  3. 尽量减少编码:通常在网络 I/O 中数据传输都是以字节形式的,也就是通常要序列化。但是我们发送要传输的数据都是字符形式的,从字符到字节必须编码。但是这个编码过程是比较耗时的,所以在要经过网络 I/O 传输时,尽量直接以字节形式发送。也就是尽量提前将字符转化为字节,或者减少字符到字节的转化过程。
  4. 根据应用场景设计合适的交互方式:所谓的交互场景主要包括同步与异步阻塞与非阻塞方式,下面将详细介绍。

知识扩展

新的输入/输出 (NIO) 库是在 JDK 1.4 中引入的。NIO 弥补了原来的 I/O 的不足,它在标准 Java 代码中提供了高速的、面向块的 I/O。通过定义包含数据的类,以及通过以块的形式处理这些数据,NIO 不用使用本机代码就可以利用低级优化,这是原来的 I/O 包所无法做到的。

参考文章

Java I/O 总结   http://www.importnew.com/23708.html

深入分析 Java I/O 的工作机制 https://www.ibm.com/developerworks/cn/java/j-lo-javaio/

NIO入门 https://www.ibm.com/developerworks/cn/education/java/j-nio/j-nio.html

Java NIO与IO 对比 : http://ifeve.com/java-nio-vs-io/

Java IO 流总结篇的更多相关文章

  1. Java中的IO流 - 入门篇

    前言 大家好啊,我是汤圆,今天给大家带来的是<Java中的IO流-入门篇>,希望对大家有帮助,谢谢 由于Java的IO类有很多,这就导致我刚开始学的时候,感觉很乱,每次用到都是上网搜,结果 ...

  2. Java基础17:Java IO流总结

    更多内容请关注微信公众号[Java技术江湖] 这是一位阿里 Java 工程师的技术小站,作者黄小斜,专注 Java 相关技术:SSM.SpringBoot.MySQL.分布式.中间件.集群.Linux ...

  3. Java IO流学习总结四:缓冲流-BufferedReader、BufferedWriter

    在上一篇文章中Java IO流学习总结三:缓冲流-BufferedInputStream.BufferedOutputStream介绍了缓冲流中的字节流,而这一篇着重介绍缓冲流中字符流Buffered ...

  4. 《二》Java IO 流的分类介绍

    一.根据流向分为输入流和输出流: 注意输入流和输出流是相对于程序而言的. 输出:把程序(内存)中的内容输出到磁盘.光盘等存储设备中        输入:读取外部数据(磁盘.光盘等存储设备的数据)到程序 ...

  5. Java IO流详解(一)——简单介绍

    文件在程序中是以流的形式来传输的.所以用Java来传输文件就得使用到Java IO流. 1.流的概念和作用 流:代表任何有能力产出数据的数据源对象或者是有能力接受数据的接收端对象<Thinkin ...

  6. Java:IO流与文件基础

    Java:IO流与文件基础 说明: 本章内容将会持续更新,大家可以关注一下并给我提供建议,谢谢啦. 走进流 什么是流 流:从源到目的地的字节的有序序列. 在Java中,可以从其中读取一个字节序列的对象 ...

  7. java IO流详解

    流的概念和作用 学习Java IO,不得不提到的就是JavaIO流. 流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象.即数据在两设备间的传输称为流,流的本质是数据传输,根据数据传输 ...

  8. Java IO流学习总结

    Java流操作有关的类或接口: Java流类图结构: 流的概念和作用 流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象.即数据在两设备间的传输称为流,流的本质是数据传输,根据数据传输 ...

  9. 揭开Java IO流中的flush()的神秘面纱

    大家在使用Java IO流中OutputStream.PrintWriter --时,会经常用到它的flush()方法. 与在网络硬件中缓存一样,流还可以在软件中得到缓存,即直接在Java代码中缓存. ...

随机推荐

  1. Jenkins环境搭建(1)-下载与安装

    Jenkins简介 Jenkins是一个功能强大的应用程序,允许持续集成和持续交付项目,它是一个免费的源代码,可以处理任何类型的构建或持续集成.集成Jenkins可以用于一些测试和部署技术. Jenk ...

  2. Unity容器中AOP应用示例程序

    转发请注明出处:https://www.cnblogs.com/zhiyong-ITNote/p/9127001.html 实在没有找到Unity容器的AOP应用程序示例的说明,在微软官网找到了教程( ...

  3. Android自定义View前传-View的三大流程-Measure

    Android自定义View前传-View的三大流程-Measure 参考 <Android开发艺术探索> https://developer.android.google.cn/refe ...

  4. 机器学习系列-tensorflow-01-急切执行API

    tensorflow急切执行概述 Eager execution is an imperative, define-by-run interface where operations are exec ...

  5. linux下 玩转ptrace

    译者序:在开发Hust Online Judge的过程中,查阅了不少资料,关于调试器技术的资料在网上是很少,即便是UNIX编程巨著<UNIX环境高级编程>中,相关内容也不多,直到我在 ht ...

  6. 树形动态规划(树形DP)入门问题—初探 & 训练

    树形DP入门 poj 2342 Anniversary party   先来个题入门一下~ 题意: 某公司要举办一次晚会,但是为了使得晚会的气氛更加活跃,每个参加晚会的人都不希望在晚会中见到他的直接上 ...

  7. python 有参装饰器与迭代器

    1.有参装饰器 模板: def auth(x): def deco(func): def timmer(*args,**kwargs ): res = func(*args,**kwargs ) re ...

  8. 怎么样从多列的DataTable里取需要的几列

    方法一: 也是广为人知的一种: YourDataTable.Columns.Remove("列名"); 但是这种情况只适合于去掉很少列的情况. 如果有很多列我却只要一两列呢,那就得 ...

  9. Alpha冲刺(4/10)——2019.4.27

    所属课程 软件工程1916|W(福州大学) 作业要求 Alpha冲刺(4/10)--2019.4.27 团队名称 待就业六人组 1.团队信息 团队名称:待就业六人组 团队描述:同舟共济扬帆起,乘风破浪 ...

  10. JS_高程7.函数表达式(2)递归

    递归函数:一个函数通过名字调用自身的情况构成的.eg: //递归实现阶乘 function factorial(num){ if(num <= 1){ return 1; }else{ retu ...