欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

最新面试问题

程序员文章站 2024-03-23 21:18:28
...

设计模式及场景

  • 单例模式:
    网站在线人数统计;数据库连接池;Windows任务管理器等
  • 工厂模式:
    IOC创建 Bean;
  • 观察者模式:
    一对多的关系,如商家上架新产品通知其粉丝;取消订单时其他相关的功能也撤销原有操作;
  • 策略模式:
    会员打折(初级会员无折扣,中级会员9折,高级会员7折);
  • 责任链模式:
    多个对象可以处理同一个请求,但具体由哪个对象处理则在运行时动态决定;
  • 装饰者模式:
    在原有功能的基础上进行功能扩展;

集合

  • ArrayList
    • ArrayList 的底层是数组队列,相当于动态数组。与 Java 中的数组相比,它的容量能动态增长。在添加大量元素前,应用程序可以使用ensureCapacity操作来增加 ArrayList 实例的容量。这可以减少递增式再分配的数量。
    • 以无参数构造方法创建 ArrayList 时,实际上初始化赋值的是一个空数组。当真正对数组进行添加元素操作时,才真正分配容量。即向数组中添加第一个元素时,数组容量扩为 10。
  • LinkList
    • LinkedList是一个实现了List接口和Deque接口的双端链表。 LinkedList底层的链表结构使它支持高效的插入和删除操作,另外它实现了Deque接口,使得LinkedList类也具有队列的特性; LinkedList不是线程安全的,如果想使LinkedList变成线程安全的,可以调用静态类Collections类中的synchronizedList方法
  • HashMap
    • JDK1.8 之前 HashMap 由 数组+链表 组成的,数组是 HashMap 的主体,链表则是主要为了解决哈希冲突而存在的(“拉链法”解决冲突).JDK1.8 以后在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为 8)时,将链表转化为红黑树(将链表转换成红黑树前会判断,如果当前数组的长度小于 64,那么会选择先进行数组扩容,而不是转换为红黑树),以减少搜索时间
  • ConcurrentHashMap
    • Java8 中的 ConcruuentHashMap 使用的 Synchronized 锁加 CAS 的机制
    • Java7 中的 Segment 数组 + HashEntry 数组 + 链表 进化成了 Java8 Node 数组 + 链表 / 红黑树,Node 是类似于一个 HashEntry 的结构。它的冲突再达到一定大小时会转化成红黑树,在冲突小于一定数量时又退回链表。

ArrayList 与 LinkList 区别

  • 是否保证线程安全: ArrayList 和 LinkedList 都是不同步的,也就是不保证线程安全;
  • 底层数据结构: Arraylist 底层使用的是 Object 数组;LinkedList 底层使用的是 双向链表 数据结构(JDK1.6 之前为循环链表,JDK1.7 取消了循环。注意双向链表和双向循环链表的区别,下面有介绍到!)
  • 插入和删除是否受元素位置的影响: ① ArrayList 采用数组存储,所以插入和删除元素的时间复杂度受元素位置的影响。 比如:执行add(E e)方法的时候, ArrayList 会默认在将指定的元素追加到此列表的末尾,这种情况时间复杂度就是 O(1)。但是如果要在指定位置 i 插入和删除元素的话(add(int index, E element))时间复杂度就为 O(n-i)。因为在进行上述操作的时候集合中第 i 和第 i 个元素之后的(n-i)个元素都要执行向后位/向前移一位的操作。 ② LinkedList 采用链表存储,所以对于add(E e)方法的插入,删除元素时间复杂度不受元素位置的影响,近似 O(1),如果是要在指定位置i插入和删除元素的话((add(int index, E element)) 时间复杂度近似为o(n))因为需要先移动到指定位置再插入。
  • 是否支持快速随机访问: LinkedList 不支持高效的随机元素访问,而 ArrayList 支持。快速随机访问就是通过元素的序号快速获取元素对象(对应于get(int index)方法)。
  • 内存空间占用: ArrayList 的空 间浪费主要体现在在 list 列表的结尾会预留一定的容量空间,而 LinkedList 的空间花费则体现在它的每一个元素都需要消耗比 ArrayList 更多的空间(因为要存放直接后继和直接前驱以及数据)。

线程池

  • 运行过程
    • 判断核心线程池是否已满(都有任务),若不是则创建一个新的工作线程来执行新任务;否则判断盘对工作队列是否已满
    • 判断工作队列是否已满,若不是则将提交的新任务加入工作队列中;若工作队列已满,则判断线程池是否已满
    • 判断线程池是否已满,若不是则创建一个新的线程来执行新任务;否则交给饱和策略来处理这个任务
  • 七大参数
    • corePoolSize 线程池核心线程大小

    • maximumPoolSize 线程池最大线程数量

    • keepAliveTime 空闲线程存活时间

    • unit 空间线程存活时间单位:keepAliveTime的计量单位

    • workQueue 工作队列

    • threadFactory 线程工厂

    • handler 拒绝策略/饱和策略

  • 拒绝策略

1、AbortPolicy:直接抛出异常(默认)

2、CallerRunsPolicy:只用调用所在的线程运行任务

3、DiscardOldestPolicy:丢弃队列里最近的一个任务,并执行当前任务。

4、DiscardPolicy:不处理,丢弃掉。

Mybatis

#{ } 与 ${}

  1. #{} 解析为一个 JDBC 预编译语句(prepared statement)的参数标记符,一个 #{ } 被解析为一个参数占位符;而${}仅仅为一个纯碎的 string 替换,在动态 SQL 解析阶段将会进行变量替换。

  2. #{} 解析之后会将String类型的数据自动加上引号,其他数据类型不会;而${} 解析之后是什么就是什么,他不会当做字符串处理。

  3. #{} 很大程度上可以防止SQL注入(SQL注入是发生在编译的过程中,因为恶意注入了某些特殊字符,最后被编译成了恶意的执行操作);而${} 主要用于SQL拼接的时候,有很大的SQL注入隐患。

4.在某些特殊场合下只能用${},不能用#{}。例如:在使用排序时ORDER BY ${id},如果使用#{id},则会被解析成ORDER BY “id”,这显然是一种错误的写法。

分页插件

实现 Interceptor 接口

public interface Interceptor {

  Object intercept(Invocation invocation) throws Throwable;

  Object plugin(Object target);

  void setProperties(Properties properties);

}

其中一个关键的方法就是 intercept,从而实现拦截

分页插件的原理就是使用MyBatis提供的插件接口,实现自定义插件,在插件的拦截方法内,拦截待执行的SQL,然后根据设置的dialect(方言),和设置的分页参数,重写SQL ,生成带有分页语句的SQL,执行重写后的SQL,从而实现分页

所以原理还是基于拦截器

使用参考

redis 5种数据结构

  • String - 字符
    set key valule / get key / del key
  • Hash - 哈希
    hset key field value ( hset myhash username lixin;hset myhash password 123):myhash{username:lixin;password:123}
    hget key filed (hget myhash username )
    hgetall key (hgetall myhash )
    hdel key field
  • List - 列表
    lpush / rpush key value (lpush mylist a)
    lrange key start end : 范围获取 lrange mylist 0 -1 : 获取全部
    lpop / rpop key : 删除列表最左边/最右边的元素,并将元素返回
    del key
  • Set - 集合
    sadd key value
    smembers key (所有)
    srem key value
  • SortedSet - 有序集合
    zadd** key score value ( zadd mysort 65 lisi ) :通过score属性来排序
    zrange key start end (withsocres)
    zrem key value

锁升级

  • 偏向锁
  • 轻量级锁
  • 重量级锁

最新面试问题

GC 垃圾回收

  • 可达性算法

GC root 对象

  • Class - 由系统类加载器(system class loader)加载的对象,这些类是不能够被回收的

  • Thread - 活着的线程

  • Stack Local - Java方法的local变量或参数

Java 中

a.虚拟机栈(栈桢中的本地变量表)中的引用的对象

b.方法区中的类静态属性引用的对象

c.方法区中的常量引用的对象

d.本地方法栈中JNI的引用的对象

mysql 索引

  • 组合索引的有效及失效场景
    Mysql从左到右的使用索引中的字段,一个查询可以只使用索引中的一部份,但只能是最左侧部分。例如索引是key index (a,b,c)。 可以支持a | a,b| a,b,c 3种组合进行查找,但不支持 b,c进行查找 .当最左侧字段是常量引用时,索引就十分有效。

IOC 容器初始化流程

两种容器类型:BeanFactory && ApplicationContext

  • Resource定位;指对BeanDefinition的资源定位过程。通俗地讲,就是找到定义Javabean信息的XML文件,并将其封装成Resource对象。

  • BeanDefinition的载入;把用户定义好的Javabean表示为IoC容器内部的数据结构,这个容器内部的数据结构就是BeanDefinition。

  • 向IoC容器注册这些BeanDefinition。

排序算法时间复杂度

最新面试问题

Java8 新特性

  • Stream流
    • Filter(过滤)
    • Sorted(排序)
    • Match(匹配)
    • Count(计数)
    • Map(映射)
      中间操作
  • Optional
    • Optional 是一个简单的容器,其值可能是null或者不是null。在Java 8之前一般某个函数应该返回非空对象但是有时却什么也没有返回,而在Java 8中,你应该返回 Optional 而不是 null。
//of():为非null的值创建一个Optional
Optional<String> optional = Optional.of("bam");
// isPresent(): 如果值存在返回true,否则返回false
optional.isPresent();           // true
//get():如果Optional有值则将其返回,否则抛出NoSuchElementException
optional.get();                 // "bam"
//orElse():如果有值则将其返回,否则返回指定的其它值
optional.orElse("fallback");    // "bam"
//ifPresent():如果Optional实例有值则为其调用consumer,否则不做处理
optional.ifPresent((s) -> System.out.println(s.charAt(0)));     // "b"`
  • Lambda
    举例
List<String> names = Arrays.asList("peter", "anna", "mike", "xenia");

Collections.sort(names, new Comparator<String>() {
    @Override
    public int compare(String a, String b) {
        return b.compareTo(a);
    }
});

lambda 方式

Collections.sort(names, (String a, String b) -> {
    return b.compareTo(a);
});

//进阶
Collections.sort(names, (String a, String b) -> b.compareTo(a));
  • 函数式接口

SpringBoot 各种 starter 如何实现

  • 首先是@EnableConfigurationProperties注解import了EnableConfigurationPropertiesImportSelector后置处理器;
  • EnableConfigurationPropertiesImportSelector后置处理器又向Spring容器中注册了ConfigurationPropertiesBeanRegistrar和ConfigurationPropertiesBindingPostProcessorRegistrar这两个bean;
  • 其中ConfigurationPropertiesBeanRegistrar向Spring容器中注册了XxxProperties类型的bean;ConfigurationPropertiesBindingPostProcessorRegistrar向Spring容器中注册了ConfigurationBeanFactoryMetadata和ConfigurationPropertiesBindingPostProcessor两个后置处理器;
  • ConfigurationBeanFactoryMetadata后置处理器在初始化bean factory时将@Bean注解的元数据存储起来,以便在后续的外部配置属性绑定的相关逻辑中使用;
  • ConfigurationPropertiesBindingPostProcessor后置处理器将外部配置属性值绑定到XxxProperties类属性的逻辑委托给ConfigurationPropertiesBinder对象,然后ConfigurationPropertiesBinder对象又最终将属性绑定的逻辑委托给Binder对象来完成。

参考文章

CMS收集器四个阶段,哪两个阶段工作线程会停止工作

  • 初始标记
    初识标记:这个过程是标记从gc root出发发的直接相关的引用。这个时间很短,但是是stop the world;

  • 并发标记
    并发标记:用户线程并行执行,进行相关的引用标记。这个时间很长,一般决定于堆内存的大小。所使用的线程数为(cpu个数+3)/4,所以当cpu核数很少时,在并发标记阶段会出现严重的性能下降。为了解决这个问题,对于cpu核数很少时,在并发标记阶段会与用户线程交叉执行,以使服务器性能不至于下降的太严重。但是这样操作会使标记过程所耗费的时间更长。

  • 重新标记
    重新标记:因为在并发标记时,用户线程在执行,可能会造成再次的实例引用。所以需要重新标记一下。这个阶段的标记也是stop the world,并且是并行标记。

  • 并发清除
    并发清除,即清除相关的垃圾。

CMS使用的是标记清除算法,会造成内存碎片,当老年代无法再次分配内存时需要FULL GC。CMS提供了一个参数-XX+UseCMSCompactAtFullCollection,即在执行FULL GC时开启内存碎片的合并整理过程。这也会引起stop the world。

CMS在进行垃圾回收时,无法处理浮动垃圾。所以在进行垃圾回收时,需要留有一定的内存供用户线程使用。CMS提供了一个内存触发垃圾回收的内存使用比例:
-XX:CMSInitiatingOccupancyFraction,如果预留的内存不够使用,就会出现Cocurrent Mode Failure失败,这时就需要启动后备预案:临时使用串行收集器重新进行老年代的垃圾收回,这个时间更长。

CMS 用来进行老年代的垃圾回收,这个与ParNew(多线程的串行垃圾回收)进行组合,用于整个的堆内存的垃圾回收。

JDK 自带命令

  • jps:查看Java进程
  • jstack:查看线程堆栈命令
  • jstat:性能监控工具命令
  • jmap:打印内存映射

使用例子

索引为什么用 B+ 树,不用 B 树

  • B+树只有叶节点存放数据,其余节点用来索引,而B-树是每个索引节点都会有Data域。这就决定了B+树更适合用来存储外部数据,也就是所谓的磁盘数据。

  • IO 次数

参考文章

常用linux命令

  • 查询端口号:netstat -anp |grep 端口号;lsof -i:端口号
  • 查询进程:pstree -aup(树状);ps aux(查询当前运行线程状态)
  • 终止进程:kill -9(强制);kill -15

限流

  • Guava 用户限流
  • Redis 限制重复操作

上一篇: Linux中进程间通信之管道

下一篇: