记一次空指针异常

今天跑monkey的时候,出现了一个异常:

java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.Class java.lang.Object.getClass()' on a null object reference

尝试用一个空的引用去获取类对象,很奇怪的错误!

搜索了整个出错的代码,需要使用对象的时候都进行了判空了呀~

最后看到了这么一个地方:

mLocationListener = sLocationInstance.new LocationListener();
这是一个内部类的创建,使用到了外部类的对象,于是猜测是不是因为sLocationInstance这个对象是空的导致的呢?

于是马上写个测试用例:

public class NullTest {
    public static NullTest test;
    private boolean isOut;

    public static class User{
        public static void use(){
            Inner inner = test.new Inner();// 要创建内部类对象,必须使用外部类的对象+.new的方式。
        }
    }
    public class Inner{
        private boolean isIn;
        public void test(){
            isIn = isOut;// 内部类可以访问外部的所以成员变量和方法
        }
    }
}
果不其然,执行User.use()方法的时候报错了,也就是文章开头的那个错误。

哦,原来是因为我们要创建内部类,但是创建内部类需要一个外部类对象,但是如果外部类对象为空的话,就出问题了。

我的理解,虚拟机在创建内部类对象的时候,需要一个外部类类型,这个外部类类型是由外部类对象得到的,但是如果这个外部类对象为空的话,那就出现上面的异常了。(字面理解,不知道对不对O(∩_∩)O哈哈~)。

内部类对象的创建条件:

要想使用new 生成一个内部类的实例,需要先指向一个外部类的实例,也就是先生成外部类的实例,因为内部类可以调用外部类的人员成员,当没有外部类实例的时候也就没有这些成员的内存空间,内部类在实例化的时候,调用外部类的成员就会出错,所以需要使用外部类的实例 + 点 + new 的方式实例化一个新的内部类。

在内部类(Inner Class),可以随意的访问外部类的成员,这可让我们更好地组织管理我们的代码,增强代码的可读性。

详细可看内部类的定义:

http://android.blog.51cto.com/268543/384844/


找到了问题,那么还要去想想为什么会出现这个问题?

在我们的代码中sLocationInstance是个Location类的单例对象,该类里面定义了一个内部类:LocationListener,还定义了一个静态内部类:

static class MyHandler extends Handler

在MyHandler的handleMessage方法中,会去执行创建LocationListener对象的过程。

public void handleMessage(Message msg) {
   case START_LOCATION_MSG: {
      mLocationListener = sLocationInstance.new LocationListener();
      break;
   }   
}

在我们的Location类中,定义了MyHandler的静态变量,并进行了初始化(类加载的时候就会去初始化了,这个时候我们的单例对象可能还没创建):

private static MyHandler mHandler = new MyHandler(sLocationThread.getLooper());
所以,handler这个对象是优先于sLocationInstance对象创建的,那么就有可能在sLocationInstance还没创建的时候,我们发送了一个msg,于是会执行到hanleMessage方法中,从而去创建内部类LocationListener的对象,这个时候就肯定会出错了。

部分代码如下:

public class Location {
        private static final HandlerThread sLocationThread = new HandlerThread("location");
        static {
            sLocationThread.start();
        }
        public static Location sLocationInstance;
        public static final int START_LOCATION_MSG = 10002;
        private static MyHandler mHandler = new MyHandler(sLocationThread.getLooper());
        public static AMapLocationListener mLocationListener;
        private static Object mLock = new Object();

        public static class MyHandler extends Handler {
            public MyHandler(Looper looper) {
                super(looper);
            }
            @Override
            public void handleMessage(Message msg) {
                int what = msg.what;
                switch (what) {
                    case START_LOCATION_MSG: {
                        mLocationListener = sLocationInstance.new LocationListener();
                        break;
                    }
                    default:
                        break;
                }
            }
        }

        public synchronized static Location getInstance() {
            if (sLocationInstance == null) {
                sLocationInstance = new Location();
            }
            return sLocationInstance;
        }

        public synchronized void startLocation() {
            Log.d(TAG, "startLocation");
            mHandler.sendEmptyMessage(START_LOCATION_MSG);
        }

        public class LocationListener {
        }
}

虽然我们mHandler发送一个msg是由sLocationInstance这个对象来调用的,按理说,既然sLocationInstance能调用handler发送一个msg,那必然这个对象是不为空的,也就不可能出现空指针异常了。理想情况当然是这样的,但系统的事情,谁有能说得准呢。或许是我们发送消息后,系统阻塞了这个消息,然后等我们的Location被gc了,而mHandler还健在的时候,收到了这个延迟的消息呢~

就比如说某个app在我们系统中报了这个错误:

Crashjava.lang.NullPointerException09:17:39112:20:35其他未知// at android.app.ActivityThread.performResumeActivity(ActivityThread.java:3128)
ActivityThread出错了~~~

无论原因怎样,还是在使用对象的时候进行下判空吧~~


以上纯属个人瞎说,不对之处就当笑话看过,勿喷!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值