switch 接受char,enum,byte,int
char实际上还是整数值。
将double或者float转成int时总是舍去尾部。
初始化和清理。。(初始化使系统处于可知状态,清理是为了合理使用有限的资源)
Java对象的创建和初始化是同一个概念。
new表达式返回一个引用,但是构造函数不返回任何值。否则编译器就要知道如何处理这个值了。
名字管理是编程语言的一项重要特性。
内存空间起名字和方法起名字。
重载和区分重载方法
方法名和参数列表顺序
对primitive的重载牵涉到自动转型的问题。同样的应用于在同一个类的继承体系中的。
1.实参和形参一样:1对1
2.形参比实参小:【byte-int】
char 会被转成int
3.形参比实参大:【long-int】
强制转型【narrowing conversion】
不能用返回值来区分方法,因为如果用 f();这种方式调用而没有返回值。那么编译器就没有办法区分是调用 void f() 还是 int f().
调用类A的实例a的方法b时,底层实际运作是:
A.b(a);
return this.用于chain的实作。
使用this在构造函数中使用构造函数。
此时this的含义不是当前对象而是指定一种调用其他构造函数的方法。
而且只能用this调用一个构造函数不能调用两个构造函数。
在普通方法中也不能调用构造函数。
static含义【这个方法或者变量没有this】
不能在static中调用非static,但是可以在非static中调用static
通过类直接调用static
不能用static方法向对象发送消息,因此它不是面向对象的。
如果有太多的static,那么就可以考虑下设计了。
清理::finalization和垃圾回收
1。java的回收机制只能回收无需再用对象所占用的内存。,而不能处理其他资源。
2。特殊情况处理要用finalize()
C++对象总是会被回收,而Java对象并不是会被回收的。
对象并不总是会被回收,垃圾回收不是析构。
finalize的用途应该是发现还没有做资源清除,而不是用来做资源清除。从而使得bug被发现。
垃圾回收有两种基本的思路:
第一种是引用计数器,在全部对象列表上遍历,引用为0就清除。但是会有循环引用的问题。而且并未在实现中使用。
第二种是追溯静态存储区和栈中的对象。有两种方式:stop-and-copy和标记和清扫。前者要两倍空间,后者适合于垃圾比较少的情况。
第三种是自适应。
初始化:
对于局部变量,java用编译时错误来保证一定会被初始化。
而对于实例变量,由于会被多个方法使用,因此初始化成特定值意义不大,统一初始化成默认值。
指定初始化:在定义时就初始化。
构造器初始化:没有办法屏蔽默认初始化。
初始化顺序:就算是变量定义在构造函数之后,其初始化也会在高中函数调用之前。
静态初始化:只在必要时候才初始化。初始化的顺序是先静态后非静态。
其过程是: 加载类-》静态初始化-》new时分配空间,生成对象实例-》设置默认值-》设置实例变量初始化-》执行构造函数【可能牵涉到继承】。
main函数所在类的static变量初始化,这些初始化操作分为以下几步:
按照定义的顺序向下执行,先static,再非static,最后构造函数。
明确的静态初始化:
static{}
非静态实例初始化:
{}
数组初始化:
编译器不允许你指定数组的大小。
int[][] a 只定义了一个数组的应用,并没有分配任何空间。
所有数组都有一个固定成员length用于边界检查,而且你也没有办法禁用和修改。
意味着此处的检查如果发生在关键点上就可能成为性能的瓶颈。
第五章:隐藏具体实现
class定义为package后,一般也将该类对应的域声明为private或者package才有意义。
类不可以是private的也不可以是protected的。但是内部类可以是private和protected的。
类的访问权限只可以是package或者public的。
如果不希望他人对该类有访问权限,就可以把所有构造器定义为私有,但是在此类的static方法中可以实例化该类。
通过将构造器定义为私有的来阻止建立此类的实例:
第一种方式是创建一个static方法来生成此类的实例,此时可以做计数等动作。
第二种是使用单例模式。
第六章:复用程序代码
方法一:组合
复用代码的功能而不是形式。
方法二:继承
初始化引用的位置:
1.定义引用的地方
2.构造器
3.使用时,惰性初始化。
孙子没有办法直接调用爷爷的被覆盖过的方法。
导出类的对象中包含了一个基类的子对象,这个子对象是包含在导出类对象之中的。
子类的初始化要先调用基类的构造函数。
调用带参数的基类构造函数就必须用到supe(x,yx,zz);
而且如果基类只有不带参数的构造函数那么在子类的构造函数中就必须用super调用带参数的构造函数。
继承技术是不常用的。
组合和继承判定的标准,问自己是否需要将新类向基类向上转型。
向下转型和RTTI相关。
final技术
final最通常的含义是“这是无法改变的”
通常由于效率和设计来阻止改变
final可以用于方法,数据和类。
final数据:
1.编译期常量:必须是primitive类型的并且以final标注。可以减少运行时的开销。
带有初值的final primitive类型。
2.运行期被初始化,但是不希望在被修改
static final只占一份存储区域
final引用使得指向的引用不能改变,但是引用所指向的数据却可以改变。
private final int i = new Random().nextInt(20);不是编译器常量。所以虽然是final却不把i大写。
private final int[] a = new int[]{1,2,3}也不是编译器常量。
不能因为某个变量是final就认为可以在编译期间知道其值。
使引用成为final没有使primitive成为final的用处广泛。
空白final:
提供了按对象初始化的灵活性,但是编译器都确保在使用前空白fianl能够被正确初始化。
final参数,使得无法在方法中改变参数所指向的对象。
final方法:
1.锁定方法:设计考虑
2.效率:允许编译器把方法调用改成inline方式,但是也仅能减少方法调用的开销。同时会带来其他的开销(比如代码量增加),要慎用。
final和private:
所有的private方法都是final的。
无法驱动final方法,因此也没有办法重载final方法。
重载只有在某个方法是类的接口之一时才能使用,如果某个方法是private的,那么他就不是类的接口,因此也不能被重载。
final类:
不打算被用于继承。
final类的所有方法都隐含是final的。
但是数据成员不一定都是final的。
初始化和类的加载:
所有的类只有在使用的时候才加载。
这包含new和访问static
在加载的时候就初始化static代码
这包含static变量和static代码块,并且按照定义的顺序来执行。
继承和初始化:
类定义成abstract但是所有方法都不是abstract的原因之一是不想生成此类的实例。
在调用子类的构造函数的时候,基类的构造函数会先得到调用,然后一级一级的向下调用直到要创建的类。
构造函数实际上是static的。
编译器会强制每个类的构造函数都调用其基类的构造函数,如果子类构造函数没有显示的调用基类的构造函数,则编译器会隐式的调用。
对于普通方法而言,构造过程已经完成。
但是对于构造函数而言却没有,为了确保对象得到初始化,只能先调用基类的构造函数,再调用子类的构造函数。
用继承来表示行为的不同,用组合表示不同的状态。
下传和运行时类别鉴定(RTTI)。
抽象类也可以有构造函数。
不是后绑定的就不是多态性。
多态性和重载是有区别的。
多态方法在构造函数中的行为。
在构造函数中调用动态绑定的方法会使用覆盖过的版本。
abstract class Glyph {
abstract void draw();
Glyph() {
System.out.println("Glyph() before draw()");
draw();
System.out.println("Glyph() after draw()");
}
}
class RoundGlyph extends Glyph {
private int radius = 1;
RoundGlyph(int r) {
radius = r;
System.out.println("RoundGlyph.RoundGlyph(), radius = " + radius);
}
void draw() {
System.out.println("RoundGlyph.draw(), radius = " + radius);
}
}
"Glyph() before draw()",
"RoundGlyph.draw(), radius = 0",
"Glyph() after draw()",
"RoundGlyph.RoundGlyph(), radius = 5"
Chapter 8 : 接口和内部类
interface 的内部成员函数默认就是public。
内部的变量默认是public static final。。
Interface是java的多重继承,因为interface不牵涉到内存。
1.使用接口时可以上传到多个类。
2.可以避免生成其实例
设计考虑:优先使用interface,在必须要有实现代码时才使用abstract。
合并接口时的名字冲突。
多个接口含有相同的签名。
用接口做常量的分组。。
VS 枚举
初始化接口中的数据成员。
public static final
可以用非常量的数据来初始化 Date date = new Date();
接口的嵌套:
嵌套在类中
嵌套在接口中
嵌套在类中的接口可以是public,private,protected和package的。
<!---->
9.处理异常
用异常处理程序做异常的中心处理。
将处理错误信息的代码和出错时执行的代码分开。
抛出异常的过程:
1.创建一个异常对象。
2.停止当前程序执行。
3.异常管理程序接管并寻找异常处理程序。
对异常而言,最重要的是类名而不是参数。
对于错误信息,最好用system.err.print,因为system.out.print可能被重定向。
异常说明:
java在语法上提供了这样的支持:客户程序员可以知道某个方法可能抛出的异常。
异常说明是用throws来完成的。
RuntimeException除外。
java提供了在编译期纠正异常的保障。
可以声称方法会抛出一个异常,而实际上又没有抛出异常。
这样做可以为异常先占个位子,以后实现后就不会牵涉到客户端代码的修改。
对于interface和abstract中方法抛出异常的支持。
getMessage()
void printStackTrace( PrintStream | PrintWriter);
Throwable fillInStackTrace();
记录栈帧的当前状态。
用getClass获取异常类的信息。
重抛异常会将异常指向原来异常发生地,而不会指向重新抛出异常的地方。
如果要加上新的异常轨迹信息,则要用到fillInStackTrace。使用fillInStackTrace后会减少异常信息。
将异常堆栈改编成到目前正在执行方法位置。
可以抛出一个和捕捉到异常不同的异常。
异常链:
捕捉一个异常并抛出另外一个异常时还要保持原有异常的信息。
Exception在java.lang中定义,但是具体的异常类不是在java.lang中定义的。
有些异常是专门为其他类库定义的。
java.io.IOException
RuntimeException(表示编程错误):
NullPointException
ArrayIndexOutOfBoundException
用finally清理:
finally中的代码总会得到执行。
finally作用
由于Java没有析构函数并且垃圾回收机制没有办法手动调用及时进行。
所以在finally中调用相应的清理代码,将除了内存之外的其他资源复原。
甚至是在没有catch的情况下,finally
10: 检测类型检测类型检测类型检测类型
RTTI:
know a reference's real type at runtime.
导致了很多问题,从而引出了该如何组织程序结构这一根本性问题。
传统的RTTI和反射机制。
分享到:
评论