比较常见的空指针(NullPointerException)

定义:NullPointerException是java.lang.NullPointerException的简称,是Java语言中的一个异常类,位于java.lang包中,父类是java.lang.RuntimeException,该异常在源程序中可以不进行捕获和处理。

发生频率: ★★★★★

发生原因:当应用程序试图在需要对象的地方使用时,抛出该异常。这种情况包括:

调用 null 对象的实例方法。

访问或修改 null 对象的字段。

将 null 作为一个数组,获得其长度。

将 null 作为一个数组,访问或修改其时间片。

将 null 作为 Throwable 值抛出。

Android 中View没有初始化

应用程序应该抛出该类的实例,指示其他对 null 对象的非法使用。

1.简单例子分析

1.1 具体事例

现在看一个我们经常在log中出现的事例,下面我们看一下这个Log

Exception in thread "main" java.lang.NullPointerException

at com.example.myproject.Book.getTitle(Book.java:16)

at com.example.myproject.Author.getBookTitles(Author.java:25)

at com.example.myproject.Bootstrap.main(Bootstrap.java:14)

1.2 分析

我们简单分析一下,这个是我们所遇到的问题。最上面一行表示我们遇到的是java.lang.NullPointerException问题,然后我们需要特别关注at这个单词,它可以告诉我们问题发生的具体位置。然后我们找到相应的位置并解决问题。我们最上面的at提示的是

at com.example.myproject.Book.getTitle(Book.java:16)

我们通过debug,首先找到Book.java类,然后看一下第16行

15  public String getTitle() {

16      System.out.println(title.toString());

17      return title;

18  }

通过上面的代码,因为我们报错的方法是getTitle,然后这个方法返回的String值,如果为空的化,只有可能title为空。所以可以了解到基本上就是title为null

2.一连串exceptions的例子

2.1具体事例

有时候APP catch的是一个Exception,然而实际抛出的是另一个Exception,像这样的:

34  public void getBookIds(int id) {

35      try {

36        book.getId(id);    // 这个方法抛出NullPointerException在22行

37      } catch (NullPointerException e) {

38        throw new IllegalStateException("A book has a null property", e)

39      }

40  }

然后这个异常的Log是这样的:

Exception in thread "main" java.lang.IllegalStateException: A book has a null property

at com.example.myproject.Author.getBookIds(Author.java:38)

at com.example.myproject.Bootstrap.main(Bootstrap.java:14)

Caused by: java.lang.NullPointerException

at com.example.myproject.Book.getId(Book.java:22)

at com.example.myproject.Author.getBookIds(Author.java:36)

... 1 more

2.2分析

这个异常中的Caused by有什么不同吗,有时候Log中有很多Caused by。我们希望在这些错误中找到最主要的问题,一般主要的问题出现在最后的Caused by。在这个异常中的主要问题是

Caused by: java.lang.NullPointerException <-- 主要导致的Exception

at com.example.myproject.Book.getId(Book.java:22) <-- 最重要的一行

然后我们根据这个异常Log,我们可以定位到Book.java类中22行的getId方法。确定是这行导致的NullPointerException。然后判断是否有值为null。

3.方法中对象参数为空导致的NullPointerException

3.1 具体事例

public void doSomething(SomeObject obj){

//一些对obj的操作

}

3.2分析

正常看上去没有什么问题,但是这个会出现隐藏的bug,平时我们在编码过程中很容易出现这种细节的问题。当我们使用下列代码的时候。

doSomething(null);

当你传递的参数对象SomeObject为null的时候,例如:

SomeObject obj = null;

doSomething(obj);

// 等同于

doSomething(null);

最后实现的就是这样的。当方法中使用SomeObject对象时。会发生NullPointerException异常。所以在我们操作的过程中。我们需要对传递过来的SomeObject对象进行非空判断,然后在对象不为空的时候进行你需要的操作,然后在为空的时候进行提示或者其他操作。

所以最后我们的处理操作是:

/**

* @param obj 参数有可能为空

*

*/

public void doSomething(SomeObject obj){

if(obj != null){

//do something

} else {

//do something else

}

}

4.创建对象数组时候抛出空指针

4.1具体事例

public class ResultList {

public String name;

public Object value;

public ResultList() {}

}

public class Test {

public static void main(String[] args){

ResultList[] boll = new ResultList[5];

boll[0].name = "iiii";

}

}

4.2分析

首先看一下main方法中的这一行代码

ResultList[] boll = new ResultList[5];

对象数组已经初始化了,但是数据中的每一个对象并没有初始化。所以我们调用boll[0].name = "iiii";这行代码会报空指针。 我们应该在调用之前进行初始化对象操作。

ResultList[] boll = new ResultList[5];

// 调用之前进行初始化

boll[0] = new ResultList();

boll[0].name = "iiii";

或者另外一种方式初始化

ResultList[] boll = {new ResultList(),new ResultList(),new ResultList(),new ResultList(),new ResultList()};

// 这种数据的定义并对每一个对象进行初始化

boll[0].name = "iiii";

这种方式就是数据的定义和初始化的两种方式。

5.在Fragment中调用onCreate方法并找控件造成NullPointerException

5.1具体事例

Fragment的onCreate()方法

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

View something = findViewById(R.id.something);

something.setOnClickListener(new View.OnClickListener() { ... }); // NPE HERE

if (savedInstanceState == null) {

getSupportFragmentManager().beginTransaction()

.add(R.id.container, new PlaceholderFragment()).commit();

}

}

Fragment的布局

xmlns:tools="http://schemas.android.com/tools"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:paddingBottom="@dimen/activity_vertical_margin"

android:paddingLeft="@dimen/activity_horizontal_margin"

android:paddingRight="@dimen/activity_horizontal_margin"

android:paddingTop="@dimen/activity_vertical_margin"

tools:context="packagename.MainActivity$PlaceholderFragment" >

android:layout_width="100dp"

android:layout_height="100dp"

android:id="@+id/something" />

5.2分析

首先我们看一张Fragment生命周期的图片

可以知道onCreate方法在onCreateView方法之前执行。然后返回null。然后如果调用方法就造成了NullPointerException可以使用以下的方法进行

@Override

public View onCreateView(LayoutInflater inflater, ViewGroup container,

Bundle savedInstanceState) {

View rootView = inflater.inflate(R.layout.fragment_main, container,

false);

View something = rootView.findViewById(R.id.something); // not activity findViewById()

something.setOnClickListener(new View.OnClickListener() { ... });

return rootView;

}

我们等到布局已经创建之后进行findViewById就不会造成NullPointerException

6.使用RecyclerView造成的NullPointerException

6.1具体事例

报错日志信息

java.lang.NullPointerException: Attempt to invoke virtual method 'void android.support.v7.widget.RecyclerView$LayoutManager.onMeasure(android.support.v7.widget.RecyclerView$Recycler, android.support.v7.widget.RecyclerView$State, int, int)' on a null object reference

at android.support.v7.widget.RecyclerView.onMeasure(RecyclerView.java:1764)

at android.view.View.measure(View.java:17430)

6.2分析

根据我们上面使用的方法,找到报错行文件。找到有可能发生NullPointerException的位置

void android.support.v7.widget.RecyclerView$LayoutManager.onMeasure

是没有提供LayoutManager造成的,所以我们需要做以下处理

// 还需要找到相应的recyclerView

final LinearLayoutManager layoutManager = new LinearLayoutManager(context);

layoutManager.setOrientation(LinearLayoutManager.VERTICAL);

recyclerView.setLayoutManager(layoutManager);

7.findViewById方法引发的NullPointerException

7.1具体事例

MainActivity

public class MainActivity extends Activity {

ViewPager pager;

MyPagerAdapter adapter;

LinearLayout layout1, layout2, layout3;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

layout1 = (LinearLayout) findViewById(R.id.first_View);

layout2 = (LinearLayout) findViewById(R.id.second_View);

layout3 = (LinearLayout) findViewById(R.id.third_View);

adapter = new MyPagerAdapter();

pager = (ViewPager) findViewById(R.id.main_pager);

pager.setAdapter(adapter);

}

private class MyPagerAdapter extends PagerAdapter

{

@Override

public int getCount() {

return 3;

}

@Override

public Object instantiateItem(ViewGroup collection, int position) {

LinearLayout l = null;

if (position == 0 )

{

l = layout1;

}

if (position == 1)

{

l = layout2;

}

if (position == 2)

{

l = layout3;

}

collection.addView(l, position);

return l;

}

@Override

public boolean isViewFromObject(View view, Object object) {

return (view==object);

}

@Override

public void destroyItem(ViewGroup collection, int position, Object view) {

collection.removeView((View) view);

}

}

}

activity_main layout


android:orientation="vertical"

android:layout_width="fill_parent"

android:layout_height="fill_parent"

android:background="#a4c639">

android:layout_width="match_parent"

android:layout_height="match_parent"

android:id="@+id/main_pager"/>

activity_first layout


android:layout_width="match_parent"

android:layout_height="match_parent"

android:id="@+id/first_View">

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="@string/hello_world" />

android:id="@+id/button1"

style="?android:attr/buttonStyleSmall"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="Button" />

activity_second layout


android:layout_width="match_parent"

android:layout_height="match_parent"

android:id="@+id/second_View">

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="@string/hello_world" />

And the activity_third layout


android:layout_width="match_parent"

android:layout_height="match_parent"

android:id="@+id/third_View">

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="@string/hello_world" />

7.2分析

首先findViewById在setContentView方法调用完之后调用,并且在setContentView中找需要的控件,如果没有则返回为null。

Sample

当使用setContentView(R.layout.activity_first);在你的代码中

如果你调用layout1 = (LinearLayout) findViewById(R.id.first_View);会返回一个View(因为你的activity_first存在这个控件,并有这个id)

如果你调用layout2 = (LinearLayout) findViewById(R.id.second_View);会返回null(因为在activity_first中找不到这个控件的id)

所以在我们使用控件的时候,如果是findViewById造成的NullPointerException,你就需要在布局文件中寻找是否有该控件和id。

总结

NullPointerException发生的原因有很多种,我们也有相应的解决方法。主要的方式有:

首先查看报NullPointerException的日志,然后找到具体的位置(1.找到at的位置,2.定位你个人的包名,如2.1中at com.example.myproject.Author.getBookIds),然后进行非空判断或者其他操作

如果日志中没有找到具体的位置,然后需要找到具体报错的外部类名和方法(如6.1中android.support.v7.widget.RecyclerView$LayoutManager.onMeasure),然后进行非空操作或者其他操作

AndroidActivity和Fragment中出现NullPointerException,如果以上方法没有问题,了解他们的生命周期,然后看看执行顺序有没有问题。

数组,对象,集合等调用方法之前需要初始化,List mList = new ArrayList<>();这种类似的操作必不可少。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,830评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,992评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,875评论 0 331
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,837评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,734评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,091评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,550评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,217评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,368评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,298评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,350评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,027评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,623评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,706评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,940评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,349评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,936评论 2 341

推荐阅读更多精彩内容