父类与子类Word下载.docx
《父类与子类Word下载.docx》由会员分享,可在线阅读,更多相关《父类与子类Word下载.docx(33页珍藏版)》请在冰点文库上搜索。
看下面的例子:
classMammal{
voidgetBirthInfo(){
System.out.println("
bornalive."
classPlatypusextendsMammal{
hatchfromeggs"
System.out.print("
amammalnormallyis"
super.getBirthInfo();
在上面的例子中,使用super.getBirthInfo()去调用超类Mammal中被重载的方法。
构造器使用super去调用超类中的构造器。
而且这行代码必须放在第一行,否则编译将出错。
publicclassSuperClassDemo{
SuperClassDemo(){}
classChildextendsSuperClassDemo{
Child(){
super();
在上面这个没有什么实际意义的例子中,构造器Child()包含了super,它的作用就是将超类中的构造器SuperClassDemo实例化,并加到Child类中。
编译器自动加入代码
编译器自动加入代码到构造器,对于这个,java程序员新手可能比较混淆。
当我们写一个没有构造器的类,编译的时候,编译器会自动加上一个不带参数的构造器,例如:
publicclassExample{}
编译后将如下代码:
publicclassExample{
Example(){}
在构造器的第一行,没有使用super,那么编译器也会自动加上,例如:
publicclassTestConstructors{
TestConstructors(){}
编译器会加上代码,如下:
TestConstructors(){
super;
仔细想一下,就知道下面的代码
经过会被编译器加代码形如:
Example(){
继承
构造器是不能被继承的。
子类可以继承超类的任何方法。
看看下面的代码:
publicvoidsayHi{
system.out.println("
Hi"
publicclassSubClassextendsExample{
this
指向同一个类中另外一个构造器,在第一行
指向当前类的一个实例,不能用于静态方法
super
调用父类的构造器,在第一行
调用父类中一个重载的方法
构造器不能被继承
方法可以被继承
编译器自动加入一个缺省的构造器
自动加入(如果没有)
不支持
编译器自动加入一个缺省的调用到超类的构造器
++++++++++++++++++++++++++JAVA技术专题综述之构造方法篇+++++++++++++++++++
类的继承机制使得子类可以使用父类的功能(即代码),并且子类也具有父类的类型。
下面介绍类在继承关系上的初始化的顺序问题。
示例1:
classSuperClass
{
SuperClass()
SuperClassconstructor"
publicclassSubClassextendsSuperClass
SubClass()
SubClassconstructor"
publicstaticvoidmain(String[]args)
SubClasssub=newSubClass();
输出结果:
SuperClass
constructor
SubClassconstructor
在子类中只实例化了一个子类对象。
从输出结果上看,程序并不是一开始就运行自己的构造方法,而是先运行其父类的默认构造方法。
注意:
程序自动调用其父类的默认构造方法。
示例2:
classSuperClass
SuperClass(Stringstr)
Superwithastring."
SubClass(Stringstr)
Subwithastring."
SubClasssub=newSubClass("
sub"
在JDK下编译此程序不能成功。
正如上例中说的:
程序在初始化子类时先要寻找其父类的默认构造方法,结果没找到,那么编译自然不能通过。
解决这个问题有两个办法:
1.在父类中增加一个默认构造方法。
2.在子类的构造方法中增加一条语句:
super(str);
且必须在第一句。
这两种方法都能使此程序通过编译,但就本程序来说运行结果却不相同。
第1种方法的运行结果是:
Subwithastring.
第2种方法的运行结果是:
Superwithastring.
Subwithastring.
第2种解决方法实际上是指定编译器不要寻找父类的默认构造方法,而是去寻找带一个字符串为参数的构造方法。
下面介绍对象的初始化顺序问题。
示例3:
classOne
One(Stringstr)
System.out.println(str);
classTwo
Oneone_1=newOne("
one-1"
Oneone_2=newOne("
one-2"
Oneone_3=newOne("
one-3"
Two(Stringstr)
publicclassTest
Testmain()start..."
Twotwo=newTwo("
two"
Testmain()start...
one-1
one-2
one-3
two
在main()方法中实例化了一个Two类的对象。
但程序在初始化Two类的对象时,并非先调用Two类的构造方法,而是先初始化Two类的成员变量。
这里Two类有3个成员变量,它们都是One类的对象,所以要先调用3次One类的相应的构造方法。
最后在初始化Two类的对象。
示例4:
staticOneone_3=newOne("
Twotwo_1=newTwo("
two-1"
------------"
Twotwo_2=newTwo("
two-2"
two-1
------------
two-2
如果一个类中有静态对象,那么它会在非静态对象前初始化,但只初始化一次。
非静态对象每次调用时都要初始化。
示例5:
3
staticTwotwo_3=newTwo("
two-3"
two-3
程序中主类的静态变量会在main()方法执行前初始化。
结果中只输出了一次one-3,这也说明:
如果一个类中有静态对象,那么它会在非静态对象前初始化,但只初始化一次。
示例6:
staticinti=0;
staticOneone_2=newOne("
Two.i="
+Two.i);
4
Two.i=0
不仅第1次创建对象时,类中所有的静态变量要初始化,第1次访问类中的静态变量(没有创建对象)时,该类中所有的静态变量也要按照它们在类中排列的顺序初始化。
综上所述:
在创建对象时,对象所在类的所有数据成员会首先进行初始化,如果其中的成员变量有对象,那么它们也会按照顺序执行初始化工作。
在所有类成员初始化完成后,才调用对象所在类的构造方法创建对象。
构造方法作用就是初始化。
静态对象(变量)在非静态对象前初始化。
静态对象(变量)只初始化一次,再次调用就不初始化了,但非静态对象在每次调用时都要初始化。
程序中的主类的静态变量会在main()方法执行前进行初始化工作。
初始化的顺序包括构造方法调用的顺序如下:
1.主类的静态成员首先初始化。
2.主类的超类的构造方法按照从最高到最低的顺序被调用。
3.主类的非静态对象(变量)初始化。
4.调用主类的构造方法。
在一个构造方法中只能调用一次其它的构造方法,并且调用构造方法的语句必须是第一条语句。
++++++++++++++++++++深入剖析java类的构造方式++++++++++++++++++++++++
概要:
本文通过查看一个精心构造的类结构的运行输出和使用javap工具查看实际生成的java字节码(bytecode)向java程序员展示了一个类在运行时是如何构造生成的。
关键字:
java构造javap字节码bytecode
按照java规范,一个类实例的构造过程是遵循以下顺序的:
1.如果构造方法(constructor,也有翻译为构造器和构造函数的)是有参数的则进行参数绑定。
2.内存分配将非静态成员赋予初始值(原始类型的成员的值为规定值,例如int型为0,float型为0.0f,boolean型为false;
对象类型的初始值为null),静态成员是属于类对象而非类实例,所以类实例的生成不进行静态成员的构造或者初始化,后面将讲述静态成员的生成时间。
3.如果构造方法中存在this()调用(可以是其它带参数的this()调用)则执行之,执行完毕后进入第6步继续执行,如果没有this调用则进行下一步。
4.执行显式的super()调用(可以是其它带参数的super()调用)或者隐式的super()调用(缺省构造方法),此步骤又进入一个父类的构造过程并一直上推至Object对象的构造。
5.执行类申明中的成员赋值和初始化块。
6.执行构造方法中的其它语句。
现在来看看精心构造的一个实例:
classParent
{
intpm1;
intpm2=10;
intpm3=pmethod();
System.out.println("
Parent'
sinstanceinitializeblock"
}
publicstaticintspm1=10;
static
sstaticinitializeblock"
}
Parent()
sdefaultconstructor"
staticvoidstaticmethod()
sstaticmethod"
intpmethod()
smethod"
return3;
classChildextendsParent
intcm1;
intcm2=10;
intcm3=cmethod();
Otherco;
publicstaticintscm1=10;
Child'
Child()
co=newOther();
Child(intm)
this();
cm1=m;
sself-defineconstructor"
intcmethod()
classOther
intom1;
Other(){
Other'
publicclassInitializationTest
publicstaticvoidmain(Stringargs[])
Childc;
programstart"
System.out.println(Child.scm1);
c=newChild(10);
programend"
进入此文件所在的目录,然后
编译此文件:
javacInitializationTest.java
运行此程序:
java?
classpath.InitializationTest
得到的结果是:
programstart
Parent'
sstaticinitializeblock
Child'
10
smethod
sinstanceinitializeblock
sdefaultconstructor
Other'
sself-defineconstructor
programend
如果没有看过上面的关于类的构造的说明,很容易让人误解为类的构造顺序是如下的结果(忽略参数绑定、内存分配和非静态成员的缺省值赋值):
1.完成父类的非静态成员初始化赋值以及执行初始化块(这个的先后顺序取决于源文件中的书写顺序,可以将初始化块置于成员声明前,那么先执行的将是初始化块,将上面的代码稍稍变动一下就可以验证这一点。
)
2.调用父类的构造方法完成父类构造。
3.完成非静态成员的初始化赋值以及执行初始化块。
4.调用构造方法完成对象的构造,执行构造方法体中的其它内容。
如果根据以上java规范中给出的顺序也可以合理的解释程序的输出结果,那么如何亲眼看到是规范中的顺序而不是以上根据程序的输出推断的顺序呢?
下面就使用JDK自带的javap工具看看实际的顺序,这个工具是一个根据编译后的字节码生成一份字节码的助记符格式的文档的工具,就像根据机器码生成汇编代码那样。
反编译:
javap-c-classpath.Child
输出的结果是(已经经过标记,交替使用黑体和斜体表示要讲解的每一块):
CompiledfromInitializationTest.java
classChildextendsParent{
intcm2;
intcm3;
publicstaticintscm1;
static{};
Child();
Child(int);
intcmethod();
staticvoidstaticmethod();
Methodstatic{}
0bipush10
2putstatic#22<
Fieldintscm1>
5getstatic#20<
Fieldjava.io.PrintStreamout>
8ldc#5<
String"
>
10invokevirtual#21<
Methodvoidprintln(java.lang.String)>
13return
M