參考鏈接: Java中的final最終變量
java對象的序列化
?
?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在一起使用時即可修飾成員變量,也可修飾成員方法。對于成員變量,該變量一旦賦值就不能改變,我們稱它為“全局常量”。可以通過類名直接訪問。對于成員方法,則是不可繼承和改變。可以通過類名直接訪問。