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继承架构:
想要取得Channel的操作对象,可以使用Channels类,它定义了静态方法newChannel(),可以让你从InputStream、OutputStream分别建立ReadableByteChannel、WritableByteChannel,有些InputStream、OutputStream实例本身也有方法可以取得Channel实例,例如:FileInputSteam、FileOutputStream都有个getChannel()方法可以分别取得FileChannel实例。
- Buffer继承架构:
- Buffer的直接子类们都有个allocate()静态方法,可以让你指定Buffer容量(Capacity),如果想取得Buffer内部的阵列,可以使用array()方法,如果有个数组想要转为某个Buffer子类实例,每个Buffer子类实例都有wrap()静态方法可以提供操作。
- Buffer是容器,填装的数据不会超过它的容量,容量大小可以使用capacity()方法取得,实际可读取或写入的数据界限(Limit)索引值可以由limit()方法得知或设定,下一个可读取数据的位置(Position)索引值,可以使用position()方法得知或设定。
NIO2文件系统
NIO2文件系统API提供一组标准接口与类,应用程序开发者只要基于这些标准接口与类进行文件系统操作,底层实际如何进行文件系统操作,是由文件系统提供者负责(由厂商操作)。
NIO2文件系统的中心是
java.nio.file.spi.FileSystemProvider
,本身为操作类,是文件系统提供者才要操作的类,作用是产生java.nio.file
与java.nio.file.attribute
中各种抽象类或接口的操作对象:应用程式开发者可以通过java.nio.file套件中FileSystems、Paths、Files等类提供的静态方法,取得相关操作对象或进行各种文件系统操作,这些静态方法内部会运用FileSystemProvider来取得所需的操作对象,完成应有的操作。
通用API
日志
- 日志API简介:
-
java.util.logging
包提供了日志功能相关类与接口,它们是从JDK1.4之后加入标准API,要取得Logger实例,必须使用Logger的静态方法getLogger():
Logger logger = Logger.getLogger("ch08.Main");
- 调用getLogger()时,必须指定Logger实例所属名称空间(Name space),名称空间以"."作为层级区分,名称空间层级相同的Logger,其父Logger组态相同。
- 取得Logger实例之后,可以使用log()方法输出讯息,输出讯息时可以使用Level的静态成员指定讯息层级(Level)。
- 指定日志层级:
- Logger与Handler预设都会先依Level过滤信息,如果没有做任何修改,取得的Logger实例之父Logger组态,就是
Logger.GLOBAL_LOGGER_NAME
名称空间Logger实例的组态,这个实例的Level设定为INFO,可通过Logger实例的getParent()取得父Logger实例,可通过getLevel()取得设定的Level实例。 - 在经过Logger过滤之后,还得再经过Handler的过滤,一个Logger可以拥有多个Handler,可通过Logger的addHandler()新增Handler实例。
- 实际上进行信息输出时,目前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:
- 对于日后可能变动的文字信息,可以考虑将信息移至程序之外,使用ResourceBundle来做信息绑定。
- .properties文档必须放置在CLASSPATH的路径设定下,文档中撰写的是键/值配对,之后在程序中可以使用键来取得配对。
- ResourceBundle的静态getBundle()方法会取得一个ResourceBundle的实例,所给定的自变量名称是信息文档的主文件名,getBundle()会自动找到对应的.properties文档,取得ResourceBundle实例后,可以使用getString()指定键来取得。
- 使用Locale:
- 国际化的三个标准是地区(Locale)信息、资源包(Resource bundle)与基础名称(Base name)。
- 地区信息代表了特定的地理、政治或文化区,由一个语言编码(由两个小写字母表示)与可选的地区编码(由两个大写字母表示)来指定。地区信息的对应类是Locale,如Locale locale=new Locale("zh","TW");。
- 资源包中包括了特定地区的相关信息。
- 代表同一组信息但不同地区的各个资源包会共享相同的基础名称。
规则表示式
- 规则表示式主要用于字符、字符串格式比较,包含字面意义字符与元字符。字面意义字符是指按照字面意义比较的字符,元字符是不按照字面意义比较,在不同情境有不同意义的字符。元字符就像是程序语言中的控制结构之类的语法,找出并理解元字符想要诠译的概念,对于规则表示式的阅读非常重要。
- 字面意义字符:
- 字母和数字在规则表示式中都是按照字面意义比较的,有些字符之前加上\之后会被当做元字符。
- 元字符在规则表示式中有特殊意义,比较标点时可以在每个符号前加上\。
- “X或Y”可以用X|Y表示,“X或Y或Z”可以用[XYZ]表示。
- 字符类:
- 规则表示式中,多个字符可以分归在一起,成为一个字符类,会比较文字中是否有任一个字符符合字符类中某个字符。字符类中可以再有字符类。
- 归类字符的方式之一是将字符放于[]中。“1到5任意数字出现”表示为[1-5]。
- 字符类中可以使用作为字符类元字符,[]则为反字符类。“a、b、c以外的字符”表示为[^abc]。
- 贪婪、逐步、独吐量词:
- {n}是贪婪量词表示法的一种,表示前面的量词出现n次,会尽可能找出长度最长的符合文字。
- 在贪婪量词表示法后加上?,将会成为逐步量词,会尽可能找出长度最短的符合文字。
- 在贪婪量词表示法后加上+,将会成为独吐量词,没有任何文字符合。
- 分组与参考:
- 可以使用()来将规则表示式分组,除了作为子规则表示式之外,还可以搭配量词使用。
- 分组计数是遇到的左括号来计数,如果有个规则表示式((A)(B(C))),其中有四个分组:((A)(B(C)))、(A)、(B(C))、(C)。
- 分组回头参考时,是在\后加上分组计数,表示参考第几个分组的比对结果 。\d\d要求比对两个数字,(\d\d)\1的话,表示要输入四个数字,输入的前两个数字与后两个数字必须相同 。
- 在程序中使用表示式,必须先针对规则表示式做剖析、验证等动作,确定规则表示式语法无误,对字符串进行比较,
java.util.regex.Pattern
实例是规则表示式在JVM中的代表对象,Pattern的构造函数被标示为private,必须通过Pattern的静态方法compile()来取得。
JDK8 API增强功能
- String新增join()静态方法可以指定每个字符串间以逗号分隔进行连接。
- Arrays上新增parallelPrefix()、parallelSetAll()与parallelSort()方法:
- parallelPrefix()方法,可以指定xxxBinaryOperator实例,执行类似Stream的reduce()过程。
- parallelSetAll()方法,用来对数组进行初始化或全面重设每个索引元素。
- parallelSort()方法,可以将指定的数组分为子数组并以平行化方式分别排序,然后再进行合并排序。
教材学习中的问题和解决过程
教材478页倒数第六行代码有错误:书上写的是
package java.util.logging;
,正确应该是import java.util.logging;
,import的作用是导入java.util.logging包的,所以不应该用package。教材451页NIOUtil.java代码中index.html会自动保存在所建项目的工作目录下,但是打开之后内容好像不太对
教材475页HandlerDemo.java代码中的config.log文件放在用户的根目录下,以我的为例(C:\用户\Think),打开后效果如图所示:
部分模式字符串:
- "/" 本地路径名分隔符
- "%t" 系统临时目录
- "%h" "user.home" 系统属性的值
- "%g" 区分循环日志的生成号
- "%u" 解决冲突的惟一号码
- "%%" 转换为单个百分数符号"%"
代码调试中的问题和解决过程
- 以下是我编写的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" );
}
}
它的输出结果是:
当把log2.setLevel(Level.WARNING);
这一行注释掉之后,发现输出结果变成了:
这也就更加可以看出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】本周学习的代码已经成功托管,截图如下:
【附2】利用wc统计代码行数,截图如下: