24届暑期实习求职经历总结

个人情况:末9 本科 成绩前30%

持续时间:3.14-5.18

最后是拿到了招联金融开发岗的offer,直接开摆

寒假阶段

主要工作:复习专业课、完善项目、准备简历

由于疫情原因22年末的寒假放的很早,期末考试推迟到了23年初进行,正好寒假就一边准备期末一边准备找实习,主要工作就是完善之前做过的项目和复习计算机网络(顺带也抽时间看了看操作系统),也算是为找实习打下了一点点基础。寒假期间也花了两天时间磨出来了一份还算可以的简历(包括根据简历写一份成文的自我介绍、将简历涉及的各个知识点简要的列出来),后续的简历修改都建立在了这份简历的基础上。

3月中旬

主要工作:修改简历,逐渐开始投简历,准备笔面试

之前一直觉得自己没有准备充分,虽然春招开始了一段时间,但是一直不敢投简历,处于一个非常纠结的状态。直到同学已经过了阿里的一面,我才开始觉得不能再等了,从此正式进入了找暑期实习的时期。第一份简历投给了阿里,当时牛客上全是阿里的hr在招人,感觉起码收到了30个hr发来的消息(群发),当时还天真的以为经济复苏,阿里要扩招了…

由于是学长的内推(顺便还请学长做了面试辅导),投递没多久就收到了面试邀请。第一次面试直接被疯狂拷打,并且和面试官的沟通上可能有一些障碍,我们经常不能理解对方的意思…周四晚上问完技术问题大概用了1h20min,紧接着又约了一个手撕的时间(据说笔试后50%要加试),又用了大概30min。最后也是不出意料的挂掉了,听学长说这个面试官级别也比较高,要求很严格,面试的时候经常问我优化的问题(真优化不出来啊…)。虽然结果不尽如人意,但是起码算没有遗憾吧:当时会的东西答得还不错,不会的也尽力蒙了。通过这次面试,确实认识到了自己的不足,也为之后的学习指引了方向。

阿里巴巴-1688-测试-一面:

  1. 选一个具体的模块,讲一下模块具体是做什么的,通过什么方式实现,碰到什么问题。比如数据存储,具体是怎么使用的。
  2. 你刚刚提到防疫政策的存储,防疫政策这个东西是什么维度的?政策最小的力度大概是到哪里,(经过沟通,是市维度的),数据量很大体现在哪,是数据的条数还是大小
  3. 政策入库几十条,主键是什么
  4. 除了redis,查询数据的维度是什么, 即你刚提到数据是市级的,用这个数据的对象是谁
  5. 在第一版还没有做存储升级的时候,使用量大概是什么样子的,或者说你们发现它的效率很差,是什么阶段发现的
  6. 用户具体怎么实现这个政策的查询
  7. 把所有的都展示出来的设计是有什么(认为会出现全部数据而不是所处地级市)
  8. 政策存储的化还是会有一些其他信息落到库里面的对吧
  9. 发现他的性能比较差的时候第一时间就想到了redis吗,还是说尝试过其他优化策略,因为我刚刚看你的数据量,就算百级好了,也不是非常大。
  10. db本身要做这个优化的话,怎么做
  11. 在设计上有使用数据库索引吗
  12. redis的优势,是什么让你选择了它

(突然想起来主从复制,但是面试官说没什么用

  1. redis用到了什么存储结构,或者说键值对的键是什么,储存的内容是什么
  2. 除了字符串redis还支持什么数据结构
  3. redis的热点问题(假设数据不会过期和失效),大多流量都打在了某个key或某几个key上,可能会导致什么问题
  4. 第二个项目为什么选到这个项目,人数,角色
  5. 简单讲一下实体关系抽取是怎么做的
  6. 数据集是怎么来的
  7. 所有你们的输入数据是一段话?最终输出是什么?是唯一的吗?原始输入是一段文字还是一句话?每个语句都会经过识别获取到一个关系吗?
  8. 因为你说你的工作是调研一些算法吗,那最终是你说去使用bert+crf的算法吗?
  9. 如果我这个模型训练的不太好,比如“湖南大学”识别除了给“湖”,没有意义,再去计算这个关系时,还会有什么效果吗?假设这个实体我已经识别出来了,我要去做一个关系的处理,会是怎么样去做的?
  10. 你提到你们的模型选用的是Google的模型是吗,你们的模型需要自己去训练吗
  11. 项目我们到这里,下面我们问一些基础的知识,我看你学过数据结构,栈和队列有什么区别。除了存储方面,还有什么区别,比如效率方面
  12. 你在日常开发中有用过队列和栈吗
  13. 两个栈实现一个队列(优化再优化)
  14. 新建一个字符串,你一般怎么做
  15. String StringBuffer StringBulider的优缺点
  16. 有具体使用过吗,除了线程安全还有什么区别吗,或者从性能角度,使用场景
  17. 同样的字符串创建一百遍哪个更快
  18. Linux系统有了解吗,你们的项目在哪开发,shell脚本了解吗,常用的Linux的命令
  19. 写shell:linux下面有一个文件,存了很多ip,一个ip一行,有很多重复,我要统计每个ip出现的次数
  20. 测试方面的问题:你写代码的时候会进行自测吗
  21. 有做过单元测试或者了解过什么单元测试的工具和框架吗
  22. 除了junit呢
  23. 假设我们要写一个单元用例,肯定要有一个校验点要写,就是需要写一个断言,你知道什么是断言吗
  24. 除了黑盒测试你还了解什么测试方法吗,白盒黑盒灰盒的区别

三月下旬到四月上旬

主要工作:复习八股,笔面试

经过上一次面试,本来决定先找点小厂练练手再去面大厂,结果小厂根本不给面试机会啊!!!最后没办法又投了蚂蚁(正好学长在群里要简历)。蚂蚁这边从开始做测评开始就很顺利,尤其是找规律的题目,可以算的上全秒了,笔试也人品爆发式的a出来了两个题目,学长还透露了他的领导对我评价很高(咱也不知道为啥哈哈哈),反正就一路顺风顺水的面完了二面,帮忙内推的学长都已经说了“我觉得你很稳,等来杭州了请你吃饭”。

不出意外的话这时候就要出意外了,在焦急的等待了一周后流程被系统自动终止了,经过打听知道二面给过了但是被组里的大leader直接挂掉了,没有给三面的机会…当时听到这个消息还是挺失望的,不过后来想想也还能接受吧,由于这个部门是支付宝的核心部门(面试官介绍支付宝的所有资金流转都和他们有关)而且又是Java研发,竞争肯定很激烈,前面估计有一些双9的大佬吧。

经过这两轮面试我感觉自己还是自信了不少的,起码真的有过leader看好自己哈哈哈哈,面试官也给了很多中肯的建议:比如将项目转化成实际的成果(专利、软著和论文)、注重在实践中学习等等。也是从这里,我认识到现在招聘网站上写到的“加分项”已经不单纯是加分项了,而是面试官非常希望你会的重点,比如:分布式、高并发等…

蚂蚁-支付宝事业线-支付宝(中国)-Java研发-一面:

  1. 你刚刚讲了第一个项目是软件工程的一个课设吗,通过这个项目你觉得一个软件的研发周期分为哪几个阶段

  2. 你认为你做的这个需求的核心逻辑是什么,是一个什么样的需求

  3. 在对这个需求进行建模的时候,你们里面设计了哪几张表,就是你的er模型是什么样的(实体关系是什么样)

  4. 除了用户表还有哪几张表,就是这几张表是什么样的关系(完全忘光了)

  5. 我确认一下你讲的这些表最开始的时候是你设计的吗

  6. 我再确认一下你们在做这个研发的时候你觉得表实体之间的关系模型设计实在整个软件研发的哪个阶段做的

  7. 我刚刚听你说在实际研发的时候没有完全按照这个表来,为什么

  8. 我问一个针对你用户表的问题,你这个用户表有哪些信息

  9. 这张表的用户的唯一性是用什么来决定的(用户id)

  10. 用户id的生成逻辑是什么样的

  11. 怎么确保每个用户的id是不一样的

  12. 你每个用户来注册的时候都会生成一个id对吗,那你们生成一个id除了算法保证之外,你是对算法完全可信还是会在代码里进一步确保不会出错

  13. 所以说你们完全信任这个算法给他生成一个id

  14. 我看你们用了redis做缓存,主要缓存了什么信息

  15. redis有哪些优点

  16. redis支不支持持久化(支持),是怎么实现的

  17. 我这边看你写了一个乐观锁,乐观锁主要用在一个什么逻辑下呢

  18. 为什么不用悲观锁

  19. 你觉得你们项目还可以改进的地方

  20. 对于这样一个系统,其实是和数据库打交道比较多的吗,我想和你聊一下数据库的事务,你讲一下事务的特点吧

  21. 场景题:在支付宝上面,我们从a账号往b账号转账,这个过程应该符合原子性和事务的一致性那你觉得整个的这样一个过程如果通过数据库的操作来描述,应该包含哪几步

  22. 如果同时a用户账户有100元,但是两笔扣款100元操作同时到达,怎么确保一个成功另一个失败

  23. 讲一下springboot的aop机制是什么逻辑,原理是什么

  24. springboot 框架大概包含哪几个基础模块(不知道)

  25. 那你有没有看过spring的基础模块,他的基础模块有哪些

  26. 我看你这边其实是用了好多工具,你在日常用这些东西有没有看过他的一些源码或者底层实现(没)没看过的话那你是通过什么方式来学习和了解spring的

  27. Java里面的hashmap的实现原理是什么样的

  28. 那hashmap是线程安全的吗

  29. 那我再问一下设计模式,常见的设计模式有哪几种

  30. 讲一个使用设计模式的案例

  31. 你基于pop3做过一个邮件系统,讲一下pop3这个协议是什么样子的

  32. 讲一下socket通信的原理

  33. 如果说客户端连接服务端,怎么判断他成功建立了连接

  34. 后面有读研打算吗

  35. 实习地和工作地的选择

  36. 为什么会这样排序

  37. 附加一个算法题:请实现 copyRandomList 函数,复制一个复杂链表。在复杂链表中,每个节点除了有一个 next 指针指向下一个节点,还有一个 random 指针指向链表中的任意节点或者 null

蚂蚁-支付宝事业线-支付宝(中国)-Java研发-二面:

这次面试是钉钉的语音通话(没有录音),在面试前几天就加了面试官的好友,约了时间。这是我面试到现在面试体验最好的一次面试,面试官很温柔,全程都是聊天,我也讲了一些自己学习过程中的小故事(经历),不过最后还是问了两个技术问题,让我意识到现在的行情只会到springboot是远远不够了。

印象比较深刻的几个问题~

  • 你刚刚说你比较擅长操作系统和计算机网络,为什么这么说呢,是考试分数比较高还是哪些方面?
  • 数据库的ER模型(一面就问了,当时不会,专门去看了)
  • 乐观锁的应用
  • 信息安全(简历上提到了,但是和他问的不是一个意思)主要指用户的数据安全,比如健康码不用了怎么处理数据…
  • 问了第二个项目具体是干什么的
  • 为什么用了谷歌的模型

最后有三个技术问题,可以说都不怎么会…

  • 了解分布式锁吗
  • 了解thread local吗
  • 了解的并发的数据结构

四月中旬到五月上旬

主要工作:拓宽知识面、学习一些分布式的相关知识(cap理论、zookeeper、分布式锁等)、疯狂笔试

从这时候开始面试机会就变少,身边也开始有几个同学拿到了满意的offer,逐渐焦急…说好的金三银四怎么连面试都没有啊,大概三四个星期只有恒生给了一个15min的面试机会(无下文),蚂蚁其他部门捞了一次(被薄纱)。笔试的话倒是不少,还记得有一周每天晚上都在笔试,有点高中时候密度练的味道了…就这华子的笔试还没到人家的及格线,连泡池子的机会都没给。五一回了一次家,感觉爸妈甚至比我还焦虑,甚至认真思考了去国外读硕士的可能性…算是找实习历程中比较黑暗的一段时间吧。没有任何一家在流程中的时候真的会很焦虑,所以如果你在泡池子可以换个角度思考一下:没消息就是好消息~

提一下银行的技术岗面试:一般来说,面试时间(15-20min)会比大厂短一些,更注重应用能力(比如大厂可能会更多的问计网或者jvm之类的,银行可能更偏向springboot和mybatis),最后有些银行上班是要求穿正装的,如果你面试也按照这种服饰标准可能会给面试官留下更好的印象!

恒生-一面

  1. 自我介绍一分钟
  2. redis的使用场景
  3. redis和数据库的数据一致性如何保证
  4. redis 的 缓存双删有了解吗
  5. mybatis-plus 的 分页是如何实现的
  6. Result工具类的封装和应用,泛式有了解吗
  7. 项目使用的乐观锁是如何实现的
  8. 场景题:有很多人一起抢奶茶,加锁,是加在抢的动作上还是奶茶上

蚂蚁-蚂蚁集团-CTO线-数据与平台技术事业群-智能交互技术部-一面

  1. redis在项目中的具体作用

  2. redis的数据结构

    1. redis的hash怎么实现
    2. redis的string怎么实现
    3. redis跳表了解过吗(不会了
  3. 算法题 两数乘积 时间复杂度On (答案是用hash表来查找(O1)

    就是两数之和的变式,但是当时两数之和实在太简单了,就没好好看优化,也没见过hash表的这种用法…没见过真的比较难想到啊

  4. 有若干大小相同的图片,如何把他们均匀的存到三台缓存服务器中

五月中下旬

主要工作:疯狂笔面试,诸逆之战当放手一搏

上面有提到考虑出国读研,但是出国读研一是要有雅思(英语黑洞哭泣),二是起码要有一份对口的实习。所以趁着五月还有最后的机会,把能投的公司全都投了一遍,换来的是一周7场笔试5场面试(还有一些因为时间不合适鸽掉的没算进去)的魔鬼赛程。经过了一段睁眼算法闭眼八股的日子,总算是看到一丝曙光。

先是QQ浏览器捞起来进行了一次面试,终于打破了长时间没面试的窘境了,虽然面试问的很难,但是整体下来和面试官聊的还算开心,尽力局,认了。后面没多久美团和招联都约了面试,总算是把面试的状态激发出来了:美团是周五晚上进行了一面,还算比较顺利,周一早上九点多就约了二面,可惜二面的时候状态不太对(发烧ing),脑子也不太灵光,寄掉了;招联是周六一下午就面完,一面过了立马约二面,虽然当时感觉自己答得还不错,但是不清楚人家具体的招聘情况,也是提心吊胆了一周。最后招联成果oc,到实习群才发现一共就30个实习生(全部岗位),而且学校基本全是985,本硕未知(总而言之看上去都比我强),这能排到我也真是奇迹,我宣布招联就是我心目中的第一大厂!!!

腾讯-pcg-QQ浏览器-一面

  1. 乐观锁的实现(version)
  2. 除了数据库加字段还有什么方式(compare and swap)
  3. CAS有什么实现方式(知道自旋锁,不太了解)
  4. synchronized和lock是什么锁
  5. synchronized和lock的区别
  6. lock加上static和不加有什么区别
  7. synchronized随JDK升级的优化
  8. 了解的并发容器
  9. 了解AQS吗
  10. 了解分布式锁吗
  11. 除了zookeeper还有什么实现方式
  12. redis实现和zookeeper有什么区别,哪个更可靠(从CAP的角度考虑)
  13. zookeeper主节点挂掉了多久之后会有新的
  14. 项目部署了吗?访问量(?)
  15. redis用来干了什么
  16. redis是自己搭建的吗,有几个节点
  17. 验证码的key是手机号吗,可能会泄露用户数据啊,如何改进(加密)
  18. 有什么加密算法可以来做
  19. 在项目中除了key-value还用到什么结构,或者redis还有什么其他结构
  20. 如何实现一共排行榜(zset)
  21. redis的主从复制有了解吗,是怎么实现的
  22. redis的持久化
  23. springboot的启动流程
  24. 当有请求来到的时候springboot是如何处理的(MVC?经过了哪些类?)
  25. 项目如何区分来自小程序和网页的请求(?)
  26. http的请求结构(请求头、请求体详情…)追:如何标识不同浏览器
  27. tcp的请求头有什么?tcp头有多少字节?
  28. 线程池的参数
  29. 为什么没有选用Netty这些框架,选用了原生socket
  30. 为什么选用了缓冲线程池
  31. 有一万个用户如何设置线程池(手动创建),如何设置参数
  32. 你提到给每个socket一个线程,如果socket连接的时候收到阻塞,会影响后面的socket连接吗,如何解决(serversocket的连接数量限制?没太理解,我觉得不太会有这个问题)
  33. 除了用线程池,还有什么实现方式

美团-到家-研发平台-iOS-一面

居然没有自我介绍

  1. 讲一下OSI的每一层和他的作用

  2. http属于哪一层

  3. TCP三次握手

  4. get和post的区别

  5. http和https的区别

  6. https怎么加密通信

  7. 数字证书的组成信息(只说了数字签名+密钥…)

  8. 数字证书如何验证(现在看不是很清楚他想问什么,我说了数字证书的验证过程,他也没继续问,答案:链接

  9. 对设计模式的了解

  10. 死锁了解吗

  11. 线程和进程的区别

  12. 当一个线程挂了会对其他线程产生影响吗

  13. 当一个进程挂了会对其他进程产生影响吗

  14. 你项目中遇到的有挑战问题,如何解决的(redis)

  15. 缓存之前时间主要花在哪,是url请求吗

(面试官主要搞ios,对redis不是特别了解,按照他在互联网混迹多年的理解来推理的,所以我答的也不一定是他想知道的)

  1. redis的更新策略
  2. redis对大规模缓存的限制
  3. redis的内存淘汰策略
  4. 算法:青蛙跳台阶(dp)

美团-到家-研发平台-iOS-二面

项目中的角色 作用

  1. Java的反射
  2. Java的内存管理机制
  3. Java的类加载机制
  4. Java的泛型

答得不好直接寄了

招联-开发-一二面

下午4:03开始的一面,整体的问答节奏非常快,大概十分钟就结束了技术问题,面试结束后十分钟不到就通知了二面

一面

  1. Spring的AOP是怎么实现的
  2. 动态代理的实现方式
  3. AOP什么时候失效
  4. 你提到你写过很多文档,在写文档的时候有什么总结和规范吗
  5. 多线程有用过吗(有),线程池的使用和参数
  6. 项目中遇到的难题
  7. Java中的深拷贝和浅拷贝
  8. 深拷贝什么时候使用呢
  9. 你对hash表有什么了解吗
  10. hash表的扩容是怎么实现的
  11. hash表的线程安全是怎么实现的
  12. 如何不用Java中的锁来实现线程安全
  13. 你对分布式锁还有什么了解
  14. 对消费金融有了解吗
  15. 成绩,打算读研吗
  16. 为什么选择深圳
  17. 想在实习中收获什么

二面

记不大清了,没什么技术问题

  1. 成绩

  2. 为什么找工作

  3. 还面过哪些公司

  4. 项目中的难点

  5. 对招联的了解

    剩下的和一面的非技术问题差不多

反问问过的:

  1. 多久出结果
  2. 部门业务
  3. 对实习生品质的期待
  4. 实习生干什么

后面接了offer又有一些厂约面试了,卷不动了(而且待遇也一般)全拒了,暑期实习面试就到此为止吧!我滴任务完成辣!

心得体会

一点拙见,各位看个乐~

  • (和美团面试官聊天的时候说的)暑期实习对同学的要求可能没那么高,大家都是从那时候过来的,知道这个阶段水平都不算很高,更注重的可能还是能不能和大家友好沟通友好相处。人家招你进来是当同事的,而且很有可能会负责在实习的时候带你,如果面试的时候你们都聊的不开心怎么可能通过捏?
  • 简历上的东西一定要完全掌握,甚至可以适当的给面试官“下套”,聊一些你擅长的东西,将问题转化成话题,唠起来,把面试的主动权掌握在自己手里,总比面试官不知道问什么而开始随机八股问答要好。
  • 头几次面试大家难免都会紧张,如果面试不太顺利也不要慌,面试后及时复盘,把面试官问到的问题做一些总结,在一次次面试中获得提高。可以想象成获得了一次和年薪大几十万的优秀学长聊天的机会,他来帮你查缺补漏,机会难得啊!
  • 最后,面试就像相亲,确实包含着很多运气成分,是双方双向选择的一个过程,如果面试官态度冷淡,你也不用感到自责愧疚,双方看不对眼而已,你肯定也不希望来到这样的环境来工作吧?

学习资料

珍藏八股:

CS_Notes(精简)

pdai(全面、深入)

JavaGuide(偏基础,适合新手看)

小林coding(主打图解)

代码随想录(算法)

招聘渠道:

投递一定要去官网!!!牛客和boss上很多都不理人,体验极差

牛客吹哨人(不定期更新招聘信息)

校招帮小程序(好像被恶意举报了)

我投过的公司:

投递过的部分公司
恒生 阿里 网易 广发 携程 核桃编程 爱奇艺 交银金科 oppo vivo 百度 快手 蚂蚁 华为 京东 顺丰 SHEIN 虹软 吉利 中欧基金 虎牙 众安 美团 商汤 用友 cvte 东方财富 网易有道 腾讯 平安银行 中国人寿 光大银行 招联 360 海信 上汽 江苏银行 宁德时代 北森 恒丰银行 华夏基金 快手 宁波银行 奇安信 特斯拉 兴业银行 海康威视 广汽 台积电 长安汽车 上汽 新华三

最后感谢一下一路push我的伙伴们,有一群有着共同目标、一起努力的伙伴真的很幸运!

二分查找

二分查找是一种比较基础的算法,思路类似于小游戏-猜出0-100的某个数字,适用于数组有序的情况。二分查找不一定会比暴力方法快,二者都是不稳定的,具体用时和数据有关系。

二分查找的思路很简单,但是在真正去写二分查找时,会发现他的边界条件很难把控。

例题:

704. 二分查找

给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1

提示:

  1. 你可以假设 nums 中的所有元素是不重复的。
  2. n 将在 [1, 10000]之间。
  3. nums 的每个元素都将在 [-9999, 9999]之间。

示例 1:

1
2
3
输入: nums = [-1,0,3,5,9,12], target = 9
输出: 4
解释: 9 出现在 nums 中并且下标为 4

示例 2:

1
2
3
输入: nums = [-1,0,3,5,9,12], target = 2
输出: -1
解释: 2 不存在 nums 中因此返回 -1

分享一下个人的思路:

  1. 记录目前可能最大和最小可能值的位置min(初始 0)、max(初始 length-1)
  2. 取最中间的位置(max+min),与target进行比较
  3. 若小于target,则min = 现在的位置+1、若大于target,则max = 现在的位置-1
  4. 当前位置 =(max+min)/ 2,循环,直至找到target或者max<min
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Solution {
public int search(int[] nums, int target) {

int length = nums.length;
int max = length-1;
int min = 0;
int p = (max+min)/2;
while(min<=max){
if(nums[p] == target){
return p;
}
else if(nums[p]<target){
min = p+1;
p = (min+max)/2;

}
else if(nums[p]>target){
max = p-1;
p = (max+min)/2;
}
}
return -1;
}
}

当然有些时候我们脑子不太清醒,可能想不清楚边界关系,那也没逝分享一个自己的小寄巧:记录一下这个值有没有被比较过(不推荐使用,属于是没办法的办法)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class Solution {
public int search(int[] nums, int target) {

int length = nums.length;
//创建一个等长的数组,比较过对应位置改位1,否则保持0
int[] search = new int[length];
int max = length;
int min = 0;
int p = length/2;
while(true){
if(nums[p] == target){
return p;
}
else if(nums[p]<target&&search[p]==0){
search[p] = 1;
min = p;
p = (p+max)/2;

}
else if(nums[p]>target&&search[p]==0){
search[p] = 1;
max = p;
p = (p+min)/2;
}
else {
return -1;
}
}
}
}

KMP

KMP是用来处理字符串匹配问题的一种高效方法

例题:找出字符串中第一个匹配项的下标

给你两个字符串 haystackneedle ,请你在 haystack 字符串中找出 needle 字符串的第一个匹配项的下标(下标从 0 开始)。如果 needle 不是 haystack 的一部分,则返回 -1

示例 1:

1
2
3
4
输入:haystack = "sadbutsad", needle = "sad"
输出:0
解释:"sad" 在下标 0 和 6 处匹配。
第一个匹配项的下标是 0 ,所以返回 0 。

示例 2:

1
2
3
输入:haystack = "leetcode", needle = "leeto"
输出:-1
解释:"leeto" 没有在 "leetcode" 中出现,所以返回 -1 。

如果按照原始的暴力方法,算法复杂度将达到O(n*m),而KMP算法可以将复杂度降低到O(n+m)。

KMP算法主要由两步构成

第一步:构造needle字符串的next数组

next数组主要用来记录当前字符串的最长前后缀有几位,比如字符串a b c d a b d 的next数组就是0 0 0 0 1 2 0

当字符串为abcd时,前后缀一点也不一样;当字符串为abcda时,字符串的开头和结尾都是a,所以next数组就是1;同理,abcdab则是对应2…

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private int[] getNext(String s){
int[] next = new int[s.length()];
int j = 0;
next[0]=0;
//A B C D A B D
//i初始值为1,否则i j相等
for (int i = 1;i<s.length();i++){
//j等于0时,说明之前还没出现过相等的前缀和后缀
while (j>0&&s.charAt(i)!=s.charAt(j)){
j = next[j-1];
}
//当两个位置的字符相同时,说明是相同的前后缀
if (s.charAt(i)==s.charAt(j)){
j++;
}
next[i]=j;
}
return next;
}

第二步:与haystack进行比较

从头开始比较haystack和needle字符串,相等时j++;不相等时,按照needle的next数组,将needle字符串后移next[j-1]位

例如:haystack为abxabcabcaby,needle为abcaby,当haystack的第9位和needle的第6位进行比较时,发现不相等,这是j将变为next[5]=2,因为当needle可以匹配到第二位时,说明必有ab,所以下次匹配时可以从needle的第三位开始匹配。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public int strStr(String haystack,String needle){
if (needle.length()==0){
return 0;
}
//得next数组
int[] next = getNext(needle);

int j = 0;
for (int i = 0;i<haystack.length();i++){
while (j>0&&haystack.charAt(i)!=needle.charAt(j)){
j = next[j-1];
}
if (haystack.charAt(i)==needle.charAt(j)){
j++;
}
if (j==needle.length()){
return i-j+1;
}
}
return -1;
}

Java 的 动态代理入门

Java的动态代理是一种在运行时动态生成代理对象的机制,可以在不修改源代码的情况下,为类和接口创建代理对象,并在代理对象上实现一些自定义的逻辑,例如日志记录、安全控制、性能监控等。Java的动态代理通过反射机制实现,在运行时动态生成代理类,从而实现代理功能。

JDK动态代理主要涉及以下两个类:

  1. java.lang.reflect.InvocationHandler:定义了一个代理对象要执行的操作,即代理逻辑。该接口只有一个方法invoke,在代理对象调用方法时,会自动回调该方法。
  2. java.lang.reflect.Proxy:提供了创建动态代理对象的方法。该类有一个静态方法newProxyInstance,该方法接收一个ClassLoader对象、一组接口和一个InvocationHandler对象作为参数,返回一个代理对象。

在使用Java动态代理时,需要先创建一个实现了InvocationHandler接口的类,在该类中实现代理逻辑,然后使用Proxy类的newProxyInstance方法创建代理对象。

动态代理有许多应用场景,例如AOP编程、RPC调用、对象持久化等。相比于静态代理,动态代理具有更好的灵活性和可扩展性,同时也减少了代码的重复性。

实例

  1. 创建接口
1
2
3
4
public interface SmsService {
String send(String message);
String receive(String message);
}
  1. 实现接口
1
2
3
4
5
6
7
8
9
10
11
12
13
public class SmsServiceImpl implements SmsService{
@Override
public String send(String message) {
System.out.println("message :" + message);
return message;
}

@Override
public String receive(String message) {
System.out.println("receive :" + message);
return message;
}
}
  1. 定义一个JDK动态代理类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class DebugInvocationHandler implements InvocationHandler {

private final Object target;

public DebugInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//当前调用的方法名
System.out.println(method);
//调用前添加的操作
System.out.println("before method "+method.getName());
//当我们的动态代理对象调用原生方法的时候,实际上是调用到了invoke方法,然后invoke方法代替我们去调用了被代理对象的原生方法
Object result = method.invoke(target,args);
//调用后添加的操作
System.out.println("after method "+method.getName());
return result;
}
}
  1. 获取代理对象的工厂类
1
2
3
4
5
6
7
8
9
10
public class JdkProxyFactory {
public static Object getProxy(Object target) {
//通过Proxy.newProxyInstance()获取到某个类的代理对象
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),//目标类的类加载
target.getClass().getInterfaces(),//代理需要实现的接口
new DebugInvocationHandler(target)//代理对象对应的自定义InvocationHandle
);
}
}
  1. 使用
1
2
3
4
5
6
7
8
public class Test {
public static void main(String[] args) {
SmsService smsService = (SmsService) JdkProxyFactory.getProxy(new SmsServiceImpl());
//创建代理对象后,我们调用的方法先被转发到对应InvocationHandler中的invoke方法,再由invoke方法调用原生方法
smsService.send("Java");
smsService.receive("Java");
}
}

CGLIB

静态代理和动态代理的对比

灵活性:动态代理更加灵活,不需要必须实现接口,可以直接代理实现类,并且可以不需要针对每个目标类都创建一个代理类。另外,静态代理中,接口一旦新增加方法,目标对象和代理对象都要进行修改,这是非常麻烦的!

JVM 层面 :静态代理在编译时就将接口、实现类、代理类这些都变成了一个个实际的 class 文件。而动态代理是在运行时动态生成类字节码,并加载到 JVM 中的。

参考

07 静态代理+JDK/CGLIB 动态代理实战 (yuque.com)

第二次个人技能测试-VUE练习

题目要求是这样滴

img

我做的是这样滴

img

四舍五入可以算是有点相似(吧

vue和原生html的一大区别是:vue是由一个个组件拼接而成的,例如:

img

每个组件可以单独写在一个vue文件中,只需在App.vue文件中引用每个组件即可,这样大大增强了组件的复用性。

每个组件应该怎么写呢?其实基本上就是在template中写html。举个例子(组件1):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<template>
<div id="first_box">
<hr>
<div id="input_part">
<input id="input" type="text">
</div>
<div id="search_button">
<button id="button">查找</button>
</div>
<div id="add_button">
<button id="add">+新建角色</button>
</div>
</div>
</template>

另外vue相较于html新加了许多新功能,例如v-for、v-if等

我是使用webstorm来 创建项目的,总体感觉使用起来还是挺简单的,没太多想写的了。

源码链接

css文件复制到assets里,除了App.vue以外的vue文件复制到components里,App.vue替代原有的App.vue。只需要将文件复制到相应的位置即可。

常见的查询优化器

什么是查询优化器

img

数据库主要由三部分组成,分别是解析器、优化器和执行引擎,其中优化器是把关系表达式转换成执行计划的核心组件,很大程度上决定了一个系统的性能。

查询优化器的分类

查询优化器主要分为两类,分别是:

RBO(基于规则的优化器,Rule-Based Optimizer)

CBO(基于规则的优化器,Cost-Based Optimizer)

RBO

  • 根据关系代数等价语义,重写查询
  • 基于启发式规则:主流RBO实现一般都有几百条基于经验归纳得到的优化规则
  • 会访问表的元信息,不会涉及具体的表的数据
  • 实现简单,优化速度快,但不保证得到最优的执行计划
列裁剪

列裁剪的基本思想在于:对于算子中实际用不上的列,优化器在优化的过程中没有必要保留它们。对这些列的删除会减少 I/O 资源占用,并为后续的优化带来便利。例如:

假设表 t 里面有 a b c 三列,执行语句:

1
select a from t where b >5

在查询过程中,只用到了表 t 中 a b 两列的数据,而 c 列的数据未被用到。因此对于 c 列,我们可以将它裁剪掉,读取数据的时候不需要将它读进来。

谓词下推

谓词下推将查询语句中的过滤表达式计算尽可能下推到距离数据源最近的地方,以尽早完成数据的过滤,进而显著地减少数据传输或计算的开销。例如:

1
select count(1) from A Join B on A.id = B.id where A.a >10 and B.b <100;

在处理Join操作之前需要首先对A和B执行Scan操作,然后再进行Join,再执行过滤,最后计算聚合函数返回,但是如果把过滤条件A.a > 10和B.b < 100分别移到A表的Scan和B表的Scan的时候执行,则可以大大减少Join操作的输入数据。优化后的语句如下:

1
select count(1) from (select * from A  where a>10) as A1 Join(select * from B  where b<100) as B1 on A1.id = B1.id;
传递闭包

将表的过滤条件传递,在表连接前对两个表的数据都完成筛选,从而减少Join操作的输入数据。

Runtime Filter

Runtime Filter是通过在join前过滤掉那些不会命中join的输入数据来大幅减少join中的数据传输和计算,从而减少整体的执行时间。例如:

img

如上图左半部分所示,在进行join运算的时候不仅需要把全量的sales数据传输到join算子里去,而且每一行sales数据都需要进行join运算(包括算哈希值、比较运算等)。这里如果items.price > 100的选择率比较高,比如达到50%,那么sales表中的大部分数据是肯定不会被join上,如果提前进行过滤掉,可以减少数据的传输和计算的开销。

上图的右半部分则是加入了runtime filter之后的执行计划,从图中可以看到在进行join的build端拉取数据的过程中新增了一个RuntimeFilterBuilder的一个算子,这个算子的作用就是在运行的过程中收集build端的信息形成runtime filter,并且发送到probe端的scan节点中去,让probe端的节点可以在scan就减少输入的数据,从而实现性能的提升。

除此之外,还有Join消除、谓词合并等优化方式…

CBO

  • 使用一个模型估算执行计划的代价,选择代价最小的执行计划

    • 执行计划的代价等于所有算子的执行代价之和
    • 通过RBO得到(所有)可能的等价执行计划
统计信息

原始表统计信息:

表或分区级别:行数、行平均大小、表在磁盘中占用字节大小

列级别:min、max、num nulls、num not nulls、num distinct value(NDV)等

推导统计信息:

选择率(selectivity):对于某一过滤条件,查询会从表中返回多大比例的数据

基数(cardinality):在查询计划中指算子需要处理的行数

执行计划枚举

考虑事项:

单表扫描:索引扫描 or 全表扫描

Join的实现:Hash Join or Sort Merge Join

多表Join:哪种连接顺序最优

通常使用贪心算法或者动态规划选出最优的执行计划。

大数据场景下使用CBO对查询性能非常重要

刚开始学数据库时还觉得关系代数没什么用,这下终于知道关系代数在SQL优化中的重要作用了

ZooKeeper 入门

安装zookeeper

  1. 监测JDK环境

img

配置环境变量

img

  1. 下载zookeeper

下载地址:http://zookeeper.apache.org/

选择第一个(已经编译好的版本)

img

解压,即可开始使用zookeeper

img

以上目录称为home目录

bin目录下为启动程序

conf目录下为配置文件

data目录(自己新建)为运行数据

logs目录下为运行日志

安装完成

测试zookeeper

  1. 首先将conf目录下的zoo_sample.cfg文件赋值一份,命名为zoo.cfg

打开zoo.cfg,将端口(clientPort)设置为2181(默认),一个端口只能同时给一个客户端提供服务

img

  1. 在home目录下,执行bin\zkServer.sh start

img

弹窗显示如图,则启动成功

1
2
3
4
//zkServer.sh的常用命令
zkServer.sh start //启动节点
zkServer.sh stop //停止节点
zkServer.sh status //查看节点状态

执行bin\zkCli.cmd localhost 2181,即可成功连接上node,输入命令进行测试即可

img

1
2
3
4
5
6
节点中常用命令
ls / 显示子节点
create /<节点路径> <节点数据> 新建节点
get /<节点路径> 读取节点数据
set /<节点路径> 设置节点数据
delete /<节点路径> 删除节点

多节点启动

  1. 在本机进行伪集群配置
  • 在home目录下的data和logs文件夹下新建文件夹和文件

img

每个server< x >文件下新建一个myid文件

img

zoo1.cfg 指向的写入 1,zoo2.cfg 指向的写 2,以此类推,切记不能随便添加空格

img

logs目录只需新建三个文件夹即可,用于存放每个节点的 日志

img

Windows下的Java版本管理–jabba

jabba相关信息:

项目地址:https://github.com/shyiko/jabba

参考文档:https://www.cjavapy.com/article/96/

安装

在管理员身份下的powershell执行

1
2
3
4
[Net.ServicePointManager]::SecurityProtocol =[Net.SecurityProtocolType]::Tls12
Invoke-Expression (
Invoke-WebRequest https://github.com/shyiko/jabba/raw/master/install.ps1 -UseBasicParsing
).Content

如果执行上面命令报错

  1. 可能是win10脚本执行策略问题,修改可以执行下边命令:
1
Set-ExecutionPolicy -Scope CurrentUser

执行后会出ExecutionPolicy:提示信息,输入RemoteSigned,回车确定后在按Y确定执行。

再执行安装命令即可正常使用

  1. 网络问题:

需要科学上网

img

安装成功↑

管理jdk版本

  1. 添加本地jdk
1
jabba link system@1.8 E:\Users\19318\.jdks\corretto-1.8.0_322-1

img

2.查询本地安装的JDK版本

1
jabba ls

3.切换jdk版本

1
jabba use system@1.8

img

附:常用命令

1
2
3
4
5
jabba ls-remote 查询服务器上可下载的安装的JDK版本
jabba ls 查询本地安装的JDK版本
jabba install openjdk@1.10-0 安装OpenJDK
jabba uninstall zulu@1.6.77 卸载JDK
jabba use adopt@1.8 切换使用的JDK版本

应用场景和局限性

我的需求是将默认版本由17变为8,在尝试了网上的其他办法没有成功后尝试的jabba,但是jabba仅可在管理员身份下的powershell中可以被找到并使用,在普通powershell、普通cmd和管理员cmd中均无法找到或使用jabba,并且默认的jdk版本仍是原来的17,具体原因未知。

参考文章:Java使用Jabba进行版本管理

我的版本如下所示

img

这是以前我的电脑就配置好的,具体怎么装俺也想不起来了,可以上网搜搜其他教程,注意配置环境变量和用户变量噢~

开始安装

先贴出来我滴参考文章吧!

用webstorm搭建vue项目(亲测,绝对实用)

大哥写的不错,也包含了安装nodejs和npm的过程,大家可以借鉴一下。下面是我的安装过程

  • 安装淘宝镜像-cnpm

这类似与npm的国内镜像源,用它下载会变快一点点~

安装命令:npm install -g cnpm --registry=https://registry.npm.taobao.org

验证命令:cnpm -v

返回如下内容则安装成功

img

如果你一下就成功了,那我恭喜你。我本人输入验证命令时遇到了报错

‘cnpm‘ 不是内部或外部命令,也不是可运行的程序 或批处理文件。

上网找了很久,总结出来报错原因应该是:没有将cnpm的执行路径添加到windows环境变量中

好,那现在问题就变成了cnpm在哪里执行捏?

首先,cnpm一般会下载在你安装的nodejs的文件夹下。按照网上的说法,有一些人的cnpm.cmd会在这个↓文件夹下,不知道大家的在不在,反正我是没有

(注意是cnpm.cmd不是普通的cnpm噢)

img

找了半天我也不知道网上博主们的cnpm.cmd是哪来的,正发愁的时候突然发现他就在前几层的文件夹(node_global)下诶

img

那现在问题就很简单啦,由于原因是:没有将cnpm的执行路径添加到windows环境变量中,现在我们只需要在用户变量的Path中添加cnpm.cmd所在的路径即可!

img

现在,我们再去用验证命令来验证,就会得到正确的结果啦

  • 安装webpack

安装命令:cnpm install webpack -g

验证命令:webpack -v

如果出现

We will use “npm” to install the CLI via “npm install -D”. Do you want to install ‘webpack-cli’ (yes/no):

直接回车(即默认no,选择yes会报错),然后执行

npm install webpack-cli -g

  • 安装Vue

安装命令:cnpm install vue -g

  • 安装Vue命令行工具,即vue-cli 脚手架

安装命令:cnpm install vue-cli -g

验证命令:vue -V (V要大写!)

img

这样我的vue就安装好啦,但是做到这里安装的vue-cli应该是2.9.6版本噢,我后续又进行了更新,下文会写!

Vue-Cli更新

参考的文章是

如何升级Vue的版本 vue2.9.6升级到vue3.0

  • 首先卸载老版本vue2.9.6

执行命令:npm uninstall vue-cli -g

  • 再安装新的vue

执行命令:cnpm install -g @vue/cli

完成!现在再输入vue -V就可以检测到新版本的vue了

WebStorm创建Vue项目

在新建项目是选择Vue.js即可,系统会自动去寻找电脑中的Node解释器和Vue CLI

img

点击创建,就可以开始创建新项目了。这时我的电脑又报错了。。。

npm ERR! code EPERM
npm ERR! syscall mkdir

具体啥意思捏?咱也不知道。反正就先把报错信息甩给bing让他搜去吧。

bing告诉我:《npm安装报错(npm ERR! code EPERM npm ERR! syscall mkdir…)

文中提到两种方法,抱着能输命令就不找文件的原则,我先试了第二种

npm cache clean --force

失败了哈哈哈,提示我不能删除之类的(即使我已经以管理员身份运行)

然后回过头看第一种,这次倒是根据文章很顺利的找到了.npmrc文件(C:\Users{账户}\下的.npmrc文件),直接delete,再次运行,问题解决(已经删完了没法给大家截图了,莫得办法)

经过一段时间的等待,WebStorm终于创建好了Vue项目,项目结构如下

img

这时候有人就要问了:“你怎么知道你安装的vue版本呢?” 检测vue版本可以在我们的webstorm的终端中输入npm list vue来查看(不在项目下输入可能会报错)

img

也可以在package.json文件中的"dependencies"下查看

img

后面再更新Vue的教程~

Springboot集成Redis做数据库缓存(原生版)

redis做缓存简单来说就是:系统把表中经常访问的记录放在了Redis中,然后用户查询时先去查询Redis再去查询MySQL,由于缓存在内存中,所以查询会很快,起到提高性能的作用。

先来看效果吧~

测试案例是把一个查询重复99999次,根据时间来判断性能的好坏。

使用redis时:

img

不使用redis时:

img

可以看出使用redis大概快了两秒钟,确实实现了优化。但是redis可以实现的优化效果作者并不是很了解,这里感觉效果不算特别明显,可能和代码有关系。

本文所用的方法是原生的redis操作,没有用到springboot自带的缓存机制(不会,下次再更)。本文代码基于之前的文章进行添加,需要源码的同学可以去原文下载。

maven配置

添加以下依赖:

1
2
3
4
5
6
7
8
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>

application配置

redis默认是没有密码的,所以如果你没有设置密码就不需要写密码啦

1
2
spring:redis:host: localhost
port:6379

Java代码

img

  • 首先在项目文件夹下新建config文件夹(放配置类)和util文件夹(放工具类)
  • 在config文件夹下新建RedisConfig类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport{
@Resource
private RedisTemplate redisTemplate;

@Bean
public RedisTemplate redisTemplateInit(){
//序列化key的实例化对象
redisTemplate.setKeySerializer(new StringRedisSerializer());
//序列化value的实例化对象
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
return redisTemplate;
}
}

RedisTemplate是Spring Data Redis提供给用户的最高级的抽象客户端,用户可直接通过RedisTemplate进行多种操作,例如set、get

  • (非必须)可以在config文件夹下新建一个RedisConstant类来存储redis的key
1
2
3
public class RedisConstant{
public static String USER_KEY="User";
}

到时候使用时直接调用参数即可

  • 在util文件夹下新建RedisUtil类,里面的内容是封装好的redis操作,网上有很多大佬写的工具类,通用性比较高,找到合适的可以直接拿来用

我找到的工具类有很多功能,代码比较长,放在文章的最后

  • 在serverimpl中添加redis的相关代码

主要逻辑是查询前先在redis中寻找有没有要查找的内容,如果没有再去数据库中查找,数据库返回的值顺便存入redis,这样下次再访问时就可以从redis中读到数据了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Service
public class UserServiceImpl implements UserService{
@Autowired
UserMapper userMapper;

@Autowired
RedisUtil redisUtil;
@Override
publicList<User> queryAll(){//试图从redis获取
List<User> users =(List<User>) redisUtil.get(RedisConstant.USER_KEY);//获取失败则去数据库查找
if(CollectionUtils.isEmpty(users)){
users=userMapper.queryAll();//返回的数据存入redis
redisUtil.set(RedisConstant.USER_KEY,users);
}
return users;
}

@Override
public void changePassword(String username,String password){//为了防止数据不一致,在数据库数据改变时先清楚之前redis的相关缓存
redisUtil.del(RedisConstant.USER_KEY);
userMapper.changePassword(username,password);
System.out.println("修改成功");
}
}

代码部分就到此结束了~

运行代码

首先你的电脑要先启动redis服务器端才可以。

redis官方是没有Windows版的,不过已经有大佬开发出了Windows版,安装起来比较简单,没什么需要注意的。大家跟着菜鸟教程先在自己电脑上安装redis吧!

安装好了,有的同学就要问了,redis现在只能在它自己的目录下运行,这可怎么办捏?

很简单,我们把redis的路径添加到环境变量中,就可以在任意位置启动redis了

img

耗,现在我们打开cmd,输入redis-server,就可以启动redis服务端了(默认的端口6379)

img

不要关闭服务端界面,再打开一个cmd,输入redis-cli,就可以启动客户端了(默认的端口6379)

img

现在,来运行我们写好的程序吧

进入localhost:8084,点击获取,我们会从数据库查到数据,同时把数据存入redis,这时我们在redis客户端就可以查到数据了~

img

如果你想不起来你的键叫什么,又不想去看代码,那么查询现在redis中存有的键:keys *

img

现在你可以自己写一个测试用例来看看效果如何了。

虽然在大项目中redis肯定不是这么用的,但是对于作者来说,起码算是用起来了,后面再学习正确的简洁的使用方式,学会了来给大噶更新!

0%