內存泄漏是當有程序再也不使用到的內存時,釋放內存失敗而產生了無用的內存消耗。內存泄漏並非指物理上的內存消失,這裏的內存泄漏是指由程序分配的內存,因爲程序邏輯錯誤而致使程序失去了對該內存的控制,使得內存浪費。程序員
Java 程序運行時的內存分配策略有三種,分別是 靜態分配
、 棧式分配
和 堆式分配
,對應的三種存儲策略使用的內存空間主要分別是 靜態存儲區(也稱方法區)
、 棧區
和 堆區
。web
- 靜態存儲區(方法區):主要存放 靜態數據
、 全局 static 數據
和 常量
。這塊內存在程序編譯時就已經分配好,而且在程序整個運行期間都存在。算法
- 棧區:當方法被執行時,方法體內的 局部變量
(其中包括基礎數據類型、對象的引用)都在棧上建立,並在方法執行結束時這些局部變量所持有的內存將會自動被釋放。由於棧內存分配運算內置於處理器的指令集中,效率很高,可是分配的內存容量有限。編程
- 堆區: 又稱動態內存分配,一般就是指在程序運行時直接 new 出來的內存,也就是 對象的實例
。這部份內存在不使用時將會由 Java 垃圾回收器(GC)來負責回收。api
在方法體內定義的(局部變量)一些基本類型的變量和對象的引用變量都是在方法的棧內存中分配的。當在一段方法塊中定義一個變量時,Java 就會在棧中爲該變量分配內存空間,當超過該變量的做用域後,該變量也就無效了,分配給它的內存空間也將被釋放掉,該內存空間能夠被從新使用。數組
堆內存用來存放全部由 new 建立的對象(包括該對象其中的全部成員變量)和數組。在堆中分配的內存,將由 Java 垃圾回收器來自動管理。在堆中產生了一個數組或者對象後,還能夠在棧中定義一個特殊的變量,這個變量的取值等於數組或者對象在堆內存中的首地址,這個特殊的變量就是咱們上面說的引用變量。咱們能夠經過這個引用變量來訪問堆中的對象或者數組。網絡
舉例說明:架構
public class Sample { int s1 = 0; Sample mSample1 = new Sample(); public void method() { int s2 = 1; // Sample 類的局部變量 s2 和引用變量 mSample2 都是存在於棧中, // 但 mSample2 指向的對象是存在於堆上 Sample mSample2 = new Sample(); } } // mSample3 指向的對象實體存放在堆上,包括這個對象的全部成員變量 s1 和 mSample1, // 而它本身存在於棧中。 Sample mSample3 = new Sample();
Java的內存管理就是對象的分配和釋放問題。在 Java 中,程序員須要經過關鍵字 new 爲每一個對象申請內存空間 (基本類型除外),全部的對象都在堆 (Heap)中分配空間。另外,對象的釋放是由 GC 決定和執行的。在 Java 中,內存的分配是由程序完成的,而內存的釋放是由 GC 完成的,這種收支兩條線的方法確實簡化了程序員的工做。但同時,它也加劇了JVM的工做。這也是 Java 程序運行速度較慢的緣由之一。由於,GC 爲了可以正確釋放對象,GC 必須監控每個對象的運行狀態,包括對象的申請、引用、被引用、賦值等,GC 都須要進行監控。app
監視對象狀態是爲了更加準確地、及時地釋放對象,而釋放對象的根本原則就是該對象再也不被引用。ide
在Java中,內存泄漏就是存在一些被分配的對象,這些對象有下面兩個特色,首先,這些對象是可達的,即在有向圖中,存在通路能夠與其相連;其次,這些對象是無用的,即程序之後不會再使用這些對象。若是對象知足這兩個條件,這些對象就能夠斷定爲Java中的內存泄漏,這些對象不會被GC所回收,然而它卻佔用內存。
在C++中,內存泄漏的範圍更大一些。有些對象被分配了內存空間,而後卻不可達,因爲C++中沒有GC,這些內存將永遠收不回來。在Java中,這些不可達的對象都由GC負責回收,所以程序員不須要考慮這部分的內存泄露。
經過分析,咱們得知,對於C++,程序員須要本身管理邊和頂點,而對於Java程序員只須要管理邊就能夠了(不須要管理頂點的釋放)。經過這種方式,Java提升了編程的效率。
所以,經過以上分析,咱們知道在Java中也有內存泄漏,但範圍比C++要小一些。由於Java從語言上保證,任何對象都是可達的,全部的不可達對象都由GC管理。
對於程序員來講,GC基本是透明的,不可見的。雖然,咱們只有幾個函數能夠訪問GC,例如運行GC的函數System.gc(),可是根據Java語言規範定義, 該函數不保證JVM的垃圾收集器必定會執行。由於,不一樣的JVM實現者可能使用不一樣的算法管理GC。一般,GC的線程的優先級別較低。JVM調用GC的策略也有不少種,有的是內存使用到達必定程度時,GC纔開始工做,也有定時執行的,有的是平緩執行GC,有的是中斷式執行GC。但一般來講,咱們不須要關心這些。除非在一些特定的場合,GC的執行影響應用程序的性能,例如對於基於Web的實時系統,如網絡遊戲等,用戶不但願GC忽然中斷應用程序執行而進行垃圾回收,那麼咱們須要調整GC的參數,讓GC可以經過平緩的方式釋放內存,例如將垃圾回收分解爲一系列的小步驟執行,Sun提供的HotSpot JVM就支持這一特性。
如下給出一個 Java 內存泄漏的典型例子:
Vector v = new Vector(10); for (int i = 1; i < 100; i++) { Object o = new Object(); v.add(o); o = null; }
在這個例子中,咱們循環申請Object對象,並將所申請的對象放入一個 Vector 中,若是咱們僅僅釋放引用自己,那麼 Vector 仍然引用該對象,因此這個對象對 GC 來講是不可回收的。所以,若是對象加入到Vector 後,還必須從 Vector 中刪除,最簡單的方法就是將 Vector 對象設置爲 null。
單例的使用在咱們的程序中隨處可見,由於使用它能夠完美的解決咱們在程序中重複建立對象的問題,不過可別小瞧它。因爲**單例的靜態特性使得其生命週期跟應用的生命週期同樣長 **,因此一旦使用有誤,當心無限制的持有Activity的引用而致使內存泄漏。
咱們看個例子:
public class AppManager { private static AppManager instance; private Context context; private AppManager(Context context) { this.context = context; } public static AppManager getInstance(Context context) { if (instance == null) { instance = new AppManager(context); } return instance; } }
這是一個普通的單例模式,當建立這個單例的時候,因爲須要傳入一個Context,因此這個Context的生命週期的長短相當重要!** (實際常見)**
一、若是此時傳入的是 Application 的 Context ,由於 Application 的生命週期就是整個應用的生命週期,因此這將沒有任何問題。
二、若是此時傳入的是 Activity 的 Context ,當這個 Context 所對應的 Activity 退出時,因爲該 Context 的引用被單例對象所持有,其生命週期等於整個應用程序的生命週期,因此當前 Activity 退出時它的內存並不會被回收,這就形成泄漏了。
正確的方式(寫法一):
public class AppManager { private static AppManager instance; private Context context; private AppManager(Context context) { this.context = context.getApplicationContext(); // 使用 Application 的 context } public static AppManager getInstance(Context context) { if (instance == null) { instance = new AppManager(context); } return instance; } }
正確的方式(寫法二):
// 在你的 Application 中添加一個靜態方法,getContext() 返回 Application 的 context ... context = getApplicationContext(); ... /** * 獲取全局的context * @return 返回全局context對象 */ public static Context getContext(){ return context; } public class AppManager { private static AppManager instance; private Context context; private AppManager() { this.context = MyApplication.getContext(); // 使用Application 的context } public static AppManager getInstance() { if (instance == null) { instance = new AppManager(); } return instance; } }
咱們看一段代碼:
public class MainActivity extends AppCompatActivity { private static MainActivity activity; // 這邊設置了靜態Activity,發生了內存泄漏 TextView saButton; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); saButton = (TextView) findViewById(R.id.text); saButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { setStaticActivity(); nextActivity(); } }); } void setStaticActivity() { activity = this; } void nextActivity(){ startActivity(new Intent(this,RegisterActivity.class)); SystemClock.sleep(1000); finish(); } @Override protected void onDestroy() { super.onDestroy(); } }
在上面代碼中,咱們聲明瞭一個靜態的 Activity 變量而且在 TextView 的 OnClick 事件裏引用了當前正在運行的 Activity 實例,因此若是在 activity 的生命週期結束以前沒有清除這個引用,則會引發內存泄漏。由於聲明的 activity 是靜態的,會常駐內存,若是該對象不清除,則垃圾回收器沒法回收變量。
咱們能夠這樣解決:
protected void onDestroy() { super.onDestroy(); activity = null; // 在onDestory方法中將靜態變量activity置空,這樣垃圾回收器就能夠將靜態變量回收 }
其實和靜態Activity頗爲類似,咱們看下代碼:
... private static View view; // 定義靜態View TextView saButton; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); saButton = (TextView) findViewById(R.id.text); saButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { setStaticView(); nextActivity(); } }); } void setStaticView() { view = findViewById(R.id.sv_view); } ...
View一旦被加載到界面中將會持有一個Context對象的引用,在這個例子中,這個context對象是咱們的Activity,聲明一個靜態變量引用這個View,也就引用了activity,因此當activity生命週期結束了,靜態View沒有清除掉,還持有activity的引用,所以內存泄漏了。
咱們能夠這樣解決:
protected void onDestroy() { super.onDestroy(); view = null; // 在onDestroy方法裏將靜態變量置空 }
咱們看下面的例子:
public class MainActivity extends AppCompatActivity { void startAsyncTask() { new AsyncTask<Void, Void, Void>() { @Override protected Void doInBackground(Void... params) { while(true); } }.execute(); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); View aicButton = findViewById(R.id.at_button); aicButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { startAsyncTask(); } }); } }
上面代碼在activity中建立了一個匿名類 AsyncTask,匿名類和非靜態內部類相同,會持有外部類對象,這裏也就是activity,所以若是你在 Activity 裏聲明且實例化一個匿名的AsyncTask對象,則可能會發生內存泄漏,若是這個線程在Activity銷燬後還一直在後臺執行,那這個線程會繼續持有這個Activity的引用從而不會被GC回收,直到線程執行完成。
咱們能夠這樣解決:
自定義靜態 AsyncTask 類,而且讓 AsyncTask 的週期和 Activity 週期保持一致,也就是在 Activity 生命週期結束時要將 AsyncTask cancel 掉。
有的時候咱們可能會在啓動頻繁的Activity中,爲了不重複建立相同的數據資源,可能會出現這種寫法:
public class MainActivity extends AppCompatActivity { private static TestResource mResource = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); if(mManager == null){ mManager = new TestResource(); } //... } class TestResource { //... } }
上面這段代碼在Activity內部建立了一個非靜態內部類的單例(mManager),每次啓動Activity時都會使用該單例的數據,這樣雖然避免了資源的重複建立,不過這種寫法卻會形成內存泄漏。
由於非靜態內部類默認會持有外部類的引用,而該非靜態內部類又建立了一個靜態的實例,該實例的生命週期和應用的同樣長,這就致使了該靜態實例一直會持有該Activity的引用,致使Activity的內存資源不能正常回收。
正確的作法爲:
將該內部類設爲靜態內部類或將該內部類抽取出來封裝成一個單例,若是須要使用Context,請按照上面推薦的使用Application 的 Context。固然,Application 的 context 不是萬能的,因此也不能隨便亂用,對於有些地方則必須使用 Activity 的 Context。
Handler 的使用形成的內存泄漏問題應該說是** 最爲常見 **了,不少時候咱們爲了不 ANR 而不在主線程進行耗時操做,在處理網絡任務或者封裝一些請求回調等api都藉助Handler來處理,但 Handler 不是萬能的,對於 Handler 的使用代碼編寫不規範即有可能形成內存泄漏。另外,咱們知道 Handler、Message 和 MessageQueue 都是相互關聯在一塊兒的,萬一 Handler 發送的 Message 還沒有被處理,則該 Message 及發送它的 Handler 對象將被線程 MessageQueue 一直持有。
因爲 Handler 屬於 TLS(Thread Local Storage) 變量, 生命週期和 Activity 是不一致的。所以這種實現方式通常很難保證跟 View 或者 Activity 的生命週期保持一致,故很容易致使沒法正確釋放。
public class SampleActivity extends Activity { private final Handler mLeakyHandler = new Handler() { @Override public void handleMessage(Message msg) { // ... } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Post a message and delay its execution for 10 minutes. mLeakyHandler.postDelayed(new Runnable() { @Override public void run() { /* ... */ } }, 1000 * 60 * 10); // Go back to the previous Activity. finish(); } }
在該 SampleActivity 中聲明瞭一個延遲 10分鐘 執行的消息 Message,mLeakyHandler 將其 push 進了消息隊列 MessageQueue 裏。當該 Activity 被 finish() 掉時,延遲執行任務的 Message 還會繼續存在於主線程中,它持有該 Activity 的 Handler 引用,因此此時 finish() 掉的 Activity 就不會被回收了從而形成內存泄漏(因 Handler 爲非靜態內部類,它會持有外部類的引用,在這裏就是指 SampleActivity)。
正確的作法爲:
在 Activity 中避免使用非靜態內部類,好比上面咱們將 Handler 聲明爲靜態的,則其存活期跟 Activity 的生命週期就無關了。同時經過弱引用的方式引入 Activity,避免直接將 Activity 做爲 context 傳進去,見下面代碼:
public class SampleActivity extends Activity { private static class MyHandler extends Handler { private final WeakReference<SampleActivity> mActivity; public MyHandler(SampleActivity activity) { mActivity = new WeakReference<SampleActivity>(activity); } @Override public void handleMessage(Message msg) { SampleActivity activity = mActivity.get(); if (activity != null) { // 每次使用前注意判空 // ... } } } private final MyHandler mHandler = new MyHandler(this); private static final Runnable sRunnable = new Runnable() { @Override public void run() { /* ... */ } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Post a message and delay its execution for 10 minutes. mHandler.postDelayed(sRunnable, 1000 * 60 * 10); // Go back to the previous Activity. finish(); } }
從上面的代碼中咱們能夠看出如何避免Handler內存泄漏,推薦使用 "靜態內部類 + WeakReference" 這種方式,每次使用前注意判空。
Java對引用的分類有Strong reference、SoftReference、WeakReference、PhatomReference四種。
在Android應用的開發中,爲了防止內存溢出,在處理一些佔用內存大並且聲明週期較長的對象時候,能夠儘可能應用軟引用和弱引用技術。
軟/弱引用能夠和一個引用隊列(ReferenceQueue)聯合使用,若是軟引用所引用的對象被垃圾回收器回收,Java虛擬機就會把這個軟引用加入到與之關聯的引用隊列中。利用這個隊列能夠得知被回收的軟/弱引用的對象列表,從而爲緩衝器清除已失效的軟/弱引用。
看個範例:
public class SampleActivity extends Activity { void spawnThread() { new Thread() { @Override public void run() { while(true); } }.start(); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); View tButton = findViewById(R.id.t_button); tButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { spawnThread(); } }); } }
其實這邊發生的內存泄漏緣由跟AsyncTask是同樣的。
正確的作法爲:
咱們自定義Thread並聲明成static這樣能夠嗎?其實這樣的作法並不推薦,由於Thread位於GC根部,DVM會和全部的活動線程保持hard references關係,因此運行中的Thread毫不會被GC無故回收了,因此正確的解決辦法是在自定義靜態內部類的基礎上給線程加上取消機制,所以咱們能夠在Activity的onDestroy方法中將thread關閉掉。
看個範例:
public class SampleActivity extends Activity { void scheduleTimer() { new Timer().schedule(new TimerTask() { @Override public void run() { while(true); } },1000); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); View ttButton = findViewById(R.id.tt_button); ttButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { scheduleTimer(); } }); } }
這裏內存泄漏在於Timer和TimerTask沒有進行Cancel,從而致使Timer和TimerTask一直引用外部類Activity。
正確的作法爲:
在適當的時機進行Cancel。
看個範例:
public class SampleActivity extends Activity { void registerListener() { SensorManager sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE); Sensor sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ALL); sensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_FASTEST); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); View smButton = findViewById(R.id.sm_button); smButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { registerListener(); } }); } }
經過Context調用getSystemService獲取系統服務,這些服務運行在他們本身的進程執行一系列後臺工做或者提供和硬件交互的接口,若是Context對象須要在一個Service內部事件發生時隨時收到通知,則須要把本身做爲一個監聽器註冊進去,這樣服務就會持有一個Activity,若是開發者忘記了在Activity被銷燬前註銷這個監聽器,這樣就致使內存泄漏。
正確的作法爲:
在onDestroy方法裏註銷監聽器。
若是成員變量被聲明爲 static,那咱們都知道其生命週期將與整個app進程生命週期同樣。
這會致使一系列問題,若是你的app進程設計上是長駐內存的,那即便app切到後臺,這部份內存也不會被釋放。按照如今手機app內存管理機制,佔內存較大的後臺進程將優先回收,若是此app作過進程互保保活,那會形成app在後臺頻繁重啓。當手機安裝了你參與開發的app之後一晚上時間手機被消耗空了電量、流量,你的app不得不被用戶卸載或者靜默。
這裏修復的方法是:
不要在類初始時初始化靜態成員。能夠考慮lazy初始化(使用時初始化)。架構設計上要思考是否真的有必要這樣作,儘可能避免。若是架構須要這麼設計,那麼此對象的生命週期你有責任管理起來。
一、finalize 方法被執行的時間不肯定,不能依賴與它來釋放緊缺的資源。時間不肯定的緣由是: 虛擬機調用GC的時間不肯定 Finalize daemon線程被調度到的時間不肯定
二、finalize 方法只會被執行一次,即便對象被複活,若是已經執行過了 finalize 方法,再次被 GC 時也不會再執行了,緣由是:
含有 finalize 方法的 object 是在 new 的時候由虛擬機生成了一個 finalize reference 在來引用到該Object的,而在 finalize 方法執行的時候,該 object 所對應的 finalize Reference 會被釋放掉,即便在這個時候把該 object 復活(即用強引用引用住該 object ),再第二次被 GC 的時候因爲沒有了 finalize reference 與之對應,因此 finalize 方法不會再執行。
三、含有Finalize方法的object須要至少通過兩輪GC纔有可能被釋放。
咱們一般會把一些對象的引用加入到集合容器(好比ArrayList)中,當咱們再也不須要該對象時,並無把它的引用從集合中清理掉,這樣這個集合就會愈來愈大。若是這個集合是static的話,那狀況就更嚴重了。
因此在退出程序以前,將集合裏面的東西clear,而後置爲null,再退出程序,以下:
private List<String> nameList; private List<Fragment> list; @Override public void onDestroy() { super.onDestroy(); if (nameList != null){ nameList.clear(); nameList = null; } if (list != null){ list.clear(); list = null; } }
當咱們再也不須要使用webView的時候,應該調用它的destory()方法來銷燬它,並釋放其佔用的內存,不然其佔用的內存長期也不能回收,從而形成內存泄漏。
正確的作法爲:
爲webView開啓另一個進程,經過AIDL與主線程進行通訊,webView所在的進程能夠根據業務的須要選擇合適的時機進行銷燬,從而達到內存的完整釋放。
爲webView開啓另一個進程,經過AIDL與主線程進行通訊,webView所在的進程能夠根據業務的須要選擇合適的時機進行銷燬,從而達到內存的完整釋放。
對於使用了BraodcastReceiver,ContentObserver,File,遊標 Cursor,Stream,Bitmap等資源的使用,應該在Activity銷燬時及時關閉或者註銷,不然這些資源將不會被回收,形成內存泄漏。
若是在程序中使用static聲明屬性,則此屬性稱爲全局屬性(也稱靜態屬性),那麼聲明成全局屬性有什麼用?咱們看下代碼:
class Person { String name; int age; static String country = "A城"; public Person(String name, int age) { this.name = name; this.age = age; } public void info() { System.out.println("姓名:" + this.name + ",年齡:" + this.age + ",城市:" + country); } }; public class Demo { public static void main(String agrs[]) { Person p1 = new Person("張三", 30); Person p1 = new Person("李四", 31); Person p1 = new Person("王五", 32); Person.country = "B城"; p1.info(); p2.info(); p3.info(); } }
以上程序很清晰的說明了static聲明屬性的好處,須要注意一點的是,類的公共屬性應該由類進行修改是最合適的(固然也能夠p1.country = ...),有時也就把使用static聲明的屬性稱爲類屬性。
直接看下代碼就清楚了:
class Person { private String name; private int age; private static String country = "A城"; public static void setCountry(String C) { country = c; } public Person(String name, int age) { this.name = name; this.age = age; } public void info() { System.out.println("姓名:" + this.name + ",年齡:" + this.age + ",城市:" + country); } public static String getCountry() { return country; } }; public class Demo { public static void main(String agrs[]) { Person p1 = new Person("張三", 30); Person p1 = new Person("李四", 31); Person p1 = new Person("王五", 32); Person.setCountry("B城"); p1.info(); p2.info(); p3.info(); } }
【特殊說明】
💥 非static聲明的方法能夠調用static聲明的屬性或方法 💥 static聲明的方法不能調用非static聲明的屬性或方法
好比如下代碼就會出錯:
class Person { private static String country = "A城"; private String name = "Hello"; public static void sFun(String C) { System.out.println("name = " + name); // 錯誤,不能調用非static屬性 fun(); // 錯誤,不能調用非static方法 } public void fun() { System.out.println("World!!!"); } };
咱們都知道,在類內部能夠定義成員變量與方法,一樣,在類內部也能夠定義另外一個類。若是在類Outer的內部定義一個類Inner,此時類Inner就稱爲內部類,而類Outer則稱爲外部類。
內部類可聲明成 public 或 private。當內部類聲明成 public 或 private時,對其訪問的限制與成員變量和成員方法徹底相同。
標識符 class 外部類的名稱 { // 外部類的成員 標識符 class 內部類的名稱 { // 內部類的成員 } }
能夠方便地訪問外部類中的私有屬性!
使用static能夠聲明屬性或方法,而使用static也能夠聲明內部類,用static聲明的內部類就變成了外部類,可是用static聲明的內部類不能訪問非static的外部類屬性。
好比以下例子:
class Outer { private static String info = "Hello World!!!"; // 若是此時info不是static屬性,則程序運行報錯 static class Inner { public void print() { System.out.println(info); } }; }; public class InnerClassDemo { public static void main(String args[]) { new Outer.Inner().print(); } }
執行結果:
Hello World!!!
一個內部類除了能夠經過外部類訪問,也能夠直接在其餘類中進行調用。
【在外部訪問內部類的格式】
外部類.內部類 內部類對象 = 外部類實例.new 內部類();
class Outer { private String info = "Hello World!!!"; class Inner { public void print() { System.out.println(info); } }; }; public class InnerClassDemo { public static void main(String args[]) { Outer out = new Out(); // 實例化外部類對象 Outer.Inner in = out.new Inner(); // 實例化內部類對象 in.print(); // 調用內部類方法 } }
除了在外部類中定義內部類,咱們也能夠在方法中定義內部類。可是須要注意的是,在方法中定義的內部類不能直接訪問方法中的參數,若是方法中的參數想要被內部類訪問,則參數前必須加上final關鍵字。
class Outer { private String info = "Hello World!!!"; public void fun(final int temp) { // 參數要被訪問必須用final聲明 class Inner { public void print() { System.out.println("類中的屬性:" + info); System.out.println("方法中的參數:" + temp); } }; new Inner().print(); } }; public class InnerClassDemo { public static void main(String args[]) { new Outer().fun(30); // 調用外部類方法 } }
在開發中,內存泄漏最壞的狀況是app耗盡內存致使崩潰,可是每每真實狀況不是這樣的,相反它只會耗盡大量內存但不至於閃退,可分配的內存少了,GC便會更多的工做釋放內存,GC是很是耗時的操做,所以會使得頁面卡頓。咱們在開發中必定要注意當在Activity裏實例化一個對象時看看是否有潛在的內存泄漏,必定要常常對內存泄漏進行檢測。
想學習更多Android知識,或者獲取相關資料請加入Android技術開發交流 878873098 進 羣 便可找羣管理免費領取。
主要是針對作移動開發一到五年,想系統深刻提高或者是困於瓶頸的小夥伴。
Android高級技術大綱,以及系統進階視頻;