面试题记录

简单算法

1.冒泡排序(目前没问过)

public void bubbleSort(int[] arr) {

for (int i = arr.length-1; i > 0; i--) {

                  boolean isSort = true;

                  for (int j = 0; j < i; j++) {

                          if(arr[j] > arr[j+1]) {

                                   int temp = arr[j];

                                   arr[j] = arr[j+1];

                                   arr[j+1] = temp;

                                   isSort = false;

                          }

                  }

                  if (isSort) {

                          break;

                  }

         }

}

2.快速排序(没主动问过,但是主动提到了就必然问)

public void recQuickSort(int[] arr, int left, int right) {

         if(left >= right) { // 递归停止条件

                  return;

         } else {

                  // 先获取枢纽,  _______ □ ________, 方块是枢纽,目的是让左边的比方块小,右边的比方块大

                  int pivot = arr[right];

                  // 开始划分,把比枢纽小的放到左边,比枢纽大的放到右边

                  int partition = partition(arr, left, right, pivot);

                  // 递归划分后的左边和右边

                  recQuickSort(arr, left, partition-1);

                  recQuickSort(arr, partition+1, right);

         }

}

 

public int partition(int[] arr, int left, int right, int pivot) {

         // 左边开始位置,减1是因为要使用++leftPtr

         int leftPtr = left - 1;

         // 右边同样使用--rightPtr,但是不用加1,是因为最右是枢纽,实际是从right-1开始的,因此right-1+1=right

         int rightPtr = right;

         // 开始划分

         while (true) {

                  // 找到左边比枢纽大的数

                  while (arr[++leftPtr] < pivot);

                  // 找到右边比枢纽小的数

                  while (rightPtr > 0 && arr[--rightPtr] > pivot);

                  // 循环终止条件

                  if (leftPtr >= rightPtr) {

                          break;

                  } else {

                          // 否则交换位置

                          swap(arr, leftPtr, rightPtr);

                  }

         }

         // 划分完后,将最右(枢纽放到方块位置)

         swap(arr, leftPtr, right);

         return leftPtr;

}

 

public void swap(int[] arr, int i, int j) {

         int temp = arr[i];

         arr[i] = arr[j];

         arr[j] = temp;

}

3.二分查找(目前没问过,海量有序数据估计需要用这个)

public int find(int[]arr, int value) {

         int min = 0;

         int max = arr.length;

         while (true) {

                  int mid = (min + max) / 2;

                  if (min > max) {

                           return -1;

                  } else if (arr[mid] == value) {

                          return mid;

                  } else if (arr[mid] > value) {

                          max = mid - 1;

                  } else if (arr[mid] < value) {

                          min = mid + 1;

                  }

         }

}

JAVA基础

  1. 八种基本数据类型(问过一次)
  2. ArrayList(问的频率高)

ArrayList是基于数组实现的,在内存中式连续的,查找快,增删慢,因为增删时需要对数组进行System.arraycopy(但是如果不扩容,且在末尾增加时,效率还是很快);

默认初始化容量是10,扩容机制:jdk1.8采用位运算,即a=a+a>>1(1.5倍),1.8以前a=a*3/2+1;

每次调用添加数据时都会校验容量,不够了就扩容;

(有时间可以看下add(E e)这个方法,没准让手写)。

  1. LinkedList(目前没问过)

LinkedList是基于链表实现的(双向链表),内存中散列分布,增删快,查找慢,因为查找要从链表起始点first开始去一步步找过去;

基本就是纯双向链表的数据结构,不用看源码,看双向链表原理就行,不看也没事,没考过。

  1. HashMap(牛逼公司会问,JDK1.8和1.8前有较大区别,这里是1.8前)

存储结构是基于数组+链表的形式,通过hash值找到具体存在数组哪个位置,然后如果hash值相同的不同key,会以链表的形式存上,链表是“新key → 旧key”,如图;

 

几个重要的参数:

初始化容量:16(不管指定多大,一定会变成2的次方形式,如指定100,会变成128);

扩容因子:默认0.75(扩容因子作用是控制数组利用率与链表长度,经验统计0.75最好);阈值:容量*扩容因子(扩容时不是直接和容量比,而是和阈值,如当前容量128,扩容因子0.75,size=97时(>128*0.75)就会扩容);

put方法(hashmap的方法也只会问这个,这个涵盖了很多)流程:

  1. Key是否为null,是就存进去(hashmap可以存null的原因);
  2. Key不为空,则算出key对应的hash值,然后hash值异或(初始化容量 - 1)找到数组中的位置;PS:这里可以解释前面为什么容量为2的次方,例如2的4次方二进制是10000,-1后变成1111,这样异或hash值才能最大程度避免链表变长,保证充分利用数组,足够散列,如图;

  1. 找到数组中对应位置后,看该位置是否已经存在值,存在的话看key是否重复,重复就替换value,不重复就放进数组中,链表指向旧值。
  2. 当容量超过阈值,会扩容为原来两倍。
  1. HashSet(目前没考过)

HashSet是基于HashMap的key不可重复实现的,底层维护了一个HashMap;HashSet不可重复,无序。

  1. TreeMap(目前没问,基于红黑树实现,底层挺恶心,需要的话我再补充上)
  2. TreeSet(目前没问,基于TreeMap实现的,同理于HashSet)
  3. HashMap线程安全问题(问了一次)

HashMap线程不安全,问如何保证线程安全;

1)HashTable线程安全,但是是在HashMap各个方法上直接加了synchronized来同步,因为缺点就是不需要同步的时候也会同步,效率极低,如public synchronized V put(K key, V value);

2)用Collections这个工具类,Collections.synchronizedMap(map),该方法是创建一个Object充当锁,如:

final Object mutex;

public V put(K key, V value) {

    synchronized (mutex) {return m.put(key, value);}

}

  1. ConcurrentHashMap,最好的方法,为啥最好还没研究。
  1. 线程创建的三种方法(挺频繁)
  1. 继承Thread类,重写run()方法;
  2. 实现Runnable接口,重写run()方法;
  3. 实现Callable接口,重写call()方法;

因为java单继承多实现,所以只重写run()方法的话尽量实现Runnable接口,除非需要重写Thread其他方法再继承该累;

Callable接口与另外两个区别:该方法有返回值、可以抛异常、可以创建Future类监听异步返回结果。

  1. synchronized、volatile、Threadlocal类区别(问了一次)

synchronized修饰函数,代码块;

volatile修饰全局变量,但是它并不一定安全,只能保证缩小概率,它只是保证在修改后立刻显示到对内存,同步期间有可能会读出之前的值;

Threadlocal类保证局部变量,在本线程内随时可取,隔离其他线程。

  1. 实现锁的方法(挺频繁)
  1. synchronized修饰方法或者代码块;
  2. Lock锁(接口,需要具体实现类,java提供了一些实现类):创建lock锁,lock.lock()开启锁,lock.unlock()解锁。

PS:有时间看看ReentrantLock(可重入锁的源码

  1. wait,sleep区别(问了一次)

Wait为Object的方法,配套notify,notifyAll使用,将当前线程挂起,会释放锁;

Sleep为Thread类方法,暂时休眠,到点执行,不释放锁;

线程异常了自动释放锁。

  1. 执行顺序问题(笔试可能会考)

静态代码块,构造函数,父类,子类等等的执行顺序

  1. finally代码块内容什么时候执行(目前没问)

比较有代表性的两段代码

1)

2)

 

 

 

  1. 什么是序列化,如何序列化(问了一次)

将类同步到本地

如何实现:1)实现serializable接口;2)借助json(如Map转json);3)反正就是能同步到本机就行。

数据库相关

  1. 什么是事物(问了一次)

大概解释,就是数据库发生的动作,主要回答出那四个特性:原子性(一个事物是一个最小单元),一致性(要么同时生效(提交),要么同时失效(回滚)),隔离性(重点),持久性(一旦事物完成,将永久在数据库生效)

  1. 事物隔离级别(针对上面的重点,基本必考)

以下简称操作者一为1,操作者二为2

四种级别,由低到高:

  1. 读未提交(1更改了数据未提交,2能读到更改后的数据,此时1回滚,2出现脏读);
  2. 读提交(避免发生1)的情况,但是会出现不可重复读,即1读取未提交,2修改并提交,1在未提交的基础上再读,读到了2修改后的结果,无法读到一开始的结果)
  3. 可重复读(避免了2)的情况,但是会出现幻读,如1读取数据未提交,2增加或删除并提交,1提交后再读,就和第一次不一样了)
  4. 序列化(级别最高,什么问题都不会出现,就是效率极低,因为1读取时,所有人不能对当前表进行任何操作)

PS:MySql默认级别是3),其余都是2)

  1. 创建索引的注意事项(问了一次)
  2. Oracle,mysql分页查询(问了一次)

常见框架

  1. Springmvc工作原理(挺频繁)
  1. 用户请求,传给dispatcherServlet;
  2. dispatcherServlet去handlerMapping找对应的映射返回给dispatcherServlet;
  3. dispatcherServlet调用handlerAdapter;
  4. handlerAdapter执行具体的controlle,得到ModelAndViewr;
  5. 将ModelAndView传给dispatcherServlet;
  6. dispatcherServlet将ModelAndView传给ViewResolver;
  7. ViewResolver将ModelAndView解析成View传回dispatcherServlet;
  8. dispatcherServlet根据view进行渲染,然后响应用户。
  1. Spring提供的事物(结合数据库事物隔离级别回答)

Spring提供5中事物处理方式,1个默认支持当前数据库默认采用的级别,四个对应数据库四种级别(然后把四个级别介绍一下)

  1. Spring的IOC、AOP(频繁)

IOC(控制反转):将创建对象的工作交给spring,目的是减少代码耦合,不然一有修改就要修改好多地方,耦合严重;

AOP(切面编程):就是纵向、切面(给面试官画画图比划比划,我也说不太好),spring采用代理方式实现切面:

  1. java提供的动态代理:使用条件是类必须实现接口,代码如下(不会让你写,能说出来就行)

  1. Cglib代理:类没有实现接口,只能用这个,代码如下(不会让你写,能说出来就行)

通过Enhancer这个加强器实现的。

  1. Mybatis里#和$的用法

#:会进行预编译,而且进行类型匹配;

$:不进行数据类型匹配。

如:变量name的类型是string, 值是"张三"的时候

              $name$ = 张三

              #name# = '张三'

其他

  1. 单例模式的几种方法(百度一下,常问)
  2. 海盗分金问题(百度一下,没问过,很简单)
  3. 已知String[] s1= {bala、bala...},String[] s2 = {bala、bala...},如何找出s1,s2相同的元素(借助HashSet,这个会笔试)
  4. 如何确定字符串里每个字母出现多少次(借助HashMap,出现一次,把key写成那个字母,value写1,再出现,value+1)
  5. jvm

https://www.cnblogs.com/junwangzhe/p/6282550.html

https://blog.csdn.net/zd836614437/article/details/64126826

  1. 数据结构(栈,队列,二叉树,二叉树问的多,添加和删除,红黑树你不引导面试官问就不会问,但是答上来必然加分的加)

 

猜你喜欢

转载自blog.csdn.net/weixin_36205175/article/details/81275297