從Android到Java

相信不少的Android開發者和我同樣,當初學習Android開發時,對Java的學習並非很是深刻;大體瞭解了類和對象是怎麼回事,對多線程及網絡編程有了一個簡單的瞭解以後,便投入到了Android開發中;感受當時瞭解的東西就夠用了,一些比較偏的點,遇到了在網上找一下就能解決問題了,總的來講不影響平常工做。javascript

可是,隨着時間的流逝,慢慢感受本身遇到了瓶頸,基礎的東西都會了;嘗試去學習一些進階的東西,卻發現很是的難;因爲不瞭解註解和反射,第一次使用Retrofit框架的時候,徹底就是一臉懵逼,搞不懂@是幹什麼用的;嘗試去解讀Glide的源碼,因爲缺少對泛型及設計模式的瞭解,連Glide底層的網絡請求時在哪裏實現都找不到;基礎不牢,寫代碼老是挖坑……。java

總之應了那句話,出來混老是要還的。想在這條道上長遠的走下去,曾經欠下的東西都得補回來。因此,這段時間對惡補了一寫Java基礎,總結了一些以前理解有誤差或錯誤的點,在這裏權當筆記記錄一下,以後又新的心得體會會持續更新。編程

基礎

基礎數據類型的範圍

類型 位數 值域
boolean JVM 決定 true/false
char 16bit 0~65535
byte 8bit -128~127
short 16bit -32768~32767
int 32bit -2147483648~2147483648
long 64bit 很大
float 32bit 範圍可變
double 64bit 範圍可變

引用變量

People man=new People();
People women=new People();
People boy=man;複製代碼

man,women,boy應該稱爲引用變量,它保存的是存取對象的方法;引用變量並非對象的容器,而是相似指向對象的指針。設計模式

以上賦值代碼表達的意思,一個People類型的變量man引用到堆上建立的一個People對象。網絡

"==" 和 "equals"

== 兩個引用變量是否引用到堆上的同一個對象。或者是基礎數據類型(int,long等)的變量是否相等。
equals 兩個對象的內容是否同樣多線程

關於"=" 產生的一個低級bug。

以前寫代碼的時候,就關於變量賦值產生過一個很經(di)典(ji)的bug。這裏來分享一下。背景很簡單,就是作RecyclerView的下拉刷新,代碼以下,很簡單。框架

public class MainActivity extends AppCompatActivity implements SwipeRefreshLayout.OnRefreshListener {

    private List<String> datas = new ArrayList<>();
    private MyAdaptetr mMyAdaptetr;
    SwipeRefreshLayout mSwipeRefreshLayout;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        mSwipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.swipeLayout);
        mSwipeRefreshLayout.setOnRefreshListener(this);
        RecyclerView mRecyclerView = (RecyclerView) findViewById(R.id.recyclerView);
        mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
        datas = getData();
        mMyAdaptetr = new MyAdaptetr(datas);
        mRecyclerView.setAdapter(mMyAdaptetr);

    }

    private List<String> getData() {
        List<String> datas = new ArrayList<>();

        for (int i = 0; i < 100; i++) {
            datas.add("item " + i);
        }
        return datas;
    }

    @Override
    public void onRefresh() {
        datas.clear();
        datas = getData();
        mMyAdaptetr.notifyDataSetChanged();
        mSwipeRefreshLayout.setRefreshing(false);
    }

}

private class MyAdaptetr extends RecyclerView.Adapter<MyAdaptetr.MyHolder> {
        private List<String> datas;

        public MyAdaptetr(List<String> datas) {
            this.datas = datas;
        }

        @Override
        public MyHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            View mView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false);
            return new MyHolder(mView);
        }

        @Override
        public void onBindViewHolder(MyHolder holder, int position) {
            holder.text.setText(datas.get(position));
        }

        @Override
        public int getItemCount() {
            return datas.size();
        }

        class MyHolder extends RecyclerView.ViewHolder {
            TextView text;

            public MyHolder(View itemView) {
                super(itemView);
                text = (TextView) itemView.findViewById(R.id.text);
            }
        }
    }複製代碼

實際場景代碼要比這複雜不少,這裏爲了說明問題,寫了一個簡易的demo,但所要表達的問題一致,onRefresh裏的代碼是有問題的,你發現了嗎?dom

乍一看,這代碼貌似沒問題,可是執行下拉刷新後,列表直接被清空了,一條數據都顯示不出來。記得當初爲了找緣由,對MyAdapter各類修改,甚至懷疑本身是否是發現了一個Android系統的bug。徹底沒有去考慮datas=getData()這行代碼的意義。直到後來打斷點發現,mMyAdaptetr.notifyDataSetChanged() 執行後,再次去Adapter的onBindViewHolder方法中查看時,竟然發現的列表的size變成了0。這下但是徹底懵逼了。
後來作了以下修改,問題得以解決:ide

@Override
    public void onRefresh() {
        datas.clear();
        //datas = getData();
        datas.addAll(getData());
        mMyAdaptetr.notifyDataSetChanged();
        mSwipeRefreshLayout.setRefreshing(false);
    }複製代碼

當時,雖然把問題解決了,可是很是的不理解。爲何第一執行datas=getData()時就能夠,第二次執行就不行了呢?datas=getData() 和 datas.addAll(getData())有區別嗎?不都是把新建的列表賦給datas嗎?函數

結合上面關於引用變量的賦值解釋,這個問題就很容易理解了。

  • 在onCreate()方法中,第一次執行datas=getData()時,在內存中建立了一段數據空間,同時datas這個變量指向了這段空間。
  • datas.clear(),清空了datas所指向的這段內容空間的數據。
  • datas=getData(),讓datas 指向了一段新的內存空間
  • datas.addAll(getData()) 向datas第一次指向的內容空間,從新填充數據。

而當咱們建立MyAdapter對象時,因爲MyAdapter只會執行一次

public MyAdaptetr(List<String> datas) {
            this.datas = datas;
        }複製代碼

所以,MyAdapter內部的datas指向的永遠都是咱們第一次建立的那塊存儲區域。
到這裏,咱們就很容易理解這個bug的本質了。

"=", 不是的賦值這麼簡單 !

多態

應用變量的類型能夠是實際對象類型的父類

//Man 繼承自People類

People mPeople=new Man();複製代碼

方法的覆蓋,參數,返回值類型,均不能改變,存取權限不能下降
方法的重載,參數不一樣,返回值類型,存取權限能夠改變,與多態無關。

People mPeople=new People();
Object o=mPeople;
int code=o.hashCode();
o.toString();
o.eat();複製代碼

編譯器是根據引用類型來判斷有哪些method能夠調用,而不是根據引用所指向的對象類型來判斷。所以,上述o.eat()將 沒法執行,即使People類這個方法,可是對於Object來講是未知的。

Java關鍵字

this 和 super

  • this 關鍵字是類內部當中對本身的一個引用,能夠方便類中方法訪問本身的屬性

  • 要從子類調用父類的方法可使用super關鍵字來引用

super.onResume()複製代碼

構造函數之this() 和 super()

使用this()來從某個構造函數調用同一個類的另一個構造函數。this()只能用在構造函數中,而且必須是第一行被執行的語句。

super() 用來調用父類的構造函數,必須是第一個被執行的語句,所以,super()和this() 不能共存。

之後自定義View的時候,構造函數該怎麼寫,終於有譜了。

abstract

被abstract標記的類稱爲抽象類,沒法被實例化;
抽象類任然能夠做爲引用變量的類型。
被abstract標記的方法被聲明時沒有內容,以分號結束;非抽象子類必須實現此方法。
若是有一個類當中有任意一個抽象的方法,那麼這個類也必須是抽象的;固然這個抽象類當中,同時能夠包括其餘非抽象的方法。

若是要限制一個類被實例化,除了使用abstract標記爲抽象類以外,還能夠將其構造函數標記爲private

static

被static標記的方法,靜態方法,能夠不須要具體的對象,可直接由類調用。
靜態方法不能調用非靜態的變量,由於他沒法得知是那個實例變量。同理可得,靜態方法不>能調用非靜態的方法。
靜態變量是共享的,同一類的全部實例共享一份靜態變量,它會在該類的任何靜態方法 執行以前就初始化,沒有被賦值時,會被設定爲該變量所屬的默認值。

Math.random();
Math.min(1,2);複製代碼

所以,帶有靜態方法的類,通常來講能夠是抽象的,沒必要要被初始化;但不是必須的。

final

static final double PI=3.1415925複製代碼

final 類型的靜態變量爲常量
final 類型的變量一旦被賦值就不能再更改
final 類型的方法不能被覆蓋
final 類型的類不能被繼承

synchronized

防止兩個線程同時進入同一對象的同一個方法

public class TestSync implements Runnable {

    private static final int COUNT = 500000;
    private int count;


    @Override
    public void run() {
        for (int i = 0; i < COUNT; i++) {
            increment();
            System.err.println("count=" + count);
        }
    }

    private synchronized void increment() {
        count = count + 1;
    }

    public static void main(String[] args) {
        TestSync mRunnable = new TestSync();
        Thread a = new Thread(mRunnable);
        Thread b = new Thread(mRunnable);
        a.start();
        b.start();
    }
}複製代碼

上面的代碼中,當a,b 兩個線程同時開始執行run方法時,在缺乏synchronized的狀況下,兩個線程將由虛擬機的調度器控制執行,所以當a線程完成count+1時,還沒來得及賦值操做,就被切換到了b線程,b線程再次執行count+1操做時,就會丟掉a完成的工做,最終會致使結果不正確,兩個線程內各自經歷COUNT次循環後,並無使最終的值達到COUNT*2 的結果。

只有increment()方法被synchronized修飾後,就能夠保證在每次在a線程完整了執行完了increment方法後,b線程才能夠執行該方法,反之亦然。這樣就能夠保證最終的執行結果的正確性

須要注意的是,synchronized(鎖)並非加在方法上,而是配在對象上。某個對象都有一把「鎖」和一把「鑰匙」存在,大部分時候並無實際意義,只有對方法使用synchronized(同步化)以後,鎖纔會變得有意義。當同時有多個線程須要執行同步化方法時,只有取得當前對象鎖的鑰匙的線程才能進入該同步化方法。其餘線程只有在得到鑰匙的線程完整的執行完畢同步化方法時,纔會得到鑰匙從而進入同步化方法,不然,就只能等待。所以,使用synchronized會消耗額外的資源,會使方法執行變慢,所以要謹慎使用。同時,使用不當會致使死鎖問題的產生。

當對象有多個同步化的方法時,鑰匙仍是隻有一把。得到鑰匙的某個線程進入到該對象的同步化方式時,其餘線程也沒法進入該對象其餘的同步化方法。此時,惟一能作的事情就是等待。

未完待續。。。。

關於Kotlin

Google I/O 大會以後Kotlin很火,那麼咱們是否意味着在Android開發中他會取代Java呢?

Google’s Java-centric Android mobile development platform is adding the Kotlin language as an officially supported development language, and will include it in the Android Studio 3.0 IDE.

這段話應該說的很清楚了。

相關文章
相關標籤/搜索