• 对象的发布与逸出


    介绍发布与逸出的定义及代码示范,以及如何使用工厂方法避免this引用在构造方法中逸出

    发布:

    “发布”一个对象是指,使对象能够在当前作用域之外的代码中使用。

    例如:将一个指向该对象的引用保存到其他代码可以访问的地方,或者在某一个非私有的方法中返回该对象的引用,或者将引用传递到其他类的方法中。

    代码示范:将一个指向该对象的引用,保存到其他代码可以访问的地方。

    // 保存在一个共有的静态变量中
    public static Set<Secret> knowSecrets;
    
    public void initialize(){
        knowSecrets = new HashSet<Secret>();
    }

    逸出:

    “逸出”是指,某个不应该发布的对象被发布出去。

    当发布某个对象时,可能会间接地发布其他对象。例如上述代码,若将一个 Secret 对象添加到 knownSecret 中,那么会发布这个 Secret 对象;因为任何代码都可以遍历这个集合,并获得对  Secret 对象的引用。同样,如果从非私有方法中返回一个引用,则会发布返回对象。

    代码示范:某一个非私有的方法中返回私有对象的引用。

    class UnsafeStates{
        private String[] states = new String[]{"AK","AL"};
        public String[] getStates(){
            return states;
        }
    }

    上述代码中,数组 states 已经逸出了它所在的作用域,因为这个本应是私有的变量,已经被发布了。

    逸出范围:当一个对象A被发布时,在A的非私有域中引用的所有对象同样会被发布。也就是,一个已经发布的对象A,能够通过非私有的变量引用和方法调用到达其他的对象,那么所能够到达的对象,均会跟随A一起发布。

    此处给出一个定义:“外部方法”。假如有一个类C,对于C来说,“外部方法”是指行为不完全由C来规定的方法,包括其他类中定义的方法以及类C中可以被改写的方法(既不是[private]方法,也不是[final]方法)。

    当把一个对象传递给外部方法时,则该对象就会面临一定的危险,因为你不知道外部方法会对该对象做些什么,因此我们需要使用封装。封装能够使得对程序的正确性进行分析变得可能,并使得无意中破坏设计约束条件变得困难。

    工厂方法避免this引用在构造方法中逸出:

    首先了解 this 引用是如何在构造方法中逸出的。

    先看一段代码:发布一个内部类的实例。

    public class ThisEscape{
        public ThisEscape(EventSource source){
            source.registerListener(
                new EventListener(){
                    public void onEvent(Event e){
                        doSomething(e);
                    }
                });
        }
    }

    上述代码中,当 ThisEscape 发布 EventListener 时,也隐含发布了 ThisEscape 实例本身,因为这个内部类的实例中包含了对 ThisEscapse 实例的隐含引用。只要其他线程在ThisEscape未构造之前(构造返回状态)调用这个类,那么this就会被新建线程共享并识别它(线程溢出)。

    下面的代码对上面的示例进行解释:

    public class ThisEscape {
         int i = 100; 
         public ThisEscape(int j){
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        System.out.println(i + j);
                    }
                }).start();
            }
         
         public static void main(String[] args) {
            ThisEscape thisE = new ThisEscape(100);
        }
    }

    上述代码给出了逸出的一个特殊示例,即 this 引用在构造方法中逸出。当内部的 new Thread  发布时,在外部封装的 ThisEscape 实例也逸出了,于是可以取到 i 值与 j 进行计算。因此当从对象的构造方法中发布对象时,只是发布了一个尚未构造完成的对象。

    在构造方法中使用 this 引用逸出的常见错误:在构造方法中启动一个线程;在构造方法中调用一个可改写的实例方法。

    如果想在构造方法中注册一个事件监听器或启动线程,那么可以使用一个私有的构造方法和一个公共的工厂方法,从而避免不必要的构造过程。如以下程序所示:

    public class SafeListener{
        private final EventListener listener;
        
        private SafeListener(){
            listener = new EventListener(){
                public void onEvent(Event e){
                    doSomething(e);
                }
            }
        }
        
        public static SafeListener newInstance(EventSource source){
            SafeListener safe = new SafeListener();
            source.registerListener(safe.listener);
            return safe;
        }
    }

    上面的代码为在构造方法中注册一个事件监听器,新建的线程无法在构造方法之前共享和识别 safe。

    这边有一个用上述方法构造 JDBC 连接池的例子:http://blog.csdn.net/luozhonghua2014/article/details/40622207 。虽然上述方法构造的连接池比较安全,但在实际应用中,如:org.apache.tomcat.jdbc.pool.ConnectionPool ,并没有用到链接中给出的样式。

  • 相关阅读:
    UICollectionView 应用
    关于UIWebView不能响应touchesBegan等四个方法的解决案例【可以检测 单击双击】
    IOS6 中新特性介绍
    KVO 使用
    IOS 学习资料汇总(^_^)
    [DEVDIV翻译] Core Animation中文翻译_第一章_什么是核心动画
    StoryBoard学习..(很详细.)
    Intent跳转到系统应用中的拨号界面、联系人界面、短信界面及其他
    sqlite语句主页
    Android的快速开发框架 afinal
  • 原文地址:https://www.cnblogs.com/tf-Y/p/5271926.html
Copyright © 2020-2023  润新知