Content
Chapter 1:对象导论
Chapter 2:一切都是对象
Chapter 3:操作符
Chapter 4:控制执行流程
Chapter 5:初始化与清理
Chapter 6:访问权限控制
Chapter 7:复用类
Chapter 1:对象导论
1.1 抽象过程
将问题空间中的元素及其在解空间中的表示称为“对象”。
OOP允许根据问题来描述问题,而不是根据运行解决方案的计算机来描述问题。
1.3 每个对象都提供服务
程序本身将向用户提供服务,它通过调用其他对象提供的服务来实现这一目的。
1.8 单根继承结构
所有的类最终都继承自单一的基类:Object。
单根继承结构使垃圾回收器的实现变得容易很多。
1.9 容器
不同容器提供了不同类型的接口和外部行为。
不同的容器对于某些操作具有不同的效率。
参数化类型-在java中呗称为范型,一对尖括号,中间包含类型信息,通过这些特征就可以识别对范型的使用。例如:
1 ArrayList
1.10 对象的创建和声明期
自动变量(automatic variable)或限域变量(scoped variable)对象被置于堆栈或静态存储区域内。
第二种方式是在被称为堆(heap)的内存池中动态的创建对象。
Java完全采用了动态内存分配方式。每当想要创建新对象时,就要使用new关键字来构建此对象的动态实例。
Java提供了被称为“垃圾回收器”的机制,它可以自动发现对象何时不再被使用,并继而销毁它。
1.11 异常处理:处理错误
异常处理将错误处理直接置于编程语言中,有时甚至置于操作系统中,异常是一种对象,它从出错地点被“抛出”,并被专门设计用来处理特定类型错误的相应的异常处理器“捕获”。
异常提供了一种从错误状况进行可靠恢复的途径。
Java一开始就内置了异常处理,而且强制你必须使用它。它是唯一可接受的错误报告方式。
Chapter 2:一切都是对象
2.2 必须由你创建所有的对象
特例:基本类型。
因为new将对象存储在堆里,故用new创建一个对象—特别是小的、简单的变量,往往不是很有效。对于这些变量,Java不用new来创建变量,而是创建一个并非是引用的“自动”变量。这个变量直接存储“值”,并置于堆栈中,因此更加高效。
Java要确定每种基本类型所占存储空间的大小,它们的大小并不像其他大多数语言那样随机器硬件架构的变化而变化。
2.3 永远不需要销毁对象
Java对象不具备和基本类型一样的生命周期。当用new创建一个Java对象时,它可以存活于作用域之外。
由new创建的对象,只要你需要,就会一直保留下去。
Java有一个垃圾回收器,用来监视new创建的所有对象,并辨别那些不会再被引用的对象,随后释放这些对象的内存空间,以供其他新的对象使用。
2.4 创建新的数据类型:类
若类的某个成员是基本数据类型,即使没有初始化,Java也会确保它获得一个默认值。当变量作为类型的成员使用时,Java才确保给定其默认值,以确保那些事基本类型的成员变量得到初始化。但是这些初始值对你的程序来说可能是不正确的,甚至是不合法的。所以最好明确地对变量进行初始化。
2.6 构建一个Java程序
通常来说,除非用new创建那个累的对象,否则实际上并未获得任何对象。执行new来创建对象时,数据存储空间才被分配,其方法才被外界调用。
当声明一个事物是static时,就意味着这个域或方法不会与包含它的那个类的任何对象实例关联在一起。所以,即使从未创建某个类的任何对象,也可以调用其static方法或访问其static域。
只需将static关键字放在定义之前,就可以将字段或方法设定为static。例如,下面的代码就生成了一个static字段并进行了初始化。
1 public class test { 2 public static void main(String[] args) { 3 statictest st1 = new statictest(); 4 statictest st2 = new statictest(); 5 System.out.println(st1.a + " " + st1.b + " " + st2.a + " " + st2.b); 6 st1.a = 3; 7 st1.b = 4; 8 System.out.println(st1.a + " " + st1.b + " " + st2.a + " " + st2.b); 9 } 10 } 11 12 class statictest{ 13 int a = 1; 14 static int b = 2; 15 }
通过代码运行结果可以看出,即使创建了两个statictest对象,statictest.b也只有一份存储空间,这两个对象共享一个b。
引用static变量有两种方法,除了通过一个对象去定位它,如st2.b,也可以通过其类名直接引用,如statictest.b。使用类名是引用static变量的首选方式,这不仅是因为它强调了变量的static结构,而且在某些情况下它还为编译器进行优化提供了更好的机会。
static方法的一个重要用法就是在不创建任何对象的前提下可以调用它。和其他方法一样,static方法可以创建或使用与其类型相同的被命名对象,因此,static方法常常拿来做“牧羊人”的角色,负责看护与其隶属同一类型的实例群。
Chapter 3:操作符
3.4 赋值
对基本数据类型赋值时, 是直接将一个地方的内容复制到了另一个地方。
但是为对象“赋值”时,对一个对象进行操作时,真正操作的是对对象的引用,实际是将“引用”从一个地方复制到另一个地方。例如:
1 public class Test { 2 public static void main(String[] args){ 3 Tank t1 = new Tank(); 4 Tank t2 = new Tank(); 5 t1.level = 9; 6 t2.level = 47; 7 System.out.println("1: t1.level: " + t1.level + ", t2.level: " + t2.level); 8 t1 = t2; 9 System.out.println("2: t1.level: " + t1.level + ", t2.level: " + t2.level); 10 t1.level = 27; 11 System.out.println("3: t1.level: " + t1.level + ", t2.level: " + t2.level); 12 13 } 14 } 15 class Tank{ 16 int level; 17 }
运行结果如下:
3.5 算数操作符
在创建Random对象时提供种子(用于随机数生成器的初始化值,随机数生成器对于特定的种子值总是产生相同的随机数序列),通过调用Random类的对象,程序可以生成许多不同类型的数字,只需调用方法nextInt()和nextFloat()即可。传递给nextInt()的参数设置了所产生随机数的上限,而其下限为0。
3.6 自动递增和递减
“++”和“--”有两种使用方式,通常称为“前缀式”和“后缀式”。“前缀递增”表示“++”操作符位于变量或表达式的前面,会先执行运算,再生成值。例如:
1 public static void main(String[] args){ 2 int i = 1; 3 System.out.println("i : " + i); 4 System.out.println("++i : " + ++i); 5 System.out.println("i : " + i); 6 System.out.println("i++ : " + i++); 7 System.out.println("i : " + i); 8 }
运行结果如下:
3.7 关系操作符
关系操作符“==”和“!=”适用于所有基本数据类型和所有对象,对对象而言比较的是对象的引用。想比较两个对象的实际内容是否相同时,必须使用所有对象都适用的特殊方法equals(),但这个方法不适用与基本数据类型。例如:
1 public class Test { 2 public static void main(String[] args){ 3 Integer n1 = new Integer(47); 4 Integer n2 = new Integer(47); 5 System.out.println("n1 == n2 ? " + (n1 == n2) + ", n1.equals(n2) ? " + n1.equals(n2)); 6 System.out.println("n1 != n2 ? " + (n1 != n2)); 7 8 Value v1 = new Value(); 9 Value v2 = new Value(); 10 v1.i = v2.i = 100; 11 System.out.println("v1 == v2 ? " + (v1 == v2) + ", v1.equals(v2) ? " + v1.equals(v2)); 12 } 13 } 14 class Value{ 15 int i; 16 17 /* 18 boolean equals(Value v){ 19 return this.i == v.i; 20 }*/ 21 }
运行结果如下:
由于equals()的默认行为是比较引用,所以在自己的新类中要重写equals()方法。
3.9 直接常量
直接常量后面的后缀字符标志了它的类型,L/l:long,F/f:float,D/d:double。
十六进制数适用于所有的整数数据类型,以前缀0x,后面跟随0-9或a-f表示。
八进制数由前缀0以及后续的0-7来表示。
在使用十六进制和八进制计数法时,以二进制形式显示结果非常有用,通过使用Integer和Long类的静态方法toBinaryString()可以实现这一点。
3.15 类型转换操作符
在将float或double转型为整型值时,总是对该数执行截尾。如果想要得到舍入结果,需要使用java.lang.Math中的round()方法。
Chapter 4:控制执行流程
4.3 迭代
Java里唯一用到逗号操作符的地方是for循环的控制表达式。在控制表达式的初始化和步进控制部分,可以使用一系列由逗号分割的语句。并且那些语句均会独立执行。
4.4 Foreach语句
foreach的简单例子:
1 //遍历float数组 2 float f[] = new float[arraysize]; 3 for(float x : f) 4 //遍历字符串 5 for(char c : "This is a String.".toCharArray())
foreach还可以用于任何Iterable对象。
4.6 break和continue
在任何迭代语句的主体部分,都可用break和continue控制循环的流程。其中,break用于强行退出循环,不执行循环中剩余的语句,而continue则停止执行当前的迭代,然后退回循环起始处,开始下一次迭代。
4.7 臭名昭著的goto
尽管goto仍是Java中的一个保留字,但在语言中并未使用它,Java没有goto。
Java中有与goto使用相同机制——标签的用法。Java中,标签起作用的唯一地方刚好是在迭代语句之前。下面是标签用于for循环的例子:
1 public static void main(String[] args){ 2 int i = 0; 3 outer: 4 for(; true ;){ 5 inner: 6 for(; i < 10; i++){ 7 System.out.println("i = " + i); 8 if(i == 2){ 9 System.out.println("continue"); 10 continue; 11 } 12 if(i == 3){ 13 System.out.println("break"); 14 i++; 15 break; 16 }if(i == 7){ 17 System.out.println("continue outer"); 18 i++; 19 continue outer; 20 } 21 if(i == 8){ 22 System.out.println("break outer"); 23 break outer; 24 } 25 for(int k = 0; k < 5; k++){ 26 if(k == 3){ 27 System.out.println("continue inner"); 28 continue inner; 29 } 30 } 31 } 32 } 33 }
continue label会终止迭代,直接转到label处继续执行迭代。但是break label终止迭代转到label处后并不重新进入迭代。
如果没有break outer语句,就没有办法从内部循环里跳出外部循环。这是由于break本身只能中断最内层的循环(continue同样也是如此)。同样的规则也适用于while。
4.8 switch
switch语句是实现多路选择(也就是从一系列路径中挑选一个)的一种干净利落的方法。但它要求使用一个选择因子,并且必须是int或char那样的整数值。
下面的代码使用switch判断随机生成的字符是元音字母还是辅音字母:
1 Random rand = new Random(47); 2 for(int i = 0; i < 20; i++){ 3 int c = rand.nextInt(26) + 'a'; 4 System.out.print((char)c + ", " + c + ": "); 5 switch(c){ 6 case 'a': 7 case 'e': 8 case 'i': 9 case 'o': 10 case 'u': 11 System.out.println("vowel"); 12 break; 13 case 'y': 14 case 'w': 15 System.out.println("Sometimes a vowel"); 16 default: 17 System.out.println("consonant"); 18 } 19 }
Chapter 5:初始化与清理
5.1 用构造器确保初始化
Java中构造器采用与类相同的名称。在创建对象时,将会为对象分配存储空间,并调用相应的构造器,来确保对象被操作之前已经被初始化。
构造器是一种特殊类型的方法,因为它没有返回值。
5.2 方法重载
每个重载的方法必须有一个独一无二的参数类型列表。参数的顺序不同也可以区分两个方法,不过一般情况下别这么做,会使代码难以维护。
在重载涉及基本类型时,如果传入的数据类型(实际参数类型)小于方法中声明的形式参数类型,实际数据类型就会被提升。char型有所不同,如果无法找到恰好接受char参数的方法,就会把char直接提升至int型。
如果传入的实际参数大于重载方法声明的形式参数,就得通过类型转换来执行窄化转换。
根据方法的返回值来区分重载方法是行不通的。
5.4 this关键字
this关键字只能做方法内部使用,表示“调用方法的那个对象”的引用。如果在方法内部调用同一个类的方法,就不必使用this。
Chapter 6:访问权限控制
Chapter 7:复用类