对象的构造和初始化
对象的构造必须由构造函数完成:
- 当对象没有提供自定义构造函数时,系统会自动提供无参构造函数,所有无参构造函数统称默认构造函数,即自定义的无参构造函数也称为默认构造函数。如果自定义了类的构造函数,但并没有自定义默认构造函数,系统也不会再提供默认构造函数。
- 一个类可以定义构造函数,且可以重载多个构造函数,一个类中不能重写自身或者父类的构造函数也没要,所有构造函数没有重写,只有重载自身的构造函数,父类的所有构造函数不能继承给子类,子类也不能重写父类的构造函数。
- 类实例的创建过程,类成员变量的值变化过程如下:
- 类在创建时,首先通过
new
运算符,在堆空间中分配类的数据成员所需的内存空间,然后对数据成员进行默认初始化,即如果时int
型变量这值为0
,引用型变量这值为null
,其他类似。确保成员对像不会没有初始值。而相比之下,局部变量在函数调用时不会进行初始化,所有局部变量不能不赋值就直接使用。 - 执行显式成员初始化。即类定义类时,可能会在定义类数据成员的同时为其赋值,这时执行的语句就是条初始化。
- 然后执行构造函数的代码体。
例:对于这样的数据成员
class A{
private int a;
A(){
a = 2;
}
}
class B{
void func(){
A a1 = new();
}
}
此时a1.a先时默认初始化为0,保证成员变量有默认值,在通过构造函数A()
再通过构造函数进行a = 2
的赋值操作;
class A{
private int a = 1;
A(){
a = 2;
}
}
class B{
void func(){
A a1 = new();
}
}
此时a1.a先时默认初始化为0,保证成员变量有默认值,然后再通过语句private int a = 1;
进行显示初始化(不叫赋值),最后通过构造函数A()
再通过构造函数进行a = 2
的赋值操作;
- 注意,在定义构造函数时,一定要注意此类是否是非
Object类
的子类,即如果该类有父类(表示父类不是Object的情况),则子类的构造函数一定会调用父类的某一构造函数,即子类对象的构造一定先从父类部分的构造开始。
- 如果此时父类中有默认构造函数,在子类中所有重载构造函数都可以不写出调用父类构造函数的代码,即此时子类默认了调用父类的默认构造函数,如果子类需要调用非默认构造函数,则只能在子类中需要调用父类非默认构造函数的子类构造函数的第一行显示调用父类的构造函数,且调用语句中,父类构造函数名必须改为:
super
- 如果此时父类没有默认构造函数,则子类的构造函数一定只能先调用父类的非默认构造函数中的其中一个,所以此时子类所以构造函数都必须在构造函数方法体的第一行显示调用父类的非默认构造函数,且该构造函数名必须改为:
super
- 类的构造函数调用其他重载的构造函数时,只能必须用
this
代替重载构造函数名。子类调用父类的构造函数只能必须用super
代替构造函数名;
类与对象的区别
静态初始化器:
结构:
static {
//java 语句
}
功能:在加载类时,由系统系自动调用,一个类只会加载一次,所以,静态初始化器只会被系统调用一次。且一个类在需要的时候才会被加载
类的定义和类的装载时刻是不一样的:
- 类定义:即编写一个类的代码段,这段代码就是类的定义,但此时的类并不正真存在,例如:我们都知道类有静态变量(类变量),它是属于类所有的,当是,当在类定义时,由于类没有被加载,所以此时静态变量其实也不存在,即静态变量的初始化实在类被加载的时候进行的,即此时才真正有实际意义的静态变量。
- 静态初始化器类似于于构造函数,构造函数的功能之一是可以修改数据成员的值(说对数据成员初始化是错误的说法,因为所谓初始化是在定义的时候进行的),而静态初始化器的功能之一就是对静态成员赋值;构造函数的调用标志是在
new
运算符之后,而静态初始化器的标志是:第一此定义类的实例时自动调用,或者第一此使用类的静态变量时自动调用,两者保持只调用一次的原则;
- 例:
package com.package1;
class StaticVar{
public static int staticNum = 11;
static{
System.out.println("StaticVar类 已被加载 ,此时sta的值为:staticNum = " + staticNum);
System.out.println("现在将staticNum的值由11改为111");
staticNum = 111;
System.out.println("修改之后sta的值为:sta = " + staticNum);
}
}
public class LeiText1 {
public static void main(String[] args){
System.out.println(StaticVar.staticNum);
}
}
输出结果为:
StaticVar类 已被加载 ,此时sta的值为:staticNum = 11
现在将staticNum的值由11改为111
修改之后sta的值为:sta = 111
111
Process finished with exit code 0
注意最后输出的111
是由语句:System.out.println(StaticVar.staticNum);
产生而其他的输出结果时有static {}
初始化器产生。而且,初始化器是自己执行的(系统执行),在使用StaticVar.staticNum
之前就使用。
如果把上面main()
函数改为如下:
public class LeiText1 {
public static void main(String[] args){
StaticVar var1 = new StaticVar();
//System.out.println(StaticVar.staticNum);
}
}
则输出结果为:
StaticVar类 已被加载 ,此时sta的值为:staticNum = 11
现在将staticNum的值由11改为111
修改之后sta的值为:sta = 111
显然此时该静态初始化器也被启动了。
再修改main()
函数如下:
public class LeiText1 {
public static void main(String[] args){
StaticVar var1 = new StaticVar();
System.out.println(StaticVar.staticNum);
}
}
结果为:
现在将staticNum的值由11改为111
修改之后sta的值为:sta = 111
111
综上所诉:静态初始化器可以在类的实例创建时或类的静态成员被引用时启动,代表的意义时该类被正式加载,且只会被加载一次,类加载时就会立即执行static {}
代码段,如果没有自定义静态初始化器,代码段相当于为空。且静态初始化器不能操作非静态变量;
静态初始化器就相当于静态方法,只不过,一般不会在静态初始化器里定义变量,不是不可,即其实静态方法可以做的基本他也可以。
例如:
package com.package1;
class StaticVar{
public static int staticNum = 11;
static{
System.out.println("StaticVar类 已被加载 ,此时sta的值为:staticNum = " + staticNum);
System.out.println("现在将staticNum的值由11改为111");
staticNum = 111;
System.out.println("修改之后sta的值为:sta = " + staticNum);
int a = 0;
System.out.println("静态初始化器定义了变量a,并将其输出:a = " + a);
LeiText1 text1 = new LeiText1();
text1.printFunc();
}
}
public class LeiText1 {
public static void main(String[] args){
StaticVar var1 = new StaticVar();
//System.out.println(StaticVar.staticNum);
}
void printFunc(){
System.out.println("被其他类的静态初始化器调用");
}
}
输出结果为
StaticVar类 已被加载 ,此时sta的值为:staticNum = 11
现在将staticNum的值由11改为111
修改之后sta的值为:sta = 111
静态初始化器定义了变量a,并将其输出:a = 0
被其他类的静态初始化器调用
Process finished with exit code 0