大佬们的暑期实习面经整理01
标题:度小满暑期实习面试记录作者:challow来源:牛客网声明:感谢牛客网和大佬们提供的面经/鞠躬,以下仅代表个人的答案,部分文字摘自网络,仅供个人学习和复习使用,勿杠。画横线的是个人复习的时候不太会的,没有其他意思。二面和三面暂时没写,以后有时间补。
一面
-
Java 中的基本数据类型
Java 中的基本数据类型主要分为整数类型、浮点类型、字符类型和布尔类型。
整数类型有 int(占 4 字节)、byte(占 1 字节)、short(占 2 字节)、long(占 8 字节);
浮点类型有 double(占 8 字节)、float(占 4 字节);
字符类型有 char(占 2 字节);
布尔类型有 boolean(不同的 JVM 有不同的实现机制)。
理论上占 1 bit,实际上单个 boolean 变量在编译的时候使用的是 int 类型。
-
自动拆箱和自动装箱
自动拆箱和自动装箱是 JDK 1.5 提供的功能。
自动装箱就是将一个基本数据类型的数据直接赋值给对应的包装类;自动拆箱就是将一个包装类型的对象直接付给对应的基本类型变量。
自动装箱和自动拆箱的好处在于可以简化基本类型变量和包装类对象之间的转换。
-
$ 和 # 的区别(考点其实是 myBatis)
sql 语句
delete from ups_role_permission_dataparams where role_id = #{roleId,jdbcType=INTEGER}在这里用到了#{}。使用 # 时:
-
用来传入参数,sql 在解析的时候会加上" ",当成字符串来解析 ,如这里 role_id = "roleid";
-
#{} 能够很大程度上防止 sql 注入;
用 ${} 传入数据直接显示在生成的 sql 中,如上面的语句,用role_id = ${roleId,jdbcType=INTEGER},那么 sql 在解析的时候值为 role_id = roleid,执行时会报错。
-
${} 方式无法防止 sql 注入;
-
$ 一般用入传入数据库对象,比如数据库表名;
-
能用 #{} 时尽量用 #{} ;
-
-
Map 集合种类,联系
Map 代表的是具有映射关系的集合。其中种类有 EnumMap,IdentityHashMap,HashMap,Hashtable,SortedMap,WeakHashMap。常用的是 HashMap 和 TreeMap。
如果不需要排序,那么优先使用 HashMap,因为 HashMap 虽然线程不安全,但是它的性能最好。如果想要保证线程安全,可以使用 ConcurrentHashMap,它的性能好于 Hashtable, Hashtable 虽然线程安全,但它无论是 put 还是 get 都做同步处理,所以性能比较低,而且由于是比较老的类,所以现在基本不使用了。
如果需要排序的话,优先使用 LinkedHashMap,这是使用双向链表实现的一种 HashMap,它继承于 HashMap,使用双向链表解决了 HashMap 不能随时保持遍历顺序和插入顺序一致的问题,即维护了 Map 的迭代顺序,除此之外使用这种数据结构可以避免使用 TreeMap 所增加的成本;
如果想要按照自然顺序排列或者是自定义顺序排列,可以使用 TreeMap,这是一种基于红黑树的 HashMap,在创建的时候它会按照键的自然顺序或者是创建映射时候提供的 Comparator 进行排序,这取决于它的构造方法,其基本的操作有 containsKey、get、put、remove ,时间复杂度是
。
-
Java 内存模型
Java内存模型详解_sllin的博客-CSDN博客_java内存模型 这个写的特别好。
Java内存模型(Java Memory Model ,JMM)就是一种符合内存模型规范的,屏蔽了各种硬件和操作系统的访问差异的,保证了Java程序在各种平台下对内存的访问都能保证效果一致的机制及规范。
Java内存模型规定了所有的变量都存储在主内存中,每条线程还有自己的工作内存,线程的工作内存中保存了该线程中使用到的变量的主内存副本拷贝,线程对变量的所有操作都必须在工作内存中进行,而不能直接读写主内存。不同的线程之间也无法直接访问对方工作内存中的变量,线程间变量的传递均需要自己的工作内存和主存之间进行数据同步进行。
JMM是一种规范,目的是解决由于多线程通过共享内存进行通信时,存在的本地内存数据不一致、编译器会对代码指令重排序、处理器会对代码乱序执行等带来的问题。
-
synchronized 和 Lock (可能是问区别)
- synchronized 是 Java 关键字,在 JVM 层面实现加锁和解锁;Lock 是一个接口,在代码层面实现加锁和解锁。
- synchronized 可以用在代码块上、方法上;Lock 只能写在代码里。
- synchronized 在代码执行完或出现异常时自动释放锁;Lock 不会自动释放锁,需要在finally中显示释放锁。
- synchronized 会导致线程拿不到锁一直等待;Lock 可以设置获取锁失败的超时时间。
- synchronized 无法得知是否获取锁成功;Lock 则可以通过 tryLock 得知加锁是否成功。
- synchronized 锁可重入、不可中断、非公平;Lock 锁可重入、可中断、可公平/不公平,并可以细分读写锁以提高效率。
补充:synchronized 的底层实现方式
因为 synchronized 可以修饰代码块和方法,所以分类来进行说明。
当 synchronized 用来修饰代码块的时候,底层是通过 monitorenter 和 monitorexit 来实现的;
而当 synchronized 在用来修饰方法的时候,是在常量池中多了 ACC_SYNCHRONIZED 指示符。
-
线程池
系统启动一个新线程的成本是比较高的,因为它涉及与操作系统交互。在这种情形下,使用线程池可以很好地提高性能,尤其是当程序中需要创建大量生存期很短暂的线程时,更应该考虑使用线程池。
与数据库连接池类似的是,线程池在系统启动时即创建大量空闲的线程,程序将一个 Runnable 对象或 Callable 对象传给线程池,线程池就会启动一个空闲的线程来执行它们的 run() 或 call() 方法,当 run() 或 call() 方法执行结束后,该线程并不会死亡,而是再次返回线程池中成为空闲状态,等待执行下一个 Runnable 对象的 run() 或 call() 方法。
从Java 5开始,Java内建支持线程池。Java 5新增了一个Executors工厂类来产生线程池,该工厂类包含如下几个静态工厂方法来创建线程池。创建出来的线程池,都是通过ThreadPoolExecutor 类来实现的。
- newCachedThreadPool():创建一个具有缓存功能的线程池,系统根据需要创建线程,这些线程将会被缓存在线程池中。
- newFixedThreadPool(int nThreads):创建一个可重用的、具有固定线程数的线程池。
- newSingleThreadExecutor():创建一个只有单线程的线程池,它相当于调用 newFixedThreadPool() 方法时传入参数为1。
- newScheduledThreadPool(int corePoolSize):创建具有指定线程数的线程池,它可以在指定延迟后执行线程任务。corePoolSize 指池中所保存的线程数,即使线程是空闲的也被保存在线程池内。
- newSingleThreadScheduledExecutor():创建只有一个线程的线程池,它可以在指定延迟后执行线程任务。
- ExecutorService newWorkStealingPool(int parallelism):创建持有足够的线程的线程池来支持给定的并行级别,该方法还会使用多个队列来减少竞争。
- ExecutorService newWorkStealingPool():该方法是前一个方法的简化版本。如果当前机器有 4 个 CPU,则目标并行级别被设置为 4,也就是相当于为前一个方法传入4作为参数。
线程池的工作流程如下图所示:
- 判断核心线程池是否已满,没满则创建一个新的工作线程来执行任务。
- 判断任务队列是否已满,没满则将新提交的任务添加在工作队列。
- 判断整个线程池是否已满,没满则创建一个新的工作线程来执行任务,已满则执行饱和(拒绝)策略。
有关于线程池的拒绝策略:
当线程池的任务缓存队列已满并且线程池中的线程数目达到 maximumPoolSize,如果还有任务到来就会采取任务拒绝策略,通常有以下四种策略:
- AbortPolicy:丢弃任务并抛出 RejectedExecutionException 异常。
- DiscardPolicy:也是丢弃任务,但是不抛出异常。
- DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复该过程)。
- CallerRunsPolicy:由调用线程处理该任务。
-
ConcurrentHashMap 怎么控制并发的
JDK 1.7中的实现:
在 jdk 1.7 中,ConcurrentHashMap 是由 Segment 数据结构和 HashEntry 数组结构构成,采取分段锁来保证安全性。Segment 是 ReentrantLock 重入锁,在 ConcurrentHashMap 中扮演锁的角色,HashEntry 则用于存储键值对数据。一个 ConcurrentHashMap 里包含一个 Segment 数组,一个 Segment 里包含一个 HashEntry 数组,Segment 的结构和 HashMap 类似,是一个数组和链表结构。
JDK 1.8中的实现:
JDK1.8 的实现已经摒弃了 Segment 的概念,而是直接用 Node 数组+链表+红黑树的数据结构来实现,并发控制使用 synchronized 和 CAS 来操作,整个看起来就像是优化过且线程安全的 HashMap,虽然在 JDK1.8 中还能看到 Segment 的数据结构,但是已经简化了属性,只是为了兼容旧版本。
二面
- 项目是怎么做的,架构
- 下单是怎么完成的,支付,创建订单
- 介绍下eureka中服务间是怎么通信的
- 使用rabbitmq做的什么
- 怎么限购的,如果不同的商品怎么限购,用户名+商品id
- cannal怎么使用的,为什么能够监听到
- 编程题:删除链表中大于一个的节点
三面
随便问了些问题。。
- 为什么做这个[项目
- 有什么优点和缺点
- 为什么选择金融类的互联网公司
- 你的职业规划是什么样子的
- 反问

