Java 向对象随机传递空引用
我创建了一个程序,它应该显示一艘宇宙飞船(基本上是两个矩形),并在每次按下空格键时从飞船中射出一颗子弹。但它不起作用,因为很明显,我的程序正在将nullShip引用传递到我的另一个类中,MoveAction类。我完全不知道为什么会这样,因为在我的Main类中,我在shot类之前实例化了我的Ship对象(调用使用Ship对象的构造函数)。如果这个问题有点傻,我很抱歉,因为我还是一个新手程序员,大部分时间都不知道自己在做什么:)下面是我的代码:Java 向对象随机传递空引用,java,oop,user-interface,nullpointerexception,calling-convention,Java,Oop,User Interface,Nullpointerexception,Calling Convention,我创建了一个程序,它应该显示一艘宇宙飞船(基本上是两个矩形),并在每次按下空格键时从飞船中射出一颗子弹。但它不起作用,因为很明显,我的程序正在将nullShip引用传递到我的另一个类中,MoveAction类。我完全不知道为什么会这样,因为在我的Main类中,我在shot类之前实例化了我的Ship对象(调用使用Ship对象的构造函数)。如果这个问题有点傻,我很抱歉,因为我还是一个新手程序员,大部分时间都不知道自己在做什么:)下面是我的代码: public enum Direction {
public enum Direction {
LEFT, RIGHT, SPACE
}
import javax.swing.JFrame;
public class Main {
public static void main(String[] args) {
JFrame frame;
Ship s1;
Shoot shoot;
// Set the frame up
frame = new JFrame();
frame.setSize(400, 300);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
frame.setVisible(true);
// Get some more necessary objects
s1 = new Ship();
shoot = new Shoot(s1);
frame.getContentPane().add(shoot);
s1.setShoot(shoot);
// Threads
Thread ship = new Thread(s1);
ship.start();
}
}
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
public class Shoot extends JPanel {
Ship s1;
public Shoot(Ship s1) {
this.s1 = s1;
addKeyBinding(KeyEvent.VK_LEFT, "left.pressed", new MoveAction(true, s1, Direction.LEFT), true);
addKeyBinding(KeyEvent.VK_LEFT, "left.released", new MoveAction(false, s1, Direction.LEFT), false);
addKeyBinding(KeyEvent.VK_RIGHT, "right.pressed", new MoveAction(true, s1, Direction.RIGHT), true);
addKeyBinding(KeyEvent.VK_RIGHT, "right.released", new MoveAction(false, s1, Direction.RIGHT), false);
addKeyBinding(KeyEvent.VK_SPACE, "space.pressed", new MoveAction(true, s1, Direction.SPACE), true);
addKeyBinding(KeyEvent.VK_SPACE, "space.released", new MoveAction(false, s1, Direction.SPACE), false);
setDoubleBuffered(true);
}
@Override
public void paintComponent(Graphics g) {
// Draw the ship
super.paintComponent(g);
s1.draw(g);
g.fill3DRect(40, 50, 10, 10, false);
}
protected void addKeyBinding(int keyCode, String name, Action action, boolean keyPressed) {
if (keyPressed) {
addKeyBinding(KeyStroke.getKeyStroke(keyCode, 0, false), name, action);
} else {
addKeyBinding(KeyStroke.getKeyStroke(keyCode, 0, true), name, action);
}
}
protected void addKeyBinding(KeyStroke keyStroke, String name, Action action) {
InputMap inputMap = getInputMap(WHEN_IN_FOCUSED_WINDOW);
ActionMap actionMap = getActionMap();
inputMap.put(keyStroke, name);
actionMap.put(name, action);
}
}
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;
public class Ship implements Runnable {
int x, y, xDirection, bx, by;
boolean readyToFire, shooting = false;
Rectangle bullet;
Shoot shoot;
public Ship() {
x = 175;
y = 275;
bullet = new Rectangle(0, 0, 3, 5);
}
public void draw(Graphics g) {
// System.out.println("draw() called");
g.setColor(Color.BLUE);
g.fillRect(x, y, 40, 10);
g.fillRect(x + 18, y - 7, 4, 7);
if (shooting) {
g.setColor(Color.RED);
g.fillRect(bullet.x, bullet.y, bullet.width, bullet.height);
}
shoot.repaint();
}
public void move() {
x += xDirection;
if (x <= 0)
x = 0;
if (x >= 360)
x = 360;
shoot.repaint();
}
public void shoot() {
if (shooting) {
bullet.y--;
shoot.repaint();
}
}
public void setXDirection(int xdir) {
xDirection = xdir;
}
public void setShoot(Shoot shoot) {
this.shoot = shoot;
}
@Override
public void run() {
try {
while (true) {
shoot();
move();
Thread.sleep(5);
}
} catch (Exception e) {
System.err.println(e.getMessage());
}
}
}
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.util.HashSet;
import javax.swing.AbstractAction;
public class MoveAction extends AbstractAction {
boolean pressed;
Ship s1;
Direction dir;
private HashSet<Direction> movement;
public MoveAction(boolean pressed, Ship s1, Direction dir) {
System.out.println("moveaction class");
this.pressed = pressed;
this.s1 = s1;
this.dir = dir;
}
@Override
public void actionPerformed(ActionEvent e) {
try {
if (movement.contains(Direction.LEFT)) {
if (pressed) {
s1.setXDirection(-1);
} else {
s1.setXDirection(0);
}
} else if (movement.contains(Direction.RIGHT)) {
if (pressed) {
s1.setXDirection(1);
} else {
s1.setXDirection(0);
}
} else if (movement.contains(Direction.SPACE)) {
if (pressed) {
if (s1.bullet == null)
s1.readyToFire = true;
if (s1.readyToFire) {
s1.bullet.x = s1.x + 18;
s1.bullet.y = s1.y - 7;
s1.shooting = true;
}
} else {
s1.readyToFire = false;
if (s1.bullet.y <= -7) {
s1.bullet = null;
s1.shooting = false;
s1.bullet = null;
s1.bullet = new Rectangle(0, 0, 0, 0);
s1.readyToFire = true;
}
}
}
} catch (NullPointerException ex) {
System.out.println("NullPointerException");
}
}
内部您的
公共拍摄(飞船s1)
构造函数您实例化了新动作动作(true,s1,Direction.LEFT),true)
,使用s1
,这也是字段的名称
问题是,在构造函数完成运行之前,不允许引用字段。在这种情况下,未定义的行为。
可能您的编译器认为您引用的s1
就是字段。我的编译器不是这样,但我不知道您使用的是哪种编译器
在任何情况下,尝试为构造函数的参数和字段-
s1
赋予不同的名称,并将参数传递给MoveAction(…)
构造函数您从不初始化成员变量HashSet movement
,这就是您总是获得NPE的原因
试试这个
private Set<Direction> movement = new HashSet<>();
private Set movement=new HashSet();
顺便说一下,将实现类
HashSet
声明为变量类型是一种糟糕的做法。您应该使用接口集。“寻求调试帮助的问题(“为什么此代码不起作用?”)必须包括所需的行为、特定的问题或错误以及在问题本身中重现这些问题所需的最短代码。没有明确问题说明的问题对其他读者没有用处。”,给定标记:堆栈跟踪的可能副本也很好:)我不知道nullpointer发生在哪里我已经将代码复制到本地项目来测试它。窗口打开了,但没有显示任何内容。除此之外,我没有任何NPE。正如您在问题中所述,您在Main中实例化了Ship:s1=newship()代码>;将其传递给shot实例:shot=newshot(s1)代码>;并在shot构造函数中使用它:this.s1=s1。。。新移动动作(真、s1、方向.左)…
。因此,正如其他人所说,请添加异常的堆栈跟踪,以便我们有更多的工作要做。我不知道您使用哪个IDE,但在Eclipse中我没有NRE,您的代码对我来说似乎很好。。。我也在使用Eclipse,但我不确定我使用的是什么编译器……这不取决于编译器。变量的范围在语言规范中定义,在本上下文中始终是参数s1
。此外,没有任何规定在构造函数完成之前不允许使用字段。如果字段已正确初始化,则没有理由不使用它。所以你的答案在这两个方面都是错误的。也许你在编译器作用域的第一件事情上是正确的,正如我所说的,在我的情况下,这应该是可行的。但是:除此之外,这里提供的程序是正确的,应该可以工作。第二个——在构造函数完成运行之前,实例的状态是未定义的,因此不能对字段进行任何假设。程序可能是正确的,但您的答案是错误的。OP需要根据人们在评论中提出的问题提供更多信息,然后我们才能理解NPE的目的。我的回答包含两个陈述和一个一般性建议。第一条语句(可能)是错误的,但第二条语句和建议(既然是建议,就不可能是错误的)都是正确的。语句1:编译器可能将s1
解释为this.s1
。不对。语句2:在构造函数完成运行之前,不允许引用字段。不对。建议:替换变量名。但这个变量与问题无关。因此无法解决OP的问题,因此该建议毫无帮助且令人困惑。我添加了它,它不再给我NPE,但我的屏幕仍然空白。您询问了空指针,此问题已得到回答。“为什么屏幕是空白的”这个问题太宽泛了。尝试调试或跟踪您的应用程序,并更接近根本原因。然后,如果你还有问题,问一个更有针对性的问题。除了你自己,没有人知道你想要实现什么逻辑。在这个阶段试图帮助您是很困难的,而且会适得其反。好吧,无论如何,谢谢你,但是你能解释一下为什么将HashSet声明为变量是不好的吗?在使用你的建议时,它似乎更有效(但不是完全有效)。下面是一个很好的答案:
private Set<Direction> movement = new HashSet<>();