/************************************************************
Title: Java2 Primer Plus
Collator: lsh
Date: 2004.10
***********************************************************/
第一部分 JAVA基础
第一章 JAVA简介
1. SDK用于编译并执行Java程序。Java运行时引擎(JRE),JRE用于执行Java程序,但不包含任何用于编译的组件。Java应用程序的使用者必须下载并安装JRE,但由于SDK中包含JRE,因此不必下载该引擎。
2. 在Windows中要检查计算机中CLASSPATH环境变量的设置,echo %CLASSPATH%;如果给CLASSPATH环境变量指定一个值,则必须保证包含当前文件夹,当前文件夹用句点(.)来表示,CLASSPATH中必须存在句点。要在CLASSPATH中添加当前文件夹,set CLASSPATH=.;%CLASSPATH%。清空CLASSPATH用set CLASSPATH=即可。
3. main是一个特殊的函数,在编写Java应用程序时必须定义该函数,main函数是启动应用程序Java运行时引擎的入口点,main函数控制程序的流程。
4. 检查JAVA2 SDK的版本,在PATH执行如下命令: java –version。
5. System类包含了几个常用的类和方法,它不能被实例化。System类提供的工具包括标准输入、标准输出、标准错误输出流;对外部定义“属性”的访问;装载文件和库的方法,以及快速复制数组某一部分的实用方法。
6. System类out属性的定义如下:public static final PrintStream out。注意out变量的类型是PrintStream。PrintStream类中包含一组print、println和write函数。out成员变量是‘标准的’输出流。该标准输出流已打开并准备接收输出数据,这个流通常指的是显示输出设备或由主机环境和用户指定的其他输出目标设备。
第二章 关键字、数据类型和变量
1. 所有的编程语言都会定义: 一套用来向编译器发布指令的关键字;用来定义如何为数字和字符等项目分配内存的数据类型; 以及用来命名内存区域的变量。关键字是程序中向JAVA编译器提供专门指令的单词,可将这些单词看作是代表特定Java功能的占位符。
2. 语法规定为使编译器能够正确处理程序而必须遵守的规则。语义则定义使计算机程序如愿运行的逻辑规则。
3. Java的关键字如下:(const,goto是JAVA保留的关键字,暂时没有用处)
abstract else interface switch
boolean extents long synchronized
break false native this
byte final new throw
case finally null throws
catch float package transient
char for private true
class goto protected try
const if public void
continue implements return volatile
default import short while
do instanceof static
double int super
4. 数据类型用来表示计算机内存的特定用法。在程序中使用时,数据类型规定所使用内存的大小以及在该内存中可存放的有效值和可对该值施加的操作。在计算机中1和0分别表示两种电脉冲(1为有脉冲,0为无脉冲)。我们用数据的最高位来表示符号(0为正,1为负)。在定义了位和字节后,就可以定义数据类型(如数字和字符)了,方法是在建立位和数据类型之间的关联后,定义这些位所代表的含义。
5. JAVA的8种基元数据类型
字节型 byte 1字节 -128~127
短整型 short 2字节 -32768~32767
整型 int 4字节
长整型 long 8字节
单精度 float 4字节 6~7位有效数字
双精度 double 8字节 15位有效数字
字符型 char 2字节
布尔型 boolean true/false
6. 除非前后都是非零数,0不被视为有效数字。在数学和计算机科学中通常使用指数形式来表示浮点数。即将第一位有效数字写在小数点之前,其他有效数字置于小数点之后,然后指定一个幂数,使用得该数乘以10的幂数后等于其实际值。
7. 可按如下方式使用Unicode编码来表示字符: \uXXXX 此处XXXX是位于0000到FFFF(十六进制)之间的一个数
7. 数据类型之间的转换
(1).只要有一个操作数是双精度型,则其他操作数一律转换为双精度型。
(2).只要有一个操作数是单精度型,则其他操作数一律转换为单精度型。
(3).只要有一个操作数是长整型,则其他操作数一律转换为长整型。
(4).其他情况下将所有的操作数都转换为整型。
8. 字面值表示实际的字符、字符串和数字,而不是内存中字符、字符串和数字的变量引用。长整型转换为单精度是编译器允许的。
第三章 运算符
1. 算术运算符: + - * / %
关系运算符: == != < > <= >=
位运算符: &(与) |(或) ^(异或) ~(非)
&运算符规定必须信号A和B都被充电,其结果才是充电。(1表示充电,0表示未充电)
|运算符规定只要信号A或B被充电,输出结果就是充电。
^异或(XOR)运算符规定如果信号A或B之一被充电,但是信号A和B不是同时被充电,则结果为充电。
~运算符也称为按位求补,它翻转所有的充电状态值。
移位运算符: << >> >>>
将一个数左移(<<)会使该值乘以2的幂。
将一个数右移(>>)会使该值除以2的幂。
右移(补零)运算符(>>>)永远不会产生负号,因为其符号位总是被补零。
移位运算异常的快,加法也是一种开销很小的运算,可以结合移位和加法来代替乘法。如:考虑一个值乘以35:等同于左移5位(32倍),加上左移一位(2倍),再加上自身(1倍)。
2. JAVA运算符优先级:优先顺序为从上至下,从左至右
------------------------------------------------------
类型 运算符
------------------------------------------------------
一元 ! ~ ++ -- + - () new
算术 * / % + -
移位 << >> >>>
比较 < <= > >= instanceof == !=
位 & ^ |
短路 && ||
三元 ?:
赋值 = "op=" (for example, *=, +=, %=, ^=)
------------------------------------------------------
3. 检查数字某一位上是否为充电(1)可以用与运算,如:21(0001 0101)检查其第五位上是否为充电,
boolean isActive = (21 & 16) == 16; 如果返回true则为充电(1),false则为未充电(0)。
第四章 流程控制
1. switch(变量)在多个可选的执行路径中作出选择,变量必须是整型或可以转变为整型。
2. 在for循环的初始化部分并不仅限于定义单个变量,也可以定义多个变量,惟一的限制是这些变量必须属于同一类型。
3.
break 标签; 跳出标号所标记的语句块,继续执行其后的语句。多用于嵌套的块中,从内层块跳到外层块之后。
continue 标签; 立即结束标号标记的那重循环的当次执行,开始下一次循环
4. JAVA的流程控制语句:
(1) if else
(2) switch (case: default:)
(3) while
(4) do while
(5) for
(6) JAVA里没有VB系列中如until之类的流程控制语句。
第五章 方法
1. 方法名,返回类型和参数列表定义了方法签名,即唯一识别该方法的“签名”。
2. 尽管返回值是签名的一部分,但它并不能作为重载方法的区分因素,编译器只会检查方法名和参数列表,而不会考虑返回类型。
3. Math类定义了两个非常重要的元素:E和PI,Math类中定义的方法都是静态的,所以使用时不必为Math类创建实例。
double pow(double a, double b) 返回以第一个参数为底,第二个参数为指数的幂。
double ceil(double a) 返回不小于参数的最小(最接近于负无穷大)双精度型整数值
double floor(double a) 返回不大于参数的最大(最接近于正无穷大)双精度型整数值
double random() 返回一个大于等于0.0且小于1.0,带正号的双精度型数
double rint(double a) 返回最接近于a的双精度型整数值
long round(double a) 返回最接近于参数的长整型数
int round(float a) 返回最接近于参数的整型数
import java.lang.Math.*;
4. Runtime.getRuntime().freeMemory() 系统可用内存
System.currentTimeMillis 从午夜到现在所经过的豪秒数
Process process = Runtime.getRuntime().exec("cmd.exe /c start cls");执行清屏命令,start指定后面要运行的控制台命令
Process process = Runtime.getRuntime().exec("cmd.exe /c start http://www.csdn.net/"); //登录网站
Process process = Runtime.getRuntime().exec("cmd.exe /c start set"); //显示系统环境变量
/*显示JAVA运行时系统的相关设置*/
import java.util.*;
import java.util.Properties;
public class Env
{
public static void main(String[] args) {
Properties prop = System.getProperties();
Enumeration e = prop.keys();
while(e.hasMoreElements()){
String key = (String) e.nextElement();
System.out.println(key + "--->" + prop.getProperty(key));
}
}
}
5. 有三种种方法可将控制权返回给调用程序:
(1) 如果该方法不返回结果,则控制流程被返回到该方法的右花括号处;
(2) 手工控制不返回任何值 return;
(3) 返回一个值 return 表达式;
第二部分 面向对象编程
第六章 类
1. 组件属性(物理属性)与状态属性并没有操作上的不同,只有逻辑上的差别,但在定义对象时鼓励按这种思路来考虑。
2. 当内存中出现不再访问的区域时,就需要进行垃圾收集,JAVA有一个在后台运行的进程可查找这些对象,但该进程只在应用程序运行内存不足时才工作,系统就可以立即(或在几分钟内)回收该内存区域。或者,如果系统内存足够,也可以只在应用程序关闭时才回收内存。如果想请求垃圾收集,可以调用System类的gc()静态方法。该方法调用运行垃圾收集的请求,但是没有任何方法强制JAVA虚拟机执行垃圾收集程序。如:System.gc()
3. Java本身没有析构函数,但具有与之类似的结构,即终结器。类可以定义finalize方法,在从内存中删除对象时可调用该方法。终结操作必须通过调用System类的runFinalization()方法显式启用。该方法调用仅指示垃圾收集程序在回收内存之前对要执行垃圾收集操作的对象调用finalize方法。但问题是,无法控制垃圾收集器何时运行,也无法判断是否已调用终结器。
4. 在方法之外定义的内部类属于该类,具有类范围(类似于成员变量),而在外部类的任何方法中都可以创建该内部类的实例。
内部类还可以在方法内部定义,在方法内部定义的内部类属于该方法,具有局部(方法)范围(类似于自动变量)。该方法可以创建该类的实例,但是外部类的其它方法无法访问该内部类。
在方法内部定义的内部类具有一些限制:
(1)它不能用访问修饰符进行声明
(2)它们不能被声明为静态类型
(3)具有局部范围的内部类只能访问所嵌入方法中声明为final的变量,引用方法的局部变量或传递给所嵌入方法的参数。
5. 内部类之外部类的变量和方法对于内部类来说具有完全的可见性。尽管是private也可见。
6. 如果将内部类声明为public类型,则可在内部类名称前面加上外部类的名称访问该类,前提是必须存在该外部类的一个实例。
Outer.Inner inner = new Outer().new Inner();
7. 如果将内部类定义为静态的,则意味着可以创建内部类的一个实例而不必创建外部类的实例。静态内部类在访问外部类的方法时存在一些限制:
(1)静态内部类的方法不能访问外部类的实例变量。
(2)静态内部类的方法只能访问外部类的静态变量。
8. 在创建类实例时实际上创建的是对该类实例的引用。定义该引用是为了明确如何访问该类,而不是将其与任何特殊实例紧密联系在一起。如果为一个类创建了两个实例,并将其中一个实例赋给另一个实例,那么这两个实例变量都将引用指向同一对象。
9. this变量指向当前实例,每个类实例都有一个隐含的this变量,this变量可访问所有的类信息,包括类变量和类方法。
10. 匿名内部类具有如下特性:
a)创建时不指定名称
b)在方法内部定义
c)没有构造函数
d)在同一语句中进行声明和构造
e)对事件处理非常有用
11. JAVA对象的生存周期:
(1)为对象分配内存。
(2)初始化属性
(3)调用合适的构造函数
(4)在程序中使用对象
(5)在内存中分离对象的引用和对象
(6)在某个时刻,运行JAVA的垃圾收集程序并查看哪些内存不再使用
(7)垃圾收集程序选择性地调用对象的finalize方法
(8)垃圾收集程序释放内存
第七章 继承
1. 任何时候,将类作为字符串使用时,都会调用toString()方法,并将该方法的结果返回给该字符串。
2. this变量引用类的当前实例,super变量则引用超类的实例。和super变量一起的还有一个super方法,super方法用来调用该超类的一个构造函数。由于超类的构造函数先于子类的构造函数执行,如果使用super方法,则必须将其作为子类构造函数的第一条语句。
3. [public] [qualifiers] class subclass extends superclass
·public 可带有值public或不带任何值,可确定软件包的访问权限
·qualifiers 合法的限定符有final,abstract,static,也可不带任何限定符
4. 将类定义为final意味着该类不能派生子类,也就是说不能扩展该类。
将类定义为abstract类型意味着该类必须带有子类,而该类本身不能实例化。抽象类通常至少包含一个抽象方法,抽象方法是在声明中使用关键字abstract来标识的,并且不包含方法体,改用分号结束声明。如:public abstract void accelerate();
5. 超类的终结器不会被执行,除非子类的终结器显式调用它。另外,超类的终结器必须是子类终结器所调用的最后一个方法。
public class MyClass extends MySuperClass {
protected void finalize {
super.finalize();
}
}
6. 子类重载方法的访问修饰符的限制级别必须低于或等于超类中的限制级别。(也就是访问权限要高于或等于超类),如子类可以将protected方法变成public方法,但是却不能使protected变成private方法。
7. 可以用父类的引用指向子类的实例,但只能访问父类中定义的方法,而不能访问子类中定义的专用方法。反之则不行。(多态性)
8. 动态绑定只绑定方法而不绑定属性
第八章 接口
1. 接口语法:
[public] interface iname [extends iname2]
所有接口都是public和abstract类型,如果接口包含属性,则所有属性都是static和final类型。通常是隐式使用。
2. 实现接口:
[public] [qualifiers] class classname implements iname, [iname2]
类可以实现任意数量的接口,类可以是abstract或final类型,类可以扩展另一个类,同时实现任意多个接口
3. 接口定义一组由所有需要提供这些功能的类共同实现的公共方法。想要访问这些功能的类都可以通过该接口引用类实例来访问这些功能。接口中所有方法都必须是abstract类型,不能为其提供任何默认行为。由于接口是抽象的,所以其所有方法天生就是抽象的,无需要再使用关键字abstract.
4. 如果定义的方法接受接口作为参数,那就只能调用在该接口中定义的方法,而不能调用该类实现的其他方法。
5. 应该这样来设计类,让类封装所代表对象的属性和行为,对象外部的功能可通过接口来实现。
6. 接口与抽象类的对比:
(1)如果功能与对象自身密切相关,则在超类中使用抽象的基类方法
(2)如果该功能只是对象的辅助行为,则可使用接口
(3)如果该功能可被全局地应用到其他无关对象,则可使用接口
第九章 异常处理
1. 一个try块后可接有零个或多个catch块(如果没有catch块,则必须有一个finally块),try块后必须跟有catch或finally块,或两者都有。
2. 正常处理异常的关键在于在靠前的catch块中定义较特殊的异常,而在靠后的catch块中定义较一般的异常。
3. 主要异常的继承层次关系:
Throwable
Error Exception
RuntimeException
4. Java中大多数异常都使用java.lang.Exception类,它是多数用户自定义异常的基类。该异常类要求引发异常的方法将异常显式声明为throwable类型。
5. java.lang.Throwable类提供了其子类常用的一组核心方法:
Throwable fillInStackTrace() 填充该执行堆栈跟踪
String getLocalizedMessage() 创建该可引发类的本地化描述
常用 String getMessage() 返回该可引发对象的错误消息字符串
常用 void printStackTrace() 将该可引发类及其反向跟踪打印到标准错误输出流
void printStackTrace(PrintStream s) 将该可引发类及其反向跟踪打印到指定的打印流
void printStackTrace(PrintWriter s) 将该可引发类及其反向跟踪打印到指定的打印机
String toString() 返回关于访可引发类的简短描述
6. 如果一个方法可能引发异常, 则必须向调用该方法的所有类作出声明。可通过为方法签名添加一个throws子句来实现这一点。这样如果调用该方法,则必须在一个try块内部调用该方法,并提供一个catch块来捕获可能引发的异常或其超类。如果不这样,可能产生编译错误。
7. 如果调用方法仍不处理此异常,那么它必须将其上抛,交给调用方法的调用方法,直到main和操作系统。通过将异常类添加到方法的异常列表中来实现(用throws MyException)。
8. 创建了Exception实例后,就可以用关键字throw引发该实例。
9. 异常带来的好处是,可以在异常中定义自己的数据和方法来帮助更好地诊断引起问题的根源。
10. 对于子类而言,如果要从方法(指子类重载的方法)中引发异常,则必须在该子类的超类中将方法声明为throwable类型。子类可从异常列表中清除某个异常,但不能添加在超类中未声明的任何异常。有一项技术可解决这个问题:将方法声明为可引发异常或throwable类型(我理解为声明为throws Exception 或 throws Throwable),这样子类就可以任意引发所需的异常。但这是一种不好的编程习惯。
11. 异常的定义:
异常:异常表示一种错误情况或某个方法意料之外的任何结果。
引发异常:检测异常情况并生成异常的过程叫做引发异常。
捕获异常:当异常被引发时,可被处理程序捕获,这叫做捕获异常。
处理异常:以上整个过程被称为处理异常。
12. 引发异常包含以下三个步骤:
(1).先声明方法可引发的异常列表;
(2).为描述错误的异常类创建一个实例;
(3).引发该异常。
第十章 Java类
1. JavaDoc标记定义,标记的含义取决于标记的上下文:
@author 表示类的作者(只适用于类和接口,必须)
@version 表示类的版本(只适用于类和界面,必须)
@param 传递给方法的每个参数都有各自特定的参数标记(只适用于方法和构造函数)
@return 描述方法的返回值(只适用于方法)
@exception 引发/异常标记列出方法可引发的异常(@throws是在JavaDoc1.2中添加的同义词)
@see 参考另外一个类或方法
@since 说明定义方法/类的API版
@serial 标记该方法中的序列化字段(也做@serialField或@serialData)
@deprecated 标记该方法受到批评,这意味着在将来某个时候该方法可能被删除,类的使用者应对此做记录,并停止使用这个方法。
2. 包装类有一个十分独特的不可变(恒常)特性,根据定义,包装类中包含的值不可改变,因此,赋给包装类的任何值都永远不会改变。
3. 所有的包装类都有一个固定的模式:
·xxxVaule 该方法返回相关的基元类型
·getXXX 该方法读取系统属性,查找传递给方法的字符串值,并将该值转换为相关的基元类型
·valueOf 该方法将字符串值赋给包装类实例
4. 它们都提供两种构造函数:
一种构造函数从类的相关基元类型构建类的一个实例;另外一种从转换为相关基元类型的字符串构建类的一个实例。
5. 包装类常用的一些方法:
Boolean
boolean equals(Object obj) 当且仅当参数不为null,且为布尔对象(表示与对象相同的布尔值)时,返回true
static Boolean getBoolean(String name) 当且仅当由参数命名的系统属性存在且等于字符串true时,返回true
Byte
static byte parseByte(String s, int radix) 假定指定的字符串表示一个字节,返回该字节的值。(默认以10为基数)
static Byte valueOf(String s,int radix) 假设指定的字符串表示一个字节,返回一个新的字节对象,并初始化为该值
static Byte decode(String nm) 将字符串解码为字节型
Character
static int digit(char ch, int radix) 返回字符ch在指定基数进制中的数字值
static char forDigit(int digit, int radix) 为指定基数进制中的数字确定字符表示法
static boolean isDigit(char ch) 判断指定的字符是否为数字
static boolean isLetter(char ch) 判断指定的字符是否为字母
static boolean isLetterOrDigit(char ch) 判断指定的字符是否为字母或数字
Double
static double parseDouble(String s) 返回一个新的双精度值,初始化为由指定字符串表示的值。(和Double类的valueOf方法的结果一样)
static Double valueOf(String s) 返回一个新的双精度对象,初始化为由指定字符串表示的值。
boolean isInfinite() 如果该Double值在数量上无穷大,则返回true
static Boolean isInfinite(double v) 如果指定数字在数量上无穷大,则返回true
boolean isNaN() 如果该Double值不是特殊的非数字(NaN)值,则返回true
static Boolean isNaN(double v) 如果该指定的数字不是特殊的非数字(NaN)值,则返回true
static double longBitsToDouble(long bits) 返回符合给定位表示法的双精度浮点值
Float
Integer
static String toBinaryString(int i) 以二进制无符号整数创建整型参数的字符串表示法
static String toBinaryString(int i) 以十六进制无符号整数创建整型参数的字符串表示法
static String toOctalString(int i) 以八进制无符号整数创建整型参数的字符串表示法
static String toString(int i, int radix) 由第二个参数指定基数,创建第一个参数的字符串表示法。
Long
Short
6. JAVA建有一个字符串表(池),保存了任何给定时刻内存中的所有字符串。与包装类相似,String类也是不可改变的,这意味着无法修改字符串的值,而必须创建一个新的字符串来存放新值。如果新建的字符串与字符串表中已有的值匹配,那么该字符串将简单地引用现有的值。
String / StringBuffer / StringTokenizer
7. 使用StringTokenizer类必须导入java.util.StringTokenizer包。
8. String常用方法一览表:
char charAt(int index) 返回指定索引的字符
int compareTo(String anotherString) 按照词典顺序比较两个字符串
int compareToIgnoreCase(String str) 按照词典顺序比较两个字符串,忽略大小写
String concat(String str) 将指定字符串连接到该字符串的末尾
boolean endsWith(String suffix) 测试该字符串是否以指定后缀结尾
boolean equals(Object anObject) 将该字符串与指定对象进行比较
byte[] getBytes(String enc) 根据指定的字符编码,将该字符串转换为字节类型,将结果存入新的字节数组中
String substring(int beginIndex, int endIndex) 返回新的字符串,作为该字符串的子字符串
boolean startsWith(String prefix, int toffset) 测试该字符串在指定索引处是否以指定前缀开头
String replace(char oldChar, char newChar) 用newChar替代该字符串中的所有oldChar,返回替换后得到的新字符串
9. StringBuffer方法一览表:
StringBuffer append(String srt) 将字符串附加到该StringBuffer上
int capacity() 返回StringBuffer的当前容量
StringBuffer delete(int start, int end) 删除该StringBuffer的子字符串中的字符
StringBuffer deleteChar(int index) 删除该StringBuffer中的指定位置的字符(使StringBuffer减少了一个字符)
StringBuffer insert(int offset, String srt) 将字符串插入到该StringBuffer中
int length() 返回该StringBuffer的长度(字符数)
StringBuffer replace(int start, int end, String str) 用指定字符串中的字符替换该StringBuffer的子字符串中的字符
StringBuffer reverse() 倒序替换该StringBuffer中包含的字符序列
void setChar(int index, char ch) 将该StringBuffer指定索引处的字符替换为ch
10. 为指定字符串构造StringTokenizer
StringTokenizer(String str, String delim, Boolean retrunDelim)
StringTokenizer方法:
int countTokens() 使用当明分割符集,该方法确定还没被分析的标记的个数并返回结果
boolean hasMoreElements() 如果在字符串中包含一个或多个标记,则返回true,如果不包含标记则返回false
boolean hasMoreTokens() 同上
Object nextElement() 除非声明的返回值是对象而不是字符串,否则返回与nextToken方法相同的值
String nextToken() 将下一个标记作String返回
String nextToken(String delim) 将下一个标记作String返回,并且将分割字符串设为由delim指定的字符串
11. 对象包装类是序列化的类,因此可在各个类之间安全地传递,而这些类可能在不同的进程中运行。(为什么要使用包装类,而不是直接使用基元类型?)
第十一章 集合类
1. 数据结构简介:
数据结构,其本质是一些可保存对象并提供添加、删除、查找对象等机制的容器。
(1)数组 数组是大小固定的容器,可通过从0开始的索引直接访问容器中的每个元素。元素不进行排序,也不提供添加或删除对象的管理功能。使用数组时,这些事情将由您自己完成。
数组非常简单,正因为这样,它也存在以下种种局限性:
l 数组长度固定,这意味着无法向一个空间已满的数组中添加新的元素。
l 在从数组中删除数组方面,数组并没有提供继承机制来使数组中的现有元素上移以填补删除后的空缺;这意味着实现搜索时,不仅需要遍历所有有意义的数据,也要遍历所的有空数据。
l 除获取数组长度以外的所有功能,都需要由开发者实现。
(2)链表 链表是一系列的节点,每个节点都包含一个对象和一个指向链表下一个节点的引用。
对链表进行插入和删除操作非常快,然而,在链表中搜索一个元素却非常的慢,需要检查链表中的所有元素。
JAVA已通过java.util.LinkedList类提供了链表的实现。
(3)堆栈 ·可在栈顶“push”(推入)一个对象
·可“peek”(获取)位于堆栈顶部的对象
·可从栈顶“pol”(弹出)一个对象
实现证明,堆栈非常适于数学运算和开发计算机编译器,但除此之外,除非想完成某些特殊的功能,否则不宜采用堆栈。
(4)队列 作为一种数据结构,队列遵守以下模式:
·元素可以入队,即被添加到队列的末尾
·项目可以出队,即从队列的前端删除该项目
当需要在请求到达时即作出处理时,队列非常有用;当处理事件和消息时,队列也很受欢迎。但队列显然不适合插入,删除和搜索操作。
(5)哈希表 哈希表这种数据结构计算每个项目的数字值,然后通过一张表中的数值来引用对应的项目。该表有两列:哈希代码和项目本身。哈希表的大小经常被定义为比所能存放数据的最大数目还大20%,以防止冲突,但冲突还是会发生,当哈希表中有两个对象发生冲突时,就必须消除这种冲突。(可解决的方法有:1.移向下一个数据槽,直至找到空数据槽 2.计算另一个哈希代码的值)
实现哈希代码的算法多种多样,大多数优秀的算法所使用的算法都能做到:只要哈希表的空间足够,就能保证到多计算两次哈希代码就能找到唯一的数据槽。
(6)树 在计算机科学中,有两种类型的树:二元树和B树。二元树限定了每个节点最多只能有2个字节点,而B树没有这个限制。
树天生就有这种排序的性质,插入算法需保证树的平衡。
平衡树具备如下性质:节点左边子树的节点数与右边子树的节点数大致相等。由于这是强制性的,所以在树中查找一个节点的平均操作时间就是该树中元素数目的自然对数。ln(N)
树的平衡性就意味着向树中插入一个元素时,可能需要调整树的原有状态。树的插入操作可能比链表之类的其它数据结构慢很多,它最适用于插入、删除操作很少而查找频繁的情况。
2. 集合类:
列表(java.util.List)是元素的有序集合,集(Set)则是元素惟一的集合。
List、Set和Map都是接口,它是通过它下面的类来实现的。
列表(List)
ArrayList 与数组类似,区别在于ArrayList没有预先确定的大小,其长度可按需增大; 此外,还可以将
ArrayList初始化为另一个java.util.Collection派生类所含的值。
Vector Vector与ArrayList几乎完全相同,但一般认为在多线程应用程序中使用Vector比较安全
elementAt(int index) 返回指定索引处的元素
get(int index) 返回指定索引处的元素
indexOf(Object o) 返回指定对象在向量列表中的数字索引
insertElementAt(Object o, int index) 在指定索引处插入一个元素
set(int index, Object o) 给指定索引处的元素赋值
setElementAt(Object o, int index) 给指定索引处的元素赋值
size() 返回向量列表中的元素个数
LinkedList 实现了链表功能,可以添加、删除或获取链表开头或结尾的元素,此外它还实现了队列数据结构的功能,虽然java.util.LinkedList提供了随机访问列表中元素的方法,但底层实现依然必须遍历列表来定位该随机访问对象,因此,其性能依然有限。
addFirst(Object o) getFirst()
addLast(Object o) getLast()
removeFirst() removeLast()
Stack Stack是Vector的一个变体,实现了堆栈数据结构的功能,Stack类在创建时只能为空, 不能像其他集合类一样从已有集合中初始化。
empty() 如果该堆栈为空,则返回true
peek() 返回栈顶对象,但不删除该对象
pop() 删除并返回栈顶对象
push(Object o) 在栈顶添加一个对象
集(Set)
HaskSet 是一个以哈希表为基础的集,所以HashSet中的数据是无序的,它可以包含null值,不能保证元素顺序始终不变。java.util.HashSet类实现了数学上的集概念,集内元素全部惟一。可通过已有集合初始化哈希表,也可将哈希集初始化为指定大小和可选加载因子(需要时可增大的倍数),还可初始化为空。
TreeSet 是以树为基础的集,所以TreeSet中的数据是有序的,从树中继承的元素可以保证以升序排列。可通过已有集合初始化TreeSet(树集),也可将其初始化为包含另一个SortedSet(Set的派生类,对值进行排序)的值,也可使用Comparator(该接口用以定义如何在排序时比较两个对象的类实现)初始化,或者初始化为空。
first() 返回集内值最小的元素
last() 返回集内值最大的元素
映射(Map) 映射是将键映射为值的对象,所有的键都必须是惟一的。
HashMap 将它的键保存在哈希表中进行维护;键并不以特定顺序排序,顺序也并非始终不变。Java.util.HashMap类实现了从键到值的映射,可将哈希映射初始化为已有的映射,也可以初始化为指定大小且带有可选加载因子(需要时可增大的倍数),还可以初始化为空值。
containsKey(Object key) 如果该映射包含指定键,则返回true
containsValue(Object valje) 如果该映射包含指定值,则返回true
get(Object key) 返回与指定键相关的值
keySet() 将该映射视作集,返回映射中的键
put(Object key, Object value) 向该映射中添加键值对
TreeMap TreeMap的键存放在树中,键的顺序一定是以升序排列。可将TreeMap(树映射)初始化为已有映射,也可将其初始化为包含另一个SortedMap(map的一个派生子类,对键进行排序)的值,也可使用Comparator(该接口用以定义如何在排序时比较两个键的类实现)初始化,或者初始化为空。
3. Iterator(迭代)是一种用于遍历集合的接口。每个集合类都有一个名为iterator()的方法,该方法返回Iterator类的一个实例。Iterator类有3个方法:
hasNext() 如果集合中还有更多元素,该方法返回true
next 返回集合中的下一个元素
remove() 删除iterator返回的最后一个元素
虽然每个集合类都提供专用的机制来访问各自的数据,但都支持iterator()方法,这就允许采用统一的机制来使用所有的集合类。对于HashMap和TreeMap,可用keySet()方法将映射视作集,返回映射中的键,Set keys = HaspMap.keySet()
然后用keys.iterator()方法生成Iterator类的一个实例,以此来遍历map。
第十二章 使用JAVA的输入和输出类
1. JAVA定义了读取字节型数据的流和读取字符型数据的流。字节型数据表示二进制数据,如图像;字符型数据表示的可读取的文本,如HTML页面或文本文件。
2. 面向字节的流类都可以实现java.io.InputStream接口或java.io.OutputStream接口。
Java.io.InputStream方法:
int available() 如果调用该输入流的方法未阻止输入流,则返回可从该输入流中读取(或跳过)的字节数
abstract int read() 从输入流中读取数据的下一个字节
int read(byte[] b) 从输入流中读取一些字节,并将这些字节存入缓冲数组b中
int read(byte[] b, int off, int len) 从输入流中读取数据的前len个字节并将其读入字节型数组中
long skip(long n) 跳过并抛弃该输入流中数据项的n个字节
java.io.OutputStream方法:
void flush() 刷新该输出流,并强制写出所有缓冲输出字节
void write(byte[] b) 将b长度字节从指定字节数组中写入该输出流
void wirte(byte[] b, int off, int len) 将指定字节数组中从偏移量off处开始的len个字节写入该输出流
abstract void write(int b) 将指定字节写到该输出流
面向字节的流:
OutputStream —— ByteArrayOutputStream
FileOutputStream
ObjectOutputStream
PipedOutputStream
FilterOutputStream —— BufferedOutputStream
DataOutputStream
PrintStream
InputStream —— ByteArrayInputStream
FileInputStream
ObjectInputStream
PipedInputStream
FilterInputStream —— BufferedInputStream
DataInputStream
LineNumberInputStream
PushbackInputStream
3. JAVA有一些读者熟知的预定义输入和输出流:
System.in(java.io.BufferedInputStream)
System.out(java.io.PrintStream)
System.err(java.io.PrintStream)
4. JAVA提供了一系列所谓的过滤流来帮助使用流;特别的,过滤流提供了额外的功能,在将数据实际写入输出流之前对输出进行预处理,或者在读取数据之后对输入进行后期处理。过滤流的工作方式是:将输出流传递给过滤流构造函数,之后即可使用过滤流替代输出流。这样我们就能使用过滤流的方法来代替原始流的方法。
JAVA定义了下面的过滤流:
BufferedInputStream 提供缓冲输入操作
BufferedOutputStream 提供缓冲输出操作
DataInputStream 允许读取基元数据类型的二进制数据
DataOutputStream 允许读取基元数据类型的二进制数据
PushbackInputStream 建立one-byte pushback缓冲区,这样就可以不必读取该字节,而直接读取输入流中的下一字节。
PrintStream 提供以文本方式输出数据的方法,允许使用熟悉的print()和println()方法将字符串输出到二进制流
5. 有4个类支持文件操作:
FileInputStream: 提供InputStream接口,以从文件中顺序读取数据
FileOutputStream: 提供OutputStream接口,以向文件中顺序写入数据
RandomAccessFile: 提供一个专门机制随机地读写文件,而不是从头到尾顺序读取,可以随意地跳到文件的不同位置。Java.io.RandomAccessFile 在一个类中同时提供输入和输出,并提供与java.io.DataInputStream和 java.io.DataOutputStream类相似的方法从文件中读到基元类型以及将基元类型写入文件。
为方便随机访问文件,java.io.RandomAccessFile类提供了seek(long pos)方法。该方法从文件的开头到当前字节,加上一个文件指针和位置pos,文件指针所处区域便是可读写的区域。
long getFilePointer() 返回文件指针的当前位置
long length() 返回文件长度
seek(long pos) 将文件指针移到指定位置(相对于文件开头)
RandomAccessFile类可以打开文件以便读取或读写文件,文件模式将在构造函数中指定:
RandomAccessFile(File file, String mode)
RandomAccessFile(String name, String mode)
在这里,mode可以是r,用于指定文件以只读状态打开,也可以是rw,用于指定文件以可读写状态打开。
File: 提供管理功能,以获取有关文件和目录的信息
java.io.File中有两种方法可列出目录中的文件:
listFiles() 返回java.io.File对象数组
list() 返回饮食目录中所含文件的文件名的字符串对象数组
java.io.File还为文件分隔符提供了独立于平台之外的提取方法。File.separator和File.separatorChar将根据所运行的操作系统正确地解析字符。方法有:
isDirectory() getName()
Java提供的其它字节流类有:
ByteArrayInputStream 读取字数组,就像它是一个InputStream对象一样
SequenceInputStream 从两个或更多的InputStream对象串接数据
PipedInputStream 用于线程之间的通信
ByteArrayOutputStream 将输出发送到byte[]对象中
PipedOutputStream 与PipedInputStream进行通信以完成线程通信
6. 字符流类负责将每个字符正确地从原始OS编码方案转换为Unicode编码,这些类对不同的字符编码方案很灵敏,并且支持国际化应用程序。
面向字符的流:
Writer —— BufferedWriter
CharArrayWriter
OutputStreamWriter —— FileWriter
FilterWriter
PipedWriter
PrintWriter
—— StringWriter (两者共有)
Reader —— BufferedReader —— BufferedInputStream
CharArrayReader
FilterReader —— LineNumberInputStream
InputStreamReader —— PushbackInputStream
PipedReader
Java.io.Reader方法:
Boolean ready() 指示是否准备好读取该项流
int read() 读取单个字符
int read(char[] cbuf) 将字符读入数组
int read(char[] cbuf, int off, int len) 将字符读入数组的某一部分
long skip(long n) 跳过字符
java.io.Writer方法:
abstract void flush() 刷新流
void write(char[] cbuf) 写字符数组
abstract void wirte(char[] cbuf, int off, int len) 写字符数组的某一部分
void write(int c) 写单个字符
void write(String str) 写字符串
void write(String str, int off, int len) 写字符串的某一部分
7. JAVA为读写文件提供了两个类:
java.io.FileReader
java.io.FileWrite
6. JAVA提供了一些helper类,像reader一样读取输入流,像writer一样读取输出流。这两个类是:
java.io.InputStreamReader
java.io.OutputStreamWriter
可通过将现有流传递给各自的构造函数来使用这些类。例如:
InputStream is = new FileInputStream("in.txt");
Reader r = new InputStreamReader(is);
... use r ...
7. JAVA为从Reader类读取已有数据并将已有数据写入Writer类提供了两种reader方法和两种Writer方法:
·CharArrayReader 允许像Reader一样读取字符数组(char[])
·CharArrayWriter 允许将Writer写入字符数组(char[])
·StringReader 允许像Reader一样读取字符串对象
·StringWriter 允许将Writer写入字符串对象
为帮助使用print()和println()方法写入Writer数据,JAVA提供了java.io.PrintWriter类
一般来说,使用BufferedReader类来读取文本文件,这样可以一次读取一行文本,readLine();使用PrinterWriter类来写文本文件,因为该类具有我们熟知的print()和println()方法。
最后一类reader和writer是java.io.PipedReader和java.io.PipedWriter类。这两个类用于进程之间的通信。
8. java.io.StreamTokenizer类是一个工具类,可通过将Reader分解为标记来帮助分析Reader,可指定从Reader返回的字符串以空格分隔,我们可以将此理解为逐字返回Reader。该类对标记进行逐一分析,返回是否处于Reader结尾的状态信息,然后使该值可用作标记StreamTokenizer类(sval)的公共属性。
StreamTokenizer类通过调用nextToken()方法获取下一个标记,该方法返回下列状态值中的一个:
·TT_EOF 指示已读取流的结尾
·TT_EOL 指示已读取行的结尾
·TT_NUMBER 指示已读取一个数字标记
·TT_WORD 指示已读取一个字标记
访问StreamTokenizer类的sval公共属性使用StreamTokenizer检索字符串的值。
9. 许多I/O流把构造函数的参数视作另一种流类型。从流中构建流的术语被称为链接。其思想是:根据最好的方式读取每个流。如果数据是文本类型,则读取该数据的最好方式是通过BufferedReader,但如果数据是基元类型集合,那最好使用DataInputStream读取。
10. 要确保足够的通用性,以便在无需了解源数据的情况下就可以读取任何源数据,最好的办法就是围绕接口来编写程序,而不是围绕实现接口的类。这样在任何时候都不必修改底层实现。