摘要:所以如果趕在之前切斷是可以避免內(nèi)存泄露的。經(jīng)過測(cè)試情況始終沒有內(nèi)存泄露。如果當(dāng)退出時(shí)候,還有消息未處理或正在處理,由于引用又引用,此時(shí)將引發(fā)內(nèi)存泄露??偨Y(jié)如果某些單例需要使用到對(duì)象,推薦使用的,不要使用的,否則容易導(dǎo)致內(nèi)存泄露。
Java內(nèi)存回收方式之前一直在簡(jiǎn)書寫作,第一次發(fā)布到SF上來,也是第一次使用SF,后面會(huì)盡量同步到SF,更多文章請(qǐng)關(guān)注:
簡(jiǎn)書?編程之樂
轉(zhuǎn)載請(qǐng)注明出處:謝謝!
Java判斷對(duì)象是否可以回收使用的而是可達(dá)性分析算法。
在主流的商用程序語言中(Java和C#),都是使用可達(dá)性分析算法判斷對(duì)象是否存活的。這個(gè)算法的基本思路就是通過一系列名為"GC Roots"的對(duì)象作為起始點(diǎn),從這些節(jié)點(diǎn)開始向下搜索,搜索所走過的路徑稱為引用鏈(Reference Chain),當(dāng)一個(gè)對(duì)象到GC Roots沒有任何引用鏈相連時(shí),則證明此對(duì)象是不可用的,下圖對(duì)象object5, object6, object7雖然有互相判斷,但它們到GC Roots是不可達(dá)的,所以它們將會(huì)判定為是可回收對(duì)象。
在Java語言里,可作為GC Roots對(duì)象的包括如下幾種:
a.虛擬機(jī)棧(棧楨中的本地變量表)中的引用的對(duì)象 b.方法區(qū)中的類靜態(tài)屬性引用的對(duì)象 c.方法區(qū)中的常量引用的對(duì)象 d.本地方法棧中JNI的引用的對(duì)象
摘自《深入理解Java虛擬機(jī)》
使用leakcanary檢測(cè)泄漏關(guān)于LeakCanary使用參考以下文章:
LeakCanary: 讓內(nèi)存泄露無所遁形?? ? ?
LeakCanary 中文使用說明
LeakCanary的內(nèi)存泄露提示一般會(huì)包含三個(gè)部分:
第一部分(LeakSingle類的sInstance變量)
引用第二部分(LeakSingle類的mContext變量),
導(dǎo)致第三部分(MainActivity類的實(shí)例instance)泄露.
即使是空的Activity,如果檢測(cè)泄露時(shí)候遇到了如下這樣的泄露,注意,把refWatcher.watct()放在onDestroy里面即可解決,或者忽略這樣的提示。
由于文章已寫很多,下面的就不再修改,忽略這種錯(cuò)誤即可。
* com.less.demo.TestActivity has leaked: * GC ROOT static android.app.ActivityThread.sCurrentActivityThread * references android.app.ActivityThread.mActivities * references android.util.ArrayMap.mArray * references array java.lang.Object[].[1] * references android.app.ActivityThread$ActivityClientRecord.activity * leaks com.less.demo.TestActivity instance
protected void onDestroy() { super.onDestroy(); RefWatcher refWatcher = App.getRefWatcher(this); refWatcher.watch(this); }leakcanary和代碼示例說明內(nèi)存泄露
案例一(靜態(tài)成員引起的內(nèi)存泄露)
public class App extends Application { private RefWatcher refWatcher; @Override public void onCreate() { super.onCreate(); refWatcher = LeakCanary.install(this); } public static RefWatcher getRefWatcher(Context context) { App application = (App) context.getApplicationContext(); return application.refWatcher; } }
測(cè)試內(nèi)部類持有外部類引用,內(nèi)部類是靜態(tài)的(GC-ROOT,將一直連著這個(gè)外部類實(shí)例),靜態(tài)的會(huì)和Application一個(gè)生命周期,這會(huì)導(dǎo)致一直持有外部類引用(內(nèi)部類隱含了一個(gè)成員變量$0), 即使外部類制空= null,也無法釋放。
OutterClass
public class OutterClass { private String name; class Inner{ public void list(){ System.out.println("outter name is " + name); } } }
TestActivity
public class TestActivity extends Activity { // 靜態(tài)的內(nèi)部類實(shí)例 private static OutterClass.Inner innerClass; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_test); OutterClass outterClass = new OutterClass(); innerClass = outterClass.new Inner(); RefWatcher refWatcher = App.getRefWatcher(this); refWatcher.watch(outterClass);// 監(jiān)控的對(duì)象 outterClass = null; }
案例二(單例模式引起的內(nèi)存泄露)
DownloadManager
public class DownloadManager { private static DownloadManager instance; private Task task ; public static DownloadManager getInstance(){ if (instance == null) { instance = new DownloadManager(); } return instance; } public Task newTask(){ this.task = new Task(); return task; } }
Task
public class Task { private Call call; public Call newCall(){ this.call = new Call(); return call; } }
Call
public class Call { public void execute(){ System.out.println("=========> execute call"); } }
TestActivity
public class TestActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_test); RefWatcher refWatcher = App.getRefWatcher(this); Task task = DownloadManager.getInstance().newTask(); Call call = task.newCall(); call.execute(); refWatcher.watch(call);// 監(jiān)控的對(duì)象 call = null; // 無法回收,DownloadManager是靜態(tài)單例,引用task,task引用了call,即使call置為空,也無法回收,切斷GC_ROOT 聯(lián)系即可避免內(nèi)存泄露,即置task為空。 } }
部分日志打印如下:當(dāng)前的GC_ROOT是DownloadManager的instance實(shí)例。
In com.leakcanary.demo:1.0:1. * com.less.demo.Call has leaked: * GC ROOT static com.less.demo.DownloadManager.instance * references com.less.demo.DownloadManager.task * references com.less.demo.Task.call * leaks com.less.demo.Call instance
關(guān)于上面兩種方式導(dǎo)致的內(nèi)存泄露問題,這里再舉兩個(gè)案例說明以加強(qiáng)理解。
案例三(靜態(tài)變量導(dǎo)致的內(nèi)存泄露)
public class TestActivity extends Activity { private static Context sContext; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_test); sContext = this; RefWatcher refWatcher = App.getRefWatcher(this); refWatcher.watch(this);// 監(jiān)控的對(duì)象 }
打印日志如下:
In com.leakcanary.demo:1.0:1. com.less.demo.TestActivity has leaked: GC ROOT static com.less.demo.TestActivity.sContext leaks com.less.demo.TestActivity instance
從這段日志可以分析出:聲明static后,sContext的生命周期將和Application一樣長(zhǎng),Activity即使退出到桌面,Application依然存在->sContext依然存在,GC此時(shí)想回收Activity卻發(fā)現(xiàn)Activity仍然被sContext(GC-ROOT連接著),導(dǎo)致死活回收不了,內(nèi)存泄露。
上面的代碼改造一下,如下。
public class TestActivity extends Activity { private static View sView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_test); sView = new View(this); RefWatcher refWatcher = App.getRefWatcher(this); refWatcher.watch(this); } }
日志如下
In com.leakcanary.demo:1.0:1. com.less.demo.TestActivity has leaked: GC ROOT static com.less.demo.TestActivity.sView references android.view.View.mContext leaks com.less.demo.TestActivity instance
案例四(單例模式導(dǎo)致的內(nèi)存泄露)
DownloadManager
public class DownloadManager { private static DownloadManager instance; private ListmListeners = new ArrayList<>(); public interface DownloadListener { void done(); } public static DownloadManager getInstance(){ if (instance == null) { instance = new DownloadManager(); } return instance; } public void register(DownloadListener downloadListener){ if (!mListeners.contains(downloadListener)) { mListeners.add(downloadListener); } } public void unregister(DownloadListener downloadListener){ if (mListeners.contains(downloadListener)) { mListeners.remove(downloadListener); } } }
TestActivity
public class TestActivity extends Activity implements DownloadManager.DownloadListener{ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_test); DownloadManager.getInstance().register(this); RefWatcher refWatcher = App.getRefWatcher(this); refWatcher.watch(this); } @Override protected void onDestroy() { super.onDestroy(); // 忘記 unregister // DownloadManager.getInstance().unregister(this); } @Override public void done() { System.out.println("done!"); } }
In com.leakcanary.demo:1.0:1. * com.less.demo.TestActivity has leaked: * GC ROOT static com.less.demo.DownloadManager.instance * references com.less.demo.DownloadManager.mListeners * references java.util.ArrayList.array * references array java.lang.Object[].[0] * leaks com.less.demo.TestActivity instance錯(cuò)誤寫法一定導(dǎo)致內(nèi)存泄露嗎?
答案是:否定的。
如下案例,有限時(shí)間內(nèi)是可以挽救內(nèi)存泄露發(fā)生的,所以控制好生命周期,其他情況:如單例對(duì)象(靜態(tài)變量)的生命周期比其持有的sContext
的生命周期更長(zhǎng)時(shí) ->內(nèi)存泄露,更短時(shí)->躲過內(nèi)存泄露。
public class TestActivity extends Activity { private static Context sContext; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_test); sContext = this; new Handler().postDelayed(new Runnable() { @Override public void run() { sContext = null; } },1000);// 分別測(cè)試1s和12s,前者不會(huì)內(nèi)存泄露,后者一定泄露。所以如果趕在GC之前切斷GC_ROOT是可以避免內(nèi)存泄露的。 } @Override protected void onDestroy() { super.onDestroy(); RefWatcher refWatcher = App.getRefWatcher(this); refWatcher.watch(this); } }Handler 引起的內(nèi)存泄露詳細(xì)分析
handler導(dǎo)致的內(nèi)存泄露可能我們大多數(shù)都犯過.
注意以下代碼中的注釋,非靜態(tài)內(nèi)部類雖然持有外部類引用,但是持有并不代表一定泄露,而是看gc時(shí)誰的命長(zhǎng)。經(jīng)過測(cè)試 情況(1)始終沒有內(nèi)存泄露。
為什么會(huì)這樣, 很早閱讀Handler源碼時(shí)候記得這幾個(gè)貨都是互相引用來引用去的,Message有個(gè)target字段, message.target = handler;
handler.post(message);又把這個(gè)message推入了MessageQueue中,而MessageQueue是在一個(gè)Looper線程中不斷輪詢處理消息,而有時(shí)候message還是個(gè)老不死,能夠重復(fù)利用。如果當(dāng)Activity退出時(shí)候,還有消息未處理或正在處理,由于message引用handler,handler又引用Activity,此時(shí)將引發(fā)內(nèi)存泄露。
public class TestActivity extends Activity { private Handler handler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); System.out.println("===== handle message ===="); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_test); // (1) 不會(huì)導(dǎo)致內(nèi)存泄露 handler.sendEmptyMessageDelayed(0x123,0); // (2) 會(huì)導(dǎo)致內(nèi)存泄露,非靜態(tài)內(nèi)部類(包括匿名內(nèi)部類,比如這個(gè) Handler 匿名內(nèi)部類)會(huì)引用外部類對(duì)象 this(比如 Activity) // 當(dāng)它使用了 postDelayed 的時(shí)候,如果 Activity 已經(jīng) finish 了,而這個(gè) handler 仍然引用著這個(gè) Activity 就會(huì)致使內(nèi)存泄漏 // 因?yàn)檫@個(gè) handler 會(huì)在一段時(shí)間內(nèi)繼續(xù)被 main Looper 持有,導(dǎo)致引用仍然存在. handler.sendEmptyMessageDelayed(0x123, 12000); } @Override protected void onDestroy() { super.onDestroy(); RefWatcher refWatcher = App.getRefWatcher(this); refWatcher.watch(this); } }
com.less.demo.TestActivity has leaked: * GC ROOT android.view.inputmethod.InputMethodManager$ControlledInputConnectionWrapper.mH * references com.android.internal.view.IInputConnectionWrapper$MyHandler.mQueue * references android.os.MessageQueue.mMessages * references android.os.Message.target , matching exclusion field android.os.Message#target * references com.less.demo.TestActivity$1.this$0 (anonymous subclass of android.os.Handler) * leaks com.less.demo.TestActivity instance
知道了原理后,即使寫出易于內(nèi)存泄露的代碼也能夠避免內(nèi)存泄露啦。
上述代碼只需在onDestroy()函數(shù)中調(diào)用mHandler.removeCallbacksAndMessages(null);
在Activity退出的時(shí)候的移除消息隊(duì)列中所有消息和所有的Runnable。
內(nèi)部類種類大致如下:
非靜態(tài)內(nèi)部類(成員內(nèi)部類)
靜態(tài)內(nèi)部類(嵌套內(nèi)部類)
局部?jī)?nèi)部類(定義在方法內(nèi)或者作用域內(nèi)的類,好似局部變量,所以不能有訪問控制符和static等修飾)
匿名內(nèi)部類(沒有名字,僅使用一次new個(gè)對(duì)象即扔掉類的定義)
為什么非靜態(tài)內(nèi)部類持有外部類引用,靜態(tài)內(nèi)部類不持有外部引用。
這個(gè)問題非常簡(jiǎn)單,就像 static的方法只能調(diào)用static的東西,非static可以調(diào)用非static和static的一樣。static--> 針對(duì)class, 非static->針對(duì) 對(duì)象,我是這么簡(jiǎn)單理解的??磮D:
匿名內(nèi)部類
將局部?jī)?nèi)部類的使用再深入一步,假如只創(chuàng)建某個(gè)局部?jī)?nèi)部類的一個(gè)對(duì)象,就不必命名了。
匿名內(nèi)部類的類型可以是如下幾種方式。
接口匿名內(nèi)部類
抽象類匿名內(nèi)部類
類匿名內(nèi)部類
匿名內(nèi)部類總結(jié):
其實(shí)主要就是類定義一次就失效了,主要使用的是這個(gè)類(不知道名字)的實(shí)例。根據(jù)內(nèi)部類的特性,能夠?qū)崿F(xiàn)回調(diào)和閉包。
JavaScript和Python的回調(diào)傳遞的是fuction,Java傳遞的是object。
Java中常常用到回調(diào),而回調(diào)的本質(zhì)就是傳遞一個(gè)對(duì)象,JavaScript或其他語言則是傳遞一個(gè)函數(shù)(如Python,或者C,使用函數(shù)指針的方式),由于傳遞一個(gè)對(duì)象可以攜帶其他的一些信息,所以Java認(rèn)為傳遞一個(gè)對(duì)象比傳遞一個(gè)函數(shù)要靈活的多(當(dāng)然java也可以用Method反射對(duì)象傳遞函數(shù))。參考《Java核心技術(shù)》
非靜態(tài)內(nèi)部類導(dǎo)致內(nèi)存泄露(前提dog的命長(zhǎng))
下面的案例將導(dǎo)致內(nèi)存泄露
其中private static Dog dog ; 導(dǎo)致Dog的命比TestActivity長(zhǎng),這就糟糕了,但是注意,如果改為private Dog dog ; 即使Dog是非靜態(tài)內(nèi)部類,也不會(huì)導(dǎo)致內(nèi)存泄露,要死也是Dog先死,畢竟Dog是TestActivity的家庭成員,開掛也得看主人。
public class TestActivity extends Activity { private static Dog dog ; class Dog { public void say(){ System.out.println("I am lovely dog!"); } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_test); dog = new Dog(); } @Override protected void onDestroy() { super.onDestroy(); RefWatcher refWatcher = App.getRefWatcher(this); refWatcher.watch(this); } }
In com.leakcanary.demo:1.0:1. * com.less.demo.TestActivity has leaked: * GC ROOT static com.less.demo.TestActivity.dog * references com.less.demo.TestActivity$Dog.this$0 * leaks com.less.demo.TestActivity instance
哪些內(nèi)部類或者回調(diào)函數(shù)是否持有外部類對(duì)象?
一個(gè)反射案例說明一切
/** * 作者: limitless * 描述: 一個(gè)案例測(cè)試所有類型內(nèi)部類對(duì)外部類對(duì)象的持有情況 * 網(wǎng)站: https://github.com/wangli0 */ public class Main { /* 持有外部類引用 */ private IAppListener mAppListener = new IAppListener() { private String name; @Override public void done() { System.out.println("匿名內(nèi)部類對(duì)象作為成員變量"); } }; /* 未持有 */ private static IAppListener sAppListener = new IAppListener() { private String name; @Override public void done() { System.out.println("匿名內(nèi)部類對(duì)象作為static成員變量"); } }; public static void main(String args[]) { Main main = new Main(); main.test1(); main.test2(); main.test3();// test3 《=》test4 main.test4(); main.test5(); main.test6(); } class Dog { private String name; } /* 持有外部類引用 */ public void test1(){ Dog dog = new Dog(); getAllFieldName(dog.getClass()); } static class Cat { private String name; } /* 未持有 */ private void test2() { Cat cat = new Cat(); getAllFieldName(cat.getClass()); } /* 持有外部類引用 */ private void test3() { class Monkey{ String name; } Monkey monkey = new Monkey(); getAllFieldName(monkey.getClass()); } /* 持有外部類引用 */ private void test4() { // 常用作事件回調(diào)的地方(有可能引起內(nèi)存泄露) IAppListener iAppListener = new IAppListener() { private String name; @Override public void done() { System.out.println("匿名內(nèi)部類"); } }; getAllFieldName(iAppListener.getClass()); } /* 持有外部類引用 */ private void test5() { getAllFieldName(mAppListener.getClass()); } /* 未持有 */ private void test6() { getAllFieldName(sAppListener.getClass()); } private void getAllFieldName(Class> clazz) { System.out.println("className: ======> " + clazz.getSimpleName()); Field[] fields = clazz.getDeclaredFields(); for (Field field : fields) { System.out.println(field.getName()); } } }
上述結(jié)果足夠說明,即使是方法內(nèi)的回調(diào)對(duì)象也是持有外部類引用的,那么雖然作用域是局部的,也有存在內(nèi)存泄露的可能。我多次強(qiáng)調(diào) 持有某對(duì)象 不代表一定泄露,看的是誰命長(zhǎng)。回調(diào)在Android開發(fā)過程中幾乎處處可見,如果持有就會(huì)內(nèi)存泄露的話,那幾乎就沒法玩了。
一般情況下,我們常常設(shè)置某個(gè)方法內(nèi)的xx.execute(new Listener(){xx});是不會(huì)導(dǎo)致內(nèi)存泄露的,這個(gè)方法執(zhí)行完,局部作用域就失效了。但是如果在execute(listener);過程中,某個(gè)單例模式的對(duì)象 突然引用了這個(gè)listener對(duì)象,那么就會(huì)導(dǎo)致內(nèi)存泄露。
下面用實(shí)例證明我的想法
TestActivity
public class TestActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_test); Task task = new Task(); task.execute(new ICallback() { @Override public void done() { System.out.println("下載完成!"); } }); } @Override protected void onDestroy() { super.onDestroy(); RefWatcher refWatcher = App.getRefWatcher(this); refWatcher.watch(this); } }
Task
public class Task { public void execute(ICallback iCallback) { DownloadManager.getInstance().execute(iCallback); } }
DownloadManager
public class DownloadManager { public static DownloadManager instance; private ICallback mICallback; public static DownloadManager getInstance(){ if (instance == null) { instance = new DownloadManager(); } return instance; } public void execute(ICallback iCallback) { this.mICallback = iCallback;// 反例,千萬不要這么做,將導(dǎo)致內(nèi)存泄露,如果注釋掉這行,內(nèi)存泄露不會(huì)發(fā)生 iCallback.done(); }
這足以證明我的想法是正確的,Callback的不巧當(dāng)使用同樣會(huì)導(dǎo)致內(nèi)存泄露 的發(fā)送。
總結(jié)如果某些單例需要使用到Context對(duì)象,推薦使用Application的context,不要使用Activity的context,否則容易導(dǎo)致內(nèi)存泄露。單例對(duì)象的生命周期和Application一致,這樣Application和單例對(duì)象就一起銷毀。
優(yōu)先使用靜態(tài)內(nèi)部類而不是非靜態(tài)的,因?yàn)榉庆o態(tài)內(nèi)部類持有外部類引用可能導(dǎo)致垃圾回收失敗。如果你的靜態(tài)內(nèi)部類需要宿主Activity的引用來執(zhí)行某些東西,你要將這個(gè)引用封裝在一個(gè)WeakReference中,避免意外導(dǎo)致Activity泄露,被弱引用關(guān)聯(lián)的對(duì)象只能生存到下一次垃圾收集發(fā)生之前。當(dāng)垃圾收集器工作時(shí),無論當(dāng)前內(nèi)存是否足夠,都會(huì)回收 只被弱引用關(guān)聯(lián) 的對(duì)象,只被 說明這個(gè)對(duì)象本身已經(jīng)沒有用處了。
public class TestActivity extends Activity { private MyHandler myHandler = new MyHandler(this); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_test); } static class MyHandler extends Handler { private WeakReferencemWeakReference; public MyHandler(Activity activity){ mWeakReference = new WeakReference (activity); } @Override public void handleMessage(Message msg) { super.handleMessage(msg); Toast.makeText(mWeakReference.get(), "xxxx", Toast.LENGTH_LONG).show(); Log.d("xx", mWeakReference.get().getPackageName()); } } @Override protected void onDestroy() { super.onDestroy(); RefWatcher refWatcher = App.getRefWatcher(this); refWatcher.watch(this); } }
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/70643.html
摘要:前言從號(hào)開始在寫下第一篇文章說是筆記還差不多,驚奇地收到有人收藏我的文章的消息,覺得有點(diǎn)開心。突然腦子抽到想爬下里標(biāo)簽下的文章有多少,哪篇被收藏最多,哪篇被點(diǎn)贊最多。?!,F(xiàn)在和大家分享下,收藏量前的文章,被那么多人收藏應(yīng)該是篇值得看的文章。 前言 從18號(hào)開始在sf寫下第一篇文章(說是筆記還差不多),驚奇地收到有人收藏我的文章的消息,覺得有點(diǎn)開心。突然腦子抽到想爬下sf里JAVA標(biāo)簽下...
摘要:此時(shí),就出現(xiàn)了線程不安全問題了。因?yàn)榈某跏贾禃?huì)是因此,重排序是有可能導(dǎo)致線程安全問題的。真的能完全保證一個(gè)變量的線程安全嗎我們通過上面的講解,發(fā)現(xiàn)關(guān)鍵字還是挺有用的,不但能夠保證變量的可見性,還能保證代碼的有序性。 對(duì)于volatile這個(gè)關(guān)鍵字,相信很多朋友都聽說過,甚至使用過,這個(gè)關(guān)鍵字雖然字面上理解起來比較簡(jiǎn)單,但是要用好起來卻不是一件容易的事。 這篇文章將從多個(gè)方面來講解vol...
摘要:圖數(shù)據(jù)類型圖引用類型深淺拷貝問題不知道什么是深拷貝和淺拷貝的請(qǐng)先去并在調(diào)試臺(tái)自己操作一下,這篇文章只會(huì)說明為何中會(huì)有這種問題。所以有的時(shí)候我們?yōu)榱吮苊鉁\拷貝,會(huì)用一些方式實(shí)現(xiàn)深拷貝。 首先要了解的js基礎(chǔ) 基本數(shù)據(jù)類型:Object、undefined、null、Boolean、Number、String、Symbol (ES6新加) Object包括: Array 、Date 、R...
閱讀 4829·2021-11-15 11:39
閱讀 2730·2021-11-11 16:55
閱讀 2225·2021-10-25 09:44
閱讀 3533·2021-09-22 16:02
閱讀 2466·2019-08-30 15:55
閱讀 3156·2019-08-30 13:46
閱讀 2713·2019-08-30 13:15
閱讀 1984·2019-08-30 11:12