- 浏览: 705976 次
- 性别:
- 来自: 北京
文章分类
- 全部博客 (272)
- Struts1.x (7)
- 事务 (2)
- Hibernate (11)
- 数据库 (14)
- JavaScript&Ajax (43)
- JSP&Servlet (2)
- Flex (1)
- 其它 (9)
- Java (22)
- 框架集成 (1)
- WebService (3)
- Tomcat (3)
- 加密和安全登录 (13)
- 基于原型的JavaScript (0)
- JavaDoc和Java编码规范 (3)
- CAS (1)
- 加密 (1)
- Axis2 (10)
- Ext2.x (3)
- SSH整合 (2)
- Ext (0)
- 正则表达式 (1)
- 设计模式 (4)
- 对象序列化技术 (3)
- CVS (2)
- Struts2 (6)
- Spring 2.x (7)
- Spring Security (2)
- Java 课程 (20)
- 程序员之死 (1)
- 软件测试 (6)
- UML (5)
- NetBeans (1)
- cxf (1)
- JMS (13)
- 设计 (5)
- ibatis2.x (3)
- Oracle (1)
- WebSphere (7)
- 概要设计 (1)
- DB2 (10)
- PowerDesigner (0)
- 软件工程 (5)
- rose (1)
- EA (1)
- LDAP (7)
- Portal&Portlet (3)
- MQ (10)
- ESB (4)
- EJB (2)
- JBoss (2)
最新评论
-
typeRos:
只有配置文件,没有代码么大神
Spring实现IBMMQ的JMS消息发布/订阅模式 -
panamera:
如果ActiveMQ服务器没有启动,这个时候消息生产者使用Jm ...
Spring JMSTemplate 与 JMS 原生API比较 -
lian819:
顶1楼, 引用文件, 配置属性, 太方便了
EXTJS 同步和异步请求 -
wilhard:
说得清楚明白
<%@ include file=""%>与<jsp:include page=""/>区别 -
刘琛颖:
总结的很好。受益了
javascript 父窗口(父页面)— 子窗口 (子页面)互相调用的方法
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 |
PipedReader |
PipedWriter |
访问字符串 |
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 <= mark <= position <= capacity
Buffer 的主要作用就是装入数据, 然后传输数据, 开始时 Buffer 的 position 为 0, limit 为 capacity, 程序调用
put 不断地向 Buffer 中放入数据(或者从 Channel 中获取一些数据), 每放入一些数据, Buffer 的 position 相应地向后
移动一些位置.
当 Buffer 装入数据结束后, 调用 Buffer 的 flip 方法, 该方法将 limit 设置为 position 所在的位置, 将 position 设置为 0,这样使得从 Buffer 中读数据时总是从 0 开始, 读完刚刚装入的所有数据即结束, 也就是说 Buffer 调用 flip 方法后, Buffer 为输出数据做好了准备. 当 Buffer 输出数据结束后, Buffer 调用 clear 方法, clear 方法不是清空 Buffer 的数据, 它仅仅将 position 置为 0 , 将 limit 置为 capacity, 这样为再次向 Buffer 重装如数据做好准备.
除此之外, Buffer 还包含如下一些常用的方法:
- int capacity()
- boolean hasRemaining(): 判断当前位置(position)和界限(limit)之间的元素是否可供处理
- int limit(): 返回 Buffer 的界限 (limit) 的位置
- Buffer limit(int newLt): 重新设置界限 (limit)的值, 并返回一个具有新的 limit 的缓冲区对象
- Buffer mark(): 设置 Buffer 的 mark 的位置, 他只能在 0 和 位置 (position) 之间做 mark
- int position(): 返回 Buffer 中的当前位置
- Buffer position(int newPs): 设置 Buffer 的新位置, 并返回一个具有改变 position 后的 Buffer 对象
- int remaining(): 返回当前位置和界限(limit)之间的元素个数
- Buffer reset(): 将位置(position)转到 mark 所在的位置
- Buffer rewind(): 将位置 (position) 设置为 0, 取消设置的 mark
除了这些移动 position, limit,mark 的方法之外, Buffer 的所有子类还提供了两类重要的方法: put 和 get 方法, 用于向 Buffer 中放入数据和从 Buffer 中取出数据. 当使用 put 和 get 方法来放入, 取出数据时, Buffer 既支持对单个数据的访问, 也支持对批量数据的访问 (以数组作为参数)
当使用 put 和 get 来访问 Buffer 中的数据时, 分为相对和绝对两种:
- 相对 (Relative): 从 Buffer 的当前位置读取或写入数据, 然后将位置 (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());
}
}
发表评论
-
java:comp/env 解释
2012-07-13 13:40 10840关于获取数据源的语法,大体有(javax.sql.D ... -
java.naming.factory.url.pkgs 的用途
2012-06-28 09:48 1760原文地址:http://blog.csdn.net/l ... -
解析 HTTP
2010-11-14 18:09 48053、HTTP 请求 客户端通过发送 HTTP 请求向服务器 ... -
Tomcat server.xml 文件
2010-11-07 17:21 1227Tomcat 服务器有一系列可配置的组件构成,这些组件 ... -
Tomcat的体系结构
2010-11-04 21:13 1483Tomcat的体系结构 Tomcat服务器是由一系列可配 ... -
第十七课时: 网络编程
2010-10-18 22:00 1153一. 网络编程的基础知 ... -
第十六课时: 多线程
2010-10-07 14:24 981一. 继承 Thread 类创建线程 通过继承 Thread ... -
第十四课时:输入/输出2
2010-09-05 15:54 1254... -
第十三课时:Annotation 注释
2010-08-29 15:55 1134从 JDK 5.0 开始,Java 增加了对元数据 ( ... -
第十二课时:JDBC 编程 (2)
2010-08-28 13:13 877示例: import java.sql.*; impor ... -
第十二课时:JDBC 编程 (1)
2010-08-08 15:52 2103一、SQL 语句基础 SQL 的全称是 Structured ... -
第11课时:异常处理
2010-07-25 16:51 1151一、异常处理机制 1、使用 try...catch 捕获异常 ... -
第十课时:与运行环境交互
2010-07-24 06:03 779一、与用户交互 1、使用 Scanner 获取键盘输入 获 ... -
第九课时:泛型
2010-07-11 17:00 1387一、定义泛型接口、类 JDK 1.5 改写了 ... -
第六课时:面向对象(5)—— 2010年05月22日
2010-07-04 13:45 990一、内部类 在某些情况下,我们把一个类放在另一个类的 ... -
第三课时:面向对象(2)
2010-05-02 23:20 1424一、成员变量与局部变量 二、类的继 ... -
第二课时:数组、面向对象(1)
2010-05-02 23:19 961一、数组 1、 ... -
第四课时:面向对象(3)
2010-05-02 23:17 903一、基本数据类型的包装类 1、 基本数据类型和包装 ... -
第一课时提纲:Java 基础(GC)
2010-03-22 23:22 1189一、Java 命名规范 1、对常量的命名规范: ...
相关推荐
这个名为“信息技术五年级.pptx”的文件显然是一份详细的教学计划,涵盖了从第一课时到第十课时的内容。以下是根据这些课时计划可能涵盖的一些关键知识点的详细说明: **第一课时:信息技术基础** 在这一课时,学生...
【知识点详解】 1. 细胞膜的流动性:细胞膜的流动性是指膜蛋白和脂质分子可以在膜上...以上是关于细胞物质输入和输出、跨膜运输方式的详细知识点解析,涵盖了细胞膜的特性、物质运输的方式以及影响因素等多个方面。
第四个选择题讨论了算法框图与集合论的结合,当输入x为特定值时,确定输出的集合,并求集合的交集。这需要理解算法的输出以及集合的概念。 5. 输出结果的计算 第五个选择题是关于算法执行后输出的结果,通常这类...
第十题考察了程序框图的综合应用,它寻找四个数中的最小值。通过一步步的比较,最终找出最小的那个数。 总之,程序框图与算法的基本逻辑结构是高中数学中理解和解决实际问题的关键工具。掌握顺序结构和条件结构,能...
1. 生物膜的流动镶嵌模型:生物膜的流动镶嵌模型是由磷脂双分子层作为基本骨架,并且蛋白质分子分布在磷脂双层的两侧,形成一种动态的结构。模型认为生物膜既不是静态的三层结构,也不是完全固定的,而是具有流动性...
* 第十四章:NumPy 模块 * 第十五章:pandas 模块 使用教材及主要参考书目: * 选用教材:自编 * 主要参考书目:SHAW Z A. 《笨办法学 Python 3》王巍巍, 译. 北京: 人民邮电出版社, 2018. 考核方式及成绩评定...
这篇文档是针对七年级学生的人教版数学教材第二章“整式的加减”的课时练习题及答案。这个章节主要涉及了代数基础知识,包括单项式、同类项、整式的加减运算等概念。 1. 单项式:单项式是指只包含一个变量或常数的...
5. **分段函数与算法设计**:第14题要求编写一个程序来计算级数的和,这涉及到如何设计合适的算法来处理分段序列的计算。第15题则要求用两种不同的循环结构(while和for)编写程序来计算平方和,这展示了如何根据...
- 第3-4周:学习数据类型和基本输入输出函数。 - 第5-10周:逐步深入到运算符、表达式、选择结构和循环结构。 - 第11-14周:多分支选择结构和循环结构的深化。 - 第15-20周:进行程序填空题和编程题的训练,强化...
《计算机组成原理(第四版)》是一本深入探讨计算机硬件系统结构的经典教材,其自测习题库为学习者提供了丰富的实践与理论结合的机会。在深入理解计算机组成原理的过程中,这些习题涵盖了一系列关键概念,包括数据...
- 比如,第一道选择题询问了给定分段函数在特定输入下的输出,第二题检验了映射的特性,第三题通过房间定价和入住率的关系探讨了优化收入的问题,第四题涉及求解函数值的问题,第五题讨论了超定量用水的费用模型,...
2. 函数的定义域和值域:函数的定义域是所有输入值的集合,值域是所有可能输出值的集合。题目中的例子展示了如何根据定义域和对应关系来确定函数的值域。 3. 函数的图像与直线的交点:函数y=f(x)的图像与直线x=a的...
1n←111(1+1.5%)n>15输出 nYn←n+1N 此算法程序为 M=11 N=1WHILE M(1+0.015)^n n=n+1 WEND PRINT n END 归纳点: 1.对含有循环结构的算法,在表示这个算法程序时用循环语句; 2.本书介绍了两种类型的循环...
12. **程序设计与结果预测**:第14题和第15题需要分析程序的执行过程,预测输出结果。 13. **概率计算**:第16题涉及到概率计算,需要理解概率与随机数生成的关系。 14. **频率分布直方图与统计计算**:第17题和第...
比如,图14-4中需要判断哪些机械的效率超过70%,这需要分析各个机械的输入功率和输出功率。 5. **压强与质量计算** - 压强是力除以接触面积的结果,例如在题目13中,要求计算手掌对地面的压强,需要用到公式p = F/...
- 第十题,根据程序框图的输出构建集合A,然后讨论函数y=xa在x∈[0,+∞)上的单调性,这涉及到函数的性质分析。 通过这些具体的例子,我们可以看到,算法与程序框图不仅是解决问题的工具,也是理解数学逻辑和提升...
第14题,函数318yx中自变量x的取值范围是x≠-2。 第15题,函数101yx 的y值随着x值增大而逐渐接近常数1。 总结来说,函数是描述两个量之间关系的重要工具,通过变量和常量的定义,我们可以建立数学模型来...
7. **分段函数的算法表示**:第十二题中,算法框图表示了一个分段函数,需要根据输入值t选择正确的函数分支进行计算,体现了算法在处理复杂逻辑时的作用。 8. **算法的输入与输出**:每个算法都有输入和输出,输入...
5. **输入与输出语句**:`input`用于获取用户输入,`print`或`disp`用于输出结果。如`x=input("x=")`获取用户输入的x值,`print (%io(2), S)`打印输出S的值。 6. **程序的流程控制**:理解程序的执行顺序至关重要,...
"处理框"("矩形框")用于表示对数据进行处理或计算的步骤,"判断框"("菱形框")用于执行条件判断,"输入/输出框"("平行四边形框")则用来接收输入或输出信息。 2. **顺序结构**:这是最基本的算法结构,按照从上...