比较常见的空指针(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生命周期的图片

比较常见的空指针(NullPointerException)_第1张图片

可以知道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<>();这种类似的操作必不可少。

你可能感兴趣的:(比较常见的空指针(NullPointerException))