`
fdb524se
  • 浏览: 11720 次
社区版块
存档分类
最新评论

Java IO 编程

 
阅读更多

Java IO 编程
2010年10月30日
   Java 的 IO 支持通过 java.io 包下的类和接口来支持,在 java.io 包下主要包括输入、输出两种 IO 流,每种输入、输出流又可分为字节流和字符流两大类。其中字节流以字节为单位来处理输入、输出操作,而字符流以字符来处理输入、输出操作。除此之外,Java 的 IO 流使用了一种装饰器设计模式,它将 IO 流分成底层节点流和上层处理流,其中节点流用于和底层物理存储节点直接关联――不同的物理节点获取节点流的方式可能存在一些差异,但程序可以把不同的的物理节点流包装成统一的处理流,从而允许程序用统一的输入、输出代码来读取不同物理存储节点的资源。
  一、File 类
  File 类是 java.io 包下代表与平台无关的文件和目录,也就是说如果希望在程序中操作文件和目录都可以通过 File 类来完成,值得指出的是不管是文件、还是目录都可以使用 File 来操作,File 能新建、删除和重命名文件和目录,File 不能访问文件内容本身。如果希望访问文件内容本身,则需要使用输入/输出流。
  1、访问文件和目录
  可以使用 绝对路径 和 相对路径
  访问文件名相关方法:
  String getName()String getPath()File getAbsoluteFile()String getAbsolutePath()String getParent()boolean renameTo(File newName) 文件检测相关方法:
  boolean exists()boolean canWrite()boolean canRead()boolean isFile()boolean isAbsolute() 获取常规文件信息
  long lastModified()long length() 文件操作的相关方法
  boolean createNewFile()boolean delete()static File createTempFile(String prefix, String suffix)static File createTempFile(String prefix, String suffix, File directory)void deleteOnExit() 目录操作的相关方法
  boolean mkdir()String[] list()File[] listFiles()static File[] listRoots() 示例:
  package code15_1;
  import java.io.File;
  public class FileTest {
  public static void main(String[] args) throws Exception {
  // 以当前路径来创建一个 File 对象
  File file = new File(".");
  // 直接获取文件名,输出一点
  System.out.println(file.getName());
  // 获取相对路径的父路径可能出错,下面代码输出 null
  System.out.println(file.getParent());
  // 获取绝对路径
  System.out.println(file.getAbsolutePath());
  // 获取上一级路径
  System.out.println(file.getAbsoluteFile().getParent());
  // 在当前路径下创建一个临时文件
  File tempFile = File.createTempFile("aaa", ".txt", file);
  // 指定当 JVM 退出时删除该文件
  tempFile.deleteOnExit();
  // 以系统当前时间作为文件名来创建文件
  File newFile = new File(System.currentTimeMillis() + "");
  System.out.println("newFile 对象是否存在:" + newFile.exists());
  // 以指定 newFile 对象来创建一个文件
  newFile.createNewFile();
  // 以 newFile 对象来创建一个目录,因为 newFile 对象已经存在,所以下面的方法返回 false,即无法创建该目录
  newFile.mkdir();
  // 使用 list 方法来列出当前路径下所有文件和路径
  String[] fileList = file.list();
  System.out.println("==============当前路径下所有文件和路径如下==============");
  for(String fileName:fileList)
  {
  System.out.println(fileName);
  }
  // listRoots 静态方法列出所有的磁盘根路径
  File[] roots = File.listRoots();
  for(File root:roots)
  {
  System.out.println(root);
  }
  }
  }
  2、文件过滤器
  在 File 的 list 方法中可以接受一个 FilenameFilter 参数,通过该参数可以只列出符合条件的文件。
  FilenameFilter 接口里包含了一个 accept(File dir, String name) 方法,该方法将依次对指定 File 的所有子目录、子文件夹进行迭代,如果该方法返回 true,这 list 方法会列出该子目录或者子文件夹。
  示例:
  package code15_1;
  import java.io.File;
  import java.io.FilenameFilter;
  public class FilenameFilterTest {
  /**
  * @param args
  */
  public static void main(String[] args) {
  File file = new File(".");
  String[] nameList = file.list(new MyFilenameFilter());
  for(String name : nameList)
  {
  System.out.println(name);
  }
  }
  }
  // 实现自己的 FilenameFilter 实现类
  class MyFilenameFilter implements FilenameFilter
  {
  @Override
  public boolean accept(File dir, String name) {
  // 如果文件名以 .java 结尾,或者文件对应一个路径,返回 true
  return name.endsWith(".java") || new File(name).isDirectory();
  }
  }
  二、理解 Java 的 IO 流
  1、流的分类
  输入流和输出流
  输入流:只能从中读取数据,而不能向其写出数据输出流:只能向其写出数据,而不能从中读取数据 Java 的输入流主要由 InputStream 和 Reader 作为基类,而输出流则主要由 OutputStream 和 Writer 作为基类。
  字节流和字符流
  字节流操作的最小数据单元是 8 位的字节字符流则主要由 Reader 和 Writer 作为基类 节点流和处理流
  可以从/向一个特定的 IO 设备(如磁盘、网络)读/写数据的流,称为节点流,节点流常常也被称为低级流(Low Level Stream)处理流则用于对一个已存在的流进行连接或封装,通过封装后流来实现数据读/写功能。处理流也被称为高级流或者包装流 2、流的概念模型
  Java 把所有设备里的有序数据抽象成流模型简化了输入/输出的处理,理解了流的概念模型也就了解了 Java IO
  Java 的 IO 流共涉及到 40 多个类,这 40 多个类都是从 4 个抽象基类派生出来的:
  InputStream/Reader:所有输入流的基类,前者是字节数入流,后者是字符输入流OutputStream/Writer:所有输出流的基类,前者是字节输出流,后者是字符输出流 三、字节流和字符流
  1、InputStream 和 Reader
  他们是所有输入流的基类, 他们两个都是抽象类.
  InputStream 里包含如下三个方法:
  int read(): 从输入流中读取单个字节,返回所读取字节数据(字节数据可直接转换成 int 类型)int read(byte[] b):从输入流中读取最多 b.length 个字节的数据,并将其存储在字节数组 b 中,返回实际读取的字节数int read(byte[] b, int off, int len):从输入流中读取最多 len 字节的数据,并将其存储在数组 b 中,放入 b 数组中时,并不是从数组起点开始,而是从 off 位置开始,返回实际读取的字节数。 在 Reader 里包含如下三个方法:
  int read(): 从输入流中读取单个字符,返回所读取的字符数据(字符数据可直接转换成 int 类型)int read(char[] cbuf): 从输入流中读取最多 cbuf.length 个字符的数据,并将其存储在字符数组 cbuf 中,返回实际读取的字符数int read(char[] cbuf, int off, int len): 从输入流中读取最多 len 个字符的数据,并将其存储在字符数组 cbuf 中,放入 cbuf 数组中时,并不是从数组的起点开始,而是从 off 位置开始,返回实际读取的字符数。 read(char[] cbuf) 或 read(byte[] b) 方法返回 -1 时,即表明到了输出流的结束点。
  正如前面提到,InputStream 和 Reader 都是抽象类,本身并不能创建实例,但它们分别有一个读取文件的输入流:FileInputStream 和 FileReader,它们都是节点流――会直接和指定文件关联。
  示例:
  public class FileInputStreamTest
  {
  public static void main(String[] args) throws IOException
  {
  // 创建字节数入流
  FileInputStream fis = new FileInputStream("FileInputStreamTest.java");
  // 创建一个长度为 1024 的“竹筒”
  byte[] bbuf = new byte[1024];
  // 用于保存实际读取的字节数
  int hasRead = 0;
  // 使用循环来重复 “取水” 过程
  while ((hasRead = fis.read(bbuf)) > 0)
  {
  // 取出 “竹筒” 中的水滴(字节),将字节数组转换成字符串输入
  System.out.print(new String(bbuf, 0, hasRead));
  }
  // 关闭文件输入流,放在 finally 块里更安全
  fis.close();
  }
  }
  FileReader 示例:
  public class FIleReaderTest
  {
  public static void main(String[] args) throws IOException
  {
  FileReader fr = null;
  try
  {
  // 创建字符输入流
  fr = new FileReader("FileReaderTest.java");
  // 创建一个长度为 32 的 “竹筒”
  char[] cbuf = new char[32];
  // 用于保存实际读取的字符数
  int hasRead = 0;
  // 使用循环来重复 “取水” 过程
  while ((hasRead = fr.read(cbuf)) > 0)
  {
  // 取出 “竹筒” 中的水滴(字节),将字符数组转换成字符串输入
  System.out.print(new String(cbuf, 0, hasRead));
  }
  }
  catch (IOException ioe)
  {
  ioe.printStackTrace();
  }
  finally
  {
  // 使用 finally 块来关闭文件输入流
  if (fr != null)
  {
  fr.close();
  }
  }
  }
  }
  除此之外,InputStream 和 Reader 还支持如下几个方法来移动记录指针:
  void mark(int readAheadLimit): 在记录指针当前位置记录一个标记 (mark)boolean markSupported(): 判断此输入流是否支持 mark() 操作, 即是否支持记录标记void reset():将此流的记录指针重新定位到上一次记录标记 (mark) 的位置long skip(long n): 记录指针向前移动 n 个字节/字符 2、OutputStream 和 Writer
  OutputStream 和 Writer 两个流都提供了如下三个方法:
  void write(int c): 将指定的字节/字符输出到输出流中, 其中 c 既可以代表字节,也可以代表字符.void write(byte[]/char[] buf): 将字节数组/字符数组中的数据输出到指定的输出流中void write(byte[]/char[] buf, int off, int len): 将字节数组/字符数组中从 off 位置开始,长度为 len 的字节/字符输出到输出流中 示例:
  public class FileOutputStreamTest
  {
  public static void main(String[] args) throws IOException
  {
  FileInputStream fis = null;
  FilrOutputStream fos = null;
  try
  {
  // 创建字节数入流
  fis = new FileInputStream("FileOutputStreamTest.java");
  // 创建字节输出流
  fos = new FileOutputStream("newFile.txt");
  byte[] bbuf = new byte[32];
  int hasRead = 0;
  // 循环从输入流中取出数据
  while ((hasRead = fis.read(bbuf)) > 0)
  {
  // 每读取一次,即写入文件输出流, 读了多少, 就写多少
  fos.write(bbuf, 0, hasRead);
  }
  }
  catch (IOException ioe)
  {
  ioe.printStackTrace();
  }
  finally
  {
  // 使用 finally 块来关闭文件输入流
  if (fis != null)
  {
  fis.close();
  }
  // 使用 finally 块来关闭文件输出流
  if (fos != null)
  {
  fos.close();
  }
  }
  }
  }
  如果希望直接输出字符串内容, 则使用 Writer 会有更好的效果, 示例:
  public class FileWriterTest
  {
  public static void main(String[] args) throws IOException
  {
  FileWriter fw = null;
  try
  {
  // 创建字符输出流
  fw = new FileWriter("poem.txt");
  fw.write("aaa\r\n");
  fw.write("bbb\r\n");
  }
  catch (IOException ioe)
  {
  ioe.printStackTrace();
  }
  finally
  {
  // 使用 finally 块来关闭文件输出流
  if (fw != null)
  {
  fw.close();
  }
  }
  }
  }
  四、输入/输出流体系
  1、处理流的用法
  使用处理流的典型思路是:使用处理流来包装节点流,程序通过处理流来执行输入/输出功能, 让节点流与底层的 I/O 设备、文件交互.
  识别处理流非常简单, 只要流的构造器参数不是一个物理节点, 而是一个已经存在的流, 那么这种流就一定是处理流; 而所有节点流都是直接以物理 IO 节点作为构造器参数的.
  示例: 使用 PrintStream 处理流来包装 OutputStream, 使用处理流后的输出流在输出时将更加方便.
  public class PrintStreamTest
  {
  public static void main(String[] args) throws IOException
  {
  PrintStream ps = null;
  try
  {
  // 创建一个接点输出流: FileOutpuptStream
  FileOutputStream fos = new FileOutputStream("test.txt");
  // 以 PrintStream 来包装 FileOutputStream 输出流
  ps = new PrintStream(fos);
  // 使用 PrintStream 来执行输出
  ps.println("普通字符串");
  // 直接使用 PrintStream 输出对象
  ps.println(new PrintStreamTest());
  }
  catch (IOException ioe)
  {
  ioe.printStackTrace(ps);
  }
  finally
  {
  ps.close();
  }
  }
  }
  2、输入/输出流体系
  Java 的输入输出流体系提供了近 40 个类
  输入/输出流体系分类
  字节输入流
  字节输出流
  字符输入流
  字符输出流
  抽象基类
  InputStream
  OutputStream
  Reader
  Writer
  访问文件
  FileInputStream
  FileOutputStream
  FileReader
  FileWriter
  访问数组
  ByteArrayInputStream
  ByteArrayOutputStream
  CharArrayReader
  CharArrayWriter
  访问管道
  PipedInputStream
  PipedOutputStream
  PipedReaderPipedWriter
  访问字符串
  StringReader
  StringWriter
  缓冲流
  BufferedInputStream
  BufferedOutputStream
  BufferedReader
  BufferedWriter
  转换流
  InputStreamReader
  OutputStreamWriter
  对象流
  ObjectInputStream
  ObjectOutputStream
  抽象基类
  FilterInputStream
  FilterOutputStream
  FilterReader
  FilterWriter
  打印流
  PrintStream
  PrintWriter
  退回输入流
  PushbackInputStream
  PushbackReader
  特殊流
  DataInputStream
  DataOutputStream
  上面表格中列出了一种以数组为物理节点的节点流, 字节流以字节数组为节点, 字符流以字符数组为节点; 与此类似的是, 字符流还可以使用字符串作为为物理节点, 用于实现从字符串读取内容, 或将内容写入字符串(实际上用 StringBuffer 充当了字符串)的功能.
  示例:
  public class StringNodeTest
  {
  public static void main(String[] args) throws Exception
  {
  String src = "SELECT * FROM USERS WHERE (1 = 1) \r\n"
  + "AND (username = ?) \r\n"
  + "AND (password = ?) \r\n"
  + "AND (address LIKE ?) \r\n";
  StringReader sr = new StringReader(src);
  char[] buffer = new char[32];
  int hasRead = 0;
  try
  {
  // 采用循环读取的访问读取字符串
  while ((hasRead = sr.read(buffer)) > 0)
  {
  System.out.print(new String(buffer, 0, hasRead));
  }
  }
  catch (IOException ioe)
  {
  ioe.printStackTrace();
  }
  finally
  {
  sr.close();
  }
  // 创建 StringWriter 时, 实际上以一个 StringBuffer 作为输出接点
  // 下面指定的 20 就是 StringBuffer 的初始长度
  StringWriter sw = new StringWriter(20);
  sw.write("SELECT * FROM EMP \r\n");
  sw.write("WHERE (1 = 1) \r\n");
  sw.write("AND (add LIKE ?) \r\n");
  System.out.println("------------- 下面是 sw 的字符串节点里的内容 -----------");
  // 使用 toString 方法返回 StringWriter 的字符串节点的内容
  System.out.println(sw.toString());
  }
  }
  3、转换流
  输入/输出流体系里还提供了 2 个转换流, 这两个转换流用于实现将字节流转换成字符流
  InputStreamReader 将字节输入流转换成字符输入流OutputStreamReader 将字节输出流转换成字符输出流示例: 下面是以获取键盘输入为例介绍转换流的用法, Java 使用 System.in 代表标准输入, 即键盘输入, 但这个标准输入流是 InputStream 类实例, 使用不方便, 而我们知道键盘输入内容都是文本内容, 所以我们可以使用 InputStreamReader 将其转化成字符输入流, 普通 Reader 读取输入内容时依然不方便, 我们可以将普通 Reader 再次包装成 BufferedReader, 利用 BufferedReader 的 readLine 方法可以一次读取一行的内容.
  public class KeyinTest
  {
  public static void main(String[] args)
  {
  BufferedReader br = null;
  try
  {
  // 将 System.in 对象转化成 Reader 对象
  InputStreamReader reader = new InputStreamReader(System.in);
  // 将普通 Reader 包装成 BufferedReader
  br = new BufferedReader(reader);
  String buffer = null;
  while ((buffer = br.readLine()) != null)
  {
  // 如果循环方式来一行一行的读取
  if (buffer.equals("exist"))
  {
  System.exit(1);
  }
  // 打印读取的内容
  System.out.println("输入内容为:" + buffer);
  }
  }
  catch (IOException ioe)
  {
  ioe.printStackTrace();
  }
  // 关闭输入流
  finally
  {
  try
  {
  br.close();
  }
  catch (IOException ioe)
  {
  ioe.printStackTrace();
  }
  }
  }
  }
  4、推回输入流
  在输入/输出流体系中, 有两个特殊的流与众不同, 就是 PushbackInputStream 和 PushbackReader, 它们分别提供了如下三个方法:
  void unread(byte[]/char[] buf): 将一个字节/字符数组内容推回到推回缓冲区里, 从而允许重复读取刚刚读取的内容void unread(byte[]/char[] b, int off, int len): 将一个字节/字符数组里从 off 开始, 长度为 len 字节/字符的内容推回到推回缓冲区里,从而允许重复读取刚刚读取的内容void unread(int b): 将一个字节/字符推回到推回缓冲区里, 从而允许重复读取刚刚读取的内容这三个方法实际上和三个 read 方法是一一对应的, 这两个推回输入流都带有一个推回缓冲区,当程序调用这两个推回输入流的 unread 方法时, 系统将会把指定数组的内容推回到该缓冲区里, 而退回输入流每次调用 read 方法时总是先从推回缓冲区读取, 只有当完全读取了推回缓冲区内的内容后, 而且还没有装满 read 所需的数组时才会从原输入流中读取. 当我们创建一个 PushbackInputStream 和 PushbackReader 时, 需要指定推回缓冲区的大小, 默认的推回缓冲区的长度为 1. 如果程序中推回到推回缓冲区的内容超出了推回缓冲区的大小, 程序将引发 Pushback buffer overflow 的 IOException.
  示例:
  import java.io.FileReader;
  import java.io.IOException;
  import java.io.PushbackReader;
  public class PushbackTest
  {
  public static void main(String[] args)
  {
  PushbackReader pr = null;
  try
  {
  // 创建一个 PushbackReader 对象, 指定推回缓冲区的长度为 64
  pr = new PushbackReader(new FileReader("src\\PushbackTest.java"), 64);
  char[] buf = new char[32];
  // 用以保存上次读取的字符串内容
  String lastContent = "";
  int hasRead = 0;
  // 循环读取文件内容
  while ((hasRead = pr.read(buf)) > 0)
  {
  // 将读取的内容转成字符串
  String content = new String(buf, 0, hasRead);
  int targetIndex = 0;
  // 将上次读取的字符串和本次读取的字符串拼起来, 查看是否包含目标字符串
  // 如果包含目标字符串
  if ((targetIndex = (lastContent + content).indexOf("new PushbackReader")) > 0)
  {
  // 将本次内容和上次内容一起推回缓冲区
  pr.unread((lastContent + content).toCharArray());
  // 再次读取指定长度的内容 (就是目标字符串之前的内容)
  char[] buf2 = new char[targetIndex];
  pr.read(buf2, 0, targetIndex);
  // 打印读取的内容
  System.out.print(new String(buf2, 0, buf2.length));
  System.exit(1);
  }
  else
  {
  // 打印上次读取的内容
  System.out.print(lastContent);
  // 将本次读取的内容设为上次读取的内容
  lastContent = content;
  }
  }
  }
  catch (IOException ioe)
  {
  ioe.printStackTrace();
  }
  finally
  {
  try
  {
  if (pr != null)
  {
  pr.close();
  }
  }
  catch (IOException ioe)
  {
  ioe.printStackTrace();
  }
  }
  }
  }
  五、重定向标准输入/输出
  Java 的标准输入/输出分别通过 System.in 和 System.out 来代表, 默认情况下它们分别代表键盘和显示器.
  在 System 类里提供了三个重定向标准输入/输出的方法:
  static void setErr(PrintStream err): 重定向 "标准" 错误输出流static void setIn(InputStream in): 重定向 "标准" 输入流static void setOut(PrintStream out): 重定向 "标准" 输出流示例: 下面程序通过重定向标准输出流, 将 System.out 的输出重定向向文件输出, 而不是在屏幕上输出
  public class RedirectOut
  {
  public static void main(String[] args)
  {
  PrintStream ps = null;
  try
  {
  // 一次性创建 PrintStream 输出流
  ps = new PrintStream(new FileOutputStream("out.txt"));
  // 将标准输出重定向到 ps 输出流
  System.setOut(ps);
  // 向标准输出输出一个字符串
  System.out.println("普通字符串");
  // 项标准输出输出一个对象
  System.out.println(new RedirectOut());
  }
  catch(IOException ioe)
  {
  ioe.printStackTrace();
  }
  finally
  {
  if (ps != null)
  {
  ps.close();
  }
  }
  }
  }
  示例: 下面程序重定向标准输入, 从而可以将 System.in 重定向到指定文件, 而不是键盘输入.
  import java.io.FileInputStream;
  import java.io.IOException;
  import java.util.Scanner;
  public class RedirectIn
  {
  public static void main(String[] args)
  {
  FileInputStream fis = null;
  try
  {
  fis = new FileInputStream("src/RedirectIn.java");
  // 将标准输入重定向到 fis 输入流
  System.setIn(fis);
  // 使用 System.in 创建 Scanner 对象, 用于获取标准输入
  Scanner sc = new Scanner(System.in);
  // 增加下面一行将只把回车作为分隔符
  sc.useDelimiter("\n");
  // 判断是否还有下一个输入项
  while (sc.hasNext())
  {
  // 输出输入项
  System.out.println(sc.next());
  }
  }
  catch (IOException ioe)
  {
  ioe.printStackTrace();
  }
  finally
  {
  if (fis != null)
  {
  try
  {
  fis.close();
  }
  catch (IOException ioe)
  {
  ioe.printStackTrace();
  }
  }
  }
  }
  }
  六、Java 虚拟机读写其他进程的数据
  Runtime 对象的 exec 方法可以运行平台上的其他程序, 该方法产生一个 Process 对象, Process 对象代表由该 Java 程序启动的子进程, Process 类提供了如下三个方法, 用于让程序和其子进程进行通信:
  InputStream getErrorStream(): 获取子进程的错误信息InputStream getInputStream(): 获取子进程的输入流OutputStream getOutputStream(): 获取子进程的输出流示例:
  public class ReadFromProcess
  {
  public static void main(String[] args)
  {
  BufferedReader br = null;
  try
  {
  // 运行 javac 命令, 返回运行该命令的子进程
  Process p = Runtime.getRuntime().exec("javac");
  // 以 p 进程的错误流创建 BufferedReader 对象
  // 这个错误流对本程序是输入流, 对 p 进程则是输出流
  br = new BufferedReader(new InputStreamReader(p.getErrorStream()));
  String buff = null;
  while ((buff = br.readLine()) != null)
  {
  System.out.println(buff);
  }
  }
  catch (IOException ioe)
  {
  ioe.printStackTrace();
  }
  finally
  {
  try
  {
  if (br != null)
  {
  br.close();
  }
  }
  catch (IOException ioe)
  {
  ioe.printStackTrace();
  }
  }
  }
  }
  九. Java 新 IO
  前面介绍 BufferedReader 时介绍到她的一个特性, 当 BufferedReader 读取输入流中的数据时, 如果没有读到有效数据时, 程序将在此处阻塞该线程的执行 (使用 InputStream 的 read 方法从流中读取数据时, 如果数据源中没有数据, 他也会阻塞线程), 也就是前面介绍的输入, 输出流都是阻塞式的输入, 输出. 不仅如此, 传统的输入, 输出流都是通过字节的移动来处理的(即使我们可因不直接去处理字节流, 但底层的实现还是依赖于字节处理), 也就是说面向流的输入/输出系统一次只能处理一个字节, 因此面向流的输入/输出系统通常效率不高.
  从 JDK 1.4 开始, Java 提供了一些列改进的输入/输出处理的新特性, 这些功能通常被称为新 I/O (New I/O), 这些类都被放在 java.nio 包及其子包下, 并且对原 java.io 包中的很多类似类以 NIO 为基础进行了改写, 新增了满足新 IO 的功能.
  1. Java 新 IO 概述
  新 IO 采用内存映射文件的方式来处理输出/输出, 新 IO 将文件或文件的一段区域映射到内存中, 这样就可以向访问内存一样访问文件了.
  Java 中 NIO 相关的包如下:
  java.nio 包: 主要提供了一些和 Buffer 相关的类java.nio.channels 包: 主要包括 Channel 和 Selector 相关的类java.nio.charset 包: 主要包含和字符集相关的类java.nio.channels.spi 包: 主要包含提供 Channel 服务的类java.nio.charset.spi 包: 主要包含提供字符集服务的相关类Channel (通道) 和 Buffer (缓冲) 是新 IO 中两个核心对象, Channel 是对传统输入/输出系统中的模拟, 在新 IO 系统中所有数据都需要通过通道传输; Channel 与传统的 InputStream, OutputStream 最大的区别在于它提供了一个 map 方法, 通过该 map 方法可以直接将 "一块数据" 映射到内存中, 如果说传统的输入/输出系统是面向流的处理, 而新 IO 则是面向块的处理.
  Buffer 可以被理解为一个容器, 它的本质是一个数组, 发送到 Channel 中的所有对象都必须先放到 Buffer 中, 而从 Channel 中读取的数据也必须先读到 Buffer 中.
  除了 Channel 和 Buffer 之外, 新 IO 还提供了用于将 UNICODE 字符串映射成字节序列以及逆映射操作的 Charset 类, 还提供了用于支持非阻塞式输入/输出 的 Selector 类.
  2、使用 Buffer
  从结构上看, Buffer 就像一个数组, 他可以保存多个类型相同的数据. Buffer 是一个抽象类, 其最常用的子类是 ByteBuffer, 他可以在底层字节数组上进行 get/set 操作, 除了 ByteBuffer 之外, 对应其他基本数据类型 (boolean 除外) 都有相应的 Buffer 类: ByteBuffer, CharBuffer, ShortBuffer, IntBuffer, LongBuffer, FloatBuffer, DoubleBuffer.
  上面这些 Buffer 类, 除了 ByteBuffer 之外, 他们都采用了相同或者类似的方法来管理数据, 只是各自管理的数据类型不同, 这些 Buffer 都没有提供构造器, 通过使用如下方法得到一个 Buffer 对象:
  static XxxBuffer allocate(int capacity): 创建一个容量为 capacity 的 XxxBuffer 对象其中 ByteBuffer 类还有一个子类: MappedByteBuffer, 它用于表示 Channel 将磁盘文件的部分或全部映射到内存中后得到的结果, 通常 MappedByteBuffer 对象由 Channel 的 map 方法返回.
  在 Buffer 中有三个重要的概念: 容量(capacity), 界限(limit) 和位置(position):
  容量 (capacity): 界限 (limit): 第一个不应该被读出或者写入的缓冲区位置索引, 也就是说, 位于 limit 后的数据既不可被读也不可被写位置 (position): 用于指明下一个可以被读出的或者写入的缓冲区位置索引(类似于 IO 流中的记录指针). 除此之外, Buffer 里还可以支持一个可选标记 (mark, 类似传统 IO 流中 mark), 该 mark 允许程序直接将 position 直接定位到该 mark 处, 这些只满足:
  0 读取或写入数据, 然后将位置 (position) 的值按处理元素的个数增加绝对 (Absolute): 直接根据索引来向 Buffer 中读取或写入数据, 使用绝对方式来访问 Buffer 里的数据时, 并不会影响位置 position 的值.示例:
  import java.nio.CharBuffer; public class BufferTest { public static void main(String[] args) { // 创建 Buffer CharBuffer buff = CharBuffer.allocate(8); System.out.println("capacity: " + buff.capacity()); System.out.println("limit: " + buff.limit()); System.out.println("position: " + buff.position()); // 放入元素 buff.put('a'); buff.put('b'); buff.put('c'); System.out.println("加入三个元素后, position = " + buff.position()); // 调用 flip() 方法 buff.flip(); System.out.println("执行 flip() 方法后, limit = " + buff.limit()); System.out.println("执行flip() 方法后, position = " + buff.position()); // 取出第一个元素 System.out.println("第一个元素 (position = 0): " + buff.get()); System.out.println("取出第一个元素后, position = " + buff.position()); // 调用 clear 方法 buff.clear(); System.out.println("执行 clear() 方法后, limit = " + buff.limit()); System.out.println("执行 clear() 方法后, position = " + buff.position()); System.out.println("执行 clear() 方法后, 缓冲区内容并没有被清除: " + buff.get(2)); System.out.println("执行绝对读取后, position = " + buff.position()); } }
  3、使用 Channel
  Channel 类似于传统的流对象, 但与传统的流不同的是, Channel 有两个主要的区别:
  Channel 可以直接将指定文件的部分或全部映射成 Buffer程序不能直接访问 Channel 中的数据, 包括读取, 写入都不行, Channel 只能与 Buffer 进行交互, 也就是说, 要从 Channel 中取得数据, 必须先用 Buffer 从 Channel 中取出一些数据, 然后让程序从 Buffer 中去除这些数据; 如果要将程序中的数据写入 Channel, 一样先让程序将数据放入 Buffer 中, 程序再将 Buffer 里的输入写入 Channel 中Channel 是一个接口, 位于 java.nio.channels 包下,系统为该接口提供了 DatagramChannel, FileChannel, Pipe.SinkChannel, Pipe.SourceChannel, SelectableChannel, ServerSocketChannel, SocketChannel 等实现类.
  所有的 Channel 都不应该通过构造起来直接创建, 而是通过传统的节点流 InputStream, OutputStream 的 getChannel 方法来返回对应的 Channel, 不同的节点流获得的 Channel 不一样, 例如 FileInputStream, FileOutputStream 的 getChannel 方法返回的是 FileChannel, 而 PipedInputStream 和 PipedOutputStream 的 getChannel 方法返回的是 Pipe.SinkChannel, Pipe.SourceChannel.
  Channel 常用的三类方法是: map, read 和 write, 其中 map 方法用于将 Channel 对应的部分或全部数据映射成 ByteBuffer; 而 read 或 write 方法都有一些冲在形式, 这些方法用于从 Buffer 中读取数据或向 Buffer 里写入数据.
  其 map 方法的方法签名为: MappedByteBuffer map(FileChannel.MapMode mode, long position, long size): 第一个参数执行映射式的模式, 分别有只读, 读写等模式, 第二三个参数用于控制将 Channel 的哪些数据映射成 ByteBuffer.
分享到:
评论

相关推荐

    Java IO 编程集合

    收集了Java IO 文件读写等操作的实例

    Java-Java IO编程教程

    Java IO编程教程 资源为视频教程资源 希望对你的 Java 学习有所帮助。

    java IO 编程实例

    import java.io.FileNotFoundException; import java.io.FileReader; public class TestFileReader { public static void main(String[] args) { int c = 0; FileReader freader = null; try { freader...

    java io案例

    io流案例 代码解析

    Java SE编程入门教程 java IO(共28页).pptx

    Java SE编程入门教程 java IO(共28页).pptx Java SE编程入门教程 java Math(共11页).pptx Java SE编程入门教程 java object(共9页).pptx Java SE编程入门教程 java static final(共24页).pptx Java SE编程...

    Java学习书目(各学习阶段都有推荐)

    《O’reilly-Java IO》- 推荐* (包含Java IO编程的各个方面) 《O’reilly-Database Programming with JDBC》- 推荐* (JDBC编程) 《O’reilly-Java Programming with Oracle JDBC》- 参考* 三、Java Web编程 ...

    javaIO流编程.pdf

    javaIO流编程.pdf

    Java SE编程入门教程 java网络编程(共29页).pptx

    Java SE编程入门教程 java IO(共28页).pptx Java SE编程入门教程 java Math(共11页).pptx Java SE编程入门教程 java object(共9页).pptx Java SE编程入门教程 java static final(共24页).pptx Java SE编程...

    第一行代码Java源代码第11章课程代码JavaIO编

    第一行代码Java源代码第11章【课程代码】JavaIO编程共20页.pdf.zip

    Java编程100个实例

    实用的Java编程实例,对于学习Java和面对Java考试有很大的作用,其中包括Java面向对象、Java IO编程、Java网络编程、Java线程、Java手机开发、用Java写自己的浏览器。。。。等等各种源码实例

    Java SE编程入门教程 javaGUI编程快速入门(1)(共82页).pptx

    Java SE编程入门教程 java IO(共28页).pptx Java SE编程入门教程 java Math(共11页).pptx Java SE编程入门教程 java object(共9页).pptx Java SE编程入门教程 java static final(共24页).pptx Java SE编程...

    Java SE编程入门教程 java泛型(共11页).pptx

    Java SE编程入门教程 java IO(共28页).pptx Java SE编程入门教程 java Math(共11页).pptx Java SE编程入门教程 java object(共9页).pptx Java SE编程入门教程 java static final(共24页).pptx Java SE编程...

    Java SE编程入门教程 java判断循环(共79页).ppt

    Java SE编程入门教程 java IO(共28页).pptx Java SE编程入门教程 java Math(共11页).pptx Java SE编程入门教程 java object(共9页).pptx Java SE编程入门教程 java static final(共24页).pptx Java SE编程...

    Java SE编程入门教程 java序列化(共14页).pptx

    Java SE编程入门教程 java IO(共28页).pptx Java SE编程入门教程 java Math(共11页).pptx Java SE编程入门教程 java object(共9页).pptx Java SE编程入门教程 java static final(共24页).pptx Java SE编程...

    java网络编程

    java网络编程包括socket tcp/udp io/nio讲解 http协议 jdbc rmi java的安全框架等知识

    JAVA程序设计:第14章 Java流式IO编程.ppt

    JAVA程序设计:第14章 Java流式IO编程.ppt

    java_io详解

    关于java io技术的详解:IO(Input/Output)是计算机输出/输出的接口。Java的核心库java.io提供了全面的IO接口,包括:文件读写,标准设备输出等等。Java中IO是以流为基础进行输入...块IO效率很高,但编程比较复杂。

    Java SE编程入门教程 java线程(共61页).pptx

    Java SE编程入门教程 java IO(共28页).pptx Java SE编程入门教程 java Math(共11页).pptx Java SE编程入门教程 java object(共9页).pptx Java SE编程入门教程 java static final(共24页).pptx Java SE编程...

    Java SE编程入门教程 java数组(共33页).pptx

    Java SE编程入门教程 java IO(共28页).pptx Java SE编程入门教程 java Math(共11页).pptx Java SE编程入门教程 java object(共9页).pptx Java SE编程入门教程 java static final(共24页).pptx Java SE编程...

    Java SE编程入门教程 java object(共9页).pptx

    Java SE编程入门教程 java IO(共28页).pptx Java SE编程入门教程 java Math(共11页).pptx Java SE编程入门教程 java object(共9页).pptx Java SE编程入门教程 java static final(共24页).pptx Java SE编程...

Global site tag (gtag.js) - Google Analytics