天天看點

Java基礎知識學廢的一天,new 一個對象數組,操作時報空指針異常

作者:财高八鬥Java

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

也是再次複習到基礎知識的一天

又是把基礎知識學廢的一天

日常開頭~

Java基礎知識學廢的一天,new 一個對象數組,操作時報空指針異常
Java基礎知識學廢的一天,new 一個對象數組,操作時報空指針異常

别慌,懵就懂了,因為沒有上下文啊~

然後開始告訴我錯誤是什麼~

Java基礎知識學廢的一天,new 一個對象數組,操作時報空指針異常

一開始看到數組對象時,我是有想法的,包括他這個錯誤,我隐隐約約感覺我學過這部分的知識,有點久遠的感覺~

發來了有趣的代碼

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]是可以指派,并且不會報錯呢?

一起思考一下~

我的想法

我自己在測了之後,光從代碼邏輯層面看不出什麼問題,我就想去看一下底層的位元組碼檔案是怎麼樣的。(我當時隻是隐約記得這是執行個體化的一個問題,但是我知道邏輯層面看不出,但是在位元組碼中肯定有所不同)

然後就有了下面的測試:

首先看的是原來測試代碼的位元組碼檔案

Java基礎知識學廢的一天,new 一個對象數組,操作時報空指針異常

隻看 new ThirdInfo[6];和 thirdInfos[i].setTitle(vipPaytitle[i]);部分的位元組碼檔案

Java基礎知識學廢的一天,new 一個對象數組,操作時報空指針異常

圈出來的這三行位元組碼代碼就是ThirdInfo [] thirdInfos = new ThirdInfo[6];的展示

bipush 6 // 将6壓入操作數堆棧。
 anewarray #2 <com/ThirdInfo> //建立一個引用型(如類,接口,數組)的數組,并将其引用值壓入棧頂
 astore_1 //把棧頂的值存到第一個變量(thirdInfos )           

能不能看懂都沒事,我們來看看第二個測試:

直接在方法中 new ThirdInfo(),我們看看它的位元組碼檔案是什麼樣的。

Java基礎知識學廢的一天,new 一個對象數組,操作時報空指針異常

new #2 <com/ThirdInfo> // 建立一個對象 dup // 複制棧頂數值(數值不能是long或double類型的)并将複制值壓入棧頂 invokespecial #3 <com/ThirdInfo.<init> : ()V> // 調用超類構造方法,執行個體初始化方法,私有方法 pop // 出棧

其實看到這裡,就能夠大緻知道是什麼原因了。

就是因為沒有執行個體化,是以看起來ThirdInfo [] thirdInfos = new ThirdInfo[6];好像是建立了6個ThirdInfo對象的這段代碼,實際上,隻是配置設定了記憶體空間。

Java基礎知識學廢的一天,new 一個對象數組,操作時報空指針異常

還可以換個更簡單的方式來證明這個情況:

我利用反射建立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]);
     }
 }           
Java基礎知識學廢的一天,new 一個對象數組,操作時報空指針異常

可以看到,實際上 thirdInfos[0] 實際上就是null

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

隻有進行執行個體化之後,才能正确使用。

反射他本質上也是進行了類的執行個體化的,這點從位元組碼中依然可以看出。

Java基礎知識學廢的一天,new 一個對象數組,操作時報空指針異常

之前說到了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]);
 }           

我們來看看他的位元組碼檔案:

Java基礎知識學廢的一天,new 一個對象數組,操作時報空指針異常

不知道大家還記不記得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);
 }           
Java基礎知識學廢的一天,new 一個對象數組,操作時報空指針異常

後記

今天就寫到這裡啦~ 感覺自己好菜啊~ 一起努力哦~