首页 > 代码库 > Thinking in Java 笔记
Thinking in Java 笔记
大二就买了这本书,如今再看这本书,看到了一些以前没看的细节,也有了不同的体会。本文使用第4版,整理每章的笔记心得。老外的书有个特点,他会花费大量的文字去阐述一个概念,这比堆代码强多了。
第 1 章 对象导论
1.1 抽象
抽象是计算机最重要的概念之一。C 在解决问题时,主要是基于计算机的结构进行抽象,而不是基于所要解决的问题的结构。而 Java 则是针对问题进行建模,根据问题来描述问题,程序可以通过添加一个新类型的对象使自身适用于某个特定问题,程序员不会受限于任何特定类型的问题。
对象具有状态、行为和标识。纯粹的面向对象程序设计的基本特性:
- 万物皆为对象:对象可以存储数据(状态),也能执行某些操作(行为)。理论上,可以抽取待解决问题中的任何概念化构建为对象。
- 程序是对象的集合,它们通过发送消息告知彼此所要做的:其实就是调用特定对象的方法。
- 每个对象都有自己和其他对象构成的存储:通过现有对象生成新对象,继承和组合。
- 每个对象都拥有其类型:每个对象都是某个类的实例 。
- 一类对象可以接收同样的消息:多态,子类能够替代父类完成某些调用。
1.2 类和对象
每个对象都是某个类的实例,一个类实际上就是一个抽象数据类型,它描述了具有相同特性(成员)和行为(功能)的对象集合。程序员可以通过定义类来适应问题,而不是被迫使用现有的用来表示机器中的存储单元的数据类型。根据需求添加新类型扩展编程语言,系统就像对待内置类型一样验证并管理它们。
把对象看做是服务提供者好处:将问题分解为一系列对象的集合;提高对象的内聚性,每个对象都专注本职工作,就是高内聚性,还有个低耦合,解耦一般就用队列实现;方便理解代码和重用。
面向对象的特点:封装、继承和多态。何为封装?即隐藏对象的属性和细节,仅对外公开接口,控制属性的读取和修改。访问控制存在的原因:隔离与提供的服务无关的部分;分离并保护接口和实现。Java 中有四种访问权限,分别是:
- public:对任何人都可访问
- private:只有类创建者和内部方法可访问
- protected:与 private 相当,区别是子类可访问
- 默认访问权限:同一包内的类可相互访问,不同包如同 private 一样
1.3 代码复用
两种方式,组合和继承,组合灵活性比较高,相比于继承耦合度低一些。如果要使用某个类提供的服务功能时,一般用组合,当要是使用类提供的接口时使用继承。
继承,使用现有的类创建新类型。子类拥有父类的成员(public, protected)并且复制了父类的接口,也就是说,子类与父类具有相同的类型。子类有两种方式改变自己的行为:添加新方法和覆盖父类的方法。当添加新方法时,如果所有子类都需要,就可以把它抽取到父类中。
Java 中,Object是所有类的直接或间接父类。单根继承的好处有:所有对象具有共用接口,利于向后兼容;所有对象都具备某些功能,易于创建和参数传递;易于垃圾回收。
1.6 多态
既然父类和子类是相同类型,那么在运行时子类就能替换父类(向上转型)来完成不同的功能,这就是多态。多态的体现是:方法的重载和覆盖。编译器(静态分发,重载)和运行系统(JVM动态分发,覆盖)会处理相关细节,保证程序正确的行为。
1.7 容器和泛型
容器,就是Java中的数据结构了,不同的容器提供不同的接口和行为,对于某些操作具有不同的效率。在JDK 5 之前容器存储的对象是Obejct,存储对象必须向上转型,会丢失其身份,当取出时需要向下转型,可能会出错,因为不知道之前放进去的是什么类型的对象,因此,JDK5增加了泛型,明确指出容器可接收的对象类型。
1.8 对象的创建和生命周期、异常和并发
Java 使用动态内存分配技术,使用关键词 new 在堆上创建对象。使用垃圾回收器释放对象占用的内存。
Java 内置了异常的处理,而且强制使用。异常提供了一种从错误进行可靠恢复的途径。
并发控制好共享资源的访问即可,Java提供了并发编程库,有现成可用的并发数据结构。
1.9 Java与Internet
网络编程会涉及到很多知识,TCP/IP,多线程,IO模型等,要写出高性能的Java程序,还是要下大工夫的,虽然大问题被JVM搞定了。
第 2 章 一切都是对象
2.1 对象
Java 通过引用来操作对象,使用 new 创建对象。那么对象被安置在哪个地方呢?计算中有5个地方可以存储数据,分别是:
- 寄存器:位于CPU内部,最快的存储区,根据需求进行分配,不能直接控制
- 堆栈:位于RAM,使用堆栈指针上下移动分配内存,速度仅次于寄存器,Java对象不在此,但引用在这里。Java系统知道栈里的元素确切生命周期
- 堆:位于RAM,存放所有的 Java 对象,分配内存比较灵活,但代价是慢
- 常量存储:常量值通常放在程序代码内部,或者放在 ROM 只读存储器中
- 非RAM存储:如将对象序列化到磁盘上,或者存在数据库中
2.2 基本类型
基本类型存储值,并置于堆栈中,高效。Java 中基本类型的大小是固定的,不随硬件架构的变化而变化。基本类型如下:
- char:16-bit,2字节,Unicode字符,0 ~ 2^16-1,Character
- byte:8 bits,1字节,-128 ~ 127,Byte
- short:16 bits,2字节,-2^15 ~ 2^15-1,Short
- int:32 bits,4字节,-2^31 ~ 2^31-1,Integer
- long:64 bits,8字节,-2^63 ~ 2^63-1,Long
- float:32 bits,4字节,IEEE754,Float
- double:64 bits,4字节,IEEE754,Double
- boolean:true/false,Boolean
- void:Void
所有数值均有正负号,JDK5的自动包装功能,自动地将基本类型转为包装类型。
高精度数字:BigInteger:支持任意精度的整数;BigDecimal:支持任意精度的定点数。
数组也是对象,能存储基本类型和引用类型,Java会确保数组被初始化。
2.3 作用域 scope
Java 使用花括号定义作用域,局部变量在花括号结束时,生命周期就结束了,而对象不是如此,它能一直保存下去,Java通过垃圾回收器管理对象的内存。一般不会出现内存泄漏,但也不是绝对的。
2.4 类,字段,方法
使用 class 关键字定义一个类,类拥有字段(成员变量)和方法,对于成员变量,即使没进行初始化,Java也会保证它有一个默认值,引用类型默认为null,数字都默认为0,布尔默认false,char默认’\u0000’(null)。对于局部变量编译器会强制进行初始化。
方法,方法名和参数合起来称为方法签名,关于参数,在Java中只有值传递。Java消除了向前引用的问题,也就是同一个类中,成员变量和方法的先后顺序可以随意。
static关键字可用来修饰字段,方法和类。修饰字段方法:表示属于类,不用新建对象就可使用。一般是修饰内部类,此类与一般的类没有差别。
2.5 注释
常用标签和html如下:
- @see:引用其他类,@see classname,@see fully-qualified-classnam#method-name
- @author:作者信息
- @version:版本信息
- <pre>code</pre>:代码
- <ol><li>1<li>2</ol>:列表
第 3 章 操作符
3.1 优先级&赋值运算符
从左到右先乘除后加减,当不确定时,使用括号明确标识即可。
赋值运算符(=),对基本类型赋值就是把一个地方的内容复制到另一个地方,比如int a=b,就是把b的内容复制给a;对对象赋值只是使这个变量也指向该对象,比如String s = a,s和a指向同一个对象。将对象传给方法时,也只是传递一个引用的值,或者说传递一个对象的别名。
3.2 算术、关系、逻辑运算符,直接常量
加减乘除,取模,一元加、减操作符,自增,自减。
== 作用于基本类型,比较值是否相等;作用于对象比较是否是同一个引用,比较对象使用equals,默认的equals比较引用,需要重写。
与(&&)、或(||)、非(!)生成一个布尔值,具有短路功能,即如果第一个表达式能确定整个表达式的结果,那么就不会运算后面的表达式。
直接常量,必须明确告诉编译器常量类型,比如10F,10D,10L,0xFF。对于char、byte、short超过其最大范围自动转为int。
指数计数法:float a = 1.39E-43F;表示1.39×e^-43,如果不加F编译器默认会当做double处理,会提示类型转换。
3.3 位操作符和移位操作符
位操作符:
- 与(&):同为1,输出为1
- 或(|):有1,输出为1
- 异或(^):不全为1,输出为1
- 非(~):取反
移位操作符,只能用来处理整数,char、byte、short移位时自动转为int:
- 左移(<<):低位补0,相当于乘以2^n,n为移动位数
- 右移(>>):使用符号位扩展,为正高位补0,为负高位补1,相当于除以2^n,n为移动位数
- 无符号右移(>>>):高位用0扩展
在进行移位时,比如int只有数值右端的低5位才有用,比如 16>>2 和 16>>34 相等,因为2^5=32,相当于对32取模。long类型就是低6位数字有效。
这里多说两句,源码中或进行移位时会经常看到(&0xFF),原因是什么呢?
一般我们会对字节byte进行操作,首先 0xFF 表示低8位(1字节),当对byte移位操作时,会自动转成int,而Java中 int类型有32位,并且在计算机中数字使用有符号的二进制补码表示。所以byte转为int时会进行符号扩展,高位以符号位填充。如果byte为正数那么其补码与原码相同,此时进不进行位与操作都无所谓,但是为负数时不同,比如byte a = -4;其转为int在内部的表示就是11111111111111111111111111111100,这进行运算显然是不正确的,有效位只有低八位,所以与0xFF位与运算,把高24位置0,运算结果就正确了。
Thinking in Java 笔记