主頁 > 後端開發 > Java開發面試高頻考點學習筆記(每日更新)

Java開發面試高頻考點學習筆記(每日更新)

2021-04-25 15:01:19 後端開發

Java開發面試高頻考點學習筆記(每日更新)

  • 1.深拷貝和淺拷貝
  • 2.介面和抽象類的區別
  • 3.java的記憶體是怎么分配的
  • 4.java中的泛型是什么?型別擦除是什么?
  • 5.Java中的反射是什么
  • 6.序列化與反序列化
  • 7.Object有哪些方法?
  • 8.JVM記憶體模型
  • 9.類加載機制
  • 10.物件的創建和物件的布局
  • 11.Java的四種參考(強參考、軟參考、弱參考和虛參考)
  • 12.記憶體泄露和記憶體溢位
  • 13.List、Set和Map三者的區別和其底層資料結構
  • 14.創建執行緒的四種方式
  • 15.NIO、AIO和BIO
  • 16.重寫和多載
  • 17.final/finally/finalize與static
  • 18.String、StringBuffer和StringBuilder的區別
  • 19.如果判斷一個物件是否該被回收?
  • 20.垃圾收集演算法
  • 21.Double與Float
  • 22.垃圾收集器
  • 23.執行緒池
  • 24.執行緒同步和執行緒通訊
  • 25.中斷執行緒
  • 26.Synchronized的用法
  • 27.Synchronized的原理
  • 28.Synchronized的四種狀態
  • 29.Synchronized與重入鎖ReentrantLock的區別
  • 30.鎖優化
  • 31.Java設計模式

Java:

1.深拷貝和淺拷貝

記憶體中有堆疊區和堆區,基本型別資料直接存在堆疊中,而參考型別(new出來的)是在堆中存盤,在堆疊中保存堆中的地址,也就是說參考型別中在堆疊中存的不是資料,而是地址,賦值其實就是拷貝,

在基本型別資料賦值的時候,沒有深淺拷貝的區別,因為直接賦予的是資料,

但在參考型別資料賦值的時候,實際上是把原來的地址復制給了新的,并沒有實際復制其中的資料,所以這是一個淺拷貝(拷貝的深度不夠),當使用新的變數操作地址中的值的時候,舊變數對應的值也會發生改變,Java中Objectclone方法默認是淺拷貝,

深拷貝會創造另外一個一模一樣的物件,新物件和原來的物件不共享記憶體,修改新物件不會影響舊物件,

參考文章

2.介面和抽象類的區別

  • 抽象類:被abstract關鍵字修飾,抽象方法也被abstract修飾,只有方法宣告,沒有方法體,

  • 抽象類不能被實體化,只能被繼承

  • 抽象類可以有屬性、方法和構造方法,但是構造方法不能用于實體化,主要用于被子類呼叫

  • 子類繼承抽象類,必須實作抽象類抽象方法,否則子類必須也是抽象類

  • 抽象類中的抽象方法只能是publicprotected

  • 介面:被interface關鍵字修飾,

  • 介面可以包含變數和方法;變數隱式設定為public static final,方法被隱式設定為public abstract

  • 介面支持多繼承,一個介面可以extends多個介面

  • 一個類可以實作多個介面

  • jdk1.8中增加了默認方法和靜態方法:default/static

  • 介面只能是功能的定義,而抽象類既可以為功能的定義也可以為功能的實作,

  • 介面和抽象類都不能被實體化,介面的實作類和抽象類的子類只有實作了介面中/抽象類中的方法才能實體化,

  • 實作介面的關鍵字是implements,繼承抽象類的關鍵字是extends,一個類可以實作多個介面,但一個類只能繼承一個抽象類,

  • 介面強調特定功能的實作,而抽象類強調所屬關系,

參考文章

3.java的記憶體是怎么分配的

記憶體分配分為在堆疊上分配和在堆上分配,大多數都是參考型別,所以堆空間用的較多,

物件根據存活時間分為年輕代、年老代、永久代(方法區)

年輕代:物件被創建時,首先分配在年輕代,年輕代有三個區域:Eden區,survivor 0區和survive 1區,Eden區大多數物件消亡速度很快,Eden是連續的記憶體空間,分配記憶體很快,Eden區滿的時候執行Minor GC,清理消亡物件,將存活的物件放在survivor 0區中,每次執行Minor GC的時候,將剩余存活物件都放在非空的survivor區中,survivor區滿之后,就會清理并轉移到另一個survivor區,也就是說總有一個survivor區是空的,HotSpot虛擬機中默認切換15次之后,仍然存活的物件放在年老代中,


年老代:年老代的空間一般比年輕代大,存放更多的物件,年老代記憶體不足的時候,執行Major GC(Full GC),如果物件比較大的情況,可能直接放在老年代上,有可能出現老年代參考新生代物件的情況,java維護一個512 byte的塊“card table”,記錄參考映射,進行Minor GC的時候直接查card table就可以了,

參考文章

4.java中的泛型是什么?型別擦除是什么?

java源代碼要運行,首先要經過編譯器編譯出位元組碼,位元組碼存盤著能被JVM解釋運行的指令,java的泛型在運行時,無法獲得型別引數的真正型別,因為編譯器編譯生成的位元組碼不包括型別引數的具體型別,
泛型是java 1.5之后引入的,其本質是引數化型別,也就是說變數的型別是一個引數,在使用的時候再指定為具體型別,泛型可以用于類、介面和方法,

public class User<T> {
	
	private T name;
}//泛型實際上就是把型別當作引數傳入了
而型別擦除機制使得Java的泛型實際上是偽泛型,型別引數只存在于編譯期,運行時,JVM并不知道泛型的存在,


public class ErasedTypeEquivalence {
  public static void main(String[] args) {
    Class c1 = new ArrayList<String>().getClass();
    Class c2 = new ArrayList<Integer>().getClass();
 System.out.println(c1 == c2); //代碼輸出是true
  }
}

在C++、C#這些支持真泛型的語言中,它們代表著不同的類,但在JVM看來他們是同一個類,無論何時定義一個泛型,相應的原始型別都會被自動提供,型別變數擦除,并使用其限定型別(無限定的變數用 Object)替換,Java 編譯器是通過先檢查代碼中泛型的型別,然后在進行型別擦除,再進行編譯,當具體的型別確定后,泛型提供了一種型別檢測的機制,只有相匹配的資料才能正常的賦值,否則編譯器就不通過,

參考文章

5.Java中的反射是什么

java反射就是把類中的各個成分映射成一個個java物件,在運行期間,對于任意一個類,都能夠知道這個類的屬性和方法,是一種動態獲取資訊、動態呼叫物件的方法,

  • 優點:動態加載類,提高代碼靈活度
  • 缺點:降低性能,可能引起安全問題

我們使用的Spring/hibernate中使用了反射機制,在使用JDBC連接資料庫使用class.forName()通過反射加載資料庫的驅動程式,
Spring框架的IOC(動態加載管理bean)創建物件,AOP(動態代理)都和反射有關系,

6.序列化與反序列化

  • 序列化:將Java物件轉換成位元組序列的程序,
  • 反序列化:將位元組序列轉換成java物件,

serializable介面是可以進行序列化的標志性介面,僅僅是告訴JVM該類物件可以進行序列化,
先讓需要序列化的類實作serializable介面;序列化物件創建輸出流ObjectOutputStream,然后呼叫writeObject()方法;反序列化物件創建輸入流Obje ctInputStream,然后呼叫readObject()方法,得到一個object物件,最后關閉流,

7.Object有哪些方法?

equals:比較物件是否相等,這里實質是比較地址是否相等,
wait:呼叫wait方法會導致執行緒阻塞,釋放該物件的鎖
notify:呼叫物件的notify方法會隨機解除該物件阻塞的執行緒,該執行緒重新獲取該物件的鎖
notifyAll:喚醒所有正在等待物件的執行緒,全部進入鎖池競爭獲取鎖
wait,notify,notifyAll必須在synchronized方法塊中使用,
toString:轉換為字串表示
getClass:回傳物件運行時類,即反射機制,
hashCode: 物件在記憶體中的地址轉換為int值,

8.JVM記憶體模型

程式計數器(PC register):執行緒執行的位元組碼行號指示器,執行緒私有,唯一一個沒有記憶體超出錯誤的區域,

  • Java虛擬機堆疊:每個執行緒創建時都會創建一個虛擬機堆疊,內部保存一個個堆疊幀,對應每一次方法呼叫,生命周期與執行緒相同,保存方法的區域變數和部分結果,參與方法的呼叫和回傳,如果執行緒請求的堆疊深度大于虛擬機所允許的深度,將拋出StackOverflow例外;如果虛擬機堆疊可以動態擴展,當擴展到無法申請足夠記憶體時拋出OutOfMemoryError例外,
  • 本地方法堆疊:與虛擬機堆疊類似,但只為native方法服務,
  • Java堆:執行緒共享記憶體,用來存放物件實體,是垃圾回收的主要區域,java堆可以處于物理上不連續的記憶體空間中,只要邏輯上連續就可以了,就類似于磁盤空間,如果在堆中沒有記憶體完成實體分配,而且堆也無法再拓展的時候,將會拋出OutOfMemoryError的例外,
  • 方法區:是執行緒共享記憶體,它用于存盤已被虛擬機加載的類資訊等資料,它可以叫做永久代也可以是元空間,在jdk1.8之后,永久代的資料被分配到堆和元空間中,元空間存盤類資訊,字串常量和運行時常量池放入堆中,方法區無法滿足記憶體分配需求時,拋出OutOfMemoryError例外,

JVM調優引數

(1) -Xms:初始化堆記憶體,默認為物理記憶體的六十四分之一
(2) -Xmx: 最大堆記憶體,默認為物理記憶體的四分之一
(3) -Xss:單個執行緒堆疊的大小
(4) -Xmn:設定新生代的大小
(5) -XX:MetaspaceSize:設定元空間大小
(6) -XX:SurvivorRatio:調節新生代eden和S0、S1的空間比例 默認為8:1:1

JVM性能監控工具

(1)jps -l:查看行程號
(2)jstack:java堆疊跟蹤工具 查看死鎖和cpu占用過高的代碼
(3)jinfo -flag查看運行的java程式引數屬性的詳情

9.類加載機制

類加載就是將類的資料從class檔案加載到記憶體,并且進行校驗決議和初始化,形成可以讓虛擬機使用的java型別,

類的生命周期:加載,鏈接,初始化,使用,卸載,

  1. 加載:通過類名獲取二進制位元組流(通過類加載器),把靜態資料結構放在方法區,記憶體中生成對應class物件,作為訪問入口,
  2. 鏈接:確保當前位元組流包含的資訊符合虛擬機要求,正式分配記憶體,設定初始值(僅分配靜態變數),虛擬機將常量池內的符號參考替換成直接參考,
  3. 初始化:按照代碼邏輯,賦予屬性真正的初始值,初始化階段就是執行類構造器方法的程序,
  4. 類加載器:包括啟動類加載器、擴展類加載器和應用程式類加載器,

10.物件的創建和物件的布局

物件創建的方法:

用new陳述句創建

呼叫clone方法,需要實作cloneable介面

反射:class的newInstance()

反序列化:從檔案中獲取一個物件的二進制流,使用ObjectInputStream的readObject方法,

物件創建的程序:

類加載檢查:判斷這個類是不是已經被加載鏈接初始化了,

為物件分配記憶體:如果記憶體規整,虛擬機使用碰撞指標法(指標向空閑區前移物件大小的距離);如果不規整則使用空閑串列法,并發安全:虛擬機維護一個串列記錄哪些記憶體塊可用,再分配的時候從串列中找到一塊足夠大的空間劃分給物件實體,并更新串列內容,

初始化分配的空間:所有屬性初始化為零,保證物件實體欄位在不賦值的時候可以直接用

設定物件頭資訊

執行構造方法初始化

逃逸:方法體內創建的物件,方法體外被其他變數參考過,這樣在方法執行完畢之后,該方法中創建的物件不能被GC回收,開啟逃逸分析之后,如果物件的作用域僅在方法內,那物件可以創建在虛擬機堆疊上,隨方法入堆疊創建,出堆疊銷毀,減少GC回收壓力,

物件的記憶體布局:包含三部分:物件頭,實體資料和對齊填充,

物件頭:運行時資料和型別指標,標記欄位包含hashcodeGC分代年齡鎖狀態標志執行緒持有鎖等資訊類元資料的指標:可以知道這個物件是哪個類的實體,

實體資料:存盤物件真正的資料,也包含父類的資料,

對齊填充:保證物件大小是8位元組的整數倍,


11.Java的四種參考(強參考、軟參考、弱參考和虛參考)

在jdk1.2之前,Java對參考的定義很傳統:如果reference型別的資料中存盤的數值是另一塊記憶體的起始地址,就稱這塊記憶體代表一個參考,

  • 強參考:Java中默認宣告的參考為強參考,只要強參考存在,垃圾回收器永遠不會回收被參考的物件,哪怕記憶體不足,JVM也只會拋出OOM錯誤,不會去回收,
 Object obj = new Object();
  • 軟參考:用于描述一些非必需但仍有用的物件,記憶體足夠的時候,軟參考物件不會被回收,只有在記憶體不足的時候,系統會回收軟參考物件,如果記憶體還是不夠才會拋出OOM例外,這種特性使他往往用于實作快取技術,在
    JDK1.2 之后,用java.lang.ref.SoftReference類來表示軟參考,
  • 弱參考:弱參考的強度比軟參考更弱,無論記憶體是否足夠,只要JVM開始垃圾回收,那些被弱參考關聯的物件都會被回收,在 JDK1.2
    之后,用java.lang.ref.WeakReference來表示弱參考,
  • 虛參考:最弱的參考關系,與其他幾種參考不同,虛參考不會決定物件的生命周期,如果一個物件僅持有虛參考,那么它就和沒有任何參考一樣,任何時期都可能被垃圾回收器回收,虛參考主要用來跟蹤物件被垃圾回收器回收的活動,且必須與參考佇列聯合使用,當垃圾回收器準備回收一個物件的時候,如果發現它還有虛參考,就會在回收物件的記憶體之前,把這個虛參考加入到與之關聯的參考佇列中,

參考文章

12.記憶體泄露和記憶體溢位

  • 記憶體泄漏:一個不再被執行緒所使用的物件或變數還在記憶體中占用空間,
  • 記憶體溢位:程式無法申請到足夠的記憶體,

記憶體泄漏的原因

1.長生命周期的物件持有短生命周期物件的參考,

2.連接未正常關閉,

3.變數作用域設定過大

避免記憶體泄漏

1.避免在回圈中創建物件

2.沒有用的物件盡早釋放

3.慎用靜態變數

4.字串的拼接使用Stringbuffer/StringBuilder

5.增大xmx和xms的值

記憶體溢位的原因

1.加載資料過大

2.死回圈或過多回圈

3.啟動引數中記憶體值設定過小

堆疊溢位

原因:遞回深度過大、區域變數過大

解決:遞回不要太深,區域變數改為靜態變數

如果排查記憶體問題
1.JConsole:能看到記憶體用量的趨勢,確定是否有問題
2.GC日志:能看到年輕代和老年代等區域配置是否合理
3.代碼中列印記憶體使用量
4.分析dump檔案:針對性的看到發生OOM時候的記憶體使用量和執行緒情況

13.List、Set和Map三者的區別和其底層資料結構

List:有序的物件

(1)ArrayList:陣列
(2)Vector:陣列
(3)LinkedList:雙向鏈表

Set:不允許重復的集合

(1)HashSet(無序且唯一):基于HashMap
(2)LinkedHashSet基于HashMap
(3)TreeSet(有序且唯一):基于紅黑樹

Map:使用鍵值對存盤

(1)HashMap:Jdk1.8之前HashMap由陣列+鏈表組成,之后再鏈表長度大于閾值(默認8)時將鏈表轉換為紅黑樹以減少搜索時間,
(2)LinkedHashMap:繼承自 HashMap,所以它的底層仍然是基于拉鏈式散列結構即由陣列和鏈表或紅黑樹組成,另外,LinkedHashMap 在上面結構的基礎上,增加了一條雙向鏈表,使得上面的結構可以保持鍵值對的插入順序,
(3)HashTable:陣列+鏈表組成,陣列是HashMap的主體,鏈表為了解決哈希沖突
(4)TreeMap:紅黑樹
ArrayList、LinkedList、Vector的區別

  • 存盤結構ArrayListVector是基于陣列實作的,而LinkedList是基于雙向鏈表實作的,
  • 執行緒安全性ArrayList不具有執行緒安全性(ArrayList添加元素的操作不是原子操作,可能會出現一個執行緒的值覆寫另一個執行緒添加的值的問題),在單執行緒的環境中,LinkedList也是不安全的,Vector實作了執行緒安全,它大部分的關鍵字都包含synchronized,但效率低,
  • 擴容機制ArrayListVector都是用陣列來存盤,容量不足的時候可以擴容,ArrayList擴容后的容量是之前的1.5倍,Vector默認是2倍,Vector可以設定擴容增量capacityIncrement,可變長度陣列的原理是當元素個數超過陣列長度時,產生一個新的陣列,將原陣列的資料復制到新陣列,再將新元素添加到新陣列中,
  • 增刪改查效率ArrayListVector中,從指定的位置檢索一個物件,或在末尾插入洗掉一個元素時間復雜度都是O(1),但是在其他位置增加和洗掉物件的時間是O(n);LinkedList,插入洗掉任何位置的時間都是O(1),但是檢索一個元素的時間是O(n),

14.創建執行緒的四種方式

繼承Thread類,重寫run方法,繼承Thread類的執行緒類不能再繼承其他父類,

實作Runnable介面,重寫run方法

通過Callable介面和Future介面創建執行緒,執行call方法,有回傳值可以拋例外

執行緒池,前三種的執行緒如果創建關閉頻繁的話會消耗系統資源影響性能,而使用執行緒池可以不用執行緒的時候放回執行緒池,用的時候再從執行緒池取,

15.NIO、AIO和BIO

BIO:傳統的網路通訊模型,同步阻塞IO,服務器實作是一個連接一個執行緒,客戶端有連接請求的時候,服務端就要啟動一個執行緒去處理,執行緒數量可能會爆炸導致崩潰,適用于連接數目小且固定的架構,
NIO:同步非阻塞,服務器實作是一個請求一個執行緒,客戶端發送的連接請求都會注冊到多路復用器上,復用器輪詢到連接有IO請求才啟動執行緒,適用于連接數目多且連接比較短的架構,比如聊天服務器,
AIO:異步非阻塞,用戶行程只需要發起一個IO操作然后立即回傳,等IO操作真正完成之后,應用程式會得到IO操作完成的通知,適用于連接數目多且連接長的架構,

16.重寫和多載

重寫(Override):重寫是子類對父類允許訪問的方法實作程序進行重新撰寫,回傳值和形參都不能改變,重寫的好處是子類可以根據特定需要,定義特定行為,例外范圍可以減少,但是不能拋出新的或更廣的例外,

class Animal{
   public void move(){
      System.out.println("動物可以移動");
   }
}
//加入Java開發交流君樣:756584822一起吹水聊天
class Dog extends Animal{
   public void move(){
      System.out.println("狗可以跑和走");
   }
}
 
public class TestDog{
   public static void main(String args[]){
      Animal a = new Animal(); // Animal 物件
      Animal b = new Dog(); // Dog 物件
//加入Java開發交流君樣:756584822一起吹水聊天
      a.move();// 執行 Animal 類的方法
      b.move();//執行 Dog 類的方法
   }
}

雖然b屬于Animal型別,但是它運行的是Dog類的move方法,因為在編譯階段,只是檢查引數的參考型別,運行時JVM指定物件的型別并運行該物件的方法,
方法重寫規則

  • (1)引數串列和被重寫方法的引數串列必須完全相同,

  • (2)訪問權限不能比父類中被重寫的方法訪問權限更低,

  • (3)父類的成員方法只能被它的子類重寫,

  • (4)宣告為final的方法不能被重寫;宣告為static的方法不能被重寫,但是能被再次宣告,

  • (5)構造方法不能被重寫,

  • (6)子類和父類在同一個包中,那么子類可以重寫父類中沒有宣告為private和final的方法;如果不在同一個包中,子類只能重寫父類宣告為publicprotected的非final方法,

當需要在子類中呼叫父類的被重寫方法時,使用super關鍵字,

多載(Overload):是在一個類里面,方法名字相同,引數不同的兩個方法,回傳型別可以相同也可以不同,每個多載的方法(或者建構式)必須有一個獨一無二的引數型別串列,常用于構造器多載,

多載規則

(1)被多載的方法必須改變引數串列,
(2)被多載的方法可以改變回傳型別,可以改變訪問修飾符,可以宣告新的或更廣的例外檢查,
(3)方法能夠在同一個類中或者在一個子類中被多載,

public class Overloading {
    public int test(){
        System.out.println("test1");
        return 1;
    }
 
    public void test(int a){
        System.out.println("test2");
    }   
 //加入Java開發交流君樣:756584822一起吹水聊天
    //以下兩個引數型別順序不同
    public String test(int a,String s){
        System.out.println("test3");
        return "returntest3";
    }   
 
    public String test(String s,int a){
        System.out.println("test4");
        return "returntest4";
    }   
 
    public static void main(String[] args){
        Overloading o = new Overloading();
        System.out.println(o.test());
        o.test(1);
        System.out.println(o.test(1,"test3"));
        System.out.println(o.test("test4",1));
    }
}

方法多載和方法重寫是java多型的不同表現,
參考文章

17.final/finally/finalize與static

  • final:java中的關鍵字,修飾符,如果一個類被宣告為final,就意味著它不能再派生出新的子類,不能作為父類被繼承,一個類不能被同時宣告final和abstract抽象類,如果變數或方法被宣告為final,就能保證它們在使用中不被改變,變數必須在宣告時賦值,以后的參考中只讀,被宣告final的方法只能使用,不能多載,
  • finally:java的一種例外處理機制,java例外處理模型的最佳補充,finally結構使代碼總會執行,而不管有無例外發生,使用finally可以維護物件的內部狀態,清理非記憶體資源,在關閉資料庫連接時,如果把資料庫連接的close()方法放到finally中,就會減少出錯的可能,
  • finalize:Java中的一個方法名,該方法是在垃圾收集器將物件從記憶體中清除出去前,做必要的清理作業,這個方法是由垃圾收集器確定這個物件沒被參考的時候呼叫的,它在Object類中定義,因此所有類都繼承了它,子類可以覆寫該方法來整理資源和清理,
  • static:static修飾的屬性在編譯器初始化,初始化之后能改變,final修飾的屬性可以在編譯器也可以在運行期初始化,但是不能被改變;static不能修飾區域變數,但是final可以,

18.String、StringBuffer和StringBuilder的區別

String是java編程中廣泛使用的,但它的底層實作實際是一個final型別的字符陣列,其中的值不可變,每次對String進行操作就會生成一個新物件,造成記憶體浪費,

private final char value[];

StringBuffer/StringBuilder:它們的底層是可變的字符陣列,都繼承AbstractStringBuilder抽象類,所以在進行頻繁的字串操作的時候,盡量使用這兩個類,它們的區別是:StringBuilder是執行緒不安全的,但執行速度較快;StringBuffer執行緒安全,但執行速度慢,StringBuffer使用synchronized關鍵字進行同步鎖,
另外,String型別的比較,“==”是比較兩個記憶體地址是否一樣,而“equals”是比較兩個字串的值是不是一樣的,
參考文章

19.如果判斷一個物件是否該被回收?

參考計數演算法:為物件增加一個參考計數器,當物件增加一個參考的時候+1,參考失效-1,參考計數為0的物件可以被回收,但是當兩個物件回圈參考的情況下,計數器永遠不為0,因此JVM不使用參考計數演算法,
可達性分析演算法:以GC Roots為起點開始搜索,可達的物件都是存活的,不可達的物件可以被回收,JVM使用該演算法進行判斷,GC Roots中包含:虛擬機堆疊中參考的物件、本地方法堆疊中參考的物件,方法區中靜態成員或常量參考的物件,


20.垃圾收集演算法

標記-清除演算法(Mark-Sweep)

標記階段:標記的程序實際上就是可達性分析演算法程序,遍歷GC Roots物件,可達的物件都做好標記,在物件的header中將其記錄為可達,

清除階段:對堆進行遍歷,如果發現有某個物件沒有可達物件標記,則回收,

缺點:兩次遍歷,效率低;GC運行時需要停止整個程式;產生大量的碎片,需要維護一個空閑串列,

復制演算法(Copying)
物件在Survivor區每經歷一次Minor GC,就將物件年齡+1,當物件年齡達到某個值時,物件復制到老年代,默認為15,JVM中EdenSurvivor區的默認比例為8:1:1,保證記憶體利用率為90%,如果每次回收有多于10%的物件存活,Survivor空間可能就不夠用了,此時借用老年代空間,

缺點:復制收集演算法在物件存活率高的時候需要進行很多的復制操作,效率會變低,老年代一般不會用該演算法,

標記-整理演算法

第一階段和標記-清楚演算法一樣,第二階段將所有存活的物件壓縮到記憶體的另一端,按順序排放,之后,清理邊界外所有的空間,
缺點:效率不高,不僅要標記存活物件,還要整理所有存活物件的參考地址;移動程序中,要全程暫停用戶應用程式,
分代收集演算法

新生代:使用復制演算法,因為大量物件需要回收,
老年代:回收的物件很少,所以采用標記清除或者標記整理演算法,

21.Double與Float

java語言支持兩種基本的浮點型別:floatdouble,32位浮點數float用1位表示符號,8位表示指數,用23位表示尾數;64位浮點數double用一位表示符號,11位表示指數,52位表示尾數,在表示超過23位的時候,float就會自動四舍五入,這就是float的精度限制,所以會出現double可以表示而float會不精確的情況,如果要將這兩個浮點數進行轉型,java提供了Float.doubleValue()Double.floatValue()方法,使用這個方法在單精度轉雙精度的時候,會出現偏差,
浮點運算很少是精確的,只要超過精度表示范圍就會產生誤差,

解決方法:可以通過String結合BigDecimal或者通過使用long型別來轉換,

參考文章

22.垃圾收集器

查看默認垃圾收集器:-XX:+PrintCommandLineFlags

  • Serial串行收集器:單執行緒收集器,只使用一個執行緒回收垃圾,需要停掉其他所有執行緒,Client模式下默認新生代垃圾收集器,新生代使用復制演算法,老年代使用標記整理演算法,Serial
    Old也作為CMS收集器的后備垃圾收集方案,JVM引數:-XX:+UseSerialGC
  • ParNew收集器:Serial的多執行緒版本,對應的JVM引數:-XX:+UseParNewGC,開啟引數之后,會使用ParNew(新生代)復制演算法+Serial
    Old(老年代)標記整理演算法的組合,Java8之后不再推薦使用這種組合,
  • Parallel scavenge收集器:新生代和老年代都使用并行,Parallel scavenge收集器可以使用自適應調節策略,把基本的記憶體資料設定好,然后設定是更關注最大停頓時間或者更關注吞吐量,給虛擬機設立一個優化目標,JVM引數是:-XX:+UseParallelGC,新生代使用復制演算法,老年代使用標記-整理演算法,
  • CMS收集器:一種以獲取最短回收停頓時間為目標的收集器,JVM引數:-XX:+UseConcMarkSweepGC,使用ParNew(新生代)+CMS(老年代)+Serial
    Old(后備)的收集器組合,優點是并發收集,停頓少,缺點是并發會造成CPU的壓力,而且標記清除演算法會產生大量空間碎片,

(1)初始標記:標記GC Roots能直接關聯到的物件,速度很快,需要停頓,
(2)并發標記:進行GC Roots Trancing的程序,不需要停頓,
(3)重新標記:修正并發標記期間因為用戶程式繼續運作而導致變動的那一部分物件重新進行標記,需要停頓,
(4)并發清除:不需要停頓,

G1垃圾收集器:它使得Eden、Survivor和Tenured等記憶體區域不再連續,而變成一個個大小一樣的region,每個region從1M到32M不等,它不再采用CMS的標記清理演算法,G1整體上使用標記整理演算法,區域上看是基于復制演算法,JVM引數:-XX:+UseG1GC,

降低停頓時間是G1和CMS共同的關注點,但G1除了追求低停頓外,還能建立可以預測的停頓時間模型,能讓使用者明確指定在一個長度為M毫秒的時間片內,是因為G1收集器在后臺維護了一個優先串列,每次根據允許的收集時間,優先選擇回收價值最大的region,
另:JVM設定引數的方法(win10):環境變數中新建變數JAVA_OPTS,在里面設定,

23.執行緒池

我們使用執行緒的時候去創建一個執行緒,這種方法非常簡便,但是會導致一個問題:如果并發的執行緒數量很多,并且每個執行緒都是執行一個時間很短的任務就結束了,這樣頻繁的創建執行緒會大大降低系統效率,


Java中引入了執行緒池來使得執行緒可以復用,執行完一個任務不會被立刻銷毀,而是可以繼續執行其他任務,

ThreadPoolExecutor類是執行緒池技術最核心的類:

其構造器中的引數意義

  • corePoolSize:核心池大小,在創建執行緒池之后,默認執行緒池中是沒有執行緒的,除非呼叫prestartAllCoreThreads()或者prestartCoreThread()方法來預創建執行緒,就是沒有任務到來之前先創建corePoolSize個執行緒,當執行緒池中的執行緒數目到達corePoolSize個之后,就會把到達的任務放到快取序列中,
  • maximumPoolSize:非常重要的引數,表示執行緒池中最多能創建多少個執行緒,
  • keepAliveTime:表示執行緒沒有任務執行時最多保持多久會終止,
  • unit:引數keepAliveTime的時間單位,
  • workQueue:阻塞佇列,用來存盤等待執行的任務,會對執行緒池的運行程序產生重大影響,有三個選擇:ArrayBlockingQueueLinkedBlockingQueueSynchronousQueue,一般使用后兩者,
  • threadFactory:執行緒工廠,主要用來創建執行緒,
  • handler:表示拒絕處理任務的策略,有四種取值:

(1)ThreadPoolExecutor.AbortPolicy:丟棄任務拋出RejectedExecutionException例外;
(2)ThreadPoolExecutor.DiscardPolicy:丟棄任務,不拋例外
(3)ThreadPoolExecutor.DiscardOldestPolicy:丟棄佇列最前面的任務,然后重新嘗試執行任務(重復該程序)
(4)ThreadPoolExecutor.CallRunsPolicy:由呼叫執行緒處理該任務

ThreadPoolExecutor類的方法

execute()submit():都是提交任務,execute方法用于提交不需要回傳值的任務,無法判斷任務是不是被執行緒池執行成功;submit提交需要回傳值的任務,執行緒池回傳future型別的物件以判斷是否執行成功,future物件具有的get()方法可以獲取回傳值,`

shutdown()shutdownNow():都是關閉執行緒池,他們的原理是遍歷執行緒池中的作業執行緒,然后逐個呼叫執行緒的interrupt方法來中斷執行緒,所以無法回應中斷的任務可能永遠無法終止,shutdownNow首先將執行緒池的狀態設定成STOP,然后嘗試停止所有正在執行或者暫停的執行緒,并回傳等待執行任務的串列;shutdown只是將執行緒池的狀態設定為SHUTDOWN,然后中斷所有沒有執行任務的執行緒,

如何合理分配執行緒池的大小:CPU密集型任務,一般公式為:最大執行緒數 = CPU核數+1;IO密集型的最大執行緒數 = CPU核數 * 2;

實作一個執行緒池:

public class Test {
     public static void main(String[] args) {   
         ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 200, TimeUnit.MILLISECONDS,new ArrayBlockingQueue<Runnable>(5));
          
         for(int i=0;i<15;i++){
            MyTask myTask = new MyTask(i);
            executor.execute(myTask);
            System.out.println("執行緒池中執行緒數目:"+executor.getPoolSize()+",佇列中等待執行的任務數目:"+
            executor.getQueue().size()+",已執行完別的任務數目:"+executor.getCompletedTaskCount());
         }
         executor.shutdown();
     }
}

執行緒池不允許使用Executors的靜態方法創建,必須通過ThreadPoolExecutor,

執行緒池的處理流程
當執行緒池提交一個任務的時候:
(1)執行緒池判斷核心執行緒池中的執行緒是不是都在執行任務,如果不是則創建一個新的作業執行緒執行任務,否則進入流程(2)
(2)執行緒池判斷作業佇列是否已滿,如果沒有滿則將新提交的任務存盤在這個任務佇列中,如果作業佇列滿了,則進入流程(3)
(3)執行緒池判斷池中的執行緒是否都處在作業狀態,如果沒有則創建一個新的作業執行緒來執行任務,如果已經滿了就交給拒絕策略(handler)來處理任務,
參考文章

四種執行緒池:
(1)newCachedThreadPool 創建一個可以快取的執行緒池,
(2)newFixedThreadPool 創建一個定長執行緒池,可以控制執行緒最大并發數,
(3)newScheduledThreadPool 創建一個定長執行緒池,支持定時和周期性任務執行,
(4)newSingleThreadExecutor 創建一個單執行緒化的執行緒池,他只會用唯一的作業執行緒來執行任務,保證所有任務按照指定順序執行,

//可以快取的執行緒池
ExecutorService cachedThreadPool = Executors.newCachedThreadPool(); //需要指定長度
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);

詳細實作代碼

24.執行緒同步和執行緒通訊

執行緒同步的五種方式:synchronized的關鍵字修飾方法、靜態資源或者代碼塊;Lock(必須放在try-catch-finally中執行,finally釋放鎖以防止死鎖);waitnotify,必須在synchronized范圍內,被synchronized鎖住的物件就是wait和notify的呼叫物件;CAS;信號量(Semaphore),

執行緒通訊的方式:

  • (1)wait()、notify()、nofityAll():等待/通知機制,執行緒A呼叫了物件O的wait方法進入等待狀態,另一個執行緒B呼叫了物件O的notifynotifyAll方法,執行緒A收到通知之后,從物件O的wait方法中回傳執行后續操作,呼叫物件的wait方法會導致執行緒阻塞,釋放該物件的鎖;呼叫物件的notify方法會隨機解除該物件阻塞的執行緒,該執行緒重新嘗試獲取該物件的鎖;從wait方法回傳的前提是獲得了呼叫物件的鎖;必須在synchronized塊或方法中使用,
  • (2)conditionConditionawait(),signalsingalAll方法代替wait和notifynotify只能隨機喚醒一個執行緒,但是用condition可以喚醒指定執行緒,
  • (3)管道
  • (4)volatile
  • (5)Thread.join:如果一個執行緒執行了Thread.join(),意味著當前執行緒A等待thread執行緒中止之后才從thread.join()回傳,

25.中斷執行緒

呼叫一個執行緒的interrupt()方法來中斷執行緒,如果該執行緒處于阻塞、限期等待或者無限期等待狀態,那么就會拋出InterruptedException,從而提前結束該執行緒,

如果執行緒的run()執行一個死回圈,并且沒有執行sleep()等會拋出InterruptedException的操作,那么呼叫interrupt()方法無法使執行緒提前結束,但是呼叫interrupt方法會設定執行緒的中斷標記,此時呼叫Thread.interrupted()Thread.currentThread().isInterrupted()方法會回傳true,因此可以在回圈體中使用interrupted()方法判斷執行緒是否處于中斷狀態,從而提前結束執行緒,

26.Synchronized的用法

執行緒安全是Java并發編程中的重點,造成執行緒安全問題主要有兩個原因:一是存在共享資料,二是存在多條執行緒共同操作共享資料,因此,當存在多個執行緒操作共享資料的時候,需要保證同一時刻有且只有執行緒在操作共享資料,其他執行緒必須等到該執行緒處理完才能進行,這種方式叫做互斥鎖,Java中,關鍵字synchronized可以保證在同一時刻,只有一個執行緒可以執行某個方法或者某個代碼塊,同時它還可以保證一個執行緒(共享資料)的變化被其他執行緒所看到(可見性保證,完全可以替代Volatile功能)

synchronized是Java的關鍵字,是一種同步鎖,

Java的內置鎖(synchronized):每個java物件都可以用做一個實作同步的鎖,這些鎖稱為內置鎖,執行緒進入同步代碼塊或方法的時候會自動獲得該鎖,退出同步代碼塊的時候會釋放該鎖,獲得內置鎖的唯一途徑就是進入鎖保護的同步代碼塊/方法,

Java的物件鎖和類鎖:在鎖的概念上與內置鎖一致,但物件鎖是用于物件實體方法或物件實體上的,類鎖是用于類的靜態方法或者一個類的class物件上的,

Java中每個物件都有一把鎖和兩個佇列,一個佇列用于掛起未獲得鎖的執行緒,一個佇列用于掛起條件不滿足而等待的執行緒,synchronized實際上是一個加鎖和釋放鎖的集成,JVM負責跟蹤物件被加鎖的次數,如果一個物件被解鎖,計數歸零,執行緒第一次給物件加鎖的時候,計數變成1,每當這個相同的執行緒在此物件上獲得鎖的時候,計數就會遞增,每當任務離開一個synchronized方法,計數就會遞減,為0的時候鎖被完全釋放,

Synchronized有三種應用方式:

修飾一個實體方法:被修飾的方法稱為實體同步方法,其作用范圍是整個方法,鎖定的事該方法所屬的物件(呼叫該方法的物件),所有需要獲得該物件鎖的操作都會對該物件加鎖,

  public synchronized void method(){}
  //等同于
  public void method(){
    synchronized(this){
    }
  }

如果一個物件有多個synchronized方法,只要一個執行緒訪問了其中的一個synchronized方法,其他執行緒不能同時訪問這個物件中任何一個synchronized方法,
當一個物件O1在不同的執行緒中執行這個同步方法的時候,會形成互斥,但是O1物件所屬類的另一物件O2是可以呼叫這個被加了synchronized關鍵字的方法的,其他執行緒呼叫O2中的相同方法時不會造成同步阻塞,程式可能在這種情況下擺脫同步機制的控制,造成資料混亂,注意:

  • (1)synchronized關鍵字不會被繼承:子類覆寫父類帶synchronized方法的時候,必須也要給子類的這個方法顯式的增加synchronized關鍵字,
  • (2)定義介面的時候不能使用synchronized關鍵字,
  • (3)構造方法不能使用synchronized關鍵字,但可以使用synchronized代碼塊完成同步,

修飾一個靜態方法:被修飾的方法被稱為靜態同步方法,其作用域是整個靜態方法,鎖是靜態方法所屬的類,

 public synchronized static void method(){}
修飾代碼塊:被修飾的代碼塊被稱為同步陳述句塊,synchronized的括號中必須傳入一個物件作為鎖,作用范圍是大括號中的代碼,鎖是synchronized括號中的內容,可以分為類鎖和物件鎖

//鎖物件為實體物件
 public void method(Object o){
  synchronized(o){
   ...
  }
 }//加入Java開發交流君樣:756584822一起吹水聊天
//鎖物件為類的Class物件 
 public class Demo{
   public static void method(){
      synchronized(Demo.class){
       ...
      }
   }
 }

27.Synchronized的原理

實際上是通過monitor(監視器),Java中的同步代碼塊是使用monitorentermonitorexit指令實作的,其中monitorenter指令插入到同步代碼塊的開始位置,monitorexit指令插入同步代碼塊的結束位置,

JVM保證這兩個指令成對出現,
當執行monitorenter指令的時候,執行緒試圖獲取鎖也就是獲取monitor物件的所有權,當計數器為0的時候就可以成功獲取,獲取后將計數器加一,在執行monitorexit指令之后,將鎖計數器減一,表明鎖被釋放,
synchronized修飾方法的時候,沒有monitorentermonitorexit指令,取而代之的是ACC_SYNCHRONIZED標識,這個標識指明這個方法是一個同步方法,

28.Synchronized的四種狀態

無鎖–>偏向鎖–>輕量級鎖–>重量級鎖(程序不可逆)

偏向鎖:大多數情況下,鎖不存在多執行緒競爭,總是由同一執行緒多次獲得;如果一個執行緒獲得了鎖,鎖進入偏向模式,此時物件頭的Mark Word結構也變為偏向鎖結構,

物件頭在第十章節中提到過,另外這篇文章講的更詳細,
當該執行緒再次請求鎖的時候,只需要檢查Mark Word鎖標記為是否為偏向鎖,以及當前執行緒ID是不是等于Mark Word的Thread Id即可,省去了大量有關鎖申請的操作,
偏向鎖只適用于只有一個執行緒訪問同步塊的場景,


輕量級鎖:當鎖是偏向鎖的時候,被另外的執行緒所訪問,偏向鎖就會升級為輕量級鎖,其他執行緒會通過自旋的形式嘗試獲取鎖,不會阻塞,從而提高性能,適用于追求回應時間,同步快執行速度非常快的情況,

代碼在進入同步塊的時候,如果同步物件鎖狀態是無鎖,虛擬機首先在當前執行緒的堆疊幀中創建鎖記錄(Lock Record)空間,拷貝物件頭的Mark Word復制到鎖記錄中,

之后虛擬機使用CAS操作嘗試將物件的Mark Word更新為指向Lock Record的指標,并將Lock Record的owner指標指向物件的Mark Word,如果這個動作成功了,那么這個執行緒就有了該物件的鎖,物件的鎖標記為設定為“00”,說明處于輕量級鎖定狀態,

如果這個動作失敗了,JVM檢查物件的Mark Word是否指向當前執行緒的堆疊幀,是則說明當前執行緒已經擁有了這個物件的鎖,否則說明多個執行緒競爭鎖,

如果有兩個以上的執行緒競爭同一個鎖,輕量級鎖不再有效,膨脹為重量級鎖,


重量級鎖:多執行緒情況,執行緒阻塞回應時間緩慢,頻繁的釋放獲取鎖會帶來巨大的性能損耗,適用于追求吞吐量,同步快執行速度較長的情景,

29.Synchronized與重入鎖ReentrantLock的區別

相對與ReentrantLock而言,synchronized鎖是重量級的,而且是內置鎖,意味著JVM可以對synchronized鎖做優化,
在synchronized鎖上阻塞的執行緒是不可中斷的,而ReentrantLock鎖實作了可中斷的阻塞,

synchronized鎖釋放是自動的,而ReentrantLock需要顯式釋放(在try-finally塊中釋放)\

執行緒在競爭synchronized鎖的時候是非公平的:如果synchronized鎖被執行緒A占有,執行緒B請求失敗,被放入佇列中,執行緒C此時來請求鎖,恰好A在此時釋放了,執行緒C會跳過佇列中等待的執行緒B直接獲得這個鎖,但是ReentrantLock可以實作鎖的公平性,

synchronized鎖是讀寫和讀讀都互斥,ReentrankWriteLock分為讀鎖和寫鎖,讀鎖可以同時被多個執行緒持有,適合于讀多寫少的并發場景,

ReentrantLock只能鎖代碼塊,但是synchronized可以鎖方法和類,ReentrantLock可以知道執行緒有沒有拿到鎖,但是synchronized不行,

有關synchronized的參考文章

30.鎖優化

在28章節中,我們提到過重量級鎖,在重量級鎖中,JVM會阻塞未獲取到鎖的執行緒,在鎖被釋放的時候喚醒這些執行緒,阻塞和喚醒依賴于作業系統,需要從用戶態切換到內核態,開銷很大,monitor呼叫了OS底層的互斥量(mutex),切換成本很高,因此JVM引入了自旋的概念,

自旋鎖與自適應自旋鎖,CAS實作:

  • 自旋鎖:很多情況下,共享資料的鎖定狀態持續時間短,切換執行緒不值得;通過讓執行緒執行忙回圈等待鎖的釋放,不讓出CPU,缺點是如果鎖被其他執行緒長時間占用,帶來很多開銷,
  • 自適應自旋鎖:自旋的次數不固定,由前一次在同一個鎖上的自旋時間和鎖的擁有者狀態來決定,
  • 優點:自旋鎖不會使執行緒狀態發生改變,一直處于用戶態,不會使執行緒阻塞,執行速度快,
  • CAS(Compare And Swap) 樂觀鎖與悲觀鎖:synchronized操作就是悲觀鎖,這種情況執行緒一旦得到鎖,其他需要鎖的執行緒就掛起的情況是悲觀鎖;CAS操作實際上是樂觀鎖,每次不加鎖而是假設沒有沖突而去完成某項操作,如果失敗了就重試,直到成功為止,悲觀在認為程式中的并發情況嚴重,樂觀在于并發情況不那么嚴重,可以多次嘗試,
  • 鎖消除:虛擬機在即時編譯器運行時,對一些代碼上要求同步而被檢測到實際不可能存在共享資料競爭的鎖進行消除,依據是:JVM會判斷一段程式中的同步明顯不會逃逸出去從而被其他執行緒訪問,JVM就把它們當作堆疊上的資料對待,認為這些資料是執行緒獨有的,
  • 鎖粗化:在加同步鎖的時候,我們盡量的把同步塊的作用范圍限制到盡量小的范圍,但是如果存在一連串的操作都對同一個物件反復加鎖解鎖,甚至加鎖出現在回圈體內,即使沒有執行緒競爭,頻繁的進行互斥同步也會導致消耗,
public static String test04(String s1, String s2, String s3) {
        StringBuffer sb = new StringBuffer();
        sb.append(s1);
        sb.append(s2);
        sb.append(s3);
        return sb.toString();
    }

上述連續的append操作就屬于這類情況,jvm檢測到一連串操作都是對同一個物件加鎖,就會把鎖同步范圍擴展(粗化)到整個一系列操作的外部,使得一連串append操作只需要加一次鎖就可以了,

31.Java設計模式

設計模式是一套被反復使用,多數人知曉的,經過分類編目的,代碼設計經驗的總結,使用設計模式是為了可重用代碼,讓代碼更容易被他人理解,實際上就是在某些場景下,針對某類問題的某種通用的解決方案,
設計模式分為三類:

  • (1)創建型模式:物件實體化的模式,創建型模式用于解耦物件的實體化程序,包括單例模式、簡單工廠、抽象工廠等,
  • (2)結構型模式:把類和物件結合在一起形成一個更大的結構,包括配接器模式、組合模式、裝飾模式等,
  • (3)行為型模式:類和物件如何互動、及劃分責任和演算法,包括模板模式、解釋器模式、觀察者模式等,

單例模式:屬于創建型模式,主要有三種寫法:懶漢式、餓漢式和登記式,

單例模式的特點:

  • (1)單例類只能有一個實體
  • (2)單例類必須自己創建自己的唯一實體
  • (3)單例類必須給所有其他物件提供這一實體

懶漢式:在第一次呼叫的時候就實體化自己,

  public class Singleton{
    private Singleton(){}
    private static Singleton single = null;
    //靜態工廠方法
    private static Singleton getInstance(){
      if(single == null) single = new Singleton();
    }
    return single;
  }

懶漢式并不考慮執行緒安全問題,所以他是執行緒不安全的,并發情況下很可能出現多個Singleton實體,要實作執行緒安全,有以下三個方式:

getInstance方法上加同步關鍵字:在并發環境下,多個一起進入getInstance里,因為還沒有實體化單例模式,single都是null,就會創建多個Singleton實體化物件,破壞了單例模式想要的結果,我們可以在getInstance方法上加synchronized鎖,

 public static synchronized Singleton getInstance(){
   if(single == null) single = new Singleton();
   return single;
 }

雙重校驗鎖定:

 public static Singleton getInstance(){
  if(singleton == null){
    synchronized (Singleton.class){
       if(singleton == null) singleton = new Singleton();
    }
  }
  return singleton;
 }

雙重校驗鎖定的單例仍然需要再加上volatile確保執行緒安全,

靜態同步類:即實作了執行緒安全,又避免了同步帶來的性能影響,

 public class Singleton{
   private static class LazyHolder{
     private static final Singleton INSTANCE = new Singleton();
   }
   private Singleton(){}
   public static final Singleton getInstance(){
     return LazyHolder.INSTANCE;
   }
 }

餓漢式:餓漢式在類創建的同時就已經創建好了一個靜態的物件供系統使用,以后不再改變,所以天生是執行緒安全的,

  public class Singleton1{
    private Singleton1(){}
    private static final Singleton1 single = new Singleton1();
    //靜態工廠方法
    public static Singleton1 getInstance(){
       return single;
    }
  }

餓漢就是類一旦加載,就把單例初始化完成,保證getInstance的時候,單例已經存在了;而懶漢比較懶,只有用戶呼叫getInstance的時候,才會初始化這個實體,

總結

生命不止堅毅魚奮斗,有夢想才是有意義的追求
給大家推薦一個免費的學習交流君樣:756584822
最后,祝大家早日學有所成,拿到滿意offer,快速升職加薪,走上人生巔峰,

Java開發交流君樣:756584822

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/279980.html

標籤:java

上一篇:破解class檔案的第一步:深入理解JAVA Class檔案

下一篇:Java類和物件(重點詳解)

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more