on java 8 学习笔记 2022.2.16
2022.2.16
問題
其實我感覺引用計數的方法不只書中提到的這種問題吧,難道不會有對象被誤刪的情況嗎?
答:不會,因為這種方法就是參考了只要有引用,它就是有效對象的路子,而只要引用大于0,那它就是有效對象,
這種初始化方法簡單明了,但有個限制:類InitialValues的每個對象都會有相同的初始值。有時候這正是你想要的,但有時你可能需要更大的靈活性。
看不懂,啥意思?我覺得已經挺靈活的了
答:看了6.7,感覺用構造器初始化不就是我們正常的操作嗎?秀了我一臉懵逼,這有啥好靈不靈活的
答:應該是這么寫的話,就相當于直接賦初始值,而構造器靈活的地方可能在于它是方法賦值,可以多樣化賦值,雖然我還是很難說服自己
getClass()方法是Object的一部分,將在第19章中進行全面探討。它會生成一個對象的類,當打印這個類時,你會看到一個表示該類類型的編碼字符串。前導的[表示這是后面緊隨的類型的數組。I表示基本類型int。為了再次確認,我在最后一行創建了一個int數組并打印了它的類型。這證實了使用可變參數列表不依賴于自動裝箱,這個示例實際上用的就是基本類型。
這最后我是沒看懂的,我覺得根本不會有這個誤區,事實上,我都不知道為什么會有人認為他會被包裝,因為你參數列表都是int,你給我裝啥?
答:這里有點繞,其實是因為作者提前輸了1,0到里面去,然后程序輸出的類型是I,可能有讀者腦子沒扭過來,因為這里可能是包裝類,也可能是基本數據類型,所以作者又new了一個int數組進去,這樣就可以確定了,因為數組是基本數據類型的,又是列表的一種,所以它的數據類型不應該變動
以前,你需要創建一組整型常量值,但它并沒有自然地將取值范圍限制在這個集合中,因此風險更大且更難使用。
我……………這又是個啥意思,啥時候的枚舉類型,我從來沒用過,沒經驗啊wc
第六章 初始化和清理
一些更快的方案并不使用引用計數,而是基于這樣一個想法:對于任何沒有被廢棄的對象,最終都能追溯到它存活在棧或靜態存儲區中的引用。這個引用鏈可能會穿過多個對象層次。因此,如果從棧和靜態存儲區開始遍歷所有引用,就能找到所有存活的對象。對找到的每個引用,還要跟蹤它指向的對象,然后跟蹤那個對象中的所有引用,依次反復進行,直到找到了源于這個(位于棧或靜態存儲區的)引用的所有對象。在這個過程中遍歷的每個對象都必須是“活”的。注意,這樣的話,廢棄的自引用對象組就不會產生問題了——它們根本不會被找到,因此自動成為垃圾。
我看了兩遍,然后這種方法其實就是一個引用指向的對象必然不可能是廢棄對象,那么我只需要找到所有的有效引用,然后把它們的對象復制到一個新區里,那么剩下的在原區里的就一定是廢棄對象,把他們全清楚了就完事了
在想一個問題,按這個道理的話,是不是有引用不是在棧或者靜態存儲區里?如果引用都是在棧或者靜態存儲區的話,那么不是應該在遍歷完棧中所有的引用以后,就可以確定存活的對象.
那么按照這個邏輯考慮的話,應該是有引用可能存儲在堆中的,這樣比較符合實際的情況,之所以堆中可能存在引用,是因為堆中的對象里可能有引用了其他的對象
那么按照這個方向思考下去,利用棧和靜態存儲區的思路,就是將原來遍歷一整個堆的操作數縮小到只是遍歷其中部分對象的操作數,確實很奇妙
java垃圾回收是將龐大的內存空間分解成一個個更小單元的塊,以此盡量減少內存空間的使用
有了塊之后,垃圾收集器就可以將對象直接復制到廢棄的塊里。
不知道為什么,我感覺這里有點問題,應該是把對象復制到新的塊中,畢竟,這種算法標記的是存活的對象,而且,把對象復制到廢棄的塊中不合理.那些廢棄塊中的對象本身不就是要刪除的,所以我個人比較認同我在其他里貼出來的博客的說法
注意下初始化對象時,對象內部的變量編譯是在構造器前的,至于具體是編譯時完成的還是運行時完成的,我個人感覺是運行時完成的.
初始化的順序是從靜態字段開始(如果它們還沒有被先前的對象創建觸發初始化的話),然后是非靜態字段。
如果有加載靜態方法,或者使用靜態方法的話,那么本類中的靜態對象都會被全部激活并初始化,如果還有靜態對象,靜態對象也會被初始化
為了總結對象創建的過程,假設有一個名為Dog的類。
注意下創建和初始化是兩碼事,一定要牢記這一點,而也正是這個原因才有了在構造器前的自動初始化.而構造器初始化實際上可以理解成又一次的初始化
在執行類中的非靜態方法時,代碼塊中的語句會先被執行
package example;public class two { } class three extends two{int i=0;{System.out.println("heool");}public void fun(){System.out.println(new three().i);}public static void main(String[] args) {three temp = new three();temp.fun();} }其實這里可以總結一下,實際上有關于方法和構造器初始化,因為二者實際上都是方法,就可以看作是類的內部變量和塊總是要先進行,然后才是方法的運行
這種寫法是合法的
public class DynamicArray {public static void main(String[] args) {Other.main(new String[]{ "fiddle", "de", "dum" });} }class Other {public static void main(String[] args) {for(String s : args)System.out.print(s + " ");} }注意下,字符串都是引用,可以說java里除了基本數據類型就都是引用了,一定要記得這一點
還有這里直接調用main真挺有意思的
如果沒有為自己的類定義toString()方法(這將在本書后面講解),可以看到默認行為就是打印類名和對象的地址。
很有意思,一個什么都能塞的數組,里面東西還都不一樣
還有,把Object后的換成…也是一樣的效果,我這里就不粘貼了
然后還有如果你有數組不是Object[]這樣的,你要強制轉化一下
這里還有一個,就是繼承后再運行時的多態現象,即Object,表現出了A的性質
然后,因為學藝不精,無法理解為什么這樣子無法通過編譯
public class VarArg {static void printArray(Object[] args) {for(Object obj : args)System.out.print(obj + " ");System.out.println();}public static void main(String[] args) {printArray(new Object[]{47, (float) 3.14, 11.11});printArray(new Object[]{"one", "two", "three" });printArray(new Object[]{new A(), new A(), new A()});}class A {@Overridepublic String toString() {return "hello";}} }看了很久,這個可變參數列表貌似就是傳一個數組進去,不過就是從本來‘’類名[]’這樣的形式換了下而已
根據經驗,你應該只在其中一個重載方法上使用可變參數列表,或者壓根兒就不使用它。
我為什么要看這玩意,我吐了
好吧,可變參數列表還是有很奇妙的地方的,因為我們現在可以這么寫了
public class OptionalTrailingArguments {static void f(int required, String... trailing) {System.out.print("required: " + required + " ");for(String s : trailing)System.out.print(s + " ");System.out.println();}public static void main(String[] args) {f(1, "one");f(2, "two", "three");f(0);} }什么意思呢?就是我們輸進去的參數現在理論上可以無限多了
{WillNotCompile}注釋標簽會把該文件排除在本書的Gradle構建之外。
如果你手動編譯它,就會看到如下所示的錯誤消息:
OverloadingVarargs2.java:14: error: reference to f is ambiguous f('a', 'b'); \^ both method f(float,Character...) in OverloadingVarargs2 and method f(Character...) in OverloadingVarargs2 match 1 error如果你給這兩個方法都添加一個非可變參數,就沒有問題了:
// housekeeping/OverloadingVarargs3.javapublic class OverloadingVarargs3 { static void f(float i, Character... args) {System.out.println("first"); } static void f(char c, Character... args) {System.out.println("second"); } public static void main(String[] args) {f(1, 'a');f('a', 'b'); } } /* 輸出: first second */根據經驗,你應該只在其中一個重載方法上使用可變參數列表,或者壓根兒就不使用它。
這里作者有一點沒說清楚,他這么操作實際上就是f()找不到對應的方法,實際上,改成了最后一種你也找不到對應的方法,所以我想作者的意思是,不要偷懶,沒有參數的方法如果要有,你就乖乖的寫去,不要亂整些沒用的騷操作,我重新看了遍,確定了作者應該是寫嗨了
反正總而言之,因為全寫可變參數列表會導致缺少默認方法,所以盡量少寫,不過我覺得倒沒什么,雖然我不用
對于枚舉類型,實際上它的作用就是跳過了字符串,直接將字符串和數字畫上等號,不過我現在能想到的有意思的應用就是在一些特殊情況下,將數組下標轉化成枚舉類型的字符串形式
public enum Spiciness {NOT, MILD, MEDIUM, HOT, FLAMING } public class SimpleEnumUse {public static void main(String[] args) {Spiciness howHot = Spiciness.MEDIUM;System.out.println(howHot);} } /* 輸出: MEDIUM */然后,枚舉類型的使用,你可以聲明對象,然后初始化對象的方式使用枚舉類型,或者你直接類似靜態變量一樣對枚舉類型內的值進行操作,不需要new,這個應該是牽扯到了單例模式
一個遍歷枚舉類型的思路,可以看看
其實感覺枚舉類型有點像接口的靜態變量,不過有一點不同吧,接口的靜態變量不能像這樣直接遍歷過去
怎么說呢,有點雞肋的感覺.簡而言之,這個你不能在類中作為字段使用,不能作為返回值使用,不能用來聲明變量(這聲明本身就沒什么意義,都不知道要分配多少空間)
類型推斷十分適合for循環:
// housekeeping/ForTypeInference.java // {NewFeature} 從JDK 11開始public class ForTypeInference { public static void main(String[] args) {for(var s : Spiciness.values())System.out.println(s); } } /* 輸出: NOT MILD MEDIUM HOT FLAMING */將類型推斷作為基本概念而創建的語言——如Kotlin和Scala——允許在任何可能有意義的地方進行類型推斷,而Java則受到向后兼容性問題的限制。使用這個新功能的最佳方法,可能是在任何你認為可以的地方嘗試它,并讓編譯器或你的IDE來提示是否可以這樣用。
簡單說,我能不用就不用,即便他貌似還挺好用的,不過想了想算了
其他
總結
以上是生活随笔為你收集整理的on java 8 学习笔记 2022.2.16的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: on java 8学习笔记
- 下一篇: on java 8 学习笔记 2022.