头图

关于培训出身的程序员

圈子里,科班程序员“鄙视”培训机构程序员似乎已经隐隐成了一种共识。

“培训机构出来的程序员几乎什么也不会。不用看技术,刚刚培训出来的就是假大空。”网上甚至有更偏激的观点:培训班出来的全是垃圾,没有例外。

“培训机构出来的程序员基础薄弱很多,对某项语言可能有点熟悉但是很死板,代码环境一变可能就不会了,更差点的就直接被当成‘敲代码机器’了。很多人刚出来没有完整的案例操作,对这个行业还没有基础的认知,更别提思维、格局了。”

外界的这些言论无形中成为这些程序员心中的刺。很多培训机构出来的程序员在面试时是不会告诉用人单位培训背景的,有些人甚至会刻意隐瞒这段经历。“好多人被公司知道后就直接被辞退了,有的人还背着房贷、要赡养老人,但也没有办法。”

在很多人看来,培训机构其实是复制粘贴出来一堆差不多的人。“人家用大学四年打下基础,你培训几个月速成,很难完全领悟。”

但是科班程序员对这个问题感受好像并没有那么强烈,硕士毕业直接进入互联网公司工作的一位程序员说:“员工间谈不上鄙视,主要是看他们做的工作是不是好,但是一般情况下,培训班出来的工作质量不会太好。代码混乱、质量低、bug 多、没法维护,别人接手你的代码会很难受。”

“大家都是打工的,没有人会管你是哪里出来的。我们只会鄙视那些技术垃圾的、配合不了的。至于你是哪里出来的,并不会很在意。”补充道。

对于这个问题,某互联网公司技术总监曾经说过:外界的确存在一些公司认为培训班出来的程序员是“速成品”,然后卡简历的情况。“其实不是说培训机构出来的程序员表现都不行,只是表现不好的程序员可能都有过培训经历,行业里一传十、十传百就会认为培训机构出来的程序员都不行。”

培训 + 外包拿下字节的历程

我认识的一位培训+外包出身的程序员,目前开发8年多,最近去面试了今日头条的移动Android资深工程师,定薪年薪60W!果然功夫还是不负有心人啊,只要坚持努力总会有结果的!

接下来分享一下他的Android资深工程师面试经历:

今日头条的面试主要分为三轮,首先是基础面试,基本面试10个题左右。

第一面是北京的开发进行视频面试,有理论和编程题组成。用的是在线编程工具,如下图;

第一面

1,请编程实现单例模式,懒汉和饱汉写法

//饱汉写法
public static Singleton getInstance() {  
        if (singleton == null) {    
            synchronized (Singleton.class) {    
               if (singleton == null) {    
                  singleton = new Singleton();   
               }    
            }    
        }    
        return singleton;   
    }

   //懒汉写法
    private static final Singleton1 single = new Singleton1();  
    //静态工厂方法   
    public static Singleton1 getInstance() {  
        return single;  
    } 

2,请编程实现Java的生产者-消费者模型

看到这个有点懵逼,要是大学毕业的时候写这个肯定没问题,这都工作多年,这也只能按照自己的思路写了。我使用synchronized锁以及wait notify实现一个比较简单的。这里就不作展示了

3,HashMap的内部结构? 内部原理?

关于HashMap的问题,不再详述,这方面的资料也挺多,不过需要注意的是Java1.7和1.8版本HashMap内部结构的区别。

4,请简述Android事件传递机制, ACTION_CANCEL事件何时触发?

第一个问题不做解释。。

关于ACTION_CANCEL何时被触发,系统文档有这么一种使用场景:

在设计设置页面的滑动开关时,如果不监听ACTION_CANCEL,在滑动到中间时,如果你手指上下移动,就是移动到开关控件之外,则此时会触发ACTION_CANCEL,而不是ACTION_UP,造成开关的按钮停顿在中间位置。
意思是当滑动的时候就会触发,不知道大家搞没搞过微信的长按录音,有一种状态是“松开手指,取消发送”,这时候就会触发ACTION_CANCEL。>

5,Android的进程间通信,Liunx操作系统的进程间通信。
关于这个问题也是被问的很多,不多啰嗦了解释了。

6,JVM虚拟机内存结构,以及它们的作用。
这个问题也比较基础,JVM的内存结构如下图所示。

7,简述Android的View绘制流程,Android的wrap_content是如何计算的。

8,有一个整形数组,包含正数和负数,然后要求把数组内的所有负数移至正数的左边,且保证相对位置不变,要求时间复杂度为O(n), 空间复杂度为O(1)。例如,{10, -2, 5, 8, -4, 2, -3, 7, 12, -88, -23, 35}变化后是{-2, -4,-3, -88, -23,5, 8 ,10, 2, 7, 12, 35}。

要实现上面的效果有两种方式:

第一种:两个变量,一个用来记录当前的遍历点,一个用来记录最左边的负数在数组中的索引值。然后遍历整个数组,遇到负数将其与负数后面的数进行交换,遍历结束,即可实现负数在左,正数在右。

第二种:两个变量记录左右节点,两边分别开始遍历。左边的节点遇到负值继续前进,遇到正值停止。右边的节点正好相反。然后将左右节点的只进行交换,然后再开始遍历直至左右节点相遇。这种方式的时间复杂度是O(n).空间复杂度为O(1)

//方法1
 public void setParted(int[] a){  
        int temp=0;  
        int border=-1;  
 
        for(int i=0;i<a.length;i++){  
            if(a[i]<0){  
                temp=a[i];  
                a[i]=a[border+1];  
                a[border+1]=temp;  
                border++;  
            }  
        }  
        for(int j=0;j<a.length;j++){  
            System.out.println(a[j]);  
        }  
    }  
 
//方法2
public void setParted1(int[] a,int left,int right){  
        if(left>=right||left==a.length||right==0){  
            for(int i=0;i<a.length;i++){  
                System.out.println(a[i]);  
            }  
            return ;  
        }  
        while(a[left]<0){  
            left++;  
        }  
        while(a[right]>=0){  
            right--;  
        }  
        if(left>=right||left==a.length||right==0){  
            for(int i=0;i<a.length;i++){  
                System.out.println(a[i]);  
            }  
            return;  
        }  
        swap(a,left,right);  
        left++;  
        right--;  
        setParted1(a,left,right);  
    }  
     private void swap(int a[],int left,int right){  
        int temp=0;  
        temp=a[left];  
        a[left]=a[right];  
        a[right]=temp;  
    }  
    public static void main(String[] args) {  
        int a[]={1,2,-1,-5,-6,7,-7,-10};  
        new PartTest().setParted1(a,0,a.length-1);   
    }  

显然,第二种实现的难点比较高,不过只要此种满足条件。

第二面

1,bundle的数据结构,如何存储,既然有了Intent.putExtra,为啥还要用bundle。

bundle的内部结构其实是Map,传递的数据可以是boolean、byte、int、long、float、double、string等基本类型或它们对应的数组,也可以是对象或对象数组。当Bundle传递的是对象或对象数组时,必须实现Serializable 或Parcelable接口。

2,android的IPC通信方式,是否使用过

这方面的资料比较多,也不方便阐述

3,Android的多点触控如何传递 核心类
4,asynctask的原理 AsyncTask是对Thread和Handler的组合包装。
5,android 图片加载框架有哪些,对比下区别
6,图片框架的一些原理知识
7,其他的一些Android的模块化开发,热更新,组件化等知识。

三面;主流框架

在Android面试的时候,经常会被问到一些Android开发中用到的一些开发框架,如常见的网络请求框架Retrofit/OkHttp,组件通信框架EventBus/Dagger2,异步编程RxJava/RxAndroid等。结合本次面试,整理下上面的几个框架,大家可以拿去,面试备用

1. EventBus

EventBus是一个Android发布/订阅事件总线,简化了组件间的通信,让代码更加简介,但是如果滥用EventBus,也会让代码变得更加辅助。

面试EventBus的时候一般会谈到如下几点

(1)EventBus是通过注解+反射来进行方法的获取的

(2)使用ConcurrentHashMap来保存映射关系 调用实体的构建:调用实体中对于Object,也就是实际执行方法的对象不应该使用强引用而是应该使用弱引用,因为Map的static的,生命周期有可能长于被调用的对象,如果使用强引用就会出现内存泄漏的问题。

(3)方法的执行 使用Dispatcher进行方法的分派,异步则使用线程池来处理,同步就直接执行,而UI线程则使用MainLooper创建一个Handler,投递到主线程中去执行。

2.Retrofit

要明确EventBus中最核心的,就是动态代理技术。

Java中的动态代理:

首先动态代理是区别于静态代理的,代理模式中需要代理类和实际执行类同时实现一个相同的接口,并且在每个接口定义的方法前后都要加入相同的代码,这样有可能很多方法代理类都需要重复。而动态代理就是将这个步骤放入运行时的过程,一个代理类只需要实现InvocationHandler接口中的invoke方法,当需要动态代理时只需要根据接口和一个实现了InvocationHandler的代理对象A生成一个最终的自动生成的代理对象A。这样最终的代理对象A无论调用什么方法,都会执行InvocationHandler的代理对象A的invoke函数,你就可以在这个invoke函数中实现真正的代理逻辑。

Retrofit中的动态代理

Retrofit中使用了动态代理是不错,但是并不是为了真正的代理才使用的,它只是为了动态代理一个非常重要的功能,就是“拦截”功能。我们知道动态代理中自动生成的A对象的所有方法执行都会调用实际代理类A中的invoke方法,再由我们在invoke中实现真正代理的逻辑,实际上也就是A的所有方法都被A对象给拦截了。 而Retrofit的功能就是将代理变成像方法调用那么简单。

Retrofit作用

Retrofit实际上是为了更方便的使用Okhttp,因为Okhttp的使用就是构建一个Call,而构建Call的大部分过程都是相似的,而Retrofit正是利用了代理机制带我们动态的创建Call,而Call的创建信息就来自于你的注解。

3.OkHttp

请求任务队列

Okhttp使用了一个线程池来进行异步网络任务的真正执行,而对于任务的管理采用了任务队列的模型来对任务执行进行相应的管理,有点类似服务器的反向代理模型。Okhttp使用分发器Dispatcher来维护一个正在运行任务队列和一个等待队列。如果当前并发任务数量小于64,就放入执行队列中并且放入线程池中执行。而如果当前并发数量大于64就放入等待队列中,在每次有任务执行完成之后就在finally块中调用分发器的finish函数,在等待队列中查看是否有空余任务,如果有就进行入队执行。Okhttp就是使用任务队列的模型来进行任务的执行和调度的。

复用连接池

Http使用的TCP连接有长连接和短连接之分,对于访问某个服务器的频繁通信,使用短连接势必会造成在建立连接上大量的时间消耗;而长连接的长时间无用保持又会造成资源你的浪费。Okhttp底层是采用Socket建立流连接,而连接如果不手动close掉,就会造成内存泄漏,那我们使用Okhttp时也没有做close操作,其实是Okhttp自己来进行连接池的维护的。在Okhttp中,它使用类似引用计数的方式来进行连接的管理,这里的计数对象是StreamAllocation,它被反复执行aquire与release操作,这两个函数其实是在改变Connection中的List<WeakReference<StreamAllocation>>大小。List中Allocation的数量也就是物理socket被引用的计数(Refference Count),如果计数为0的话,说明此连接没有被使用,是空闲的,需要通过淘汰算法实现回收。

4. RxJava

从15年开始,前端掀起了一股异步编程的热潮,在移动Android编程过程中,经常会听到观察者与被观察者等概念。

观察者与被观察者通信

Observable的通过create函数创建一个观察者对象。

public final static <T> Observable<T> create(OnSubscribe<T> f) {
  return new Observable<T>(hook.onCreate(f));
}

Observable的构造函数如下:

protected Observable(OnSubscribe<T> f) {
  this.onSubscribe = f;
}

创建了一个Observable我们记为Observable1,保存了传入的OnSubscribe对象为onSubscribe,这个很重要,后面会说到。

onSubscribe方法

public final Subscription subscribe(Subscriber<? super T> subscriber) {
  return Observable.subscribe(subscriber, this);
}
private static <T> Subscription subscribe(Subscriber<? super T> subscriber, Observable<T> observable) {
  ...
  subscriber.onStart();
  onSubscribe.call(subscriber);
  return hook.onSubscribeReturn(subscriber);
}

线程切换过程(Scheduler)

RxJava最好用的特点就是提供了方便的线程切换,但它的原理归根结底还是lift,使用subscribeOn()的原理就是创建一个新的Observable,把它的call过程开始的执行投递到需要的线程中;而 observeOn() 则是把线程切换的逻辑放在自己创建的Subscriber中来执行。把对于最终的Subscriber1的执行过程投递到需要的线程中来进行。

Android进阶之路

对我们开发者来说,一定要打好基础,随时准备战斗 。不论寒冬是否到来,都要把自己的技术做精做深。虽然目前移动端的招聘量确实变少了,但中高端的职位还是很多的,这说明行业只是变得成熟规范起来了。竞争越激烈,产品质量与留存就变得更加重要,我们进入了技术赋能业务的时代。

技术之路就是漫长的打怪升级,不断学习前人的踩坑经验才能提升自己,切忌闭门造车! 所谓独乐乐不如众乐乐,在我当时确立好Android方向时,就已经开始梳理自己的成长路线了,包括技术要怎么系统地去学习,都列得非常详细,免费分享给大家。

从架构基础开始,分了8个模块来逐步从基础进阶到架构师的环节:

架构师筑基语言基础

目前Android APP开发主流语言就是Java语言,Java语言最大的特性就是提高了软件的交互可能性,可以说安卓手机几乎所有应用程序都是利用Java语言来进行编写的。

设计思想解读开源框架

随着互联网企业的不断发展,产品项目中的模块越来越多,用户体验要求也越来越高,想实现小步快跑、快速迭代的目的越来越难,插件化技术应用而生。如果没有插件化技术,美团、淘宝这些集成了大量“app”的应用,可能会有几个g那么大。

所以,当今的Android移动开发,不会热修复、插件化、组件化,80%以上的面试都过不了。

360°全方位性能调优

在不同层次的开发工程师手里,因为技术水平的参差不齐,即使很多手机在跑分软件性能非常高,打开应用依然存在卡顿现象。

另外,随着产品内容迭代,功能越来越复杂,UI页面也越来越丰富,也成为流畅运行的一种阻碍。综上所述,对APP进行性能优化已成为开发者该有的一种综合素质,也是开发者能够完成高质量应用程序作品的保证。

Android框架体系架构

Android开发者也往往因为网上Copy代码习惯了而导致对这块经常“使用”的代码熟悉而又陌生:熟悉的是几乎天天在和它们打交道, 天天在复制这些代码 ;陌生的是虽然天天和这些代码打交道,但是并没有深入研究过这些代码的原理,代码深处的内涵。

NDK模块开发(音视频开发系列)

在Android中,NDK是一系列工具的集合,主要用于扩展Android SDK。NDK提供了一系列的工具可以帮助开发者快速的开发C或C++的动态库,并能自动将so和Java应用一起打包成apk。

Flutter学习进阶

自2020 年来无疑是 Flutter 技术如火如荼发展的一年。现在这门技术也依然非常有价值。

每一个移动开发者都在为 Flutter 带来的“快速开发、富有表现力和灵活的 UI、原生性能”的特色和理念而痴狂,从超级 App 到独立应用,从纯 Flutter 到混合栈,开发者们在不同的场景下乐此不疲的探索和应用着 Flutter 技术,也在面临着各种各样不同的挑战。

微信小程序开发

微信小程序作为现在比较火的编程开发应用场景之一,深受市场的青睐,这让不少开发者眼馋不已。但是对于初学者来说,就完全摸不着头脑了,不知道微信小程序开发制作需要学习那些知识,有需要的朋友可以参考本篇。

最后分享一份面试宝典,祝每个人面试顺利。

Android BAT高级开发面试题及答案解析

面试的前一周时间内,就可以开始刷题冲刺了。请记住,刷题的时候,技术的优先,算法的看些基本的,比如排序等即可,而智力题,除非是校招,否则一般不怎么会问。

以上内容均免费分享给大家,需要完整版的朋友,点这里可以看到全部内容。或者点击 【这里】 查看获取方式。

最后一些思考:
我有时候经常会思考,以后Android开发该往什么方向走?现在我们所掌握的技术以后会不会被替代淘汰?
哪些方向会成为绝对主流方向呢?
像如今前景比较好的音视频来说,并不是用Android去开发,而属于NDK开发,用的C++语言。
Flutter跨平台开发现在只是一种选择,面试加分项,实际用的公司不多。
人工智能方向,门槛对于大多Android开发来说很高,没有相关专业的名校硕士学历很难入场,对数据结构算法等要求很高。
未来有点难以掌控的感觉。但是还好,可以掌控现在。技术无止境,不能停止学习。如果不能引领潮流,那就做一个跟随者吧!

夏夏是只猫
42 声望13 粉丝

此生最大愿望就是不秃头!!!