在看韩顺平的java基础视频,坦克大战中在保存记录的时候出现空指针的错误(运行时)现在编不下去了

package test6;

import java.io.*;
import java.util.*;

class Recorder
{
private static int enNum=20;
private static int myLife=3;
private static int allShot=0;
static FileWriter fw=null;
static BufferedWriter bw=null;
static FileReader fr=null;
static BufferedReader br=null;

private  static Vector<Enemy> ets=new  Vector<Enemy>();
//保存敌人的坐标,方向 数量
public static void keepRecAndEnemy()
{
    try {
        fw=new FileWriter("d:\\javatank.txt");
        bw=new BufferedWriter(fw);
        bw.write(allShot+"\r\n");
        for(int i=0;i<ets.size();i++)
        {
            Enemy et=ets.get(i);
            if(et.isLive)
            {
                String record=et.x+" "+et.y+" "+et.direct;
                bw.write(record+"\r\n");
            }
        }
    } catch (IOException e) {
        e.printStackTrace();
    }finally{
        try {
            bw.close();
            fw.close();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

}
  //从文件中读取记录
public static void  getRecording()
{
    try {
        fr=new FileReader("d:\\javatank.txt");
        br=new BufferedReader(fr);
        String n=br.readLine();
        allShot=Integer.parseInt(n);

    } catch (Exception e) {
        // TODO 自动生成的 catch 块
        e.printStackTrace();
    }finally{
        try {
            br.close();
            fr.close();
        } catch (Exception e) {
            // TODO 自动生成的 catch 块
            e.printStackTrace();
        }

    }

}


//玩家击毁敌人坦克数量保存到文件中

public static void keepRecording()
{
    try {
        fw=new FileWriter("d:\\javatank.txt");
        bw=new BufferedWriter(fw);
        bw.write(allShot+"\r\n");
    } catch (IOException e) {
        // TODO 自动生成的 catch 块
        e.printStackTrace();
    }finally{
        try {
            bw.close();
            fw.close();
        } catch (IOException e) {
            // TODO 自动生成的 catch 块
            e.printStackTrace();
        }

    }

}

public static int getEnNum() {
    return enNum;
}
public static void setEnNum(int enNum) {
    Recorder.enNum = enNum;
}
public static int getMyLife() {
    return myLife;
}
public static void setMyLife(int myLife) {
    Recorder.myLife = myLife;
}
public static void  reduceEnemy()
{
    enNum--;
}
public static int getAllShot() {
    return allShot;
}
public static void setAllShot(int allShot) {
    Recorder.allShot = allShot;
}
public static void addallShot()
{
    allShot++;
}
public  static Vector<Enemy> getEts() {
    return ets;
}
public static void setEts(Vector<Enemy> ets1) {
    System.out.print("whita ");
    ets = ets1;
}
}

//炸弹类
class Bomb
{
int x,y;
int life=6;
boolean isLive=true;
public Bomb(int x,int y)
{
this.x=x;
this.y=y;

}
public void lifeDown()
{
    if(life>0)
    {
        life--;
    }else
    {
        this.isLive=false;
    }

}

}

//定义坦克的类
class Tank
{

int x=0;
int y=0;
int direct=0;
int color;
int speed=1;
boolean isLive=true;

public int getColor() {
    return color;
}
public void setColor(int color) {
    this.color = color;
}

public int getDirect() {
    return direct;
}
public void setDirect(int direct) {
    this.direct = direct;
}


public void moveUp()
{
    y=y-speed;
}
public void moveRight()
{
    x=x+speed;
}
public void moveDown()
{
    y=y+speed;
}
public void moveLeft()
{
    x=x-speed;
}

public int getX() {
    return x;
}
public void setX(int x) {
    this.x = x;
}
public int getY() {
    return y;
}
public void setY(int y) {
    this.y = y;
}

public Tank(int x,int y){
this.x=x;
this.y=y;
}

}

//定义我的坦克,继承坦克类
class Mytank extends Tank
{

Vector<Shot> ss=new  Vector<Shot>();
Shot s=null;
public Mytank(int x,int y)
{
    super(x, y);
    this.x=x;
    this.y=y;

}
public void shotEnemy()
{
    switch(this.direct)
    {
    case 0:
        s=new Shot(x+10,y,0);
        ss.add(s);
        break;
    case 1:
        s=new Shot(x+30,y+10,1);
        ss.add(s);
        break;
    case 2:
        s=new Shot(x+10,y+30,2);
        ss.add(s);
        break;
    case 3:
        s=new Shot(x,y+10,3);
        ss.add(s);
        break;
    }
    Thread t=new Thread(s);
    t.start();
}
}

//敌人的坦克

class Enemy extends Tank implements Runnable
{

int times=0;
Vector<Shot> ss=new Vector<Shot>();

//判断是否重叠
Vector<Enemy> ets=new Vector<Enemy>();
public void setEts(Vector<Enemy> ets)
{
    this.ets=ets;
}
//判断重叠的函数
public boolean isTouchOther()
{
    switch(this.direct)
    {
    case 0://我的(敌人)坦克方向
        for(int i=0;i<ets.size();i++)
        {
            Enemy et=ets.get(i);
            if(this!=et)
            {
                if(et.direct==0||et.direct==2)//敌人的坦克的方向
                {
                    if(this.x>et.x&&this.x<et.x+20&&this.y>et.y&&this.y<et.y+30)
                    {
                        return true;                
                    }
                    if(this.x+20>et.x&&this.x+20<et.x+20&&this.y>et.y&&this.y<et.y+30)
                    {
                        return true;
                    }
                }
                if(et.direct==1||et.direct==3)
                {
                    if(this.x>et.x&&this.x<et.x+30&&this.y>et.y&&this.y<et.y+20)
                    {
                        return true;                
                    }
                    if(this.x+20>et.x&&this.x+20<et.x+30&&this.y>et.y&&this.y<et.y+20)
                    {
                        return true;
                    }
                }
            }
        }
        break;
    case 1:
        for(int i=0;i<ets.size();i++)
        {
            Enemy et=ets.get(i);
            if(this!=et)
            {
                if(et.direct==0||et.direct==2)//敌人的坦克的方向
                {
                    if(this.x+30>et.x&&this.x+30<et.x+20&&this.y>et.y&&this.y<et.y+30)
                    {
                        return true;                
                    }
                    if(this.x+30>et.x&&this.x+30<et.x+20&&this.y+20>et.y&&this.y+20<et.y+30)
                    {
                        return true;
                    }
                }
                if(et.direct==1||et.direct==3)
                {
                    if(this.x+30>et.x&&this.x+30<et.x+20&&this.y>et.y&&this.y<et.y+20)
                    {
                        return true;                
                    }
                    if(this.x+30>et.x&&this.x+30<et.x+30&&this.y+20>et.y&&this.y+20<et.y+20)
                    {
                        return true;
                    }
                }
            }
        }
        break;
    case 2:
        for(int i=0;i<ets.size();i++)
        {
            Enemy et=ets.get(i);
            if(this!=et)
            {
                if(et.direct==0||et.direct==2)//敌人的坦克的方向
                {
                    if(this.x>et.x&&this.x<et.x+20&&this.y+30>et.y&&this.y+30<et.y+30)
                    {
                        return true;                
                    }
                    if(this.x+20>et.x&&this.x+20<et.x+20&&this.y+30>et.y&&this.y+30<et.y+30)
                    {
                        return true;
                    }
                }
                if(et.direct==1||et.direct==3)
                {
                    if(this.x>et.x&&this.x<et.x+30&&this.y+30>et.y&&this.y+30<et.y+20)
                    {
                        return true;                
                    } 
                    if(this.x+20>et.x&&this.x+20<et.x+30&&this.y+30>et.y&&this.y+30<et.y+20)
                    {
                        return true;
                    }
                }
            }
        }

        break;
    case 3:
        for(int i=0;i<ets.size();i++)
        {
            Enemy et=ets.get(i);
            if(this!=et)
            {
                if(et.direct==0||et.direct==2)//敌人的坦克的方向
                {
                    if(this.x>et.x&&this.x<et.x+20&&this.y>et.y&&this.y<et.y+30)
                    {
                        return true;                
                    }
                    if(this.x>et.x&&this.x<et.x+20&&this.y+20>et.y&&this.y+20<et.y+30)
                    {
                        return true;
                    }
                }
                if(et.direct==1||et.direct==3)
                {
                    if(this.x>et.x&&this.x<et.x+20&&this.y>et.y&&this.y<et.y+20)
                    {
                        return true;                
                    }
                    if(this.x>et.x&&this.x<et.x+30&&this.y+20>et.y&&this.y+20<et.y+20)
                    {
                        return true;
                    }
                }
            }
        }
        break;

    }
    return false;

}


public Enemy(int x,int y)
{
    super(x,y);
    this.x=x;
    this.y=y;

}
public void run() {
    while (true)
    {
        switch(direct)
        {
        case 0:
            for(int i=0;i<30;i++)
            {
                if(y>0&&!this.isTouchOther())
                {
                y-=speed;
                }
            try{
                Thread.sleep(50);

            }catch(Exception e)
            {
                e.printStackTrace();
            }
            }

            break;
        case 1:
            for(int i=0;i<30;i++)
            {
                if(x<400&&!this.isTouchOther()){
                x+=speed;
                }
            try{
                Thread.sleep(50);

            }catch(Exception e)
            {
                e.printStackTrace();
            }
            }

            break;
        case 2:
            for(int i=0;i<30;i++)
            {
                if(y<300&&!this.isTouchOther()){
                y+=speed;
                }
            try{
                Thread.sleep(50);

            }catch(Exception e)
            {
                e.printStackTrace();
            }
            }

            break;
        case 3:
            for(int i=0;i<30;i++)
            {
                if(x>0&&!this.isTouchOther())
                {
                x-=speed;
                }
            try{
                Thread.sleep(50);

            }catch(Exception e)
            {
                e.printStackTrace();
            }
            }

            break;
        }
        this.direct=(int)(Math.random()*4);
        if(this.isLive==false)
        {
            break;
        }
        this.times++;
        if(times%2==0)
        {

            if(isLive)
            {
                //判断是否需要加入子弹
                    if(ss.size()<5)
                    {
                        Shot s=null;
                        //没有子弹,,添加子弹
                        switch(direct)
                        {
                        case 0:
                            s=new Shot(x+10,y,0);
                            ss.add(s);
                            break;
                        case 1:
                            s=new Shot(x+30,y+10,1);
                            ss.add(s);
                            break;
                        case 2:
                            s=new Shot(x+10,y+30,2);
                            ss.add(s);
                            break;
                        case 3:
                            s=new Shot(x,y+10,3);
                            ss.add(s);
                            break;
                        }
                        Thread t=new Thread(s);
                        t.start();

                    }
            }
        }
        }
    }

}

//定义子弹类
class Shot implements Runnable
{
int x;
int y;
int direct;
int speed=1;
boolean isLive=true;

public Shot(int x,int y,int direct)
{
    this.x=x;
    this.y=y;
    this.direct=direct;
}

//子弹的 线程
public void run() {
    while(true)
    {
        try {
            Thread.sleep(50);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    switch (direct)
    {
    case 0:
        y=y-speed;
        break;
    case 1:
        x=x+speed;
        break;
    case 2:
        y=y+speed;
        break;
    case 3:
        x=x-speed;
        break;

    }
    if(x<0||x>400||y<0||y>300)
    {
        this.isLive=false;
        break;
    }

    }
}
}

上面是全部代码了,运行的时候按exit的时候就会报错, Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
里面是报这句有空指针的:Recorder.setEts(mp.ets);

代码要分批了,下面是成员的类:

package test6;

//自画坦克游戏 移动 敌人发弹 爆炸

//敌人坦克不能重叠
//1增加一个开始的面板
//2记录下敌人坦克的数量和玩家的成绩
//3退出,存盘,记录,继续上局,声音

import java.awt.*;
import java.io.*;
import java.util.*;
import java.awt.event.*;

import javax.imageio.ImageIO;
import javax.swing.*;

public class MyTankGame6 extends JFrame implements ActionListener{
StartPanel sp=null;
MyPanle mp=null;

JMenuBar jmb=null;
JMenu jm=null;
JMenuItem jmi1,jmi2,jmi3;

public static void main(String []args){
    MyTankGame6 mytankgame=new MyTankGame6();
    }
public MyTankGame6(){

    sp=new StartPanel();
    Thread t=new Thread(sp);
    t.start();
    this.add(sp);

    jmb=new JMenuBar();
    jm=new JMenu("Game(G)");//助记符
    jm.setMnemonic('g');//快捷方式
    jmi1=new JMenuItem("Begin");
    jmi1.addActionListener(this);
    jmi1.setActionCommand("newgame");
    jmi2=new JMenuItem("exit"); 
    jmi2.addActionListener(this);
    jmi2.setActionCommand("exit");
    jmi3=new JMenuItem("saveExit");
    jmi3.addActionListener(this);
    jmi3.setActionCommand("saveExit");
    jmb.add(jm);
    jm.add(jmi1);
    jm.add(jmi2);
    jm.add(jmi3);

    this.setJMenuBar(jmb);



    this.setSize(600,500);
    this.setVisible(true);
    this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public void actionPerformed(ActionEvent e) {
    if(e.getActionCommand().equals("newgame"))
    {
        MyPanle mp=new MyPanle();
        this.addKeyListener(mp);
        this.remove(sp);
        this.add(mp);
        Thread t=new Thread(mp);
        t.start();
        this.setVisible(true);
    }
    else if(e.getActionCommand().equals("exit"))
    {
        Recorder.keepRecording();
        System.exit(0);
    }
    else if(e.getActionCommand().equals("saveExit"))
    {
        //保存坦克的坐标,和数量
        System.out.print("tuichu ");

// Recorder rd=new Recorder();
Recorder.setEts(mp.ets);
Recorder.keepRecording();

        System.exit(0);//退出
    }
}

}

class StartPanel extends JPanel implements Runnable
{
int times=0;
public void paint(Graphics g)
{
super.paint(g);
g.fillRect(0,0, 400, 300);
if(times%2==0)
{
g.setColor(Color.yellow);
Font f=new Font("华文新魏",Font.BOLD,20);
g.setFont(f);
g.drawString("stage1", 200,100);
}
}

public void run() {
    while(true)
    {
        try {
            Thread.sleep(500);

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        times++;        
        this.repaint();

    }
}
}

class MyPanle extends JPanel implements KeyListener ,Runnable
{
//定义坦克
Mytank mt=null;
Vector ets=new Vector();

  Vector<Bomb> bomb=new Vector<Bomb>();
  int enSize=3;
  //定义照片
  Image image1=null;
  Image image2=null;

  public void showInfo(Graphics g)
  {
      //画出作为提示信息的坦克
      this.DrawTank(10, 300, g, 0, 1);
      g.setColor(Color.black);
      g.drawString(Recorder.getMyLife()+"", 50, 320);
      this.DrawTank(10, 360, g, 0, 0);
      g.setColor(Color.black);
      g.drawString(Recorder.getEnNum()+"", 50, 380);

      //画出玩家的成绩
      g.setColor(Color.black);
      Font f=new Font("宋体",Font.BOLD,20);
      g.setFont(f);
      g.drawString("你的成绩为:", 420, 30);
      this.DrawTank(420, 50, g, 0, 0);
      g.drawString(Recorder.getAllShot()+"", 450, 80);
  }

  //创建坦克和子弹,画出来
  public void paint(Graphics g)
  {
      super.paint(g);
      g.fillRect(0, 0, 400, 300);
      this.showInfo(g);


      if(mt.isLive)
      {
          this.DrawTank(mt.getX(), mt.getY(), g, mt.direct, 0);
      }


      for(int i=0;i<ets.size();i++)
      {
          Enemy e=ets.get(i);
          if(e.isLive)
          {
          this.DrawTank(e.getX(), e.getY(), g, e.getDirect(), e.getColor());
          for(int j=0;j<e.ss.size();j++)
          {
              Shot es=e.ss.get(j);
              if(es.isLive)
              {
                  g.draw3DRect(es.x, es.y, 1, 1, false);
              }else
              {
                  e.ss.remove(es);
              }
          }
          }
      }

      for(int i=0;i<mt.ss.size();i++)
      {
          Shot myShot=mt.ss.get(i);
          if(myShot!=null&&myShot.isLive!=false )
          {
          g.fill3DRect(myShot.x, myShot.y, 1, 1, false);
          }
          if(myShot.isLive==false)
          {
              mt.ss.remove(myShot);

          }
      }
      //画炸弹
      for(int i=0;i<bomb.size();i++)
      {

          Bomb b=bomb.get(i);
          if(b.life>3)
          {
              g.drawImage(image1, b.x, b.y, 30, 30,this);
          }else {
              g.drawImage(image2, b.x, b.y, 30, 30,this);
          }
          b.lifeDown();
          if(b.life==0)
          {

              bomb.remove(b);
          }
      }


  }

  public MyPanle(){
      //坦克 子弹 炸弹 初始化
      Recorder.getRecording();
      mt=new Mytank(100, 100);

      for(int i=0;i<enSize;i++)
      {
          Enemy et=new Enemy((i+1)*50,0);
          et.setColor(1);
          et.setDirect(2);
          et.setEts(ets);
          Thread t1=new Thread(et);
          t1.start();
          Shot s=new Shot(et.x+10,et.y+30,2);
          et.ss.add(s);
          Thread t2=new Thread(s);
          t2.start();
          ets.add(et);
      }

      try {
         image1=ImageIO.read(new File("./bomb1.jpg"));
         image2=ImageIO.read(new File("./bomb2.jpg"));
    } catch (Exception e) {
        e.printStackTrace();
    }

// image1=Toolkit.getDefaultToolkit().getImage(Panel.class.getResource("/bomb1.jpg"));
// image2=Toolkit.getDefaultToolkit().getImage(Panel.class.getResource("/bomb2.jpg"));
//

}

  //画坦克的函数
  public void DrawTank(int x,int y,Graphics g,int direct,int type)
  {
      switch(type)
      {case 0:
          g.setColor(Color.red);
          break;
      case 1:
          g.setColor(Color.yellow);
          break;          
      }

      switch (direct)
      {
      //向上
      case 0:
          g.fill3DRect(x, y, 5, 30, false);
          g.fill3DRect(x+15, y, 5, 30, false);
          g.fill3DRect(x+5, y+5, 10, 20, false);
          g.fillOval(x+5,y+10, 10 ,10);
          g.drawLine(x+10, y+15, x+10,y);
          break;

          //向右
      case 1:
          g.fill3DRect(x, y, 30, 5, false);
          g.fill3DRect(x, y+15, 30, 5, false);
          g.fill3DRect(x+5, y+5, 20, 10, false);
          g.fillOval(x+10, y+5, 10, 10);
          g.drawLine(x+15, y+10, x+30, y+10);
          break;

          //向下
      case 2:
          g.fill3DRect(x, y, 5, 30, false);
          g.fill3DRect(x+15, y, 5, 30, false);
          g.fill3DRect(x+5, y+5, 10, 20, false);
          g.fillOval(x+5,y+10, 10 ,10);
          g.drawLine(x+10, y+15, x+10,y+30);
          break;

          //向左
      case 3:
          g.fill3DRect(x, y, 30, 5, false);
          g.fill3DRect(x, y+15, 30, 5, false);
          g.fill3DRect(x+5, y+5, 20, 10, false);
          g.fillOval(x+10, y+5, 10, 10);
          g.drawLine(x+15, y+10, x, y+10);
          break;
      }

  }
  //判断是否及击中我的坦克
  public void hitMe()
  {
      for(int i=0;i<ets.size();i++)
      {
          Enemy et=ets.get(i);
          for(int j=0;j<et.ss.size();j++)
          {
              Shot s=et.ss.get(j);
              if(mt.isLive)
              {
                 if(this.hit(s,mt))
                 {

                 }
              }
              }
      }

  }

  public void hitEnemyTank()
  {
    //判断是否打中 坦克,击中就 
    for(int i=0;i<mt.ss.size();i++)
    {
        Shot s=mt.ss.get(i);
        if(s.isLive)
        {
            for(int j=0;j<ets.size();j++)
            {
                Enemy e=ets.get(j);
                if(e.isLive)
                {
                    if(this.hit(s, e))
                    {
                     Recorder.reduceEnemy();
                     Recorder.addallShot();
                    }
                }
            }
        }
    }

  }
  //子弹打中坦克就消失
  public boolean hit(Shot s,Tank e)
  {
      boolean b2=false;
      switch (e.direct)
      {
      case 0:
      case 2:
          if(s.x>e.x&&s.x<e.x+20&&s.y>e.y&&s.y<e.y+30)
          {
              s.isLive =false;
              e.isLive=false;
              b2=true;
              Bomb b=new Bomb(e.x,e.y);
              bomb.add(b);

          }

          break;
      case 1:
      case 3:
          if(s.x>e.x&&s.x<e.x+30&&s.y>e.y&&s.y<e.y+20)
          {
              s.isLive =false;
              e.isLive=false;
              b2=true;
              Bomb b=new Bomb(e.x,e.y);
              bomb.add(b);
          }
          break;
      }
      return b2;

  }



  //按键上下左右动
public void keyPressed(KeyEvent arg0) {
    if(arg0.getKeyCode()==KeyEvent.VK_W)
    {

// System.out.print("anxiak");
this.mt.setDirect(0);
this.mt.moveUp();
}else if(arg0.getKeyCode()==KeyEvent.VK_D)
{
this.mt.setDirect(1);
this.mt.moveRight();
}else if(arg0.getKeyCode()==KeyEvent.VK_S)
{
this.mt.setDirect(2);
this.mt.moveDown();
}else if(arg0.getKeyCode()==KeyEvent.VK_A)
{
this.mt.setDirect(3);
this.mt.moveLeft();
}

    //按下J发射子弹
    if (arg0.getKeyCode()==KeyEvent.VK_J)
    {
        if(mt.ss.size()<5)
            //不许超过5颗子弹
        {
        this.mt.shotEnemy();
        }
    }
    this.repaint();

}
public void keyReleased(KeyEvent arg0) {


}
public void keyTyped(KeyEvent arg0) {


}
public void run() {
    while(true)
    {
        try {
            Thread.sleep(50);
        } catch (InterruptedException e) {
            // TODO 自动生成的 catch 块
            e.printStackTrace();
        }

        this.hitEnemyTank();
        this.hitMe();
        this.repaint();
    }


}
}

这种山寨野鸡老师录制的视频看的后果就是,直接丢上一堆代码,从来不讲原理,甚至连基本的调试也不教。看这种视频有用么?

哪怕你在正规的学校学习一个星期,起码老师会告诉你,怎么去调试程序。这很重要。你现在只知道错误发生在这几百行中的某一行。当然你没法解决。

你学会了调试,知道了发生错误的上下文,包括丢出错误的行,它的调用堆栈上的若干方法是什么,此时,你面对的是几行代码和几个变量,就是你再不会,你也知道怎么解决。

很多培训班的老师基本自己都不会写程序,也不知道从哪里胡乱下载几个现成的程序,就开始误人子弟了。

我再强调下,你需要学习的是怎么调试程序。你编写这个程序的目的不是因为你缺少一个游戏玩。
当然,你可以找人花上很多时间给你改好。但是你从中得不到任何有用的东西。
培训班的老师强调的是,随便展示一些代码,得到一个程序,但是你看完还是啥也不会。
程序是调试调出来的,而不是跟着视频,人家怎么改,我怎么改,人家怎么写,我怎么写,这么做出来的。