一、起因
采用下面的代码为对象数组里面的每个元素的字段赋值时报错。
public class StudentArray {
public static void main(String[] args){
Student[] stuArr = new Student[5]; //创建对象数组,stuArr存储地址
for(int i = 0; i < stuArr.length; ++i){
System.out.println(stuArr[i]); //null
stuArr[i].number = i + 1; //报错
stuArr[i].state = (int)Math.round(Math.random() * 3);
stuArr[i].score = (int)Math.round(Math.random() * 100);
System.out.println(stuArr[i]);
}
}
}
class Student{
int number;
int state;
int score;
@Override
public String toString() {
return "Student{" +
"number=" + number +
", state=" + state +
", score=" + score +
'}';
}
}
错误信息如下:自定义对象数组的元素的值为null,报空指针异常。
null
Exception in thread "main" java.lang.NullPointerException: Cannot assign field "number" because "stuArr[i]" is null
at com.zhangqu.java.method.StudentArray.main(StudentArray.java:19)
原因分析:因为Student[] stuArr = new Student[5];
创建对象数组时,返回系统为该对象数组在堆区分配的空间的地址,并将地址值存储到引用变量stuArr
,stuArr
的数据类型为类数组Student[]
,是引用类型;而该数组的每一个元素都是类Student
的对象(本质是引用,存储null或地址值),数组的每一个元素的数据类型为类Student
,也是引用类型,由于没有继续使用new
为这些引用类型的Student
类对象分配空间,所有数组的元素默认被初始化为null
。
不较真的情况下,可以把Student[] stuArr = new Student[5];
看做如下形式:
Student stu1 = null;
Student stu2 = null;
......
Student stu5 = null;
Student[] stuArr = {stu1, stu2, ..., stu5}
二、正确代码
public class StudentArray {
public static void main(String[] args){
Student[] stuArr = new Student[5]; //创建对象数组,stuArr存储地址, 初始化变量stuArr
int num = 0;
for (Student stu : stuArr) {
System.out.println(stu); //null
stu = new Student(); //用new分配空间,显式的为数组的每一个元素赋值,stu存储地址
System.out.println(stu);
System.out.println(stu.getClass().toString()); //获取数据类型
stu.number = ++num;
stu.state = (int)Math.round(Math.random() * 3);
stu.score = (int)Math.round(Math.random() * 100);
System.out.println(stu);
}
}
}
class Student{
int number;
int state;
int score;
@Override
public String toString() {
return "Student{" +
"number=" + number +
", state=" + state +
", score=" + score +
'}';
}
}
运行结果:
null
Student{number=0, state=0, score=0}
class com.zhangqu.java.method.Student
Student{number=1, state=1, score=10}
null
Student{number=0, state=0, score=0}
class com.zhangqu.java.method.Student
Student{number=2, state=0, score=42}
......
内存分析概要图如下:
由于是自定义的类,所以创建对象数组时,第一步是用new
为数组对象本身分配空间(初始化),第二步是用new
为数组对象的元素分配空间(赋值)。
public class StudentArray {
public static void main(String[] args){
}
@Test
void testObjArr(){
StuObjArr[] stuObjArrs = new StuObjArr[2]; //第一步,为对象数组分配空间,初始化stuObjArrs
for(int i = 0; i < stuObjArrs.length; i++){
stuObjArrs[i] = new StuObjArr(); //第二步,为数组元素分配空间,为stuObjArrs[i]赋值
System.out.println(stuObjArrs[i]); //打印数组元素在堆区的地址
}
}
}
class StuObjArr{
int id;
String name;
}
运行结果:
com.zhangqu.java.method.StuObjArr@2a70a3d8
com.zhangqu.java.method.StuObjArr@289d1c02
最后附上一张数据类型图,基础真的很重要。
引用类型的变量,只能存储两类值,null
或地址值(含变量的数据类型)