看见标题之后,很多人都会非常惊讶,构造器和多态?有什么必然的联系吗?
反正我看见第一眼的时候非常的惊讶,那么我们就来一探究竟吧。
构造器的调用顺序:
在这里先笼统的概括一下,后面会有更加详细的。调用的顺序:
1、父类的构造器
2、按照定义顺序来对成员进行初始化
3、调用自己的构造器
下面拿一段代码来验证一下:
public class Test {
public static void main(String [] args) {
SwordMan sowrdman = new SwordMan();
}
}
class Hero {
Hero() { System.out.println("construct Hero"); }
}
class SwordMan extends Hero {
Sword sword = new Sword();
SwordMan() { System.out.println("construct SwordMan"); }
}
class Sword {
Sword() { System.out.println("construct Sword"); }
}
输出结果如下:
construct Hero
construct Sword
construct SwordMan
看输出结果的顺序我们也能看出来构造器的顺序是什么。
并且我们可以看出来,父类的构造器在子类构造的时候就会自动的去调用。但是这是为什么呢?我们来捋一捋,构造器的作用是检查对象是不是被正确的构造,对于父类的成员,子类构造器(通常都是private类型的)没办法访问,只有父类的构造器有知识和权限来访问和初始化内部成员,所以在创建一个子类类型的对象的时候,所有的构造器必须都执行,这样才能完整的初始化这个对象,这就是为什么强制子类调用父类构造器的原因。
继承和清理
当对象不再被使用的时候,垃圾收集机制会将它回收,但是假如说必须对一些资源进行清理的时候, 那么在继承的时候就要好好考虑如何清理资源这个问题了。
看下面的例子:
//释放资源部分
public class Test {
public static void main(String [] args) {
SwordMan swordman = new SwordMan();
swordman.dispose();
}
}
class Hero {
Hero() { System.out.println("construct Hero"); }
void dispose() {
System.out.println("dispose Hero");
}
}
class SwordMan extends Hero {
Sword sword = new Sword();
SwordMan() { System.out.println("construct SwordMan"); }
void dispose() {
System.out.println("dispose SwordMan");
sword.dispose();
super.dispose();
}
}
class Sword {
Sword() { System.out.println("construct Sword"); }
void dispose() {
System.out.println("dispose Sword");
}
}
输出结果如下:
construct Hero
construct Sword
construct SwordMan
dispose SwordMan
dispose Sword
dispose Hero
上面的程序写出了资源释放的顺序,就像C++当中一样,先构造的后析构,后构造的先析构,所以说释放资源的步骤要和构造的顺序相反,先释放自身的,后释放成员的,最后释放父类的资源。
注意:在这个地方,父类的dispose方法在子类当中不会自动调用,需要在子类当中进行显式调用super.dispose方法。
共享资源的释放
对于共享资源,释放的时候需要我们小心一点。
现在想一个问题,假如说现在有一台公用计算机有很多个人一起用,他是共享资源,但是假如说其中一个人因为其他的需求而放弃使用这台计算机,那么他需要拆了计算机吗?肯定不对,就算你一个人满足不了需求,这台计算机还是有用的,因为其他人也要使用,当所有的人都不想用的时候,我们就需要考虑机器换代的问题了,那么这时,拆除计算机也合情合理。
下面使用一段代码来看看上述流程如何实现。
public class Test {
public static void main(String [] args) {
Computer computer = new Computer();
User [] user = {
new User(computer),
new User(computer),
new User(computer),
};
for(User u : user) {
u.dispose();
}
}
}
class Computer {
private int recount;
private static long count = 0; //记录这是第几个computer
private final long id = count++; //计算机的id
public Computer() {
System.out.println("constructing " + this);
}
public void dispose() {
if(--recount == 0) { //当没有人使用的时候 才释放掉
System.out.println("disposing " + this);
}
}
public void addRecount() { //增加对象的重用
recount++;
}
public String toString() {
return "computer " + id;
}
}
class User {
private Computer computer;
private final long id = count++; //用户的 id
private static long count = 0; //记录这是创建的第几个对象
public User(Computer computer) {
System.out.println("constructing " + this);
this.computer = computer;
computer.addRecount(); //当共享对象被使用时增加重复使用次数
}
public void dispose() {
System.out.println("disposing " + this);
computer.dispose();
}
public String toString() {
return "user " + id;
}
}
显示结果:
constructing computer 0
constructing user 0
constructing user 1
constructing user 2
disposing user 0
disposing user 1
disposing user 2
disposing computer 0
在这里要注意了,在共享资源重复使用的时候,需要调用addRecount方法来计算重复使用次数,在recount为0的时候才开始释放资源,这种计数的方法叫做引用计数
小技巧:
1、在这里面,count和id都是long类型的,而不是int类型,这样做的话,计数器不会发生溢出的情况。
2、id是fina类型的,是为了保证对象的id不会发生改变
构造器内部的多态方法的行为
我们都知道,调用动态绑定的方法的时候都是调用重写(覆盖)这个方法的方法体,但是我们不知道方法体是什么样的,所以这样调用的时候,我们也不知道会出现什么样的错误。下面用一段代码来解释一下。
public class Test {
public static void main(String [] args) {
HalfCircle hc = new HalfCircle(5);
}
}
class Circle {
private int radius = 3;
public Circle() {
System.out.println("constructing Circle");
display();
}
public void display() {
System.out.println("radius = " + this.radius);
}
}
class HalfCircle extends Circle {
private int radius;
public HalfCircle(int radius) {
System.out.println("constructing HalfCircle");
this.radius = radius;
display();
}
public void display() {
System.out.println("radius = " + this.radius);
}
}
显示的结果让我们很惊讶:
constructing Circle
radius = 0
constructing HalfCircle
radius = 5
我们用肉眼跑一下程序,创建半圆(HalfCircle)对象的时候先进行父类Circle的构造函数,其中调用的display方法是被HalfCircle类重写的,所以调用的方法体是子类的,子类的radius成员是没被初始化int类型数据,输出的是radius = 0,所以说构造器内调用多态方法的时候很危险。
但是这个没被初始化的radius成员为什么是0 呢?
这就要把初始化的顺序详细化一点
1、是在其他事情发生之前,将分配给对象的储存空间初始化为二进制的0
2、调用父类构造器
3、按声明的顺序对成员进行初始化
4、调用子类的构造器主体
所以说radius初始化为0的原因就知道了
好了,就写到这里了,希望大家能学到点儿知识
喜欢的就关注我吧,多多点赞,会变好看,多多留言,会变有钱呦!!!