隐式使用This 引用逸出

本文探讨了Java并发编程中的对象发布与逸出概念,并通过示例代码详细解释了This引用逸出的问题及其解决方案。

       发布(Publish)一个对象是指使对象能够在当前作用域之外的代码中被使用。例如将指向该对象的引用保存到其他代码可以访问到的地方 ,或者在一个非私有的方法中返回一个对象的引用或者将该引用传递到其他类的方法中。当发布一个对象时,在该对象的非私有域中的引用同样被发布出去,一般来说如果一个已经发布的对象能够通过非私有的变量和方法调用到其他的对象那么这个对象也被发布。

       逸出(Escape)是指 一个比应该被发布的对象被发布出去。

 

在 《Java Concurrency in Practice》中关于隐式使用This 引用逸出的示例,比较难以理解如何逸出。ThisEscape发布EventListener时,也隐含地发布了ThisEscape实例本身,因为在这个内部类的实例中包含了对ThisEscape 实例的隐含引用。这个个人的理解代码是:

 

 

/**
 *
 * @author zhangwei_david
 * @version $Id: ThisEscape.java, v 0.1 2014年11月3日 上午9:37:54 zhangwei_david Exp $
 */
public class ThisEscape {
    private String name = null;

    public ThisEscape(EventSource source) {
        source.registerListener(new EventListener() {

            public void onEvent(Event event) {
                doSomething(event);
            }

        });
        name = "TEST";
    }

    /**
     *
     * @param event
     */
    protected void doSomething(Event event) {
        System.out.println(name.toString());
    }
}
import java.awt.Event;

/**
 * 
 * @author zhangwei_david
 * @version $Id: Listener.java, v 0.1 2014年11月3日 上午9:40:13 zhangwei_david Exp $
 */
public interface EventListener {
    public void onEvent(Event event);

}

 

/**
 *
 * @author zhangwei_david
 * @version $Id: EventSource.java, v 0.1 2014年11月3日 上午9:38:40 zhangwei_david Exp $
 */
public class EventSource {

    public void registerListener(EventListener listener) {
        listener.onEvent(null);
    }

}

 

/**
 *
 * @author zhangwei_david
 * @version $Id: Client.java, v 0.1 2014年11月3日 上午9:45:48 zhangwei_david Exp $
 */
public class Client {

    /**
     *
     * @param args
     * @throws InterruptedException
     */
    public static void main(String[] args) throws InterruptedException {
        EventSource es = new EventSource();
        new ThisEscape(es);

    }

}

 运行的结果是:

Exception in thread "main" java.lang.NullPointerException
	at com.cathy.demo.concurrency.escape.ThisEscape.doSomething(ThisEscape.java:33)
	at com.cathy.demo.concurrency.escape.ThisEscape$1.onEvent(ThisEscape.java:21)
	at com.cathy.demo.concurrency.escape.EventSource.registerListener(EventSource.java:15)
	at com.cathy.demo.concurrency.escape.ThisEscape.<init>(ThisEscape.java:18)
	at com.cathy.demo.concurrency.escape.Client.main(Client.java:21)

  这个就是由于在name 初始化之前,就使用了ThisEscape实例,而此时实例尚未完成初始化。

 同样修改SafeListener

import java.awt.Event;

/**
 *
 * @author zhangwei_david
 * @version $Id: SafeListener.java, v 0.1 2014年11月3日 上午10:20:26 zhangwei_david Exp $
 */
public class SafeListener {

    private final EventListener listener;
    private String              name = null;

    private SafeListener() {
        listener = new EventListener() {

            public void onEvent(Event event) {
                doSomething();
            }
        };
        name = "TEST";
    }

    public static SafeListener newInstance(EventSource eventSource) {
        SafeListener safeListener = new SafeListener();
        eventSource.registerListener(safeListener.listener);
        return safeListener;
    }

    /**
     *
     */
    protected void doSomething() {
        System.out.println(name.toString());
    }
}
/**
 *
 * @author zhangwei_david
 * @version $Id: Client.java, v 0.1 2014年11月3日 上午9:45:48 zhangwei_david Exp $
 */
public class Client {

    /**
     *
     * @param args
     * @throws InterruptedException
     */
    public static void main(String[] args) throws InterruptedException {
        EventSource es = new EventSource();
        //        new ThisEscape(es);
        SafeListener.newInstance(es);
    }

}

 结果是:

TEST

 

直接引用引用在不同的IT场景中有不同的表现和区别,以下从几个方面详细说明: ### 引用 - **直接引用**:是指在代码中明确地指定要引用的对象、库、文件等。在C++中引用dll库时,需要明确地将`.h`和`.lib`文件复制到项目根目录下,`.dll`放在debug(`.exe`所在)目录下,并且在项目中引入特定的代码,如`#include "xxxx.h"`和`#pragma comment(lib, "xxxx.lib")`,这就是典型的直接引用dll库的方[^3]。 - **引用**:则是在代码中不需要显地指定引用的对象,系统会自动进行引用。在.Net 6中启用SDK声明全局引用,只需要在项目属性里勾选“全局使用”选项,就可以免于手动使用`using`引用系统的SDK相关组件,这就是引用的体现[^2]。 ### 代码可读性 - **直接引用**:由于明确指出了引用的对象,使得代码的意图更加清晰,阅读代码时可以直接知道引用了哪些外部资源。在C++中直接引用dll库的代码,开发者可以很清楚地看到引用了哪个头文件和库文件[^3]。 - **引用**:代码中没有明显的引用语句,对于不熟悉系统引用规则的开发者来说,可能会不清楚代码依赖了哪些外部资源,降低了代码的可读性。 ### 维护难度 - **直接引用**:当引用的对象发生变化时,需要在代码中直接修改引用部分,维护相对较为繁琐。如果dll库的文件名或路径发生变化,就需要修改`#include`和`#pragma comment`语句中的相关内容[^3]。 - **引用**:系统自动处理引用,当引用的对象发生变化时,只要系统的引用规则没有改变,代码本身不需要进行修改,维护相对简单。 ### 编译和运行时影响 - **直接引用**:在编译时,编译器可以直接根据引用语句找到对应的资源进行编译,编译过程相对明确。运行时,程序也会按照引用的路径去加载资源。 - **引用**:编译时,编译器需要根据系统的引用规则去查找和加载资源,可能会增加编译的复杂度。运行时,也依赖系统的规则来加载资源,如果规则配置不当,可能会导致资源加载失败。 ### 类型转换方面 - **直接引用**:在类型转换中,直接引用通常需要显地进行类型转换操作。将一个`int`类型的变量转换为`double`类型,需要使用的转换语法,如`double d = (double)i;`。 - **引用**:在类型转换中,引用允许编译器自动进行类型转换。在某些情况下,编译器会自动将`int`类型的变量转换为`double`类型,如`double d = 10;`。 ### 示例代码 以下是C++中直接引用dll库和C#中引用系统组件的示例代码: #### C++直接引用dll库 ```cpp #include <iostream> using namespace std; #include "xxxx.h" using namespace xxxxx; //.h中会有说明命名空间 #pragma comment(lib, "xxxx.lib") int main() { // 使用dll中的函数 return 0; } ``` #### C#引用系统组件 ```csharp // 在项目属性中勾选“全局使用”后,无需手动using class Program { static void Main() { // 直接使用系统组件中的类和方法 } } ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值