前言
上一節提到了實例變量和實例對象的區別,那么這節就繼續深入一下變量以及對象。
主要內容:
- 實例變量和類變量
- 父,子類實例變量和內存分配機制
- final 的注意事項
1. 實例變量和類變量
1.1 定義
- 實例變量:在類里面沒有使用static修飾的變量,例如:int i;也稱為非靜態變量
- 類變量:在類里面使用了static修飾的變量,例如:static int i; 也稱為靜態變量
1.2 區別
- 首先在同一個JVM內每個類對應一個Class對象,也就是說,只會給該類劃分一次內存空間,因此類變量才會只有一次初始化。相對于實例變量,當每次建立對象(實例),都會劃分一塊新的內存空間。
所以,所有的該類的實例對象,都是共用一個類變量。
1.3 初始化
- 首先看一段代碼
class test{
//1.weight 初始化的值是多小
double weight=2.3;
{
weight=4.3;
}
test(){
this.weight=4.6;
}
// 2.向前引用(為什么)
int num1=num2+2;
static int num2=4;
// 3.請說出name 初始化的值應該是什么
static{
name="我愛java";
}
static String name="給我丶鼓勵";
}
- 有答案了嗎?有疑問嗎?
- 首先得把該類的初始化過程理解清楚。第一步是建立劃分一個類對象的內存空間,然后把變量初始化(優先是把類變量給初始化,然后再到實例變量初始化)。第二步,再對變量進行賦值,在賦值的過程中,是按照代碼順序由上往下賦值的。
- 在了解完類初始化過程之后,第一個問題weight=4.6就很顯然了。但是有趣的事,在編譯的時候,是可以看到,其實編輯器是把weight=2.3,和非靜態代碼塊放在構造方法里面了。順序是由上往下排,所以結果還是4.6而已。
- 為什么可以向前引用呢?那是因為在類初始化的時候,類變量是優先與實例變量初始化的,所以才可以向前引用。
- name=“給我丶鼓勵”,為什么呢?因為這里就很好地解釋了初始化其實是兩步。變量先初始化默認的值,例如int 是0,然后再把變量給賦值。所以name=null,然后name="我愛java",然后name="給我丶鼓勵"。
2. 父,子類實例變量和內存分配機制
2.1 繼承的初始化
- 先看代碼
class father{
int age=44;
//構造方法
father(){
System.out.println("父類的構造輸出"+this.age);
this.say();
}
public void say(){
System.out.println("父類的say"+this.age);
}
}
class son extends father{
int age=33;
//構造方法
son(){
System.out.println("子類構造方法");
}
public void say(){
System.out.println("子類的say:"+this.age);
}
}
public class second{
public static void main(String[] ages){
son myson=new son();
}
}
- 你覺得會輸出什么?
- 答案是
父類的構造輸出44
子類的say:0
子類構造方法
- 為什么同一個this。第一行輸出的是父類的44而第二行是子類的方法,而且是0呢? 這個首先涉及繼承的初始化問題,以及繼承變量的方法和變量的區別了。
- 首先在初始化的時候,是優先把父類給初始化,因為在子類構造函數中,是先調用super()方法,此方法是把父類初始化。所以是整個初始化過程是 fater->son。所以首先輸出的是:父類的構造方法輸出44;
- 為什么第二句是子類的say:0呢?
"因為子類son把父類的say()重寫了,因此,當調用的時候,是調用子類的say()的。"
恩~其實這個說話不正確的。首先需要理解這個this是誰。其實這個this是son。不信?
System.out.println(this.getClass());
輸出結果是:class son 。
所以,this.say()調用的自然是son的say()方法了。
那為什么輸出是0呢?
因為值此時賦值只是到父類,子類的age還沒賦值,還是初始值0呢。
2.2 繼承的變量和方法的區別以及內存控制
- 一句話,變量沒有重寫,方法有重寫。
- 用起來有什么區別呢? 還是剛剛的son類和fater類:
son myson=new son();
father myfather=myson;
System.out.println(myson.age);
myson.say();
System.out.println(myfather.age);
myfather.say();
//將會輸出什么
33
子類的say:33
44
子類的say:33
為什么?因為,變量沒有重寫,方法有重寫。所以父類還是可以調用自己的變量,而方法是調用子類的方法。
細心的同學注意到,為什么
father myfather=myson
明明是new 子類,但卻可以輸出父類的age呢?
- 那是因為在建立子類對象的時候,在劃分地址的時候,也有一塊內存地址是存父類的變量,因此。子類中的super(),也是可以調用父類的變量的值。(可以試試噢~)
3. final 的注意事項
3.1 final方法不能被重寫,只能被程序顯式地賦值一次。
3.2 final最大作用是"宏替換"
3.3 在內部類中的局部變量,需要用final。