面试总结(知乎+京东)

知乎面试问题
一面
  • 算法题
    两个有序数组,输出第k小的数字
    思想时:分别折半查找,每个数组记录自己的left,right索引,进行查找。
  • hashmap
    • 源码结构
    • hashmap在扩容时空间创建+新旧节点的对应关系+如果扩容过程中查找该怎么查找**(分析如下)**
      • 究竟哪些节点需要申请新的存储空间?哪些不需要呢?
        • 需要新申请空间的节点:newTab,即为新table数组,申请空间个数为32、64、128等。
        • 不需要新申请空间的节点:所有链表或者红黑树上的节点;
        • 源码:resize()方法
          Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
      • 新旧索引有什么关系?
        • 因为我们使用的增长策略是2的整数次幂方式,table的容量在更改时,同一元素在table中的索引要么不变,要么移动到相对原位置而言距离2的整数次幂的一个位置。
        • 旧链表中的节点如何对应到新的table索引中?
          思想:对这个旧链表进行遍历,从而先生成两个链表;然后一个放在原来索引位置j处,一个放在[j+oldCap]处;
        • 那么生成两个链表的策略是怎样的呢?
          if ((e.hash & oldCap) == 0) 条件成立的节点是放在原来索引位置j处的链表;
          否则,放在j+oldCap索引处的链表;
      • 查找参数包括:hash(key)和key。那么接下来如何进行查找?
        • 先获得first节点的索引,其索引策略是:
          first = tab[(n - 1) & hash]
        • 接下来如果first节点为红黑树节点,则按照红黑树查找方式查找;
        • 如果first节点为链表节点,则按照链表查找方式进行查找;
        • 那么问题来了,如果此时hashMap在扩容,那么tab究竟是新tab数组还是旧tab数组?
          通过resize()方法的源码可以发现,有这样一步操作:
          Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
          table = newTab;
          并且这步操作的后面,才会发生生成新的两个链表的操作。所以如果hashMap扩容过程中发生查找操作,则在table没有被重新赋值前就在旧table中查找;否则在新table中查找;但在新table的查找中,可能新的2个链表还没有生成导致存在的元素查找结果也是null;不过本来hashMap也是线程不安全的,返回并发导致的错误结果也能理解。
  • jvm内存模型+gc算法
  • mysql索引实现
  • B树和B+树的对比
  • LRU cache的实现方式?自己说了链表方式,不知道还有什么方式
    • LinkedHashMap的实现方式
      • 一个包含3个指针节点的双向链表。节点指针为before,next,after。分别指向:
        双向链表的前一个节点;
        hash值冲突的逻辑链表;
        双向链表的后一个节点;
        参考:https://blog.csdn.net/caoxiaohong1005/article/details/79909083
      • 时间复杂度
        • get时,最好O(1),最差O(N);
        • put时,最好O(1),最差O(N);
      • 空间复杂度
        O(N)
    • 单链表+hashmap
      • 底层使用有头指针的单链表,同时用hashmap对每个key维护一个前驱节点;
      • 时间复杂度
        • get时,首先从hashmap中找到对应的节点,然后移到头节点,并更改hashmap的映射关系;
        • set时,如果cache未满,则直接插入到头节点前面,并更改hashmap的映射关系;
        • set时,如果cache满了,则将重用链表尾节点,然后挪到表头,并更改hashmap的映射关系;
      • 空间复杂度
        O(N)
  • 网络编程socket,自己说不会也就没有再问
  • TCP/IP 的三次握手和四次挥手过程
二面
  • 讲解实习项目架构和自己实现的模块功能

  • springAOP的实现原理

    • java动态代理可以对类实现吗?不知道,只知道是对interface的实现
    • java动态原理
      • 使用到的class和interface包括:Proxy类和invocationHandler接口。

      • 需要自己写的class和interface
        1)被代理对象的interface
        2)被代理对象的interfaceImpl
        3)动态代理的InvocationHandler实现类

      • JVM生成动态代理类
        传入参数:定义代理类的类加载器,代理类要实现的接口列表,invocationHandler实例

          import java.lang.reflect.InvocationHandler;
          import java.lang.reflect.Method;
          import java.lang.reflect.Proxy;
        
          /**
           * @Author: cxh
           * @CreateTime: 18/9/23 00:48
           * @ProjectName: JavaBaseTest
           */
          public class AopTest {
          	public static void main(String[] args) {
          	//定义被代理对象
          	Subject subjectImpl = new SubjectImpl();
          	//定义InvocationHandler接口实例
          	InvocationHandler invocationHandler = new InvocationHandlerImpl(subjectImpl);
          	//生成动态代理实例(定义代理类的类加载器,代理类要实现的接口列表,invocationHandler实例)
          	Subject subject = (Subject) Proxy.newProxyInstance(invocationHandler.getClass().getClassLoader(), subjectImpl.getClass().getInterfaces(), invocationHandler);
        
          	System.out.println("subject.getClass().getName():" + subject.getClass().getName());
          	subject.before();
          	}
          }
        
          /**
           *被代理对象的接口
           */
          interface Subject {
          void before();
        
          void after();
          }
        
          /**
          * 被代理对象
          */
          class SubjectImpl implements Subject {
        
          	@Override
          	public void before() {
          		System.out.println("this is method of before().");
          	}
        
          	@Override
          	public void after() {
          		System.out.println("this is method of after().");
          	}
          }
        
          /**
          * 动态代理,必须实现InvocationHandler接口
          */
          class InvocationHandlerImpl implements InvocationHandler {
        
          	Subject subject;
        
          	/**
          	 * 构造函数参数必须包括:被代理对象,这样调用动态代理的invoke()方法时,才会调用被代理对象的方法.
          	*/
          	InvocationHandlerImpl(Subject subject) {
          		this.subject = subject;
          	}
        
          	@Override
          		public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
          			System.out.println("before invoke()");
          			System.out.println("the  method is :" + method);
          			method.invoke(subject, args);
          			System.out.println("after invoke()");
        
          			return null;
          		}
          }  
           
          -----------   
          输出结果:  
          subject.getClass().getName():$Proxy0
          before invoke()
          the  method is :public abstract void Subject.before()
          this is method of before().
          after invoke()
        
  • springIOC的实现原理 :三级缓存+提前曝光+java的对象引用原理

    • Spring循环依赖的理论依据其实是Java基于引用传递,当我们获取到对象的引用时,对象的field或者或属性是可以延后设置的。Spring单例对象的初始化其实可以分为三步:
      • createBeanInstance, 实例化,实际上就是调用对应的构造方法构造对象,此时只是调用了构造方法,spring xml中指定的property并没有进行populate。
      • populateBean,填充属性,这步对spring xml中指定的property进行populate
      • initializeBean,调用spring xml中指定的init方法,或者AfterPropertiesSet方法
        会发生循环依赖的步骤集中在第一步和第二步。
    • spring在解决循环依赖中使用了三级缓存
      • 单例对象的cache:
        private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);
      • 单例对象工厂的cache:
        private final Map <String, ObjectFactory?> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);
      • 提前曝光的单例对象的cache:
        private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);
    • singleton初始化过程图片:
      在这里插入图片描述
    • 显然:两个循环依赖都是通过构造器函数进行依赖的,则spring无法解决此种依赖。因为提前暴露出来的原始bean需要执行完实例构造器函数。
  • interface 和 abstractclass 的区分和适用场景

    • 区别
      • 变量类型:抽象类中的成员变量可以是各种类型的,但是接口中的只能是public static final类型的。
      • 单根继承:一个类只能继承一个抽象类,但可以实现多个接口;
      • 实现关键字:接口需要实现implements;而抽象类需要继承extends;
      • 强调内容:interface强调特定功能的实现;abstract class强调所属关系;
    • 使用场景
      • 接口:
        • 如果想实现多继承,则使用接口;
        • 用于定义mix-in类型,表示类具有某特定性能,如Serializable接口,没有任何方法,只是用于表示实现该接口的类可以序列化,当然序列话方法要自己写代码去实现。
      • 抽象类:
        • 如果基本功能不断改变,则用抽象类。因为不断改变功能时,用接口的话,就要改变所有实现了该接口的类。
        • 某些场合下,只靠纯粹的接口不能满足类与类之间的协调,还必需类中表示状态的变量来区别不同的关系。abstract的中介作用可以很好地满足这一点.
京东面试总结
一面
  • 算法题
  • 实习项目
  • 数据库为什么要使用线程池
    • 原因:各种池其实原因都一样
      • 实现方式:
        预先创建一定数量的线程,当有请求达到时,线程池分配一个线程提供服务,请求结束后,该线程又去服务其他请求。
      • 优势
        1)避免了线程和内存对象的频繁创建和释放
        2)降低了服务端的并发度
        3)减少了上下文切换和资源的竞争,提高资源利用效率
    • MySQL的线程池改进过程
      1)One-Connection-Per-Thread
      每一个数据库连接,Mysql-Server都会创建一个独立的线程服务,请求结束后,销毁线程。
      2)基于1)的thread-cache,将线程缓存起来,以供下次使用,避免频繁创建和释放的问题,但是无法解决高连接数的问题。1)的方式随着连接数暴增,导致需要创建同样多的服务线程,高并发线程意味着高的内存消耗,更多的上下文切换(cpu cache命中率降低)以及更多的资源竞争,导致服务出现抖动。
      3)Thread-Pool实现方式
      1.线程处理的最小单位是statement(语句),一个线程可以处理多个连接的请求。
      2.在保证充分利用硬件资源情况下(合理设置线程池大小),可以避免瞬间连接数暴增导致的服务器抖动。
    • 线程池常配置的属性
      1)user和password
      2)initialPoolSize、minPoolSize、maxPoolSize
      3)acquireIncrement :声明当连接池中连接耗尽时再一次新生成多少个连接,默认为3个
      4)maxIdleTime :超过多长时间连接自动销毁,默认为0,即永远不会自动销毁
      5)maxStatements :JDBC的标准参数,用以控制数据源内加载的PreparedStatements数量
      ps:但由于预缓存的statements属于单个connection而不是整个连接池。所以设置这个参数需要考虑到多方面的因素。
      如果maxStatements与maxStatementsPerConnection均为0,则缓存被关闭
      6)idleConnectionTestPeriod :每xx秒检查所有连接池中的空闲连接
    • 线程池和连接池
      • 线程池
      1. 线程池实现在server端,通过创建一定数量的线程服务DB请求
      2. 线程池服务的最小单位是语句,即一个线程可以对应多个活跃的连接
      3. 可以将server端的服务线程数控制在一定的范围,减少了系统资源的竞争和线程上下文切换带来的消耗,同时也避免出现高连接数导致的高并发问题
      • 连接池
        1)连接池通常实现在Client端,是指应用(客户端)创建预先创建一定的连接,利用这些连接服务于客户端所有的DB请求
        2)如果某一个时刻,空闲的连接数小于DB的请求数,则需要将请求排队,等待空闲连接处理。通过连接池可以复用连接,避免连接的频繁创建和释放,从而减少请求的平均响应时间,并且在请求繁忙时,通过请求排队,可以缓冲应用对DB的冲击
        PS:连接池和线程池相辅相成,通过连接池可以减少连接的创建和释放,提高请求的平均响应时间,并能很好地控制一个应用的DB连接数,但无法控制整个应用集群的连接数规模,从而导致高连接数,通过线程池则可以很好地应对高连接数,保证server端能提供稳定的服务。
  • 毕设+神经网络
  • java实现文件的导入导出用到的类有哪些?
    只记得InputStream,OutputStream.
  • java如何实现将一个Date按照某种日期的格式显示?
    SimpleDateFormat可以实现
二面
  • 讲实习项目

  • Spring的功能有哪些
    自己从spring组成,和各个模块及常用模块的功能都讲了一遍

  • Spring事务的隔离级别

    • Spring事务的隔离级别和MySQL的事务隔离级别的关系
      • Spring使用的事务隔离级别必须是MySQL能支持的,故如果Spring设定的隔离级别MySQL不支持则此Spring的设定无效。
      • 如果Spring和MySQL设定了不同的隔离级别,那么以Spring会在事务开始时,根据你程序中设置的隔离级别,调整数据库隔离级别与你的设置一致。
  • Mybatis的二级缓存

    • 一级缓存
      • 生命周期:基于sqlSession
    • 二级缓存
      • 生命周期:基于application
      • 作用范围:按照每个namepace一个缓存来存贮和维护,同一个namespace放到一个缓存对象中。
    • cache使用的注意事项
      • 只能在【只有单表操作】的表上使用缓存
        不只是要保证这个表在整个系统中只有单表操作,而且和该表有关的全部操作必须全部在一个namespace下。
      • 在可以保证查询远远大于insert,update,delete操作的情况下使用缓存
    • 避免使用二级缓存
      多表操作不管多表操作写到哪个namespace下,都会存在某个表不在这个namespace下的情况,这会导致查询使用缓存的时候结果就是错的。
  • MySQL的索引建立原则
    自己先说了索引类型,不同引擎支持的索引类型,以及索引的使用规则

  • java中的锁怎么实现的
    自己讲了volatile,synchronized的实现原理,以及从jdk1.6开始synchronized对锁的处理:偏向锁,轻量级锁,重量级锁。以及锁只能升级不能降级。

  • java反射都用过什么?

  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
乎中,关于2022年Spring面试题的讨论有很多。Spring是一个非常流行的Java企业级开发框架,相关面试题主要围绕该框架的核心概念、特性以及应用进行。 首先,一些常见的Spring面试题可能涉及到IoC(控制反转)和DI(依赖注入)的概念和原理。回答此类问题时,我们可以解释IoC是一种设计模式,通过将对象的创建和管理交给Spring容器来实现,这样可以提高代码的可维护性和扩展性。而DI是IoC的一种具体实现方式,通过依赖注入来实现对象之间的解耦。 其次,关于Spring的AOP(面向切面编程)也是一个常见的面试题。我们可以解释AOP是一种通过将横切关注点(例如日志记录、性能监控等)与主要业务逻辑进行解耦的编程思想。Spring提供了一种基于代理模式的AOP实现方式,可以通过切面、连接点、切点和通等概念来实现对关注点的管理和处理。 此外,关于Spring MVC的问题也常常出现在面试中。我们可以解释Spring MVC是Spring框架中用于开发Web应用的模块,它采用了基于MVC设计模式的思想,通过DispatcherServlet、HandlerMapping、Controller、ViewResolver等组件来实现请求的处理和响应的生成。 另外,对于Spring中的常用注解(例如@Controller、@Service、@Autowired等)的理解,以及它们在项目中的应用也是一个常见的面试题。我们可以解释这些注解的作用和使用方式,说明它们可以简化开发流程、提高代码的可读性和可维护性。 总结来说,回答Spring面试题主要需要对Spring框架的核心原理、概念和应用有一定的理解,并能够将其与具体的项目场景进行结合,以展示个人的实际应用能力和经验。同时,在准备面试时,也可以通过查阅相关书籍、官方文档和参与实际开发来巩固和扩展自己的识。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值