You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java Graphic2D绘图按钮触发NullPointerException问题求助

解决你的Swing绘图NullPointerException问题

嘿,我看到你作为Java新手在使用Graphics2D绘图时遇到了NullPointerException,咱们来一步步搞定它!

首先看错误栈,异常出现在actionPerformed方法调用drawshape之后,根本原因很简单:你声明的Graphics2D g2d变量从来没有被初始化过,它一直是null,当你在forward、left这些方法里调用g2d的方法时,自然就抛出空指针异常了。

而且你还踩了Swing绘图的一个常见坑:Swing的绘图是由系统管理的,我们不能直接持有一个Graphics对象来绘图,而是要通过重写JComponent的paintComponent方法,在系统传递过来的Graphics对象上进行绘制。

下面是修改后的完整代码,我标注了关键改动:

1. 自定义绘图面板类

首先创建一个专门负责绘图的面板,所有绘图逻辑都放在这里:

class DrawingPanel extends JPanel {
    // 存储绘图需要的参数
    private int x, y, len, dir;
    private double angle;
    private String shape;

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        // 从系统传递的Graphics对象转换为Graphics2D,这是正确的获取方式
        Graphics2D g2d = (Graphics2D) g.create();
        // 开启抗锯齿,让绘图更平滑
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

        // 定位到起始坐标
        moveTo(g2d, x, y);
        // 执行绘图逻辑
        drawshape(g2d, len, angle, shape);

        g2d.dispose(); // 用完释放资源,避免内存泄漏
    }

    // 绘图逻辑,现在接收Graphics2D参数
    private void drawshape(Graphics2D g2d, int len, double angle, String s) {
        for (int i = 0; i < s.length(); i++) {
            char z = s.charAt(i);
            if (z == 'F') {
                forward(g2d, len);
            } else if (z == '+') {
                right(g2d, angle);
            } else if (z == '-') {
                left(g2d, angle);
            } else if (z == 'r') {
                g2d.setColor(Color.red);
            } else if (z == 'b') {
                g2d.setColor(Color.blue);
            } else if (z == 'g') {
                g2d.setColor(Color.green);
            }
        }
    }

    private void left(Graphics2D g2d, double a) {
        g2d.rotate(-a * Math.PI / 180);
    }

    private void right(Graphics2D g2d, double a) {
        g2d.rotate(a * Math.PI / 180);
    }

    private void forward(Graphics2D g2d, int len) {
        g2d.drawLine(0, 0, len, 0);
        g2d.translate(len, 0);
    }

    private void moveTo(Graphics2D g2d, int x, int y) {
        g2d.translate(x, y);
    }

    // 设置绘图参数并触发重绘
    public void setDrawingParams(int x, int y, int len, int dir, double angle, String shape) {
        this.x = x;
        this.y = y;
        this.len = len;
        this.dir = dir;
        this.angle = angle;
        this.shape = shape;
        repaint(); // 通知系统重绘面板
    }
}

2. 修改主窗口类

然后更新你的Main类,使用上面的自定义面板来完成绘图:

package com.company;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class Main extends JFrame implements ActionListener {
    Container c;
    JLabel label_x, label_y, label_len, label_angle, label_direction, label_shape;
    JTextField text_x, text_y, text_len, text_angle, text_direction, text_shape;
    JButton draw;
    // 添加自定义绘图面板
    private DrawingPanel drawingPanel;

    public Main() {
        c = getContentPane();
        c.setLayout(null);

        // 初始化绘图面板,设置位置和大小
        drawingPanel = new DrawingPanel();
        drawingPanel.setBounds(20, 200, 550, 350);
        drawingPanel.setBackground(Color.white);
        c.add(drawingPanel);

        // 以下是原来的UI组件代码,保持不变
        label_x = new JLabel("x");
        label_x.setBounds(20, 20, 50, 30);
        text_x = new JTextField();
        text_x.setBounds(70, 20, 50, 30);

        label_len = new JLabel("Length");
        label_len.setBounds(130, 20, 50, 30);
        text_len = new JTextField();
        text_len.setBounds(180, 20, 50, 30);

        label_angle = new JLabel("Angle");
        label_angle.setBounds(240, 20, 50, 30);
        text_angle = new JTextField();
        text_angle.setBounds(290, 20, 50, 30);

        label_y = new JLabel("y");
        label_y.setBounds(20, 60, 50, 30);
        text_y = new JTextField();
        text_y.setBounds(70, 60, 50, 30);

        label_direction = new JLabel("Direction");
        label_direction.setBounds(130, 60, 70, 30);
        text_direction = new JTextField();
        text_direction.setBounds(200, 60, 50, 30);

        label_shape = new JLabel("Shape to draw");
        label_shape.setBounds(260, 60, 100, 30);
        text_shape = new JTextField();
        text_shape.setBounds(360, 60, 50, 30);

        draw = new JButton("Draw");
        draw.setBounds(200, 140, 100, 50);
        draw.addActionListener(this);

        // 添加所有UI组件
        c.add(label_x);
        c.add(label_y);
        c.add(text_x);
        c.add(text_y);
        c.add(label_angle);
        c.add(label_direction);
        c.add(label_len);
        c.add(label_shape);
        c.add(draw);
        c.add(text_angle);
        c.add(text_len);
        c.add(text_shape);
        c.add(text_direction);

        c.setBackground(Color.gray);
        setVisible(true);
        setSize(600, 600);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 添加关闭操作,防止程序后台运行
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        if (e.getSource() == draw) {
            try {
                // 获取输入参数
                int x = Integer.parseInt(text_x.getText());
                int y = Integer.parseInt(text_y.getText());
                int len = Integer.parseInt(text_len.getText());
                int dir = Integer.parseInt(text_direction.getText());
                double angle = Double.parseDouble(text_angle.getText());
                String shape = text_shape.getText();

                // 传递参数给绘图面板并触发重绘
                drawingPanel.setDrawingParams(x, y, len, dir, angle, shape);
            } catch (NumberFormatException ex) {
                // 处理用户输入非数字的情况,给友好提示
                JOptionPane.showMessageDialog(this, "请输入有效的数字!", "输入错误", JOptionPane.ERROR_MESSAGE);
            }
        }
    }

    public static void main(String[] args) {
        // 在事件调度线程中启动Swing程序,这是规范做法
        SwingUtilities.invokeLater(Main::new);
    }
}

关键改动说明

  • 移除了原来未初始化的Graphics2D g2d成员变量,改为在paintComponent方法中获取系统提供的Graphics对象。
  • 创建DrawingPanel类专门负责绘图,符合单一职责原则,代码更清晰。
  • 点击Draw按钮时,不再直接调用绘图方法,而是传递参数给面板并调用repaint()触发系统重绘。
  • 添加了输入异常捕获,处理用户输入非数字的情况,提升程序健壮性。
  • 使用SwingUtilities.invokeLater启动程序,确保UI组件在事件调度线程中创建,符合Swing规范。

额外小提示

  • 尽量避免使用null布局(setLayout(null)),推荐使用布局管理器(比如BorderLayout、FlowLayout),这样界面在窗口大小变化时能自动调整。
  • 绘图完成后记得调用g2d.dispose()释放资源,避免内存泄漏。

内容的提问来源于stack exchange,提问作者aya

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.07 16:12:42