文章标题
hello word
2020年,真的是特殊的的一年无论是对个人,国家,甚至全球。我们经历了难以想象的疫情,全国封禁宅家,停工停学,降薪裁员,这场疫情真的改变改变了很多人的命运,同样还有我。这一年,利用疫情全身心的投入跳槽换工作的准备,历时3个月,面了小影,有赞,快手,滴滴,字节,还投递了钉钉无反应。。。面过的都是拿到了书面或者口头offer最终选择去了字节哈。
明确自己的目标公司后,首先还是要问问自己有没有把握一次命中。没有,则需要沉淀自己,通过选择备选公司去打磨自己,目的是
坦诚清晰的沟通,对自己的未来有明确的规划,有上进心,有责任心,要能看出潜力,不卑不亢,自信但不要自负。
在软件设计的过程中设计模式占着举足轻重的作用,高可用的程序必定是建立在良好的设计上的,而设计模式正式这些良好设计的积累总结。设计模式又是符合面向的对象的六大原则而拓展出来的,如同抽象的具体实现,给软件开发者以指导作用。因此面向对象的设计原则是重中之重,下面将一一分析这六大原则。
当面试官问这道题是,他想考察什么内容呢?
面向对象编程的原则分别有六个
单一职责原则英文为Single Resposibility Principle,简单来说,一个类中应该是一组相关性很高的函数、数据的封装。但关于单一职责的划分界限并不是那么清晰,很多时候需要靠个人经验的界定。在开发功能之初,往往会将功能实现放在第一位。变量,方法都会堆积在一个类中方便调用,如一个简单的图片加载器包括:缓存的功能,网络图片的加载,未优化前代码如下
1 | public class ImageLoader { |
但如果了解单一职责的原则,我们就应该将他分为两个类进行实现ImageCache负责缓存,ImageLoader负责加载图片。优化后代码如下,
1 | public class ImageLoaderV1 { |
单一职责原则充分体现了面向对象特性中的封闭
开闭原则英文为Open Close Principle,是Java最基础的设计原则,它指导我们设计更稳定和灵活的系统。它的定义为对修改封闭,对扩展开放,要确保原有软件模块的正确性以及尽量少的影响原有模块就应该遵循开闭原则。通过分析上面的图片加载器,我们发现上面的图片加载器只是内存的缓存,我们需要扩展出磁盘缓存可用用户选择,这时我么会发现添加新的缓存方式需要修改原有的代码,且如果还需要扩展双缓存机制(内存+磁盘),则又需要修改原来的代码,这时候我们发现ImageCache这个类不支持缓存的拓展。这时候我们就应该考虑将缓存类进行抽象,定义出不同的缓存实现供ImageLoader引用,动态的添加不同的缓存方式,使程序更加稳定和灵活。代码如下
1 | // 缓存接口 |
由此,我们可以愉快的进行缓存的拓展,同时不需要修改原来的代码,符合开闭原则
里氏替换原则英文是Liskov Substitution Principle, 定义为所有引用基类的地方必须能透明的使用其子类的对象,里氏替换原则就是依赖于java特性中的继承和多态两大特性,而核心就是抽象,能使子类替换父类就是继承。
继承的优点
缺点
上面内存缓存和磁盘缓存都可以替代ImageLoader的变量,IImageCache就是体现了里氏替换原则原则。里氏替换原则和开闭原则往往是生死相依,不弃不离,里氏替换达到了对扩展开放对修改封闭的效果,两者都强调了一个重点特性抽象
本人从事Android客户端开发,最近在在深入Framwork学习,希望我的学习分享能帮到你。首先我们看一下关于Android系统的启动流程我们应该如何去分析,这里通过一张思维导图帮助大家整理思路。思维导图一方面罗列了本文的大纲,另一方面也是希望读者朋友可以保存图片至自己的复习资料里。以便随时通过该大纲去复习,可以关注我,收藏文章进一步沟通学习!
面对这道面试题,面试官试想考察什么呢?
Android中有哪些系统进程,具体我们可以在init.rc的配置找到相应的启动代码
1 | start zygote |
上面几个熟悉的服务进程都是通过init单独创建启动的,还有 一个我们熟悉的SystemServer,这个服务进程是通过Zygote进程创建而不是init进程。
关于Zygote的的相关知识可以参考跟我学:Android面试之深入Android Framework谈谈对Zygot的理解
它的启动流程是
在ZygoteInit.java的代码逻辑中,可以看到启动的关键代码
1 | int pid = Zygote.forkSystemServer(...) // 通过Zygote创建子线程 |
系统服务的启动指startBootstrapServices();startCoreServices();startOtherServices();内部的服务启动。启动startBootstrapServices()中包括了熟悉的AMS,PMS,StartPowerManager等等。startCoreServices()包含了BatteryService,UsageStatsService,WebViewUpdateService等等。以及startOtherServices()中的其他服务。
通过源码的阅读,我们可以发现最终服务是通过ServiceManager.addService(…) 将服务进行注册,然后才可以可被其他进程可见和使用
主线程:并没有找到哪个系统服务是在主线程的中的
工作线程: AMS,PMS运行在自己的工作线程,或者在公用的工作线程DisplayThread,FgThread, IoThread, UiThread。
Binder线程:应用在跨进程调用时肯定是在binder线程里的,然后再去切换线程
从上小结的startBootstrapServices();startCoreServices();startOtherServices();我们可以看出解决方式为
当AMS等其他服务都启动完毕,会调用mActivityManagerService.systemReady((),在这里面会通过startHomeActivityLocked(currentUserId, “systemReady”);启动桌面应用的activity, Launcher.java。启动后会通过PMS查找所有的已安装的应用,然后显示在界面上
看到最后,我相信你一定对Android系统的启动流程有一定了解了吧。回头看看第一部分的考查内容讲讲呗,对于每一个点的重点我都细心的为你做了高亮。还有纸上得来终觉浅,绝知此事要躬行。本文参考了Android api28的源码,希望你也去踩着点过一遍源码,肯定能加深理解!
我是Yangcy,该吃吃该喝喝,该学还得学,我们一起加油!
刷算法题需要一定的节奏,同样对待每一道算法题也需要认真的对待不可操之过急,否则会导致:算法虐我千万遍,我待算法如初恋!如果这句话是从对算法态度上说爱如初恋那必须得肯定你的态度,但如果是每一道算法题,反复都能虐你千万遍,那这个初恋的滋味肯定不好受。我们应该是面对新题对待如初恋,对待已经做过的题应该是勾勾手指,手到擒来的老司机,否则就是白刷。不仅浪费时间而且浪费精力,所以刷题必须讲究方式方法,建议反复阅读此文跟我学:LeetCode刷题大法。下面这里通过一张思维导图帮助大家整理这道题的思路。思维导图一方面罗列了本文的大纲,另一方面也是希望读者朋友可以保存图片至自己的复习资料里,以便随时通过该大纲去复习,注意关注标红的重点解法。
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
1 | 输入: "abcabcbb" |
示例 2:
1 | 输入: "bbbbb" |
示例 3:
1 | 输入: "pwwkew" |
Related Topics
哈希表
双指针
字符串
Sliding Window
通过对题目的阅读,我们可以得出以下几个结论
首先希望读者能写出双重循环的模板代码,在此再次强调此代码是数组问题暴力法的套路墙裂建议背诵默写,如果还是不能随手写出回过头看一下此文跟我学:LeetCode刷题之1.两数之和,好了继续此问题的暴力解决法就是通过双重循环,加内部一层循环判断[i,j]之间的元素是否是唯一的,然后做相应的移动和记录最大值。注意此处的双重循环的i,j范围,是由于allUnique()方法的for循环范围。
1 | public int lengthOfLongestSubstring(String s) { |
复杂度分析
时间复杂度:O(n^3),三重循环
空间复杂度:O(min(n, m))O(min(n,m)),我们需要 O(k)的空间来检查子字符串中是否有重复字符,其中 k 表示 Set 的大小。而 Set 的大小取决于字符串 n 的大小以及字符集/字母 m 的大小。
该题的实现思路主要是基于:滑动窗口。
什么是滑动窗口?
其实就是一个队列,比如例题中的 abcabcbb,进入这个队列(窗口)为 abc 满足题目要求,当再进入 a,队列变成了 abca,这时候不满足要求。所以,我们要移动这个队列!
如何移动?
我们只要把队列的左边的元素移出就行了,直到满足题目要求!如左出右进变为bca
一直维持这样的队列,找出队列出现最长的长度时候,求出解!
这里推荐使用数组,数组的存取都是O(1)的实现,当然Map在此种情况下也是的。代码如下
1 | public int lengthOfLongestSubstring(String s) { |
复杂度分析
时间复杂度:O(n),只进行一次遍历
空间复杂度:O(m),m 是字符集的大小128
两种解法,充分体现了以空间换时间思想的,来一次加快算法处理速度。所以在算法题中哈希表数组和Map的代码也需要背诵记忆,才能游刃有余。
若你反复AC,且充分理解了本题解法,可以尝试去解答同类题992该题是困难题不过无妨,同类题的意思就是变形,往往是增加了特定的条件或者是针对不同的场景。所以同类题的目的是,透过现象看本质,一时的解不出不碍事,按我说的方法跟我学:LeetCode刷题大法,通过此种方法一定是越来越自信的刷题。
看到最后,我相信你对本题应该有不一样的感受了吧!在此若有不对之处或建议等,留言告诉我;若有疑惑或者不一样的看法,也请告诉我,我们可以一起探讨学习!
我是Yangcy,该吃吃该喝喝,该学还得学,我们一起加油!
本题是LeetCode第二题,难度标记为中等。此题为链表题,不出意外遇到链表题都是画图解题,因为链表题的核心是在符合条件下的链表next指针的变化,除了链表的增删改查之外,如何反转链表的代码,我墙裂建议你先理解背诵,刷链表题基础必备代码,如下
1 | pubic ListNode reverse(ListNode head) { |
这里通过一张思维导图帮助大家整理思路。思维导图一方面罗列了本文的大纲,另一方面也是希望读者朋友可以保存图片至自己的复习资料里,以便随时通过该大纲去复习。
给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。
如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。
您可以假设除了数字 0 之外,这两个数都不会以 0 开头。
示例:
1 | 输入:(2 -> 4 -> 3) + (5 -> 6 -> 4) |
Related Topics
链表
数学
通过对题目的阅读,我们可以得出以下几个结论
最后要判断 carry 是否大于 0,因为例如 (1 -> 2 -> 3) + (7 -> 8 -> 9) 会得到结果 8 -> 0 -> 3 -> 1,所以在最后判断 l1 和 l2 都已经为空了,但是此时 carry 大于 0,所以要将这最后一位数加入到链表尾部
1 | public ListNode addTwoNumbers(ListNode l1, ListNode l2) { |
首先我们需要理解递归,墙裂建议背诵默写递归的模板代码,递归解法的套路如下
1 | public void recur(int level, Param param) { |
这题使用递归很好理解,两链表的节点从左往右进行相加,进位,直到两链表都到达尾部。
最后代码如下
1 | public ListNode addTwoNumbers(ListNode l1, ListNode l2) { |
若你反复AC,且充分理解了本题的两种题解,我敢保证下面这题解出来只是时间长短问题。
在这里还是提醒刷题的朋友,多看几次本文跟我学:LeetCode刷题大法
LeetCode 445. 两数相加 II
看到最后,我相信你对本题应该有不一样的感受了吧!在此若有不对之处或建议等,留言告诉我;若有疑惑或者不一样的看法,也请告诉我,我们可以一起探讨学习!
我是Yangcy,该吃吃该喝喝,该学还得学,我们一起加油!
不想赘述算法与数据结构本身存在的重要性以及对工作面试的重要性。我只想谈谈如何通过解决一道道算法题,慢慢的改变自己对算法的态度,并且从算法中获得自信,如果以下论述我们产生了些许共鸣!请关注我,让我们一起努力,一起刷爆leetcode吧。
曾几何时,在无数个瞬间。可曾遇到过这些情况
放弃的理由千千万万
讲真放弃的理由千千往往,坚持的理由我觉得只有兴趣和生存。只要是抱着生存的理由,那放弃的理由分分钟淹没他,除非真的找不到工作活不下去,就算如此,还能转行,直接逃避……,说这些就是想让自己和大家认清本质,坚定一下刷算法的的信念,走出第一步告诉自己:我需要刷题,我要刷题!
当看到这里,我相信刷过题的人都有过类似经验
这经历完全是自我打击的过程,说到底还是打开的姿势不对。没有找准刷题的基本思想和步骤,试试下面的刷题步骤
我想说且是重点的,未来我将带领各位盆友一起刷算法题,后续我会通过视频+图文的方式来分享每一题的思路和多种解法代码。同时教会大家更方便,高效的刷题。
我是Yangcy,该吃吃该喝喝,该学还得学,我们一起加油!