从Android到Java (一)

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.

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容

  • (一)Java部分 1、列举出JAVA中6个比较常用的包【天威诚信面试题】 【参考答案】 java.lang;ja...
    独云阅读 6,937评论 0 62
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,293评论 18 399
  • 从三月份找实习到现在,面了一些公司,挂了不少,但最终还是拿到小米、百度、阿里、京东、新浪、CVTE、乐视家的研发岗...
    时芥蓝阅读 42,014评论 11 349
  • 自媒体时代的学习,选择如此之多,如微博、微信公众号或是其他听课的App,随时随地,随心所欲,大多数都可以自行安排时...
    懒癌又努力的猪妹妹阅读 351评论 0 0
  • 把事情变得轻松、简单,让一家人都简简单单、轻轻松松... 轻松而有序地做事,工作,让孩子找到学习的规律,有序而简单...
    里娃阅读 158评论 0 2