读书笔记篇-Java核心技术卷二


图怪兽_6dd165f9ad93478283b9e73261991b0b_24895.png

第一章 JavaSe8的流库

流提供了- 种让我们可以在比集合更高的概念级别上指定计算的数据视图。通过使用流,我们可以说明想要完成什么任务,而不是说明如何去实现它。我们将操作的调度留给具体实现去解决。例如,假设我们想要计算某个属性的平均值,那么我们就可以指定数据源和该属性,然后,流库就可以对计算进行优化,例如,使用多线程来计算总和与个数,并将结果合并。

流表面上看起来和集合很类似,都可以让我们转换和获取数据。但是,它们之间存在着显著的差异:

  • 流并不存储其元素。这些元素可能存储在底层的集合中,或者是按需生成的。
  • 流的操作不会修改其数据源。例如,filter 方法不会从新的流中移除元素,而是会生成一个新的流,其中不包含被过滤掉的元素。
  • 流的操作是尽可能惰性执行的。这意味着直至需要其结果时,操作才会执行。例如,如果我们只想查找前5个长单词而不是所有长单词,那么filter方法就会在匹配到第5个单词后停止过滤。因此,我们甚至可以操作无限流。

流的创建

  • collection接口下的所有实现类自带

image.png

  • 数组通过java.util.stream Stream类静态方法进行创建流
    1
    2
    3
    public static<T> Stream<T> of(T t) {
    return StreamSupport.stream(new Streams.StreamBuilderImpl<>(t), false);
    }

filter map flatMap方法

  • filter转换会产生-一个流,它的元素与某种条件相匹配。下面,我们将一个字符串流转换,f11ter的引元是Predicate即从T到boolean的函数。
  • 在使用map时,会有一个函数应用到每个元素上,并且其结果是包含了应用该函数后所产生的所有结果的流。
  • 产生一个流,它是通过将mapper应用于当前流中所有元素所产生的结果连接到一起而获得的。(注意,这里的每个结果都是一个流。)

抽取子流和连接流

调用stream.limit(n)会返回一个新的流,它在n个元素之后结束(如果原来的流更短,那么就会在流结束时结束)。这个方法对于裁剪无限流的尺寸会显得特别有用。例如,

1
Strear<Double> randoms = Stream.generate(Math::random).limit(100);

会产生一个包含100个随机数的流。调用stream.skip(n)正好相反:它会丢弃前n个元素。这个方法在将文本分隔为单词时会显得很方便,因为按照split方法的工作方式,第一个元素是没什么用的空字符串。

其他的流转换

distinct方法会返回一个流,它的元素是从原有流中产生的,即原来的元素按照同样的顺序剔除重复元素后产生的。这个流显然能够记住它已经看到过的元素。

Option

Optional对象是一种包装器对象, 要么包装了类型T的对象,要么没有包装任何对象。对于第一种情况,我们称这种值为存在的。Optional 类型被当作-种更安全的方式,用来替代类型T的引用,这种引用要么引用某个对象,要么为nu11。但是,它只有在正确使用的情况下才会更安全。

如何使用Optional值,有效地使用Optional的关键是要使用这样的方法:

它在值不存在的情况下会产生-个可替代物,而只有在值存在的情况下才会使用这个值。

1
String result = optionlString.orElse("");// The wrapped string, or”if none

你还可以调用代码来计算默认值:

1
2
String result = optionlString.orElseGet(()->Local.getDefault().getDisplayName());
// The function is only called when needed

或者可以在没有任何值时抛出异常:

1
2
String result = optionlString.orElseGet(IllegalStateExection::new);
//Supply a nethod that yields an exception objet

刚刚看到了如何在不存在任何值的情况下产生相应的替代物。另一条使用可选值的策略是只有在其存在的情况下才消费该值。ifPresent方法会接受-个函数。如果该可选值存在,那么它会被传递给该函数。否则,不会发生任何事情。

1
2
3
4
5
6
7
optionalValue.ifPresent(v -> Process );
//例如,如果在该值存在的情况下想要将其添加到某个集中,那么就可以调用
optionalValue.ifPresent(v -> results. add());
//或者直接调用
optionalValue.ifPresent(results:add);
//当调用ifPresent时,从该函数不会返回任何值。如果想要处理函数的结果,应该使用map
Optional<Boolean> added = optionalValue.map(results::add);

约简操作

reduce方法是-种用于从流中计算某个值的通用机制,其最简单的形式将接受-个二元函数,并从前两个元素开始持续应用它。如果该函数是求和函数,那么就很容易解释这种机制:

1
2
List<Integer> values = ... ;
Optional<Integer> sun = values.stream.reduce((x, y)→x+y);

在上面的情况中,reduce方法会计算v0+v1+v2+..其中v是流中的元素。如果流为空,那么该方法会返回一个optional,因为没有任何有效的结果。

警告:不要修改在执行某项流操作后会将元素返回到流中的集合(即使这种修改是线程安全的)。记住,流并不会收集它们的数据,数据总是在单独的集合中。如果修改了这样的集合,那么流操作的结果就是未定义的。JDK文档对这项需求并未做出任何约束,并且对顺序流和并行流都采用了这种处理方式。更准确地讲,因为中间的流操作都是惰性的,所以直到执行终结操作时才对集合进行修改仍旧是可行的。例如,下面的操作尽管并不推荐,但是仍旧可以工作.

为了让并行流正常工作,需要满足大量的条件:

  • 数据应该在内存中。必须等到数据到达是非常低效的。
  • 流应该可以被高效地分成若干个子部分。由数组或平衡二叉树支撑的流都可以工作得很好,但是Stream. iterate返回的结果不行。
  • 流操作的工作量应该具有较大的规模。如果总工作负载并不是很大,那么搭建并行计算时所付出的代价就没有什么意义。
  • 流操作不应该被阻塞。

    换句话说,不要将所有的流都转换为并行流。只有在对已经位于内存中的数据执行大量计算操作时,才应该使用并行流。

第二章 输入输出

输入/输出流

在Java API中,可以从其中读入一个字节序列的对象称做输入流,而可以向其中写人一个字节序列的对象称做输出流。这些字节序列的来源地和目的地可以是文件,而且通常都是文件,但是也可以是网络连接,甚至是内存块。抽象类InputStream和outputstream构成了输人/输出(I/O)类层次结构的基础。

流家族

0_1317892654T848.gif
0_1317893033Samy.gif

Reader和Writer的层次结构

0_1317892158nZfc.gif

注意: java. io.Closeab1e接口扩展了java.lang.AutoCloseable接口。因此,对任何Closcable进行操作时,都可以使用try-with-resource 语句( try-with-resource语句是指声明了一个或多个资源的try语句)。 为什么要有两个接口呢?因为Closeable接口的close方法只抛出IOException,而AutoCloseable.close方法可以抛出任何异常。

未命名文件.png

如何读入文本

最简单的处理任意文本的方式就是使用构建Scanner对象。或者,我们也可以将短小的文本文件像下面这样读人到一个字符串中:

1
String content = new String(Files.readAllBytes(path),charset);

但是,如果想要将这个文件-行行地读入, 那么可以调用:

1
List<String> lines = Files.readAll(paht,charset);

如果文件太大,那么可以将行惰性处理为一个Stream对象:

1
try (Stream<String> lines = Files.lines(path, charset);

随机访问文件

RandomAccessFile类可以在文件中的任何位置查找或写人数据。磁盘文件都是随机访问的,但是与网络套接字通信的输人1输出流却不是。你可以打开一个随机访问文件,只用于读人或者同时用于读写,你可以通过使用字符串“r” (用于读入访问)或“rw” (用于读人1写出访问)作为构造器的第二个参数来指定这个选项。

1
2
RandomAccessFile in = RandomAccessFile("employee.bat", "r");
RandomAccessFile inOut = new RandomAccessFile("employee.dat", "rw");

ZIP 文档

ZIP文档(通常)以压缩格式存储了一个或多个文件,每个ZIP文档都有一个头,包含诸如每个文件名字和所使用的压缩方法等信息。在Java中,可以使用Z1pInputStream来读人ZIP文档。你可能需要浏览文档中每个单独的项,getNextEntry 方法就可以返回一个描述这些项的ZipEntry类型的对象。向ZipInputStre am的getInputstream方法传递该项可以获取用于读取该项的输入流。然后调用closeEntry来读入下一项。

1
2
3
4
5
6
7
ZipInputStream zip = new ZipInputStream(new ZipInputStream(zipname));
ZipEmpty zipEmpty;
while((zipEmpty.getNextEntry())!=null){
InputStream input = zip.getInputStream(zipEmpty);
//read the context of in
zipEmpty.close();
}

文件加锁机制

考虑一下多个同时执行的程序需要修改同一个文件的情形,很明显,这些程序需要以某种方式进行通信,不然这个文件很容易被损坏。文件锁可以解决这个问题,它可以控制对文件或文件中某个范围的字节的访问。

假设你的应用程序将用户的偏好存储在;一个配置文件中,当用户调用这个应用的两个实例时,这两个实例就有可能会同时希望写这个配置文件。在这种情况下,第一个实例应该锁定这个文件,当第二个实例发现这个文件被锁定时,它必须决策是等待直至这个文件解锁,还是直接跳过这个写操作过程。
要锁定一个文件,可以调用FileChannel类的lock或tryLock方法:

1
2
FileChannel fileChannel = FileChannel.open(Paths.get("TestFileLock.java"));
fileChannel.lock();

1
FileLock fileLock = fileChannel.lock();

第一个调用会阻塞直至可获得锁,而第二个调用将立即返回,要么返回锁,要么在锁不可获得的情况下返回null。这个文件将保持锁定状态,直至这个通道关闭,或者在锁上调用了release方法。你还可以通过下面的调用锁定文件的-部分:

1
FileLock lock(long start, long size, boolean shared)

1
FileLock tryLock(long start, long size, boolean shared)

如果shared标志为false,则锁定文件的目的是读写,而如果为true,则这是一个共享锁,它允许多个进程从文件中读入,并阻止任何进程获得独占的锁。并非所有的操作系统都支持共享锁,因此你可能会在请求共享锁的时候得到的是独占的锁。调用FfileLock类的isShared方法可以查询你所持有的锁的类型。

注意:如果你锁定了文件的尾部,而这个文件的长度随后增长超过了锁定的部分,那么增长出来的额外区域是未锁定的,要想锁定所有的字节,可以使用Long.MAX _VALUE来表示尺寸。

正则表达式

正则表达式( regular expression)用于指定字符串的模式,你可以在任何需要定位匹配某种特定模式的字符串的情况下使用正则表达式。例如,我们有-个示例程序就是用来定位HTML文件中的所有超链接的,它是通过查找 模式的字符串来实现此目的的。

当然,在指定模式时,… 标记法并不够精确。你需要精确地指定什么样的字符序列才是合法的匹配,这就要求无论何时,当你要描述-一个模式时,都需要使用某种特定的语法。下面是一个简单的示例,正则表达式

1
[j]awa.+t

匹配下列形式的所有字符串:

  • 第一个字母是J或j。
  • 接下来的三个字母是ava
  • 字符串的其余部分由一个或多个任意的字符构成。例如,字符串”Javanese”就匹配这个特定的正则表达式,但是字符串“core Java’就不匹配。正如你所见,你需要了解-点这种语法,以理解正则表达式的含义。幸运的是,对于大多數情况,一小部分很直观的语法结构就足够用了。
  • 字符类(charater cass)是一个括在括号中的可选择的字符集,例如,[Jj]、 [0-9]、[A-Za-z]或[^0-9]。这里“_”表示是-一个范围(所有Unicode值落在两个边界范围之内的字符),而^表示补集(除了指定字符之外的所有字符)。
  • 如果字符类中包含“-”,那么它必须是第一-项或最后-项; 如果要包含“[“,那么它必须是第一_项; 如果要包含“A”,那么它可以是除开始位置之外的任何位置。其中,你只需要转义“[”和“\”。
  • 有许多预定的字符类,例如\d (数字)和\plSc) ( Unicode货币符号)。

元字符

元字符 正则表达式的写法 意义
. “.” 任意一个字符
^ “^Spring” 以Spring开始的字符
$ “EEEE$” 匹配行结束符,以”EEEE”结束的字符
\d “\d” 0-9的任何一个数字
\D “\D” 任何一个非数字字符
\s “\s” 空白字符,如”\t”,”\n”
\S “\S” 非空白字符
\w “\w” 可用作标识符的字符,但不包括“$”
\W “\W” 不可用作标识符的字符
\p{Lower} \p{Lower} 小定字母a-z
\p{Upper} \p{Upper} 大写字母A-Z
\p{ASCII} \p{ASCII} ASCII字符
\p{Alpha} \p{Alpha} 字母字符
\p{Digit} \p{Digit} 十进制数字,0-9
\p{Alnum} \p{Alnum} 数字或字母字符
\p{Punct} \p{Punct} 标点符号:!“#%&()*=-
\p{Graph} \p{Graph} 可见字符:[\p{Alnum}\p{Punct}]
\p{Print} \p{Print} 可打印字符[\p{Graph}\x20]
\p{Blank} \p{Blank} 空格或制表符:[\t]
\p{Cntrl} \p{Cntrl} 控制字符:[\x00-\x1F\x7F]

方括号

方括号括起来若干个字符来表示一个元字符,该元字符可代表方括号中的任何一个字符。reg=“[abc]4”—– 则”a4”,”b4”,”c4”,都是和此正则表达式匹配的字符串

方括号例子 意义
[^456] 代表4,5,6之外的任何字符
[a-r] 代表a~r中的任何一个字母
[a-Za-Z] 代表任意一个英文字母
[a-e[g-z]] 代表a-e或者g-z中的任意一个字母
[a-o&&[def]] 代表字母d,e,f(交运算)
[a-o&&[^bc]] 代表字母a,d(差运算)

小括号

将 () 之间括起来的表达式定义为“组”(group),并且将匹配这个表达式的字符保存到一个临时区域,这个元字符在字符串提取的时候非常有用。

限定修饰符

使用限定修饰符来限定元字符出现的次数

限定修饰符 意义 示例
? 0次或1次 A?
* 0次或多次 A*
+ 1次或多次 A+
{n} 正好出现n次 A{2}
{n,} 至少出现n次 A{2,}
{n,m} 出现n到m次 A{2,6}

第三章 XML

eXtensible Markup Language 可扩展标记语言

解析XML文档

  • dom解析

    灵活性高,可操作性强,把数据全部载入内存进行操作,无法解析超大的XML文件。

  • sax解析

    按行进行解析,效率高,无需担心内存不够问题。

XML Schema

XML Schema 的作用是定义 XML 文档的合法构建模块,类似 DTD。

  • 定义可出现在文档中的元素
  • 定义可出现在文档中的属性
  • 定义哪个元素是子元素
  • 定义子元素的次序
  • 定义子元素的数目
  • 定义元素是否为空,或者是否可包含文本
  • 定义元素和属性的数据类型
  • 定义元素和属性的默认值以及固定值

第四章 网络

Telnet工具

语法

1
telnet [-8acdEfFKLrx][-b<主机别名>][-e<脱离字符>][-k<域名>][-l<用户名称>][-n<记录文件>][-S<服务类型>][-X<认证形态>][主机名称或IP地址<通信端口>]

参数说明

  • -8 允许使用8位字符资料,包括输入与输出。
  • -a 尝试自动登入远端系统。
  • -b<主机别名> 使用别名指定远端主机名称。
  • -c 不读取用户专属目录里的.telnetrc文件。
  • -d 启动排错模式。
  • -e<脱离字符> 设置脱离字符。
  • -E 滤除脱离字符。
  • -f 此参数的效果和指定”-F”参数相同。
  • -F 使用Kerberos V5认证时,加上此参数可把本地主机的认证数据上传到远端主机。
  • -k<域名> 使用Kerberos认证时,加上此参数让远端主机采用指定的领域名,而非该主机的域名。
  • -K 不自动登入远端主机。
  • -l<用户名称> 指定要登入远端主机的用户名称。
  • -L 允许输出8位字符资料。
  • -n<记录文件> 指定文件记录相关信息。
  • -r 使用类似rlogin指令的用户界面。
  • -S<服务类型> 设置telnet连线所需的IP TOS信息。
  • -x 假设主机有支持数据加密的功能,就使用它。
  • -X<认证形态> 关闭指定的认证形态。

第五章 数据库编程

JDBC的设计

从一开始,Java 技术开发人员就意识到了Java在数据库应用方面的巨大潜力。从1995年开始,他们就致力于扩展Java标准类库,使之可以运用SQL访问数据库。他们最初希望通过扩展Java,就可以让人们“纯”用Java语言与任何数据库进行通信。但是,他们很快发现这是一项无法完成的任务:因为业界存在许多不同的数据库,且它们所使用的协议也各不相同。尽管很多数据库供应商都表示支持Java提供-套数据库访问的标准网络协议,但是每一家 企业都希望Java能采用自己的网络协议。所有的数据库供应商和工具开发商都认为,如果Java能够为SQL访问提供一套“纯” Java API,同时提供-个驱动管理器,以允许第三方驱动程序可以连接到特定的数据库,那它就会显得非常有用。这样,数据库供应商就可以提供自已的驱动程序,将其插人到驱动管理器中。这将成为-种向驱动管理器注册第三方驱动程序的简单机制。

这种接口组织方式遵循了微软公司非常成功的ODBC模式,ODBC 为C语言访问数据库提供了一套编程接口。JDBC和ODBC都基于同一个思想:根据API编写的程序都可以与驱动管理器进行通信,而驱动管理器则通过驱动程序与实际的数据库进行通信。所有这些都意味着JIDBC API是大部分程序员不得不使用的接口。

第六章日 期和时间API

光阴似箭,我们可以很容易地设置一个起点,然后向前和向后以秒来计时。那为什么处理时间会如此之难呢?问题出在人类自身上。如果我们只需告诉对方:“1523793600 时来见我,别迟到!”那么一切都会很简单。但是我们希望时间能够和朝夕与季节挂钩,这就使事情变得复杂了。Java 1.0有一个Date类,事后证明它过于简单了,当Java 1.1引入Calendar类之后,Date类中的大部分方法就被弃用了。但是,Calendar 的API还不够给力,它的实例是易变的,并且它没有处理诸如闰秒这样的问题。第3次升级很吸引人,那就是Java SE 8中引入的java. time API,它修正了过去的缺陷,并且应该会服役相当长的- -段时间。在本章中,你将会学习是什么使时间计算变得如此烦人,以及日期和时间API是如何解决这些问题的。

时间线

1
2
3
4
5
6
Instant start = Instant.now();
Thread.sleep(1000);
Instant end = Instant.now();
Duration between = Duration.between(start, end);
long l = between.toMillis();
System.out.println(l);

本地时间

1
2
3
LocalDate now = LocalDate.now();
LocalDate of = LocalDate.of(1999, 02, 15);
LocalDate date = LocalDate.of(1998, Month.FEBRUARY, 10);

第七章国际化

多数程序员相信将他们的程序进行国际化需要做的所有事情就是支持Unicode并在用户接口中对消息进行翻译。但是,在本章你将会看到,国际化一个程序所要做的事情绝不仅仅是提供Unicode支持。在世界的不同地方,日期、时间、货币甚至数字的格式都不相同。你需要用一种简单的方法来为不同的语言配置菜单与按钮的名字、消息字符串和快捷键。

Locale 对象

一旦有了一个Locale,你能用它做什么呢?答案是它所能做的事情很有限。Locale类中唯- -有用的是那些识别语言和国家代码的方法,其中最重要的一个是getdisplayName,它返回一个描述Locale的字符串。这个字符串并不包含前面所说的由两个字母组成的代码,而是以一种面向用户的形式来表现,比如 中文 (中国)

相关方法

1
2
3
4
5
String country = defaultLocale.getCountry();//返回国家地区代码
String language = defaultLocale.getLanguage();//返回国家的语言
String displayCountry = defaultLocale.getDisplayCountry();//返回适合向用户显示的国家信息
String displayLanaguage = defaultLocale.getDisplayLanaguage();//返回适合向用户展示的语言信息
String displayName = defaultLocale.getDisplayName();//返回适合向用户展示的语言环境名

获取当前本地对象

1
2
3
4
5
6
7
public class TestLocale {
public static void main(String[] args) throws Exception{
Locale aDefault = Locale.getDefault();
String displayName = aDefault.getDisplayName();
System.out.println(displayName);//中文 (中国)
}
}

如果需要改变地域查看效果

1
2
3
javac TestLocale.java -d .
java -Duser.language=de -Duser.region=CH com.example.annotation.controller.TestLocale
//输出 Deutsch (Schweiz)

数字格式

1
2
3
4
5
Locale loc = Locale.GERMAN;
NumberFormat format = NumberFormat.getCurrencyInstance(loc);
double amt = 12134.23;
String s = format.format(amt);
System.out.println(s);//¤ 12.134,23

货币

1
2
3
4
5
6
NumberFormat dollarInstance = NumberFormat.getCurrencyInstance(Locale.US);
NumberFormat euroInstance = NumberFormat.getCurrencyInstance(Locale.GERMANY);
String res1 = dollarInstance.format(2000);
String res2 = euroInstance.format(2000);
System.out.println(res1);//$2,000.00
System.out.println(res2);//2.000,00 €

日期和时间

当格式化日期和时间时,需要考虑4个与Locale相关的问题:

  • 月份和星期应该用本地语言来表示。
  • 年月日的顺序要符合本地习惯。
  • 公历可能不是本地首选的日期表示方法。.
  • 必须要考虑本地的时区。

image.png

1
2
3
4
5
6
7
FormatStyle medium = FormatStyle.MEDIUM;
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofLocalizedDate(medium);
DateTimeFormatter dateFormatter = DateTimeFormatter.ofLocalizedDate(medium);
DateTimeFormatter timeFormatter = DateTimeFormatter.ofLocalizedTime(medium);
//with locale
Locale china = Locale.CHINA;
DateTimeFormatter chinaDateTimeFormatter = DateTimeFormatter.ofLocalizedDateTime(medium).withLocale(china);

排序和范化

资源包

文本文件和字符集

消息格式化

Java类库中有-个MessageFormat类,它与用printf方法进行格式化很类似,但是它支持Locale,并且会对数字和日期进行格式化。

第八章脚本、 编译与注解处理.

脚本语言是一种通过在运行时解释程序文本,从而避免使用通常的编辑/编译/链接/运行循环的语言。脚本语言有许多优势:

  • 便于快速变更,鼓励不断试验。
  • 可以修改运行着的程序的行为。
  • 支持程序用户的定制化。

另-方面,大多数脚本语言都缺乏可以使编写复杂应用受益的特性,例如强类型、封装和模块化。

脚本引擎

image.png

1
2
3
4
5
6
7
8
public class Engine {
public static void main(String[] args) throws Exception{
ScriptEngineManager scriptEngineManager = new ScriptEngineManager();
ScriptEngine nashorn = scriptEngineManager.getEngineByName("nashorn");
Object eval = nashorn.eval("n=1999");
System.out.println(eval);//1999
}
}

编译器API

有许多工具都需要调用Java编译器,例如:

  • 开发环境。
  • Java教学和辅导程序。
  • 自动化的构建和测试工具。
  • 处理Java代码段的模板工具,例如JavaServer Pages (JSP)。

在过去,应用程序是通过在jdk/1ib/toos.jar类库中未归档的类调用Java编译器的。如今-个用于编译的公共API成为Java平台的一部分,并且它再也不需要使用tools.jar了。

1
2
3
4
5
6
7
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();

OutputStream outStream = ...;
OutputStream errStream = ...;

int code = compiler.run(null, outStream, errStream, "-sourcepath", "src", "Test.java");
//if return 0 instance of success

动态Java代码生成

在用于动态Web页面的JSP技术中,可以在HTML中混杂Java代码,例如:

1
<p>The current date and time is <b><k= new java.uti1,Date(); %></b></p>

JSP引擎动态地将Java代码编译到Servlet中。

使用注解

注解是那些插入到源代码中使用其他工具可以对其进行处理的标签。这些工具可以在源码层次上进行操作,或者可以处理编译器在其中放置了注解的类文件。

注解不会改变程序的编译方式。Java 编译器对于包含注解和不包含注解的代码会生成相
同的虚拟机指令。为了能够受益于注解,你需要选择-个处理工具,然后向你的处理工具可以理解的代码中插入注解,之后运用该处理工具处理代码。注解的使用范围还是很广泛的,并且这种广泛性让人乍一看会觉得有些杂乱无章。下面是关于注解的一些可能的用法:

  • 附属文件的自动生成,例如部署描述符或者bean信息类。
  • 测试、日志、事务语义等代码的自动生成。

注解简介

我们首先介绍基本概念,然后将这些概念运用到一个具体示例中:我们将某些方法标注为AWT构件的事件监听器,然后向你展示一个能够分析注解和连接监听器的注解处理器。然后,我们对其语法规则进行详细讨论。最后我们以两个注解处理的高级示例结束本章。其中一个可以处理源代码级别的注解。另外一个使用了Apache的字节码工程类库,可以向注解过的方法中添加额外的字节码。

第九章安全

Java技术提供了以下三种确保安全的机制:

  • 语言设计特性(对数组的边界进行检查,无不受检查的类型转换,无指针算法等)。
  • 访问控制机制,用于控制代码能够执行的操作(比如文件访问,网络访向等)。
  • 代码签名,利用该特性,代码的作者就能够用标准的加密算法来认证Java代码。这样,该代码的使用者就能够准确地知道谁创建了该代码,以及代码被标识后是否被修改过。

类加载器

第十章高级Swing

第十一章高级AWT

第十二章本地方法


读书笔记篇-Java核心技术卷二
https://mikeygithub.github.io/2021/05/18/yuque/读书笔记-Java核心技术卷二/
作者
Mikey
发布于
2021年5月18日
许可协议