美文网首页
从Android到Java (一)

从Android到Java (一)

作者: IAM四十二 | 来源:发表于2017-05-30 23:39 被阅读191次
    Java

    相信很多的Android开发者和我一样,当初学习Android开发时,对Java的学习并不是非常深入;大致了解了类和对象是怎么回事,对多线程及网络编程有了一个简单的了解之后,便投入到了Android开发中;感觉当时了解的东西就够用了,一些比较偏的点,遇到了在网上找一下就能解决问题了,总的来说不影响日常工作。

    但是,随着时间的流逝,慢慢感觉自己遇到了瓶颈,基础的东西都会了;尝试去学习一些进阶的东西,却发现非常的难;由于不了解注解和反射,第一次使用Retrofit框架的时候,完全就是一脸懵逼,搞不懂@是干什么用的;尝试去解读Glide的源码,由于缺乏对泛型及设计模式的了解,连Glide底层的网络请求时在哪里实现都找不到;基础不牢,写代码总是挖坑……。

    总之应了那句话,出来混总是要还的。想在这条道上长远的走下去,曾经欠下的东西都得补回来。所以,这段时间对恶补了一写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里的代码是有问题的,你发现了吗?

    乍一看,这代码貌似没问题,但是执行下拉刷新后,列表直接被清空了,一条数据都显示不出来。记得当初为了找原因,对MyAdapter各种修改,甚至怀疑自己是不是发现了一个Android系统的bug。完全没有去考虑datas=getData()这行代码的意义。直到后来打断点发现,mMyAdaptetr.notifyDataSetChanged() 执行后,再次去Adapter的onBindViewHolder方法中查看时,居然发现的列表的size变成了0。这下可是完全懵逼了。
    后来做了如下修改,问题得以解决:

        @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.

    这段话应该说的很清楚了。

    相关文章

      网友评论

          本文标题:从Android到Java (一)

          本文链接:https://www.haomeiwen.com/subject/xakdfxtx.html