Java核心技术卷II(第8版) – 读书笔记 – 第1章

1、输入流:可以从中读取一个字节。InputStrean

输出流:可以向其中写入一个字节。OutputStream

2、流来源、去处可能是文件,也可能是网络等。

3、由于Unicode多个字节表示一个字,所以不可能用流读取Unicode编码的文件。因此引入了Reader和Writer处理他们(基于双字节)。

4、基础的流只有两个:InputStream和OutputStream

read()每次读取一个字节,读到最后返回-1,字节不可用时会阻塞。

read(byte[] b, int off, int len)从off偏移开始读取字节,读取len个,返回实际读取的个数。

avaliable,不阻塞的返回可用字节数,-1为结尾。

都结束操作后,应该用close关闭,否则会耗尽系统资源(比如Linux下的nofile)。

write也是阻塞的写入。

flush是清空输出流,将缓存物理写入。close时会隐式调用这个。

5、所有的其他实用流都继承自InputStream/OutputStream,一次读取一个字节。

6、字符流用于处理Unicode,一次读取两个字节,Reader和Writer。

Reader和InputStream基本一致,但是read返回的是Unicode码元(0~65535之间的数),结尾一样返回-1。

Writer也是类似,只不过传入的c必须是Unicode码。

7、Java SE 5额外引入了几个接口:Closeable、Flushable、Readable、Appendable。

前两个接口就是对应了close和flush方法。Input/OutputStream、Reader/Writer都实现了Closeable,写的还实现了Flushable。

8、Readable接口只有一个方法:

这个CharBuffer是影射到内存中的可随机访问的块。FileReader、BufferedReader等都实现了这个方法。

可以追加一个或者多个字节,只有Writer实现了它。

9、Java的流使用组合的方式实现复杂的功能。例如从文件中读取需要使用FileInputStream,从流中读取整数需要DataInputStream,但是后者只接受InputStream参数而不接受文件名参数,于是:

如果我们希望读取的再快一些,可以在FileInputStream上追加包装一个BufferedInputStream。

10、PushBackInputStream,可以“回推”输入流。如果希望预先浏览,还能回退的流,可以用它的unread(…)方法。

11、ZipInputStream可以读写zip文件,例如我们直接读取Zip文件中的double:

读写zip文件需要结合getNextEntry()方法,如下:

12、OutputStreamWriter:把Unicode字符转变为字节流(以借助下层链上的XXOutputStream输出到文件、网络等)。

InputStreamReader:将包含字节输入流转变成提供Unicode读取的读入器:

这貌似也是不同流转化编码的唯一方式。同理,输出别的编码,要使用OutputStreamWriter:

13、如果只是读写文件(没编码的问题),可以直接使用FileReader和FileWriter,他们都有不需要底层FileInput/OutputStream的构造函数:

14、PrintWriter可以文本格式、数字等方法输出到文件,构造函数中可以带一个boolean参数,决定是否启用输出缓冲区,同时,还可以设置文件的字符编码。

15、总结:以二进制格式写数据:DataOutputStream,以文本格式写数据:PrintWriter。

16、读取文本数据:BufferedReader(<JDK5)或者Scanner(>JDK5)。

17、BufferedReader只能读取单个字符,如果要读数字等格式,最好用Scanner。

18、可以用文本格式存储对象:例如各个域之间用|分开(假定域内不含有|),则输出的时候用PrintWriter写String,读取的时候用Scanner读取一行,然后用String分割:

注意|输入正则的一部分,需要转义。

19、JDK1.4之后,引入了java.nio.Charset,用语多编码的表示。

20、几种常见的编码:
ISO-8859-1 就是最原始的ASCII。
ISO-8859-15 1的基础上,加入法语和荷兰语,并使用欧元符号。
UTF-8 不解释了。。

通过调用Charset.forname获得一个编码:

Java中的编码名字不区分大小写!

每个编码有很多别名,可以用aliases()取得这些别名。

21、如果不确定JDK中有哪些字符集,可以调用static方法availableCharsets获取所有可用的字符集编码:

22、一旦获得了一个Charset,就可以在Java的Unicode和指定的编码格式之间进行转化,下面以GBK和Unicode之间做为例子。

从Unicode到GBK:

从GBK到unicode:

暂时没有成功……诡异。。

23、DataOutput接口定义了以二进制格式输出,写各种类型数据的方法:

writeInt
writeByte
writeDouble
….
writeUTF

它们的写都是固定的,如int固定4字节,double是8。

总之是从Java内部类型转换到二进制表示,使用的都是大端。

对于UTF,使用的是一个UTF-8的修改版本,只有Java虚拟机是这么用的,因此跨语言时不要使用。

DataOutputStream实现了DataOutput接口,当然要用需要包装一层FileOutputStream,甚至Buffered神马的,比如:

24、类似的DataInput定义了和上面对应的读取方法。一般来说writeInt之后(输出成了二进制),是必须要能够从readInt反解析回来的。

25、RandomAccessFile同时实现了DataInput和DataOutput,主要是用于磁盘上的文件:

这个模式还可以选rws和rwd,rws要求每个I/O操作都要同步,rwd可以将多次I/O合并成一次。

seek()用于将文件指针放到文件内部任何位置。

getFilePointer()返回当前文件指针的位置。

length()返回文件总字节数。

26、下面的例子使用DataOutputStream、RandomAccessFile等来实现固定字节Record的读取和随机访问。

切记:String的一个char占用两个字节!!

27、ZipInputStream和ZipOutputStream是用于操作zip文件的。

它们都是嵌套结构,一个Stream内含若干个ZipEntry(就是Zip内置的文件)

对于ZipInputStream,需要首先调用getNextEntry(),之后再调用read(),当然也可以用其他DataInput等包装读取。当-1时,表示当前entry完毕,需要先调用CloseEntry(),然后再读取getNextEntry(),直到返回null,表示全部都读取完毕。

对于ZipOutputStream,新建一个文件时,需要putNextEntry(),写入完毕后,调用closeEntry()。

一个读写zip文件的例子如下:

需要注意的是,读取时,每切换一个Entry,要新建一个Scanner!!!

28、Java提供了“对象序列化”机制,可以将任何对象写入到流中,并在将来取回。

29、ObjectOutputStream用于输出对象,ObjectInputStream用于读取。对象必须实现了Serialize接口。但这个接口不含任何方法。所以序列化理论上不需要做其他事情,Stream会自动扫描所有域,并逐一序列化/反序列化。

30、为了保证对象的一致性(一个对象可能被多个其他对象引用)。每个对象内部有唯一的ID。

31、相同序号重复出现将被替换为只存储对象序号的引用。

32、如果不想让某个域被序列化,将其表为transient(瞬时的)即可,如:

33、如果想大规模重写序列化、反序列化,可以自己写readObject和writeObject:

34、如果想同时重写超类中的数据域,则要使用Externalizable接口。如果你对继承结构复杂的类序列化,并且想要更快的性能,应该使用Externalizable,它会更快。

35、对于Enum的枚举,序列化可以正常工作。对于public static final xxx 这种定义的常量,一般不会正常工作,此时慎重使用对象序列化。

36、因为序列化会存储类结构的指纹,因此如果类结构变化了,想搞一个版本号怎么办?简单,加上:

37、其实也可以用序列化做clone:先Output再Input。当然这比clone慢很多。

38、前面的各种Stream关注的是文件内容。而文件的管理,与文件系统,则由File类完成。

39、File还可以有dir和name的构造:

File既表示文件也可以是目录,用isFile()和isDirectory()区分。

File中的separator是路径分隔符,windows为\,Linux为/

File还有很多功能,如创建临时文件,遍历目录等等。。

40、JDK 1.4后引入了New I/O(java.nio),包含了下述操作系统的新特性:

  • 字符编码
  • 非阻塞I/O
  • 内存映射文件
  • 文件加锁

41、操作系统支持将文件映射到内存的一块中进行操作,以减少不断I/O的时间浪费。

用nio进行文件映射很简单:

(1)和往常一样打开FileInputStream
(2)FileChannel channel = stream.getChannel() 获得这个通道。
(3)FileChannel的map方法获得映射:

有三种默认:只读、可写、私人(可以更改,但不会更新到文件)。

之后就可以用ByteBuffer进行操作了。

可以一次get()出一个byte,也可以用getInt(),getDouble()等以二进制的方式读取基本类型。可以用order()设置大、小端。

42、Buffer这个超类和其子类IntBuffer、ByteBuffer、DoubleBuffer等,也是nio新引进的。

43、缓冲区是相同类型的信息块,每个缓冲区都有:固定的容量(设置的)、读写位置、界限(超出后无意义)、标记(用于重复读等)。

44、缓冲区主要是用来循环执行“写、读”等操作。
(1)初始:位置为0,界限等于容量,不断用put写入到缓冲区。
(2)当数据全写完或者到达容量限制时,需要切换到读操作。
(3)调用flip,将位置复位到0。
(4) 此时不断get,当remainning()返回正是,表示还有未读取完的。最后调用clear()重回写状态
如果要重新读入,可以用rewind或者mark/reset。

关于Buffer和Channel可以围观一下这篇神文,解释的很好:

http://www.cnblogs.com/focusj/archive/2011/11/03/2231583.html

45、有时候我们想对文件加锁,还是用FileChannel,它有lock()方法:

它会阻塞,直到获得一个锁,也可以用trylock,不阻塞。

lock也有设置独占、共享的版本:

如果shared=true,则共享,但放置别人独占。false则独占,排斥其他全部。

46、锁为JVM不可重入(同一个JVM启动的类不能全占有),实际是进程不可重入。

47、正则表达式,模式化的字符串:

匹配Java、java,java/Java**

正则表达式的规则不再多说了,用的太多了。

Java与正则相关的API主要是Pattern和Matcher类。

compile时可以加选项:

CASE_INSENSITIVE:大小写不敏感
MULTILINE:可以跨行
DOTALL:匹配所有终止。

Java的正则也支持组群。

group(int gourpNum)

0是全串,从1开始,按照在pattern中按照括号的逐一递增排序。

Matcher的replaceAll替换所有,之中可以用$n表示引用组群。

Pattern的split可以用正则分割字符串。

一个提取网页中所有<a href=””>,并把所有url替换为#的例子:

本章完毕。

 

 

Leave a Reply

Your email address will not be published.