Java I/O类
1,概述
描述Java的I/O机制,我们可以通过一个生活中的例子来形容。
拿读文件举例,我们在程序中,需要读外部文件,这就像是从河里抽水,我们需要一个抽水机,然后把抽水机丢到河流里,所以第一步,建立抽水机和河流的链接:
FileInputStream fis = new FileInputStream("e:/yellow_river.txt");
架好设备后,就可以打开水龙头获得水啦,但获得的水需要存放,所以第二步,我们还需要先有一个盛水的容器:
byte [] bottle = new byte[10];
有了抽水机,有了盛水的容器,我们只需要将容器放到水龙头下,打开抽水机的水龙头,就可以得到水啦:
fis.read(bottle);
通过这三步,我们就从河里抽出了十个单位的水,并且放到了我们准备的瓶子里。之后瓶子里的水,也就是数据,就任由我们处置啦。
Java为我们提供了两种类型的抽水机,一种是XXXInputStream,它抽水的单位是字节,当然盛水的容器自然就是字节数组byte[]。
还有一种抽水机XXXReader,它抽水的单位是字符,盛水容器就是字符数组char[]。
经过这两种分类后,我们还根据抽水的源头不同再次分了类。如同抽水机根据不同的水源环境,做了特殊的处理。就像抽水机还可分为河用,湖用,海用。
XXX就表示数据的来源。如同FileInputStream,就是专门从文件中读取数据的;ByteArrayInputStream,就是专门从字符数组也就是内存中读数据的。
假设现在用户不满足只是从各种地方,按各种大小的杯子得到水。用户想,直接打开水龙头,就是热水,就是热咖啡!
显然抽水机已经力不从心了,如果把加热,煮咖啡的功能都加到抽水机中,抽水机会显得太庞杂,坏了也不好查找问题。
所以我们就在抽水机的水龙头下,接了一根加热管:
FileInputStreamfis = new FileInputStream("e:/yellow_river.txt");
ObjectInputStream ois= new ObjectInputStream(fis);
这样,只需要调用加热管的水龙头,就可以获得热水啦:
ois.readDouble();
当然我们这根管子是的功能不是加热,而是加工,将数据加工成对象,这样我们就可以直接从文件中获取对象啦。
所以,总的来说,在Java中读文件,就像抽水,首先,需要根据读取单位,根据读取源,选择合适的抽水机。如果需要,还可以在抽水机后添加管子,增强功能。
试着按照排水的例子,描述写文件的机制。
2,基本使用:
既然已经了解了Java的I/O系统,我们现在要熟悉它的基本使用。
1>按字节读文件 FileInputStream&FileOutputStream
FileInputStream fis = new FileInputStream("e:/in.txt");
while (fis.available() > 0) {
byte[] b = new byte[10];
int nResult = fis.read(b);
if (nResult == -1) break;
System.out.println(new String(b));
}
fis.close();
FileOutputStream(StringstrFilename)与FileOutputStream(Stringname,booleanappend) 的区别:当文件已经存在时,第一个会将原来的文件内容覆盖,第二个则可以选择覆盖或写到原来内容的后面。
2>按字符读文件 FileReader&FileWriter
FileReader rdFile = new FileReader("e:/in.txt");
while (rdFile.ready()) {
char[] chIn = new char[10];
int nResult = rdFile.read(chIn);
if (nResult == -1) break;
System.out.println(chIn);
}
rdFile.close();
3>带缓存的读写 BufferedReader&BufferedWriter
FileReader rdFile = new FileReader("e:/in.txt");
BufferedReader brdFile = new BufferedReader(rdFile);
FileWriter wrFile = new FileWriter("e:/out.txt");
BufferedWriter bwrFile = new BufferedWriter(wrFile);
String strLine;
while ((strLine = brdFile.readLine()) != null) {
bwrFile.write(strLine);
bwrFile.newLine();
}
brdFile.close();
bwrFile.close();
4>文件操作
String fileName="D:"+File.separator+"hello.txt";
File f=new File(fileName);
//创建一个新文件
f.createNewFile();
//创建一个文件夹
f.mkdir();
//删除一个文件
if(f.exists()){
f.delete();
}else{
System.out.println("文件不存在");
}
//列出指定目录的全部文件名
String[] str=f.list();
for (int i = 0; i < str.length; i++) {
System.out.println(str[i]);
}
//列出指定目录的全部文件路径
File[] files=f.listFiles();
for (int i = 0; i < files.length; i++) {
System.out.println(files[i]);
}
//判断一个指定的路径是否为目录
if(f.isDirectory()){
System.out.println("YES");
}else{
System.out.println("NO");
}
//搜索指定目录的全部内容
print(f);
public static void print(File f){
if(f!=null){
if(f.isDirectory()){
File[] fileArray=f.listFiles();
if(fileArray!=null){
for (int i = 0; i < fileArray.length; i++) {
print(fileArray[i]); //递归调用
}
}
}
}else{
System.out.println(f);
}
}
5>从控制台获取输入
Scanner cin=new Scanner(System.in);
System.out.println("请输入你的名字:");
String name=cin.nextLine();
System.out.println("你输入你的年龄");
int age=cin.nextInt();
System.out.println("你的名字是:"+name+" "+"你的年龄是:"+age);}
3,深入理解
既然我们已经了解了Java I/O 系统,并学会了一些简单的I/O操作,接下来请打开Java Doc,让我们看看Sun公司是怎样通过面向对象的思想,设计I/O系统的。
总的来说,InputStream和OutputStream是按字节读写数据的抽象类,Reader和Writer是按字符读写数据的抽象类:
1,继承它们的都是针对特定数据源的“抽水机”或“排水机”,提供了read和write的基本实现。构造器一般都需要提供数据源。
2,其中有一个子类叫FilterXXX,这就是“管子”的抽象类,它的继承类,覆盖了read和write方法,并提供了更强大更方便的方法。它的构造器一般都需要提供“抽水机”和“排水机”。
推回流 :可以预览
PushbackInputStream pbin = new PushbackInputStream(new BufferedInputStream(new FileInputStream("test.dat")));
int b = pbin.read();
if(b!='<')pbin.unread(b);
这种设计模式是“装饰器”,详细可参考Java IO中的设计模式
3,字节流与字符流的相互转换
OutputStreamWriter类将字符流,按指定的编码方式编码,转换成字节流。
InputStreamReader类将字节流,按指定的编码方式解码,转换成字符流。
而计算机文件中存储的,本质上都是字节,并且从文件中
InputStream方法
读数据 | int read() int read(byte[] b) int read(byte[] b, int off, int len) |
跳过 | long skip(long n) |
可读字节 | int available() |
关闭 | void close() |
标记 | void mark(int readlimit) void reset() boolean markSupported() |
OutputStream方法
写数据 | void write(int n) void write(byte[] b) void write(byte[] b, int off, int len) |
关闭 | void close() |
清空缓存 | void flush() |
阻塞
read和write方法都会阻塞,直到数据确实被读入或写出。
不可能阻塞的代码:
int bytesAvailable = in.available();
if( bytesAvailable > 0){
byte[] data = new byte[bytesAvailable];
in.read(data);
}
关闭
完成读写后,应该调用close方法关闭它, 释放操作系统资源。
4,Java NIO(New IO)
是从Java 1.4版本开始引入的一个新的IO API,可以替代标准的Java IO API。
Java NIO 由以下几个核心部分组成:
Channels
Buffers
Selectors
基本上,所有的 IO 在NIO 中都从一个Channel 开始。Channel 有点象流。 数据可以从Channel读到Buffer中,也可以从Buffer 写到Channel中。
Channel:
FileChannel:从文件中读写数据。
DatagramChannel:能通过UDP读写网络中的数据。
SocketChannel:能通过TCP读写网络中的数据。
ServerSocketChannel:可以监听新进来的TCP连接,像Web服务器那样。对每一个新进来的连接都会创建一个SocketChannel。
Buffer:capacity,position(0~capacity-1),limit(capacity,position)
ByteBuffer
CharBuffer
DoubleBuffer
FloatBuffer
IntBuffer
LongBuffer
ShortBuffer
Buffer的分配:
ByteBuffer buf = ByteBuffer.allocate(48);
向Buffer中写数据:
1>从Channel写:int bytesRead = inChannel.read(buf);
2>通过put方法写:buf.put(127);
写模式切换到读模式:flip()
从Buffer中读取数据:
1>读取数据到Channel:int bytesWritten = inChannel.write(buf);
2>使用get()方法:byte aByte = buf.get();
清空:clear(),compact()
标记:mark(),reset()