SHEIN 一面基础面试_2020-10-9
自我介绍和性格介绍
具体可以参考三七互娱的一面面经初面_2020-0927。
了解性问题
- 如何学习新知识
我认为学习应该是这样的:
- 首先明白自己的学习方向
- 可以按照就业方向去寻找,比如你是后端的,还是前端的,或者是安全,客户端等等
- 或者按照你所学习的编程语言去选择,比如JAVA,PYTHON,C++等。
- 然后根据该学习方向去搜索合适的技术栈,不需要一次性全部写完整,因为很多知识点是没办法一开始想到的,可以先列出重要的或者基础的技术栈,然后在学习某个技术或者的框架的时候去学习更多的知识点。
- 列学习计划,我认为学习计划的确定是因人而异的,因为有很多人的学习方式方法不同,比如快速学习方式和缓慢的学习方式就是不一样的。可以根据自己的自主学习能力去列自己的计划。一般计划最重要的就是两个方面,时间还有学习方式。
- 比如我在学习SSM的时候,我会先去了解每个框架的功能,然后发现需要熟悉Web知识,然后我会先花几天去系统学习。然后每个框架可以看书看文档学习,比较难理解的可以看视频学习。然后每学习一个框架,做一个练手的小demo巩固知识。这样一般就有了大概的认识了。然后进行整合可以找一些小的项目去实践练习。之后就是在了解这个框架的前提下去深入了解其核心,如果想要提升还可以多看源码等等方式,作为一个加固的作用。
- 你了解SHEIN吗
首先SHEIN是一个跨境的电商公司,主要是卖时尚的女装到欧美或者中东等市场,在海外电商中排名第五。
其次我现在应聘的工作,JAVA工程师,是面向B端的,做供应链相关的。
C有释义为:Consumer、Client,我们每天都在接触C端产品,为消费者、个人用户或终端用户,比如:微信、头条、抖音、美团等等。
B释义为:Business,作为职场人士也会经常接触B端产品,通常为企业或商家为工作或商业目的而使用的系统型软件、工具或平台,最常见的就是报销系统,基本每个职场人士都会用到。
基础问题
- 讲一下hashmap的get流程
- HashMap底层是一个数组,数组的对象是一个node。
- node继承接口Entry,有四个成员变量,哈希值hash,键值key,值value还有next指针。
- get方法。若表为空,则会触发初始化,然后返回null。如果不为空,则会根据key生成的哈希值找到对应的数组位置,然后再调用equals方法进行比较,找到对应的结点,然后返回值。如果没有找到就返回null。
- 讲一下链表和红黑树两个结构
- 链表。链表是一个线性表,但是在位置上是离散的。每个结点有一个指向下一个结点的指针,每次查找都需要从头结点进行遍历比较。优点是很好的利用不连续的空间,缺点是查找的时间复杂度是O(n)。
- 红黑树。其实红黑树也是一颗不太严格的平衡二叉树,且结点上附带红黑两种颜色的属性。不太严格即不是要求子树高度差不大于1,而是从根到叶子的最长路径不能超过最短路径的两倍,是广义上的平衡二叉树。所以在查找效率上是类似于二分查找的,是高于链表的。而红黑树比平衡二叉树的优势在于插入删除所带来的旋转结点次数较少。
- jdk1.7 和 jdk1.8 hashmap扩容机制
具体的请看底层结构
- 当向容器添加元素的时候,会判断当前容器的元素个数,如果大于等于阈值---即当前数组的长度乘以加载因子的值的时候,就要自动扩容
- Java里的数组是无法自动扩容的,方法是使用一个新的数组代替已有的容量小的数组。
- jdk 1.7 是直接计算新的数组容量,创建新的数组,然后利用transfer函数将原数组的所有结点复制到新数组中。复制的方式是,重新计算改结点在新数组的位置,然后采用头插法的方式放入,但这会改变原链表的顺序。
- jdk 1.8 取消了transfer这个函数。因为发现hashmap每次扩容两倍,原结点所在的位置只会有两种可能,要么在原来的索引位置,要么是原来的索引加上原来的容量。这一点取决于原数组容量对应的位置,比如默认数组长度为16,即二进制10000,即扩容之后取决于哈希值第五位是否为1。然后使用桶方式去进行扩容选择,分为两个桶,一个原索引,一个原来的索引加上原来的容量。然后直接一条链进行赋值。
- 多线程的运行方式
- Thread:
- Thread类是在java.lang包中定义的。
- 一个类只要继承了Thread类同时覆写了本类中的run()方法就可以实现多线程操作了
- 一个类只能继承一个父类,这是此方法的局限。
- Runnable
- 使用Runnable定义的子类中没有start()方法,只有Thread类中才有。
- Thread类,有一个构造方法:public Thread(Runnable targer)此构造方法接受Runnable的子类实例,也就是说可以通过Thread类来启动Runnable实现的多线程。
- 优势:
- 避免点继承的局限,一个类可以继承多个接口。
- 适合于资源的共享
- Callable
- Callable 和 Runnable 的使用方法大同小异, 区别在于:
- Callable 使用 call() 方法, Runnable 使用 run() 方法
- call() 可以返回值, 而 run()方法不能返回。
- call() 可以抛出受检查的异常,比如ClassNotFoundException, 而run()不能抛出受检查的异常。
- Callable 和 Runnable 的使用方法大同小异, 区别在于:
- 多线程的难点
- 如果多个线程各自使用各自的资源,不互相干扰,那么就只需要考虑它们占用的内存情况。
- 那么多线程的难点我认为就是在于共享资源的一致性。可以考虑加锁或者使用CAS。
- 线程池组成
可以从线程池模型或者线程池流程去考虑
- 核心线程
- 最大线程
- 阻塞队列
其它参数:
- 超时时间
- 超时时间单位
- 线程工厂
- 拒绝策略
- 线程池有哪些
- CacheThreadPool:此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小
- FixedThreadPool:创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小
- SingleThreadExecutor:一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行
- ScheduledThreadPool:创建一个定长线程池,支持定时及周期性任务执行。
生产者消费者模型
public class ProductConsume { public static void main(String[] args) { Data data = new Data(); new Thread(()->{for(int i = 0;i < 10;i++) { try { data.increment(); } catch (InterruptedException e) { e.printStackTrace(); } } }, "A").start(); new Thread(()->{for(int i = 0;i < 10;i++) { try { data.decrement(); } catch (InterruptedException e) { e.printStackTrace(); } } }, "B").start(); } } class Data { private int num = 0; public synchronized void increment() throws InterruptedException { if (num != 0){ this.wait(); } num++; System.out.println(Thread.currentThread().getName()+"=>"+num); this.notify(); } public synchronized void decrement() throws InterruptedException { if (num == 0){ this.wait(); } num--; System.out.println(Thread.currentThread().getName()+"=>"+num); this.notify(); } }
- 关于io你学习了多少,里面使用了什么模式
io学习笔记在方法实现
- 首先我学习了Java io的基本知识,包括分类和基本使用。
- 然后我学习了NIO,学习了buffer,channel,selector等基本概念和使用。
- 之后我利用以上知识,学着做了IO聊天室,进行一个知识的巩固和实践。
- 之后我打算继续学习Netty这个框架。
使用的模式:
装饰者模式:动态地将责任附加到对象上,若要扩展功能,装饰者模提供了比继承更有弹性的替代方案。
通俗的解释:装饰模式就是给一个对象增加一些新的功能,而且是动态的,要求装饰对象和被装饰对象实现同一个接口,装饰对象持有被装饰对象的实例。BufferedReader bufferedReader=new BufferedReader(inputStreamReader);
适配器模式:将一个类的接口,转换成客户期望的另一个接口。适配器让原本接口不兼容的类可以合作无间。
FileInputStream fileInput = new FileInputStream(file); InputStreamReader inputStreamReader = new InputStreamReader(fileInput);
- 你讲讲innodb的底层原理
这个我还没有学习到,然后我打算找个时间学习下,具体的学习笔记会放在innodb底层原理,请有兴趣的朋友可以去看下。
- 最左匹配原则
顾名思义:最左优先,以最左边的为起点任何连续的索引都能匹配上。同时遇到范围查询(>、<、between、like)就会停止匹配。
即如果建立(a, b)的复合索引,只有当a匹配上的时候,才会匹配到b的索引。
因为mysql是这样建立(a, b)的复合索引的。
- 首先以a列建立B+数索引,然后将b列按顺序加上去。
- 所以其实查找条件where a = x and b = y时,是先找到a的结点位置,然后进行索引下推,找到匹配b的结点。
项目相关
这部分没有太多参考性,简单说
- 你之前的酒店管理系统是多人合作,合作中会遇到什么问题
- 功能的确定
- 接口的设计,包括命名
- 使用git的冲突解决,有时未按照单一职责进行设计
- 人员时间和任务的安排
从这个项目中学到了什么
学到了文档的书写。通过了此次实习,我与小伙伴互相交流讨论,确定项目的需求和总体设计,分工完成了对项目文档的编写,更好地开发接下来的任务。
学会了团队合作。由于当时是疫情期间,我们通过Git进行项目的版本管理和线上合作开发,更加熟悉了流程,包括分支建立,版本上传和处理冲突,也学习到了良好的分工有多么重要。
学到了前后端分离开发。学会了前后端框架上的整合,并且了解到了分离版本中,页面的切换都是依靠前端去完成,而后端只负责数据的处理和封装。
从小伙伴那里学到了编程的技巧。学到了在返回数据时候对数据的封装,包括了状态码和数据体等。还有就是学到了链式编程技巧。
- Spring Security 和 Shiro 有什么区别
- Shiro是一个比较轻量级的安全框架,使用起来也比较的简单。如果在实际工作中不需要那么复杂的东西,那么使用Shiro是比较好的。
- Shiro功能划分明确清晰,有四大核心功能:
- Authentication:身份认证/登录,验证用户是不是拥有相应的身份;
- Authorization:授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用户是否能做事情;
- Cryptography:加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储;
- Session Management:会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通JavaSE环境的,也可以是如Web环境的;
- Shiro三个核心组件:
- Subject:主体,代表了当前“用户”,这个用户不一定是一个具体的人,与当前应用交互的任何东西都是Subject
- SecurityManager:安全管理器;即所有与安全有关的操作都会与SecurityManager交互;且它管理着所有Subject;可以看出它是Shiro的核心,它负责与后边介绍的其他组件进行交互,如果学习过SpringMVC,你可以把它看成DispatcherServlet前端控制器;
- Realms:域,Shiro从从Realm获取安全数据(如用户、角色、权限),就是说SecurityManager要验证用户身份,那么它需要从Realm获取相应的用户进行比较以确定用户身份是否合法;也需要从Realm得到用户相应的角色/权限进行验证用户是否能进行操作;
- Shiro功能划分明确清晰,有四大核心功能:
- Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反转Inversion of Control ,DI:Dependency Injection 依赖注入)和AOP(面向切面编程)功能,可以很好地跟Spring相关的功能集成起来,拥有比较好的社区支持,也相对来说比较的复杂。
- 它的设计是基于框架内大范围的依赖的,可以被划分为以下几块。
- Web/Http 安全:这是最复杂的部分。通过建立 filter 和相关的 service bean 来实现框架的认证机制。当访问受保护的 URL 时会将用户引入登录界面或者是错误提示界面。
- 业务对象或者方法的安全:控制方法访问权限的。
- AuthenticationManager:处理来自于框架其他部分的认证请求。
- AccessDecisionManager:为 Web 或方法的安全提供访问决策。会注册一个默认的,但是我们也可以通过普通 bean 注册的方式使用自定义的 AccessDecisionManager。
- AuthenticationProvider:AuthenticationManager 是通过它来认证用户的。
- UserDetailsService:跟 AuthenticationProvider 关系密切,用来获取用户信息的。
- 它的设计是基于框架内大范围的依赖的,可以被划分为以下几块。