一、类的构造方法
通过new关键字创建对象实例时,调用类的构造方法对 对象的内存 进行初始化。其工作过程是这样的:
(1)分配存储新建的对象实例的内存。
(2)调用构造方法,并传入指定的参数。
(3)构造方法使用这些参数执行初始化新对象所需的一切操作。
关于构造方法:
(1) 构造方法的名称始终和类名一样。
(2) 声明构造方法时不指定返回值类型,连 void 都不用。
(3) 一个类可以有多个构造方法,每个构造方法参数不同。
默认构造方法:
每个类至少需要一个构造方法。
如果没有定义类时,没有显式定义构造方法,javac 编译器自动提供了一个构造方法(叫作默认构造方法)。这个构造方法:
(1)没有参数。
(2)不执行任何特殊的初始化操作。
此时:对象实例的变量需要通过增加代码显式初始化。如:
Circle c = new Circle();
c.r = 0.25;
多个构造方法之间的调用:
类定义中可能存在多个构造方法,这些构造方法可能共用大量的初始化代码。此时,可以通过this()进行构造方法的互相调用,来复用初始化代码。如:
// 这是基本构造方法:携带一个入参,初始化半径
public Circle(double r) { this.r = r; }
// 这个构造方法使用this()调用前一个构造方法,采用默认半径1.0
public Circle() { this(1.0); }
注:使用 this() 时有个重大的限制:只能出现在构造方法的第一个语句中。
二、类中字段的默认值,Java是如何实现初始化--自动生成初始化代码
在类定义中,字段可以指定默认值、或不指定默认值。如:
class A {
int i = 0;
float r;
}
1、对于没有指定默认值的字段,Java是如何赋值的呢?
如果没有指定初始值,字段自动使用默认值初始化:false、 \u0000、0、0.0 或 null。具体使用哪个值,根据字段的类型而定。
2、如果定义时指定了字段的默认值,Java是如何实现赋值的呢?
如:
class Dog{
final int legNum = 4;
String name;
public Dog(String name) {
this.name = name;
}
}
通过new Dog("Tom");创建对象时,Java是如何实现把对象实例内存中的legNum字符赋值为4的呢?
Java对类定义中指指定初始值的字段,会生成初始化代码。根据是实例字段、还是类字段,其工作方式有差异:
(1)实例字段
- java编译器会自动为 指定了初始值的字段 生成初始化代码,然后把这些代码放到类的所有构造函数中。
- 这些初始化代码 按照 字段在类中定义的顺序 插入到构造方法中。
- 如果类存在多个构造方法,其中某个构造方法使用了this()方式调用了另外一个构造方法。那么java编译器不会把 初始化代码 插入到当前构造方法中,而是插入到this()对应的构造方法中。通过调用this()实现执行 初始化代码。
样例:如下,java编译器只会在第一个构造方法中 插入针对legNum字段的初始化代码。第二个、第三构造方法通过this()方式调用第一个构造方法实现legNum字段初始化代码的执行。
class Dog{
final int legNum = 4;
int age;
String name;
public Doc(int age, String name) {
this.age= age;
this.name = name;
}
public Dog(String name) {
this(2, name);
}
public Dog() {
this(2, "Tom");
}
}
加入的初始化代码后的构造函数为:
public Doc(int age, String name) {
legNum = 4;
this.age= age;
this.name = name;
}
(2)类字段
如果是类字段。在没有创建对象实例情况下,指定了初始值的类字段就是分配内存和赋值。java如何实现的呢?
javac 会为每个类自动生成一个类初始化方法。类字段在这个方法的主体中初始化。这个方法只在首次使用类之前调用一次(经常是在 Java 虚拟机首次加载类时)。
- 和实例字段的初始化一样,类字段的初始化表达式按照 类字段 在类定义中的的顺序插入 类初始化方法。
- 由于类字段初始化在 类初始化方法中是顺序的,所以,类字段的初始化表达式可以使用在其之前声明的类字段。
- 类初始化方法是内部方法,对Java 程序员不可见。在类文件中,它的名称是 <clinit>。
三、Java也允许开发人员自己编写类中字段的 初始化代码,称之为“初始化程序”
(1)针对类字段的 初始化程序 称之为 “静态初始化程序”
“静态初始化程序”由 static 和 其后的 花括号+代码块 组成。(没有方法名)。样例:
public class TrigCircle {
// 这是静态查找表和各自的初始化程序
private static final int NUMPTS = 500;
private static double sines[] = new double[NUMPTS];
private static double cosines[] = new double[NUMPTS];
// 这是一个静态初始化程序,填充上述数组
static {
double x = 0.0;
double delta_x = (Circle.PI/2)/(NUMPTS-1);
for(int i = 0, x = 0.0; i < NUMPTS; i++, x += delta_x) {
sines[i] = Math.sin(x);
cosines[i] = Math.cos(x);
}
}
……
}
- 在类定义中,静态初始化程序可以放在字段和方法定义能出现的任何位置。
- 一个类可以有任意多个静态初始化程序。
- 静态初始化程序 的主体会和所有静态字段的 初始化代码 一起合并到类初始化方法中。
- 静态初始化程序 不能使用this关键字。
(2)针对实例字段的 初始化程序 称之为 “实例初始化程序”
“静态初始化程序”由 花括号+代码块 组成。(没有方法名)。样例:
public class TrigCircle {
private static final int NUMPTS = 100;
private int[] data = new int[NUMPTS];
// 这是一个实例初始化程序,填充上述数组
{
for(int i = 0; i < NUMPTS; i++)
data[i] = i;
}
……
}
- 在类定义中,实例初始化程序可以放在字段和方法定义能出现的任何位置。
- 一个类可以有任意多个实例初始化程序。
- 实例初始化程序 的主体会和所有实例字段的 初始化代码 一起合并到类的 构造方法 的开头。
- 不过,实际开发中很少使用 实例初始化程序 。