栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 面试经验 > 面试问答

如何将侦听器添加到双向绑定的对象

面试问答 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

如何将侦听器添加到双向绑定的对象

您的绑定和

toBind
属性正在被垃圾回收。

托马斯·米库拉(Tomas Mikula)在他的博客上简要地描述了“过早的垃圾收集”问题。


首先,对于任何试图重现此问题的人来说,请速记一点。由于所描述的行为取决于发生的垃圾回收,因此它可能并不总是发生(取决于内存分配,所使用的GC实现以及其他因素)。如果添加行

root.setonMouseClicked(e -> System.gc());

对该

start()
方法进行操作,然后单击场景中的空白区域将请求进行垃圾回收,并且该问题将(至少更有可能)在此之后(如果尚未出现)显现出来。


问题是绑定使用

WeakListener
s侦听属性更改并将这些更改传播到绑定的属性。如果没有其他活动引用,弱侦听器的设计目的是不要阻止对其附加的属性进行垃圾回收。(其基本原理是避免在属性不再在范围内时强迫程序员强制清理绑定。)

在您的示例代码中,控制器及其属性

toBind
可以进行垃圾回收。

在之后

start()
方法完成,所有你都保证有被引用
Application
,当你调用创建的实例
launch()
中,
Stage
结果表明,和任何从这些引用。当然,这包括
Scene
(所引用的
Stage
),的
root
,的子代
root
,其子代等,这些的属性,以及这些属性中任何一个的(非弱)侦听器。

因此,

stage
对的引用是
scene
,对的引用是,
GridPane
这是其根,对的引用也对
textarea
。

在

textarea
有附加到它的侦听器的引用,但听者保持没有额外的引用。

(在代码的第二个版本中,

ChangeListener
附加到的非弱
textarea.textProperty()
引用有对的引用
toBind
。因此,在该版本中,
ChangeListener
禁止
toBind
将GC进行GC,然后您可以在其上看到侦听器的输出。)

加载FXML时,将

FXMLLoader
创建控制器实例。虽然该控制器实例具有对string属性和文本区域的引用,但事实并非如此。因此,一旦加载完成,就没有对控制器的实时引用,并且可以
StringProperty
对其进行定义的垃圾回收。文本区域
textProperty()
仅对上的侦听器具有
弱引用
toBind
,因此文本区域无法防止
toBind
被垃圾回收。

在大多数实际情况下,这将不是问题。

StringProperty
除非您打算在某处使用它,否则您不太可能创建此附加对象。因此,如果您添加以“自然”方式使用此代码的任何代码,则很可能会看到问题消失。

因此,例如,假设您添加了一个标签:

<Label fx:id="label" GridPane.rowIndex="1"/>

并将其文本绑定到属性:

  public void initialize() {    textarea.textProperty().bindBidirectional(toBind);    textarea.textProperty().addListener((observable, oldValue, newValue) -> {      System.out.print("textarea: ");      System.out.println(newValue);    });    toBind.addListener((observable, oldValue, newValue) -> {      System.out.print("toBind: ");      System.out.println(newValue);    });    label.textProperty().bind(toBind);  }

然后场景中有对标签的引用,依此类推,因此不进行GC处理,并且标签

textProperty
通过绑定到来具有弱引用
toBind
。由于
label
不是GC,因此弱引用
toBind
可以在垃圾回收中幸存,并且不能进行GC,因此您将看到期望的输出。

或者,如果您在

toBind
其他地方(例如在
Application
实例中)引用该属性,则:

public class Controller {  @FXML  textarea textarea;  private StringProperty toBind = new SimpleStringProperty();  public void initialize() {    textarea.textProperty().bindBidirectional(toBind);    textarea.textProperty().addListener((observable, oldValue, newValue) -> {      System.out.print("textarea: ");      System.out.println(newValue);    });    toBind.addListener((observable, oldValue, newValue) -> {      System.out.print("toBind: ");      System.out.println(newValue);    });  }  public StringProperty boundProperty() {      return toBind ;  }}

然后

package sample;import javafx.application.Application;import javafx.beans.property.StringProperty;import javafx.fxml.FXMLLoader;import javafx.scene.Parent;import javafx.scene.Scene;import javafx.stage.Stage;public class Main extends Application {    private StringProperty boundProperty ;    @Override    public void start(Stage primaryStage) throws Exception{        FXMLLoader loader = new FXMLLoader(getClass().getResource("sample.fxml"));        Parent root = loader.load();        Controller controller = loader.getController();        boundProperty = controller.boundProperty();        root.setonMouseClicked(e -> System.gc());        primaryStage.setScene(new Scene(root, 400, 300));        primaryStage.show();    }    public static void main(String[] args) {        launch(args);    }}

您会再次看到预期的行为(即使在垃圾回收之后)。

最后(最后一点很微妙),如果您用

textarea.textProperty()
匿名内部类替换侦听器:

textarea.textProperty().addListener(new ChangeListener<String>() {  @Override  public void changed(Observablevalue<? extends String> observable, String oldValue, String newValue) {    System.out.print("textarea: ");    System.out.println(newValue);  }});

那么这也会阻止的GC

toBind
。原因是匿名内部类的实例包含对封闭实例的隐式引用(在这种情况下,即控制器的实例):此处控制器保留对的引用
toBind
。相比之下,Lambda表达式则不这样做。



转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/464354.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号