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


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

    日常开头~

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

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

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

    发来了有趣的代码

    1.  public class ThirdInfo {
    2.      private String title;
    3.      private int number;
    4.      private String pay;
    5.      private String count;
    6.  ​
    7.      @Override
    8.      public String toString() {
    9.          return "ThirdInfo{" +
    10.                  "title='" + title + ''' +
    11.                  ", number=" + number +
    12.                  ", pay='" + pay + ''' +
    13.                  ", count='" + count + ''' +
    14.                  '}';
    15.     }
    16.  ​
    17.      public ThirdInfo(String title, String count, String pay) {
    18.          this.title = title;
    19.          this.pay = pay;
    20.          this.count = count;
    21.     }
    22.  ​
    23.      public ThirdInfo(String title, int number, String pay) {
    24.          this.title = title;
    25.          this.number = number;
    26.          this.pay = pay;
    27.     }
    28.      public ThirdInfo(String title, String pay) {
    29.          this.title = title;
    30.          this.pay = pay;
    31.     }
    32.      public String getTitle() {   return title; }
    33.  ​
    34.      public void setTitle(String title) {    this.title = title; }
    35.  ​
    36.      public int getNumber() {   return number; }
    37.  ​
    38.      public void setNumber(int number) {  this.number = number; }
    39.  ​
    40.      public String getPay() { return pay;}
    41.  ​
    42.      public void setPay(String pay) { this.pay = pay;}
    43.  ​
    44.      public String getCount() {  return count; }
    45.  ​
    46.      public void setCount(String count) {this.count = count;}
    47.  }
    48.  ​
    49.  class testal{
    50.      public static void main(String[] args) {
    51.          ThirdInfo [] thirdInfos = new ThirdInfo[6];
    52.  ​
    53.          String vipPay[] = new String[]{
    54.                  "3笔", "¥1000.00", "¥100.00", "100积分", "¥10.00", "100积分"
    55.         };
    56.          String vipPaytitle[] = new String[]{
    57.                  "会员消费笔数","储值余额消费","赠送余额消费","积分抵线变动","积分抵扣金额","积分赠送变动"
    58.         };
    59.  ​
    60.          for (int i = 0;i<vipPay.length;i++){
    61.              thirdInfos[i].setTitle(vipPaytitle[i]);
    62.              thirdInfos[i].setPay(vipPay[i]);
    63.         }
    64.  ​
    65.          for (ThirdInfo info : thirdInfos) {
    66.              System.out.println(info);
    67.         }
    68.     }
    69.  }
    70. 复制代码

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

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

    1.  String[] str = new String[6];
    2.  String vipPaytitle[] = new String[]{
    3.      "会员消费笔数", "储值余额消费", "赠送余额消费", "积分抵线变动", "积分抵扣金额", "积分赠送变动"
    4.  };
    5.  for (int i=0;i<vipPaytitle.length;i++){
    6.      str[i]=vipPaytitle[i];
    7.  }
    8.  for (int i=0;i<vipPaytitle.length;i++){
    9.      System.out.println(str[i]);
    10.  }
    11. 复制代码

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

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

    一起思考一下~

    我的想法

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

    然后就有了下面的测试:

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

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

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

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

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

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

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

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

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

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

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

    1.  public class Test {
    2.  ​
    3.      public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
    4.          Class<?> aClass = Class.forName("com.ThirdInfo");
    5.          ThirdInfo o =(ThirdInfo) aClass.newInstance();
    6.          System.out.println(o);
    7.  ​
    8.          ThirdInfo [] thirdInfos = new ThirdInfo[6];
    9.          System.out.println(thirdInfos[0]);
    10.     }
    11.  }
    12. 复制代码

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

    1.  ThirdInfo [] thirdInfos = new ThirdInfo[6];
    2.  thirdInfos[0]=new ThirdInfo();
    3.  System.out.println(thirdInfos[0]);
    4. 复制代码

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

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

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

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

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

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

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

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

    为什么 new String[6]可以?

    String也是个对象~

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

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

    1.  String[] str = new String[6];
    2.  for (int i = 0; i < 6; i++) {
    3.      str[i] = ""+i;
    4.  }
    5.  for (int i = 0; i < 6; i++) {
    6.      System.out.println(str[i]);
    7.  }
    8. 复制代码

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

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

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

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

    1.  public static void main(String[] args) {
    2.      String str1 = "hello";
    3.      String str2 = "hello";
    4.      str1="abc,hao";
    5.      // 判断地址, 它由true -->false
    6.      System.out.println(str1 == str2);
    7.  }
    8. 复制代码

    更详细的可以看看这篇

  • 相关阅读:
    基础矩阵和本质矩阵
    年薪高达50W的测开,到底是做什么的?
    代码随想录算法训练营第八天|二叉树(截止到左叶子之和)
    requests请求douban.com获得网页源代码
    【VMware vSAN】全新vSAN 8 ESA快速存储架构配置文件服务并创建文件共享。
    客户需求调研的三个实用工具
    浅谈中压系统母线弧光保护的必要性
    ROS2机器人笔记220805-重要备忘录-
    python+Django社区疫情防控系统 uniapp微信小程序
    uni-app:实现view元素强制换行(解决长字符和英文字符不换行问题)
  • 原文地址:https://blog.csdn.net/m0_71777195/article/details/127569007