父类构造方法调用被重写的实例方法时注意的问题:
package lesson4;
class Test extends Base
{
public static void main(String[] args)
{
Test t = new Test();//在调用父类Base的构造方法时,所调用的test()为动态调用,也即调用子类Test的test()
System.out.println("Hello World!");
}
public void test()
{
System.out.println("Test test");
}
}
class Base
{
//构造方法,引用了将来被重写的方法
Base()
{
test();//如果实例化子类,当调用该构造方法时,调用被子类覆写的test(),如果test()没被覆写,则调用Base的test()。
// 和实例方法动态调用一样,当子类实例化时,递归到Base(),发现test()实例方法,现在子类中查找,没找到的话上溯到父类……
//如果还没找到,则抛出异常
}
public void test()
{
System.out.println("Base test!");
}
}
尽量把类中使用的辅助方法封装起来(private)。
如果类让使一些方法供外部使用(public),又不想有被重写的风险,可是添加final 关键字,及即public final。如果某个类不想被继承,可设置为final类,也可以让该类具有一个唯一的构造方法,并设置为private访问级别,这样子类无法调用父类的构造方法,也就无法继承了。
package lesson4;
class Test
{
{
System.out.println("******此时a已被系统分配了内存,尽管初始化块在int a语句上面**********");
a = 10;
System.out.println("a初始化块,但a不能被引用,因为这里在a的有效范围之外。但可以赋值。有点不明白,吼吼\n初始化块完毕!");
}
int a = 100;//给a赋值100;词句含义:实例int a 和赋值并不是同步执行,而是先给a分配内存,之后系统负责执行初始化操作
//这里a其实是第二次赋值。上边的初始化块先赋值。
public static void main(String[] args)
{
Test t = new Test();
System.out.println("此时a="+t.a);//此时a=100
}
public void test()
{
System.out.println("Test test");
}
}
结果:
看看总体执行顺序:
package lesson4;
class Test
{
{
System.out.println("******此时啊已被系统分配了内存,尽管在初始化块在int a语句上面**********");
a = 10;
System.out.println("a初始化块,但a不能被引用,因为这里在a的有效范围之外。但可以赋值。有点不明白,吼吼\n初始化块完毕!");
}
int a = 100;//给a赋值100;词句含义:实例int a 和赋值并不是同步执行,而是先给a分配内存,之后系统负责执行初始化操作
//这里a其实是第二次赋值。上边的初始化块先赋值。
public static void main(String[] args)
{
Test t = new Test();
System.out.println("此时a="+t.a);//此时a=100
}
public Test()
{
System.out.println("===Test 构造方法执行===");//初始化执行完毕之后再执行构造方法
}
}
结果:
Java在加载并初始化某个类的时候,总是保证该类的所有父类(继承树)全部加载并初始化.。
当然前提是类加载器在加载成功后已经在运行环境中生成了该类的Class对象。