java final的使用

1、如果一個數據既是static又是final,那麼它會擁有一塊無法改變的存儲空間。

2、final data: 當final用於基本數據類型時,final讓其值(value)保持不變,但是當用於object reference時,final僅讓reference保持不變。也就是說當reference一旦被初始化用於代表某個對象時,便再也不能改變指向另一個對象,但對象本身的內容確實可以改變的。final對array的作用和對reference的作用一樣。參考以下例子:

public class Test1{
    private final int li_int=12;
    private final InClass inClass1=new InClass(5);
    private final InClass inClass2=new InClass(8);
    public void modifiedFinal(int a){
    //下面語句出現編譯錯誤,不能修改final基本類型的值
    //li_int = a;
    //下面語句出現編譯錯誤,不能將已經初始化的final變量指向另一個對象
    //inClass1=inClass2;
    //下面語句成功,雖然引用不能改變但final變量引用的對象本身內容是可以改變的
    inClass1.mod(a);
}
class InClass{
  int li_a=0;
  public InClass(int a){
   li_a=a;
  }
  public int mod(int b){
   li_a=b;
   return li_a;
  }
}
public static void main(String args[]){
  Test1 test1=new Test1();
  test1.modifiedFinal(100);
  System.out.println(test1.inClass1.li_a);
}
}

3、blank finals:java允許將數據成員聲明爲final,卻不賦初值。但是,blank finals必須在使用之前初始化,且必須在構造函數中初始化。請參考以下例子:

public class Test2{
//final變量一開始允許不賦值
private final int li_int;
public Test2(int a){
  //下面語句編譯通過,對定義爲空的final變量的賦值必須在構造方法中進行,而且必須要賦值,不賦值也報錯
  li_int = a;
}
public int mod(int a){
  //下面語句編譯出錯,對定義爲空的final變量的賦值必須在構造方法中進行
  //li_int = a;
  return li_int;
}
}

4、final arguments: 聲明arguments爲final,可以保證該argument不能再被指向它處,當argment是基本數據類型時,就意味着值不能改變。參考以下例子:

public class Test3{
private  int li_int=12;
private  InClass inClass1=new InClass(5);
private  InClass inClass2=new InClass(8);
public void modifiedFinal(final int a,final InClass in){
  //下面語句出現編譯錯誤,不能修改final基本類型的值
  //a = 15;
  //下面語句出現編譯錯誤,不能將已經初始化的final變量指向另一個對象
  //in=inClass2;
  //下面語句成功,雖然引用不能改變但final變量引用的對象本身內容是可以改變的
  in.mod(a);
}
class InClass{
  int li_a=0;
  public InClass(int a){
   li_a=a;
  }
  public int mod(int b){
   li_a=b;
   return li_a;
  }
}
public static void main(String args[]){
  int a=100;
  //內部類初始化
  Test3 test3=new Test3();
  Test3.InClass in=test3.new InClass(30);
  System.out.println(in.li_a);
  test3.modifiedFinal(a,in);
  System.out.println(in.li_a);
}
}

5、final methods: 可以鎖住該method,不讓繼承類改變其意義(不允許子類覆寫);允許編譯器對此method作爲inline method調用。參考以下例子:

public class Test4{
private final int li_int=0;
public final int pub_fi_mod(){
  return li_int;
}
protected final int pro_fi_mod(){
  return li_int;
}
private final int pri_fi_mod(){
  return  li_int;
}
private int pri_mod(){
  return li_int;
}
}

public class Test5 extends Test4{
private int li_i=100;
//下面的方法編譯出錯,不能覆蓋final方法,只針對public和protected,子類中方法與父類中private的方法名相同不是覆蓋,與父類中同方法名的方法沒有任何關係(除了名字相同)。
/*
public int pub_fi_mod(){
  return li_i;
}
protected int pro_fi_mod(){
  return li_i;
}*/
private final int pri_fi_mod(){
  return  li_i;
}
private int pri_mod(){
  return li_i;
}
public static void main(String args[]){
  System.out.println(new Test5().pri_mod());
}
}

6、fianl(method) vs private(method): class所有的private methods自然而然都是final,private methods僅僅是隱藏class中的某段程序代碼而已,不能被overrid,即使子類中恰好有同名的method,也不會產生什麼效果;其中兩者的區別是在子類中可以出現與private方法有相同簽名的方法,而public或protected的final方法不能被重寫,但允許方法名相同但參數列表不同的重構方法出現。借用以上例子,將Test5修改後編譯通過:

public class Test5 extends Test4{
private int li_i=100;
//下面的方法編譯出錯,不能覆蓋final方法
/*
public int pub_fi_mod(){
  return li_i;
}
protected int pro_fi_mod(){
  return li_i;
}
*/
//但允許參數列表不同的重構方法出現
public int pub_fi_mod(int a){
  return li_i;
}
protected int pro_fi_mod(int a){
  return li_i;
}
private final int pri_fi_mod(){
  return  li_i;
}
private int pri_mod(){
  return li_i;
}
public static void main(String args[]){
  System.out.println(new Test5().pri_mod());
}
}

7、final classes: 當把一個class聲明爲final時,也就決定了此class將不能被繼承(比如String類,此類爲final類,具體可以參見其實現java.lang.String)。final classes的methods可以是final,也可以是非final的;其中的數據成員可以是final的也可以不是,他們將服從final data的原則。參考以下例子:

public final class Test6{
private final int li_int=0;
public int li_a=123;
public final int mod(){
  return li_int;
}
public int pri_mod(){
  return li_a;
}
public static void main(String args[]){
  System.out.println(new Test6().pri_mod());
}
}

//Test6是final類,所以Test7不能繼承
public class Test7 extends Test6{
private int li_int=0;
}

PS:從以上可以看出,final是將一個對象的地址不變,對基本類型的值保持不變(因爲基本類型變量指向的物理地址存放value而對象變量指向的物理地址存放對象內容的地址)。

PS:以前讀書時老師說java中final定義常量,只說對了一半,對基本類型是對的,對String也是對的,因爲String雖然是對象,但不會出現String變量地址不變而其內容發生改變的情況(String是一個整體不能只改變其中的一個字符),所以也是對的,但對其他的對象只能保持其引用地址不變不能保證其內容不變,所以是錯的。

再補充一些內容:

1、對final屬性在聲明時就賦值,而且賦的值是常量的話,那編譯器會將所有用到此屬性的地方都替換成常量,這個請參考下面的代碼:

package com.xx.dryr.test1;

import java.lang.reflect.Field;

public class Test1Class1{

public final int x = 100;

public int f(Test1Class1 t1c11,Test1Class1 t1c12) throws Exception{

int i = t1c11.x;

System.out.println("i's value is "+i);

changeX(t1c11);

int j = t1c12.x;

System.out.println("j's value is "+j);

return j - i;

}

public static void changeX(Test1Class1 t1c1) throws Exception{

Class clazz = t1c1.getClass();

Field fieldX = clazz.getDeclaredField("x");

fieldX.setAccessible(true);

fieldX.setInt(t1c1, 300);

System.out.println("fieldX's vlaue is "+fieldX.getInt(t1c1));

}

public int test() throws Exception{

return f(this,this);

}

public static void main(String[] args) throws Exception{

Test1Class1 t1c1 = new Test1Class1();

System.out.println(t1c1.test());

}

}

運行結果是:

i's value is 100

fieldX's vlaue is 300

j's value is 100

0

雖然在changeX方法中,已經將x的值修改爲300,但因爲編譯時所有使用到x的地方都使用100替換了,所以在運行時再怎麼修改x的值都不會對使用到x的地方產生影響。

2、否則,對不是在編譯時確定final屬性值的情況下,final屬性的值是可以改變的。請參考如下代碼,對上面的代碼稍微做了修改,讓final屬性x在構造方法中初始化:

package com.xx.dryr.test1;

import java.lang.reflect.Field;

public class Test1Class1{

public final int x ;

public Test1Class1(){

x = 100;

}

public int f(Test1Class1 t1c11,Test1Class1 t1c12) throws Exception{

int i = t1c11.x;

System.out.println("i's value is "+i);

changeX(t1c11);

int j = t1c12.x;

System.out.println("j's value is "+j);

return j - i;

}

public static void changeX(Test1Class1 t1c1) throws Exception{

Class clazz = t1c1.getClass();

Field fieldX = clazz.getDeclaredField("x");

fieldX.setAccessible(true);

fieldX.setInt(t1c1, 300);

System.out.println("fieldX's vlaue is "+fieldX.getInt(t1c1));

}

public int test() throws Exception{

return f(this,this);

}

public static void main(String[] args) throws Exception{

Test1Class1 t1c1 = new Test1Class1();

System.out.println(t1c1.test());

}

}

運行結果是:

i's value is 100

fieldX's vlaue is 300

j's value is 300

200

從上面的例子中可見,final屬性的值還是可以被改變的,但只有在特殊情況下(沒有在編譯時被替換),使用特殊的方式(像反射這樣的方式),final屬性的值纔可以被改變。所以說一般情況下說final屬性的值是不允許被修改的還是可以說的,但必須得知道這些例外情況的。

 

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