在看了《Effective Java》Item2中对JavaBean的描述后,再结合Item1和Builder模式,遂想有没有其他方式避免JavaBean创建的线程安全问题呢?
以如下JavaBean类为例,
public class JavaBean { private int count; private String level; private List<String> messages; public int getCount() { return count; } public void setCount(int count) { this.count = count; } public String getLevel() { return level; } public void setLevel(String level) { this.level = level; } public List<String> getMessages() { return messages; } public void setMessages(List<String> messages) { this.messages = messages; } }
分步创建对象如下,因为几个set方法的调用导致对象的状态可能不一致,存在线程安全问题。比如javaBean对象在执行一系列set方法过程中,其他线程调用get方法,这时得到的属性值可能就不对了。
JavaBean javaBean = new JavaBean(); javaBean.setCount(1); javaBean.setLevel("first"); javaBean.setMessages(new ArrayList<String>());
为了避免创建过程中被访问,可以用匿名对象+链式调用的方式,如下代码,
public class JavaBean { private int count; private String level; private List<String> messages; public int getCount() { return count; } public JavaBean setCount(int count) { this.count = count; return this; } public String getLevel() { return level; } public JavaBean setLevel(String level) { this.level = level; return this; } public List<String> getMessages() { return messages; } public JavaBean setMessages(List<String> messages) { this.messages = messages; return this; } }
JavaBean javaBean1 = new JavaBean().setCount(1).setLevel("second").setMessages(new ArrayList<String>()); System.out.println(javaBean1.getLevel());
以上写法只有对象完全初始化好后,才将引用赋给javaBean1,避免创建过程中被其他线程访问。
但这个时候,以上分步创建对象的方式仍然可以用。怎么能强制使用链式调用呢?想来想去觉得还得对链式调用进行封装。
增加如下代码,
private JavaBean(){ } public static JavaBean newInstance(){ return new JavaBean(); } public static JavaBean newInstance(int count, String level){ return new JavaBean().setCount(count).setLevel(level); }
这样一来,实际上跟直接使用参数化的构造函数一样,如下
public JavaBean(){ } public JavaBean(int count, String level){ this.count = count; this.level = level; }
总结:
1. 在写JavaBean时,set方法尽量返回JavaBean类型;
2. 在创建对象时,尽量使用链式调用。