利用构造器保证初始化
在Java 中,类的设计者通过构造器保证每个对象的初始化。
构造器名称与类名相同。
在Java 中,对象的创建与初始化是统一的概念,二者不可分割。
方法重载
区分重载方法
Java 中以参数列表区分重载方法。
重载与基本类型
基本类型可以自动从较小的类型转型为较大的类型。
如果传入的参数类型大于方法期望接收的参数类型,你必须首先做下转换,如果你不做的话,编译器就会报错。
自动转换顺序:
char,byte,short,int,long,float,double
char不会转byte和short,而是直接转int。
无参构造器
如果你创建一个类,类中没有构造器,那么编译器就会自动为你创建一个无参构造器。
但是,一旦你显式地定义了构造器(无论有参还是无参),编译器就不会自动为你创建无参构造器。
this关键字
this 关键字只能在非静态方法内部使用。
当你调用一个对象的方法时,this 生成了一个对象引用。你可以像对待其他引用一样对待这个引用。如果你在一个类的方法里调用其他该类中的方法,不要使用 this,直接调用即可,this 自动地应用于其他方法上了。
this 关键字只用在一些必须显式使用当前对象引用的特殊场合。
// 方法定义
Leaf increment() {
i++;
return this;
}
// 调用
Leaf x = new Leaf();
x.increment().increment().increment();
this 关键字在向其他方法传递当前对象时也很有用:
Peeler.peel(this);
在构造器中调用构造器
在一个构造器中,当你给 this 一个参数列表时,它是另一层意思。它通过最直接的方式显式地调用匹配参数列表的构造器。
能通过this 调用一次构造器。另外,必须在方法的第一行使用 this 关键字调用构造器,否则编译器会报错。
参数列表中的变量名 s 和成员变量名 s 相同,会引起混淆。你可以通过 this.s 表明你指的是成员变量 s,从而避免重复。
编译器不允许在一个构造器之外的方法里调用构造器。
static 的含义
static 方法中不会存在 this。你不能在静态方法中调用非静态方法(反之可以)。静态方法是为类而创建的,不需要任何对象。一个类中的静态方法可以被其他的静态方法和静态属性访问。
垃圾回收器
finalize() 方法的工作原理"假定"是这样的:当垃圾回收器准备回收对象的内存时,首先会调用其 finalize() 方法,并在下一轮的垃圾回收动作发生时,才会真正回收对象占用的内存。
finalize() 是一个潜在的编程陷阱。
在Java 中,对象并非总是被垃圾回收:
1、 对象可能不被垃圾回收;
2、 垃圾回收不等同于析构;
3、 垃圾回收只与内存有关;
将对finalize() 的需求限制到一种特殊情况,即通过某种创建对象方式之外的方式为对象分配了存储空间。
看起来之所以有 finalize() 方法,是因为在分配内存时可能采用了类似 C 语言中的做法,而非 Java 中的通常做法。这种情况主要发生在使用"本地方法"的情况下,本地方法是一种用 Java 语言调用非 Java 语言代码的形式。
本地方法目前只支持 C 和 C++,但是它们可以调用其他语言写的代码,所以实际上可以调用任何代码。在非 Java 代码中,也许会调用 C 的 malloc() 函数系列来分配存储空间,而且除非调用 free() 函数,不然存储空间永远得不到释放,造成内存泄露。但是,free() 是 C 和 C++ 中的函数,所以你需要在 finalize() 方法里用本地方法调用它。
绝对不能直接调用 finalize()方法。
在不需要类似析构器行为的时候,Java 的垃圾回收器极大地简化了编程,并加强了内存管理上的安全性。一些垃圾回收器甚至能清理其他资源,如图形和文件句柄。然而,垃圾回收器确实增加了运行时开销,由于 Java 解释器从一开始就很慢,所以这种开销到底造成多大的影响很难看出来。随着时间的推移,Java 在性能方面提升了很多,但是速度问题仍然是它涉足某些特定编程领域的障碍。
构造器初始化
自动初始化的进行,会在构造器被调用之前发生。
编译器不会强制你一定要在构造器的某个地方或在使用它们之前初始化元素——初始化早已得到了保证。
初始化的顺序
在类中变量定义的顺序决定了它们初始化的顺序。即使变量定义散布在方法定义之间,它们仍会在任何方法(包括构造器)被调用之前得到初始化。
静态数据的初始化
初始化的顺序先是静态对象(如果它们之前没有被初始化的话),然后是非静态对象。
初始化顺序:
1、 静态初始化;
2、 对象中的所有基本类型数据设置为默认值,数字会被置为0,布尔型和字符型也相同),引用被置为null;
3、 字段定义处的初始化动作;
4、 构造器初始化;
显式的静态初始化
static {
i = 47;
}
静态初始化只会执行一次,在构造器之前调用。
非静态实例初始化
{
mug1 = new Mug(1);
mug2 = new Mug(2);
System.out.println("mug1 & mug2 initialized");
}
会多次初始化,在构造器之前调用,对于支持"匿名内部类"的初始化是必须的。
数组初始化
// 三种创建数组的方式
int[] a = {1, 2, 3, 4, 5};
int[] a = new int[rand.nextInt(20)];
int[] a = new int{1, 2, 3, 4, 5};
对于数组,初始化动作可以出现在代码的任何地方,但是第一种使用一种特殊的初始化表达式,它必须在创建数组的地方出现。
可变参数列表
// 定义
void printArray(Object... args) {
for (Object obj: args) {
System.out.print(obj + " ");
}
System.out.println();
}
// 调用
printArray(47, (float) 3.14, 11.11);
printArray((Object[]) new Integer[] {1, 2, 3, 4});
printArray(); // Empty list is OK
可变参数实质上是一个数组。
如果你有一组事物,可以把它们当作列表传递,而如果你已经有了一个数组,该方法会把它们当作可变参数列表来接受。
可变参数的个数可以为 0。
可变参数列表使得方法重载更加复杂了,尽管乍看之下似乎足够安全。
static void f(float i, Character... args) {
System.out.println("first");
}
static void f(Character... args) {
System.out.println("second");
}
public static void main(String[] args) {
f(1, 'a');
f('a', 'b'); // 会报错
}
可能需要通过在某个方法中增加一个非可变参数解决这个问题。
static void f(float i, Character... args) {
System.out.println("first");
}
static void f(char c, Character... args) {
System.out.println("second");
}
应该总是在重载方法的一个版本上使用可变参数列表,或者压根不用它。
枚举类型
Java 5 中添加了特性 enum 关键字。
由于枚举类型的实例是常量,因此按照命名惯例,都用大写字母表示(如果名称中含有多个单词,使用下划线分隔)。
在你创建 enum 时,编译器会自动添加一些有用的特性。例如,它会创建 toString()
方法,以便你方便地显示某个 enum 实例的名称,这从上面例子中的输出可以看出。编译器还会创建 ordinal()
方法表示某个特定 enum 常量的声明顺序,static values()
方法按照 enum 常量的声明顺序,生成这些常量值构成的数组。
由于switch 是在有限的可能值集合中选择,因此它与 enum 是绝佳的组合
switch(degree) {
case NOT:
System.out.println("not spicy at all.");
break;
case MILD:
case MEDIUM:
System.out.println("a little hot.");
break;
case HOT:
case FLAMING:
default:
System.out.println("maybe too hot");
}