代码之家  ›  专栏  ›  技术社区  ›  asalamon74

在构造函数警告中泄漏此信息

  •  76
  • asalamon74  · 技术社区  · 15 年前

    我想避免(大多数)NetBeans 6.9.1的警告,我对 'Leaking this in constructor' 警告。

    我理解这个问题,在构造函数中调用一个方法并传递“ this 很危险,因为 “可能尚未完全初始化。

    在我的singleton类中很容易修复警告,因为构造函数是私有的,并且只从同一个类调用。

    旧代码(简化):

    private Singleton() {
      ...
      addWindowFocusListener(this);
    }
    
    public static Singleton getInstance() {
    
      ...
      instance = new Singleton();
      ...
    }
    

    新代码(简化):

    private Singleton() {
      ...
    }
    
    public static Singleton getInstance() {
    
      ...
      instance = new Singleton();
      addWindowFocusListener( instance );
      ...
    }
    

    public class MyClass {
    
      ...
      List<MyClass> instances = new ArrayList<MyClass>();
      ...
    
      public MyClass() {
        ...
        instances.add(this);
      }
    
    }
    

    当然,我想要一个不需要使用这个类修改所有代码的修复(例如,通过调用init方法)。

    10 回复  |  直到 9 年前
        1
  •  43
  •   chiccodoro    11 年前

    因为你一定要把你的 instances.add(this) 在构造函数的末尾,您 我应该安全地告诉编译器简单地取消警告吗? (*) . 从本质上讲,警告并不一定意味着有什么问题,它只是需要你的注意。

    如果你知道你在做什么,你可以用 @SuppressWarnings 注释。正如Terrel在他的评论中提到的,以下注释是从NetBeans 6.9.1开始的:

    @SuppressWarnings("LeakingThisInConstructor")
    

    (*)更新: 正如Isthar和Sergey指出的那样,在某些情况下,“泄漏”的构造函数代码看起来非常安全(正如您所问的那样),但事实并非如此。有更多的读者可以批准这个吗?出于上述原因,我正在考虑删除此答案。

        2
  •  35
  •   chiccodoro    12 年前

    [奇科多罗的评论:泄漏原因/时间的解释 this 可能导致问题,即使泄漏语句放在构造函数的最后:]

    最终字段语义不同于“普通”字段语义。一个例子,

    我们玩网络游戏。让我们让一个游戏对象从网络中检索数据,让一个玩家对象监听游戏中的事件以进行相应的操作。游戏对象隐藏所有网络详细信息,玩家只对事件感兴趣:

    import java.util.*;
    import java.util.concurrent.Executors;
    
    public class FinalSemantics {
    
        public interface Listener {
            public void someEvent();
        }
    
        public static class Player implements Listener {
            final String name;
    
            public Player(Game game) {
                name = "Player "+System.currentTimeMillis();
                game.addListener(this);//Warning leaking 'this'!
            }
    
            @Override
            public void someEvent() {
                System.out.println(name+" sees event!");
            }
        }
    
        public static class Game {
            private List<Listener> listeners;
    
            public Game() {
                listeners = new ArrayList<Listener>();
            }
    
            public void start() {
                Executors.newFixedThreadPool(1).execute(new Runnable(){
    
                    @Override
                    public void run() {
                        for(;;) {
                            try {
                                //Listen to game server over network
                                Thread.sleep(1000); //<- think blocking read
    
                                synchronized (Game.this) {
                                    for (Listener l : listeners) {
                                        l.someEvent();
                                    }
                                }
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                    }            
                });
            }
    
            public synchronized void addListener(Listener l) {
                listeners.add(l);
            }
        }
    
        public static void main(String[] args) throws InterruptedException {
            Game game = new Game();
            game.start();
            Thread.sleep(1000);
            //Someone joins the game
            new Player(game);
        }
    }
    //Code runs, won't terminate and will probably never show the flaw.
    

    似乎一切正常:对列表的访问已正确同步。这个例子的缺陷在于,这个例子会把玩家泄露给运行线程的游戏。

    决赛相当 scary :

    …编译器有很大的自由移动对最终字段的读取 跨越同步屏障

    这几乎破坏了所有正常的同步。但幸运的是

    只能看到对象引用的线程 之后 那个物体 完全地 初始化可以确保看到该对象的正确初始化值。 final 领域。

    在该示例中,构造函数将对象引用写入列表。(因此还没有完全初始化,因为构造函数没有完成。)在写入之后,构造函数仍然没有完成。它只需要从构造函数返回,但我们假设它还没有返回。现在,执行器可以完成它的工作并向所有侦听器广播事件,包括尚未初始化的播放器对象!玩家(姓名)的最后一个字段可能不会被写入,并将导致打印 null sees event! .

        3
  •  13
  •   Colin Hebert    15 年前

    您拥有的最佳选择:

    • 提取你的 WindowFocusListener 属于另一个类(也可以是内部类或匿名类)。最好的解决方案,这样每个类都有特定的用途。
    • 忽略警告消息。

    使用单一实例作为泄漏构造函数的变通方法并不是真正有效的。

        4
  •  12
  •   Nate W.    14 年前

    在这种情况下,创建类实例的工厂会很有帮助。如果工厂负责创建类的实例,那么您将拥有一个调用构造函数的集中位置,并且添加一个必需的 init() 方法。

    关于您的即时解决方案,我建议您移动任何泄漏的呼叫 this 到构造函数的最后一行,然后在“证明”这样做是安全的之后,用注释抑制它们。

    在Intellij IDEA中,可以在行的正上方使用以下注释来禁止显示此警告:
    //noinspection ThisEscapedInObjectConstruction

        5
  •  4
  •   Andrew    13 年前

    一个人可以写:

    addWindowFocusListener(Singleton.this);
    

    这将阻止NB显示警告。

        6
  •  2
  •   CoolBeans Jake    14 年前

    使用嵌套类(按照科林的建议)可能是您的最佳选择。这是伪代码:

    private Singleton() {
      ...
    }
    
    public static Singleton getInstance() {
    
      ...
      instance = new Singleton();
      addWindowFocusListener( new MyListener() );
      ...
    
      private class MyListener implements WindowFocusListener {
      ...
      }
    }
    
        7
  •  2
  •   Will    13 年前

    不需要单独的侦听器类。

    public class Singleton implements WindowFocusListener {
    
        private Singleton() {
          ...
        }    
    
        private void init() {
          addWindowFocusListener(this);
        }
    
        public static Singleton getInstance() {    
          ...
          if(instance != null) {
            instance = new Singleton();
            instance.init();
          }
          ...
        }
    }
    
        8
  •  1
  •   CAB    15 年前

    注释@SuppressWarnings(“LeakingThisInConstructor”)仅适用于类AN,而不适用于构造函数本身。

    溶出液我建议: create private method init()/*在这里使用这个*/并从构造函数调用它。Netbeans不会警告你。

        9
  •  0
  •   sage88    12 年前

    假设您最初有一个这样的类,它将自己用作ActionListener,因此您最终调用了生成警告的AddActionListener(this)。

    private class CloseWindow extends JFrame implements ActionListener {
        public CloseWindow(String e) {
            setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
            setLayout(new BorderLayout());
    
            JButton exitButton = new JButton("Close");
            exitButton.addActionListener(this);
            add(exitButton, BorderLayout.SOUTH);
        }
    
        @Override
        public void actionPerformed(ActionEvent e) {
            String actionCommand = e.getActionCommand();
    
            if(actionCommand.equals("Close")) {
                dispose();
            }
        }
    }
    

    正如@colin hebert所提到的,您可以将actionListener划分为自己的类。当然,这将需要对您要在其上调用.dispose()的JFrame的引用。如果您不希望填满变量名空间,并且希望能够对多个JFrame使用ActionListener,那么可以使用getSource()来检索按钮,然后通过一系列getParent()调用来检索扩展JFrame的类,然后调用getSuperClass来确保它是一个JFrame。

    private class CloseWindow extends JFrame {
        public CloseWindow(String e) {
            setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
            setLayout(new BorderLayout());
    
            JButton exitButton = new JButton("Close");
            exitButton.addActionListener(new ExitListener());
            add(exitButton, BorderLayout.SOUTH);
        }
    }
    
    private class ExitListener implements ActionListener {
        @Override
        public void actionPerformed(ActionEvent e) {
            String actionCommand = e.getActionCommand();
            JButton sourceButton = (JButton)e.getSource();
            Component frameCheck = sourceButton;
            int i = 0;            
            String frameTest = "null";
            Class<?> c;
            while(!frameTest.equals("javax.swing.JFrame")) {
                frameCheck = frameCheck.getParent();
                c = frameCheck.getClass();
                frameTest = c.getSuperclass().getName().toString();
            }
            JFrame frame = (JFrame)frameCheck;
    
            if(actionCommand.equals("Close")) {
                frame.dispose();
            }
        }
    }
    

    上述代码适用于扩展JFrame的类的任何级别上的任何子级按钮。显然,如果您的对象只是一个JFrame,那么只需要直接检查该类,而不是检查超级类。

    最终,使用这个方法,你会得到一个类似这样的引用:mainClass$closeWindow,它有超类jframe,然后你将这个引用强制转换为jframe并处理它。

        10
  •  0
  •   Community Mohan Dere    9 年前

    包装你的 this 在双括号中。如果Netbean在子语句中,则默认忽略一些错误。

      public MyClass() {
         ...
         instances.add((this));
      }
    

    https://stackoverflow.com/a/8357990

    推荐文章