提问人:Nur1 提问时间:6/14/2022 最后编辑:Andrew ThompsonNur1 更新时间:6/26/2022 访问量:283
Java FlatLaf - 在 UI 可见时在另一个线程上序列化 TableModel 时出现 NullPointerException
Java FlatLaf - NullPointerException when serializing TableModel on another Thread while UI is visible
问:
当关联的 JTable 对象在 JFrame 中可见时,尝试序列化 TableModel 对象时,会引发 NullPointerException。我创建了一个最小的、可重现的示例,并且还将包括 Stack-Trace。
这是 NullPointerException 原因:
Cannot invoke "javax.swing.CellRendererPane.paintComponent(java.awt.Graphics, java.awt.Component, java.awt.Container, int, int, int, int, boolean)" because "this.rendererPane" is null
我希望有人能帮忙。也许下面的堆栈跟踪有助于更好地理解它。
我正在使用 AdoptOpenJDK14 (14.0.2.12-hotspot)
下面是最小的、可重现的示例:
序列化 .java(程序的入口点)
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
import javax.swing.table.AbstractTableModel;
import com.formdev.flatlaf.FlatDarkLaf;
public class Serialization {
enum SERIALIZATION_THREAD {
MAIN,
THREAD,
EDT
}
public static void main(String[] args) throws Exception {
// Create GUI with frame and model
UI ui = new UI();
// Serialize the table model
System.out.println("SERIALIZING ON MAIN THREAD");
sleep(1000);
serialize(ui.model, SERIALIZATION_THREAD.MAIN); // DOESN'T WORK
System.out.println("SERIALIZING ON SEPARATE THREAD");
sleep(1000);
serialize(ui.model, SERIALIZATION_THREAD.THREAD); // DOESN'T WORK
System.out.println("SERIALIZING ON EDT");
sleep(1000);
serialize(ui.model, SERIALIZATION_THREAD.EDT); // WORKS
}
public static void serialize(Serializable object, SERIALIZATION_THREAD thread) {
switch (thread) {
case MAIN:
serialize0(object);
break;
case THREAD:
new Thread(() -> serialize0(object)).start();
break;
case EDT:
SwingUtilities.invokeLater(() -> serialize0(object));
break;
default:
break;
}
}
public static void serialize0(Serializable object) {
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("some.dat"))) {
oos.writeObject(object);
} catch (IOException e) {
e.printStackTrace();
}
}
public static void sleep(int ms){
try {
Thread.sleep(ms);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class UI {
JFrame frame;
MyAbstractModel model;
public UI() throws InvocationTargetException, InterruptedException {
SwingUtilities.invokeAndWait(() -> {
FlatDarkLaf.setup(); // When using FlatLaf, it causes a NullPointer when serializing.
model = new MyAbstractModel();
frame = new JFrame();
frame.add(new JTable(model));
frame.setSize(300, 300);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setVisible(true);
System.out.println("Done Building GUI");
});
}
}
class MyAbstractModel extends AbstractTableModel {
private List<String> list = new ArrayList<>();
public MyAbstractModel() {
list = Collections.synchronizedList(new ArrayList<>());
list.add("A");
list.add("B");
list.add("C");
}
@Override
public int getRowCount() {
return list.size();
}
@Override
public int getColumnCount() {
return 1;
}
@Override
public Object getValueAt(int rowIndex, int columnIndex) {
return rowIndex + "-" + columnIndex;
}
@Override
public String getColumnName(int column) {
return "Column " + column;
}
@Override
public Class<?> getColumnClass(int columnIndex) {
return String.class;
}
@Override
public boolean isCellEditable(int rowIndex, int columnIndex) {
return false;
}
@Override
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
// TODO
}
}
这是我得到的 Stacktrace。
线程“AWT-EventQueue-0”java.lang.NullPointerException 中的异常:无法调用“javax.swing.CellRendererPane.paintComponent(java.awt.Graphics, java.awt.Component, java.awt.Container, int, int, int, int, boolean)”,因为“this.rendererPane”为空 在 java.desktop/javax.swing.plaf.basic.BasicTableUI.paintCell(BasicTableUI.java:2191) 在 java.desktop/javax.swing.plaf.basic.BasicTableUI.paintCells(BasicTableUI.java:2092) 在java.desktop/javax.swing.plaf.basic.BasicTableUI.paint(BasicTableUI.java:1888) 在 com.formdev.flatlaf.ui.FlatTableUI.paint(FlatTableUI.java:397) 在 java.desktop/javax.swing.plaf.ComponentUI.update(ComponentUI.java:161) 在 java.desktop/javax.swing.JComponent.paintComponent(JComponent.java:797) 在 java.desktop/javax.swing.JComponent.paint(JComponent.java:1074) 在 java.desktop/javax.swing.JComponent.paintToOffscreen(JComponent.java:5255) 在 java.desktop/javax.swing.RepaintManager$PaintManager.paintDoubleBufferedImpl(RepaintManager.java:1643) 在 java.desktop/javax.swing.RepaintManager$PaintManager.paintDoubleBuffered(RepaintManager.java:1618) 在java.desktop/javax.swing.RepaintManager$PaintManager.paint(RepaintManager.java:1556) 在java.desktop/javax.swing.RepaintManager.paint(RepaintManager.java:1323) 在 java.desktop/javax.swing.JComponent._paintImmediately(JComponent.java:5203) 在 java.desktop/javax.swing.JComponent.paintImmediately(JComponent.java:5013) 在 java.desktop/javax.swing.RepaintManager$4.run(RepaintManager.java:865) 在 java.desktop/javax.swing.RepaintManager$4.run(RepaintManager.java:848) 在 java.base/java.security.AccessController.doPrivileged(AccessController.java:391) 在 java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:85) 在 java.desktop/javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:848) 在 java.desktop/javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:823) 在 java.desktop/javax.swing.RepaintManager.prePaintDirtyRegions(RepaintManager.java:772) 在java.desktop/javax.swing.RepaintManager$ProcessingRunnable.run(RepaintManager.java:1884) 在 java.desktop/java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:316) 在 java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:770) 在 java.desktop/java.awt.EventQueue$4.run(EventQueue.java:721) 在 java.desktop/java.awt.EventQueue$4.run(EventQueue.java:715) 在 java.base/java.security.AccessController.doPrivileged(AccessController.java:391) 在 java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:85) 在 java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:740) 在 java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203) 在 java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124) 在 java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113) 在 java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109) 在 java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101) 在 java.desktop/java.awt.EventDispatchThread.run(EventDispatchThread.java:90) 线程“AWT-EventQueue-0”java.lang.NullPointerException中的异常:无法调用“javax.swing.JTable.getColumnModel()” 因为“this.table”为 null 在 java.desktop/javax.swing.plaf.basic.BasicTableUI.getPreferredSize(BasicTableUI.java:1768) 在 java.desktop/javax.swing.JComponent.getPreferredSize(JComponent.java:1680) 在 java.desktop/javax.swing.JTable.setWidthsFromPreferredWidths(JTable.java:3205) 在 java.desktop/javax.swing.JTable.doLayout(JTable.java:3117) 在 java.desktop/java.awt.Container.validateTree(Container.java:1722) 在 java.desktop/java.awt.Container.validateTree(Container.java:1731) 在 java.desktop/java.awt.Container.validateTree(Container.java:1731) 在 java.desktop/java.awt.Container.validateTree(Container.java:1731) 在 java.desktop/java.awt.Container.validate(Container.java:1657) 在 java.desktop/javax.swing.RepaintManager$3.run(RepaintManager.java:745) 在 java.desktop/javax.swing.RepaintManager$3.run(RepaintManager.java:743) 在 java.base/java.security.AccessController.doPrivileged(AccessController.java:391) 在 java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:85) 在 java.desktop/javax.swing.RepaintManager.validateInvalidComponents(RepaintManager.java:742) 在 java.desktop/javax.swing.RepaintManager$ProcessingRunnable.run(RepaintManager.java:1883) 在 java.desktop/java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:316) 在 java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:770) 在 java.desktop/java.awt.EventQueue$4.run(EventQueue.java:721) 在 java.desktop/java.awt.EventQueue$4.run(EventQueue.java:715) 在 java.base/java.security.AccessController.doPrivileged(AccessController.java:391) 在 java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:85) 在 java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:740) 在 java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203) 在 java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124) 在 java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113) 在 java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101) 在 java.desktop/java.awt.EventDispatchThread.run(EventDispatchThread.java:90) 做
答:
@camickr是对的。事实上,如果你像这样启动你的 gui,并在 EDT 上创建 gui 组件,你的问题就会消失。记下模式并始终使用它。我不认为问题与序列化有任何关系,但在您恢复我删除的序列化代码后,请随时告诉我其他情况。
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.swing.table.AbstractTableModel;
import java.io.ObjectOutputStream;
import javax.swing.JFrame;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
import com.formdev.flatlaf.FlatDarkLaf;
public class Serialization extends JFrame {
public void setGui() {
FlatDarkLaf.setup(); // When using FlatLaf, it causes a NullPointer when serializing.
MyAbstractModel model = new MyAbstractModel();
JTable table = new JTable(model);
getContentPane().add(table);
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
}
public static void main(String[] args) {
try {
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
Serialization f = new Serialization();
f.setGui();
f.setSize(200, 200);
f.setVisible(true);
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
}
class MyAbstractModel extends AbstractTableModel {
private List<String> list = new ArrayList<>();
public MyAbstractModel() {
list = Collections.synchronizedList(new ArrayList<>());
list.add("A");
list.add("B");
list.add("C");
}
@Override
public int getRowCount() {
return list.size();
}
@Override
public int getColumnCount() {
return 1;
}
@Override
public Object getValueAt(int rowIndex, int columnIndex) {
return rowIndex + "-" + columnIndex;
}
@Override
public String getColumnName(int column) {
return "Column " + column;
}
@Override
public Class<?> getColumnClass(int columnIndex) {
return String.class;
}
@Override
public boolean isCellEditable(int rowIndex, int columnIndex) {
return false;
}
@Override
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
// TODO
}
}
根据您的改进,我冒昧地更改为以下内容:main
public static void main(String[] args) throws Exception {
// Create GUI with frame and model
UI ui = new UI();
for (int i = 0; i < args.length; i++) {
SERIALIZATION_THREAD t = SERIALIZATION_THREAD.valueOf(args[i]);
// Serialize the table model
System.out.printf("Serializing on thread %s%n", args[i]);
serialize(ui.model, t);
sleep(1000);
}
}
并发现,与我自己的调查相对应,你唯一可以通过而不会失败的论点是 EDT。似乎对“null”中表的内部引用为空。不知道为什么,但看起来涉及线程。BasicTableUI
评论