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系统的。

Java I/O类

总的来说,InputStream和OutputStream是按字节读写数据的抽象类,Reader和Writer是按字符读写数据的抽象类:

1,继承它们的都是针对特定数据源的“抽水机”或“排水机”,提供了read和write的基本实现。构造器一般都需要提供数据源。

2,其中有一个子类叫FilterXXX,这就是“管子”的抽象类,它的继承类,覆盖了read和write方法,并提供了更强大更方便的方法。它的构造器一般都需要提供“抽水机”和“排水机”。

Java I/O类

推回流 :可以预览

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()