Java实习生/校招的面试建议(一面) 自己的经历和理解

1.自我介绍

自我介绍例子:    2020-10-06更新
	面试官好, 我叫xxx, 是一名xxx大学的大四学生,xx公司是一家很有影响力的公司,我应聘的是java开发
	的工作,从事Java开发是我的爱好以及梦想,为此在学校里我很早就结合岗位要求进行了准备,包括对Java
	基础的理解与深入,各种框架的学习,以及一些组件比如Docker redis Nginx es git 以及mq等等,在
	学习的过程中呢,我也对这些技术在自己的项目上进行了相应的应用, 目前正在学习xx, 以后有可能的话
	还想转xx方向

2.项目经历

1.整体技术栈
	数据库+框架 如springBoot+ mysql+ MyBatis
2.你主要做了什么 如:
	全栈
	只是后端 或者 只是部分
3.功能细分 -->引申出技术栈   如:
	搜索模块 用到了es
	热点排行 用到了Redis (不用说用来干什么,你提及了面试官会问你)
4.遇到了什么问题  如:
	数据库数据量太多 导致查询太慢 (引导面试官问你mysql索引)
	

3.基础问题

(1).异常

1.分为什么异常 ,有什么区别
	Throwable包含了错误(Error)和异常(Excetion两类)
2.为什么有些异常需要抛出(如Thread.sleep()) 有些不需要(1/0)
	运行时异常:
			 这些异常是不检查的异常, 是在程序运行的时候可能会发生的, 
			 所以程序可以捕捉, 也可以不捕捉. 这些错误一般是由程序的逻辑
			 错误引起的, 程序应该从逻辑角度去尽量避免。
	非运行时异常:
			检查异常是运行时异常以外的异常, 也是Exception及其子类, 
			这些异常从程序的角度来说是必须经过捕捉检查处理的, 否则不能
			通过编译. 如IOException、SQLException等。

(2)其它基础

object的方法:
    wait()、notify()、notifyAll()equals()方法、toString()

JDK1.8新特性  (其余也了解)
	1.Lambda表达式
	2.Stream流计算
	3.函数式接口
	4.HashMap引入红黑树
	5.方法引用和构造器调用
	6.新时间日期API
	7.接口中的默认方法和静态方法
	说出五个左右就差不多了

synchronized 和 Lock的区别
	1.synchronized是一个关键字,而Lock是一个类
	2.synchronized无法判断 获取锁的状态, Lock可以
	3.synchronized会自动释放锁, Lock必须手动上锁和解锁
	4.synchronized线程阻塞会一直等待 Lock却不一定
	5.synchronized适用于少量代码同步, Lock适用于大量代码同步

(3)类加载过程:

编译期:    将java文件 加载为class文件  javac
1.加载
    将class文件中的二进制数据读取到内存中
2.验证
    文件格式验证 元数据验证 字节流的验证 符号引用的验证 
3.准备
    为类的静态变量分配内存,并将其赋默认值
4.解析
    将常量池中的符号引用替换为直接引用(内存地址)的过程
5.初始化
    为类的静态变量赋初值

(4)面向对象及面向对象的原则

面向对象的方法主要是把事物给对象化,包括其属性和行为。面向对象编程更贴近实际生活
的思想。总体来说面向对象的底层还是面向过程

jdk醉了呀   接单开最里依
 
1.单一职责原则
2.里氏替换原则
3.依赖倒置原则
4.接口隔离原则
5.最少知识原则
6.开闭原则

(5)集合

1. Collection
    List
        异常ConcurrentModificationException
        LinkedList, ArrayList, Vector(安全), Stack

    Set
        TreeSet、HashSet、LinkHashSet。

2.  Map
    	HashMap      Hashtable(安全),
3. Iterator

(6)hashMap (put流程转化为自己的理解来表达)

底层:
    数组 + 链表 + 红黑树 链表长度大于8且数组长度为64时转为红黑树 红黑树的长度小于6时转为链表结构
    在极限情况下,写入11个数据会导致其转化为红黑树,在写入第九个数据,它不会直接转为红黑树,而是看到数组长度才16,认为是不是因为长度小了导致的数据都在一个下标里,所以扩容为32,下次10个数据同样的操作扩容到64,到了第11个才进行转化为红黑树。红黑树的平均查找长度是log(n),如果长度为8,平均查找长度为log(8)=3,链表的平均查找长度为n/2,当长度为8时,平均查找长度为8/2=4,这才有转换成树的必要;
    默认状态:  初始化数组长度为16 当数组长度到0.75时扩容  (问?为什么是0.75,大于或小于会怎么样)
为什么初始化长度为16,为什么是2的次幂(初始化的值为向上的最近的一个二次幂 如16->16 17->32)
    底层采用的是长度-1的值和hash值的&与运算来计算数组的下标,当且仅当长度为2的n次方时,计算的结果等同于hash 
    % 长度的结果,从而实现散列分布,减少碰撞,而&与运算的效率高于%,所以使用的是与运算,类似的,kafka中的分
    区因为不一定是2的n次方,所以采用的是%取余来计算分区。本质都是为了使数据分布均匀。
扩容:
    resize 数组中的数据必须重新计算其在新数组中的位置,并放进去
put的流程:
    1、put(key, value)中直接调用了内部的putVal方法,并且先对key进行了hash操作;
    2、putVal方法中,先检查HashMap数据结构中的索引数组表是否位空,
        如果是的话则进行一次resize操作;
    3、以HashMap索引数组表的长度减一与key的hash值进行与运算,
        得出在数组中的索引,如果索引指定的位置值为空,则新建一个k-v的新节点;
    4、如果不满足的3的条件,则说明索引指定的数组位置的已经存在内容,
        这个时候称之碰撞出现;
    5、在上面判断流程走完之后,计算HashMap全局的modCount值,以便对外部并发的迭代
        操作提供修改的Fail-fast判断提供依据,于此同时增加map中的记录数,
        并判断记录数是否触及容量扩充的阈值,触及则进行一轮resize操作;
    6、在步骤4中出现碰撞情况时,从步骤7开始展开新一轮逻辑判断和处理;
    7、判断key索引到的节点(暂且称作被碰撞节点)的hash、key是否和当前待插入节点
        (新节点)的一致,如果是一致的话,则先保存记录下该节点;如果新旧节点的内容不一致时,则再看被碰撞节点是否是树(TreeNode)类型,如果是树类型的话,则按照树的操作去追加新节点内容;如果被碰撞节点不是树类型,则说明当前发生的碰撞在链表中(此时链表尚未转为红黑树),此时进入一轮循环处理逻辑中;
    8、循环中,先判断被碰撞节点的后继节点是否为空,为空则将新节点作为后继节点,作为后继节点之后并判断当前链表长度是否超过最大允许链表长度8,如果大于的话,需要进行一轮是否转树的操作;如果在一开始后继节点不为空,则先判断后继节点是否与新节点相同,相同的话就记录并跳出循环;如果两个条件判断都满足则继续循环,直至进入某一个条件判断然后跳出循环;
    9、步骤8中转树的操作treeifyBin,如果map的索引表为空或者当前索引表长度还小于64(最大转红黑树的索引数组表长度),那么进行resize操作就行了;否则,如果被碰撞节点不为空,那么就顺着被碰撞节点这条树往后新增该新节点;
    10、最后,回到那个被记住的被碰撞节点,如果它不为空,默认情况下,新节点的值将会替换被碰撞节点的值,同时返回被碰撞节点的值(V)。

(7)反射

forName和ClassLoader的区别
    Class.forName:除了将类的.class文件加载到jvm中之外,还会默认对类进行初始化,执行类中的静态代码块,以及对静态变量的赋值等操作。
    ClassLoader:将.class文件加载到jvm中,默认不会对类进行初始化,只有在newInstance才会去执行static块。

(8)多线程

创建多线程的方式:   
    通过实现Runnable接口  重写run方法
    通过继承Thread类    重写run方法
    通过实现Callable接口,重写call方法 结合futureTask创建线程
Callable和Runnable的区别
    Runnable没有返回值;Callable可以返回执行结果,是个泛型,和Future、FutureTask配合可以用来获取异步执行的结果
    Callable接口的call()方法允许抛出异常;Runnable的run()方法异常只能在内部消化,不能往上继续抛
    注:Callalble接口支持返回执行结果,需要调用FutureTask.get()得到,此方法会阻塞主进程的继续往下执行,如果不调用不会阻塞。

(9)手写单例模式

饿汉式
class Singleton {
 private static Singleton instance=new Singleton();
 private Singleton(){}
 static Singleton getInstance() {
 return instance;
 }
}
懒汉式
class Singleton {
 private static Singleton instance=null;
 private Singleton(){}
 static Singleton getInstance() {
 if(instance==null)
 instance=new Singleton();
 return instance;
 }
}

(10)MySQL

三范式:
    第一范式:保证每列的原子性
    第二范式:保证一张表只描述一件事情
    第三范式----保证每列都和主键直接相关

去重: distinct   -->select distinct xxx from xxx where xxx;

联合查询: left join, right join, inner join

排序: order by 升序:asc  降序:desc
分页: limit  pageSize()
     mysql分页limit使用方法, 为什么查询第一页和最后一页效率不一样,怎么解决
    它会从开头向结尾进行扫描,偏移量很大,所以后面的效率会很低
    先用子查询走索引定偏移位置,再来查询就快了
    

事务:
    ACID原则: 原子性 一致性 隔离性 持久性
    原子性: 要么同时成功,要么同时失败
    一致性:
            最终一致性  
            脏读:一个事务读取了另一个事务未提交的数据,和线程安全类似 如A-C转账 同时B-A转账,不能是读取的都是A最开始的值,而应该是隔离起来的
            不可重复读完整性: 事务前后完整性要保持一致,比如转账前后
    持久性: 事务一旦提交就不可逆转
    流程:
        1.关闭自动提交 set autocommit = 0
        2.开启事务: start TRANSACTION
        3.提交:     commit
        4.回滚:     rollback
        5.开启自动提交 set autocommit = 1
        ----了解----
        savepoint 保存点名
        rollback to savepoint 保存点名  //回滚到保存点
        RELEASE SAVEPOINT 保存点名   //删除保存点

索引: 索引是帮助mysql高效获取数据的数据结构
    主键索引 (主键)
        唯一的主键
    唯一索引 (UNIQUE key)
        可以有多个,保证每一列的值不重复
    常规索引 (KEY/INDEX)
    全文索引 (FullText)
    组合索引
    
    创建索引:
        CREATE INDEX 索引名 on 表(字段);
    索引原则;
        1.索引不是越多越好
        2.经常变动的项不要加索引
        3.小数据的表不要加索引
        4.索引一般加在常用来查询的字段上面
        
    复合索引:
        哪里用到,
        三个字段的复合索引,现在第一个字段没有用到,问这个复合索引会生效吗(最左配原则)
        建立索引失效的情况

主键自增查询最后一条数据,如何查找
	select * from table where id=(select max(id) from table);

mysql常用函数
	xxxxxxxxxxxxxxxxxxxxxxx自己找去

(11)高并发

1.除了加锁之外,如何保证i++的线程安全
	i++操作
	    原子类AtomicInteger的getAndIncrement()
2.ArrayList安全吗?那如何保证安全
	CopyOnWriteArrayList  CopyOnWriteArraySet ConcurrentHashMap
	有可能会问你他们与相应的集合类有什么区别? 它为什么安全
3.Stream流计算:
    List.stream().filter(xx).map(xx).sorted(xx).limit(数字).forEach()
4.阻塞队列:
    ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
    方法:add与remove 抛出异常   offer和poll 不报错有返回值
        put与take 默认永久的阻塞队列     加入long的时间择优超时时间
等等......

(12)redis

1.你用Redis用来作什么?
		缓存
2.Redis里有过期时间,那么redis是如何实现过期自动删除的
	    expire 命令? 不 !! 问的是原理: 三大删除策略
	    1.被动删除/惰性删除:  查询的时候看看有没有过期,过期了再删除
	    2.主动删除: 被动删除无法及时删除一些冷数据所以定期删除一些
	    3.当前已用内存超过maxmemory限定时,触发主动清理策略
3.怎么设置redis的缓存:
	 (扩展到缓存击穿->再引申到缓存穿透,击穿,雪崩的区别以及解决方案) : 对于被频繁查询的数据,应该是设置比较长的过期时间,以防止缓存击穿,对于其它数据过期时间则应该是某个范围内的随机值,以防缓存雪崩

4.先更新数据库还是更新缓存:  答:先更新数据库,再删除缓存,为了保证缓存删除成功,应该使用binlog异步异步删除

2020 -10-18更新

(13)偏底层

1.servlet的生命周期

1.加载和实例化
    当Servlet容器启动时,或者在容器检测到需要这个Servlet来响应第一个请求时,
    创建Servlet实例
2.初始化
    容器将调用Servlet的init()方法初始化这个对象,初始化的目的是为了让Servlet对象
    在处理客户端请求前完成一些初始化的工作,
3.请求处理
    调用Servlet的service()方法对请求进行处理。要注意的是,在service()
    方法调用之前,init()方法必须成功执行
4.服务终止
    当容器检测到一个Servlet实例应该从服务中被移除的时候,容器就会调用实例的
    destroy()方法,以便让该实例可以释放它所使用的资源

2.MySQL底层是什么数据结构?为什么不用B树或哈希

底层是B+树; 
B树能够在非叶节点中存储数据,但是这也导致在查询连续数据时可能会带来更多的随机 I/O(树的高度),而 B+ 树的所有叶节点可以通过指针相互连接,能够减少顺序遍历时产生的额外随机 I/O;
哈希虽然能够提供 O(1) 的单数据行操作性能,但是对于范围查询和排序却无法很好地支持,最终导致全表扫描;

3.ES为什么快

es采用倒排索引;如查找某个字符, 正排索引是遍历所有的内容, 看哪些里面含有要查找的内容,而倒排索引是将文章内容进行分词之后, 类似于以词作为key, 文章的索引作为value来存储, 搜索的时候就可以以关键字作为key来获取了

4.过滤器和拦截器的区别

1.拦截器是基于 Java反射机制的,所以呢可以获得容器里的bean,从而在拦截器中做一些
    业务的处理.过滤器是基于函数回调,是servlet的规范
2.过滤器可以拦截包括静态资源的所有请求,而拦截器就不可以拦截静态资源
3.拦截器是被包裹在过滤器之中的

5.线程的生命周期

1.新建
2.就绪
3.运行
4.阻塞:
    等待阻塞
        wait()方法  
    同步阻塞
        去抢占线程失败
    其它阻塞
        sleep()方法 和 join()方法
5.死亡
    执行完 或 异常而退出

6.线程池

1.七大参数       理解线程池可以看银行柜台的例子辅助理解, 若是想知道,我也可以补充
public ThreadPoolExecutor(  int corePoolSize,       //核心线程池大小
                            int maximumPoolSize,    // 最大线程池大小
                            long keepAliveTime,     // 超过了没有人调用就会释放
                            TimeUnit unit,          // 超时单位
                            BlockingQueue<Runnable> workQueue  // 阻塞队列
                            ThreadFactory threadFactory,  //线程工厂,创建线程的,一般不用修改
                            RejectedExecutionHandler handler  //拒绝策略)
2.四大拒绝策略
ThreadPoolExecutor.AbortPolicy()       //默认的,会报错
ThreadPoolExecutor.CallerRunsPolicy()  //哪来的回哪去
ThreadPoolExecutor.DiscardPolicy()   //丢掉任务,不会抛出异常
ThreadPoolExecutor.DiscardOldestPolicy()  //尝试和最早的竞争,若失败则丢掉,也不会有异常

(14) 2021/10/21更新一点点

线程和进程的区别

做个简单的比喻:进程=火车,线程=车厢
线程在进程下行进(单纯的车厢无法运行)
一个进程可以包含多个线程(一辆火车可以有多个车厢)
不同进程间数据很难共享(一辆火车上的乘客很难换到另外一辆火车,比如站点换乘)
同一进程下不同线程间数据很易共享(A车厢换到B车厢很容易)
进程要比线程消耗更多的计算机资源(采用多列火车相比多个车厢更耗资源)
进程间不会相互影响,一个线程挂掉将导致整个进程挂掉(一列火车不会影响到另外一列火车,
但是如果一列火车上中间的一节车厢着火了,将影响到所有车厢)

进程可以拓展到多机,进程适合多核(不同火车可以开在多个轨道上,同一火车的车厢不能在行进的不同的轨道上)
进程使用的内存地址可以上锁,即一个线程使用某些共享内存时,其他线程必须等它结束,才能使用这一块内存。
(比如火车上的洗手间)-"互斥锁"进程使用的内存地址可以限定使用量
(比如火车上的餐厅,最多只允许多少人进入,如果满了需要在门口等,等有人出来了才能进去)-“信号量”

链接:https://www.zhihu.com/question/25532384/answer/411179772
来源:知乎

并发和并行的区别

并发是多线程操作同一个资源
并行是多线程同时执行

并行,当系统有一个以上CPU时,当一个CPU执行一个进程,另一个CPU可以执行另一个进程,两个进程互不抢占CPU资源,
可以同时进行,这种方式我们称之为并行。所以,并发是指在一段时间内宏观上多个程序同时运行
(非常快,看起来是同时的)。并行指的是同一个时刻,多个任务确实真的在同时运行。

Java真的能开启线程吗?

不可以,底层调用的是start0()方法,这是一个本地的方法

线程有几个状态

6个
public enum State {
		//新建状态
        NEW,
        //运行状态
        RUNNABLE,
        //阻塞状态
        BLOCKED,
        //等待状态
        WAITING,
        //超时等待
        TIMED_WAITING,
        //终止
        TERMINATED;
    }

wait 和 sleep的区别

1. 来自不同的类, wait ->  Object   sleep -> Thread
2. wait会释放锁,sleep是抱着锁睡觉的,不会释放锁
3. wait必须在同步代码块当中,而sleep可以在任何地方

=============================

至于什么八大基本类型, String的方法,final-finally-finalize区别等等这个应该不需要写出来了,对于框架在一面中一般只会问你用来作什么,不会有太深入的问题

若是有什么错误的地方,欢迎指出来,一起学习,一起共勉

友情链接:狂神说

相关文章

暂无评论

暂无评论...