码迷,mamicode.com
首页 > 编程语言 > 详细

Java IO——堵塞式

时间:2016-04-19 10:17:24      阅读:316      评论:0      收藏:0      [点我收藏+]

标签:

目录
 
  1.  File类
  2.  InputStream和OutputStream
  3.  Reader和Writer
  4.  RandomAccessFile
  5.  对象序列化
  6.  标准I/O
  7.  进程控制
 
内容
 
  1.  File类
 
File类给人的感觉就是文件类,其实对其恰当的诠释却是Filepath(文件路径),java.io.File类不仅可以代表一个特定的文件,还可以代表一个目录下面的一组文件名称。当File指向一个文件集时,就可以对此集合调用list()方法来返回一个字符串数组。
假设我们需要查看一个目录列表,如果仅仅是显示,不是带有条件的选择的话,直接调用list()即可,但多数我们需要的是有选择性的查看,比如想查看当前目录下面有多个个文件是以“.java”结尾的文件。这种情况我们就需要使用“目录过滤器”。
package com.wetostudio.demo1801;

import java.io.File;
import java.io.FilenameFilter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Pattern;

public class DirList {
public static void main(String[] args) {
     //用于显示当前的文件路径
     System.out.println(System.getProperty("user.dir"));

     File path = new File(".");
     String[] list;
     if(args.length == 0){
          list = path.list();
     }
     else{
     //回调
          list = path.list(new DirFilter(args[0]));
     }
     Arrays.sort(list,String.CASE_INSENSITIVE_ORDER);
     for(String dirItem:list){
          System.out.println(dirItem);
     }
     }
}
/**
 * DirFilter类存在的原因就是实现accept方法,将实现提供给list()调用,使list()可以回调accept(),这种结构通常称为回调。
 * 也是策略模式(有争议)的例子,list实现了基本的功能,而且按照FilenameFilter的形式提供了这个策略,以便完善list在提供服务时所需的算法。
 * 因为list接受FilenameFilter作为参数,任何实现FilenameFilter的类的对象都可以选择list的行为方式。策略模式提供了代码的灵活性。
 * 
 * File中list已经实现了显示当前路径的文件名  public String[] list(){...}  AND  public String[] list(FilenameFilter filter){...}
 * 但是显示文件列表的同时需要定制化的处理,这个时候就需要实现FilenameFilter接口
 * public String[] list(FilenameFilter filter) {
 *      String names[] = list();
 *      if ((names == null) || (filter == null)) {
 *          return names;
 *      }
 *      List<String> v = new ArrayList<>();
 *      for (int i = 0 ; i < names.length ; i++) {
 *          if (filter.accept(this, names[i])) {   //调用了FilenameFilter中的accept函数
 *              v.add(names[i]);
 *          }
 *      }
 *      return v.toArray(new String[v.size()]);
 *  }
 */

class DirFilter implements FilenameFilter{
     private Pattern pattern;
     public DirFilter(String regex){
          pattern = Pattern.compile(regex);
     }
     @Override
     public boolean accept(File dir, String name) {
          // TODO Auto-generated method stub
          //return false;
          return pattern.matcher(name).matches();
     }

}
测试参数自己选取,可以参照上面的正则表达式。
在这里并不是只了解java是怎样实现显示特定目录下显示特定文件的方法,而是学习java内部的实现机制。在这里《Thinking in Java》认为是策略模式,而有些书(比如《疯狂Java讲义》)则认为是Command模式。
番外篇1:策略模式 和 Command模式
 
 
  2.  InputStream和OutputStream
 
请看java.io.*包下面的关于InputStream和OutputStream的层次如下图1.1(画图真是浪费时间)。
技术分享
图1.1    InputStream和OutputStream层次图
流的概念代表任何有能力产出数据的数据源对象或者是有能力接收数据端对象。流的概念屏蔽了许多处理数据的细节,方便的编程的实现。
Java的IO分成输入和输出两部分。InputStream和Reader派生而来的类都包含read(),用于读取单个字节或者字节数组;OutputStream和Writer派生而来的类都包含write(),用于写单个字节或者字节数组。但是我们通常不会用到这些方法,这样说吧,他们之所以存在是因为别的类使用它们,以便提供更有用的接口。因此,我们很少使用单一的类来创建流对象,而是通过叠加多个对象来达到某种目的,从图1.1可以看出来FilterInputStream和FilterOutputStream类都是采用装饰器设计模式。
图1.1具体的类细节,查看JDK的API。
番外篇2:装饰器设计模式
装饰器的缺点:提供灵活性的同时,同时增加代码的复杂性。就像这里面分析的Java流,我们必须创建许多类——“核心”I/O类型加上所有的装饰器,才能得到我们所希望的单个I/O对象。
 
  3.  Reader和Writer
 
如果说InputStream和OutputStream是面向字节的,那么Reader和Writer就是提供兼容Unicode和面向字符的I/O功能。
就我们使用I/O操作的时候,大多数可以通过字节和字符方式进行。对于一些文本读写当然推荐用字符方式来处理,当然也可以通过适配器类:InputStreamReader和OutputStream分别可以把InputStream转换成Reader,OutputStream转换成Writer。
但有的时候必须要求使用字节方式进行流操作,比如java.util.zip就是面向字节的。
具体的Reader结构在这里面直接上截图,如下图3.1(图没有走到底,比如BufferedReader下还有个子类LineNumberReader) 。
技术分享
图3.1 Reader层次图
Writer的下图3.2所示。
技术分享
图3.2 Writer的层次图
 
  4.  RandomAccessFile
 
首先RandomAccessFile类是不属于以上I/O的继承,它自成一家,直接继承java.lang.Object。RandomAccessFile类操作的是字节。
RandomAccess用李刚的话解释“任意访问”,RandomAccessFile允许自由定位文件的记录指针( file pointer),可以不从文件开始的地方开始I/O操作。RandomAccessFile包含一个记录指针,标示当前I/O操作的位置,当程序创建一个RandomAccessFile对象的时候,该对象的文件记录指针位于文件头(0处),当读/写了n个字节后,文件的记录指针回向后移动n个字节。但是RandomAccessFile允许任意的移动记录指针。
—>  long getFilePointer():Returns the current offset in this file. offset:偏移
—>  void seek(long pos):Sets the file-pointer offset, measured from the beginning of this file, at which the next read or write occurs. The offset may be set beyond the end of the file. Setting the offset beyond the end of the file does not change the file length. The file length will change only by writing after the offset has been set beyond the end of the file.还是En解释的够全面.
介绍了这么多,告诉你个不幸的是RandomAccessFile的大部分功能被nio方式取代。
 
  5.  对象序列化
 
  6.  标准I/O
 
标准I/O这个术语参考Unix中“程序所使用的单一信息流”概念。程序的所有输出都来自标准输入,它的所有输出也被送到标准输出,以及所有的错误信息发送到标准错误。意义所在:可以很容易的把程序串以来,让一个program的输出作为另一个program的输入。
Java提供了System.in,System.out和System.err。System.out和System.err被包装成printStream,但是System.in就没有被包装,未经加工的InputStream。这意味我们可以直接使用System.out和System.err,但是在读取System.in的时候必须对其进行包装。我们会使用ReadLine()一次一次的读取输入,为此我们将System.in包装成BufferedReader来使用这要求我们必须使用InputStreamReader把System.in转换成Reader。如下面的例子:
package demo1808;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class Echo {
public static void main(String[] args) throws IOException{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String readLine = null;
while((readLine = br.readLine()) != null && readLine.length() != 0){//readLine()可以产生异常
System.out.println(readLine);
}

}
}
对比System.in,System.out操作起来简单的多,System.out是一个PrintStream,而PrintStream是一个OutputStream,PrintStream有一个可以接受OutputStream作为参数的构造器,so只需要调用它就可以实现将System.out转换PrintStream。
package demo1808;

import java.io.PrintStream;

public class ChangeSysOut {
public static void main(String[] args) {
PrintStream ps = new PrintStream(System.out,true);
ps.print("Hello,changing System.out");
}
}
 
   6.1   标准I/O重定向
java的System提供了一些静态的方法来允许对标准输入、输出和错误的IO流进行重定向。
技术分享
IO重定向操作的是字节流,而不是字符流。
 
  7.  进程控制
 
主要是通过java.lang.Process,java.lang.ProcessBuilder等(略过)
 
 
番外篇1:Strategy 模式 和 Command模式
 
Strategy 模式
应用场景:CRM(客户关系系统)中对于销售部门来说,向不同的客户提供不同的报价,这是一个重大复杂的问题,对不同的客户采用不同的报价算法。
/**
 * 主要完成对不同用户的提供不同的报价算法
 */
public class Price {
     public double quoto( double goodPrice,String customerType){
           if( "NormalCustomer".equals(customerType)){
              System. out.println( "NormalCustomer price : "+goodPrice);
               return goodPrice;
          } else if( "TeamCustomer".equals(customerType)){
              System. out.println( "TeamCustomer price : "+goodPrice*0.85);
               return goodPrice*0.85;
          } else if( "LargeCustomer".equals(customerType)){
              System. out.println( "LargeCustomer price : "+goodPrice*0.8);
               return goodPrice*0.8;
          }
           //other customer
           return goodPrice;
     }
}
以上的设计产生下面两个问题:
  • 价格类包含所有的算法,结构庞大,难以维护
那么就可能会有这样的设计,如下
public class Price {
     public double quoto( double goodPrice,String customerType){
           if( "NormalCustomer".equals(customerType)){
               return this.caluPriceNormalCustomer(goodPrice);
          } else if( "TeamCustomer".equals(customerType)){
               return this.caluPriceTeamCustomer(goodPrice);
          } else if( "LargeCustomer".equals(customerType)){
               return this.caluPriceLargeCustomer(goodPrice);
          }
           //other customer
           return goodPrice;
     }
 
     private double caluPriceLargeCustomer( double goodPrice) {
           ...
     }
 
     private double caluPriceTeamCustomer( double goodPrice) {
           ...
     }
 
     private double caluPriceNormalCustomer( double goodPrice) {
           ...
     }
}
感觉上可以啦,维护上相对来说简单点,只需要修改特定的方法就ok。真的就这样啦?——如果出现更多的客户类型,依然会使得这个Price类很臃肿。
 
  • 扩展性差,面对增加的多种类型的客户
比如增加更多的客户类型;还有就是在用户类型不变的条件下,增加了其他的因素,比如增加时间的概念,在节假日(五一、十一等)对用户又采取不同的价格算法。
分析上面出现的问题:各种计算价格的方式就好比是具体的算法,使用这些计算方式来计算报价的程序,就相当于是使用算法的客户。上面设计产生问题的根本原因就是算法和使用算法的客户是耦合的。
解决方案:策略模式定义一系列的算法,把他们封装起来,并且使他们可以相互替换。模式使得算法可独立于使用它 的客户而变化。
通过策略模式解决上面问题,应该把所有的价格计算方式独立出来,每个计算方式单独用一个类实现,将一系列的算法类定义一个公共的接口,算法实现是这个接口的不同实现,地位是平等,可以相互转换。这样一来新增一个算法就新增加一个算法实现类,要维护某个算法,也只是修改某个算法的实现即可,解决了可扩展性,可维护性。
为了实现让算法独立于使用它的客户,策略模式引入一个上下文的对象,这个对象负责持有算法,但是不负责决定使用哪个算法,他把选择的功能交给客户,有客户选择具体的算法后,设置到上下文对象中,让上下文对象持有客户选择的算法,当客户通知上下文对象执行功能的时候,上下文对象则调用具体的算法。这样实现了算法与客户的分离。
下面通过策略模式对原有问题进行重新设计。
技术分享
设计类图
如上图所示Price类就是一个上下文类,通过Price只是持有PriceStrategy接口算法,Client才是决定选择具体算法的类。
部分的代码如下所示。
package strategy.domo;

public class Client {

//private static double goodsPrice = 1000;

public static void main(String[] args){
PriceStrategy lps = new LargePriceStrategy();
double goodsPrice = 1000;
Price price = new Price(lps);

System.out.println(price.caluPrice(goodsPrice));

}
}
 
package strategy.domo;

public class Price {

private PriceStrategy ps = null;

public Price(PriceStrategy ps){
this.ps = ps;
}

public double caluPrice(double goodsPrice){
return ps.priceStrategyInterface(goodsPrice);
}
}
 
package strategy.domo;

public interface PriceStrategy {
public double priceStrategyInterface(double goodsPrice);
}
 
package strategy.domo;

public class NormalPriceStrategy implements PriceStrategy {

public double priceStrategyInterface(double goodsPrice) {
// TODO Auto-generated method stub
System.out.println("普通用户");
return goodsPrice*1;
}

}
//省略TeamPriceStrategy和LargePriceStrategy实现
 
策略模式的功能就是把具体的算法从具体的业务处理中独立出来,把他们的实现成单独的类。策略模式的重心不是怎样实现算法本身,而是如何组织,调用这些算法,让程序有很好的维护性和扩展性。
if-elseif语句本事是一个平等的功能结构,而策略模式也就是协调调用平等的策略算法实现,在这一点是相通的,所以在遇到if-elseif语句的时候可以参考策略模式的设计。
策略模式中有两种方式来进行策略的选择,其一就是像上面的例子,通过client来选择具体的策略算法,然后把执行算法的过程丢给上下文(上例中Priice);其二就是client不管,由上下文来决定选择执行哪个算法,例如容错恢复(参考“软件工程”—>“设计模式”—>“策略模式”)。
 
 
Command 模式
 
 
 
 
 
 
 
番外篇2:装饰器设计模式
 
练习1:设计一个在文件集操作的程序,文件可以在本地,也可以遍布在整个目录树中,依次显示当前文件下的文件夹以及文件列表,可以遍历整个目录树。例如
环境:
—>DirHome
——>java
———>jdk
———>document.doc
——>spring
———>spring.zip
———>springAPI
————>document.doc
——>pass.txt
输出:
  ./java
  ./java/jdk
  ./java/document.doc
  ./spring
  ./spring/springAPI
  ./spring/springAPI/document.doc
  ./spring/spring.zip
  ./pass.txt
增加条件:显示特定的文件夹和文件(策略模式设计)
 
练习2:重定向输入输出,将文件in.txt文件转化到out.txt中
 
练习3:编写ReadProcess.java类,然后command运行“javac ReadProcess.java”把显示信息打印到本地的out.txt中
 
练习4:实现向指定文件,指定位置插入内容的功能,补全方法
public static void insert(String fileName,long pos,String insertContent) throws IOException{
     ….
}
编写测试用例InsertContent.java类
 
 
 
 
待续

Java IO——堵塞式

标签:

原文地址:http://www.cnblogs.com/sideofcode/p/5406944.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!