The Java Programming Language (Four Edition)
第一章 快速浏览
第二章 类与对象
第三章 类的扩展
第四章 接口
第五章 嵌套类和接口
第六章 枚举类型
第七章 语言符号、值和变量
第八章 包装器类
第九章 运算符和表达式
第十章 控制流
第十一章 泛型类型
第十二章 异常和断言
第十三章 字符串与正则表达式
第十四章 线程
第十五章 注解
第十六章 反射
第十七章 垃圾回收器与内存
第十八章 包
第十九章 文档注释
第二十章 I/O 包
第二十一章 集合
第二十二章 各种常用工具
第二十三章 系统编程
第二十四章 国际化和本地化
第二十五章 标准包
第一章 快速浏览
第二章 类与对象
2.1、静态字段
将字段声明为 static(静态字段),会使该字段只有一个实例,并且该实例能被该类的所有对象共享。
而且无论创建了该类的多少个实例,这个字段的都只有一份(内存中唯一?)。
2.2、初始化块
字段另一种更复杂的初始化方式就是使用初始块。
初始块是一段出现在类声明中的语句块,它位于所有成员、构造器以及声明之外,它可以初始化对象的字段。
它们会被优先执行,就像它们放置在类的每一个构造器的开头那样,如果有多个块,它们会按照在类中出现的先后顺序执行。
只有当类的所有构造器都声明会抛出某个检查型异常时,它的初始块才可以抛出该异常。
应该审慎的使用初始块,用它们来表示那些构造器不能单独轻易完成的工作。
例如
public long idNum;
private static long nextID = 0;
{
idNum = nextID++;
}
2.3、静态初始化
静态初始块被声明为 static,只能引用类的静态成员,不能抛出任何检查型异常。
类内部的初始化顺序是从前到后的,即代码中的“字段初始化器”或“初始块”都是按照从头到尾的顺序执行。
例如:要确保静态字段 kk 在被访问前已初始化。
static int kk = 9;
static{
System.out.println("1");
}
static{
System.out.println(kk);
}
2.4、参数值
准确的讲,Java 只有一种参数传递方式,即值传递,这样有利于保持简单,传递的也只是这个值的副本。
2.5、使用方法来控制访问
控制内部数据访问的方法有时被称为访问方法(accessor method),使用它们可以增强类数据的封装性。
第三章 类的扩展
3.1、扩展类的构造器
扩展类的构造器必须通过隐式或显式地调用其超类的构造器,将继承而来的字段的构造工作委托给超类。
构造器的顺序依赖:
1、调用其超类的构造器(同样 3步顺序递归调用到 Object);
2、用这些字段的初始器和初始化块来初始化它们;
3、执行构造体。
3.2、继承与重定义成员
● 重载(overloading):提供多个具有相同名字的方法,但是它们拥有可以彼此区分开的不同签名。
签名是由方法名及其参数的类型和数量组成的,它并不包含返回类型或者抛出的异常列表,并且我们无法基于这些因素来重载方法。
不同重载的方法可以有不同类型的返回值。
● 重写(overriding):将方法的超类实现替换为自己的实现。其签名必须相同,但是他们的返回类型可以按照某种方式变换。
1、签名(方法名、参数类型和个数)必须相同。
2、返回类型:引用类型的可以返回超类所声明的子类(is a 关系),基本类型的返回必须相同。
3、访问修饰符:可以改变,但只能提供更多的访问权限,否则违反契约(不能替代超类实现)。
4、可以改变其他方法修饰符(synchronized、native和strictfp),static 则是必须保持一致,final 可以修饰重写的方法(超类被重写的方法则不能是 final)。
5、throws 子句:重写 throws 子句可以少于超类所列出的数量、或者更为具体,还可以没有任何 throws 子句。但是不能比超类的范围广(必须是多态兼容的)。
6、私有的方法:对于超类的 private 方法,子类没有重写,对私有方法的调用总会调用当前类中所声明的方法实现。
例如:
public static void name() throws IndexOutOfBoundsException {....}
正确的重写: public static void name() {....}
错误的重写: public static void name() throws ClassNotFoundException {....}
3.3、兼容类型的实现
● 向上转型:向继承结构高端转换,其线程是安全的。
● 向下转型:向继承结构低端转换,非安全线程。
instanceof 操作符检测一个对象所输入的类,返回值为 false/true,常用语安全的将一个引用向下转型。
例:if (sref instanceof More)
mref = (More) sref;
第四章 接口
4.1、抽象类与抽象方法
通过使用抽象类,我们可以声明只定义了部分实现的类,而让扩展类去提供其部分方法或者全部方法的具体实现。
任何拥有 abstract 方法的类都必须声明为 abstract。
不能创建抽象类的对象,应为某些可能会被调用的方法没有任何有效的实现。
不能是 static 因为 static 不能是 abstract 的。
4.2、接口
接口是纯设计的表现形式,而类则是设计和实现的混合物。
接口类型的引用只能访问接口成员。 Exam xxx = new ImplExam();
● 接口常量:隐式 public final static,必须有初始器(值)。
● 接口方法:隐式 public abstract,除注解外不允许有其他方法修饰符(如 synchronized、native和strictfp 等用于定义实现细节的修饰符);不能是 final 因为它还没有被实现;不能是 static 因为 static 不能是 abstract 的。
4.3、扩展接口
可以使用 extends 扩展接口(接口继承接口),接口支持多重继承其他接口。
public interface C extends A, B {....}
● 继承和隐藏常量
1、新声明常量都将隐藏继承而来的常量。
2、继承而来的常量可以使用该常量完全限定名访问(X.val,常见引用 static 成员形式)。
3、一个接口继承了两个或者多个名字相同常量,那么该常量所有简单引用都具有二义性,会导致编译出错。所以,必须显式使用接口常量,例如 X.val 和 Y.val 等等。
4、在继承一个类的同时还实现了多个接口,也可能会遇到二义性的问题。
● 继承、覆盖和重载方法
1、一个接口同时实现了两个签名相同的方法,只会有一个这样的方法存在,不会有二义性。
2、遇到签名相同,返回不同的方法,除非某些返回的类型是其他的子类型,否则不同的返回会产生二义性。
4.4、接口的应用
任何一个希望被继承的主要类,不管是否抽象类,都应该是一个接口的实现。(同样是 is a 的关系,继承只能扩展一个,但是接口能实现多个。)
第六章 枚举类型
枚举是一个特殊的类,它要表示的每一个具名常量都有与之对应的预定义实例。
第七章 语言符号、值和变量
第八章 包装器类
8.1、包装器类构造
每一个包装器为它所指的基本类型定义一个不可改变对象。
例如: new Integer(1); 创建对象,他的值始终保持1,不可改变其值。
8.2、装/拆箱转换
Integer val = 3;
int x = val.intValue();
装/拆箱转换需分配一个包装器类实例,这样会消耗内存。
由于包装器类是不可变的,所以两个拥有相同值的对象在一定范围是可以互换使用的(数值型为 -128~127)。
例如:
public static boolean sameArgs(Integer a, Integer b) {
return a == b; //判断是否对象相同
}
true sameArgs(12, 12);
false sameArgs(12, new Integer(12));
false sameArgs(1280, 1280);
第九章 运算符和表达式
9.1、类型转换
● 隐式拓宽:将整形转换为浮点型,反之则不可以,整形转换浮点型不会产生精度丢失现象。
长整型 long --> (隐式转换)float --> (强制转换)long 也是可行的,但会丢失精度。
● 显式强制类型转换
1、浮点数转换为整数时,小数点后的部分会舍去。
2、double 强制转换 float 可能会发生:a.精度丢失;b.得到0;c.或者大于 float 型范围,得到无穷大。
3、整数间转换,通过舍去高位,从而有可能被动改变符号。
9.2、运算符优先级
用括号将运算符括起来,可提高可读性并确保正确的优先顺序。
第十章 控制流
10.1、增强的 for 语句
for(Type loop-variable : set-expression)
statement
● 对于数组:使用 for-each 的优点在于不必使用数组下标,缺点是只能查看,不能修改数组元素。
● 对于元素集合:迭代的元素提供简答语法结构,但不能同时迭代多个集合。
● 不是同步的,非线程安全的。
10.2、标号
label: statement
利用标号命名语句,在配合 break lable; 和 continue lable; 将控制流转到严格受限的地方。
第十一章 泛型类型
第十二章 异常和断言
12.1、断言(assertion)
用来检查永远都应该是 true 的情况,如果发现断言是 false,就会抛出异常。添加测试断言可以为我们提供一种在程序中的错误引发奇怪后果前捕获它们的途径。
语法: assert eval-expr [: detail-expr]
在断言被关闭时(默认情况下),断言是不被计算了。所以 :
assert ++i < max; ++i 块有可能不会起作用,建议分开写。
断言应该用来测试从来不发生的情况,因为断言会抛出 Error 而不是 Exception。然而,使断言成为必须会使代码的运行环境变得复杂,因为当前断言的打开及关闭间反复操作会带来复杂性,所以不建议使用。
第十三章 字符串与正则表达式
13.1、正则表达式的匹配
java.util.regex 包提供 正则表达式(regular expression)用于“检测字符串是否和某种模式匹配”或者“挑选字符串中各个部分”。
● 重用已编译的模式:执行匹配所涉及的所有状态都驻留在匹配器中,所以多个匹配器可以共享同一模式。
Pattern p = Pattern.compile(regularExpression); //将给定的正则表达式编译到模式中
Matcher m = p.matcher(sequence); //利用输入序列从模式创建匹配器
boolean b = m.matches(); //尝试将整个输入序列与该模式匹配
● 仅使用一次正则表达式
boolean b = Pattern.matches(regex, sequence); //编译给定正则表达式并尝试将给定输入与其匹配
13.2、正则表达式的替换
通过调用模式的 matcher 方法从模式创建匹配器。创建匹配器后,可以使用它执行三种不同的匹配操作:
● matches 方法尝试将整个输入序列与该模式匹配。
● lookingAt 尝试将输入序列从头开始与该模式匹配。
● find 方法扫描输入序列以查找与该模式匹配的下一个子序列。
每个方法都返回一个表示成功或失败的布尔值。通过查询匹配器的状态可以获取关于成功匹配的更多信息。
ps.正则表达式本身复杂,只有在确实需要的情况下才使用它。在使用的时候,尽量确保模式的清晰度。
第十四章 线程
在计算机中每次执行一步的操作序列被称为线程(thread),而这种单线程编程模型正是大多数程序员所使用的模型。线程可以独立于其他线程执行一项任务,线程间也可以共享访问对象。当两个线程在修改同一块数据时,如果它们是会对数据造成破坏的交叉方式去执行,那么就会出现竞争危机,解决的方法是利用一个锁和对象关联起来,这个锁可以告知该对象是否正在被使用。
14.1、创建线程
● 扩展 Thread 类来创建新的线程,重写 run 方法。
class PrimeThread extends Thread {
long minPrime;
PrimeThread(long minPrime) {
this.minPrime = minPrime;
}
public void run() {
// compute primes larger than minPrime
. . .
}
}
下列代码会创建并启动一个线程:
PrimeThread p = new PrimeThread(143); //创建该类实例
p.start(); //启动线程,Java 虚拟机调用该线程的 run 方法。
● 声明实现 Runnable 接口的类。该类然后实现 run 方法。
class PrimeRun implements Runnable {
long minPrime;
PrimeRun(long minPrime) {
this.minPrime = minPrime;
}
public void run() {
// compute primes larger than minPrime
. . .
}
}
下列代码会创建并启动一个线程:
PrimeRun p = new PrimeRun(143); //分配该类对象
new Thread(p).start(); //利用对象构造一个新的线程,并启动
ps.每个线程都有一个标识名,多个线程可以同名。如果线程创建时没有指定标识名,就会为其生成一个新名称。
14.2、隐藏线程的 run 方法
run 方法是公共的,为了防止客户端私自调用该方法,我们可以不起实现 Runable 接口,而是定义一个内部的 Runable 对象,确保不被滥用。例如:
public class Ping2 {
public Ping2() {
Runnable service = new Runnable(){
public void run() {
//......
}
} ;
new Thread(service).start();
}
}
14.3、线程的结束
有三种情况可使线程终止:
● run 方法正常返回(常规方式)
● run 方法意外结束
● 应用程序终止
Thread.currentThread().interrupt(); 向线程发送中断:中断一个线程通常不会影响它正在执行的操作,但是像 sleep 和 wait 这样会抛出 InterruptedException 异常的方法除外。
一个线程可以使用某种形式的 join 方法来等待另一个线程终止。
不要使用 stop 方法:首先因为 stop 不能强制终止任何线程,无力对付恶意方法;其次,stop 会影响同步锁,破坏对象。所以建议转而使用 interrupt 方法。
14、4 同步
当多线程使用同一个对象时,存在着由于交叉操作而破坏数据的可能性,所以在某些动作操作对象之前,必须先获得对象的锁来阻止其他对象获得这个锁,直至它被持有者释放为止。
利用 synchronized 的声明,我们可以确保多个运行的线程不会互相干扰,因为每一个方法都将互斥地执行(在该方法调用结束前不能被其他方法调用)。
线程安全:多个线程可以并发调用对象上的方法,并且在每个线程中被调用的方法都在执行所期望的作业。
第十五章、注解
注解以结构化的方式提供了程序及其元素(类、方法、字段、变量等等)的信息,这些信息能够被外部工具自动处理。
第十六章 反射
第十七章 垃圾回收器与内存
Java 虚拟机可以使用一种称为垃圾回收(garbage collection)的技术来确定对象在程序中何时不再被引用,因此它可以安全地释放对象占用的内存空间。
当我们从任何可执行代码都无法到达某个对象时,它所占的空间就可以被回收。垃圾回收会占用一定的系统资源,通常情况下,只有需要更多的内存空间或者避免发生内存溢出时,垃圾回收器才会运行。但是程序可能没有发生内存溢出,甚至在没有接近内存退出的时候就退出了,所以可能根本不需要执行垃圾回收。
垃圾回收并不能保证内存中总是会有空间来创建新对象,不停的创建对象并置于使用列表中,就会造成内存泄漏。垃圾回收解决了很多(但并非全部)的内存分配问题。
System.runFinalization(); //运行挂起 finalization 的所有对象的终止方法
System.gc(); //运行垃圾回收器
Runtime.getRuntime().runFinalization();
Runtime.getRuntime().gc();
第十八章 包
18.1、导入机制
如果声明了一个对 Xxx 类的引用,那么编译器就会按照下面的顺序搜索该类型:
1、包括继承类型在内的当前类型。
2、当前类型的嵌套类型。
3、显式命名的导入类型(单类型导入, import attr.Attributed;)
4、在同一个包中声明的其他类型。
5、隐式命名的导入类型(按需导入,import attr.*;)
为了避免导入同名的两个类型产生二义性,建议使用完全限定名。
第十九章 文档注释
19.1、剖析文档注释
/**
* 文档注释第一句应该是一个良好的摘要。
* 文档注释中可以嵌套标准的 HTML 标签,用于格式指令或链接到其他文档。
* 如果想使用 @ 字符,请使用 @,否则会被当作文档注释标签的开始。
*/
第二十章 I/O 包
java.io 包是用流(stream)定义 I/O 的,流是有序的数据序列,它有一个源(输入流)和目的地(输出流)。
java.nio 包是用缓冲区(buffer)和通道(channel)定义 I/O 的,它用于高吞吐量服务器。
java.net 包基于对套接字的使用,用一种基于流或通道的模型提供对网络 I/O 的特殊支持。
20.1、流的概述
I/O 主要有两部分:
● 字符流(character stream,16位的 UTF-16 字符),它基于文本的 I/O,有读取器(reader)和写入器(write)。
● 字节流(byte stream,通常是 8位),它基于数据的 I/O,有输入流(input stream)和输出流(output stream)。
转换流 InputStreamReader 与 OutputStreamWriter 可以通过指定或者默认的编码机制在 字符流 与 字节流 之间互相转换,类似于“黏合剂”,使我们可以以一种统一的、平台无关的方式使用现有的 8位字符编码机制去处理本地字符集。
InputStreamReader 对象读取字节,并使用适合该流的编码机制将其转换为字符。
OutputStreamWriter 对象将接受提供的字符,使用适合的编码机制将它们转换为字节。
(注:无 ReaderStreamInput 类将字符转换为字节,也无 WriterOutputStream 这个类将字节转译为字符)
InputStreamReader(InputStream in);
OutputStreamWriter(OutputStream out);
关闭转换流的同时也会关闭掉将其关联的字节流,所以一定要慎重。
20.2、数据字节流
通过流来传输特定类型的二进制数据。DataInput 和 DataOutput。
20.3、对象序列化
将对象的表示转换为字节流的过程成为序列化,而从字节流中重构一个对象的过程被称为反序列化。
当 ObjectOutputStream 写入序列化对象是,该对象必须实现 Serializable 接口,声明该类为可序列化的。
第二十一章 集合
21.1、迭代
对 Collection 使用 iterator 方法将获得一个 Iterator 对象,这个对象可以遍历集合内容,而且每次都只能访问一个元素。
与增强型 for 循环相比,它可以通过 remove 方法移除元素,这种方式是安全的。
21.2、同步
java.util 包中提供的所有集合实现都是非同步的(除了 Vector、Dictionary、Hashtable 等遗留下来的集合)。
第二十二章 各种常用工具
● Formatter:用于产生格式化文本。
● Random:用以产生伪随机数序列的类。
● Scanner:该类用于扫描文本,并根据正则表达式模式将其解析成基本数据类型或者字符串。
● Timer/TimerTask:用于调度将来某个时刻运行任务的一种方式(包括重复发生),每个 Timer 对象都有一个相关联的线程。
第二十三章 系统编程
应用程序有时必须同 Java 虚拟机的运行时系统或底层操作系统进行交互。java.lang 中有三个主要的类提供了这个访问:
23.1、System 类
System 类提供了用于操纵系统状态的的静态方法,并起到了资源仓库的作用。它有四个通用领域的功能性:
● 标准 I/O 流
● 操纵系统属性
● 用于访问当前 Runtime 对象的工具方法和便利方法。
● 安全性
23.2、Runtime 类
提供了访问虚拟机的运行时系统的接口。当前 Runtime 对象提供了对每个运行时系统的功能进行交互的能力,例如与垃圾回收器交互、执行其他程序及关闭运行时系统。
23.3、Process 类
表示的是正在运行的进程,它是通过调用 Runtime.exec 来执行另一个程序时创建的,也可以通过直接使用 ProcesssBuilder 对象来创建。
第二十四章 国际化和本地化
24.1、区域
java.util.Locale 对象描述了区域。
24.2、资源束
java.util.ResourceBundle 类。
24.3、货币
java.util.Currency 类可以帮助我们正确格式化货币值。
24.4、时间、日期、日历
时间是用长整型 long 表示的,从 1970 年开始,可以通过 java.util.Date 类获得时间,时间的比较可以用 before 和 after 方法,或者可以用 getTime/setTime 方法获得/设置时间的 long 型值。Date 没有提供本地化支持,我们可以使用更有效复杂的区域敏感的 Calendar 和 DateFormat 类来代替它。
抽象类 Calendar 表示不同标记时间的方式,同时也提供了许多用的日历字段。
GregorianCalendar 类表示日历。
SimpleDateFormat 类格式化、解析时间的类。
例:
Calendar cal = new GregorianCalendar(1972, Calendar.OCTOBER, 26);
System.out.println(cal.getTime());
第二十五章 标准包
● java.awt:抽象工具箱的抽象层,用来编写平台无关的图形用户界面。
● java.applet:Applet 类和相关类型,用于编写可嵌入其他应用程序的子程序,如 HTML 浏览器。
● java.beans:用于编写客户端可重用的 JavaBeans 构件。
● java.lang.instrument:用于定义代理的服务。
● java.lang.managerment:用于监视并管理其所在的虚拟机以及操作系统服务。
● java.math:数学
● java.net:网络
● java.rmi:远程方法调用
● java.security 与相关的工具包:安全工具。
● java.sql:关系数据库访问
● javax.* :标准扩展工具包
● javax.sound:创建和操作声音的子包。
● javax.swing:GUI构建的 Swing 包。
posted on 2008-10-11 23:28
黄小二 阅读(333)
评论(0) 编辑 收藏 所属分类:
J2SE