JAVA常見問題合集

麪筋分類彙總-測開向

面向對象

  • 面向過程和麪向對象

  • 面向對象的三大基本特徵:封裝、繼承、多態

 - 封裝:隱藏內部細節html

  • 繼承:複用現有代碼java

  • 多態:改寫對象行爲程序員

  • JAVA爲何是面向對象的,爲何還用int等基礎類型

    • 面向對象的特徵:封裝,繼承,多態。JAVA語言符合這些特徵。
    • 由於在每一個封裝體裏,基本仍是面向過程的代碼思想,所以仍是須要這些基礎數據類型。
  • 面向對象軟件開發的優勢有哪些?

    • 更符合人們思考習慣的思想,更多體現的是指揮者,指揮對象作事情,與面向過程的執行者區別;
    • 能夠將複雜問題簡單化,保證了較高的開發效率,保證了程序的魯棒性和可維護性。
    • 易維護、易複用、易擴展,因爲面向對象有封裝、繼承、多態性的特性,能夠設計出低耦合的系統。
    • 代碼開發模塊化,更易維護和修改。
    • 加強代碼的可靠性和靈活性。
    • 增長代碼的可理解性。
  • 一、封裝

    • 封裝:隱藏對象的屬性和實現細節,僅對外公開接口,控制在程序中屬性的讀和修改的訪問級別,將抽象獲得的數據和行爲(或功能)相結合,造成一個有機的總體,也就是將數據與操做數據的源代碼進行有機的結合,造成「類」,其中數據和函數都是類的成員。
    • 封裝的目的:是加強安全性和簡化編程,使用者沒必要了解具體的實現細節,而只是要經過外部接口,以特定的訪問權限來使用類的成員。
    • 面向對象就是使用程序處理事情時以對象爲中心去分析;與面向過程不一樣,面向過程關心處理的邏輯、流程等問題,而不關心事件主體。而面向對象即面向主體,因此咱們在解決問題時應該先進行對象的封裝(對象是封裝類的實例,好比張三是人,人是一個封裝類,張三隻是對象中的一個實例、一個對象)。好比咱們平常生活中的小兔子、小綿羊均可以封裝爲一個類。
  • 封裝的定義和好處有哪些?

    • 一是提升了數據的安全性。用private把類的細節與外界隔離起來,從而實現數據項和方法的隱藏,而要訪問這些數據項和方法惟一的途徑就是經過類自己,類纔有資格調用它所擁有的資源(方法,數據項屬性等等)。
    • 二是提升了代碼的可用性和可維護性。經過隱藏隔離,只容許外部對類作有限的訪問,開發者能夠自由的改變類的內部實現,而無需修改使用該類的那些程序。只要那些在類外部就能被調用的方法保持其外部特徵不變,內部代碼就能夠自由改變,各取所需,利於分工。
    • 三就是提升了代碼的重用性,封裝成工具類之後可以減小不少繁瑣的步驟。
  • 抽象的定義?抽象和封裝的不一樣點?

    • 抽象是把想法從具體的實例中分離出來的步驟,所以,要根據他們的功能而不是實現細節來建立類。Java支持建立只暴漏接口而不包含方法實現的抽象的類。這種抽象技術的主要目的是把類的行爲和實現細節分離開。
    • 抽象和封裝是互補的概念。一方面,抽象關注對象的行爲。另外一方面,封裝關注對象行爲的細節。通常是經過隱藏對象內部狀態信息作到封裝,所以,封裝能夠當作是用來提供抽象的一種策略。
  • 二、繼承

    • 繼承是指:保持已有類的特性而構造新類的過程。繼承後,子類可以利用父類中定義的變量和方法,就像它們屬於子類自己同樣。
      • 繼承是面向對象的基本特徵之一,支持單繼承,不支持多繼承;繼承機制容許建立分等級層次的類。
      • 繼承就是子類繼承父類的特徵和行爲,使得子類對象(實例)具備父類的實例域和方法,或子類從父類繼承方法,使得子類具備父類相同的行爲。
      • 繼承之間是子父類的關係。繼承機制能夠很好的描述一個類的生態,也提升了代碼複用率,在Java中的Object類是全部類的超類,常稱做上帝類。
    • 做用:
      • 繼承提升了代碼複用性,提供了多態的前提。
  • 單繼承和多繼承

    • 只支持單繼承(一個類只能有一個父類),不支持多繼承(多繼承用接口實現)。
    • 單繼承:java類是單繼承的,一個類只容許有一個父類。
      • public class A extends B{ } //繼承單個父類
    • 多繼承:java接口多繼承的,一個類容許繼承多個接口。
      • public class A extends B implements C{ } //同時繼承父類和接口
      • public class A implements B,C{ } //繼承多個接口
    • 支持多重繼承
      • class A {}
      • class B extends A{}
      • class C extends B{}
  • 三、多態

    • 在父類中定義的屬性和方法被子類繼承以後,能夠具備不一樣的數據類型或表現出不一樣的行爲,這使得同一個屬性或方法在父類及其各個子類中具備不一樣的含義。
    • 多態:同一個行爲具備多個不一樣表現形式或形態的能力。
      • 是指一個類實例(對象)的相同方法在不一樣情形有不一樣表現形式。多態機制使具備不一樣內部結構的對象能夠共享相同的外部接口。這意味着,雖然針對不一樣對象的具體操做不一樣,但經過一個公共的類,它們(那些操做)能夠經過相同的方式予以調用。
    • 多態的優勢:
      • 消除類型之間的耦合關係
      • 可替換性
      • 可擴充性
      • 接口性
      • 靈活性
      • 簡化性
    • 多態存在的三個必要條件:
      • 繼承
      • 重寫(子類繼承父類後對父類方法進行從新定義)
      • 父類引用指向子類對象
    • 子類對象的多態性使用前提:
      • 有類的繼承或實現關係;
      • 由子類對父類方法的重寫(覆蓋)
    • 寫一個多態:
 

java基礎

  • Integer和int的區別

    • int則是java的一種基本數據類型,Integer是int的包裝類
    • int的默認值是0,Integer的默認值是null
    • Integer變量必須實例化後才能使用,而int變量不須要
    • Integer實際是對象的引用,當new一個Integer時,其實是生成一個指針指向此對象;而int則是直接存儲數據值
  • java常見的幾種運行時異常RuntimeException

    • NullPointerException - 空指針引用異常
    • ClassCastException - 類型強制轉換異常。
    • IllegalArgumentException - 傳遞非法參數異常。
    • ArithmeticException - 算術運算異常
    • ArrayStoreException - 向數組中存放與聲明類型不兼容對象異常
    • IndexOutOfBoundsException - 下標越界異常
    • NegativeArraySizeException - 建立一個大小爲負數的數組錯誤異常
    • NumberFormatException - 數字格式異常
    • SecurityException - 安全異常
    • UnsupportedOperationException - 不支持的操做異常
  • Java 異常中的getMessage()和toString()方法的異同

    • e.toString(): 得到異常種類和錯誤信息
    • e.getMessage():得到錯誤信息
    • e.printStackTrace():在控制檯打印出異常種類,錯誤信息和出錯位置等
  • java中的length和length(),size

  • 重寫與重載

    • 重載:
      • 發生在同一個類裏面,兩個或者是多個方法的方法名相同可是參數不一樣的狀況。
      • 注:參數順序不一樣也參數列表不一樣的狀況。
      • 重載與返回值類型和修飾符無關。
    • 重寫或覆蓋:
      • 是指子類從新定義了父類的方法;
      • 重寫必須有相同的方法名,參數列表和返回類型。
      • 子類函數的訪問修飾權限不能少於父類的。
  • 棧和堆的區別

    • 1.棧內存存儲的是局部變量,基本類型的變量表示的是數據自己;而堆內存存儲的是實體,每一個實體對象都有地址值和默認初始化值;
    • 2.棧內存的讀取和更新速度要快於堆內存,由於局部變量的生命週期很短;
    • 3.棧內存使用一級緩存,存放的變量生命週期一旦結束就會被釋放;而堆內存使用二級緩存,存放的實體會被垃圾回收機制不定時的回收。
  • java集合

  • String中兩種初始化方式比較:直接賦值和構造函數初始化

    • 經過直接賦值建立對象是在方法區的常量池:
      • String s1 = "abc";
      • 首先在常量池中查找"abc",若是沒有則在常量池建立該對象
      • 在棧中建立s1的引用,將s1直接指向對象"abc"
      • 所以在這裏"abc"是常量池中的對象,若是聲明另外一個String類型的對象引用,並將它指向對象"abc",則這兩個引用指向的是同一個常量池中的對象。
      • 直接賦值:存儲在常量池。
      • 屢次直接賦值:將存儲在常量池中的"hello"的地址傳遞給s變量,且常量池已有字符串公用。
    • 經過構造方法建立字符串對象是在堆內存:
      • String s = new String(「abc」);
      • 凡是通過 new 建立出來的對象,都會在堆內存中分配新的空間,建立新的對象,因此s是String類新建立的對象;
      • 每new一次,都會建立新的對象,即便對象值相同。
  • String、StringBuilder與StringBuffer

    • 字符修改上的區別
      • String:字符串常量,不可變字符串,每次對String的操做均可能生成新的String對象,效率低下,並且大量浪費有限的內存空間。
        • 注:對string從新賦值,若是字符串常量池不存在這個新的賦值對象,就會創造新的對象,若是存在,就不會建立。
      • StringBuffer:字符串變量,可變字符串、效率低、線程安全;
      • StringBuilder:字符串變量,可變字符序列、效率高、線程不安全;
        • 注:StringBuffer 和 StringBuilder 類的對象可以被屢次的修改,而且不產生新的未使用對象。
    • 三者在執行速度方面的比較:
      • StringBuilder > StringBuffer > String
    • 繼承結構不一樣
      • String繼承自CharSequence接口,StringBuilder和StringBuffer繼承自Appendable接口。
    • 小結:
      • (1)若是要操做少許的數據用 String;
      • (2)多線程操做字符串緩衝區下操做大量數據 StringBuffer;
      • (3)單線程操做字符串緩衝區下操做大量數據 StringBuilder(推薦使用)。
  • HashMap和Hashtable的區別

    • 底層都是數組+鏈表實現
    • 一、繼承的父類不一樣: Hashtable繼承自Dictionary類,而HashMap繼承自AbstractMap類。但兩者都實現了Map接口。
    • 二、線程安全性不一樣: HashMap是線程不安全的,可用於單線程;Hashtable是線程安全的,可用於多線程。
    • 三、是否提供contains方法
      • HashMap把Hashtable的contains方法去掉了,改爲containsValue和containsKey,由於contains方法容易讓人引發誤解。
      • Hashtable則保留了contains,containsValue和containsKey三個方法,其中contains和containsValue功能相同。
    • 四、key和value是否容許null值:其中key和value都是對象,而且不能包含重複key,但能夠包含重複的value。
      • Hashtable中,key和value都不容許出現null值。可是若是在Hashtable中有相似put(null,null)的操做,編譯一樣能夠經過,由於key和value都是Object類型,但運行時會拋出NullPointerException異常,這是JDK的規範規定的。
      • HashMap中,null能夠做爲鍵,這樣的鍵只有一個;能夠有一個或多個鍵所對應的值爲null。當get()方法返回null值時,多是 HashMap中沒有該鍵,也可能使該鍵所對應的值爲null。所以,在HashMap中不能由get()方法來判斷HashMap中是否存在某個鍵, 而應該用containsKey()方法來判斷。
    • 五、兩個遍歷方式的內部實現上不一樣:
      • Hashtable、HashMap都使用了 Iterator。而因爲歷史緣由,Hashtable還使用了Enumeration的方式 。
    • 六、hash值不一樣:
      • 哈希值的使用不一樣,HashTable直接使用對象的hashCode。而HashMap從新計算hash值。
    • 七、內部實現使用的數組初始化和擴容方式不一樣:
      • HashTable在不指定容量的狀況下的默認容量爲11,而HashMap爲16,Hashtable不要求底層數組的容量必定要爲2的整數次冪,而HashMap則要求必定爲2的整數次冪。
      • Hashtable擴容時,將容量變爲原來的2倍加1,而HashMap擴容時,將容量變爲原來的2倍。
  • map的put方法

  • HashMap爲何是線程不安全的?

    • HashMap是線程不安全的,咱們應該使用ConcurrentHashMap
    • Hashtable 中的方法是Synchronize的,而HashMap中的方法在缺省狀況下是非Synchronize的
    • HashMap的線程不安全主要體如今下面兩個方面:
      • 1.在JDK1.7中,當併發執行擴容操做時會形成環形鏈和數據丟失的狀況。
      • 2.在JDK1.8中,在併發執行put操做時會發生數據覆蓋的狀況。
    • 注:ConcurrentHashMap使用了分段鎖技術來提升了併發度,不在同一段的數據互相不影響,多個線程對多個不一樣的段的操做是不會相互影響的。每一個段使用一把鎖。因此在須要線程安全的業務場景下,推薦使用ConcurrentHashMap,而HashTable不建議在新的代碼中使用,若是須要線程安全,則使用ConcurrentHashMap,不然使用HashMap就足夠了。
  • 哈希表:哈希函數構造;哈希表解決地址衝突的方法

    • 散列函數構造方法:
      • 1.直接定址法:H(key) = a*key + b
      • 2.除留餘數法:H(key) = key % p(p爲不大於散列表表長,但最接近或等於表長的質數p)
      • 3.數字分析法:選取r進制數數碼分佈較爲均勻的若干位做爲散列地址
      • 4.平方取中法:取關鍵字的平方值的中間幾位做爲散列地址
      • 5.摺疊法:將關鍵字分割成位數相同的幾部分,而後取這幾部份的疊加和做爲散列地址
    • 處理衝突的方法:
      • 1.開放定址法(閉哈希表):在衝突的哈希地址的基礎上進行處理,獲得新的地址值。Hi = (H(key)+di) % m(m表示散列表表長,di爲增量序列)
        • 1)線性探測法:dii=1,2,3,…,m-1
        • 2)二次探測法:di=12,-12,22,-22,…,k2,-k2 ( k<=m/2 )
          • 衝突發生時,以原哈希地址爲中心,在表的左右進行跳躍式探測,比較靈活。
        • 3)僞隨機數法:di=僞隨機數序列。
          • 具體實現時,應創建一個僞隨機數發生器,(如i=(i+p) % m),並給定一個隨機數作起點。
      • 線性探測再散列的優勢是:只要哈希表不滿,就必定能找到一個不衝突的哈希地址,而二次探測再散列和僞隨機探測再散列則不必定。
      • 注:在開放定址的情形下,不能隨便物理刪除表中已有元素,若刪除元素將會截斷其餘具備相同散列地址的元素的查找地址。若想刪除一個元素,給它作一個刪除標記,進行邏輯刪除。
      • 2.鏈地址法、拉鍊法(開哈希表)
        • 將全部哈希地址爲i的元素構成一個稱爲同義詞鏈的單鏈表,並將單鏈表的頭指針存在哈希表的第i個單元中,於是查找、插入和刪除主要在同義詞鏈中進行。鏈地址法適用於常常進行插入和刪除的狀況。
      • 3.再哈希法:同時構造多個不一樣的哈希函數,發生衝突時,使用其餘哈希函數求值。這種方法不易產生彙集,但增長了計算時間。
      • 4.創建公共溢出區:將哈希表分爲基本表和溢出表兩部分,凡是和基本表發生衝突的元素,一概填入溢出表
  • 數組與鏈表

    • 數據中數組的內存是順序存儲的,而鏈表是隨機存取的。
    • 數組隨機訪問效率很高,但插入刪除操做的效率比較低。
    • 鏈表在插入刪除操做上相對數組有很高的效率,而若是訪問鏈表中的某個元素,那就要從表頭逐個遍歷,直到找到所須要的元素爲止,因此鏈表的隨機訪問效率比數組低。
    • 鏈表不存在越界問題,數組有越界問題。
    • 數組節省空間可是長度固定。鏈表雖然變長,可是佔了更多的存儲空間。
    • 靜態)數組從棧中分配內存空間,對於程序員方便快速,可是自由度小。鏈表從堆中分配空間,自由度大,但申請管理比較麻煩。
  • arraylist和linkedlist的區別

    • ArrayList和LinkedList都實現了List接口,他們有如下的不一樣點:
    • ArrayList是基於索引的數據接口,它的底層是數組。它能夠以O(1)時間複雜度對元素進行隨機訪問。與此對應,LinkedList是以元素列表的形式存儲它的數據,每個元素都和它的前一個和後一個元素連接在一塊兒,在這種狀況下,查找某個元素的時間複雜度是O(n)。
    • 相對於ArrayList,LinkedList的插入,添加,刪除操做速度更快,由於當元素被添加到集合任意位置的時候,不須要像數組那樣從新計算大小或者是更新索引。
    • LinkedList比ArrayList更佔內存,由於LinkedList爲每個節點存儲了兩個引用,一個指向前一個元素,一個指向下一個元素。
  • 辨析:replace,replaceAll,replaceFirst

    • replace和replaceAll:
      • 相同點:替換全部匹配的字符串(都是替換全部)
      • 不一樣點:replace支持字符替換,字符串替換; replaceAll是正則表達式替換
    • replaceFirst: 同replaceAll同樣,也是基於規則表達式的替換
      • 不一樣之處是:只替換第一次出現的字符串
  • protected,private,public

  • 判斷一個類是否「無用」,則需同時知足三個條件:

    • 該類全部的實例都已經被回收,也就是Java堆中不存在該類的任何實例;
    • 加載該類的ClassLoader已經被回收
    • 該類對應的java.lang.Class對象沒有在任何地方被引用,沒法在任何地方經過反射訪問該類的方法
  • 垃圾回收算法:複製算法、標記-清除算法、標記-整理算法、分代收集算法

    • 如何肯定某個對象是垃圾:引用計數法。
      • 在java中是經過引用來和對象進行關聯的,也就是說若是要操做對象,必須經過引用來進行。那麼很顯然一個簡單的辦法就是經過引用計數來判斷一個對象是否能夠被回收。不失通常性,若是一個對象沒有任何引用與之關聯,則說明該對象基本不太可能在其餘地方被使用到,那麼這個對象就成爲可被回收的對象了。這種方式成爲引用計數法。
      • 這種方式的特色是實現簡單,並且效率較高,可是它沒法解決循環引用的問題,所以在Java中並無採用這種方式(Python採用的是引用計數法)。
    • 垃圾回收算法:複製算法、標記-清除算法、標記-整理算法、分代收集算法
      • 標記-清除算法:
        • 分爲兩個階段:標記階段和清除階段。標記階段的任務是標記出全部須要被回收的對象,清除階段就是回收被標記的對象所佔用的空間。
        • 實現起來比較容易,可是有一個比較嚴重的問題就是容易產生內存碎片,碎片太多可能會致使後續過程當中須要爲大對象分配空間時沒法找到足夠的空間而提早觸發新的一次垃圾收集動做。
      • 複製算法:
        • 將可用內存按容量劃分爲大小相等的兩塊,每次只使用其中的一塊。當這一塊的內存用完了,就將還存活着的對象複製到另一塊上面,而後再把已使用的內存空間一次清理掉,這樣一來就不容易出現內存碎片的問題。
        • 實現簡單,運行高效且不容易產生內存碎片,可是卻對內存空間的使用作出了高昂的代價,由於可以使用的內存縮減到原來的一半。
        • Copying算法的效率跟存活對象的數目多少有很大的關係,若是存活對象不少,那麼Copying算法的效率將會大大下降。
      • 標記-整理算法:
        • 該算法標記階段和Mark-Sweep同樣,可是在完成標記以後,它不是直接清理可回收對象,而是將存活對象都向一端移動,而後清理掉端邊界之外的內存。
      • 分代收集算法:
        • 是目前大部分JVM的垃圾收集器採用的算法。
        • 它的核心思想是根據對象存活的生命週期將內存劃分爲若干個不一樣的區域。通常狀況下將堆區劃分爲老年代(Tenured Generation)和新生代(Young Generation),老年代的特色是每次垃圾收集時只有少許對象須要被回收,而新生代的特色是每次垃圾回收時都有大量的對象須要被回收,那麼就能夠根據不一樣代的特色採起最適合的收集算法。
        • 新生代:Copying算法,由於新生代中每次垃圾回收都要回收大部分對象,也就是說須要複製的操做次數較少,可是實際中並非按照1:1的比例來劃分新生代的空間的,通常來講是將新生代劃分爲一塊較大的Eden空間和兩塊較小的Survivor空間,每次使用Eden空間和其中的一塊Survivor空間,當進行回收時,將Eden和Survivor中還存活的對象複製到另外一塊Survivor空間中,而後清理掉Eden和剛纔使用過的Survivor空間。
        • 老年代:Mark-Compact算法。根據老年代的特色:每次回收都只回收少許對象。
        • 在堆區以外還有一個代就是永久代(Permanet Generation):存儲class類、常量、方法描述等。對永久代的回收主要回收兩部份內容:廢棄常量和無用的類。
    • 典型的垃圾收集器:
      • 垃圾收集算法是 內存回收的理論基礎,而垃圾收集器就是內存回收的具體實現。
      • 下面介紹一下HotSpot(JDK 7)虛擬機提供的幾種垃圾收集器,用戶能夠根據本身的需求組合出各個年代使用的收集器。
      • 小結:Serial/Serial Old,ParNew,Parallel Scavenge,Parallel Old,CMS,G1
  • 內存溢出和內存泄露

  • java會不會內存泄漏

    • 內存泄漏:一個再也不被程序使用的對象或變量還在內存中佔用存儲空間。
    • Java的垃圾回收機制能夠回收這類再也不使用的對象。
    • 可是Java還存在內存泄漏的問題。
    • 緣由:
      • 靜態集合類,如哈希表:由於是靜態的,生命週期與程序一致,在程序結束前不能釋放,形成內存泄漏;
      • 變量不合理的做用域:若是一個變量定義的做用範圍大於使用範圍,可能形成內存泄漏。
      • 其餘:創建各類連接後,監聽器,單例模式中靜態存儲單例對象等等。
  • 抽象類

    • 抽象類和抽象方法必須用abstract修飾;
    • 抽象方法:只有方法聲明,沒有方法體,定義在抽象類中;
    • 格式:修飾符 abstract 返回值類型 函數名(參數列表){}
    • 抽象類不能夠被實例化,即不能夠用new建立對象。
      • 抽象類經過其子類實例化,而子類須要覆蓋掉抽象類中全部抽象方法才能夠建立對象,不然該子類也是抽象類。
  • 接口

    • 抽象類和接口:
      • 抽象類中能夠定義抽象方法和非抽象方法;
      • 當抽象類中的方法都是抽象的時,能夠定義爲接口。
    • 接口最重要的體現:解決單繼承的弊端。
      • 多繼承父類中有方法主體,致使調用時的不肯定性;
      • 接口中沒有方法體,由子類來定義。
    • 接口的特色:
      • 接口不能夠建立對象;
      • 子類必須覆蓋掉接口中的全部的抽象方法後,子類才能夠實例化,不然子類是一個抽象類。
    • 固定修飾符:
      • 成員變量(實際上是常量):public static final
      • 成員方法:public abstract
    • 代碼體現:
interface A{ void show();}
interface B{ void show();}
class C implements A,B{public void show(){...}}
C c = new C();
c.show();
  • 抽象類和接口的區別

    • 共性:都是不斷抽取出來的抽象的概念。
    • 不一樣1:
      • 抽象類體現的是繼承關係,一個類只能單繼承;
      • 接口體現的是實現關係,一個類能夠多實現。
    • 不一樣2:
      • 抽象類是繼承,是is a的關係;
      • 接口是實現,是like a的關係。
    • 不一樣3:
      • 抽象類中能夠定義非抽象方法,供子類直接使用;
      • 接口的方法都是抽象,接口中的成員都有固定修飾符。
  • 線程的實現方式有哪些 extend Thread、implement runnable、implement callable

  • 序列化和反序列化

    • 定義:
      • Java序列化就是指把Java對象轉換爲字節序列的過程。
      • Java反序列化就是指把字節序列恢復爲Java對象的過程。
    • 做用:
      • 序列化:在傳遞和保存對象時,保證對象的完整性和可傳遞性。對象轉換爲有序字節流,以便在網絡上傳輸或者保存在本地文件中。
      • 反序列化:根據字節流中保存的對象狀態及描述信息,經過反序列化重建對象。
    • 總結:核心做用就是對象狀態的保存和重建。

設計模式

  • 單例模式

    • 什麼狀況下會用到:
      • 假若有不少地方都須要使用配置文件的內容,也就是說,不少地方都須要建立 AppConfig對象的實例,這就致使系統中存在多個AppConfig的實例對象,在配置文件內容不少的狀況下會嚴重浪費內存資源。相似AppConfig這樣的類,咱們但願在程序運行期間只存在一個實例對象。
    • 優勢:速度快、在使用時不須要建立、直接使用便可。
    • 缺點:可能存在內存浪費

END

相關文章
相關標籤/搜索