每日任務
2018年3月2日
-Service-和-IntentService-的區別
Service
Service是長期執行在後臺的應用程式元件。
Service不是一個單獨的程式,它和應用程式在同一個程式中,Service也不算是一個執行緒,它和執行緒沒有任何關係,所有它不能直接處理耗時操作。如果直接把耗時操作放在Service的onStartCommand()中,很容易引起ANR,如果有耗時操作就必須開啟一個單獨的執行緒來處理。
複製程式碼
IntentService
IntentService是繼承於Service並處理非同步請求的一個類,在IntentService內有一個工作執行緒來處理耗時操作,啟動IntentService的方式和啟動傳統Service一樣,同時,當任務執行完後,IntentService會自動停止,而不需要我們去手動控制。另外,可以啟動IntentService多次,而每一個耗時操作都會以工作佇列的方式在IntentService的onHandlerIntent回撥方法中執行,並且,每次只會執行一個工作執行緒,執行完第一個再執行第二個,以此類推。
複製程式碼
2018年3月3日
閱讀記錄
複習點
- 閱讀:精通比特幣-HD協議:
- 閱讀:JVM——Java虛擬機器架構 :heavy_check_mark:
- 閱讀:Kotlin Primer·第二章·基本語法
- 閱讀:Android面試一天一題(1 Day):heavy_check_mark:
- 閱讀:Android studio程式碼格式規範
- 閱讀:Android進階筆記09:Android開發經驗部分總結:heavy_check_mark:
- 閱讀:借騰訊開源 VasDolly,談談 Android 簽名和多渠道打包的原理!
- 閱讀:收集了50道基礎的java面試題
- 閱讀:Android開源框架原始碼鑑賞:VirtualAPK
-堆和棧的區別
堆和棧的區別
- 各司其職
最主要的區別就是棧記憶體是用來儲存區域性變數和方法呼叫資訊
而堆記憶體用來儲存java中的物件。無論是成員變數、區域性變數還是類變數,它們指向的物件都儲存在堆記憶體中。
複製程式碼
- 空間大小
棧的記憶體要遠遠小於堆記憶體,如果你使用遞迴的話,那麼你的棧很快就會充滿併產生StackOverflowError。
複製程式碼
- 獨有還是共享
棧記憶體歸屬於執行緒的私有記憶體,每個執行緒都會有一個棧記憶體,其儲存的變數只能在其所屬執行緒中可見。
而堆記憶體中的物件對所有執行緒可見,可以被所有執行緒訪問。
複製程式碼
- 異常錯誤
如果執行緒請求的棧深度大於虛擬機器所允許的深度,將丟擲StackOverflowError異常,
如果JVM棧可以動態擴充套件(大部分JVM是可以的),當擴充套件時無法申請到足夠的記憶體則丟擲outOfmemoryError異常。
而堆記憶體沒有可用的空間儲存生成的物件,JVM會丟擲java.lang.OutOfMemoryError。
複製程式碼
重要複習點
待閱讀
2018年3月5日
重要複習點
1. 使用非同步載入
2. 使用軟引用進行圖片的快取
複製程式碼
尺寸壓縮之inSampleSize 為了防止載入圖片時記憶體溢位,需要先計算取樣率,然後再去載入圖片。
- inSampleSiz只能是2個平方,如計算結果是7會按照4進行壓縮,計算結果是15會按照8進行壓縮。
- 存在兩種演算法:
-
演算法一:圖片長與目標長比,圖片寬與目標寬比,取最大值。
-
演算法二:取目標長寬的最大值來計算,這樣會減少過度的尺寸壓縮。
2018年3月6日
Android-壓縮大圖到容量超小的圖片
壓縮圖片的寬高
/**
* 計算圖片的壓縮比
*
* @param options
* @param reqWidth
* @param reqHeight
* @return
*/
public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;//壓縮比
if (height > reqHeight || width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
while ((halfHeight / inSampleSize) >= reqHeight &&
(halfWidth / inSampleSize) >= reqWidth) {
inSampleSize*=2;
}
}
return inSampleSize;
}
複製程式碼
呼叫calculateInSampleSize計算壓縮比。並解碼原圖為Bitmap:
/**
* @param imagePath
* @param displayWidth
* @param displayHeight
* @return
*/
private Bitmap compress(String imagePath, int displayWidth, int displayHeight) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;//只測量image 不載入到記憶體。
BitmapFactory.decodeFile(imagePath, options);//測量image
options.inPreferredConfig = Bitmap.Config.RGB_565;//設定565編碼格式,省記憶體,
options.inSampleSize = calculateInSampleSize(options, displayWidth, displayHeight);
options.inJustDecodeBounds = false;
Bitmap bitmap = BitmapFactory.decodeFile(imagePath, options);//按照Options配置去載入圖片到記憶體
return bitmap;
}
複製程式碼
這裡比較重要的inJustDecodeBounds欄位,當inJustDecodeBounds為true時,呼叫BitmapFactory.decode時並沒有把圖片載入到記憶體中去,只是去測量圖片的寬高,不不佔用記憶體,當inSampleSize為false時,呼叫BitmapFactory.decoe時就把圖片載入到記憶體去了,所有獲取bitmap應在inJustDecodeBounds為false後的BitmapFactory.decode去獲取。
設定編碼格式 android中預設格式是ARG8888,我們解碼圖片一般用ARG565節省圖片載入到記憶體的大小。
ALPHA_8 代表8位Alpha點陣圖
ARGB_4444 代表16位ARGB點陣圖
ARGB_8888 代表32位ARGB點陣圖
RGB_565 代表8位RGB點陣圖
複製程式碼
點陣圖位數越高代表其可以儲存的顏色資訊越多,當然影像也就越逼真
圖片質量壓縮
ByteArrayOutputStream out = new ByteArrayOutputStream();//位元組流輸出
bitmap.compress(Bitmap.CompressFormat.JPEG,50,out);//壓縮成jpeg格式
複製程式碼
compress中第一個引數是輸出檔案的格式,在Bitmap列舉類CompressFormat中定義,有JPEG,PNG PNG,WEBP,一般選擇JPEG,壓縮出來的容量小,WEBP很耗時, 耗時時間比較:WEBP>PNG>JPEG, 壓縮大小:PNG>WEBP>JPEG. 第二個引數是壓縮的質量比例,也就是壓縮畫素的顯示色彩,當100時表示不壓縮。當為50時表示壓縮50%的質量。設定這個引數可以有效的極大的縮小圖片的大小,可以按照自己的需求進行設定, 但建議一般不要大於60.第三個引數就是想要寫入的圖片資料的位元組流陣列了。
位元組流寫出檔案
我們經過上述步驟後,就拿到了位元組流資料了,此時我們可以根據專案需求直接上傳位元組流或者儲存為本地圖片再上傳。
ByteArrayOutputStream out = new ByteArrayOutputStream();//位元組流輸出
bitmap.compress(Bitmap.CompressFormat.JPEG, 50, out);//壓縮成jpeg格式 壓縮畫素質量為50%
String fileName = imagePath.substring(imagePath.lastIndexOf("/") + 1, imagePath.lastIndexOf("."));//獲取檔名
File outFile = new File("/storage/emulated/0/photoPickTemp", fileName + "_temp.jpeg");//建立壓縮後的image檔案
try {
if (!outFile.exists()) {//判斷檔案是否存在
if (outFile.createNewFile()) {//判斷建立新檔案是否成功
FileOutputStream fos = new FileOutputStream(outFile);
byte[] bytes = out.toByteArray();//位元組陣列
int count = bytes.length;
fos.write(bytes, 0, count);
fos.close();//關閉流
out.close();
}
}
} catch (Exception e) {
e.printStackTrace();
}
複製程式碼
獲取當前螢幕寬高
工具類
封裝
public class ImageUtil {
public ImageUtil(){
}
public static File compressImage(String imagePath,int displayWidth,int displayHeight){
BitmapFactory.Options options=new BitmapFactory.Options();
options.inJustDecodeBounds=true;//只測量image 不載入到記憶體
BitmapFactory.decodeFile(imagePath,options);//測量image
options.inPreferredConfig= Bitmap.Config.RGB_565;//設定565編碼格式 省記憶體
options.inSampleSize=calculateInSampleSize(options,displayWidth,displayHeight);//獲取壓縮比 根據當前螢幕寬高去壓縮圖片
options.inJustDecodeBounds=false;
Bitmap bitmap=BitmapFactory.decodeFile(imagePath,options);//按照Options配置去載入圖片到記憶體
ByteArrayOutputStream out=new ByteArrayOutputStream();//位元組流輸出
bitmap.compress(Bitmap.CompressFormat.JPEG,50,out);//壓縮成JPEG格式 壓縮畫素質量為50%
String fileName=imagePath.substring(imagePath.lastIndexOf("/")+1,imagePath.lastIndexOf("."));//獲取檔名稱
File outFile=new File("/storage/emulated/0/PhotoPickTemp",fileName+"_temp.jpeg");//建立壓縮後的image檔案
try {
if(!outFile.exists()){//判斷新檔案是否存在
if(outFile.createNewFile()){//判斷建立新檔案是否成功
FileOutputStream fos=new FileOutputStream(outFile);//建立一個檔案輸出流
byte[] bytes=out.toByteArray();//位元組陣列
int count=bytes.length;//位元組陣列的長度
fos.write(bytes,0,count);//寫到檔案中
fos.close();//關閉流
out.close();
}
}
} catch (IOException e) {
e.printStackTrace();
}
return outFile;
}
public static int calculateInSampleSize(BitmapFactory.Options options,int reqWidth,int reqHeight){//計算圖片的壓縮比
final int height=options.outHeight;//圖片的高度
final int width=options.outWidth;//圖片的寬度 單位1px 即畫素點
int inSampleSize=1;//壓縮比
if(height>reqHeight||width>reqWidth){
final int halfHeight=height/2;
final int halfWidth=width/2;
while ((halfHeight/inSampleSize)>=reqHeight
&&(halfWidth/inSampleSize)>=reqWidth){
inSampleSize*=2;
}
}
return inSampleSize;
}
}
複製程式碼
Android-效能優化
佈局優化
- 佈局巢狀過深
ConstraintLayout 約束佈局
複製程式碼
- 使用合適的佈局
三種常見的ViewGroup的繪製速度:
FragmentLayout > LinearLayout >RelativeLayout
複製程式碼
- 列表控制元件優化
convertView複用,ViewHolder的使用避免重複遍歷節點
複製程式碼
- 使用include標籤
<merge>標籤可以和<include>標籤一起使用從而減少佈局層級
複製程式碼
- ViewStub延時載入
ViewStub本身沒有寬高,載入起來幾乎不消耗什麼資源,當對他setVisibility(View.VISIBLE)的時候會呼叫他的引用的真實佈局填充到當前位置,從而實現了延時載入,節省了正常載入的速度。
複製程式碼
- 移除Activity預設背景
主要我們不需要Activity的預設背景,就可以移除掉,減少Activity的渲染時間,提升啟動效率。
複製程式碼
執行緒優化
ViewPager,ScrollView-巢狀ViewPager滑動衝突解決
Android事件分發機制
- 對於dispathTouchEvent,onTouchEvent,return true 是終結事件傳遞。return false 是回溯到父View的onTouchEvent方法。
- ViewGroup想把自己分發給自己的onTouchEvent,需要攔截器onInterecptTouchEvent方法return true把事件攔截下來。
- ViewGroup的攔截器預設是不攔截的。所以super.onInterecptTouchEvent()= return true;
- View沒有攔截器,為了讓View可以把事件分發給自己的onTouchEvent,View的dispatchTouchEvent預設實現(super)就是把事件分發給自己的onTouchEvent
ViewGroup和View的dispatchTouchEvent是做事件分發,那麼這個事件分發可能分發出去的四個目標。
java鎖的種類以及辨析
自旋鎖
採用讓當前執行緒不停地在迴圈體內執行實現的。當迴圈條件被其他執行緒改變時,才進入臨界區
阻塞鎖(synchronized)
可重入鎖(遞迴鎖)
指的是同一執行緒外層函式獲得鎖之後,內層遞迴函式仍然有獲取該鎖的程式碼,但不受影響。 可重入鎖最大的作用是避免死鎖。
互斥鎖的優缺點:
- 優點:能有效防止因多執行緒搶奪資源造成資料安全的問題
- 缺點:需要消耗大量的CPU資源。
互斥鎖的使用前提:多條執行緒搶奪同一塊資源。
** 自旋鎖更適合做一些較短的操作 **
複製程式碼
ANR
ANR
應用程式無響應。
為什麼會產生ANR
-
5s內無法響應使用者輸入事件(例如鍵盤輸入,觸控螢幕等)
-
BroadcastReceiver在10s內無法結束
造成以上兩種情況的首要原因就是在主執行緒(UI執行緒)裡面做了太多的阻塞耗時操作,例如:檔案讀寫,資料庫讀寫,網路查詢。
如何避免ANR
不要在主執行緒裡面做繁重的操作
ANR的處理
- 主執行緒堵塞的
開闢單獨的子執行緒來處理耗時阻塞事物。
- CPU滿負荷,I/O阻塞的
I/O阻塞一般就是檔案讀寫或者資料庫操作執行在主執行緒了,也可以通過開闢子執行緒的方式非同步執行。
- 記憶體不夠用的
增大VM記憶體,使用largeHeap屬性,排查記憶體洩露
RxJava
給 Android 開發者的 RxJava 詳解-flatMap():
2018年3月8日
Android-安裝檔案(APK)瘦身
- 掌握良好的編碼習慣
- 使用混淆(Proguard)
- 廣泛使用Lint(程式碼檢查)
- 對資原始檔進行取捨(沒必要適配低端機或非主流機型)
- 資原始檔最少化配置(重用,去除不需要的庫)
- 壓縮圖片(.9圖)
- 限制app支援的cpu架構的數目(armabi 和 x86 架構就夠了)
- 在合適的時候使用程式碼渲染影像
2018年3月9日
Android應用程式啟動過程原始碼分析
Step 1. Launcher.startActivitySafely
在Android系統中,應用程式是由launch啟動起來的,其實,launch本身也是一個應用程式,其他的應用程式安裝後,就會launcher的介面上出現一個相應的圖示。點選這個圖示,launcher就會對應的應用程式啟動起來。
複製程式碼
Android2017-2018最新面試題(3-5年經驗個人面試經歷)
大圖載入控制元件
2018年3月12日
Android外掛化與熱修復
DynamicApk-攜程
- 支援類的載入
- 用代理的方式載入Activity
- 反射方式載入資源
沒有像DroidPlugin那樣是資源獨立的,而是採用同一套資源,用資源名稱去區分不同的資源,針對資源的id也做修改,Activity的載入方式是採用代理的方式讓系統識別的。
DroidPlugin-360手機助手
原理:利用Android一個程式可以執行多個apk的機制,通過API欺騙讓系統認為只有宿主app存在,同時通過預先佔坑來創造外掛app的執行環境,最後通過動態代理實現函式hook,Binder代理繞過部分系統服務限制,從而實現應用的元件化。
- DroidPlugin這個是跟360安全的產品密切相關的,也非常適合360手機助手。 DroidPlugin的目標是使任何一個app都可以在DroidPlugin的宿主應用中直接執行。而不是將app安裝到手機中,再去體驗app。
- DroidPlugin中使用了大量的hook相關技術,特別是在Activity的註冊上,做了預先註冊一些Activity,然後在新的Activity啟動時欺騙系統是預先註冊過的Activity。 DroidPlugin最精華的就是hook技術。
HotFix-QQ空間熱補丁技術
主要技術是:系統在安裝app的時候會在類載入器中載入apk中的預設classes.dex檔案。這個類載入器可以載入後續的補丁Dex檔案,而且可以調整Dex檔案的載入順序,這樣就可以用同名的類去替換掉原始的含有bug的類,已達到修復bug的目的。
AndFix-阿里支付寶
- AndFix的原理是方法的替換,把有bug的方法替換成補丁檔案中的方法,在Native層使用指標替換的方式替換bug的方法,已達到修復bug的目的。
Alibaba-AndFix Bug熱修復框架原理及原始碼解析
Tinker-微信
在客戶端將補丁檔案和app原始檔案進行合併,用客戶端的儲存空間換取網路傳輸的流量和app的執行效率。Tinker目前開源支援的比較好,網上資料相對多一些,在編譯時通過新舊兩個Dex檔案生成差異的patch.dex檔案,在app中將差異的patch.dex檔案重新跟原始安裝包的原始的Dex檔案生成新的Dex檔案。這個過程會耗費app的執行時間和記憶體,所以Tinker將其單獨放在一個後臺程式:patch中。為了將補丁包儘量的小,Tinker自研了DexDiff演算法,他深度利用Dex的格式來減少差異檔案的大小。
2018年3月13日
Java基礎
java中==和equals和hashCode的區別
-
==
在用關係操作符 == 比較的是值本身,
int n=3;
int m=3;
System.out.println(n==m);
String str = new String("hello");
String str1 = new String("hello");
String str2 = new String("hello");
System.out.println(str1==str2);
str1 = str;
str2 = str;
System.out.println(str1==str2);
複製程式碼
結果是:true,false,true
物件型別的比較,比較的是地址(引用),而非值本身,也是就是說他們實際儲存的記憶體地址不同。
-
equals
比較兩個物件的引用是否相等,即 是否指向同一個物件。
總結
-
對於==,如果作用於基本資料型別,則直接比較其儲存的“值”是否相等,如果作用於引用型別的變數,則比較的是所指向的物件的地址。
-
對於 equals 方法,注意:equals不能作用於基本資料型別,如果沒有對equals進行重寫,則比較的是 引用類所指向的地址。如果重寫了,比較的就是物件的內容。
hashCode
用來鑑定兩個物件是否相等,Object類中的hashCode方法返回物件在記憶體中地址轉換成的一個int值,所以如果沒有重寫hashCode方法,任何物件的hashCode方法是不相等的。
設計hashCode()時最重要的因素就是:無論何時,對同一個物件呼叫hashCode都應該產生同一個值。
如果重寫了equals方法就必須要重寫hashCode方法,以便使用者將物件插入到雜湊表中。
equals相等的兩個物件,hashCode一定相等,equals不相等的兩個物件,卻並不能證明他們的hashCode不相等。
equals方法不相等的兩個物件,hashCode有可能相等,
在每個覆蓋了equals方法的類中,也必須覆蓋hashCode方法,如果不這樣做的話,就會違反Object.hashCode的通用約定。從而導致該類無法結合所有基於雜湊的集合一起正常運作。
int、char、long 各佔多少位元組數
-
byte 是 位元組
-
bit 是 位
1 byte = 8 bit
- char在java中是2個位元組,java採用unicode,2個位元組來表示一個字元
- short 2個位元組
- int 4個位元組
- long 8個位元組
- float 4個位元組
- double 8個位元組
int 與 integer 區別
- Integer是int的包裝類,int則是java的一種基本資料型別。
- Integer變數必須例項化後才能使用,而int變數不需要。
- Integer是物件的引用,當new一個Integer時,實際上生成一個指標指向此物件,而int則是直接儲存資料值。
- Integer預設值是null,int的預設值是0。
記錄-
2018年3月14日
String、StringBuffer、StringBuilder區別
- String :字串常量(執行緒安全)
字串是不變的,他們的值在創造後就不能改變。 字串緩衝區支援可變字串。因為字串物件是不可變的,所以他們可以共享。
總結歸納了String的兩個最重要的特點:
- String是值不可變的常量,是執行緒安全的
- String類使用了final修飾符,String類是不可繼承的。
-
StringBuilder:字串變數(非執行緒安全)
-
StringBuffer:字串變數(執行緒安全)
是一個容器,最終會通過toString方法變成字串。
String與StringBuffer區別
- 主要區別:在修改時物件自身是否可變
-
String在修改時不會改變物件自身。每次對String型別進行改變的時候其實都等同於生成了一個新的String物件,然後將指標指向了新的String物件,所以經常改變內容的字串最好不要用String。
-
StringBuffer在修改時會改變物件自身。每次結果都會對StringBuffer物件本身進行操作,而不是生成新的物件,改改變物件引用,所以一般情況下我們推薦使用StringBuffer,特別是字串物件經常改變的情況下,StringBuffer的主要操作是append和insert方法
- StringBuffer物件和String物件之間的互轉:
String s = “abc”;
StringBuffer sb1 = new StringBuffer(“123”);
StringBuffer sb2 = new StringBuffer(s); //String轉換為StringBuffer
String s1 = sb1.toString(); //StringBuffer轉換為String
複製程式碼
總結
- 如果要操作少量的資料用String
- (多執行緒下)經常需要對一個字串進行修改,例如追加、插入和刪除等操作,使用StringBuffer更加適合一些。
StringBuffer與StringBuilder區別
- StringBuilder是可變的物件,是5.0新增的 此類提供一個與StringBuffer相容的API,但不保證同步,該類被設計用作StringBuffer的一個簡易替換,用在字串緩衝區被單個執行緒使用的時候(這種情況很普遍)
- 執行緒安全性:
- StringBuffer執行緒安全的
- StringBuilder執行緒非安全
- String、StringBuilder、StringBuffer速度區別
- 大多數情況下:StringBuffer > String 由於String物件不可變,重複新建物件,StringBuffer物件可變。
- StringBuilder > StringBuffer 當我們在字串緩衝區被多個執行緒使用時,JVM不能保證StringBuilder的操作是安全的,雖然他速度快,但是可以保證StringBuffer是可以正確操作的,當然大多數情況下就是我們是在單執行緒下進行的操作,使用大多數情況下建議用StringBuilder而不是用StringBuffer。
- 特殊情況, String > StringBuffer
//String效率是遠要比StringBuffer快的:
String S1 = “This is only a” + “ simple” + “ test”;
StringBuffer Sb = new StringBuilder(“This is only a”).append(“simple”).append(“ test”);
複製程式碼
//String速度是非常慢的:
String S2 = “This is only a”;
String S3 = “ simple”;
String S4 = “ test”;
String S1 = S2 +S3 + S4;
複製程式碼
總結
- 如果要操作少量的資料用String
- 多執行緒操作字串緩衝區下操作大量資料用StringBuffer;
- 單執行緒操作字串緩衝區下操作大量資料用StringBuilder;
對java多型的理解
- 什麼是多型
- 物件導向的三大特性:繼承,封裝,多型,從一定角度來看,封裝和繼承幾乎都是為多型而準備的,
- 多型的定義:
指允許不同類的物件對同一訊息做出響應,即同一訊息可以根據傳送的物件的不同而採用多種不同的行為。 (傳送訊息就是函式呼叫)
複製程式碼
- 實現多型的技術稱為:
動態繫結,是指在執行期間判斷所引用物件的實際型別,根據其實際的型別呼叫其相應的方法。
複製程式碼
- 多型的作用:
消除型別之間的耦合關係。
複製程式碼
- 多型存在的三個必要條件:
1.要有繼承,2. 要有重寫,3.父類引用指向子類物件。
複製程式碼
- 多型的好處:
- 可替換性。多型對已存在的程式碼具有可替換性。
- 可擴充套件性。增加新的子類不影響已存在類的多型性,繼承性,以及其他特性的執行和操作,實際上新增功能更容易獲得多型功能。
- 介面性。多型是超類通過方法簽名。向子類提供一個共同介面,由子類來完善或者覆蓋它而實現的。
- 靈活性。他在應用中提現了靈活多樣的操作,提高了使用效率。
- 簡化性。多型簡化對應用軟體的程式碼編寫和修改過程,尤其是在處理大量物件的運算和操作時,這個特點尤為突出和重要。
java中多型的實現方式:介面實現,繼承父類進行方法的重寫,同一個類中方法過載。
多型的三要素:1.繼承。2.重寫。3.父類引用指向子類物件
package objectandclass;
class A {
public void show(D obj){
System.out.println("A and D");
}
public void show(A obj){
System.out.println ("A and A");
}
}
class B extends A{
public void show(B obj){
System.out.println("B and B");
}
public void show(A obj){
System.out.println("B and A");
}
}
class C extends B{}
class D extends B{}
A a1 = new A();
A a2 = new B();
B b = new B();
C c = new C();
D d = new D();
a1.show(b); //A and A
a1.show(c); //A and A
a1.show(d); //A and D
a2.show(b); //B and A
a2.show(c); //B and A
a2.show(d); //A and D
b.show(b); //B and B
b.show(c); //B and B
b.show(d); //A and D
複製程式碼
2018年3月15日
什麼是內部類?內部類的作用
定義
就是定義在另外一個類裡面的類,與之對應,包含內部類的類被稱為外部類。
作用
- 內部類提供了更好的封裝,可以把內部類隱藏在外部類之內,不允許同一個包中的其他類訪問該類。
- 內部類的方法可以直接訪問外部類的所有資料,,,包括私有的資料。
- 內部類所實現的功能使用外部類都可以實現,只是有時候使用內部類更方便。
public class Outer {
private static int i =1;
private int j =10;
private int k =20;
public static void outer_f1(){
}
public void out_f2(){
}
class Inner{
// 內部類中不允許定義靜態變數。
// static int inner_i=199;
int j =100;//內部類中的外部類的例項變數可以共存
int inner_i =1;
void inner_f1(){
Log.e("",i+"");//外部類的變數如果和內部類的變數沒有同名的,則可以直接用變數名訪問外部類的變數。
Log.e("",j+"");//在內部類中訪問內部類自己的變數直接用變數名。
Log.e("",""+this.j);//也可在內部類中用 this.變數名 來訪問內部類變數。
//訪問外部類中與內部類同名的例項變數可以用 外部類名.this.變數名
Log.e("",""+k);//外部類的變數如果和內部類的變數沒有同名的,則可以直接用變數名訪問外部類的變數。
outer_f1();
out_f2();
}
}
// 外部類的非靜態方法訪問成員內部類。
public void outer_f3(){
Inner inner = new Inner();
inner.inner_f1();
}
// 外部類的靜態方法訪問成員內部類,與外部類外部訪問成員內部類一樣。
public static void outer_f4(){
Outer outer = new Outer();
Inner inner = outer.new Inner();
inner.inner_f1();
}
public static void main(String[] args){
outer_f4();
}
}
複製程式碼
2018年3月16日
抽象類和介面
- 抽象類:對一類事物的抽象
定義: 如果一個類中沒有包含足夠多的資訊來描述一個具體的物件,這樣的類就是抽象類。
- 介面:對某一行為的抽象
定義: 介面在java中是一個抽象型別,是抽象方法的集合。一個類通過實現介面的方式,從而繼承介面中的抽象方法。
抽象類的意義:
- 為子類提供一個公告的型別;
- 封裝子類中的重複內容(成員變數和方法)
- 定義有抽象的方法,子類雖然有不同的實現,但該方法的定義是一致的。
區別總結:
- 抽象類只能單繼承,介面能多實現。
- 抽象類是一個類,可以被任意許可權修飾符修飾,靜態和非靜態,final和非finl屬性。可以有抽象方法和非抽象方法;介面只能被public final 修飾,只能有靜態方法,即使沒有顯示的宣告,而且是不能修改的。
- 抽象的事物不同:
抽象類是對事物的抽象,介面是對行為的抽象;抽象類是對整個類的抽象,包括行為,屬性;介面是對類的行為(區域性)進行抽象。
- 定義的時候,定義抽象類和介面的思想不同:
設計抽象類是自下而上的過程,我子類需要,所以我定義抽象類;設計介面是自上而下的過程,我介面規範某一行為,我某類需要這個行為,我某類實現某介面。
- 抽象類可以有構造器,而介面不能有構造器。
核心區別:
呼叫者使用的動機不同,實現介面 為了使用其他規範的某一個行為;
繼承抽象類是為了使用這個類的屬性和行為
複製程式碼
總結
- 抽象類和介面都不能直接例項化,若要例項化,抽象類變數必須指向實現所有的抽象方法的子類物件。介面變數必須指向實現所有介面方法的類物件。
- 抽象類要被子類繼承,介面要被類實現。
- 介面只能做方法申明,抽象類中可以做方法申明,也可以做方法實現。
- 介面裡定義的變數只能是公共的靜態的常量,抽象類中的變數是普通變數。
- 抽象類中抽象方法必須全部被子類實現,如果子類不能全部實現父類抽象方法,那麼 該子類只能是抽象類,同樣,一個實現介面的時候,如不能全部實現介面方法,那 麼該類也只能是抽象類。
- 抽象方法只能申明,不能實現,介面是設計的結果,抽象類是重構的結果。
- 抽象類裡可以沒有抽象方法。
- 如果一個類裡有抽象方法,那麼這個類一定是抽象類。
- 抽象方法要被實現,所以不能是靜態的。也不能是私有的。
- 介面可繼承介面,並可多繼承介面,但類只能單繼承。
- 抽象類主要用來抽象類別,介面主要用來抽象功能。
- 抽象類中,且不包含任何實現,派生類必須覆蓋他們,介面中的所有方法都必須是未實現的。
使用場景
再設計類時,如果有些方法我們能確定,而有些方法不能確定,這時候就可以定義成抽象類。抽象類的應用場景非常多,例如模板方法模式就是抽象類的一個應用,JDK中inputStream和outputStream也是抽象類的一個應用,這兩個類定義瞭如何讀寫資料的方法,而沒有定義從哪裡去讀,具體從哪裡讀是由具體的實現子類確定。
我們在定義相互呼叫規則時,可以使用介面,面向介面進行程式設計的好處,就是能極大地降低軟體系統的耦合性,介面的定義按照介面進行呼叫,而實現者去實現介面。 在JDK中存在很多針對介面的程式設計,例如用於我們比較兩個物件的Comparable介面就是一個典型的案例,我們在自定義物件時,如果實現了該介面,那麼我們把物件儲存到treeset集合中,treeset將針對介面呼叫物件的compareTo方法。