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,就是专门从字符数组也就是内存中读数据的。
假设现在用户不满足只是从各种地方,按各种大小的杯子得到水。用户想,直接打开水龙头,就是热水,就是热咖啡!
显然抽水机已经力不从心了,如果把加热,煮咖啡的功能都加到抽水机中,抽水机会显得太庞杂,坏了也不好查找问题。
所以我们就在抽水机的水龙头下,接了一根加热管:
FileInputStream fis = 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();
当文件已经存在时,第一个会将原来的文件内容覆盖,第二个则可以选择覆盖或写到原来内容的后面。
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是按字符读写数据的抽象类。
而继承它们的都是针对特定数据源的“抽水机”或“排水机”,提供了read和write的基本实现。构造器一般都需要提供数据源。
其中有一个子类叫FilterXXX,这就是“管子”的抽象类,它的继承类,覆盖了read和write方法,提供了更强大方便的方法。它的构造器一般都需要提供“抽水机”和“排水机”。
这种设计模式是“装饰器”。详细可参考Java IO中的设计模式
各个抽水机和排水管的简介:
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()