1. 程式人生 > >java(三)物件的序列化與static、final關鍵字

java(三)物件的序列化與static、final關鍵字

Java序列化是指把Java物件轉換為位元組序列的過程;而Java反序列化是指把位元組序列恢復為Java物件的過程。java中存有Cloneable介面,實現此介面的類都具有被拷貝能力,比new一個物件要快,拷貝分為淺拷貝和深拷貝,淺拷貝導致物件屬性不徹底。
class Professor 
{
    String name;
    int age;
    Professor(String name,int age)
    {
        this.name=name;
        this.age=age;
    }
}
class student implements Cloneable
{
    String name;
    int age;
    Professor p;
    student(String name,int age,Professor p)
    {
        this.name=name;
        this.age=age;
        this.p=p;
    }
    public Object clone()
    {
        student o=null;
        try
        {
            o=(student)super.clone();
        }
        catch(CloneNotSupportedException e)
        {
            System.out.println(e.toString());
        }
      
        return o;
    }
    public static void main(String[] args)
    {
          Professor p=new Professor("wang",5);
          student s1=new student("zhangsan",1,p);
          student s2=(student)s1.clone();
          s2.p.name="lisi";
         s2.p.age=3;
    System.out.println("name="+s1.p.name+","+"age="+s1.p.age);
    }
}
如程式碼中s2變了,s1也變了,說明他們指向同一個物件,這就是淺複製,關鍵是在clone()方法上,他並不是將物件的所有屬性全拷貝過來,而是選擇性拷貝,拷貝規則為:1)基本型別,只拷貝其值。2)例項物件,拷貝其地址引用,新拷貝物件原物件共用該例項。3)字串,拷貝地址引用,修改時會從字串常量池中新生一個原字串不變。解決辦法在clone裡新建一個物件。但是在如果大量物件都是拷貝生成,每個類都寫一個clone()方法,工程量就很大。序列化實現物件的複製。通過位元組流拷貝物件
public class CloneUtils {
    @SuppressWarnings("unchecked")
    public static <T extends Serializable> T clone(T obj){
        T cloneObj = null;
        try {
            //寫入位元組流
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            ObjectOutputStream obs = new ObjectOutputStream(out);
            obs.writeObject(obj);
            obs.close();
            
            //分配記憶體,寫入原始物件,生成新物件
            ByteArrayInputStream ios = new ByteArrayInputStream(out.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(ios);
            //返回生成的新物件
            cloneObj = (T) ois.readObject();
            ois.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return cloneObj;
    }
}
使用該工具類的物件必須要實現Serializable介面,否則是沒有辦法實現克隆。無須繼承cloneable介面實現clone()方法。Static關鍵字Static代表全域性和靜態的意思,可以修飾成員變數和成員方法,也可修飾程式碼塊。java的記憶體分配有棧和堆,棧中存放基本變數,陣列和物件引用,堆中存放物件,當有static修飾的變數或方法,則會為其分配固定區域切既然是靜態那他作用於內是不變的,刪除了就不會再有。static修飾的變數是為類所共有,不依賴於例項,先於物件而存在,任一例項修改其他例項也會使用修改後的變數值。java程式設計思想中有一句話"static方法就是沒有this的方法。在static方法內部不能呼叫非靜態方法,反過來是可以的。而且可以在沒有建立任何物件的前提下,僅僅通過類本身來呼叫static方法。這實際上正是static方法的主要用途。”這段話雖然只是說明了static方法的特殊之處,但是可以看出static關鍵字的基本作用,簡而言之,一句話來描述就是:方便在沒有建立物件的情況下來進行呼叫(方法/變數)。
static修飾的變數:靜態變數,在類載入時候完成初始化,在記憶體中僅有一個,jvm也只會為他分配一次記憶體,所有例項共享,可通過類名直接訪問。例項變數與例項共存亡。在物件之間共享資料或方便訪問時用靜態變數。非靜態變數是物件所擁有建立物件時初始化存在多個副本,各個物件的副本相互不影響。靜態變數的初始化順序是按照定義的數序進行初始化。靜態方法:可以通過類名直接訪問Math類的所有方法都是static的,不依賴於物件 可以進行訪問,沒有物件那麼靜態方法是沒有this的。靜態方法中不可以訪問非靜態成員方法或變數,但是非靜態成員方法可以訪問靜態/變數。(靜態依賴於類普通依賴於物件的建立)。
程式碼塊:靜態程式碼塊會隨著類的載入一塊執行,而且他可以隨意放,可以存在於該了的任何地方。可以有多個,在類初次被載入時會按照static塊的順序來執行每個塊並且只執行一次。java中的static和c++中的是不一樣的,java中static不會影響到變數或者方法的作用域,能夠影響作用域的只有private、public、protected
public class Test {
	public static void main(String[] args) {
		System.out.println(s.age);//報錯The field s.age is not visible
		System.out.println(s.name);
		
	}
}
class s{
	public static String name="zhangsan";
	private static int age=10;
}
private修飾的原因造成。在靜態方法中沒有this,在非靜態中通過this訪問非靜態成員變數會怎樣
public class Test {
	static int value = 10;
	public static void main(String[] args) {
		new Test().show();
	}
	private void show() {
		int value = 1;
		System.out.println(this.value);
	}
}
結果為10.this代表當前物件,new Test()呼叫show的物件就是new Test()生成的物件。static變數是所有物件共享,show是區域性變數不可能與this關聯,所以輸出10,靜態物件獨立於物件但是仍可以通過物件訪問。java語法規定static不允許修飾區域性變數。
Static也存在一些缺陷。1)它只能呼叫static變數。2)它只能呼叫static方法。3)不能以任何形式引用this、super。

4)static變數在定義時必須要進行初始化,且初始化時間要早於非靜態變數。
無論是變數,方法,還是程式碼塊,只要用static修飾,就是在類被載入時就已經準備好了,也就是可以被使用或者已經被執行,都可以脫離物件而執行。反之,如果沒有static,則必須要依賴於物件例項。
public class Test {
    Person person = new Person("Test");
    static{
        System.out.println("test static");
    }    
    public Test() {
        System.out.println("test constructor");
    } 
    public static void main(String[] args) {
        new My();
    }
}
class Person{
    static{
        System.out.println("person static");
    }
    public Person(String str) {
        System.out.println("person "+str);
    }
}
 
class My extends Test {
    Person person = new Person("My");
    static{
        System.out.println("myc static");
    }    
    public My() {
        System.out.println("my constructor");
    }
}
test static   1、首先載入Test類執行靜態程式碼塊,然後執行My()
my static 2、因沒載入且其繼承Test先載入Test,但已加執行my其靜態方法
person static 3、載入後執行建構函式,生成物件先初始化父類的成員變數,執行new person()但是沒有載入person類,先載入就會執行person的靜態方法
person Test  4、完成成員初始化。
test constructor 5、完成父類構造器完成初始化
person My      6、自身成員變數初始化
my constructor 7/建構函式初始化


final關鍵字final最終的意思,他修飾的部分不會改變,final修飾資料可以看做常量,編譯期常量,永遠不改變,執行期初始化希望他也不變。編譯期的常量在編譯時即可參與運算在類載入完成後不可改變,編譯期常量只能用基本型別,在定義時要初始化。執行期常量可以是基本型別和引用型別,基本型別其值不變,引用型別引用不變,應用物件的內容可以變final修飾方法:最終的方法,不可被繼承更改,使用final修飾方法,是為了鎖定方法,父類的final方法是不能被子類所覆蓋的,也就是說子類是不能夠存在和父類一模一樣的方法的。final修飾類,不能被繼承,final修飾的類成員變數可以是final也可以不是,成員方法預設final。如果final修飾引數,代表該引數不可改變。final和static在一起使用時即可修飾成員變數,也可修飾成員方法。對於成員變數,該變數一旦賦值就不能改變,我們稱它為“全域性常量”。可以通過類名直接訪問。對於成員方法,則是不可繼承和改變。可以通過類名直接訪問。