objectinputstream objectoutputstream

解决方案 »

  1.   

    继承ArrayList...toString()方法重写一下哦...
      

  2.   

    java 序列化操作
    http://blog.csdn.net/ilibaba/archive/2009/03/10/3975680.aspx
      

  3.   

    java i/o 系统 
    对编程语言的设计者来说,创建一套好的输入输出(i/o)系统,是一项难度极高的任务。
    file 类 
    在介绍直接从流里读写数据的类之前,我们先介绍一下处理文件和目录的类。 你会认为这是个关于文件的类,但他不是。你能用他来表示某个文件的名字,也能用他来表示目录里一组文件的名字。如果他表示的是一组文件,那么你还能用list( )方法来进行查询,让他会返回string数组。由于元素数量是固定的,因此数组会比容器更好一些。如果你想要获取另一个目录的清单,再建一个file对象就是了。
    目录列表器 
    假设你想看看这个目录。有两个办法。一是不带参数调用list( )。他返回的是file对象所含内容的完整清单。不过,如果你要的是个"限制性列表(restricted list)"的话 —— 比方说,你想看看所有扩展名为.java的文件 —— 那么你就得使用"目录过滤器"了。这是个专门负责挑选显示file对象的内容的类。 filenamefilter接口的声明:
    public interface filenamefilter { boolean accept(file dir, string name);}
    accept( )方法需要两个参数,一个是file对象,表示这个文件是在哪个目录里面的;另一个是string,表示文件名。虽然你能忽略他们中的一个,甚至两个都不管,不过你大概总得用一下文件名吧。记住,list( )会对目录里的每个文件调用accept( ),并以此判断是不是把他包括到返回值里;这个判断依据就是accept( )的返回值。 切记,文件名里不能有路径信息。为此你只要用一个string对象来创建file对象,然后再调用这个file对象的getname( )就能了。他会帮你剥离路径信息(以一种平台无关的方式)。然后再在accept( )里面用正则表达式(regular expression)的matcher对象判断,regex是否和文件名相匹配。兜完这个圈子,list( )方法返回了一个数组。 
    匿名内部类 
    注意,filter( )的参数必须是final的。要想在匿名内部类里使用其作用域之外的对象,只能这么做。 能用匿名内部类来创建专门供特定问题用的,一次性的类。这种做法的好处是,他能把解决某个问题的代码全都集中到一个地方。不过从另一角度来说,这样做会使代码的可读性变差,所以要慎重。 
    查看和创建目录 
    file类的功能不仅限于显示文件或目录。他还能帮你创建新的目录甚至是目录路径(directory path),如果目录不存在的话。此外他还能用来检查文件的属性(大小,上次修改的日期,读写权限等),判断file对象表示的是文件还是目录,及删除文件。renameto( )这个方法会把文件重命名成(或说移动到)新的目录,也就是参数所给出的目录。而参数本身就是个file对象。这个方法也适用于目录。 
    输入和输出 
    i/o类库常使用"流(stream)"这种抽象。所谓"流"是一种能生成或接受数据的,代表数据的源和目标的对象。流把i/o设备内部的具体操作给隐藏起来了。 java的i/o类库分成输入和输出两大部分。所有inputstream和reader的派生类都有一个基本的,继承下来的,能读取单个或byte数组的read( )方法。同理,所有outputstream和writer的派生类都有一个基本的,能写入单个或byte数组的write( )方法。但通常情况下,你是不会去用这些方法的;他们是给其他类用的 —— 而后者会提供一些更实用的接口。因此,你非常少会碰到只用一个类就能创建一个流的情形,实际上你得把多个对象叠起来,并以此来获取所需的功能。java的流类库之所以会那么让人犯晕,最主要的原因就是"你必须为创建一个流而动用多个对象"。 
    inputstream的种类 
    inputstream的任务就是代表那些能从各种输入源获取数据的类。这些源包括: 
    byte数组 string对象 文件 类似流水线的"管道(pipe)"。把东西从一头放进去,让他从另一头出来。 一个"流的序列(a sequence of other streams)",能将他们组装成一个独立的流。 其他源,比如internet的连接。(这部分内容在thinking in enterprise java中讨论。) 
    这些数据源各自都有和之相对应的inputstream的子类。此外,filterinputstream也是inputstream的子类,其作用是为基类提供"decorator(修饰)"类,而decorator又是为inputstream设置属性和接口的。表12-1. inputstream的种类 类 功能 构造函数的参数 用法 bytearrayinputstream 以缓冲区内存为inputstream 要从中提取byte的那个缓冲区 一种数据源:要把他连到filterinputstream对象,由后者提供接口。 stringbufferinputstream 以string为inputstream 需要一个string对象。实际上程式内部用的是stringbuffer。 一种数据源:要把他连到filterinputstream对象,由后者提供接口。 fileinputstream 专门用来读文件的 一个表示文件名的string对象,也能是file或 filedescriptor对象。 一种数据源:要把他连到filterinputstream对象,由后者提供接口。 pipedinputstream 从pipedoutputstream提取数据。实现"管道"功能。 pipedoutputstream 一种多线程环境下的数据源,把他连到filterinputstream对象,由后者提供的接口。 sequenceinputstream 将两个或更多的inputstream合并成一个inputstream。 两个inputstream对象,或一个inputsteam对象容器的enumerator 一种数据源:要把他连到filterinputstream对象,由后者提供接口。 filterinputstream 一个为decorator定义接口用的抽象类。而decorator的作用是为inputstream实现具体的功能。详见表12-3。 见表 12-3 见表 12-3 
    outputstream的种类 
    这部分都是些决定往哪里输出的类:是byte的数组(不能是string;不过你能根据byte数组创建字符串)还是文件,或是"管道"。 此外,filteroutputstream还是decorator类的基类。他会为outputstream安装属性和适用的接口。表12-2. outputstream的种类 类 功能 构造函数的参数 用法 bytearrayoutputstream 在内存里创建一个缓冲区。数据送到流里就是写入这个缓冲区。 缓冲区初始大小,可选。 要想为数据指定目标,能用filteroutputstream对其进行包装,并提供接口。 fileoutputstream 将数据写入文件。 一个表示文件名的字符串,也能是file或filedescriptor对象。 要想为数据指定目标,能用filteroutputstream对其进行包装,并提供接口。 pipedoutputstream 写入这个流的数据,最终都会成为和之相关联的pipedinputstream的数据源。否则就不成其为"管道"了。 pipedinputstream 要想在多线程环境下为数据指定目标,能用filteroutputstream对其进行包装,并提供接口。 filteroutputstream 一个给decorator提供接口用的抽象类。而decorator的作用是为outputstream实现具体的功能。详见表12-4 见表12-4 见表12-4 
    添加属性和适用的接口 
    使用"分层对象(layered objects)",为单个对象动态地,透明地添加功能的做法,被称为decorator pattern。(模式是thinking in patterns (with java)的主题。)decorator模式需求所有包覆在原始对象之外的对象,都必须具有和之完全相同的接口。这使得decorator的用法变得非常的透明--无论对象是否被decorate过,传给他的消息总是相同的。这也是java i/o类库要有"filter(过滤器)"类的原因:抽象的"filter"类是所有decorator的基类。(decorator必须具有和他要包装的对象的全部接口,不过decorator能扩展这个接口,由此就衍生出了非常多"filter"类)。 decorator模式常用于如下的情形:如果用继承来解决各种需求的话,类的数量会多到不切实际的地步。java的i/o类库需要提供非常多功能的组合,于是decorator模式就有了用武之地。不过decorator有个缺点,在提高编程的灵活性的同时(因为你能非常容易地混合和匹配属性),也使代码变得更复杂了。java的i/o类库之所以会这么怪,就是因为他"必须为一个i/o对象创建非常多类",也就是为一个"核心"i/o类加上非常多decorator。 为inputstream和outputstream定义decorator类接口的类,分别是filterinputstream和filteroutputstream。这两个名字都起得不怎么样。filterinputstream和filteroutputstream都继承自i/o类库的基类inputstream和outputstream,这是decorator模式的关键(惟有这样decorator类的接口才能和他要服务的对象的完全相同)。 
    用filterinputstream读取inputstream 
    filterinputstream及其派生类有两项重要任务。datainputstream能读取各种primitive及string。(所有的方法都以"read"打头,比如readbyte( ), readfloat( ))。他,及他的搭档dataoutputstream,能让你通过流将primitive数据从一个地方导到另一个地方。这些"地方"都列在表12-1里。 其他的类都是用来修改inputstream的内部行为的:是不是做缓冲,是不是知道他所读取的行信息(允许你读取行号或设定行号),是不是会弹出单个字符。后两个看上去更像是给编译器用的(也就是说,他们大概是为java编译器设计的),所以通常情况下,你是不大会用到他们的。 
      

  4.   

    不论你用哪种i/o设备,输入的时候,最佳都做缓冲。所以对i/o类库来说,比较明智的做法还是把不缓冲当特例(或去直接调用方法),而不是像目前这样把缓冲当作特例。表12-3. filterinputstream的种类 类 功能 构造函数的参数 用法 datainputstream 和dataoutputstream配合使用,这样你就能以一种"可携带的方式(portable fashion)"从流里读取primitives了(int,char,long等) inputstream 包含了一整套读取primitive数据的接口。 bufferedinputstream 用这个类来解决"每次要用数据的时候都要进行物理读取"的问题。你的意思是"用缓冲区。" inputstream,及可选的缓冲区的容量 他本身并不提供接口,只是提供一个缓冲区。需要连到一个"有接口的对象(interface object)"。 linenumberinputstream 跟踪输入流的行号;有getlinenumber( )和setlinenumber(int)方法 inputstream 只是加一个行号,所以还得连一个"有接口的对象"。 pushbackinputstream 有一个"弹压单字节"的缓冲区(has a one byte push-back buffer),这样你就能把最后读到的那个字节再压回去了。 inputstream 主要用于编译器的扫描程式。可能是为支持java的编译器而设计的。用的机会不多。 
    用filteroutputstream往outputstream里面写东西
    datainputstream的另一半是dataoutputstream。他的任务是把primitive数据和string对象重新组织成流,这样其他机器就能用datainputstream读取这个流了。dataoutputstream的方法都是以"write"开头的,比如writebyte( ),writefloat( )等等。printstream的用意是要以一种大家都能看懂的方式把primitive数据和string对象打印出来。这一点同dataoutputstream不同,后者是要将数据装入一个流,然后再交给 datainputstream处理。printstream的两个最重要的方法是print( )和println( )。这两个方法都已作了重载,因此能打印各种数据。print( )和println( )的差别在于,后者会多打印一个换行符。使用printstream的时候会比较麻烦,因为他会捕捉所有的ioexception(所以你必须直接调用checkerror( )来检查错误条件,因为这个方法会在碰到问题的时候返回true)。再加上,printstream的国际化做得也不好,而且还不能以和平台无关的方式处理换行(这些问题都已在printwriter里得到解决,我们接下来再讲)。bufferedoutputstream 是个decorator,他表示对流作缓冲,这样每次往流里写东西的时候他就不会再每次都作物理操作了。输出的时候大致都要这么做。表12-4. filteroutputstream的种类 类 功能 构造函数的参数 用法 dataoutputstream 和datainputstream配合使用,这样你就能用一种"可携带的方式(portable fashion)"往流里写primitive了(int, char, long,等) outputstream 包括写入primitive数据的全套接口。 printstream 负责生成带格式的输出(formatted output)。dataoutputstrem负责数据的存储,而printstream负责数据的显示。 一个outputstream及一个可选的boolean值。这个boolean值表示,要不要清空换行符后面的缓冲区。 应该是outputstream对象的最终包覆层。用的机会非常多。 bufferedoutputstream 用 这个类解决"每次往流里写数据,都要进行物理操作"的问题。也就是说"用缓冲区"。用flush( )清空缓冲区。 outputstream, 及一个可选的缓冲区大小 本身并不提供接口,只是加了一个缓冲区。需要链接一个有接口的对象。 
    reader 和 writer类系 
    java 1.1对最底层的i/o流类库作了重大修改。第一次看到reader和writer的时候,你会觉得"他们大概是用来取代inputstream和outputstream的" (和我相同)。但事实并非如此。虽然inputstream和outputstream的某些功能已淘汰了(如果你继续使用,编译器就会发警告),但他们仍然提供了非常多非常有价值的,面向byte的i/o功能,而reader和writer则提供了unicode兼容的,面向字符的i/o功能。此外:
    java 1.1还对inputstream和outputstream作了新的补充,所以非常明显这两个类系并没有被完全替代。有时,你还必须同时使用"基于byte的类"和"基于字符的类"。为此,他还提供了两个"适配器(adapter)"类。inputstreamreader负责将inputstream转化成reader,而outputstreamwriter则将outputstream转化成writer。 
    reader和writer要解决的,最主要的问题就是国际化。原先的i/o类库只支持8位的字节流,因此不可能非常好地处理16位的unicode字符流。unicode是国际化的字符集(更何况java内置的char就是16位的unicode字符),这样加了reader和writer之后,所有的i/o就都支持unicode了。此外新类库的性能也比旧的好。
    数据源和目的
    几乎所有的java i/o流都有和之对应的,专门用来处理unicode的reader和writer。但有时,面向byte的inputstream和outputstream才是正确的选择;特别是java.util.zip;他的类都是面向byte的。所以最明智的做法是,先用reader和writer,等到必须要用面向byte的类库时,你自然会知道的,因为程式编译不过去了。下面这张表格列出了这两个类系的数据源和目的之间的关系(也就是说,在这两个类系里,数据是从哪里来的,又是到那里去的)。
    数据源和目的 java 1.0的类 java 1.1的类 inputstream reader的适配器:inputstreamreader outputstream writer的适配器: outputstreamwriter fileinputstream filereader fileoutputstream filewriter stringbufferinputstream stringreader (没有对应的类) stringwriter bytearrayinputstream chararrayreader bytearrayoutputstream chararraywriter pipedinputstream pipedreader pipedoutputstream pipedwriter 
    总之,这两个类系即便不是一摸相同,也至少是非常相像。
    修改流的行为
    不管是inputstream还是outputstream,用的时候都要先交给filterinputstream和filteroutputstrem,并由后者,也就是decorator做一番改造。reader和writer继承了这一传统,不过不是完全照搬。下面这张表的对应关系比前面那张更粗略。这是因为这两个类系的组织结构不同。比方说bufferedoutputstream是filteroutputstream的子类,但bufferedwriter却不是filterwriter的子类(后者虽然是个abstract类,但却没有子类,所以他看上去只是起一个"占位子"的作用,这样你就不会去惦记他在哪里了)。但不管怎么说,他们的接口还是非常相似的。
    filter类 java 1.0的类 java 1.1的类 filterinputstream filterreader filteroutputstream filterwriter(这是个无派生类的抽象类) bufferedinputstream bufferedreader(也有readline( )) bufferedoutputstream bufferedwriter datainputstream 尽量用datainputstream (除非你用bufferedreader的时候要用readline( )) printstream printwriter linenumberinputstream(过时了) linenumberreader streamtokenizer streamtokenizer(换一个构造函数,把reader当参数传给他) pushbackinputstream pushbackreader 
    有一条非常清晰:别再用datainputstream的readline( )(编译时会警告你这个方法已"过时了(deprecated)"),要用就用bufferedreader的。此外,datainputstream仍然是i/o类库的"种子选手"。为了让向printwriter的过渡变得更简单,printwriter除了有一个拿writer做参数的构造函数之外,更有一个拿outputstream做参数的构造函数。不过printwriter格式上并不比printstream的更好;他们的接口实际上是完全相同的。 printwriter的构造函数里更有一个可选的,能自动地进行清空操作的选项。如果你设了这个标记,那么每次println( )之后,他都会自动清空。
    没变过的类
    java从1.0升到1.1时,有几个类没有变过:
    在java 1.1 中无相对应的类的 java 1.0 的类dataoutputstreamfilerandomaccessfilesequenceinputstream
    特别是dataoutputstream,用法都一点没变,所以你就能用inputstream和outputstream来读写能传输的数据了。
    自成一派: randomaccessfile
    randomaccessfile是用来访问那些保存数据记录的文件的,这样你就能用seek( )方法来访问记录,并进行读写了。这些记录的大小不必相同;不过其大小和位置必须是可知的。首先,你可能会不太相信,randomaccessfile竟然会是不属于inputstream和outputstream类系的。实际上,除了实现datainput和dataoutput接口之外(datainputstream和dataoutputstream也实现了这两个接口),他和这两个类系毫不相干,甚至都没有用inputstream和outputstream已准备好的功能;他是个完全独立的类,所有方法(绝大多数都只属于他自己)都是从零开始写的。这可能是因为randomaccessfile能在文件里面前后移动,所以他的行为和其他的i/o类有些根本性的不同。总而言之,他是个直接继承object的,独立的类。基本上,randomaccessfile的工作方式是,把datainputstream和dataoutputstream粘起来,再加上他自己的一些方法,比如定位用的getfilepointer( ),在文件里移动用的seek( ),及判断文件大小的length( )。此外,他的构造函数还要一个表示以只读方式("r"),还是以读写方式("rw")打开文件的参数 (和c的fopen( )一模相同)。他不支持只写文件,从这一点上看,如果randomaccessfile继承了datainputstream,他也许会干得更好。只有randomaccessfile才有seek方法,而这个方法也只适用于文件。bufferedinputstream有一个( )方法,你能用他来设定标记(把结果保存在一个内部变量里),然后再调用reset( )返回这个位置,不过他的功能太弱了,而且也不怎么实用。randomaccessfile的绝大多数功能,如果不是全部的话,已被jdk 1.4的nio的"内存映射文件(memory-mapped files)"给取代了。下面我们会讲到这部分内容的。
    常见的i/o流的使用方法
    虽然i/o流的组合方式有非常多种,但最常用的也就那么几种。下
    输入流
    第一到第四部分演示了怎么创建和使用inputstream。第四部分还简单地演示了一下outputstream的用法。
    1. 对输入文件作缓冲
    要想打开打开文件读取字符,你得先用string或file对象创建一个fileinputreader。为了提高速度,你应该对这个文件作缓冲,因此你得把fileinputreader的reference交给bufferedreader。由于bufferedreader也提供了readline( )方法,因此他就成为你最终要使用的那个对象,而他的接口也成为你使用的接口了。当你读到了文件的末尾时,readline( )会返回一个null,于是就退出while循环了。最后,用close( )来关闭文件。单从技术角度上说,程式退出的时候(不管有没有垃圾要回收)都应该调用finalize( ),而finalize( )又会调用close( )。不过各种jvm的实现并不一致,所以最佳还是明确地调用close( )。
      

  5.   

    system.in是个inputstream,而bufferedreader需要一个reader作参数,所以要先通过inputstreamreader来转转手。
    2. 读取内存
    (stringreader的)read( )方法会把读出来的byte当作int,所以要想正常打印的话,你得先把他们转换成char。
    3. 读取格式化的内存
    要想读取"格式化"的数据,你就得用datainputstream了,他是个面向byte的i/o类 (不是面向char的),因此你只能从头到底一直用inputstream了。当然你能把所有东西(比方说文件) 都当成byte,然后用inputstream读出来,但这里是string。要想把string变成成byte数组,能用string的getbytes( )方法,而bytearrayinputstream是能处理byte数组的。到了这一步,你就不用担心没有合适的inputstream来创建datainputstream了。如果你是用readbyte( )逐字节地读取datainputstream的话,那么无论byte的值是多少,都是合法的,所以你无法根据返回值来判断输入是否已结束了。你只能用available( )来判断更有多少字符。注意,available( )的工作方式会随读取介质的不同而不同;严格地讲,他的意思是"能不被阻塞地读取的字节的数目。"对文件来说,他就是整个文件,但如果是其他流,情况就不一定了,所以用之前要多留一个心眼。你也能像这样,用异常来检查输入是不是完了。但不管怎么说,把异常当成控制流程来用总是对这种功能的滥用。
    4. 读取文件
    (试试把bufferedwriter去掉,你就能看到他对性能的影响了—— 缓冲能大幅提高i/o的性能)。linenumberinputstream这是个傻乎乎的,没什么用的类输入流用完之后,readline( )会返回null。如果写文件的时候不调用close( ),他是不会去清空缓冲区的,这样就有可能会落下一些东西了。
    输出流
    根据写数据的方式不同,outputstream主要分成两类;一类是写给人看的,一类是供datainputstream用的。虽然randomaccessfile的数据格式同datainputstream和dataoutputstream的相同,但他不属于outputstream的。
    5. 存储和恢复数据
    printwriter会对数据进行格式化,这样人就能读懂了。不过如果数据输出之后,还要恢复出来供其他流用,那你就必须用dataoutputstream来写数据,再用datainputstream来读数据了。当然,他们能是所有流,不过我们这里用的是个经缓冲的文件。dataoutputstream和datainputstream是面向byte的,因此这些流必须都是inputstream和outputstream。如果数据是用dataoutputstream写的,那么不管在哪个平台上,datainputstream都能准确地把他还原出来。这一点真是太有用了,因为没人知道谁在为平台专属的数据操心。如果你在两个平台上都用java,那这个问题就根本不存在了 。用dataoutputstream写string的时候,要想确保将来能用datainputstream恢复出来,唯一的办法就是使用utf-8编码,也就是像例程第5部分那样,用writeutf( )和readutf( )。utf-8是unicode的一种变形。unicode用两个字节来表示一个字符。不过,如果你处理的全部,或主要是ascii字符(只有7位),那么无论从存储空间还是从带宽上看,就都显得太浪费了,所以utf-8 用一个字节表示ascii字符,用两或三个字节表示非ascii的字符。此外,字符串的长度信息存在(字符串)的头两个字节里。writeutf( )和readutf( )用的是java自己的utf-8版本,所以如果你要用一个java程式读取writeutf( )写的字符串的话,就必须进行一些特别处理了。有了writeutf( )和readutf( ),你就能放心地把string和其他数据混在一起交给dataoutputstream了,因为你知道string是以unicode的形式存储的,而且能非常方便地用dataoutputstream恢复出来。writedouble( )会往流里写double,而他"影子"readdouble( )则负责把他恢复出来(其他数据也有类似的读写方法)。不过要想让读取方法能正常工作,你就必须知道流的各个位置上都放了些什么数据。因为你完万能把double读成byte,char,或其他什么东西。所以要么以固定的格式写文件,要么在文件里提供额外的解释信息,然后一边读数据一边找数据。先提一下,对于复杂数据的存储和恢复,对象的序列化可能会比较简单。
    6. 读写随机文件
    正如我们前面所讲的,如果不算他实现了datainput和dataoutput接口,randomaccessfile几乎是完全独立于其他i/o类库之外的,所以他不能和inputstream和outputstream合起来用。虽然把bytearrayinputstream当作"随机存取的元素(random-access element)"是一件非常合情合理的事,但你只能用randomaccessfile来打开文件。而且,你只能假定randomaccessfile已做过缓冲了,因为即便没做你也无能为力。构造函数的第二个参数的意思是:是以只读("r") 还是读写("rw")方式打开randomaccessfile。randomaccessfile的用法就像是datainputstream和dataoutputstream的结合(因为他们的接口是等效的)。此外,你还能用seek( )在文件里上下移动,并进行修改。随着jdk 1.4的new i/o的问世,你该考虑一下是不是用"内存映射文件(memory-mapped file)"来代替randomaccessfile了。
    管道流
    这一章只会大致地提一下pipedinputstream,pipedoutputstream,pipedreader和pipedwriter。这并不是说他们不重要,只是因为管道流是用于线程间的通信的,所以除非你已理解了多线程,否则是不会理解他的价值的。我们会在第13章用一个例子来讲解这个问题。
    读写文件的实用程式
    把文件读进内存,改完,再写文件。这是再普通不过的编程任务了。不过java的i/o就是有这种问题,即便是做这种常规操作,你也必须写一大串代码——根本就没有辅助函数。更糟的是,那些喧宾夺主的decorator会让你忘了该怎样打开文件。因此比较明智的做法还是自己写一个辅助类。下面就是这样一个类,他包含了一些"能让你将文本文件当作字符串来读写"的static方法。此外,你还能创建一个"会把文件的内容逐行存入arraylist的"textfile类,(这样在处理文件的时候,就能使用arraylist的功能了)://: com:bruceeckel:util:textfile.java// static functions for reading and writing text files as// a single string, and treating a file as an arraylist.// {clean: test.txt test2.txt}package com.bruceeckel.util;import java.io.*;import java.util.*;public class textfile extends arraylist { // tools to read and write files as single strings: public static string read(string filename) throws ioexception { stringbuffer sb = new stringbuffer(); bufferedreader in = new bufferedreader(new filereader(filename)); string s; while((s = in.readline()) != null) { sb.append(s); sb.append("\n"); } in.close(); return sb.tostring(); } public static void write(string filename, string text) throws ioexception { printwriter out = new printwriter( new bufferedwriter(new filewriter(filename))); out.print(text); out.close(); } public textfile(string filename) throws ioexception { super(arrays.aslist(read(filename).split("\n"))); } public void write(string filename) throws ioexception { printwriter out = new printwriter( new bufferedwriter(new filewriter(filename))); for(int i = 0; i < size(); i++) out.println(get(i)); out.close(); } // simple test: public static void main(string[] args) throws exception { string file = read("textfile.java"); write("test.txt", file); textfile text = new textfile("test.txt"); text.write("test2.txt"); }} ///:~所有这些方法都会直接往外面抛ioexception。由于一行读出来之后,后面的换行符就没了,因此read( )会在每行的后面再加一个换行符,然后接到stringbuffer的后面(出于效率考虑)。最后他会返回一个"含有整个文件的内容"的string。write( )的任务是打开文件,然后往里面写东西。任务完成之后要记着把文件给close( )了。(textfile)的构造函数用read( )方法将文件转化成string,然后用string.split( )和换行符转换成数组(如果你要经常使用这个类,或许应该重写一遍构造函数以提高性能)。此外,由于没有相应的"join"方法,非static的write( )方法只能手动地逐行打印文件。为了确保他能正常工作,main( )作了一个基本测试。虽然他只是个小程式,但到后面你就会发觉,他却能帮你节省非常多时间,同时让生活变得轻松一点。
    标准i/o
    "标准i/o"是unix的概念,他的意思是,一个程式只使用一个信息流(这种设计思想也以某种形式体目前windows及其他非常多操作系统上)。所有输入都是从"标准输入"进来的,输出都从"标准输出"出去,错误消息都送到"标准错误"里。标准i/o的好处是,他能非常容易地把程式串连起来,并且把一个程式的输出当作另一个程式的输入。这是一种非常强大的功能。
    读取标准输入
    java遵循标准i/o的模型,提供了syetem.in,system.out,及system.err。本书一直都在用system.out往标准输出上写,而他(system.out)是个已预先处理过的,被包装成printstream的对象。和system.out相同,system.err也是个printstream,不过system.in就不对了,他是个未经处理的inputstream。也就是说,虽然你能直接往system.out和system.err上写,不过要想读system.in的话,就必须先做处理了。通常情况下,你会用readline( )一行一行地读取输入,因此要把system.in包装成bufferedreader。但在这之前还得先用inputsteamreader把system.in转换成reader。
    将system.out转换成printwriter
    system.out是printstream,也就是说他是outputstream。不过printwriter有一个能将outputstream改造成printwriter的构造函数。有了这个构造函数,你就能随时将system.out转化成printwriter了:为了启动自动清空缓冲区的功能,一定要使用双参数版的构造函数,并且把第二个参数设成true。这点非常重要,否则就有可能会看不到输出了。
    标准i/o的重定向
    java的system类还提供了几个能让你重定向标准输入,标准输出和标准错误的静态方法:setin(inputstream)setout(printstream)seterr(printstream) 如果程式在短时间内输出了大量的信息,使得翻屏的速度非常快,以致于你都没法读了,这时对输出进行重定向就会显得非常有用了 。对于那些要重复测试用户输入的命令行程式来说,对输入进行重定向也是非常重要的。i/o重定向处理的不是character流,而是byte流,因此不能用reader和writer,要用inputstream和outputstream。
      

  6.   

    到底是ArrayList整个写入,还是ArrayList的内容写入?也不说清楚。