又是把Java基础知识学废的一天,new 一个对象数组,操作时报空指针异常

简介: 又是把Java基础知识学废的一天,new 一个对象数组,操作时报空指针异常

今天的文章素材又是来自我和我的朋友程交流~

也是再次复习到基础知识的一天

又是把基础知识学废的一天

日常开头~

image.png


别慌,懵就懂了,因为没有上下文啊~

然后开始告诉我错误是什么~

image.png

一开始看到数组对象时,我是有想法的,包括他这个错误,我隐隐约约感觉我学过这部分的知识,有点久远的感觉~

发来了有趣的代码

 public class ThirdInfo {
     private String title;
     private int number;
     private String pay;
     private String count;
 ​
     @Override
     public String toString() {
         return "ThirdInfo{" +
                 "title='" + title + ''' +
                 ", number=" + number +
                 ", pay='" + pay + ''' +
                 ", count='" + count + ''' +
                 '}';
     }
 ​
     public ThirdInfo(String title, String count, String pay) {
         this.title = title;
         this.pay = pay;
         this.count = count;
     }
 ​
     public ThirdInfo(String title, int number, String pay) {
         this.title = title;
         this.number = number;
         this.pay = pay;
     }
     public ThirdInfo(String title, String pay) {
         this.title = title;
         this.pay = pay;
     }
     public String getTitle() {   return title;  }
 ​
     public void setTitle(String title) {    this.title = title; }
 ​
     public int getNumber() {   return number;  }
 ​
     public void setNumber(int number) {  this.number = number; }
 ​
     public String getPay() { return pay;}
 ​
     public void setPay(String pay) { this.pay = pay;}
 ​
     public String getCount() {  return count; }
 ​
     public void setCount(String count) {this.count = count;}
 }
 ​
 class testal{
     public static void main(String[] args) {
         ThirdInfo [] thirdInfos = new ThirdInfo[6];
 ​
         String vipPay[] = new String[]{
                 "3笔", "¥1000.00", "¥100.00", "100积分", "¥10.00", "100积分"
         };
         String vipPaytitle[] = new String[]{
                 "会员消费笔数","储值余额消费","赠送余额消费","积分抵线变动","积分抵扣金额","积分赠送变动"
         };
 ​
         for (int i = 0;i<vipPay.length;i++){
             thirdInfos[i].setTitle(vipPaytitle[i]);
             thirdInfos[i].setPay(vipPay[i]);
         }
 ​
         for (ThirdInfo info : thirdInfos) {
             System.out.println(info);
         }
     }
 }

如果不事先说他会报错,你顺着看下来,甚至还会觉得是对的。

因为就算没见过上面这样的代码,也可能见过下面这样的代码

 String[] str = new String[6];
 String vipPaytitle[] = new String[]{
     "会员消费笔数", "储值余额消费", "赠送余额消费", "积分抵线变动", "积分抵扣金额", "积分赠送变动"
 };
 for (int i=0;i<vipPaytitle.length;i++){
     str[i]=vipPaytitle[i];
 }
 for (int i=0;i<vipPaytitle.length;i++){
     System.out.println(str[i]);
 }

String是对象没人会骂我吧,那接着说new String[6] 创建了一个对象数组也没人反对吧。

那再说,为什么new ThirdInfo[6]是不可以操作的,但是我new String[6]是可以赋值,并且不会报错呢?

一起思考一下~

我的想法

我自己在测了之后,光从代码逻辑层面看不出什么问题,我就想去看一下底层的字节码文件是怎么样的。(我当时只是隐约记得这是实例化的一个问题,但是我知道逻辑层面看不出,但是在字节码中肯定有所不同)

然后就有了下面的测试:

首先看的是原来测试代码的字节码文件

image.png

只看 new ThirdInfo[6];和 thirdInfos[i].setTitle(vipPaytitle[i]);部分的字节码文件

image.png

圈出来的这三行字节码代码就是ThirdInfo [] thirdInfos = new ThirdInfo[6];的展示

 bipush 6 // 将6压入操作数堆栈。
 anewarray #2 <com/ThirdInfo> //创建一个引用型(如类,接口,数组)的数组,并将其引用值压入栈顶
 astore_1 //把栈顶的值存到第一个变量(thirdInfos )

能不能看懂都没事,我们来看看第二个测试:

直接在方法中 new ThirdInfo(),我们看看它的字节码文件是什么样的。

image.png

 new #2 <com/ThirdInfo> // 创建一个对象
 dup // 复制栈顶数值(数值不能是long或double类型的)并将复制值压入栈顶
 invokespecial #3 <com/ThirdInfo.<init> : ()V> // 调用超类构造方法,实例初始化方法,私有方法
 pop // 出栈

其实看到这里,就能够大致知道是什么原因了。

就是因为没有实例化,所以看起来ThirdInfo [] thirdInfos = new ThirdInfo[6];好像是创建了6个ThirdInfo对象的这段代码,实际上,只是分配了内存空间。

image.png

还可以换个更简单的方式来证实这个情况:

我利用反射创建ThirdInfo对象进行输出,和输出ThirdInfo [] thirdInfos = new ThirdInfo[6];第一个对象,看看他们的结果是什么~

 public class Test {
 ​
     public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
         Class<?> aClass = Class.forName("com.ThirdInfo");
         ThirdInfo o =(ThirdInfo) aClass.newInstance();
         System.out.println(o);
 ​
         ThirdInfo [] thirdInfos = new ThirdInfo[6];
         System.out.println(thirdInfos[0]);
     }
 }

image.png

可以看到,实际上 thirdInfos[0] 实际上就是null

 ThirdInfo [] thirdInfos = new ThirdInfo[6];
 thirdInfos[0]=new ThirdInfo();
 System.out.println(thirdInfos[0]);

只有进行实例化之后,才能正确使用。

反射他本质上也是进行了类的实例化的,这点从字节码中依然可以看出。

image.png

之前说到了invokespecial是调用超类构造方法,实例初始化方法,私有方法

invokevirtual 的意思是调用实例方法.

更详细的很难说,学过但我忘了

阅读字节码文件,也没你想的那么难,反正不会去查就好了,很多中文版手册的~

所以在使用前肯定都会去进行实例化的。

聊完这个,又回到了之前的问题上。

为什么 new String[6]可以?

String也是个对象~

说到这个,又回到了以前学JVM的知识上来了.

写了一小段代码,这是可以正确执行的

 String[] str = new String[6];
 for (int i = 0; i < 6; i++) {
     str[i] = ""+i;
 }
 for (int i = 0; i < 6; i++) {
     System.out.println(str[i]);
 }

我们来看看他的字节码文件:

image.png

不知道大家还记不记得String不可变性,当我们使用当对字符串重新赋值时,需要重写指定内存区域赋值,不能使用原有的value进行赋值。

因为String它的不可变性,看似只是改变str[0]=null的值,实际上将一个新的字符串("abc")赋值给str[0]时,真正改变的是str[0]的指向。

看个简单的例子吧,是我以前文章里面的。

 public static void main(String[] args) {
     String str1 = "hello";
     String str2 = "hello";
     str1="abc,hao";
     // 判断地址, 它由true -->false
     System.out.println(str1 == str2);
 }

image.png

更详细的可以看看这篇

通过这个文章重新再深入认识认识String吧! 作者:宁在春

后记

今天就写到了这里啦~ 感觉自己还好菜啊~ 一起努力哦~

希望你是满载而归的~


目录
相关文章
|
2天前
|
存储 Java 索引
Java数组
Java数组
9 0
|
2天前
|
存储 算法 Java
【Java探索之旅】掌握数组操作,轻松应对编程挑战
【Java探索之旅】掌握数组操作,轻松应对编程挑战
9 0
|
2天前
|
存储 Java C语言
【Java探索之旅】基本类型与引用类型 数组的应用 二维数组
【Java探索之旅】基本类型与引用类型 数组的应用 二维数组
10 0
|
2天前
|
存储 机器学习/深度学习 Java
【Java探索之旅】数组使用 初探JVM内存布局
【Java探索之旅】数组使用 初探JVM内存布局
11 0
|
2天前
|
存储 Java 编译器
【Java探索之旅】数组概念与初始化指南:动静结合
【Java探索之旅】数组概念与初始化指南:动静结合
10 0
|
2天前
|
Java 索引
JAVA刷题之数组的总结和思路分享
JAVA刷题之数组的总结和思路分享
|
4天前
|
Java 程序员 编译器
|
6天前
Swagger2异常:java.lang.NumberFormatException: For input string: ““
Swagger2异常:java.lang.NumberFormatException: For input string: ““
14 1
|
7天前
|
存储 Java 程序员
JavaSE&Java的异常
JavaSE&Java的异常
21 0
|
算法 搜索推荐 Java
Java基础知识之典型范例二
Java基础知识之典型范例二
96 0