Java多态深入探究-----构造器和多态、继承和清理

看见标题之后,很多人都会非常惊讶,构造器和多态?有什么必然的联系吗?
反正我看见第一眼的时候非常的惊讶,那么我们就来一探究竟吧。

构造器的调用顺序:
在这里先笼统的概括一下,后面会有更加详细的。调用的顺序:
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的原因就知道了

好了,就写到这里了,希望大家能学到点儿知识
喜欢的就关注我吧,多多点赞,会变好看,多多留言,会变有钱呦!!!

1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值