Java 基礎知識(一)

final 關鍵字

使用final關鍵字的三種情況:數據、方法、類

final 數據

final數據的情況:

  • 1、永不會改變的編譯時常量

    • 在編譯的時候編譯器將其帶入到用到它的計算式子中,這可以減少運行時的負擔。
    • JAVA中的這類常量必須是基本數據類型,並且必須使用final關鍵字,必須在使用之前賦值。
    • 一個既是static又是final的域只佔據一段不能改變的內存空間
    • static final和final沒多大區別
    • 帶有final static 的基本類型都有全大寫命名,單詞之間用下劃線連接
  • 2、在運行時被初始化,然後不再被改變

    • 當對對象引用,而不是基本的數據類型時,final使得引用恆定不變,一旦引用初始化指向一個對象,則一直指向該對象,但是具體的對象內容是可以改變。
    • 這樣的限制也適用於數組,數組也是對象。所以用final修飾的數組名,初始化之後就不能在指向其他的數組了,但是可以修改數組中的元素值。
    • static final:在程序進行裝在的時候就會存在某一個指定內存空間中,對於該類的對象來說,用這個兩個關鍵字修飾的變量是唯一的,並不會在類創建的時候創建。
    • final:是在類創建的時候創建,所以同一個類的不同對象的值是不一樣的。

    從上面可以看出,讓基本數據類型成爲final比讓引用成爲final更有意義

空白final

指被申明爲final但是又沒有給定初始值的域。根據前面第1條中知道,必須在使用前對final域進行初始化,所以,在該類的構造器中必須進行初始化。這樣就可以讓一個類中的final域的值根據不同的對象而不一樣。如下面的代碼所示:


 public class BlankFinal {

    private final int i = 1;//常量 初始化
    private final int j;//常量 空final
    private final Peppet peppet;//引用 空final
    //以上兩個變量在沒有進行構造函數初始化的時候會一直報錯

    public BlankFinal(int j) {
        this.j = j;//初始化之後上面的空白final就會沒有出錯了
        this.peppet = new Peppet(j);
    }

    public static void main(String[] args) {
        BlankFinal bf =new BlankFinal();
//        bf.i++;//出錯,不能修改了
        bf.peppet.i = 3;
    }
}

class Peppet {
    public int i;
    public Peppet(int i) {
        this.i = i;
    } 
}

final參數

當指定傳入的參數是final的類型時,如果參數是引用類型,則在方法中不能修改參數所指向的引用,如果是基本數據類型,則不能修改參數的值。代碼如下:

public class FinalParam {

    void with(final Demo demo) {
//        demo = new Demo();//報錯,不能修改
    }

    void without(Demo demo) {
        demo = new Demo();//能修改
    }

    int f(final int i) {
//        return i++;//出錯,i不能改變
        return i+1;
    }
}

class Demo {
    public void test(){}
}

final方法

使用final方法的原因

  • 1、第一原因是把方法鎖定。

    可以在將一個方法見面加上關鍵字final,這樣就可以防止任何繼承類來修改這個方法。這樣可以確保在繼承中使用該方法的行爲不會改變,並且不會被覆蓋。

  • 2、第二個原因是效率問題

    以前可以通過給方法加上final,在編譯器編譯的時候就會將代碼的副本代替到調用的地方,這樣可以在一定程度上減少方法調用帶來的開銷,從而提高效率。

    問題:當方法體很長的時候,這樣就會使得代碼變得很膨脹,可能就會看不到內嵌代碼所帶來的任何性能上的提升。

    結論:在現在的JDK中可以讓編譯器和JVM去處理代碼的效率問題,只有當需要明確的禁止將方法進行覆蓋的時候,纔將該方法設置爲final。

final和private關鍵字

一個類中的private方法實際上都隱式的指定了是final的,由於子類無法獲取到private方法,所以也就無法覆蓋private方法了,如果對一個private方法再添加final關鍵字是不起任何作用的。

覆蓋:只有在某個方法是其基類的接口中的一部分的時候,纔會出現覆蓋。如果某個方法是private,那該方法就不是基類中接口的一部分,這樣的方法不能被子類繼承的,即使子類中有相同方法名的方法,那也只是方法名相同而已。

final類

當定義一個類爲final類的時候,表明該類不允許被繼承,換句話說,該類不會有任何的子類。由於final類是不允許被繼承的,所以final類中所有的方法都被隱式的加上了final關鍵字,final類不會影響該類中其他的域是否被指明爲final。

static關鍵字

應用的場景

  • 1、只爲某一個特定域創建單一的內存空間,不需要考慮創建對象的問題
  • 2、希望某個方法不與包含它的任何對象關聯在一起,即不創建對象也能調用該方法
  • 3、使用static關鍵字修飾靜態代碼塊
  • 4、在使用import導包的時候,在import之後加上static關鍵字,就是靜態導包

使用static

只需要將static放在定義的域或者方面前面,即可以將該方法或者域定義爲static類型。當一個域或者方法被申明爲static的時候,就意味着這個域或者方法不會與包含它的任何對象相關聯。所以,即使從未創建過某個類的對象,也可以調用該類的static域或者方法,在有些地方將這種static的方法或者域成爲類數據和類方法。

在使用static域或者方法的時候,既可以直接使用ClassName.StaticDomainOrMethod,也可以像其他普通的域或者方法一樣,通過對象來進行訪問。

Note:
每一個類的代碼程序代碼在什麼時候被加載?

  • 1、在創建類的第一個對象時會加載該類的程序代碼
  • 2、當訪問該類的靜態域或者靜態方法時也會加載

實際上構造器也是一個static方法,所以更準確的來說,類是在任何static成員被訪問的時候加載的。

繼承和初始化

首先看看如下的代碼:

public class StaticInit extends Insect{

    private int k= printInit("StaticInit.k initialized");

    public StaticInit() {
        System.out.println("k="+k);
        System.out.println("j="+j);
    }

    private static int x2 = printInit("static StaticInit.x2 initialized");

    public static void main(String[] args) {
        System.out.println("StaticInit Constructor");
        StaticInit staticInit = new StaticInit();
    }


}

class Insect {
    private int i=9;
    protected int j;

    public Insect() {
        System.out.println("i="+i+" , j="+j);
        j = 39;
    }

    private static int x1 = printInit("static Insect.x1 initialized");

    static int printInit(String s) {
        System.out.println(s);
        return Math.round(10);
    }
}

流程說明:

  • 1、首先是找到StaticInit.main()方法,於是開始加載StaticInit類,在加載StaticInit的時候,發現該類繼承自Insect類,於是就來到Insect類中開始對該類中static域進行初始化,然後就會到StaticInit類中的static域被初始化。
  • 2、此時兩個類的代碼都已經加載完畢了。此時就來到了main方法裏面,開始打印輸出了『StaticInit Constructor』,然後就開始準備創建StaticInit的對象了。
  • 3、在創建對象之前,對象中的所有的基本數據類型都會被初始化爲默認值,對象的引用會被初始化爲null,然後開始調用基類的構造器,在開始構造基類對象的時候,會從上到下實例化類中的變量i,j(j會給與默認值0),然後進入基類的構造器。
  • 4、在基類構造器完成之後,就會回到子類StaticInit中的構造器,構造的順序跟基類中的構造順序是一樣的。先從上到下實例化k,然後就會進入到構造器中,執行兩個打印語句。此時就完成了StaticInit的對象的構造了,回到main裏,結束運行。

以上代碼的運行結果如下:

static Insect.x1 initialized
static StaticInit.x2 initialized
StaticInit Constructor
i=9 , j=0
StaticInit.k initialized
k=10
j=39

使用靜態代碼塊

在一個類中,可以用static 關鍵字將一個段代碼塊進行包括,這被包括的代碼就會像靜態域一樣進行加載,例如下面的一段代碼,在定義book2和book4的時候並不是static的,但是,由於處於靜態代碼塊中,所以book2和book4依然是用靜態加載的方式進行的加載。

static {
        Book book2 = new Book("static成員book2成員變量初始化");
        Book book4 = new Book("static成員book4成員變量初始化");
    }

靜態導包

就是在導入某一個包的時候,在import後面加上static的關鍵字,就將該包下的所有的類方法(即static方法)都直接導入,此時在訪問所導入的類的類方法的時候,就不需要使用ClassName.methodName的方法了,而是直接使用methodName進行調用即可。

成員初始化

java 會盡量保證在每個變量在使用之前都是已經初始化了狀態,對於方法的局部變量,如果在使用之前沒有進行初始化,這會以編譯器報錯的形式來提醒。如果是類中的數據成員(即類的字段),是基本數據類型的時候,即時沒有給出初始值,但是編譯器會給它們賦予默認值。比如:int 的默認值是0,char的默認值是空的,float和double的默認值都是0.0。

this指針

this指針指的當前這個類的對象的引用,而static變量是不屬於對象的,是屬於類的,所以,this指針是不能訪問靜態的成員變量和方法的。

線程的創建和啓動

線程的創建有兩種方法:

  • 1、通過實現Runnable接口
  • 2、繼承Thread類

在使用Runnable的時候,可以直接通過實現類的run方法運行,也可以將一個Runnable對象傳遞給Thread的構造器,然後調用Thread的對象的start()方法來啓動這個線程。如果用Thread來啓動線程的話,使用的是run方法,這此時的run方法則僅僅是一個普通方法。

繼承和多態

class Base
{
    public void method(){} 
}
class Son extends Base
{
    public void method(){}

    public void methodB(){}
}

如上面,Son是Base的子類,在使用Base base = new Son();的時候,最後base是Base的實例對象,此時base是兺呢個訪問到Son中的methodB方法。

replaceAll注意

replaceAll傳入的兩個參數中,第一個是一個正則表達式,在正則表達式中的”.”表示的是所有的字符,所以,形如下面的替換就會出現問題:

String str = "com.huaxin.";
str = str.replaceAll(".", "/");
System.out.println("str:"+str);

此時打印的是:

str:///////////

所以需要如下更正:

str = str.replaceAll("\\.", "/");

加\的原因是\本身是需要轉義的。

接口和抽象類

抽象類

抽象方法:僅有聲明,沒有方法體的方法。如:

abstract void fo();

包含抽象方法的類稱爲抽象類。

如果繼承一個抽象類,則該類必須爲父抽象類的所有抽象方法提供方法的定義,否則,該類也必須是一個抽象類。

接口

是一個完全的抽象類,不會提供任何具體的實現(抽象類只是包含抽象方法,並不都是抽象方法,所以抽象類中是存在有方法體的方法的,而接口是完全不存在的),接口中也可以包含域,但是這些域是隱式的static和final的類型。如下的代碼:

public class TestInterface {
    public static void main(String[] args) {
        System.out.println(Demo1.j);
    }
}
class Demo1 implements Demo3 {
    @Override
    public void foo() {
        System.out.println(i+"  "+j);
    }  
}
interface Demo3 {
    int i=0;
    int j=1; 
    public void foo();
}

另見:http://www.stevenwash.xin:8988

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章