20145215《Java程序设计》第8周学习总结

20145215《Java程序设计》第八周学习总结

教材学习内容总结

NIO与NIO2

认识NIO

  • NIO使用频道(Channel)来衔接数据节点,在处理数据时,NIO可以设定缓冲区(Buffer)容量,在缓冲区中对感兴趣的数据区块进行标记,像是标记读取位置、数据有效位置,对于这些区块标记,提供了clear()、rewind()、flip()、compact()等高级操作。
  • 例如在10.1.1中看过的dump()方法:
public static void dump(InputStream src,OutputStream dest)throws IOException{
try(InputStream input = src;OutputStream output = dest){
byte[] data = new byte[1024];
int length;
while((length = input.read(data))!=-1){
output.write(data,0,length);
}
}
}

如果使用NIO的话,可以这样写:

public static void dump(ReadableByteChannel src,WritableByteChannel dest)throws IOException{
ByteBuffer buffer = ByteBuffer.allocate(1024);
try(ReadableByteChannel srcCH = src;WritableByteChannel destCH = dest){
while(srcCH.read(buffer)!=-1){
buffer.flip();
destCH.write(buffer);
buffer.clear();
}
}
}
  • Channel继承架构:

    20145215《Java程序设计》第8周学习总结

想要取得Channel的操作对象,可以使用Channels类,它定义了静态方法newChannel(),可以让你从InputStream、OutputStream分别建立ReadableByteChannel、WritableByteChannel,有些InputStream、OutputStream实例本身也有方法可以取得Channel实例,例如:FileInputSteam、FileOutputStream都有个getChannel()方法可以分别取得FileChannel实例。

  • Buffer继承架构:

    20145215《Java程序设计》第8周学习总结
  1. Buffer的直接子类们都有个allocate()静态方法,可以让你指定Buffer容量(Capacity),如果想取得Buffer内部的阵列,可以使用array()方法,如果有个数组想要转为某个Buffer子类实例,每个Buffer子类实例都有wrap()静态方法可以提供操作。
  2. Buffer是容器,填装的数据不会超过它的容量,容量大小可以使用capacity()方法取得,实际可读取或写入的数据界限(Limit)索引值可以由limit()方法得知或设定,下一个可读取数据的位置(Position)索引值,可以使用position()方法得知或设定。

NIO2文件系统

  • NIO2文件系统API提供一组标准接口与类,应用程序开发者只要基于这些标准接口与类进行文件系统操作,底层实际如何进行文件系统操作,是由文件系统提供者负责(由厂商操作)。

  • NIO2文件系统的中心是java.nio.file.spi.FileSystemProvider,本身为操作类,是文件系统提供者才要操作的类,作用是产生java.nio.filejava.nio.file.attribute中各种抽象类或接口的操作对象:

    20145215《Java程序设计》第8周学习总结

  • 应用程式开发者可以通过java.nio.file套件中FileSystems、Paths、Files等类提供的静态方法,取得相关操作对象或进行各种文件系统操作,这些静态方法内部会运用FileSystemProvider来取得所需的操作对象,完成应有的操作。

通用API

日志

  • 日志API简介:
  1. java.util.logging包提供了日志功能相关类与接口,它们是从JDK1.4之后加入标准API,要取得Logger实例,必须使用Logger的静态方法getLogger():
Logger logger = Logger.getLogger("ch08.Main");
  1. 调用getLogger()时,必须指定Logger实例所属名称空间(Name space),名称空间以"."作为层级区分,名称空间层级相同的Logger,其父Logger组态相同。
  2. 取得Logger实例之后,可以使用log()方法输出讯息,输出讯息时可以使用Level的静态成员指定讯息层级(Level)。
  • 指定日志层级:
  1. Logger与Handler预设都会先依Level过滤信息,如果没有做任何修改,取得的Logger实例之父Logger组态,就是Logger.GLOBAL_LOGGER_NAME名称空间Logger实例的组态,这个实例的Level设定为INFO,可通过Logger实例的getParent()取得父Logger实例,可通过getLevel()取得设定的Level实例。
  2. 在经过Logger过滤之后,还得再经过Handler的过滤,一个Logger可以拥有多个Handler,可通过Logger的addHandler()新增Handler实例。
  3. 实际上进行信息输出时,目前Logger的Handler处理完,还会传播给父Logger的所有Handler处理(在通过父Logger层级的情况下)。
  • 如果你不想让父Logger的Handler处理日志,可以调用Logger实例的setUseParentHandlers()设定为false,可以使用Logger实例的setParent()方法指定父Logger。
  • Logger与Handler都有setFilter()方法,可以指定Filter操作对象,如果想让Logger与Handler除了依层级过滤之外,还可以加入额外过滤条件,就可以操作Filter接口:
import java.util.logging;
public interface Filter{
public boolean isLoggable(LogRecord record);
}
  • 可以通过logging.properties来设定Logger组态,启动JVM时,指定java.util.logging.config.file系统属性为.properties名称。

国际化基础

  • 应用程序根据不同地区使用者,呈现不同语言、日期格式等称为本地化(Localization),如果应用程序设计时,可在不修改应用程序情况下,根据不同使用者直接采用不同语言、日期格式等,这样的设计考虑称为国际化(internationalization),简称i18n。
  • 使用ResourceBundle:
  1. 对于日后可能变动的文字信息,可以考虑将信息移至程序之外,使用ResourceBundle来做信息绑定。
  2. .properties文档必须放置在CLASSPATH的路径设定下,文档中撰写的是键/值配对,之后在程序中可以使用键来取得配对。
  3. ResourceBundle的静态getBundle()方法会取得一个ResourceBundle的实例,所给定的自变量名称是信息文档的主文件名,getBundle()会自动找到对应的.properties文档,取得ResourceBundle实例后,可以使用getString()指定键来取得。
  • 使用Locale:
  1. 国际化的三个标准是地区(Locale)信息、资源包(Resource bundle)与基础名称(Base name)。
  2. 地区信息代表了特定的地理、政治或文化区,由一个语言编码(由两个小写字母表示)与可选的地区编码(由两个大写字母表示)来指定。地区信息的对应类是Locale,如Locale locale=new Locale("zh","TW");。
  3. 资源包中包括了特定地区的相关信息。
  4. 代表同一组信息但不同地区的各个资源包会共享相同的基础名称。

规则表示式

  • 规则表示式主要用于字符、字符串格式比较,包含字面意义字符与元字符。字面意义字符是指按照字面意义比较的字符,元字符是不按照字面意义比较,在不同情境有不同意义的字符。元字符就像是程序语言中的控制结构之类的语法,找出并理解元字符想要诠译的概念,对于规则表示式的阅读非常重要。
  • 字面意义字符:
  1. 字母和数字在规则表示式中都是按照字面意义比较的,有些字符之前加上\之后会被当做元字符。
  2. 元字符在规则表示式中有特殊意义,比较标点时可以在每个符号前加上\。
  3. “X或Y”可以用X|Y表示,“X或Y或Z”可以用[XYZ]表示。
  • 字符类:
  1. 规则表示式中,多个字符可以分归在一起,成为一个字符类,会比较文字中是否有任一个字符符合字符类中某个字符。字符类中可以再有字符类。
  2. 归类字符的方式之一是将字符放于[]中。“1到5任意数字出现”表示为[1-5]。
  3. 字符类中可以使用作为字符类元字符,[]则为反字符类。“a、b、c以外的字符”表示为[^abc]。
  • 贪婪、逐步、独吐量词:
  1. {n}是贪婪量词表示法的一种,表示前面的量词出现n次,会尽可能找出长度最长的符合文字。
  2. 在贪婪量词表示法后加上?,将会成为逐步量词,会尽可能找出长度最短的符合文字。
  3. 在贪婪量词表示法后加上+,将会成为独吐量词,没有任何文字符合。
  • 分组与参考:
  1. 可以使用()来将规则表示式分组,除了作为子规则表示式之外,还可以搭配量词使用。
  2. 分组计数是遇到的左括号来计数,如果有个规则表示式((A)(B(C))),其中有四个分组:((A)(B(C)))、(A)、(B(C))、(C)。
  3. 分组回头参考时,是在\后加上分组计数,表示参考第几个分组的比对结果 。\d\d要求比对两个数字,(\d\d)\1的话,表示要输入四个数字,输入的前两个数字与后两个数字必须相同 。
  • 在程序中使用表示式,必须先针对规则表示式做剖析、验证等动作,确定规则表示式语法无误,对字符串进行比较,java.util.regex.Pattern实例是规则表示式在JVM中的代表对象,Pattern的构造函数被标示为private,必须通过Pattern的静态方法compile()来取得。

JDK8 API增强功能

  • String新增join()静态方法可以指定每个字符串间以逗号分隔进行连接。
  • Arrays上新增parallelPrefix()、parallelSetAll()与parallelSort()方法:
  1. parallelPrefix()方法,可以指定xxxBinaryOperator实例,执行类似Stream的reduce()过程。
  2. parallelSetAll()方法,用来对数组进行初始化或全面重设每个索引元素。
  3. parallelSort()方法,可以将指定的数组分为子数组并以平行化方式分别排序,然后再进行合并排序。

教材学习中的问题和解决过程

  • 教材478页倒数第六行代码有错误:书上写的是package java.util.logging;,正确应该是import java.util.logging;,import的作用是导入java.util.logging包的,所以不应该用package。

  • 教材451页NIOUtil.java代码中index.html会自动保存在所建项目的工作目录下,但是打开之后内容好像不太对

    20145215《Java程序设计》第8周学习总结

  • 教材475页HandlerDemo.java代码中的config.log文件放在用户的根目录下,以我的为例(C:\用户\Think),打开后效果如图所示:

    20145215《Java程序设计》第8周学习总结

部分模式字符串:

  1. "/" 本地路径名分隔符
  2. "%t" 系统临时目录
  3. "%h" "user.home" 系统属性的值
  4. "%g" 区分循环日志的生成号
  5. "%u" 解决冲突的惟一号码
  6. "%%" 转换为单个百分数符号"%"

代码调试中的问题和解决过程

  • 以下是我编写的Logger测试代码:
package ch08;
import java.util.logging.*; public class LoggerTest {
public static void main(String[] args) {
Logger log = Logger.getLogger("lavasoft" );
log.setLevel(Level.INFO);
Logger log1 = Logger.getLogger("lavasoft" );
System.out.println(log==log1); //true
Logger log2 = Logger.getLogger("lavasoft.blog" );
log2.setLevel(Level.WARNING); log.info("aaa" );
log2.info("bbb" );
log2.fine("fine" );
}
}

它的输出结果是:

20145215《Java程序设计》第8周学习总结

当把log2.setLevel(Level.WARNING);这一行注释掉之后,发现输出结果变成了:

20145215《Java程序设计》第8周学习总结

这也就更加可以看出logger的名字是有层级关系的。

心得体会

本周虽然学习量相比于前几周不算多,但是我感觉学习的压力却比以往更大,这主要是因为这两章的内容相比之前更不容易理解。有的时候看着看着甚至会觉得有些迷茫,仔细反思了一下自己,或许前面的基础打得还是不够扎实,才让自己现在学起来比较累。经过这两个月以来的学习,我越来越感觉到学习是一个循序渐进的过程,是一个积累的过程,也是考验我们耐心与毅力的最好途径,学习Java是这样,学习其他东西亦是如此。只有将自己的心静下来,才能真正学到知识,否则既花费了时间和精力,到头来还是什么都没得到。

学习进度条

代码行数(新增/累积) 博客量(新增/累积) 学习时间(新增/累积) 重要成长
目标 5000行 30篇 400小时 编写了Hello Java代码
第一周 100/100 2/2 12/12 编写了Hello Java代码
第二周 200/300 2/4 15/27 理解了printf和println的区别
第三周 450/750 1/5 22/49 对对象有了更深层次的理解
第四周 869/1619 1/6 28/77 对对象的三大特征有了更全面的认识
第五周 1123/2742 1/7 25/102 学会了异常处理
第六周 863/3605 2/9 30/132 理解了线程
第七周 505/4110 2/11 28/160 掌握了日期和时间的运用
第八周 490/4600 2/13 26/186 掌握了git托管的节奏

【附1】本周学习的代码已经成功托管,截图如下:

20145215《Java程序设计》第8周学习总结

【附2】利用wc统计代码行数,截图如下:

20145215《Java程序设计》第8周学习总结

参考资料

上一篇:3-2-1-0-GO


下一篇:(转载)OC学习篇之---类的三大特性:封装,继承,多态