对象的创建、发布、逸出

对象创建、发布

对象的创建,许多新手也都耳熟能详了,例如

public class School {
	private static Person person = new Person();
}

但是因为private关键字的作用,其他类无法直接访问person对象,所以产生了“发布”对象概念。对象的“发布”指的是“使对象能够在当前作用域之外的代码使用”。改变private关键字就可以达到目的:

public class School {
	public static Person person = new Person();
}

也有延迟创建方法,这样只有在调用init()函数时才实例化Person对象,相对来说更为优质:

public class School {
	private static Person person;
	
	public Person init() {
		person = new Person();
		return person;
	}
}

类比面向对象的封装特性,可以说“封装”是一种更加严格规范的“发布”。


对象逸出

对象发布、创建最容易犯的错误就是对象逸出,“对象逸出”指的是某不应该发布的对象被发布的情况。

例如School类引用了Person类,但是假若要求不该让外界知道Person对象的具体内容,那么之前的操作都不应该进行。但是时常发生的是:你认为已经封装好了对象,但是不经意的操作仍然造成了“对象逸出”。根据形式不同可以分为“显式逸出”和“隐式逸出”。

 

显式逸出

显示逸出主要问题在于向外界暴露了对象的引用。比如向外发布一个数组,很多人会选择这么做:

public class School {
	private Person[] personArray = new Person[] {new Person("Marco", 28), new Person("tremedous", 25)};
	
	public Person[] getPersonArray() {
		return personArray;
	}
}

这么发布Person数组并不安全,因为调用getPersonArray()函数的地方会持有personArray在堆内存中的引用,可以间接暴露、改变原personArray中的元素。

所以假若你只是想定义一个数组,只想让外界读取数组元素,不能改写数组,getPersonArray()向外界返回的应该只是personArray数组的一个副本。

public class School {
	private Person[] personArray = new Person[] {new Person("Marco", 28), new Person("tremedous", 25)};
	
	public Person[] getPersonArray() {
		//Person[] personArrayError = personArray;    错误,personArrayError仍然指向personArray数组引用地址
		
		Person[] personArrayCopy = Arrays.copyOf(personArray, personArray.length);
		return personArrayCopy;
	}
}

要格外注意新手最容易犯的错,Person[] personArrayError = personArray并不是得到了复制数组,而是在栈中产生了一个新的personArrayError变量保存Person数组在堆中地址的引用。这是personArrayError语句执行的示意图:

显式逸出还有一种情况:一个私有的对象添加到一个集合中去,其他类仍可以通过遍历集合(visitList)获得该对象:

public class School {
	private static Person person = new Person();
	public List<Person> visitList = new ArrayList();
	
	
	public List initList() {
		visitList.add(person);
		return visitList;
	}
}

 

隐式逸出

隐式逸出主要问题在于新建其他对象时候暴露了this引用,例如:

public class School {
	public static Person person;
	public static Room room;
	
	public School() {
		this.person = new Person();
		this.room = new Room();
	}
}

这里造成了this引用在构造函数逸出。因为在person对象发布(实例化)的同时,也间接发布了this应用。这样School类的构造函数还未结束,就发布了一个尚未构造完成的School对象。单线程执行问题不大, 多线程要考虑竞态条件(线程执行时序),因而这是一种不安全的对象构造过程。

该如何规避呢?多看看一些别人的工程代码后,你就容易得到你的答案:

public class School {
	public static Person person;
	public static Room room;
	
	public School() {
		initParam();
	}
	
	private synchronized void initParam() {
		this.person = new Person();
		this.room = new Room();
	}
}

首先,initParam()函数用了synchronized互斥锁,意味着同一时刻只能有一个线程来实例化person和room对象。

再者,调用initParam(),未到达方法出口,不会提前暴露this引用,也就不会造成未构造完成的School对象被提前发布了。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值