JavaSE_65_对象的构造方法以及初始化
2014-04-23 01:26
363 查看
有关类的继承的内容大概其就这么多了。之前提过了对象的构造以及初始化。现在在类的继承的基础上,继续来说对象的构造以及初始化,主要讨论子类如何完成继承父类的初始化。
当调用类的构造器来创建对象时,它将给新建的对象分配内存,并对对象进行初始化操作。
现在我们来探讨对对象进行初始化操作时候的细节。
对象的初始化操作将递归如下的步骤来进行:
1.设置实例变量的值为缺省的初始值 (0, false, null),不同的数据类型有不同的初始值。
2.调用类的构造器 (但是还没有执行构造方法体),绑定构造器参数。
3.如果构造器中有this()调用,则根据this()调用的参数调用相应的重载构造器,然后,转到步骤5;否则转到步骤4。
4.除java.lang.Object类外,调用父类的中的初始化块初始化父类的属性,然后调用父类构造器,如果在构造器中有super()调用,则根据super()中的参数调用父类中相应的构造器。
5.使用初始化程序和初始化块初始化成员。
6.执行构造器方法体中其他语句。
所谓的初始化块,就是我们前面提到的所谓“游离块”。不管使用哪个构造器创建对象,它都会被首先运行,然后才是构造器的主体部分被执行。
我们来看一个例子:
class Person {
private String name;
private int age;
private String sex;
public Person() {
System.out.println("构造器Person()被调用");
sex = "Male";
System.out.println("name=" + name + " ,age=" + age + " ,sex=" + sex);
}
public Person(String theName) {
// 调用构造器Person()
this();
System.out.println("构造器Person(String theName)被调用");
name = theName;
System.out.println("name=" + name + " ,age=" + age + " ,sex=" + sex);
}
public Person(String theName, int theAge) {
// 调用构造器Person(String theName)
this(theName);
System.out.println("构造器Person(String theName,int theAge)被调用");
age = theAge;
System.out.println("name=" + name + " ,age=" + age + " ,sex=" + sex);
}
// 初始化块
{
name = "Tony Blair";
age = 50;
sex = "Female";
System.out.println("初始化块执行后:name=" + name + " ,age="
+ age + " ,sex=" + sex);
}
}
public class TestPerson {
public static void main(String[] args) {
Person person = new Person();
}
}
编译执行上面的程序,将会得到如下的输出:
初始化块执行后:name=Tony Blair ,age=50 ,sex=Female
构造器Person()被调用
name=Tony Blair ,age=50 ,sex=Male
可以看到,初始化块会先于构造器调用执行。读者可以将main()方法中调用的创建Person对象的构造器换成其他两个,在观察它的结果。同样可以得出上面的结论。
提示:初始化块的机制并不是必须的,你完全可以将属性的初始化和属性的声明结合在一起,如: String name = "Tony Blair";
下面我们看一个对象初始化的例子,以加深对对象初始化的理解。
class Person {
private String name;
private int age;
private String sex;
public Person() {
System.out.println("构造器Person()被调用");
sex = "Male";
System.out.println("name=" + name + " ,age=" + age + " ,sex=" + sex);
}
public Person(String theName) {
System.out.println("构造器Person(String theName)被调用");
name = theName;
System.out.println("name=" + name + " ,age=" + age + " ,sex=" + sex);
}
public Person(String theName, int theAge) {
System.out.println("构造器Person(String theName,int theAge)被调用");
name = theName;
age = theAge;
System.out.println("name=" + name + " ,age=" + age + " ,sex=" + sex);
}
// 初始化块
{
name = "Tony Blair";
age = 50;
sex = "Female";
System.out.println("Person初始化块执行后:name=" + name + " ,age="
+ age + " ,sex=" + sex);
}
}
这里定义了一个父类Person,它里面定义了三个构造器以及一个初始化块。
我们再来定义一个Person类的子类Teacher,如下:
class Teacher extends Person {
// 部门
String department;
// 教龄
int schoolAge;
public Teacher() {
System.out.println("构造器Teacher()被调用");
}
public Teacher(String name) {
// 调用父类中的构造器Person(String theName)
super(name);
System.out.println("构造器Teacher(String name)被调用");
}
public Teacher(int theSchoolAge) {
schoolAge = theSchoolAge;
}
public Teacher(String dept, int theSchoolAge) {
// 调用本类中重载的构造器Teacher(int theSchoolAge)
this(theSchoolAge);
department = dept;
}
// 初始化块
{
department = "教务部";
System.out.println("Teacher初始化块执行后:name=" + name + " ,age=" + age
+ " ,sex=" + sex);
}
}
这个类中定义了四个构造器:一个不带参数的构造器;一个带一个String数据类型参数的构造器,它通过super()显式调用父类的构造器;一个带一个int数据类型参数的构造器;一个带两个参数的构造器,通过this()来调用类中带int类型参数的构造器。
public class TestInit {
public static void main(String[] args) {
System.out.println("------------------------------------");
Teacher t1 = new Teacher();
System.out.println("");
System.out.println("------------------------------------");
Teacher t2 = new Teacher("Tom");
System.out.println("");
System.out.println("------------------------------------");
Teacher t3 = new Teacher("财务部", 20);
}
}
这个程序通过三种构造器来创建三个Teacher对象,因为调用的构造器不同,所以对象初始化的步骤也有所不同。因为在这几个程序中,在几个关键部分都已经有信息打印到控制台,所以,只要执行这个程序,就可以看出各个调用构造器创建对象的运行细节。
编译并运行TestInit,可以在控制台上得到如下的信息:
------------------------------------
Person初始化块执行后:name=Tony Blair ,age=50 ,sex=Female
构造器Person()被调用
name=Tony Blair ,age=50 ,sex=Male
Teacher初始化块执行后:name=Tony Blair ,age=50 ,sex=Male
构造器Teacher()被调用
------------------------------------
Person初始化块执行后:name=Tony Blair ,age=50 ,sex=Female
构造器Person(String theName)被调用
name=Tom ,age=50 ,sex=Female
Teacher初始化块执行后:name=Tom ,age=50 ,sex=Female
构造器Teacher(String name)被调用
------------------------------------
Person初始化块执行后:name=Tony Blair ,age=50 ,sex=Female
构造器Person()被调用
name=Tony Blair ,age=50 ,sex=Male
Teacher初始化块执行后:name=Tony Blair ,age=50 ,sex=Male
在讲述数据类型的时候,我们已经知道,各种简单数据类型之间是可以进行相互转换的,有些转换可以通过系统自动完成,而有些转换必须在程序中通过强制转换来完成。
而对于引用类型,也有一个相互转换的机制。同样的,在引用类型数据进行转换的时候,分为自动造型和强制造型两种情况。
当从子类转换成父类的时候(或者实现类转换成接口
4000
,下同),造型可以自动完成,比如,Teacher是Person的子类,则如果将一个Teacher对象赋给一个Person类型的变量的时候,造型自动完成。
当从父类转换成子类的时候(或者接口转换成实现类),必须使用强制造型,比如,Teacher是Person的子类,如果需要将一个Person对象赋给一个Teacher类型变量的时候,必须使用强制造型。
对象的强制造型可以使用运算符“()”来完成,格式如下:
SupClass sup = new SubClass();
SubClass sub = (SubClass)sup;
需要注意的是,无论是自动造型还是强制造型,都只能用在有继承关系的对象之间。
并不是任意的父类类型数据都可以被造型为子类类型,只有多态情况下,原本就是子类类型的对象被声明为父类的类型,才可以通过造型恢复其“真实面目”,否则会在程序运行时出错。
在造型的时候,往往需要使用instanceof来判断一个对象是否可以进行造型,以避免运行时的错误(Runtime error)。
比如,Person类有两个子类:Teacher 和Student,还有一个单独的类Test,我们来看下面的代码片断:
Test t = new Test();
Person p = new Student();
Teacher t = (Teacher)p;//将会出现运行时错误
Student s = (Student)t;//因为Test类和Student之间没有任何的继承关系
//所以在编译的时候就会出错(编译错误,compile error)
此时,可以先使用instanceof来判断要造型的对象是否为可以造型的类型,如:
Person p = new Student();
if(p instanceof Teacher){
Teacher t = (Teacher)p;
}
这样就可以避免出现运行时错误。
当调用类的构造器来创建对象时,它将给新建的对象分配内存,并对对象进行初始化操作。
现在我们来探讨对对象进行初始化操作时候的细节。
对象的初始化操作将递归如下的步骤来进行:
1.设置实例变量的值为缺省的初始值 (0, false, null),不同的数据类型有不同的初始值。
2.调用类的构造器 (但是还没有执行构造方法体),绑定构造器参数。
3.如果构造器中有this()调用,则根据this()调用的参数调用相应的重载构造器,然后,转到步骤5;否则转到步骤4。
4.除java.lang.Object类外,调用父类的中的初始化块初始化父类的属性,然后调用父类构造器,如果在构造器中有super()调用,则根据super()中的参数调用父类中相应的构造器。
5.使用初始化程序和初始化块初始化成员。
6.执行构造器方法体中其他语句。
所谓的初始化块,就是我们前面提到的所谓“游离块”。不管使用哪个构造器创建对象,它都会被首先运行,然后才是构造器的主体部分被执行。
我们来看一个例子:
class Person {
private String name;
private int age;
private String sex;
public Person() {
System.out.println("构造器Person()被调用");
sex = "Male";
System.out.println("name=" + name + " ,age=" + age + " ,sex=" + sex);
}
public Person(String theName) {
// 调用构造器Person()
this();
System.out.println("构造器Person(String theName)被调用");
name = theName;
System.out.println("name=" + name + " ,age=" + age + " ,sex=" + sex);
}
public Person(String theName, int theAge) {
// 调用构造器Person(String theName)
this(theName);
System.out.println("构造器Person(String theName,int theAge)被调用");
age = theAge;
System.out.println("name=" + name + " ,age=" + age + " ,sex=" + sex);
}
// 初始化块
{
name = "Tony Blair";
age = 50;
sex = "Female";
System.out.println("初始化块执行后:name=" + name + " ,age="
+ age + " ,sex=" + sex);
}
}
public class TestPerson {
public static void main(String[] args) {
Person person = new Person();
}
}
编译执行上面的程序,将会得到如下的输出:
初始化块执行后:name=Tony Blair ,age=50 ,sex=Female
构造器Person()被调用
name=Tony Blair ,age=50 ,sex=Male
可以看到,初始化块会先于构造器调用执行。读者可以将main()方法中调用的创建Person对象的构造器换成其他两个,在观察它的结果。同样可以得出上面的结论。
提示:初始化块的机制并不是必须的,你完全可以将属性的初始化和属性的声明结合在一起,如: String name = "Tony Blair";
下面我们看一个对象初始化的例子,以加深对对象初始化的理解。
class Person {
private String name;
private int age;
private String sex;
public Person() {
System.out.println("构造器Person()被调用");
sex = "Male";
System.out.println("name=" + name + " ,age=" + age + " ,sex=" + sex);
}
public Person(String theName) {
System.out.println("构造器Person(String theName)被调用");
name = theName;
System.out.println("name=" + name + " ,age=" + age + " ,sex=" + sex);
}
public Person(String theName, int theAge) {
System.out.println("构造器Person(String theName,int theAge)被调用");
name = theName;
age = theAge;
System.out.println("name=" + name + " ,age=" + age + " ,sex=" + sex);
}
// 初始化块
{
name = "Tony Blair";
age = 50;
sex = "Female";
System.out.println("Person初始化块执行后:name=" + name + " ,age="
+ age + " ,sex=" + sex);
}
}
这里定义了一个父类Person,它里面定义了三个构造器以及一个初始化块。
我们再来定义一个Person类的子类Teacher,如下:
class Teacher extends Person {
// 部门
String department;
// 教龄
int schoolAge;
public Teacher() {
System.out.println("构造器Teacher()被调用");
}
public Teacher(String name) {
// 调用父类中的构造器Person(String theName)
super(name);
System.out.println("构造器Teacher(String name)被调用");
}
public Teacher(int theSchoolAge) {
schoolAge = theSchoolAge;
}
public Teacher(String dept, int theSchoolAge) {
// 调用本类中重载的构造器Teacher(int theSchoolAge)
this(theSchoolAge);
department = dept;
}
// 初始化块
{
department = "教务部";
System.out.println("Teacher初始化块执行后:name=" + name + " ,age=" + age
+ " ,sex=" + sex);
}
}
这个类中定义了四个构造器:一个不带参数的构造器;一个带一个String数据类型参数的构造器,它通过super()显式调用父类的构造器;一个带一个int数据类型参数的构造器;一个带两个参数的构造器,通过this()来调用类中带int类型参数的构造器。
public class TestInit {
public static void main(String[] args) {
System.out.println("------------------------------------");
Teacher t1 = new Teacher();
System.out.println("");
System.out.println("------------------------------------");
Teacher t2 = new Teacher("Tom");
System.out.println("");
System.out.println("------------------------------------");
Teacher t3 = new Teacher("财务部", 20);
}
}
这个程序通过三种构造器来创建三个Teacher对象,因为调用的构造器不同,所以对象初始化的步骤也有所不同。因为在这几个程序中,在几个关键部分都已经有信息打印到控制台,所以,只要执行这个程序,就可以看出各个调用构造器创建对象的运行细节。
编译并运行TestInit,可以在控制台上得到如下的信息:
------------------------------------
Person初始化块执行后:name=Tony Blair ,age=50 ,sex=Female
构造器Person()被调用
name=Tony Blair ,age=50 ,sex=Male
Teacher初始化块执行后:name=Tony Blair ,age=50 ,sex=Male
构造器Teacher()被调用
------------------------------------
Person初始化块执行后:name=Tony Blair ,age=50 ,sex=Female
构造器Person(String theName)被调用
name=Tom ,age=50 ,sex=Female
Teacher初始化块执行后:name=Tom ,age=50 ,sex=Female
构造器Teacher(String name)被调用
------------------------------------
Person初始化块执行后:name=Tony Blair ,age=50 ,sex=Female
构造器Person()被调用
name=Tony Blair ,age=50 ,sex=Male
Teacher初始化块执行后:name=Tony Blair ,age=50 ,sex=Male
在讲述数据类型的时候,我们已经知道,各种简单数据类型之间是可以进行相互转换的,有些转换可以通过系统自动完成,而有些转换必须在程序中通过强制转换来完成。
而对于引用类型,也有一个相互转换的机制。同样的,在引用类型数据进行转换的时候,分为自动造型和强制造型两种情况。
当从子类转换成父类的时候(或者实现类转换成接口
4000
,下同),造型可以自动完成,比如,Teacher是Person的子类,则如果将一个Teacher对象赋给一个Person类型的变量的时候,造型自动完成。
当从父类转换成子类的时候(或者接口转换成实现类),必须使用强制造型,比如,Teacher是Person的子类,如果需要将一个Person对象赋给一个Teacher类型变量的时候,必须使用强制造型。
对象的强制造型可以使用运算符“()”来完成,格式如下:
SupClass sup = new SubClass();
SubClass sub = (SubClass)sup;
需要注意的是,无论是自动造型还是强制造型,都只能用在有继承关系的对象之间。
并不是任意的父类类型数据都可以被造型为子类类型,只有多态情况下,原本就是子类类型的对象被声明为父类的类型,才可以通过造型恢复其“真实面目”,否则会在程序运行时出错。
在造型的时候,往往需要使用instanceof来判断一个对象是否可以进行造型,以避免运行时的错误(Runtime error)。
比如,Person类有两个子类:Teacher 和Student,还有一个单独的类Test,我们来看下面的代码片断:
Test t = new Test();
Person p = new Student();
Teacher t = (Teacher)p;//将会出现运行时错误
Student s = (Student)t;//因为Test类和Student之间没有任何的继承关系
//所以在编译的时候就会出错(编译错误,compile error)
此时,可以先使用instanceof来判断要造型的对象是否为可以造型的类型,如:
Person p = new Student();
if(p instanceof Teacher){
Teacher t = (Teacher)p;
}
这样就可以避免出现运行时错误。
相关文章推荐
- 构造方法以及对象初始化过程
- JavaSE入门学习23:Java面向对象之构造方法
- 构造方法、类的初始化块以及类字段的初始化顺序
- 7 面向对象(成员变量和局部变量的区别,类作为形式参数的问题,匿名对象,封装,private关键字,this关键字,构造方法,类的初始化过程,static关键字)
- Java静态初始化,实例初始化以及构造方法
- Java静态初始化,实例初始化以及构造方法
- 面向对象中的构造方法以及关键字static的理解
- 面向对象 构造方法 初始化对象
- 嵌入式软件开发培训笔记——Java第三天(方法重载、对象的构造与初始化过程分析、封装等)
- 构造方法、类的初始化块以及类字段的初始化顺序
- 图解Java对象初始化过程以及方法调用
- Objective-C(五、@class,id类型,类对象构造方法,加载和初始化,description,Logging宏)——iOS开发基础
- 对象的this以及构造方法
- java 初始化顺序以及由此可能引发的构造方法的潜在问题。
- 构造方法实现对象初始化
- 对象的初始化、继承时,对象的初始化过程、关于构造方法、抽象类……
- JavaSE入门学习23:Java面向对象之构造方法
- Java第11次作业:什么是继承?继承的好处?什么是覆写?super()?构造代码块?子父类初始化顺序? 抽象类能用final声明吗?final关键字声明类 方法 变量以及全局常量?抽象类的构造方法?
- Java静态初始化,实例初始化以及构造方法
- 【JavaSE学习笔记】面向对象_01(入门,匿名对象,成员变量,局部变量,封装,this,构造方法)