您的位置:首页 > 编程语言 > Java开发

Thinking in Java 笔记1-11

2015-07-05 22:07 375 查看
2.2.1 Java中变量可能存在于5个区域:(充分显示了箱子模型(Box Model)和箭头模型(Arrow Model)的差异)

寄存器 — 不能直接控制

堆栈 — 对象引用存在于其中 但对象本身不存在其中

堆 — 用于存放所有对象

非常灵活 编译器不需要知道对象的生命周期

代价是堆中的存储分配,清理可能比堆栈中慢

常用储存,直接存放在代码中,可能被存放在ROM中

非RAM储存

例子:

流对象

持久化对象

持久化机制

Java提供了轻量级的持久化的支持

还有JDBC等机制

2.2.2 BitInteger和BitDecimal是一种以速度换精度的实现方式

2.2.3 Java数组类型

Java确保数组会被初始化

以少量内存开销和运行检查为代价换取安全性和效率的提高(Java有时还会优化这些数组)

创建数组对象的本质是创建了引用数组

每个引用都会被自动初始化 — null

2.4 Java的字段

字段有时也被称作数据成员

字段如果非原生类型,必须进行初始化操作

如果变量是一个局部变量,那么将不会进行初始化,将得到一个随机值,如果没有定义,则会出现Error,而不是Warning

2.8.2 Javadoc

使用javadoc的方法用两种

嵌入HTML代码

使用”文档标签“

javadoc会插入自己的标题 所以HTML代码不要使用\

除了内部类,类只有包访问权限和public

7.1 初始化引用的四种方法

在定义对象的地方初始化,能够在构造器调用前被初始化

类的构造器中

在使用这些对象前,又称惰性初始化

使用实例初始化

7.4.1 保持正确清理

将清理动作置于finally语句中

7.4.2

@Override 覆写某个方法时,可以选择加这个注解,防止不小心写成重载

7.8 final关键字

final数据

一个永不改变的编译时常量 — 减少了运行时负担

一个运行时被初始化的值,并不希望再被改变

即使static又是static的域用大写表示

final参数

允许参数列表将参数列明为final,相当于只读引用,但引用指向的对象可变

final方法

方法锁定,防止继承类修改它的意义

效率 — 这种想法正在收到劝阻,如果不是第一个原因,可以不用final了:编译器发现一个方法是final是会根据判断,将该方法的所有调用转为内嵌调用,并以方法体内地实际代码的副本代替方法调用,这能消除额外开销

新版本的虚拟机(特别是hotspot技术),可以优化去掉效率反而降低的内嵌调用,因此可以不用final方法优化了。

final类

类锁定,防止类继承

final类的域可以根据个人的意愿选择是不是final,也就是说final类仅限制继承,而不是将字段final化

7.9 初始化和类的加载

每个类的编译代码都存在于它自己的独立文件中

该文件只在被需要还是才会被加载

加载逻辑:

仍然遵守无继承时的逻辑

static成员完全加载完后才加载非static

仍然按照从基类到派生类的顺序

修正 先构建器再非static成员

多态的好处:

创建可拓展(Scalable,也是Scala的目的之一)的程序

改善代码的组织结构和可读性(另外一种思潮 — DuckType也是类似多态或者就是多态的一种)

方法调用绑定

前期绑定 — 在程序执行前绑定 — 面向过程语言默认绑定方式

后期绑定(动态绑定/运行时绑定) — 运行时根据对象的类型进行绑定

需要一种机制,能在运行时确定对象类型(Java 反射机制)

Java除了static方法和final方法之外,其他所有方法都是默认后期绑定

final方法能够关闭动态绑定。然而大多数情况性能不会有什么改观,所以最好根据设计决定用不用final,而不是提高性能的目的

8.2.3 可拓展性

在一个良好的OOP程序中,大多数或者所有方法都应该只于基类接口通讯。这样的程序是可扩展

缺陷

只有非private方法才会被覆盖,对于导出类中,对于基类的private方法,最好用不同的名字

只有普通的方法调用可以多态。如果直接访问一个域,访问在编译期就会解析(不通过get/set方法)

任何域访问操作都会由编译器解析,因此不是多态的。

所以应该尽量把变量设置成private

如果方法是静态的,则不具备多态性

静态方法与类,而非对象绑定

8.3 构造器与多态

构造器是static方法,只不过static声明是隐式的。

调用顺序:

调用基类构造器

声明顺序调用成员初始化方法

调用导出类构造器的主体

实际做的:

在任何事物发生前,将对象的存储空间初始化为0

如果构造器调用了基类的构造器中,调用了被覆盖过的方法,则调用派生类的版本(这时派生类的对象的构造器还没有被调用,很可能出问题)

按照声明的顺序调用成员初始化方法

调用导出类的构造器主体

优点:

所有东西都至少初始化为0,而不是垃圾。

准则:

用尽可能简单的方法使对象进入正常状态,如果可以,避免调用别的方法

在构造器类唯一可以安全调用的方法是基类的final方法和private方法,因为它们不会覆盖

8.4 协变返回类型

导出类中被覆盖的方法可以返回基类方法返回类型的某种导出类型

导出类返回基类方法中返回类型的子类

8.5 用继承进行设计

用组合能获得更多灵活性(比如状态模式),但要求编译时确定类型

通则:用继承表达行为的差异,用字段表示状态的差异

8.5.1 纯继承与拓展

继承是一种is-a关系

Is-like-a关系通过拓展接口实现

通过向下转型实现对拓展的方法调用

8.5.2 向下转型与运行时类型识别

Java中所有转型都会得到检查

如果检查结果为失败会返回一个ClassCastException(类转换异常)

这种检查被称作RTTI(运行时类型识别)

RIIT不仅包括类型检查,还有其他功能

比如提供一种方法,查看你所要处理的类型

9.1 抽象类和抽象方法

abstract关键字用于声明抽象方法

如果子类要能够创建对象,就需要实现基类所有方法,否则加一个abstract限定这个类。

可以使类的抽象性明确

还是很有用的抽象工具,使能沿公共方法沿着继承层次结构向上移动

9.2 接口

被用来建立类与类之间的协议(某些语言用protocol表示,scala为trait(特质)我觉得更加优雅)

接口可以包含域,但是这些域为隐式的static和final的

可以显式声明方法为public,但它们默认为public

将接口的具体实现中解耦使得接口可以应用于多种不同的具体实现,因此增加可复用性

接口也可以继承

9.4 多重继承

C++中的多重继承会使你背上很重的包袱,因为每个类都有具体实现

在导出类中,不强制要求必须有一个是抽象的或者具体的基类

使用接口的核心原因:

为了能够向上转型为多个基类型

防止客户端程序员创建该类的对象(同抽象类)

接口还是抽象类:

如果要创建不带任何方法定义和成员变量的基类,那么用接口

第一选择用接口

9.5.1 组合接口的名字冲突

在打算组合的不同接口中使用相同的方法名通常会造成代码可读性的混乱,应该尽量避免这种情况

9.6 适配接口

接口的一种常见用法就是策略设计模式。

编写一个执行某些操作的方法,而在方法将接受一个同样是你指定的接口。

使你的方法更灵活,通用,可复用

9.7 接口中的域

可以实现类似enum的功能

需要变量初始化

值位于接口的静态存储区域

最好用enum替代这种写法

9.8 嵌套接口

接口可以嵌套类或者其他接口。

在类中嵌套接口非常常见 – 可以拥有public和“包访问”两种可视性

接口可以为private

可以强制该接口的方法不要添加任何数据类型(即 不允许向上转型)

最初可能是为了语法一致性

9.9 接口与工厂

接口是实现多重继承的途径,而生成遵循遵循某种接口的对象的典型方式就是工厂模式

完全于接口的实现分离

10.2 链接到外部类

内部类对象秘密地捕获一个这个外围类的引用。

在你访问该外部类的成员时,就是使用这引用来选择外围类的成员。

内部类的对象只能在与外围类的对象相关联才能创建。(非static内部类)

如果编译器访问不到这个引用则会报错

10.3 .new和.this

.this用于生成对外部类对象的引用

编译器就能知晓并受到检查,所以没有额外开销

.new用于提供对其他外部类对象的引用

不能直接引用外部类的名字,必须使用外部类的对象来创建该内部类对象

拥有内部类对象的前提是拥有外部类对象

但如果是静态内部类(嵌套类),就不需要对外部类对象的引用

10.4 内部类和向上转型

将内部类向上转型为基类,尤其是接口时,内部类就有了用武之地

实现了某个接口的对象,得到对此接口的引用,与向上转型为这个对象的基类,效果一致

不能访问静态类就像不能访问静态成员一样

private内部类给类的设计者提供了一种途径来完全组织任何依赖于类型的编码,并完全隐藏了实现的细节

10.5 在方法和作用域类的内部类

可以在方法里面或者在任意的作用域类定义内部类

理由:

实现了某类型的接口,于是可以创建并返回对其的引用

想要解决一个复杂的问题,想创建类来辅助你的解决方案,但不希望该类是公共可用的

可以:

一个定义在方法中的类

一个定义在作用域的类,并作用域在方法内部

一个实现了接口的匿名类

一个匿名类,它拓展了有非默认构造器的类

一个匿名类,它执行字段初始化

一个匿名类,它通过实例初始化实现构造(匿名类不能有构造器)

10.6 匿名内部类

在匿名类中定义字段时,还可以进行初始化

形参必须要是final或等效的final

可以实例初始化来让匿名内部类创建一个构造器的效果(就是一块域)

限制:你不能重载实例初始化的方法,所以你只能有一个这样的构造器

10.7 嵌套类(静态内部类)

如果不需要内部类对象和外围类对象有联系,那么可以将其变为嵌套类

普通内部类引式保存了一个引用。

嵌套类:和C++相似,不过C++中哪些类不能访问私有成员

创建内部类对象,但不需要外围类的对象

不能从嵌套类的对象中访问非静态的外围类对象普通内部类的字段与方法。

普通内部类不能有static数据和字段,也不能包含嵌套类,但嵌套类可以

10.7.1 接口内部的类

嵌套类可以作为接口的一部分,自动是public,static的。

10.8 为什么需要内部类

每个内部类都能独立继承一个接口的实现,无论外围类情况如何。

如果拥有的是抽象类或具体的类,而不是接口,那就只能使用内部类才能实现多重继承

其他特性:

内部类可以有多个实例,每个实例都有自己的状态信息,并与外围类对象信息相互独立

单个外围类中,可以让多个内部类以不同形式实现同一个接口,或继承同一个类

创建内部类的对象的适合并不依赖于外围类对象的建立

内部类无让人困惑的is-a关系,它就是一个独立的实体

10.8.1 闭包和回调

内部类是面向对象的闭包,

通过内部类提供闭包的功能是优良的解决方案,比指针更灵活,安全

10.9 内部类的继承

需要在导出类中传入外部类的引用并调用外部类对象的构造函数

10.10 内部类的覆盖

内部类的覆盖类似于方法的覆盖

这些内部类都是独立的个体,出于各自的名字空间中

但也可以指明需要继承的内部类 — 实现多态

10.11 局部内部类

能访问当前代码块的常量

为什么依然使用局部内部类而不是匿名内部类?

需要一个已命名的构造器,或重载构造器,而匿名内部类只能实例初始化 — 需要布置一个内部类对象

10.13 内部类标示符

如果内部类为匿名类,编译器会简单的产生一个数字作为其标示符。

如果内部类嵌套在别的nei’b内部类中,直接将它们的名字加在外围类标示符的¥后面

11 持有对象

许多语言,如果Perl,Python,Ruby都有容器的本地支持

11.2 基本概念

Collection

一个独立元素的序列,这些元素都服从一条或者多条规则。

比如

List按照插入顺序保存元素

Set不能有重复元素

Queue按照排队规则来确定对象产生的顺序

所有Collection都可以用foreach语法进行遍历

Map

一组成对的“键值对”对象,允许通过健来查找值

Arraylist允许用数字查找值 — 将数字和对象绑定在了一起

映射表允许我们用另一个对象来查找某个对象 — 被称关联数组/字典

强大的编程工具

11.5 List在Collection基础上增加了大量方法使得可以在List中间插入或移除元素

11.6 迭代器是一个对象,用于遍历并选择序列中的对象 — 轻量级对象

创建它代价小

11.7 Set

Set和Collection拥有一样的接口

11.14 总结 新程序中不应该使用过时的Vector,HashTable,Stack
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: