1

I wish to serialize a class which displays a JFrame with a JScrollBar. The program saves and loads fine except once the JScrollBar becomes visible, at which point I get the following exception:

Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException: Cannot invoke "javax.swing.JScrollPane.getViewportBorder()" because "this.scrollpane" is null

When I get this exception, the program doesn't load properly afterward.

I've reduced my code as much as possible to isolate the problem and now I'm stuck. All I know is that if I declare the JFrame inside the run() method, I have to replace backgroundPanel.repaint() with frame.setVisible(true) and everything works fine. But I need the JFrame accessible outside of the run() method in the full program. Only other thing I've found is that the JScrollBar's getViewportBorder() method returns null even before the JScrollBar itself becomes visible, though the serialization only fails once the bar becomes visible.

Here is the problematic code:

import java.io.*;
import java.lang.Runnable;
import javax.swing.*;

class GameLauncher implements Runnable, Serializable {

    public static void main(String[] args) {
        new Thread(new GameLauncher()).start();
    }

    JFrame frame;
    public void run() {
        frame = new JFrame("Frame");
        JPanel backgroundPanel = new JPanel();
        JScrollPane scrollPane = new JScrollPane();
        scrollPane.setViewportView(backgroundPanel);
        frame.add(scrollPane);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(100, 100);
        frame.setVisible(true);
        for (int i = 0; i < 3; i++) {
            backgroundPanel.add(new JLabel("Text"));
            backgroundPanel.repaint();
            saveFile();
        }
    }

    private void saveFile() {
        try {
            ObjectOutputStream objectOut = new ObjectOutputStream(new FileOutputStream(new File("save.ser")));
            objectOut.writeObject(this);
            objectOut.close();
            System.out.println("File saved");
        }
        catch(IOException ex) {
            System.out.println("File not saved");
            ex.printStackTrace();
        }
    }
}

edit: I realise I was unclear in my question. I get the error when saving the file, not when loading it. It just fails to load properly if the error occurs during saving.

  • 1} A Swing GUI must be started on the [Event Dispatch Thread](https://docs.oracle.com/javase/tutorial/uiswing/concurrency/dispatch.html) with a call to the SwingUtilities invokeLater method. 2) You may have to serialize each Swing component individually, so you can restore the GUI in the correct order. 3) Don't forget the serialVersionUID for the class. 4) You've shown no restore code, which is where you're getting the abend. – Gilbert Le Blanc Apr 08 '21 at 12:47
  • 1
    ***Why*** is the code trying to serialize windows, containers & components rather than the *state* of those components? E.G. see [this example](https://stackoverflow.com/a/7778332/418556) serializes the size & location of a `JFrame`. – Andrew Thompson Apr 08 '21 at 12:52
  • 1
    @GilbertLeBlanc Today I learned about the Event Dispatch Thread! Thank for you pointing me in that direction! If I'm going to have to serialize each Swing component one by one, I might just try to save their state instead and make a fresh GUI each time the program loads. In response to points 3 & 4, I left the serialVersionUID off because it wasn't essential to this example, same for the restore code because the error happens on saving, not loading. Incomplete loading is just the symptom. – Jeremy Price Apr 10 '21 at 15:04
  • 1
    @AndrewThompson Thanks for linking the in-depth example, it was very informative and I will try rebuilding this program so no actual Swing components need to get serialized! – Jeremy Price Apr 10 '21 at 15:05

0 Answers0