Java编程思想读书笔记(一)

来源:互联网 发布:linux 图形安装 kvm 编辑:程序博客网 时间:2024/04/30 23:24

第1章 对象导论

1. 将类的一个对象置于某个新的类中,称为“创建一个成员对象”。

2. 使用现有的类合成新的类,称为“组合”(composition),如果组合是动态发生的,通常被称为“聚合”(aggregation)。组合经常被视为“has-a”(拥有)关系,如“汽车拥有引擎”。

3. 在Java中,动态绑定是默认行为,不需要添加额外的关键字来实现多态。

4. 在Java中,所有的类最终都继承自单一的基类,这个终极基类就是Object。

5. 向上转型(派生类赋值给基类)是安全的,如Circle是一种Shape类型;但是不知道某个Object是Circle还是Shape,所以除非确切知道所要处理的对象的类型,否则向下转型(基类赋值给派生类)几乎是不安全的。

 

 

第2章 一切都是对象

1. 在Java里,一切都被视为对象。尽管一切都看作对象,但操纵的标识符实际上是对象的一个“引用”。引用可独立存在,即你拥有一个引用,并不一定需要有一个对象与它关联。例:如果想操纵一个词或句子,则可以创建一个String引用:String s;但这里所创建的只是引用,并不是对象。

2. 对象的存储位置:

(1)寄存器。这是最快的存储区。

(2)。位于通用RAM中,这是一种快速有效的分配存储方法,仅次于寄存器。

(3)。一种通用的内存池(也位于RAM区),用于存放所有的Java对象。

(栈比堆要高效,考虑:栈指针保存在寄存器中,堆分配空间时要做一系列查找分配工作。)

(4)常量存储。常量值通常直接存放在程序代码的内部,这样做是安全的,因为它们永远不会被改变。

(5)非RAM存储。如磁盘等。

3. 基本类型不用new来创建变量,而是创建一个并非是引用的“自动”变量。这个变量直接存储“值”,并置于栈中,因此更加高效。如boolean、char(16-bit)、byte(8bits)、short(16bits)、int(32bits)、long(64bits)、float(32bits)、double(64bits)、void。

4. 在Java中,每种基本类型所占存储空间的大小并不像其它大多数语言那样随机器硬件架构的变化而变化,这是其更具可移植性的原因之一。

5. Java支持有符号数。

6. Java确保数组会被初始化,而且不能在它的范围之外被访问。这种范围检查,是以每个数组上少量的内存开销及运行时的下标检查为代价的。

7. 尽管以下代码在C和C++中是合法的,但在Java中却不能这样书写:

{

         intx = 12;

         {

                   intx = 96; //illegal

}

}

编译器将会报告变量x已经定义过。

8. Java对象不具备和基本类型一样的生命周期。当用new创建一个Java对象时,它可以存活于作用域之外。如下代码:

{

         Strings = new String(“a string”);

}// end of scope

引用s在作用域终点就消失了。然而,s指向的String对象仍继续占据内存空间。

9. 若类的某个成员是基本数据类型,即使没有进行初始化,Java也会确保它获得一个默认值。(char类型被初始化为一个空格)注意:当变量作为类的成员使用时,Java才确保给定其默认值,以确保那些是基本类型的成员变量得到初始化(C++没有此功能),防止产生程序错误。然而确保初始化的方法并不适用于“局部”变量(即并非某个类的字段)。如果在某个方法定义中有 int x; 那么变量x得到的可能是任意值(与C和C++中一样),而不会被自动初始化为零。

10. 方法的参数列表中必须指定每个所传递对象的类型及名字。像Java中任何传递对象的场合一样,这里传递的实际上也是引用。对于前面所提到的特殊数据类型boolean等来说是一个例外。通常,尽管传递的是对象,而实际上传递的是对象的引用

11. 一个static字段对每个类来说都只有一份存储空间,而非static字段则是对每个对象有一个存储空间。

补充:通常,static方法不能直接调用非static成员变量和方法。对于一般的非static成员变量和方法来说,需要有一个对象的实例才能调用,所以要先生成对象的实例,它们才会实际的分配内存空间。而对于static的对象和方法,在程序载入时便已经分配了内存空间,它只和特定的类相关联,无需实例化。如果要在static方法中调用非static成员变量和方法,须先实例化,在static方法中调用实例对象的非static成员变量和方法。

12. java.lang 是默认导入到每个Java文件中的,所以它的所有类都可以被直接使用。(编写的代码中,java.lang不会显式出现)。

 

 

第3章 操作符

1. 对象“赋值”:对一个对象进行操作时,我们真正操作的是对对象的引用。所以倘若“将一个对象赋值给另一个对象”,实际是将“引用”从一个地方复制到另一个地方。(引用于对象之间存在关联,但这种关联可以被改变。)

2. ==和!=比较的是对象的引用。equals()方法的默认行为是比较引用,如果定义类的对象中对equals()方法进行重写,则可以实现比较对象的实际内容是否相等的效果。

3. “与”(&&)、“或”(||)、“非”(!)操作只可应用于布尔值。与在C和C++中不同的是:不可将一个非布尔值当作布尔值在逻辑表达式中使用。注意,如果在应该使用String值的地方使用了布尔值,布尔值会自动转换成适当的文本形式。

4. 如果对char、byte或者short类型的数值进行移位处理,那么在移位进行之前,它们会被转换为int类型,并且得到的结果也是一个int类型的值。

5. 直接将float或double转型为整数值时,总是对该数字执行截尾。如果想要得到四舍五入的结果,需要使用java.lang.Math中的round()方法。

6. 只要类型比int小(即char、byte或者short),那么在运算前,这些值会自动转换成int。通常,表达式中出现的最大的数据类型决定了表达式最终结果的数据类型。float*double=double,int*long=long.

7. Java没有sizeof,因为所有数据类型在所有机器中的大小都是相同的。

 

 

第4章 控制执行流程

1. Java编译器生成它自己的“汇编代码”,但是这个代码是运行在Java虚拟机上的,而不是直接运行在CPU硬件上。

2. switch语句要求使用一个选择因子,并且必须是int或char那样的整数值。假若将一个字符串或者浮点数作为选择因子使用,那么它们在switch语句里是不会工作的。

 

 

第5章 初始化与清理

1. 每个重载的方法都必须有独一无二的参数类型列表。(参数顺序的不同也足以区分两个方法,但不建议这样做,会使代码难以维护。)

2. 方法重载时,如果可以重载的方法间只是参数类型不同,传入的数据类型(实际参数类型)小于方法中声明的形式参数类型,实际数据类型就会被提升至该方法所接受的类型。char型略有不同,如果无法找到恰好接受char参数的方法,就会把char直接提升至int型。(P81)

如果传入的实际参数较大,就得通过类型转换来执行窄化转换。如果不这样做,编译器就会报错。即先类型转换,后传入参数。

3. 要是你没有提供任何构造器,编译器会认为“你需要一个构造器,让我给你制造一个吧”;但假如你已写了一个构造器,编译器就会认为“啊,你已写了一个构造器,所以你知道你在做什么;你是刻意省略了默认构造器。”即编译器此时是不会为你制造一个默认构造器的。

4. this关键字只能在方法内部使用,表示对“调用方法的那个对象”的引用。

5. 可能为一个类写了多个构造器,有时可能想在一个构造器中调用另一个构造器,以避免重复代码,可用this关键字做到。此时,在构造器中,如果为this添加了参数列表,将产生对符合此参数列表的某个构造器的明确调用。如:

public class A{

         A(Strings){

}

         A(Strings,int i){

         this(s);//相当于A(s);

}

}

然而,需要遵守如下规则:

(1)尽管可以用this调用一个构造器,但却不能在一个构造器中调用两个构造器,即在一个构造器中最多只能调用一个构造器;

(2)必须将构造器调用置于最起始处,否则编译器会报错;

(3)除构造器之外,编译器禁止在其他任何方法中调用构造器。

6. static方法就是没有this的方法。在static方法的内部不能调用非静态方法,当然,这不是完全不可能:如果你传递一个对象的引用到静态方法里,然后通过这个引用,你就可以调用非静态方法和访问非静态数据成员了。

7. Java中垃圾回收遵守的原则:

(1)对象可能不被垃圾回收;

(2)垃圾回收并不等于“析构”;

(3)垃圾回收只与内存有关。

如果JVM并未面临内存耗尽的情形,它是不会浪费时间去执行垃圾回收以恢复内存的。

8. 在C++中可以创建一个局部对象(也就是在栈上创建,这在Java中行不通),在Java中不允许创建局部对象,必须使用new创建对象。(Java对象都在堆上创建,不能在栈上创建

9. 在类的内部,变量定义的先后顺序决定了初始化的顺序。即使变量定义散布于方法定义之间,它们仍旧会在任何方法(包括构造器)被调用之前得到初始化。

10. 静态对象的初始化先于非静态对象,静态对象只被初始化一次。

11. 在声明数组时,编译器不允许指定数组的大小。即这样:int a[10];编译器会报错。数组元素中的基本数据类型值会自动被初始化。

12. switch与enum是绝佳的组合。

 

 

第6章 访问权限控制

1. 访问权限控制的等级,从最大权限到最小权限依次为:public、protected、包访问权限(没有关键字)和private

2. 如果不提供任何访问权限修饰词,则意味着它是“包访问权限”,即当前的包中的所有其他类对那个成员都有访问权限,但对于这个包之外的所有类,这个成员却是private。

3. 使用关键字public,就意味着public之后紧跟着的成员声明自己对每个人都是可用的。

4. 关键字private的意思是,除了包含该成员的类之外,其他任何类都无法访问这个成员。(注意:C++中声明为private,只能是类本身,以及友元函数和友元类访问;类的对象实例是不能访问private类成员的。而Java中private属性的权限扩大到了包含该成员的整个类范围。)

5. protected(继承访问权限):基类的创建者会希望有某个特定成员,把对它的访问权限赋予派生类而不是所有类。这就需要protected来完成这一工作。protected也提供包访问权限,即相同包内的其他类可以访问protected元素。(毕竟protected大于包访问权限)

6. 类既不可以是private的,也不可以是protected的(事实上,一个内部类可以是private或protected的但那是特例)。所以对于类的访问权限,仅有两个选择:包访问权限或public。

7. 相同目录下的所有不具有明确package声明的文件,都被视作是该目录下默认包的一部分。

 

 

第7章 复用类

1. 每一个非基本类型的对象都有一个toString()方法,而且当编译器需要一个String而你却只有一个对象时,该方法便会被调用。

2. 当创建一个类时,总是在继承,因此,除非已明确指出要从其他类中继承,否则就是在隐式地从Java的标准根类Object进行继承。

3. 当创建一个导出类的对象时,对象所包含的基类的子对象被包装在导出类对象的内部。Java会自动在导出类的构造器中插入对基类构造器的调用。

4. @Override注解表明覆盖某个方法,可以防止在不想重载时而意外地进行了重载。

5. “is-a”的关系是用继承来表达的,而”has-a”的关系则是用组合来表达的。

6. 新类是现有类的一种类型。由导出类转型成基类,在向上转型的过程中,类接口中唯一可能发生的事情是丢失方法,而不是获取它们。

7. 一个既是static又是final的field(个人理解:字段,变量)只占据一段不能改变的存储空间。

8. 当对对象引用运用final时,引用恒定不变。一旦引用被初始化指向一个对象,就无法再把它改为指向另一个对象。然而,对象其自身却是可以被修改的。

9. 类中所有的private方法都隐式地指定为是final的。由于无法取用private方法,所以也就无法覆盖它。可以对private方法添加final修饰词,但并不能为该方法增加任何额外的意义。

10. 当将某个类的整体定义为final时,表明你不打算继承该类,而且也不允许别人这样做。换句话说,出于某种考虑,你对该类的设计永不需要做任何变动,或者处于安全考虑,你不希望它有子类。注意:final类的field可以根据个人意愿选择为是或不是final,但由于final类禁止继承,所以final类中所有的方法都隐式指定为final的,因为无法覆盖它们。在final类中可以为方法添加final修饰词,但无任何额外意义。

11. Java中允许生成“空白final”,所谓空白final是指被声明为final但又未给定初值的field。无论什么情况,编译器都确保空白final在使用前必须被初始化。它使得一个类中的final field可以做到根据对象而有所不同,但又保持其恒定不变的特性。(通常在类中定义final field,在不同的构造器中给予不同的初值。)

12. 类的代码在初次使用时才加载。通常是指加载发生于创建类的第一个对象之时,但是当访问static域或static方法时,也会发生加载(构造器也是static方法,尽管static关键字并没有显示给出,因此更准确地讲,类是在其任何static成员被访问时加载的)。初次使用之处也是static初始化之处。

假设有一public类A,运行A.java代码的过程:第一件事情是试图访问A.main()(一个static方法),于是加载器开始启动并找出A类的编译代码(A.class)。在对它进行加载的过程中,如果它有一个基类,于是继续进行加载。如果该基类还有其自身的基类,那么第二个基类就会被加载,如此类推。接下来,根基类中的static初始化即被执行,然后是下一个导出类,以此类推,必要的类都加载完后,对象就可以被创建了。

说明:不能误认为所有执行都是从main()开始的,典型的例子是:public类里有static field需要被初始化,而这个field在初始化过程中有输出的话,它会优于main()中的输出,其实想想,如果static field没有先被初始化,在main()中万一用到它,就来不及了。所以类的加载必须保证先对static field进行初始化(如果定义处确实需要初始化)。详见P146。

总结:运行Java代码虽是从main()进入的,但在真正执行前,有必要看当前public类有木有基类(因为往往导出类会与基类有关联),一直上溯。到最顶层时,应该看static field是否明确要求初始化,若有,必须先初始化,一直向下递推。等所有准备工作都做好了,对象才可以被创建。(这其中体现了一种依赖的思想:如果A的发生是在B已经发生的前提下进行的,那么要使得A发生,必须确保B已经发生。类的继承、static field(属于类的)即是此。)

13. 尽管面向对象编程对继承极力强调,但在开始一个设计时,一般应优先选择使用组合(或者可能是代理[代理使得该类中的方法不必完全暴露在使用它的类中,而是可以选择性地调用它的方法。详见P131]),只在确实必要时才使用继承。

 

 

第8章 多态

1. Java中除了static方法和final方法(private方法属于final方法)之外,其他所有的方法都是后期绑定。这意味着通常情况下,我们不必判定是否应该进行后期绑定—它会自动发生。

2. 只有非private方法才可以被覆盖,在导出类中,对于基类中的private方法,最好采用不同的名字。

3. Java中不具有多态性的情况:

(1)static、final(private)方法;

(2)在编译期进行解析的field,通常为类定义中已初始化的基本数据类型。

4. 对象初始化顺序:

(1)在其他任何事物发生之前,将分配给对象的存储空间初始化为二进制的零;

(2)调用基类构造器。这个步骤会不断地反复递归下去,首先是构造这种层次结构的根,然后是下一层导出类,等等,直到最低层的导出类;(当然,如果基类中有成员对象,先对成员对象进行初始化,这一点和C++一致,也是体现依赖的思想。)

(3)按声明顺序调用成员的初始化方法;(指最低层的导出类中的成员)

(4)调用导出类构造器的主体。

即:基类成员对象构造器、基类构造器、导出类成员对象构造器、导出类构造器。


0 0
原创粉丝点击