《Java编程思想》之I/O系统

《Java编程思想》之I/O系统 - #include - 博客频道 - CSDN.NET

《Java编程思想》之I/O系统


分类:
《Java编程思想》
java


141人阅读
评论(0)
收藏
举报

 

1、Java中“流“类库让人迷惑的主要原因:创建单一的结果流,却需要创建多个对象。

2、使用层叠的数个对象为单个对象动态地、透明地添加职责的方式,称作“修饰器“模式。修饰器必须与其所修饰的对象具有相同的接口,这使得修饰器的基本应用具有透明性——我们可以想修饰过或没有修饰过的对象发送相同的消息。

3、为什么使用修饰器

         在直接使用扩展子类的方法时,如果导致产生了大量的、用以满足所需的各种可能的组合的子类,这是通常就会使用修饰器——处理太多的子类已经不太实际

4、修饰器的缺点:增加代码的复杂性。

5、JavaI/O类库操作不便的原因在于:我们必须创建许多类——“核心”I/O类型加上所有的修饰器,才能得到我们所希望的单个I/O对象。

6、FilterInputStream和FilterOutputStream是用来提供修饰器类接口以控制特定输入流和输出流的俩个类。它们分别继承自I/O类库中的基类InputStream和OutputStream。

7、DataInputStream是FilterInputStream的直接子类,它允许我们读取不用的基本类型数据以及String对象。

8、InputStream和OutputStream是面向字节的,而Reader和Writer是面向字符与兼容Unicode

9、在InputStream和OutputStream的继承层次结构仅支持8位的字节流,并且不能很好地处理16位的Unicode。由于Unicode用用于字符国际化,所以添加Reader和Write继承结构就是为了在所有的I/O操作中都支持Unicode。另外添加了Reader和Write的I/O流类库使得它的操作比旧类库更快。

10、但是,Reader和Write并不是用来取代nputStream和OutputStream的,因为InputStream和OutputStream在面向字节形式的I/O中仍然提供了极有价值的功能。

11、有时,我们需要把来自于“字节”层次结构的类和“字符”层次结构的类结合起来使用。这时,需要使用“适配器(adapter)”类:InputStreamReader可以把InputStream转换为Reader,而OutputStreamWriter可以把OutputStream转换为Writer。

12、当我们使用DataOutputStream时,写字符串并且让DataInputStream能够恢复它的唯一可靠的做法就是使用UTF-8编码。UTF-8是Unicode的变体,后者把所有字符都存储成两个字节的形式。而我们使用的只是ASCII或者几乎是ASCII字符(只占7位),这么做就显得极其浪费空间和宽带,所以UTF-8将ASCII字符编码成单一字节的形式,而非ASCII字符则编码成两到三个字节的形式。另外,字符串的长度存储在两个字节中

13、System.in是一个没有被加工过的InputStream,所以在读取System.in之前必须对其进行加工。

14、标准I/O重定向:如果我们突然开始在显示器上创建大量输出,而这些输出滚动得太快以至于无法阅读是,重定向输出就显得极为有用。重定向有一下方法:

 

  1. SetIn(InputStream)  
  2. SetOut(InputStream)  
  3. SetErr(InputStream)  

注意:重定向操作的是字节流(InputStream、OutputStream),而不是字符流。

15、JDK1.4的java.nio.*包引入了新的JavaI/O类库,其目的在于提高速度。速度的提高来自于所使用的结构更接近操作系统的I/O的方式:通道和缓冲器。

         我们可以把它想象成一个煤矿,通道是一个包含煤层(数据)的矿藏,而缓冲器则是派送到矿藏的卡车。卡车载满煤炭而归,我们再从卡车上获得煤炭。也就是说,我们并没有直接和通道交互;只是和缓冲器交互,并把缓冲器派送到通道。通道要么从缓冲器获得数据,要么向缓冲器发送数据。

         唯一直接与通道交互的缓冲器是ByteBuffer——也就是说,可以存储未加工字节的缓冲器。ByteBuffer是一个相当基础的类:通过告知分配多少存储空间来创建一个ByteBuffer对象,并且还有一个方法选择集,用以原始的字节形式或基本数据类型输出和读取数据。

16、FileInputStream、FileOutputStreaam以及用于既读又写的RandomAccessFile被使用nio重新实现过,都有一个getChannel()方法返回一个FileChannel(通道)。通道是一种相当基础的东西:可以向它传送用于读写的ByteBuffer,并且可以锁定文件的某些区域用于独占式访问。Reader和Writer这种字符模式类不能产生通道;但是java.nio.channels.Channels类提供了实用方法,用以在通道中产生Reader和Wirter。

以下

17、转换数据:缓冲器容纳的是普通的字节,为了把它们转化成字符,我们要么在输入它们的时候对其进行编码,要么在将其从缓冲器输出时对它们进行解码。请参看下面例子:

 

  1. import java.io.FileInputStream;  
  2. import java.io.FileOutputStream;  
  3. import java.nio.ByteBuffer;  
  4. import java.nio.channels.FileChannel;  
  5. import java.nio.charset.Charset;  
  6.    
  7. public class GetChannel{  
  8.    public static void main(String[] args) throws Exception{  
  9.       //写入data.txt  
  10.       FileChannel fc = new FileOutputStream("data.txt").getChannel();  
  11.       fc.write(ByteBuffer.wrap("Some text".getBytes()));  
  12.       fc.close();  
  13.       //读取data.txt  
  14.       fc = new FileInputStream("data.txt").getChannel();  
  15.       ByteBuffer buff = ByteBuffer.allocate(1024);  
  16.       fc.read(buff);  
  17.       buff.flip();  
  18.        
  19.       System.out.println(buff.asCharBuffer());  
  20.       //使用系统默认的字符集进行解码(输出时对其进行解码)  
  21.       buff.rewind();/返回数据开始的部分  
  22.       String encoding = System.getProperty("file.encoding");  
  23.       System.out.println("Decodedusing " + encoding + ":"  
  24.            + Charset.forName(encoding).decode(buff));  
  25.       //再次写入(输入时对其进行编码)  
  26.       fc = new FileOutputStream("data.txt").getChannel();  
  27.       fc.write(ByteBuffer.wrap("Some text".getBytes("UTF-16BE")));  
  28.       fc.close();  
  29.       //再次读取  
  30.       fc = new FileInputStream("data.txt").getChannel();  
  31.       buff = ByteBuffer.allocate(1024);  
  32.       fc.read(buff);  
  33.       buff.flip();  
  34.       System.out.println(buff.asCharBuffer());  
  35.       //再次写入(输入时对其进行编码)  
  36.       fc = new FileOutputStream("data.txt").getChannel();  
  37.       buff = ByteBuffer.allocate(24);  
  38.       buff.asCharBuffer().put("Some text");  
  39.       fc.write(buff);  
  40.       fc.close();  
  41.       //再次读取  
  42.       fc = new FileInputStream("data.txt").getChannel();  
  43.       buff.clear();  
  44.       fc.read(buff);  
  45.       buff.flip();  
  46.       System.out.println(buff.asCharBuffer());  
  47.    }  
  48. }  

运行结果:

18、获取基本类型:尽管ByteBuffer只能保存字节类型的数据,但是它具有从其所容纳的字节中产生出各种不同的类型值的方法。

 

  1. import java.nio.ByteBuffer;  
  2.   
  3. public class GetData{  
  4.    private final static int BSIZE = 1024;  
  5.    public static void main(String[] args){  
  6.     
  7.       ByteBuffer bb = ByteBuffer.allocate(BSIZE);  
  8.       int i = 0;  
  9.       while(i++ < bb.limit()){  
  10.         if(bb.get() != 0){  
  11.            System.out.println("nonzero");  
  12.         }  
  13.       }  
  14.       System.out.println("i = " + i);  
  15.       bb.rewind();  
  16.       //char  
  17.       bb.asCharBuffer().put("Howdy");  
  18.       char c;  
  19.       while((c = bb.getChar()) != 0){  
  20.         System.out.print(c + " ");  
  21.       }  
  22.       System.out.println();  
  23.       bb.rewind();  
  24.       //short。是一个特例,必须加上(short)进行类型转换  
  25.       bb.asShortBuffer().put((short)471142);  
  26.       System.out.println(bb.getShort());  
  27.       bb.rewind();  
  28.       //int  
  29.       bb.asIntBuffer().put(99471142);  
  30.       System.out.println(bb.getInt());  
  31.       bb.rewind();  
  32.       //long  
  33.       bb.asLongBuffer().put(99471142);  
  34.       System.out.println(bb.getLong());  
  35.       bb.rewind();  
  36.       //float  
  37.       bb.asFloatBuffer().put(99471142);  
  38.       System.out.println(bb.getFloat());  
  39.       bb.rewind();  
  40.       //double  
  41.       bb.asDoubleBuffer().put(99471142);  
  42.       System.out.println(bb.getDouble());  
  43.       bb.rewind();  
  44.    }  
  45. }  

运行结果:

《Java编程思想》之I/O系统

19、视图缓冲器:可以让我们通过某个特定的基本数据类型的视窗查看其底层的ByteBuffer。

 

  1. ByteBuffer bb = ByteBuffer.allocate(BSIZE);  
  2. //asIntBuffer() 创建此字节缓冲区的视图,作为 int 缓冲区。  
  3. IntBuffer ib = bb.asIntBuffer();  

20、不同机器可能会有不同的字节排序方法来存储数据。默认的ByteBuffer是以高位优先的顺序存储数据的。考虑下面两个字节:

《Java编程思想》之I/O系统

如果我们以short(ByteBuffer.asShortBuffer)高位优先读出的是97(0000000001100001),若更改ByteBuffer更改为地位优先,则读出的是24832(0110000100000000)。再看下面例子:

 

  1. import java.nio.ByteBuffer;  
  2. import java.nio.ByteOrder;  
  3. import java.util.Arrays;  
  4.    
  5. public class Endians{  
  6.    public static void main(String[] args){  
  7.       ByteBuffer bb = ByteBuffer.wrap(new byte[12]);  
  8.       bb.asCharBuffer().put("abcdef");  
  9.       System.out.println(Arrays.toString(bb.array()));  
  10.        
  11.       bb.rewind();  
  12.       bb.order(ByteOrder.BIG_ENDIAN);//设置为高位排序  
  13.       bb.asCharBuffer().put("abcdef");  
  14.       //array()只能对有数组支持的缓冲器调用  
  15.       System.out.println(Arrays.toString(bb.array()));  
  16.        
  17.       bb.rewind();  
  18.       bb.order(ByteOrder.LITTLE_ENDIAN);//设置为低位排序  
  19.       bb.asCharBuffer().put("abcdef");  
  20.       System.out.println(Arrays.toString(bb.array()));  
  21.    }  
  22. }  

运行结果:

21、如果想把一个字节数组写到文件中去,那么应该使用ByteBuffer.wrap()方法把字节数组包装起来,然后用getChannel()方法在FileOutputStream上打开一个通道,接着将来自于ByteBuffer的数据写到FileChannel中去。

22、存储器映射文件:允许我们创建和修改那些因为太大而不能放入内存的文件。有了存储器映射文件,我们就可以假定整个文件都在内存中,而且可以完全把它当作非常大的数组来访问。

    尽管“旧”的I/O在用nio实现后性能有所提高,但是“映射文件访问”往往可以更加显著地加快速度

23、文件加锁机制:允许我们同步访问某个作为共享资源的文件。文件锁对其它操作系统进程是可见的,因为Java的文件加锁之间映射到本地操作系统的加锁工具。另外,利用对映射文件的部分加锁,可以对巨大的文件进行部分加锁,以便其他进程可以对修改文件中未被加锁的部分

24、压缩:Java压缩类库是按字节方式的,它们继承自InputStream和OutputStream。

1).GZIP:如果只想对单个数据流(而不是一系列互异数据)进行压缩,那么它是比较适合的选择。下面是对单个文件进行压缩的例子:

 

  1. import java.io.BufferedOutputStream;  
  2. import java.io.BufferedReader;  
  3. import java.io.FileInputStream;  
  4. import java.io.FileOutputStream;  
  5. import java.io.IOException;  
  6. import java.io.InputStreamReader;  
  7. import java.util.zip.GZIPInputStream;  
  8. import java.util.zip.GZIPOutputStream;  
  9.    
  10. public class GZIPcompress{  
  11.    public static void main(String[] args) throws IOException{  
  12.       //InputStreamReader中设置字符编码为GBK  
  13.       BufferedReader in = new BufferedReader(new InputStreamReader(new  
  14.            FileInputStream("test.txt"), "GBK"));  
  15.       BufferedOutputStream out = new BufferedOutputStream((  
  16.            new GZIPOutputStream(new FileOutputStream("test.txt.gz"))));  
  17.       System.out.println("Writingfile");  
  18.       int c;  
  19.       while((c = in.read()) != -1){  
  20.         //使用GBK字符编码将此 String编码到 out中  
  21.         out.write(String.valueOf((char) c).getBytes("GBK"));  
  22.       }  
  23.       in.close();  
  24.       out.close();  
  25.       System.out.println("ReadingFile");  
  26.       BufferedReader in2 = new BufferedReader((new InputStreamReader  
  27.            (new GZIPInputStream(new FileInputStream("test.txt.gz")))));  
  28.       String s;  
  29.       while((s = in2.readLine()) != null){  
  30.         System.out.println(s);  
  31.       }  
  32.    }  
  33. }  

2).用Zip进行多文件压缩:对于每一个要加入压缩档案的文件,都必须调用putNextEntry(),并将其传递给一个ZipEntry对象。

25、对象的序列化:将那些实现Serializable接口的对象转换成一个字节序列,并能够在以后将这个字节序列完全恢复为原来的对象。利用它可以实现“轻量持久性”。

1).持久化”:意味一个对象的生存周期并不取决于程序是否正在执行。通过将一个序列化对象写入磁盘,然后再重新调用程序时恢复该对象,就能够实现持久性的效果。

2).轻量级”:因为它不能用某种“persistent”(持久)关键字来简单定一个对象,并让系统自动维护其他细节问题。相反,对象必须在程序中显示地序列化和反序列化还原。

26、对象序列化的概念加入到语言中是为了支持两种主要特性:

1).Java的“远程方法调用”(Remote Method Invocation,RMI)。

2).Java Beans。

27、

1).序列化:创建OutputStream对象,封装到ObjectOutputStream,再调用writeObject()

2).反序列化:创建InputStream对象,封装到ObjectInputStream,再调用readObject()

3).Serializable的对象在还原的过程中,没有调用任何构造器

28、对象序列化是面向字节的,因此采用InputStream和OutputStream层次结构。

29、序列化的控制

1).实现Externalizable接口,接口继承自Serializable,添加了writeExternal()和readExternal()两个方法,它们会在序列化和反序列化还原的过程中被自动调用。

2).反序列过程中,对于Serializable对象,对象完全以它存储的二进制位为基础来构造,而不是调用构造器。而Externalizeble对象,普通的缺省构造函数会被调用

3).transient(瞬时)关键字:只能和Serializable对象一起使用。

 

  1. private transient String password;  

password将不会被保存到磁盘中。

30、序列化/反序列化static成员:显示调用serializeStaticState()和deserializeStaticState()

31、Preferences:用于存储和读取用户的偏好(preferences)以及程序配置项的设置。只能用于小的、受限的数据集合——我们只能保存基本数据类型和字符串,并且每个字符串的存储类型长度不能超过8K(不是很小,单我们也不想用它来创建任何重要的东西)。它是一个键-值集合(类似映射),存储在一个节点层次结构中。

 

  1. import java.util.Arrays;  
  2. import java.util.Iterator;  
  3. import java.util.prefs.BackingStoreException;  
  4. import java.util.prefs.Preferences;  
  5.    
  6. public class PreferencesDemo{  
  7.    public static void main(String[] args) throws BackingStoreException{  
  8.       Preferences prefs = Preferences.userNodeForPackage(PreferencesDemo.class);  
  9.       prefs.put("日期""2012年02月05日");  
  10.       prefs.put("天气""晴");  
  11.       prefs.putInt("放假多少天了?"24);  
  12.       prefs.putBoolean("现在在家?"true);  
  13.        
  14.       int usageCount = prefs.getInt("寒假还剩多少天?"16);  
  15.       usageCount--;  
  16.       prefs.putInt("寒假还剩多少天?", usageCount);  
  17.        
  18.       Iterator<String> it = Arrays.asList(prefs.keys()).iterator();  
  19.       while(it.hasNext()){  
  20.         String key = it.next().toString();  
  21.         //null作为缺省  
  22.         System.out.println(key + ":" + prefs.get(key, null));  
  23.       }  
  24.       System.out.println("放假多少天了?" + prefs.getInt("放假多少天了?"0));  
  25.    }  
  26. }  

注意:每次运行,usageCount的值没都减少1。即每次打印出“寒假还剩多少天?”后面的数字都减少1。然而,在程序第一次运行之后,并没有任何本地文件出现。Preferences API利用合适的系统资源完成了这个任务,并且这些资源会随操作系统的不同而不同。例如在windows里,就使用注册表。

32、正则表达式

1).
在Java ,“\\”意味“我正在插入一个正则表达式反斜杠”,那么随后的字符具有特殊意义。若想插入一个字面意义上的反斜杠,得这样子表示“\\\\”。

2).量词:

·贪婪的:竟可能的模式发现尽可能的匹配。

·勉强的:用问号来指定,匹配满足模式所需的最少字符数。

·占有的:只有在Java语言中才可用,并且它也更高效,常常用于防止正则表达式失控,因此可以是正则表达式执行起来更有效。

3).注意abc+与(abc)+的不同。

4).在java中,正则表达式是通过java.util.regex包里面的Pattern和Matcher类来实现的。

·Pattern对象表示一个正则表达式的编译版本。静态的complie()方法将一个正则表达式字符串编译成Pattern对象。

·Matcher对象有matcher()方法和输入字符串编译过的Pattern对象中产生Matcher对象。

5).split()分裂操作将输入字符串断开成字符串对象数组。

 

你可能感兴趣的:(java编程思想)