从高频笔/面试题思考Android学习/进阶路线(Java篇)

写在前面

标题谈进阶,属实有一些夸大。
我一直在思考什么样的文章才是一篇好文章,我的定义是首先要有人看,其次重要的是内部有价值。所以针对于这个出发点,我决定从大家比较关注的面试题入手,然后尝试从中分析对Android进阶路线的帮助。
虽然我只是一个应届生,但是我善于总结别人的经验,不要脸的去向别人请教。我实习的公司有蚂蚁金服技术专家,360浏览器技术负责人...在与他们的请教(后来他们见到我都躲着走,哈哈)中,我学到了很多东西。因此我会结合这一系列的内容,把这篇文章写出来。算是即使一种开源分析,也是一种供我个人复盘的场所。

所以我起了这么一个很飘的名字,当然我也把这个名字告诉了我的基友们,让他们提提意见:

我们看你是:飘了,拽了,感觉自己个头都不矮了;疯了,狂了,打算在宇宙之间称王了。

可能这是一个长久的任务,会时不时的更新,会不会开GitHub,或者其他还不好说。不过点个赞,整个关注,肯定不迷路。哈哈~


开始

Java篇

毫无疑问,我们大多数入坑都是因为这门综合性复合毒品:Java(当然可能还有像RN这种技术,不过因为我不了解所以这部分暂不涉及),因此开始部分将从Java基础入手。

当然也会涉及到最近风头正盛的Kotlin。关于Kotlin的部分我还在思考怎么把它写下来,因为只针对Kotlin的面试还真没有,所以通过面试点去总结Kotlin显然不现实。所以Kotlin相关的部分,我会暂时以笔记的形式接入。

知识点大杂烩

1、谈一谈多态?

这种问题相当的宽泛,而且随便百度就可以搜到一大堆的内容。但是我觉得仅仅是背诵概念真的没有意思。更多的应该从实战中去理解概念的含义。毕竟它们算是叩开Java大门的第一个概念,但又是贯穿我们整个开发生涯的概念。
由其当我们阅读优秀框架是,这种思想就会潜移默化的帮我们化解很多设计上的难题。

2、常见集合?

对于常用集合的源码分析是很高频的问题。而且也能被问得很细。
比如:

  • 我在面试实习岗的时候,也就是被我之后的导师问过这方面的问题。当时从ArrayList聊到LinkedList再到HashMap,甚至问到了JDK1.8的改版,进而问到红黑树(虽然也没怎么答好)。
  • 还有在面知乎的时候,被问到:HashMap,index重复后,新Node结点挂在这个链表的头还是尾....
  • 再有的就是被问到,ConcurrentHashMap以及分段锁的...当时我被问到这个东西的时候是一脸懵逼?锁还能分段?优秀优秀,惹不起...

所以关于常见集合其实只能靠自己去看源码去总结。毕竟他们属于我们日常开发的熟客,还不深入的了解一番?

3、线程同步相关

同步相关的内容,问的比较多的是synchronized和Lock的异同点。关于区别还是很好回答的,不过一旦面试官追问,那考察的就是我们的积累问题了。
比如:

  • synchronized是怎么实现的?
  • 写一套单例,为什么要加锁?不加不行么?volatile关键字在这做什么用?其他方式不行么?只用static可不可以保证单例?了解过枚举写单例么?
  • 自旋锁、悲观锁、乐观锁、分段锁了解么?CAS了解么?AtomicInteger相关的类为了解决什么问题?

必须要插嘴,这些问题我都被问过,我是一个2018年的应届生。不知道看官觉得难不难,反正我是很懵的...因为在我看来,Android开发好像很少用到同步。其实针对这个问题我有问过我的Leader。以下是我Leader的原话:

很少用到?你才开发了几天?做了几个项目?...等你做久了,你才真正的发现就是TM的很少用。但是我给你解释一下为什么。有些Android面试官是Java转过来的,问一些同步内容很正常;其实更多的是现在狼多肉少,面试官为了更好的筛选候选人。日常开发用的的确很少,就像咱们这种小项目框架都能做...不过你不学,总有人学,那你就被刷掉呗。其实当你去尝试编写涉及到网络的框架时,你会发现同步是一个比较重的环节,如果做UI或作是普通业务,同步的考虑并不多。

4、JVM相关

其实最开始我是一直不理解// 最初我一直不理解学习JVM对做Android有什么帮助,但是现在明白了,它本身就不是Android层面的内容,所以学习它的意义并不是在于直接的对Android能力进行提升,但是JVM可以帮助我们更好的理解Java运行机制,所以也就间接的帮助我们提升对开发Android的理解。
入坑写了第一行代码时,一直不明白这一行行代码是怎么运行起来的。当开始尝试学习和理解JVM后,这个疑问就烟消云散了因为之前是会写代码,而不懂原理,现在连代码也不会写了~
当然这只是开玩笑,接下来,我谈一谈我在学习JVM后,对Java的理解(如有不当之处,还望评论区指出,谢谢啦)。首先,我们的代码都是一个个.java文件,通过编译后变成.class文件,当我们需要这个类时,就会将这个包含二进制文件的class文件装载到内存中。将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对象,用来封装方法区内的数据结构。而我们的new就是通知JVM去给这个类,分配内存,构建内存地址,并赋值给引用。当我们执行对应的方法时,我们的方法便被封装成了JVM中栈帧的概念,而栈帧运行在中,随着方法的结束和开始,对应了栈帧的弹出和压入。

5、int和Integer的坑。


public class Main{

    public static void main(String[] args) {
        //例子1
        Integer i1 = 66, i2 = 66, i3 = 166, i4 = 166;
        System.out.println(i1 == i2);//true
        System.out.println(i3 == i4);//false
    }
}

导致这个的原因是自动拆装箱机制。说白了就是JVM帮我们对int提升为Integer,或者把Integer拆为int。
因为==比较的是对象的地址是否相同,所以这个问题就转化为了f1和f2的内存地址是否相同。这里就引入了装箱机制。 因为是把100赋值给了一个Integer类型的引用,所以这里产生了一次装箱操作,而装箱的本质就是调用了Integer.valueOf(int i)。

    //而这里的low和high,在IntegerCache被初始化的时候分别给赋值成了-128和127
    public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }

看了源码,这个问题很清晰,因为在装箱过程中使用了IntegerCache,而100正好是这个范围,所以不会去new对象,而166则相反。

那么再让我们看另一个例子:


//例子2
Integer a = new Integer(6);
Integer b = 6; // 将6自动装箱成Integer类型
int c = 6;
System.out.println(a == b); // false 两个引用没有引用同一对象
System.out.println(a == c); // true a自动拆箱成int类型再和c比较

而Double和Float则没有缓存:

public static Double valueOf(double d) {
  return new Double(d);
}

public static Float valueOf(String s) throws NumberFormatException {
        return new Float(FloatingDecimal.getThreadLocalInstance().readJavaFormatString(s).floatValue());
}

尾声

暂时想到了这么多,就先写这么多,如果以后找到其他更好的内容,再更新一个《补充篇》吧。

推荐一个立志减少IT面试踩坑的公众号

因为身边的同学从事互联网相关职业的比较多,并且大家闲时聊天时总会吐槽找工作有很多坑,所以打算把身边同学找工作的经验(包括Android/Java/Web),统统收集起来。提供给想从事这方面同学,希望圈内好友可以共同进步,共同少踩坑。

从高频笔/面试题思考Android学习/进阶路线(Java篇)
求关注,不迷路
上一篇:Java类加载机制,这篇大概、也许、可能就够了


下一篇:序列特征在推荐算法中的应用