文章目录
5.1 用构造器确保初始化
Java中构造器采用与类相同的名称,防止构造器与类中的方法名冲突,同时方便编译器自动调用。每个方法小写的编码风格不适用于构造器。
构造器没有返回值,这与返回值为空(void)是不同的。
Java提供默认的无参构造器,也可以自己编写有参数的构造器。如果已经定义了 一个构造器,编译器就不会提供默认的无参构造器。
5.2 方法的重载
通过参数类型列表可以区分方法,参数的顺序不同也会用于区分。
根据方法的返回值来区分重载方法是行不通的。
5.4 this关键字
如果你希望在方法的内部获得对当前对象的引用,由于这个引用是由编译器偷偷传进来的,所以没有标识符可用。可以使用this关键字,this关键字只能在方法内部使用,表示对“调用方法的那个对象”的引用。在方法内部调用同一个类的另一个方法,直接调用即可,不用this。
public class Person {
int a = 1;
public Person getPerson() {
return this; // 返回对象的引用
}
public int f() {
int a = 10;
return this.a; // 用于区分【成员变量】和【局部变量】
}
}
5.4.1 在构造器中调用构造器
public class Person {
int a;
Perosn() {
this(10); // 构造器调用置于起始位置
// this("bbb"); // 不能调用两个构造器
}
Person(int a) {
}
Person(String b) {
}
void print() {
// this(10); // 除构造器之外,禁止在其他任何方法中调用构造器
}
}
5.4.2 static方法
static方法就是没有this的方法。因此在static方法内部不能调用非静态方法,反过来可以。可以在没有创建任何对象的前提下,通过类来调用static方法。
5.5 终极处理和垃圾回收
一旦垃圾回收器准备好释放对象占用的存储空间,将首先调用finalize()方法。
public class Person {
protected void finalize() {
System.out.println("我被清理了");
}
}
Person person = new Person();
person = null; // 对象不再被引用
System.gc(); // 强制终止
垃圾回收器如何工作
Java从堆分配空间的速度,可以和其他语言从堆栈上分配空间的速度相媲美。
在某些Java虚拟机中,堆的实现更像是一个传送带,每分配一个对象,堆指针就往前移动一格。这意味着对象存储空间的分配空间很快。虽然薄记工作需要少量开销,但是没有查找可用空间的开销大。同时垃圾回收器对对象重新排列,实现了一种高速的、有无线空间可供分配的堆模型。
引用计数法:简单但速度很慢的垃圾回收技术,从未用于Java虚拟机的实现中
——每个对象含有一个引用计时器,当有引用连接时加1,当引用离开作用域或被置为null时减1。管理引用的开销不大,但是会在整个程序生命周期持续。垃圾回收器会遍历列表,释放引用数为0的对象空间。
——很难发现循环引用。
自适应:
从堆栈和静态存储区开始,遍历所有的引用。对于发现的每个引用,必须追踪它所引用的对象,然后是此对象包含的所有引用,如此反复,直到根源于堆栈和静态存储区的引用所形成的网络全部被访问为止。这就解决了循环引用的问题。
处理存活对象:
(1)停止-复制
暂停程序的运行,将所有存活的对象从当前堆复制到另一个堆,没有被复制的全部都是垃圾。对象在复制到新堆时是一个挨着一个的。
缺点:需要修改堆栈的引用。还需要维护两个堆,在两个堆之间互相倒腾。可能产生的垃圾很少但仍然需要复制。
(2)标记-清扫
在寻找存活对象时给对象设一个标记。没有标记的对象不会被清理。所以剩下的堆空间是不连续的。
5.6 成员的初始化
对于方法的局部变量,程序员必须初始化,否则编译器会报错。
如果类的字段是基本类型,会有一个初始值,对象引用会是null。
初始化顺序
变量定义的先后顺序决定了初始化的顺序。即使变量定义散布在方法定义之间,它们仍旧会在任何方法(包括构造器)被调用之前得到初始化。先初始化静态对象,然后初始化非静态对象,然后调用构造方法。
静态初始化、非静态初始化
public class Person {
static int a;
static int b;
static {
a = 10;
b = 20;
}
int c;
int d;
{
c = 30;
d = 40;
}
}
5.8 数组初始化
数组只是相同类型的、用一个标识符名称封装到一起的一个对象序列或基本类型数据序列。数组通过方括号下标操作符定义和使用。
/** 数组的定义 */
int[] a1; // 更常用,一个int类型数组
int a1[];
/** 数组的初始化 */
int[] a1 = { 1, 2, 3, 4 }; // 只能在数组被定义时使用
int[] a2;
a2 = new int[10];
int[] a3;
a3 = new int[] { 1, 2, 3, 4 };
/** 数组的长度 */
a1[0];
a1[a1.length-1]; // 数组的length成员
/** 打印一维数组 */
Arrays.toString(a1);
可变参数列表
/** 将多个参数以Object类型数组的形式传入 */
public class A {
static void printArray(Object[] args) {
for (Object obj : args) {
System.out.println(args + " ");
}
}
public static void main(String[] args) {
printArray(new Object[] { new Integer(47), new String("aa") })
}
}
/** Java SE5新特性,不用显式编写数组语法了,编译器会填充数组,因此获取到的仍然是数组*/
public class A {
static void printArray(Object... args) {
for (Object obj : args) {
System.out.println(args + " ");
}
}
public static void main(String[] args) {
printArray(new Integer(47), new String("aa"))
printArray((Object[])new int[] { 1, 2 })
}
}
5.9 枚举类型
/** 枚举类的创建 */
public enum A {
NOT, MILD, HOT_HOT // 实例是常量,使用大写
}
/** 常用方法 */
A[] values = A.values() // 以声明顺序产生一个数组
for (A a: values) {
System.out.println(a);
System.out.println(a.ordinal()); // 声明的顺序
}
由于switch是要在有限的集合中进行选择,因此它与enum正是绝佳的组合。
A a = A.NOT;
switch (a) {
case NOT:
xxx;
break;
case MILD:
xxx;
break;
default:
xxx;
}