今天的文章素材是來自和我的朋友的交流~
也是再次複習到基礎知識的一天
又是把基礎知識學廢的一天
日常開頭~
别慌,懵就懂了,因為沒有上下文啊~
然後開始告訴我錯誤是什麼~
一開始看到數組對象時,我是有想法的,包括他這個錯誤,我隐隐約約感覺我學過這部分的知識,有點久遠的感覺~
發來了有趣的代碼
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]是可以指派,并且不會報錯呢?
一起思考一下~
我的想法
我自己在測了之後,光從代碼邏輯層面看不出什麼問題,我就想去看一下底層的位元組碼檔案是怎麼樣的。(我當時隻是隐約記得這是執行個體化的一個問題,但是我知道邏輯層面看不出,但是在位元組碼中肯定有所不同)
然後就有了下面的測試:
首先看的是原來測試代碼的位元組碼檔案
隻看 new ThirdInfo[6];和 thirdInfos[i].setTitle(vipPaytitle[i]);部分的位元組碼檔案
圈出來的這三行位元組碼代碼就是ThirdInfo [] thirdInfos = new ThirdInfo[6];的展示
bipush 6 // 将6壓入操作數堆棧。
anewarray #2 <com/ThirdInfo> //建立一個引用型(如類,接口,數組)的數組,并将其引用值壓入棧頂
astore_1 //把棧頂的值存到第一個變量(thirdInfos )
能不能看懂都沒事,我們來看看第二個測試:
直接在方法中 new ThirdInfo(),我們看看它的位元組碼檔案是什麼樣的。
new #2 <com/ThirdInfo> // 建立一個對象 dup // 複制棧頂數值(數值不能是long或double類型的)并将複制值壓入棧頂 invokespecial #3 <com/ThirdInfo.<init> : ()V> // 調用超類構造方法,執行個體初始化方法,私有方法 pop // 出棧
其實看到這裡,就能夠大緻知道是什麼原因了。
就是因為沒有執行個體化,是以看起來ThirdInfo [] thirdInfos = new ThirdInfo[6];好像是建立了6個ThirdInfo對象的這段代碼,實際上,隻是配置設定了記憶體空間。
還可以換個更簡單的方式來證明這個情況:
我利用反射建立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]);
}
}
可以看到,實際上 thirdInfos[0] 實際上就是null
ThirdInfo [] thirdInfos = new ThirdInfo[6];
thirdInfos[0]=new ThirdInfo();
System.out.println(thirdInfos[0]);
隻有進行執行個體化之後,才能正确使用。
反射他本質上也是進行了類的執行個體化的,這點從位元組碼中依然可以看出。
之前說到了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]);
}
我們來看看他的位元組碼檔案:
不知道大家還記不記得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);
}
後記
今天就寫到這裡啦~ 感覺自己好菜啊~ 一起努力哦~