又是把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吧! 作者:宁在春

后记

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

希望你是满载而归的~


目录
相关文章
|
3月前
|
Java 程序员 数据库连接
我们详细地讲解一下 Java 异常及要如何处理
我是小假 期待与你的下一次相遇 ~
|
4月前
|
Java
深入JavaSE:详解Java对象的比较。
总的来说,Java对象的比较就像海洋生物的比较,有外在的,有内在的,有面对所有情况的,也有针对特殊情况的。理解并掌握这些比较方式,就能更好地驾驭Java的世界,游刃有余地操作Java对象。
77 12
|
5月前
|
编解码 JavaScript 前端开发
【Java进阶】详解JavaScript的BOM(浏览器对象模型)
总的来说,BOM提供了一种方式来与浏览器进行交互。通过BOM,你可以操作窗口、获取URL、操作历史、访问HTML文档、获取浏览器信息和屏幕信息等。虽然BOM并没有正式的标准,但大多数现代浏览器都实现了相似的功能,因此,你可以放心地在你的JavaScript代码中使用BOM。
148 23
|
5月前
|
Java 数据安全/隐私保护
Java 类和对象
本文介绍了Java编程中类和对象的基础知识,作为面向对象编程(OOP)的核心概念。类是对象的蓝图,定义实体类型;对象是具体实例,包含状态和行为。通过示例展示了如何创建表示汽车的类及其实例,并说明了构造函数、字段和方法的作用。同时,文章还探讨了访问修饰符的使用,强调封装的重要性,如通过getter和setter控制字段访问。最后总结了类与对象的关系及其在Java中的应用,并建议进一步学习继承等概念。
117 1
|
6月前
|
设计模式 缓存 Java
重学Java基础篇—Java对象创建的7种核心方式详解
本文全面解析了Java中对象的创建方式,涵盖基础到高级技术。包括`new关键字`直接实例化、反射机制动态创建、克隆与反序列化复用对象,以及工厂方法和建造者模式等设计模式的应用。同时探讨了Spring IOC容器等框架级创建方式,并对比各类方法的适用场景与优缺点。此外,还深入分析了动态代理、Unsafe类等扩展知识及注意事项。最后总结最佳实践,建议根据业务需求选择合适方式,在灵活性与性能间取得平衡。
336 3
|
6月前
|
SQL druid Oracle
【YashanDB知识库】yasdb jdbc驱动集成druid连接池,业务(java)日志中有token IDENTIFIER start异常
客户Java日志中出现异常,影响Druid的merge SQL功能(将SQL字面量替换为绑定变量以统计性能),但不影响正常业务流程。原因是Druid在merge SQL时传入null作为dbType,导致无法解析递归查询中的`start`关键字。
|
5月前
|
存储 缓存 Java
理解Java引用数据类型:它们都是对象引用
本文深入探讨了Java中引用数据类型的本质及其相关特性。引用变量存储的是对象的内存地址而非对象本身,类似房子的地址而非房子本身。文章通过实例解析了引用赋值、比较(`==`与`equals()`的区别)以及包装类缓存机制等核心概念。此外,还介绍了Java引用类型的家族,包括类、接口、数组和枚举。理解这些内容有助于开发者避免常见错误,提升对Java内存模型的掌握,为高效编程奠定基础。
250 0
|
5月前
|
Java
java中一个接口A,以及一个实现它的类B,一个A类型的引用对象作为一个方法的参数,这个参数的类型可以是B的类型吗?
本文探讨了面向对象编程中接口与实现类的关系,以及里氏替换原则(LSP)的应用。通过示例代码展示了如何利用多态性将实现类的对象传递给接口类型的参数,满足LSP的要求。LSP确保子类能无缝替换父类或接口,不改变程序行为。接口定义了行为规范,实现类遵循此规范,从而保证了多态性和代码的可维护性。总结来说,接口与实现类的关系天然符合LSP,体现了多态性的核心思想。
114 0
|
6月前
|
存储 算法 安全
Java对象创建和访问
Java对象创建过程包括类加载检查、内存分配(指针碰撞或空闲列表)、内存初始化、对象头设置及初始化方法执行。访问方式有句柄和直接指针两种,前者稳定但需额外定位,后者速度快。对象创建涉及并发安全、垃圾回收等机制。
Java对象创建和访问
|
Java API Scala
Java Optional空指针处理
直到真正了解了空指针异常,才能算一名合格的Java开发人员。在我们逼格闪闪的java码字符生涯中,每天都会遇到各种null的处理,Java8之后提供了一种更优雅的方式来处理空指针——Optional。
3720 0