JAVA基础(二):函数执行顺序(构造函数,多态,this指针)
1 abstract class Glyph {
2 Glyph() {
3 System.out.println( " Glyph() before draw() " );
4 draw(); // 不明白的地方是这里.
5 System.out.println( " Glyph() after draw() " );
6 }
7 abstract void draw();
8 }
9
10 class RoundGlyph extends Glyph {
11 private int radius = 1 ;
12 RoundGlyph( int r) {
13 radius = r;
14 System.out.println( " RoundGlyph.RoundGlyph(), radius = " + radius);
15 }
16
17 void draw() {
18 // radius = z;
19 System.out.println( " RoundGlyph.draw(), radius = " + radius);
20 }
21 }
22
23 public class PolyConstructors {
24 public static void main(String[] args) {
25 new RoundGlyph( 5 );
26 }
27 }
1 Glyph() before draw()
2 RoundGlyph.draw(), radius = 0
3 Glyph() after draw()
4 RoundGlyph.RoundGlyph(), radius = 5
1类初始化时构造函数调用顺序:
2 首先加载类,遇到extends字样会停止加载当前类,加载父类,之后再继续加载。
3 当把一个类完全加载后,这个类的静态成员将被先加载。之后进行如下流程:
4(1)初始化对象的存储空间为零或null值;
5(2)调用父类构造函数;
6(3)按顺序分别调用类成员变量和实例成员变量的初始化表达式;
7(4)调用本身构造函数
8
9构造函数的初始化顺序:
101 .当执行new RoundGlyph( 5 )时,首先会加载‘RoundGlyph. class ’,但是这个时候JVM发现 RoundGlyph extends (继承于) Glyph ,所以他会加载Glyph. class
112 .然后会初始化Glyph类的static成员数据,这个类没有static成员数据,所以没有这一步。
123 .会执行Glyph类的构造函数Glyph()
134 .接着初始化RoundGlyph类的成员数据,最后调用RoundGlyph()构造函数来生产对象
<示例2>
1public class Test2 extends Test1 {
2 {
3 System.out.print( " 1 " );
4 }
5 Test2() {
6 System.out.print( " 2 " );
7 }
8 static {
9 System.out.print( " 3 " );
10 }
11 {
12 System.out.print( " 4 " );
13 }
14 public static void main(String[] args) {
15 new Test2();
16 }
17}
18
19class Test1 {
20 Test1() {
21 System.out.print( " 5 " );
22 }
23 static {
24 System.out.print( " 6 " );
25 }
26}
1635142
1( 1 ) 虚拟机试图加载Test2,其父类为Test1
2 ( 2 ) 虚拟机加载Test1
3 ( 3 ) Test1静态初始化 -----> 输出 " 6 "
4 ( 4 ) 虚拟机加载Test2
5 ( 3 ) Test2静态初始化 -----> 输出 " 3 "
6 ( 5 ) new Test2()构造Test2首先构造父类Test1
7 ( 6 ) Test1构造 -----> 输出 " 5 "
8 ( 7 ) Test2执行初始化语句块(虚拟机调用Test2初始化方法init) -----> 输出 " 1 " 和 " 4 "
9 ( 8 ) Test2构造 -----> 输出 " 2 "
10
11 =================================
12 先输出6:
13 因为父类的Static的变量 / 常量会被子类共享
14 所以,首先输出6
15 接着,会先初始化自身的static的变量 / 常量
16
17 然后,在构造函数中,会首先调用父类的默认构造函数(如果没有显示调用super(args)的话)
18 在调用父类构造函数时,在执行构造函数体中的数据内容时,先执行方法外的程序段,如变量申明,或者程序段。然后再调用构造函数中的程序段。
19 然后再执行自身的构造函数,执行前,也如上述说:在执行本构造函数体中的数据内容时,先执行方法外的程序段,如变量申明,或者程序段。然后再调用构造函数中的程序段。
20
21 这就是一个对象的实例化过程。
类初始化时构造函数调用顺序:
首先加载类,遇到extends字样会停止加载当前类,加载父类,之后再继续加载。
当把一个类完全加载后,这个类的静态成员将被先加载。之后进行如下流程:
( 1 )初始化对象的存储空间为零或null值;
( 2 )调用父类构造函数;
( 3 )按顺序分别调用类成员变量和实例成员变量的初始化表达式;
( 4 )调用本身构造函数
<示例3>this指针
1public class CalC {
2 void amethod(){
3 System.out.println("CalC.amethod");
4 }
5 CalC(){
6 amethod();
7 System.out.println("Hu?");
8 }
9 public static void main(String[] args) {
10 // TODO Auto-generated method stub
11 CalC cc = new CalChild();
12 cc.amethod();
13 }
14}
15class CalChild extends CalC{
16 void amethod(){
17 System.out.println("CalChild.amethod");
18 }
19}
1CalChild.amethod
2Hu?
3CalChild.amethod
实例方法专用于类的对象,但是在内存中只有一份实例方法供类的所有实例共享,因为为每个对象都复制一份所有实例方法代价很昂贵。
所以系统中专门有这样一种机制来保证:当你调用一个方法时,代码是按专用于这个对象的方式进行,这种机制就是this。
每个实例方法都有一个名为this的变量,指的是调用该方法的当前对象。这种机制是当你的方法引用了类的一个实例变量是由编译器隐含使用的。
即使对实例变量的每个引用实质上是有 this. 前缀的
当调用一个实例方法时,this变量将被设置成引用它所起作用的特定类对象。
所以的对象都共享这个备份,为了区分开到底是哪个对象在调用这个方法,关键的地方就是this的使用。this把调用方法的上下文对应到当前对象上。
本例分析:
调用java中的所有成员变量或者成员函数都隐含了this。所以这个地方就很明了了:构造子类,this指针代表的当前对象是子类实例,子类实例为啥不调用自己overriding的方法呢?!
CalC cc = new CalChild();这一步也叫“异类搜集”,以父类类型声明变量作为子类对象的引用,此时子类的所有属性方法都失效,只有子类中重写父类的这样一中方法可以被变量调用,而不调用父类的同名方法,从而实现多态!
此程序前后两次打印都是一个原理。。
参考java基础(一)
<示例4>
1class super1{
2 {
3 System.out.println("super1 ok");
4 }
5 super1() {
6 System.out.println("3");
7 }
8 }
9
10 class Employee extends super1{
11 private String name;
12 private double salary=1500.00;
13 private Date birthday;
14 public Employee(String n,Date DoB){
15 System.out.println("2");
16 name=n;
17 birthday=DoB;
18 }
19 public Employee(String n){
20 this(n,null);
21 System.out.println("4");
22 }
23 }
24 class Manager extends Employee{
25 {
26 System.out.println("Manager ok");
27 }
28 private String department;
29 public Manager(String n,String d){
30 super(n);
31 department=d;
32 }
33 }
34
35 public class test1{
36 public static void main(String args[]){
37 new Manager("Smith","sales");
38 }
39 }
1super1 ok
23
32
44
5Manager ok
你总是可以从派生类构造器中调用一个适当的基础类构造器,对基础类构造器的调用必须放在派生类构造器的首位。
如果派生类构造器第一个语句不是对基础类构造器的调用,则编译器将替你插入一个对默认基础类构造器的调用:super()
如果你自己定义了自己的构造函数,则编译器认为是你会负责一个对象构造的全部细节问题,包括对默认构造器的任何要求,编译器不将管理。
如果你构造函数中要使用super() super(…) this() this(…)等字样,则必须放在第一行。
如果你没有显示指定super() super(…)字样,系统会在构造函数第一行自动插入super()字样;如果你指定了,系统将按你指定的进行而不是默认的了。
<练习1>
1class B{
2 B(){
3 System.out.println("B()");
4 }
5 }
6 class C extends B{
7 C(){
8 System.out.println("C()");
9 }
10
11 public class A{
12 public static void main(String[] arg){
13 new C();
14 }
15 }
B()
C()
<练习2>
1class A {
2 public A() {
3 System.out.println("A's c is here ");
4 }
5 void println() {
6 System.out.println("A's v is here ");
7 }
8 }
9 class B extends A {
10 public B() {
11 System.out.println("B's c is here ");
12 }
13 void println() {
14 System.out.println("B's v is here ");
15 }
16 }
17 public class Chp_4_2 {
18 public static void main(String[] args) {
19 B b = new B();
20 }
21 }
子类的构造函数,除非显示的写上super或者super(Object),第一行会被插入super()的
因为class B extends class A ,所以你可以这样看B的构造方法
public B() {
super();
System.out.println("B's c is here");
}
所以A's c is here会输出
<练习3>
1public class JRun1 {
2public JRun1() {
3System.out.println(" 构造函数");
4}
5static{
6System.out.println("static{}");
7}
8{
9System.out.println("{}");
10}
11public static void main(String[] args) {
12System.out.println("main()");
13new JRun1();
14}
15}
static{}
main()
{}
构造函数
import java.util.Calendar;
public class SingletonTest{
private static final SingletonTest instance = new SingletonTest();
private static final int BIZZ = 100;
{
System.out.println(BIZZ);
}
private int size = 0;
private static int YEAR = Calendar.getInstance().get(Calendar.YEAR);
{
System.out.println("after generate instance line");
}
private SingletonTest() {
System.out.println("Constructor");
size = YEAR - 1930;
}
public int getSize() {
return size;
}
public static void main(String[] args) {
System.out.println(instance.getSize());
}
}
100
after generate instance line
Constructor
-1930
1 import java.util.Calendar;
2
3 public class SingletonTest{
4 private static final SingletonTest instance = new SingletonTest();
5 private int size = 0;
6 private static int YEAR = Calendar.getInstance().get(Calendar.YEAR);
7 {
8 System.out.println("after generate instance line");
9 }
10 private SingletonTest() {
11 System.out.println("Constructor");
12 size = YEAR - 1930;
13 }
14 public int getSize() {
15 return size;
16 }
17 public static void main(String[] args) {
18 System.out.println(instance.getSize());
19 }
20 }
1 after generate instance line
2 Constructor
3 -1930
1 import java.util.Calendar;
2
3 public class SingletonTest{
4 private static final int BIZZ = 100;
5 {
6 System.out.println(BIZZ);
7 }
8 private int size = 0;
9 private static int YEAR = Calendar.getInstance().get(Calendar.YEAR);
10 private static final SingletonTest instance = new SingletonTest();
11 {
12 System.out.println("after generate instance line");
13 }
14 private SingletonTest() {
15 System.out.println("Constructor");
16 size = YEAR - 1930;
17 }
18 public int getSize() {
19 return size;
20 }
21 public static void main(String[] args) {
22 System.out.println(instance.getSize());
23 }
24 }
<练习>
1public class Parent {
2 public void test(){ }
3 public Parent(){
4 test();
5 }
6 public static void main(String[] args){
7 new Child();
8 }
9}
10
11class Child extends Parent{
12 private int instanceValue = 20;
13 public void test(){
14 System.out.println("instance value is: " + instanceValue);
15 }
16 }
17看输入结果是什么?
0
1(网友)花之剑:定义实例变量instanceValue(默认为0)->调用父类无参构造方法->调用子类的test()(因多态性,test被重写)->构造方法结束->实例变量的初始化,此时instanceValue=20
2
3(网友)花之剑:
41.如果子类无自己的构造方法,子类默认继承父类无形参构造方法
5顺序应该是:
6定义实例变量instanceValue(默认为0)->调用父类无参构造方法->调用子类的test()(因多态性,test被重写)->构造方法结束->实例变量的初始化,此时instanceValue=20
72.如果子类有自己的构造方法,子类构造方法第一行默认super().
8顺序应该是:
9定义实例变量instanceValue(默认为0)->调用父类无参构造方法->调用子类的test()(因多态性,test被重写)->父类无形参构造方法结束->实例变量的初始化,此时instanceValue=20 -->子类的构造方法。
10
11public class Parent{
12 public void test(){}
13 public Parent(){
14 test();
15 }
16 public static void main(String[] args){
17 new Child();
18 }
19}
20
21class Child extends Parent{
22 private int instanceValue = 20;
23 public Child(){
24 //super();
25 System.out.println("instance value is: " + instanceValue);
26 }
27 public void test(){
28 System.out.println("instance value is: " + instanceValue);
29 }
30}