相信在编写java的代码过程中,我们经常会碰到这样一个问题:明明代码的语法没有错误,运行过程中也没有报错,但为什么总是实现不了自己想要的功能呢?其实,这是常见的逻辑错误。我在初学的时候也是如此,但我慢慢学会使用“步骤跟踪法”即在恰当的位置使用打印输出语句来验证,就像《洋葱》这首歌里写的:“如果你愿意一层一层地剥开我的心。”这样就能找出错误,下面我就介绍给同样初学的大家。
下面来看两个例子:
public class drawPixelwyh { public static void main(String[] args){ drawPixelwyh ui=new drawPixelwyh(); ui.showUI(); } public void showUI(){ //其他代码省略... //窗体:默认边框布局 JFrame jf=new JFrame(); Graphics g=jf.getGraphics();//画笔 jf.setVisible(true); PixelMouse mouse=new PixelMouse(); mouse.gr=g; } } public class PixelMouse implements MouseListener,ActionListener{ //全局变量:属性 public Graphics gr; //保存传递过来的画笔对象 gr.drawImage(bufferImage, 40, 90,1080,540, null); |
这个错误是非常常见的空指针错误。我们先按下解决方法不谈,先按照一步步来检验错误(运行时错误出现在代码最后一行)。
①bufferImage这里不可能错误,因为就算这里是空对象系统也是不会报错的,而只有在调用空对象(gr)的方法时才会出错。
②因此,我们在报错的前一行使用打印语句: System.out.println("gr:"+gr);这时会输出:gr:null。这样就验证了我们的想法是正确的。
③然后我们再在mouse.gr=g;前写一行打印语句:System.out.println("传入前的g:"+g);这时会输出:传入前的g:null。这样就说明了我们在传入前的画笔g就已经是空对象了。
④然后我们再检查这一行前面的代码,发现是Graphics g=jf.getGraphics();和jf.setVisible(true);这两行的位置弄反了,因为获取画笔必须在窗体可见之后,这时我们就知道如何修改了。
或许这个错误比较显而易见,但对初学者的我们来说这种解决问题的思路是非常重要的,这样不仅可以解决问题,更重要的是能再一次理清自己编写代码时的逻辑。
再来看一个例子:
public class Threadop extends Thread{ public Graphics gr; public int op; public BufferedImage bufferImage; public Pixel pixel = new Pixel(); public String name; //区分不同处理效果 public Threadop(Graphics g){ this.gr=g; System.out.println("创建线程对象"); } public void setop(int op){ this.op=op; } public void setPixelName(String n) { name = n; } public void run(){ Webcam webcam = Webcam.getDefault(); webcam.open(); while(true){ bufferImage=webcam.getImage(); //System.out.println("进入while循环:"+name); System.out.println("gr="+gr); gr.drawImage(bufferImage, 300, 150, 540, 540, null); } } } public class PixelMouse implements MouseListener,ActionListener{ public Threadop tr=new Threadop(gr); public void actionPerformed(ActionEvent e){ name = e.getActionCommand(); if("打开摄像头".equals(name)){ tr.start(); System.out.println(tr+" op "+tr.op); } } } public class drawPixelwyh { public static void main(String[] args){ drawPixelwyh ui=new drawPixelwyh(); ui.showUI(); } public void showUI(){ //其他代码省略... //窗体:默认边框布局 JFrame jf=new JFrame(); jf.setVisible(true); Graphics g=jf.getGraphics();//画笔 PixelMouse mouse=new PixelMouse(); System.out.println("开始传画笔"); mouse.gr=g; } } |
在这里同样会报空指针错误(nullpointerexception),同时错误出现在“gr.drawImage(bufferImage, 300, 150, 540, 540, null);”这一行。我们从上个例子类似可知,只有调用空对象的方法系统才会报错,但我们明明在创建线程类的构造方法中把PixelMouse中的画笔传过去了,而PixelMouse中的gr也已经在drawPixel的showUI()方法中的mouse.gr=g;语句中绑定在窗体的画笔上了,这是怎么回事呢?想要解决这个问题,同样需要用到步骤跟踪法,下面是具体步骤:
①我们在线程类中的run方法出错前一行打印画笔:System.out.println("gr="+gr);这时输出gr=null;符合我们的猜想。
②我们在线程类的构造方法中打印输出语句:System.out.println("创建线程对象");同时在showUI方法的
mouse.gr=g;前使用打印语句:System.out.println("开始传画笔");这时输出结果:创建线程对象 开始传画笔。因此我们要注意这个问题:类在运行过程中,首先是实现其具有的属性(PixelMouse中的tr这个线程对象),再实现其拥有的方法(drawPixel中的mouse.gr=g,这个方法不同于一般明显写出的方法),所以我们在构造方法中想将画笔传过去是不可行的,因为此时窗体上的画笔还没有获取,自然tr中的画笔就是空的了。
③因此我们可以这样改正:写构造方法时并不需要传画笔,而是在PixelMouse中的actionPerformed方法里加tr.gr=gr;语句来传画笔 ,这样就可以避免这个问题,同时也不要忘记创建线程对象tr时不需要参数gr。
总结:
通过这两个例子,我们初学者不仅可以学会如何自己寻找错误,也可以理清自己编写程序时的逻辑,希望这个方法能给大家一些帮助!