20211104面试总结

闲话

到了现在还在面试的可能不多了,大多找到了心仪的公司。自己太菜,在牛客上看,便投了某家。

这时候好像不怎么手撕代码了,八股文也问的少了。


一、凉经 回忆

1.自我介绍。

2.项目介绍。

3.servlet是怎么暴露在外界的,servlet怎么处理请求的,Tomcat怎么运行,是一个容器,怎么工作的…

4.平时写好一个项目后,该怎么编译,具体原理是什么…等,(这里我都没回答出来,后来给我说了下,Maven的package打成jar包,然后运行在服务器上…)

5.hashmap什么时候扩容,扩容的数字为什么是8…,碰撞因子是多少?

6 ArrayList扩容怎么扩容的。是时间换空间还是空间换时间

7讲一下springMVC的原理,dispatcherservlet为什么能处理那么多请求,为什么功能强大,和servlet的区别是什么。

二、总结答案

1.项目介绍,我就介绍的十分糟糕,同学坐在我旁边,说要不是做过这些项目,可能根本就不知道我说的是什么。
我也觉得自己项目介绍的十分糟糕,面试了这么多回,还是没长进,自己没有好好总结,面试官说从项目背景,需求,技术栈,业务等方面来介绍,我两下就说完了。明显没什么条理性。

自己学习黑马视频的时候做的黑马旅游网项目,所以感觉没什么好说的,但面试官也能从中提取出来 要点。需要什么原理,技术场景 等。现在做个总结:

这是我在Java初期做的一个开源项目,做的是一个旅游网站,是一个javaweb类型项目 使用到的技术有HTML,css,servlet,ajax,servlet,MySQL等。

旅游网站就有基本的登录,注册,分类查看,分页,搜索等功能。

技术选型的话 分为三层

web层 service层 dao层

web层 servlet html Jackson

service层有javamail

dao层有mysql jdbctemplate

在dao中写出具体的方法,service调用dao进行查询等操作。servlet得到的数据转化为JSON传给前端。

第二个项目是一个企业权限管理系统,技术选型有maven springmvc spring mybatis oracle jsp等

基本功能有,登录,注册,订单的增删改查,货物的修改查询,用户管理。是一个权限管理系统,所以用到springsecurity,对用户的权限进行赋予。比如设置各个用户不同的权限,有的可以查询,有的可以增删等。

2.关于servlet这块知识一直没有掌握好,今天总结下:
如果你想吧自己桌面上的资源让其他人访问,那么其他人就需要一个url来对你的电脑进行访问,但是不能,此时servlet就出现了,servlet就可以处理别人的请求,让他们访问你的电脑资源。
servlet:
如我们学习使用的tomcat,就是一个servlet容器。servlet容器,那里面放着的就是servlet对象。他的处理过程大致分为三个方面:

  • 接受请求
  • 处理请求
  • 响应请求
    我们编写的就是处理请求这一块,后面又出现了一些框架,框架就很好的处理了这些事情。
    在servlet中,我们实验web.xml进行配置servlet,如servlet-mapping和servlet-name这样让servlet暴露在外界的。

servlet大概的执行流程:

  • 项目启动,创建servletcontext
  • 读取web.xml,依次反射创建listener,filter,servlet。
  • 再执行filter.dofilter方法
  • 最后servlet处理请求,响应结果。

tomcat会传入三个对象:servletconfig,servletresquest,servletresponse,
我们写的web.xml其实就被servletconfig处理了。

而resquest和response:
http请求到了tomcat,tomcat通过字符串分析,会将请求头,请求地址,请求参数,都装进request对象。

request.getUrl();
request.getHeader();

而response,开始没有处理结果时,还是空的对象,当有结果时候,通过response.write(),将结果写入response结果的缓冲区,tomcat在servlet处理结束后,拿到response,遍历里面的信息,组成http响应,最后返回给客户端。

servlet是个接口,有5个方法:init,getServletConfig,service,getServletInfo,destroy。
其中init,service,destroy是生命周期方法,init和destroy各自只执行一次,service在每次请求时都会执行。
httpservlet实现了servlet的service方法。在httpservlet中只需要去重写dopost和doget方法即可。

3.servlet和springmvc的关系

4.我们编译了一些java类,其中肯定也包含了main方法。编译成class文件后,把它打成jar包,上传到服务器上。此时就可以使用java命令实现。
如果我们把jar包解压后,就可以看到META-INF的目录,目录里有MANIFEST.MF,这个文件就是定义如何导入外部jar包。

5.hashmap扩容:当元素个数超过数组大小loadfactor时 就回把数组长度乘以2倍。
存储结点>原数组长度
0.75 (这里是大于,没有等于)。
当结点长度为8的时候,就回树化,变成红黑树,红黑树存储的是value。
为什么是8转化为红黑树?
树结构的空间是点结构空间的2倍,我们使用hash时,这些点是均匀分布的,符合泊松分布的,当链表越来越长的时候,就需要提高效率了,此时就需要设置阈值(8),转变成红黑树。
链表是符合泊松分布的,当长度为8时,这已经是千万分之一的概率了,这是通过概率所决定的,因此设为8的时候会树化。(当树的长度降为6的时候,会转变为链表)

6.arraylist的扩容机制:
arraylist的变量:

//定义一个空数组以供使用
private static final Object[] EMPTY_ELEMENTDATA = {};

//也是一个空数组,跟上边的空数组不同之处在于,这个是在默认构造器时返回的,扩容时需要用到这个作判断
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

//存放数组中的元素
transient Object[] elementData;

//数组的长度,此参数是数组中实际的参数,区别于elementData.length
private int size;

arraylist里面有三个构造函数,不同的构造函数会影响不同的扩容机制。

1.默认的无参构造,返回空数组,长度为0public ArrayList() {
	this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

2.给定初始容量的构造函数。
	就是构造一个指定长度的空数组。
	public ArrayList(int initialCapacity) {
    if (initialCapacity > 0) {
        this.elementData = new Object[initialCapacity];
    } else if (initialCapacity == 0) {
        this.elementData = EMPTY_ELEMENTDATA;
    } else {
        throw new IllegalArgumentException("Illegal Capacity: "+
                                           initialCapacity);
    }
}


3.包含特定元素集合的构造函数。
	把传入的集合转换为数组,通过Arrays.copyOf方法把集合中的元素拷贝到elementData。
	
public ArrayList(Collection<? extends E> c) {
    elementData = c.toArray();
    if ((size = elementData.length) != 0) {
        // c.toArray might (incorrectly) not return Object[] (see 6260652)
        if (elementData.getClass() != Object[].class)
            elementData = Arrays.copyOf(elementData, size, Object[].class);
    } else {
        // replace with empty array.
        this.elementData = EMPTY_ELEMENTDATA;
    }
}
扩容是从add方法开始的:
add有两种方法
public boolean add(E e) {
    ensureCapacityInternal(size + 1);  
    elementData[size++] = e;
    return true;
}
public void add(int index, E element) {
    rangeCheckForAdd(index);

   ensureCapacityInternal(size + 1);  // Increments modCount!!
    System.arraycopy(elementData, index, elementData, index + 1,size - index);
    elementData[index] = element;
    size++;
}

都调用了ensureCapacityInternal这个方法,size + 1保证可以存入下一个元素。初始size为0,这里+1,括号传入的参数minCapacity就是1。

private void ensureCapacityInternal(int minCapacity) {
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}

这里先看内圈的方法
calculateCapacity方法:

private static int calculateCapacity(Object[] elementData, int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    return minCapacity;
}

DEFAULTCAPACITY_EMPTY_ELEMENTDATA就是默认的数组,长度为10。
无参的构造方法就回返回DEFAULT_CAPACITY(10)和minCapacity(1)的最大值。
有参的构造方法时:此方法返回1.

minCapacity第一次长度为1,后面的调用的时候每次都是数组长度的size+1.

内圈的方法看完后,再看外圈的方法:

private void ensureExplicitCapacity(int minCapacity) {
    modCount++;
    // overflow-conscious code
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}

这里比较minCapacity 和elementData的长度,这里的elementData长度是数组的实际长度。如果传入一个长度为5的有参构造方法,那么elementData.length=5,size为0,minCapacity=0+1=1;这里就不需要进行grow扩容,依次下去, 2 3 4 都不需要,但是到5的时候(5+1),当插入是就需要扩容了。

grow方法:

private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    // minCapacity is usually close to size, so this is a win:
    elementData = Arrays.copyOf(elementData, newCapacity);
}

int newCapacity = oldCapacity + (oldCapacity >> 1);
这里右移一位,就是除以2,因此就是原来倍数的1.5倍。
因为我们的minCapacity每次+1,可能就会超过newCapacity (即原来倍数的1.5倍),超过就将大的值给newCapacity 。
再判断newCapacity 和设定的 MAX_ARRAY_SIZE。一般来说, MAX_ARRAY_SIZE= Integer.MAX_VALUE - 8。
如果大于;hugeCapacity

private static int hugeCapacity(int minCapacity) {
    if (minCapacity < 0) // overflow
        throw new OutOfMemoryError();
    return (minCapacity > MAX_ARRAY_SIZE) ?
        Integer.MAX_VALUE :
        MAX_ARRAY_SIZE;
}

如果大于,返回 Integer.MAX_VALUE,否则是Integer.MAX_VALUE - 8。

最后通过复制将原数组元素放到新的大容量数组中。

参考链接:https://blog.csdn.net/qq_26542493/article/details/88873168

7.springmvc的工作原理,这里回答的还是model,view,controller,流程图。
问我为什么dispatcherservlet功能强大,hr给我说:要考虑多线程并发…。

这里 我参考了DispatcherServlet解析
主要说到,dispatcherservlet 里的doService和doDispatch两个方法,其中doDispatch获取异步请求处理器。这里应该就用到了多线程的知识,处理逻辑比较复杂,doDispatch获取处理器适配器,执行handler,返回ModelAndView。里面的源码较多,大家可以看注释。

总结

这篇文章当时面完第二天小结了一下,自己对基础要么理解深入,要么记忆深一些,否则在面试中很吃亏,需要不断回顾一些知识点,还有需要看源码,自己不想看源码的话,就看别人的讲解和注释,知道大概的逻辑,比如,这里的Arraylist扩容,就比之前的了解深入一些了,dispatcherservlet不要只知道表面,深入理解最好。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值