开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 1 天,点击查看活动详情
家人们,俺来了。下面是食用指南,先看完指南好不好👀👀🤔
前言____食用指南
- 俺是没有参与工作过,使用知识都是来源书本加自己理解,只能参考,具体细节可能也会有问题,欢迎大佬指正。
- 俺的内容主要是围绕俺这个月准备春招食用
- 没有什么准备好,大佬天天都在准备,俺落后了,希望大家能早日跟上
- 俺准备找java类型的,所以后面内容都是java相关。
在简历上,我们在专业技能的第一条,就是:精通/熟悉javase基础知识,对集合、线程、io等都有深入/自己的理解,具备良好的面向对象编程思想,并熟练使用设计模式。
javase 基础知识
集合
很久之前,我写过一次集合的知识,这里作为参考Collection和Map - 掘金 (juejin.cn)
我们可以知道,集合分两类,Collection和Map。Collection包括,queue,set和list,set底下又有HashSet、TreeSet;list底下又有ArrayList和LinkList。Map有HashMap和TreeMap。具体如图:
那么好了我们第一个问题:List,Set,Queue,Map有什么区别?
- list:存储元素有序,可重复
- set:存储元素无序,不可重复
- queue:元素有序,可重复。按特定的排队规则来确定先后顺序
- map:key-value存储/键值对存储。key 无序,不可重复,value无序可重复
问题二:我们怎么选择集合?
这个问题主要从集合特点来思考
- 当我们需要键值获取元素时候,我们用map接口下集合,如果需要排序,使用treemap,如果不需要排序常用hashmap,需要保证线程安全就用concurrentHashMap。
- 如果我们只需要存储元素值,可以选择collection接口下集合,如果需要保证元素唯一,使用set相关的,不需要保证元素唯一就用list相关的。
这个问题考虑第二点,为啥要使用集合?
保存一组类型相同的数据,我们一般使用数组,但是数组缺点是长度不可变,数组存储数据,有序,可重复,特点单一。但是集合的加入,提高了数据存储的灵活性,疑问集合不仅保存了不同类型不同数量的对象,还保存了具有映射关系的数据。
问题三:ArrayList 和 Vector 的区别?ArrayList 与 LinkedList 区别?
- 都是list实现类,ArrayList和LinkedList的线程不安全的,Vector是线程安全的
- ArrayList和Vector底层使用的都是object数组,LinkedList底层是双向链表。
- 采用数组存储的,在插入和删除元素的时间复杂度受元素位置的影响。链表不会
- 采用数组存储的,可以进行随机访问。
- 数组会声明一点空间,使用空间浪费主要是预留的容量,但是liankedlist空间主要是在存储元素,因为他需要多存放直接前驱后直接后继
问题四:Arraylist的扩容机制
ArrayList 底层是数组队列,相当于动态数组,和普通数组相比,他容量可以自动增长,那么如何增长?
这就需要从ArrayList的构造函数说起了,
从源码里面可以看出来 以无参数构造方法创建 ArrayList 时,实际上初始化赋值的是一个空数组。当真正对数组进行添加元素操作时,才真正分配容量。即向数组中添加第一个元素时,数组容量扩为 10。
当我们要 添加 第 1 个元素到 ArrayList 时,elementData.length 为 0 (因为还是一个空的 list),因为执行ensureCapacityInternal() 方法 ,所以 minCapacity 此时为 10。 当添加第二个元素时候minCapacity 为 2,此时 e lementData.length(容量)在添加第一个元素后扩容成 10 了......直到添加第 11 个元素,minCapacity(为 11)比 elementData.length(为 10)要大。进入 grow 方法进行扩容。
grow里面:int newCapacity = oldCapacity + (oldCapacity >> 1),所以 ArrayList 每次扩容之后容量都会变为原来的 1.5 倍左右(oldCapacity 为偶数就是 1.5 倍,否则是 1.5 倍左右)
所以扩容:首先声明为null当插入数据时候,初始化长度10,当长度超过10,就按照每次容量变大0.5来扩容
问题五:无序和不可重复什么意思?
-
无序性是指存储的数据在底层数组中并非按照数组索引的顺序添加 ,而是根据数据的哈希值决定的。
-
不可重复性是指添加的元素按照 equals() 判断时 ,返回 false,需要同时重写 equals() 方法和 hashCode() 方法。
问题六:比较 HashSet、LinkedHashSet 和 TreeSet 三者的异同
- 都是set实现类,都能保证元素唯一,但是都线程不安全
- hashset底层数据结构是hash表,linkedHashset底层是链表和哈希表满足fifo,treeset底层是红黑树
- 三者的应用场景不同。HashSet 用于不需要保证元素插入和取出顺序的场景,LinkedHashSet 用于保证元素的插入和取出顺序满足 FIFO 的场景,TreeSet 用于支持对元素自定义排序规则的场景
问题七: HashMap 和 Hashtable 的区别
- hashmap线程不安全,hashtable线程安全
- hashmap效率高一点,因为线程安全问题,但是hashtable基本淘汰了
- hashmap 可以存储 null 的 key 和 value,但 null 作为键只能有一个,null 作为值可以有多个;hashtable 不允许有 null 键和 null 值,否则会抛出 NullPointerException。
- hashtable默认初始化大小11,每次扩容变成2n+1,HashMap默认初始化大小16,每次扩容变成2n。
- hashmap当链表长度大于阈值(默认为 8)时,将链表转化为红黑树(将链表转换成红黑树前会判断,如果当前数组的长度小于 64,那么会选择先进行数组扩容,而不是转换为红黑树)
hashmap扩容,请自行查看源码。参考Java 1.8 HashMap扩容原理 - 掘金 (juejin.cn)
IO
谈谈io流&&文件复制的几种方法 - 掘金 (juejin.cn)
io里面的设计模式:
装饰器模式
装饰器模式 可以在不改变原有对象的情况下拓展其功能。
比如
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("input.txt")))
适配器模式
适配器模式 主要用于接口互不兼容的类的协调工作,你可以将其联想到我们日常经常使用的电源适配器
// InputStreamReader 是适配器,FileInputStream 是被适配的类
InputStreamReader isr = new InputStreamReader(new FileInputStream(fileName), "UTF-8");
工厂模式
这个在spring等里面使用最多。 工厂模式可以分为简单工厂、工厂方法和抽象工厂模式 简单工厂模式严格来讲并不算是一种设计模式,更多的时候是一种编程习惯。
简单工厂的实现思路是,定义一个工厂类,根据传入的参数不同返回不同的实例,被创建的实例具有共同的父类或接口。简单工厂适用于需要创建的对象较少或客户端不关心对象的创建过程的情况。
示例: 创建一个可以绘制不同形状的绘图工具,可以绘制圆形,正方形,三角形,每个图形都会有一个draw()方法用于绘图,首先可以定义一个接口或者抽象类,作为这三个图像的公共父类,并在其中声明一个公共的draw方法为工厂类传入不同的type可以new不同的形状,返回结果为Shape 类型,这个就是简单工厂核心的地方了。
工厂方法模式 工厂方法模式具有良好的封装性,代码结构清晰,一个对象创建是有条件约束的,如果一个调用者需要一个具体的产品对象,只要知道这个产品的类名或约束字符串即可,不用知道创建对象的过程如何,降低了模块间的耦合。工厂模式还拥有优秀的可扩展性,在增加产品类的情况下,只要适当地修改具体的工厂类或扩展一个工厂类,就可以适应变化。工厂方法模式是典型的解耦框架,高层模块只需要知道产品的抽象类或接口,其他的实现类都不用关心。
抽象工厂模式是工厂方法模式的升级版本。在有多个业务品种、业务分类时,通过抽象工厂模式产生需要的对象是一种非常好的解决方式,抽象方法适用于下和工厂方法一样客户端不需要知道它所创建的对象的类,需要一组对象共同完成某种功能,可能存在多组对象完成不同功能以及系统结构稳定,不会频繁的增加对象的情况。
观察者模式
对文件的监听
设计模式有哪些
单例模式、工厂方法模式、抽象工厂模式、建造者模式和原型模式; 结构型包括:代理模式、装饰模式、适配器模式、组合模式、桥梁模式、外观模式和享元模式; 行为型包括:模板方法模式、命令模式、责任链模式、策略模式、迭代器模式、中介者模式、观察者模式、备忘录模式、访问者模式、状态模式和解释器模式。 面试中不要求23种设计模式全部了解,但至少应掌握单例模式和工厂模式。 加分回答 可以说出知道的框架所用到的设计模式或底层设计模式,例如Spring中的单例模式、工厂模式,AQS的模板模式