2020年三、四月java面试题汇总

文章目录

  • 一、JavaSE基础
    • 谈谈对OOP的理解
    • Java基本类型所占字节和范围
    • int(long) 和 float(double) 哪个表示的范围大?为什么?
    • Int和Integer的区别,在使用时有什么区别?
    • 重写,重载
    • ==与equals()
    • 接口与抽象类
    • String底层使用什么实现的?为什么不可变?
    • String,StringBuilder,StringBuffer的区别?
    • 什么是值传递?什么是引用传递?
    • 格式化日期用的什么?
    • IO流中都有哪些类?
    • 如何创建文件和目录?
    • 面向对象编程的六大原则
    • 了解哪些设计模式?具体说说其中的某个?
  • 二、集合
    • List,Set,Map区别(扩容过程)
      • (1)List集合
      • (2)Set集合
      • (3)Map
    • HashMap遍历怎么遍历?
    • 平时什么情况下使用HashMap?
    • 简单说一下什么是hash碰撞?怎么解决的?
    • TreeMap和LinkedHashMap有什么区别?
    • 如何删除一些集合中满足条件的元素?
  • 三、多线程
    • 1. 创建线程有哪些方法(4种)?
    • 2. Runnable和Callable有什么不同?
    • 3. start()和run()方法有什么区别?
    • 4. 线程池有哪些参数
    • 5. 线程池有几种(5种)?拒绝策略有几种(4种)?阻塞队列有几种(3种)?
    • 6. 如何检测死锁?
    • 7. volatile底层是怎么实现的?
    • 8. volatile与synchronized有什么区别?
    • 9. java中的线程有几种状态(6种)
  • 四、MySql
    • sql优化
    • 索引
      • 什么是索引
      • 索引的类型
      • 如何判断一条sql语句是否使用了索引(执行计划)
      • MySQL 服务占用 cpu 100%,如何排查问题?
    • 事务
      • Mysql事务的隔离级别
      • 什么是脏读?什么是幻读?
    • 存储引擎
      • 存储引擎对比
      • 如何选择合适的存储引擎
      • Innodb有哪些特性?
      • Innodb的事务锁有哪些?(4种)
    • 数据库的乐观锁怎么实现的?
    • 使用什么工具创建表?
    • 表结构是怎么设计的?
    • 数据库集群是怎么同步的?
    • 如何防止SQL注入?
  • 五、Redis
    • 1. Redis有哪些优势?
    • 2. 有几种数据类型?你用到了哪些数据类型(5种)?在哪儿用到的?
    • 3. redis的过期策略有哪些?
    • 4. 持久化
      • (1)RDB
      • (2)AOF
    • 5. Redis集群保证了CAP中的什么?
    • 6. Redis主从复制原理
    • 7. Redis集群会有写操作丢失吗?为什么?
  • 六、Linux
    • 1. 什么是管道?
    • 2. 查看cpu,内存占用的命令
  • 七、Spring
    • 1. IoC
    • 2. Aop
    • 3. 解释一下 AOP 里面的几个名词:
    • 4. Aop在项目中用在什么地方?
    • 5. IoC和Aop有什么作用?
    • 6. AspectJ的Aop和Spring的Aop有什么区别?
    • 7. Spring如何实现事务的控制?
  • 八、SpringMVC
    • 1. SpringMVC执行流程
    • 2. @RequestParam和@PathVariable
    • 3. @ResponseBody和@RequestBody
    • 4. 如何拦截请求?
  • 九、MyBatis
    • 1. #{}和${}都用过吗?有什么区别?
    • 2. 模糊查询怎么使用?
    • 3. 动态查询的sql怎么写?
    • 4. MyBatis的缓存机制
  • 十、SpringBoot
    • 1. SpringBoot配置文件加载顺序
    • 2. SpringBoot的核心注解?由哪些子注解组成?
    • 3. SpringBoot的自动配置
    • 4. Spring Boot Starter
  • 十一、RabbitMQ
    • RabbitMQ的组成
    • RabbitMQ使用场景
    • 如何保证消息必达?
    • 如何保证消息不被重复消费?
  • 十二、web编程
    • 简单说一下HTTP
    • 简单说一下http请求四次挥手的过程?
    • 什么是Cookie,什么是Session,有什么区别?
    • Session的工作原理
    • 如果客户端Cookie被禁用,Session还能使用吗?
    • Tomcat是如何配置内存的?
    • 防止表单重复提交的方法有哪些?
    • 什么情况下会出现跨域问题,如何解决?
  • 十三、其它
    • 1. maven配置文件有哪些主要的元素?
    • 自我介绍+项目介绍(你用了那些技术,项目用了哪些技术)
    • 还有什么想问的
    • 你的期望薪资是多少?
    • 你的离职原因是什么?

2020年3月份一些中小型互联网或计算机软件公司的社招题目,我会尽最大努力确保答案的正确性,但是我也才疏学浅,如果有错误或者遗漏的地方,欢迎指出。持续更新中……。

一、JavaSE基础

谈谈对OOP的理解

  1. 首先面向对象是一种编程思想
  2. 万物皆对象。我的电脑是一个对象,我的手机是一个对象等等,OOP可以理解为使用代码来模拟现实生活
  3. 三大特性:封装、继承和多态
    1. 封装就是隐藏类的内部信息,不允许外部程序直接访问,而是通过getter(获取)和setter(设置)方法完成,提高数据的安全性
    2. 继承是指:父类的基本特征和行为,子类也会有,子类也可以改变这些特征和行为。
    3. 多态就是多个对象调用同一个方法,可能会得到的是不同的结果。

Java基本类型所占字节和范围

类型 字节数 位数 取值范围
byte 1 8 -27 ~ 27-1
short 2 16 -215 ~ 215-1
char 2 16
int 4 32 -231 ~ 231-1
long 8 64 -263 ~ 263-1
float 4 32 3.402823e+38 ~ 1.401298e-45
double 8 64 1.797693e+308 ~ 4.9000000e-324
boolean 1(前7位是0) 1 0或者1

int(long) 和 float(double) 哪个表示的范围大?为什么?

float表示的 范围更大floatint 都是4个字节,而 float 还要表示小数,为什么 float 表示的数字范围大?

  • int 底层使用的是补码的方式表示一个数:1 位符号位 + 31 位二进制数
  • float 底层使用的是IEEE 754 浮点单精度数字格式,简单来说就是用指数的形式去表示一个数:1 位符号位 + 8 位指数 + 23位尾数

Int和Integer的区别,在使用时有什么区别?

  1. Integerint的包装类,int则是java的一种基本数据类型
  2. Integer变量必须实例化后才能使用,而int变量不需要
  3. Integer实际是对象的引用,当new一个Integer时,实际上是生成一个指针指向此对象;而int则是直接存储数据值
  4. Integer的默认值是nullint的默认值是0

重写,重载

  • 方法重载是指同一个类中的多个方法具有相同的名字,但这些方法具有不同的参数列表,即参数的数量或参数类型不能完全相同
  • 方法重写是存在子父类之间的,子类定义的方法与父类中的方法具有相同的方法名字,相同的参数表和相同的返回类型
    • 子类中不能重写父类中的final方法
    • 子类如果不是抽象类,则必须重写父类中的abstract方法

==与equals()

  • ==号在比较基本数据类型时比较的是值,而在比较两个对象时比较的是两个对象的地址值
  • equals()方法存在于Object类中,其底层依赖的是==号,如果类没有重写Object中的equals()方法的类中,则调用equals()方法其实和使用==号的效果一样

接口与抽象类

相同点:

  1. 都不能被实例化
  2. 接口的实现类或抽象类的子类都只有实现了抽象方法后才能实例化。

不同点:

  1. 意义不同:接口是功能的封装,解决对象能干什么;抽象类是事物的抽象,解决对象到底是什么。
  2. 内容不同:接口中的内容只能有:抽象方法(public abstract修饰),常量(public static final修饰),在JDK8中,接口还可以定义static静态方法和default方法;而抽象类可以有普通类的所有内容和抽象方法。
  3. 接口支持多继承,类只允许单继承

String底层使用什么实现的?为什么不可变?

String的底层使用的是char数组。这个char数组和String这个类都是final修饰的,所以不可变。

String,StringBuilder,StringBuffer的区别?

String是只读字符串,它并不是基本数据类型,而是一个对象。从底层源码来看是一个final类型的字符数组,所引用的字符串不能被改变,一经定义,无法再增删改。每次对String的操作都会生成新的String对象。

String的每次+操作 : 隐式在堆上new了一个跟原字符串相同的StringBuilder对象,再调用append方法 拼接
+后面的字符,最后再调用toString方法,返回String对象

StringBufferStringBuilder他们两都继承了AbstractStringBuilder抽象类,从AbstractStringBuilder抽象类中我们可以看到他们的底层都是可变的字符数组,所以在进行频繁的字符串操作时,建议使用StringBufferStringBuilder来进行操作。

另外StringBuffer 对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。StringBuilder 并没有对方法进行加同步锁,所以是非线程安全的

什么是值传递?什么是引用传递?

  • 值传递:是对 基本类型 变量而言的,传递的是该变量的一个副本,改变副本不影响原变量.
  • 引用传递:一般是对于 对象类型 变量而言的,传递的是该对象首地址的一个副本,并不是原对象本身,改变副本会影响原变量(本质都是同一个对象)。

格式化日期用的什么?

使用的是 SimpleDateFormat

  • 把日期转换为指定格式的字符串
    //使用SimpleDateFormat类,在构造方法中指定日期的格式:y年,M月,d日,H小时,m分钟,s秒
    SimpleDateFormat sdf = new SimpleDateFormat("yyy y-MM-dd HH:mm:ss");
    //调用SimpleDateFormat实例方法format(Date)可以把日期转换为字符串
    String text = sdf.format(date);
    
  • 把字符串转换为日期
    text = "2068年5月12日 8:28:58";
    //先创建SimpleDateFormat对象, 指定的格式串要与字符串匹配
    SimpleDateFormat anotherSdf = new SimpleDateFormat("yyyy年M月dd日 H:mm:ss");
    //调用SimpleDateFormat的实例方法parse(String)可以把字符串转换为日期对象, 如果格式串与字符串不匹配就会产生异常
    date2 = anotherSdf.parse(text);
    

IO流中都有哪些类?

节点流(介质流)

父类 InputStream(输入流、字节流) OutputStream(输出流、字节流) Reader(输入流、字符流) Writer(输出流、字符流)
文件 FileInputStream FileOutputStream FileReader FileWriter
数组 ByteArrayInputStream ByteArrayOutputStream CharArrayReader CharArrayWriter
字符串 StringReader StringWriter
管道 PipedInputStream PipedOutputStream PipedReader PipedWriter

处理流(装饰流)

父类 InputStream(输入流、字节流) OutputStream(输出流、字节流) Reader(输入流、字符流) Writer(输出流、字符流)
转换流 InputStreamReader OutputStreamWriter
缓冲流 BufferedInputStream BufferedOutputStream BufferedReader BufferedWriter
数据流 DataInputStream DataOutputStream
对象流 ObjectInputStream ObjectOutputStream
打印流 PrintStream PrintWriter

如何创建文件和目录?

File 对象可以表示文件或目录:

//第一个参数为父路径字符串,第二个参数为子路径名字
File f = new File("D:", "test");
f.mkdir();           //创建文件夹, 如果已存在重名的文件(夹),创建失败
f.createNewFile();   //创建文件

f.delete();          //删除文件

面向对象编程的六大原则

  1. 开闭原则

    开闭原则的意思是:对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。简言之,是为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类。

  2. 里氏替换原则

    所有引用基类的地方必须能透明地使用其子类的对象,也就是说子类可以扩展父类的功能,但不能改变父类原有的功能

    里氏替换原则是对开闭原则的补充。实现开闭原则的关键步骤就是抽象化,而基类与子类的继承关系就是抽象化的具体实现,所以里氏替换原则是对实现抽象化的具体步骤的规范。

  3. 依赖倒转原则

    高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。简单的说就是尽量面向接口编程.

  4. 接口隔离原则

    客户端不应该依赖它不需要的接口;一个类对另一个类的依赖应该建立在最小的接口上。接口最小化,过于臃肿的接口依据功能,可以将其拆分为多个接口。

  5. 迪米特法则,又称最少知道原则

    一个实体应当尽量少地与其他实体之间发生相互作用,使得系统功能模块相对独立。简单的理解就是高内聚,低耦合,一个类尽量减少对其他对象的依赖。

  6. 单一职责原则

    单一职责原则通俗来说就是一个类只负责一项职责。如果一个类有多个职责,这些职责就耦合在了一起。当一个职责发生变化时,可能会影响其它的职责。另外,多个职责耦合在一起会影响复用性。

了解哪些设计模式?具体说说其中的某个?

  1. 单例模式:懒汉式、饿汉式、双重校验锁、静态加载,内部类加载、枚举类加载。保证一个类仅有一个实例,并提供一个访问它的全局访问点。
  2. 代理模式:动态代理和静态代理,动态代理有jdk动态代理和cglib动态代理。
  3. 适配器模式:将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
  4. 装饰者模式:动态给类加功能。
  5. 观察者模式:有时被称作发布/订阅模式,观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。
  6. 策略模式:定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。
    外观模式:为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
  7. 命令模式:将一个请求封装成一个对象,从而使您可以用不同的请求对客户进行参数化。
  8. 创建者模式:将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。
  9. 抽象工厂模式:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
  • 常用的设计模式汇总,超详细!
  • Java设计模式:23种设计模式全面解析(超级详细)

二、集合

List,Set,Map区别(扩容过程)

(1)List集合

存储特点:有序,可重复。存储顺序与添加顺序一致,可以存储重复的数据

  • ArrayList
    底层数据结构是数组,访问快,添加,删除慢
    初始化容量:10
    扩容:1.5倍
  • Vector
    底层数据结构是数组,它是线程安全的,ArrayList不是线程安全的
    初始化容量:10
    扩容:2倍
  • LinkedList
    底层是双向链表,访问慢,添加和删除效率高

(2)Set集合

存储特点:无序,不可重复。存储顺序与添加顺序可能不一样;不能存储重复(通过equals()方法来判断)的数据

  • HashSet
    • 底层是HashMap
    • HashSet添加元素就是把元素作为键添加到底层的HashMap
  • TreeSet
    • TreeSet实现了SortedSet接口,可以根据元素自然排序,要求集合中的元素必须是可比较的(ComparatorComparable
    • TreeSet底层是TreeMap
    • TreeSet添加元素就是把元素作为键添加到底层的TreeMap

(3)Map

Map是按<键,值>对的形式存储数据,Mapkey不允许重复

  • HashMap

    • 底层是数组+链表,键与值都可以为null,线程不安全
    • JDK8中,当链表长度超过 8 时,链表转换为红黑树;插入时,新增的结点插入到链表的尾部
    • 初始化容量为16
    • 加载因子:0.75,当键值对的数量 > 容量*加载因子时,按2倍大小扩容
    • 在创建时可以指定初始化容量,HashMap会把初始化容量自动调整为2的幂次方
    • 这边也可以引申到一个问题就是HashMap是先插入数据再进行扩容的,但是如果是刚刚初始化容器的时候是先扩容再插入数据。
  • CurrentHashMap

    • 底层采用数组+链表+红黑树,利用CAS+Synchronized来保证线程安全

    详见:
    深入浅出ConcurrentHashMap1.8
    HashMap和ConcurrentHashMap的知识总结

  • HashTable

    • 底层数组+链表,无论key还是value都不能为null,线程安全,实现线程安全的方式是在修改数据时锁住整个HashTable,效率低,ConcurrentHashMap做了相关优化
    • 初始容量为11,扩容:2倍+1

HashMap遍历怎么遍历?

两种方式遍历:使用EntrySet遍历;使用KeySet 遍历

平时什么情况下使用HashMap?

比如说读取系统参数,在服务之间传递一些键值对时使用。

简单说一下什么是hash碰撞?怎么解决的?

首先根据键的哈希码,经过hash函数计算hash值,然后根据hash值计算数组下标,当下标相同时,就会出现hash碰撞。

HashMap采用链表法,将数组下标相同的键值对用链表存起来,解决了hash碰撞。

TreeMap和LinkedHashMap有什么区别?

  • HashMap 相较来说写入慢,读取快,上面介绍过了,就不赘述了;
  • LinkedHashMap它内部有一个链表,保持Key插入的顺序,写入快,读取慢。迭代的时候,也是按照插入顺序迭代;
  • TreeMap可以根据Key的自然顺序(如整数从小到大)或者自己定义的比较器,实现 Key 的有序排列。

如何删除一些集合中满足条件的元素?

不能使用for或者foreach循环加list.remove()进行删除,因为使用remove方法删除后,集合的长度会变,导致一些元素被漏掉。

  1. 使用外部迭代器删除集合中的所有奇数:

    List<Integer> list = new ArrayList<>();
    
    Iterator<Integer> iterator = list.iterator();
    
    while (iterator.hasNext()) {
        Integer i = iterator.next();
        if (i % 2 == 1) {
            iterator.remove();
        }
    }
    
  2. 使用内部迭代器(流),删除集合中的所有奇数:

    List<Integer> list = new ArrayList<>();
    
    list = list.stream().
                filter(x -> x % 2 == 0).
                collect(Collectors.toList());
    

三、多线程

1. 创建线程有哪些方法(4种)?

  1. 继承Thread类,重写run方法(其实Thread类本身也实现了Runnable接口)
  2. 实现Runnable接口,重写run方法
  3. 实现Callable接口,重写call方法(有返回值)
  4. 使用线程池(有返回值)

2. Runnable和Callable有什么不同?

  1. 最大的不同点:实现Callable接口的任务线程能返回执行结果;而实现Runnable接口的任务线程不能返回结果;
  2. Callable接口的call()方法允许抛出异常;而Runnable接口的run()方法的异常只能在内部消化,不能继续上抛;

    Callable接口支持返回执行结果,此时需要调用FutureTask.get()方法实现,此方法会阻塞主线程直到获取结果;当不调用此方法时,主线程不会阻塞!

3. start()和run()方法有什么区别?

  • start()方法来启动线程,而启动后的线程运行的是run()方法中的代码。
  • run()方法当作普通方法的方式调用时,并不会开启新的线程。

4. 线程池有哪些参数

最常用的三个参数:

  • corePoolSize:核心线程数
  • queueCapacity:任务队列容量(阻塞队列)
  • maxPoolSize:最大线程数

三个参数的作用:

  1. 当线程数小于核心线程数时,创建线程。
  2. 当线程数大于等于核心线程数,且任务队列未满时,将任务放入任务队列。
  3. 当线程数大于等于核心线程数,且任务队列已满
    1. 若线程数小于最大线程数,创建线程
    2. 若线程数等于最大线程数,抛出异常,拒绝任务

5. 线程池有几种(5种)?拒绝策略有几种(4种)?阻塞队列有几种(3种)?

五种线程池:

ExecutorService threadPool = null;
threadPool = Executors.newCachedThreadPool();//有缓冲的线程池,线程数 JVM 控制
threadPool = Executors.newFixedThreadPool(3);//固定大小的线程池
threadPool = Executors.newScheduledThreadPool(2);//一个能实现定时、周期性任务的线程池
threadPool = Executors.newSingleThreadExecutor();//单线程的线程池,只有一个线程在工作
threadPool = new ThreadPoolExecutor();//默认线程池,可控制参数比较多   

四种拒绝策略:

RejectedExecutionHandler rejected = null;
rejected = new ThreadPoolExecutor.AbortPolicy();//默认,队列满了丢任务抛出异常
rejected = new ThreadPoolExecutor.DiscardPolicy();//队列满了丢任务不异常
rejected = new ThreadPoolExecutor.DiscardOldestPolicy();//将最早进入队列的任务删,之后再尝试加入队列
rejected = new ThreadPoolExecutor.CallerRunsPolicy();//如果添加到线程池失败,那么主线程会自己去执行该任务

三种阻塞队列:

BlockingQueue<Runnable> workQueue = null;
workQueue = new ArrayBlockingQueue<>(5);//基于数组的先进先出队列,有界
workQueue = new LinkedBlockingQueue<>();//基于链表的先进先出队列,无界
workQueue = new SynchronousQueue<>();//无缓冲的等待队列,无界

6. 如何检测死锁?

利用Java自带工具JConsole
Java线程死锁查看分析方法

7. volatile底层是怎么实现的?

当一个变量定义为volatile后,它将具备两种特性:1. 可见性,2. 禁止指令重排序。

可见性:编译器为了加快程序运行速度,对一些变量的写操作会现在寄存器或CPU缓存上进行,最后写入内存。而在这个过程中,变量的新值对其它线程是不可见的。当对volatile标记的变量进行修改时,先当前处理器缓存行的数据写回到系统内存,然后这个写回内存的操作会使其他CPU里缓存了该内存地址的数据无效。

处理器使用嗅探技术保证它的内部缓存、系统内存和其他处理器的缓存的数据在总线上保持一致。如果一个正在共享的状态的地址被嗅探到其他处理器打算写内存地址,那么正在嗅探的处理器将使它的缓存行无效,在下次访问相同内存地址时,强制执行缓存行填充。

8. volatile与synchronized有什么区别?

  1. volatile仅能使用在变量上,synchronized则可以使用在方法、类、同步代码块等等。
  2. volatile只能保证可见性和有序性,不能保证原子性。而synchronized都可以保证。
  3. volatile不会造成线程的阻塞,而synchronized可能会造成线程的阻塞.

9. java中的线程有几种状态(6种)

  • NEW:新建状态,创建了一个线程对象
  • RUNNABLE:可以运行状态,线程调用了start()方法。
  • BLOCKED:阻塞状态,遇到了阻塞事件,或者等待锁对象.
  • WAITING:等待状态。wait()join()
  • TIMED_WAITING:等待状态,sleep()
  • TERMINATED:终止状态

2020年三、四月java面试题汇总_第1张图片

更多多线程常见面试题及答案

四、MySql

sql优化

  1. sql优化主要是对查询进行优化,应尽量避免全表扫描,首先应考虑在 whereorder by 涉及的列上建立索引
  2. 任何地方都不要使用 select * from t ,用具体的字段列表代替“*”,不要返回用不到的任何字段。
  3. 避免在 where 子句中使用!=<>操作符,否则将引擎放弃使用索引而进行全表扫描,
  4. 应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描。可以在null上设置默认值,确保表中没有null值。
  5. 复合索引必须指定索引中第一个字段作为检索条件,否则不使用索引
  6. innot in 也要慎用,否则会导致全表扫描。对于连续的数值,能用 between 就不要用 in 了。
  7. 模糊查询时,以"%"开头,会导致全表扫描

更多sql优化的几种方法

索引

什么是索引

索引(Index)是帮助MySQL高效获取数据的数据结构,可以理解为一本字典的目录,提高程序的检索 和查询效率

索引的类型

  • 主键索引:数据列不允许重复,不允许为NULL,一个表只能有一个主键。

  • 唯一索引:数据列不允许重复,允许为NULL值,一个表允许多个列创建唯一索引。

    #创建唯一索引
    ALTER TABLE table_name ADD UNIQUE (column);
    #创建唯一组合索引
    ALTER TABLE table_name ADD UNIQUE (column1,column2);
    
  • 普通索引:基本的索引类型,没有唯一性的限制,允许为NULL值。

    #创建普通索引
    ALTER TABLE table_name ADD INDEX index_name (column);
    #创建组合索引
    ALTER TABLE table_name ADD INDEX index_name(column1, column2, column3);
    
  • 全文索引:是目前搜索引擎使用的一种关键技术。

    #创建全文索引
    ALTER TABLE table_name ADD FULLTEXT (column);
    

如何判断一条sql语句是否使用了索引(执行计划)

通过执行计划,可以判断本次查询中对于数据行定位是否是从索引获取的

EXPLAIN  查询语句 

对于查询的评级由好到坏:const > primary(主键) > ref > range > index > all

  • 平时进行SQL优化时,最低要求达到range级别,尽可能达到ref级别

MySQL 服务占用 cpu 100%,如何排查问题?

  1. 一般情况下,mysql占用过高多是有慢查询,打开慢查询日志,找到执行较慢的语句。另一种方法:也可以在mysql命令行中调用 show processlist;,查看当前 mysql 使用频繁的 sql 语句。
  2. 分析查询效率低的原因,一般是因为没有使用索引,要么sql优化,要么给字段加上索引。

事务

Mysql事务的隔离级别

事务隔离级别 说明 脏读 不可重复读 幻读
读未提交(read-uncommitted) 读取数据的事务对象,可以读取到另一个事务对象尚未提交的数据
不可重复读(read-committed) 读取数据的事务对象,只能读取另一个事务对象提交过后的数据
可重复读(repeatable-read)
(默认)
读取数据的事务对象,在另一个事务对象提交前后读取的内容要保持一致
串行化(serializable) 串行读取

什么是脏读?什么是幻读?

  • 脏读:事务A读取了事务B更新的数据,然后B回滚操作,那么A读取到的数据是脏数据

  • 不可重复读:事务 A 多次读取同一数据,事务 B 在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果 不一致。

  • 幻读:系统管理员A将数据库中所有学生的成绩从具体分数改为ABCDE等级,但是系统管理员B就在这个时候插入了一条具体分数的记录,当系统管理员A改结束后发现还有一条记录没有改过来,就好像发生了幻觉一样,这就叫幻读。事务1在两次查询的过程中,事务2对该表进行了插入、删除操作,从而事务1第二次查询的结果发生了变化。

总结:不可重复读的和幻读很容易混淆,不可重复读侧重于修改,幻读侧重于新增或删除。解决不可重复读的问题只需锁住满足条件的行,解决幻读需要锁表

存储引擎

存储引擎对比

  • MyIsam:

    • 使用三个文件表示每个表:

      • 格式文件 — 存储表的结构(mytable.frm)
      • 数据文件 — 存储表的数据(mytable.MYD)
      • 索引文件 — 存储表的索引(mytable.MYI)
    • 对表文件中数据行查询提速,有很大帮助。

    • 灵活的AUTO_INCREMENT字段处理

    • 可被转换为压缩、只读表来节省空间

    • 但是MyIsam不支持事务,因此在进行数据行添加,修改,删除时,无法保证数据安全性

  • INNODB:MySQL数据库的默认引擎;

    • 每个InnoDB表在数据库目录中以.frm格式文件表示
    • InnoDB表空间tablespace被用于存储表的内容
    • 提供一组用来记录事务性活动的日志文件
    • 支持事务处理
    • 提供全ACID兼容
    • MySQL服务器崩溃后提供自动恢复
    • 多版本(MVCC)和行级锁定
    • 支持外键及引用的完整性,包括级联更新和删除
  • MEMORY:其数据存储在内存中,且行的长度固定,这两个特点使得MEMORY存储引擎非常快;

    • 在数据库目录内,每个表均以.frm格式文件表示;
    • 表数据及索引被存储在内存中;
    • 表级锁机制;
    • 不能包含TEXT或BLOB字段;

如何选择合适的存储引擎

  1. MyISAM表最适合于大量的数据读而少量数据更新的混合操作。MyISAM表的另一种适用情形是使用压缩的中读表。
  2. 如果查询中包含较多的数据更新操作,应使用InnoDB。其行级锁机制和多版本的支持为数据读取和更新的混合提供了良好的并发机制。
  3. 可使用MEMORY存储引擎存储非永久需要的数据,或者是能够从基于磁盘的表中重新生成的数据。

Innodb有哪些特性?

  1. 插入缓冲
  2. 二次写
  3. 自适应哈希
  4. 预读

innodb引擎的4大特性

Innodb的事务锁有哪些?(4种)

行级锁

  • 共享锁(S Lock) : 允许事务读一行数据
  • 排它锁(X Lock) : 允许事务删除或更新一行数据

表级锁

  • 意向共享锁(IS Lock):事务想要获得一张表中某几行的共享锁
  • 意向排它锁(IX Lock):事务想要获得一张表中某几行的排它锁

由于Innodb引擎支持的均为行锁,所以意向锁其实不会阻塞除全表扫描之外的任何请求

数据库的乐观锁怎么实现的?

乐观锁:假设不会发生并发冲突,只在提交操作时检查是否违反数据完整性。

实现方式

  1. 在数据库中加入一个版本号字段,每次在执行数据的修改操作时,先查出相应的版本号,然后带上查出来的这个版本号去修改数据库,一旦该版本号和数据的版本号一致就可以执行修改操作并对版本号执行+1操作,否则就执行失败
  2. 上述方法中的版本号也可以用时间戳替换,时间戳不需要 +1 操作。

使用场景
乐观锁适用于写比较少的情况下(多读场景),即冲突真的很少发生的时候,这样可以省去了锁的开销,加大了系统的整个吞吐量。但如果是多写的情况,一般会经常产生冲突,这就会导致上层应用会不断的进行retry,这样反倒是降低了性能,所以一般多写的场景下用悲观锁就比较合适。

使用什么工具创建表?

使用Power Designer来创建表

表结构是怎么设计的?

  1. 首先,表结构的设计尽量要满足数据库三范式
  2. 但是还是根据实际需求进行取舍,有时可能会拿冗余换速度,最终用目的要满足客户需求。

第一范式:每一个字段是原子性不能再分;
第二范式:在第一范式基础之上,要求数据库中所有非主键字段完全依赖主键,不能产生部分依赖;
第三范式:在第二范式基础之上,要求非主键字段不能产生传递依赖于主键字段

数据库集群是怎么同步的?

2020年三、四月java面试题汇总_第2张图片

  1. 当 master 主服务器上的数据发生改变时,则将其改变写入二进制事件日志文件中
  2. salve 从服务器会在一定时间间隔内对 master 主服务器上的二进制日志进行探测,探测其是否发生过改变,如果探测到 master 主服务器的二进制事件日志发生了改变,则开始一个 I/O Thread 请求 master 二进制事件日志
  3. 同时 master 主服务器为每个 I/O Thread 启动一个dump Thread,用于向其发送二进制事件日志
  4. slave 从服务器将接收到的二进制事件日志保存至自己本地的中继日志文件中
  5. salve 从服务器将启动 SQL Thread 从中继日志中读取二进制日志,在本地重放,使得其数据和主服务器保持一致;
  6. 最后 I/O Thread 和 SQL Thread 将进入睡眠状态,等待下一次被唤醒

如何防止SQL注入?

SQL注入产生的原因,就是用户提交的数据,被数据库系统编译而产生了开发者预期之外的动作。也就是,SQL注入是用户输入的数据,在拼接SQL语句的过程中,超越了数据本身,成为了SQL语句查询逻辑的一部分,然后这样被拼接出来的SQL语句被数据库执行,产生了开发者预期之外的动作。

解决办法:

  1. 最简单的就是采用预编译语句集(PreparedStatement),在SQL语句中放置占位符’?’,在执行前,数据库会先对含有占位符的SQL语句进行校验和预编译;执行时才会把参数传递进去,防止参数参与编译变成sql语句的一部分。
  2. 采用正则表达式将包含有 单引号('),分号(;) 和 注释符号(--)的语句给替换掉来防止SQL注入

五、Redis

1. Redis有哪些优势?

  1. 单线程,利用redis队列技术并将访问变为串行访问,消除了传统数据库串行控制的开销

  2. 支持数据持久化,支持AOF和RDB两种持久化方式。

  3. 分布式 读写分离模式

  4. 支持丰富数据类型

  5. 支持事务,操作都是原子性,所谓原子性就是对数据的更改要么全部执行,要不全部不执行。

2. 有几种数据类型?你用到了哪些数据类型(5种)?在哪儿用到的?

String类型

  • 计数:由于Redis单线程的特点,我们不用考虑并发造成计数不准的问题,通过 incrby 命令,我们可以正确的得到我们想要的结果。
  • 限制次数:比如登录次数校验,错误超过三次5分钟内就不让登录了,每次登录设置key自增一次,并设置该key的过期时间为5分钟后,每次登录检查一下该key的值来进行限制登录。

Hash类型

  • 缓存一些信息

List类型

  • 可以用作栈,队列等等

Set类型

  • 利用集合的交并集特性,比如在社交领域,我们可以很方便的求出多个用户的共同好友,共同感兴趣的领域等。

Zset类型

  • 和set数据结构一样,zset也可以用于社交领域的相关业务,并且还可以利用zset 的有序特性,还可以做类似排行榜的业务。

3. redis的过期策略有哪些?

定期删除+惰性删除

定期删除redis 默认是每隔 100ms 就会扫描一定数量的数据库的expires字典中的key,检查其是否过期,如果过期就删除。定期删除可能会导致很多过期 key 到了时间并没有被删除掉,所以就使用惰性删除。

expires字典会保存所有设置了过期时间的key的过期时间数据,其中,key是指向键空间中的某个键的指针,value是该键的毫秒精度的UNIX时间戳表示的过期时间。键空间是指该Redis集群中保存的所有键。

惰性删除:获取 key 的时候,如果此时 key 已经过期,就删除,不会返回任何东西。

4. 持久化

(1)RDB

Redis Database(RDB),就是在指定的时间间隔内将内存中的数据集快照写入磁盘,数据恢复时将快照文件直接再读到内存。

RDB 保存了在某个时间点的数据集(全部数据)。存储在一个二进制文件中,只有一个文件。默认是 dump.rdb。 RDB 技术非常适合做备份,可以保存最近一个小时,一天,一个月的全部数据。保存数据是在单独的进程中写文件,不影响 Redis 的正常使用。 RDB 恢复数据时比其他 AOF 速度快。

  • 优点:由于存储的是数据快照文件,恢复数据很方便,也比较快
  • 缺点:
    • 会丢失最后一次快照以后更改的数据。 如果你的应用能容忍一定数据的丢失,那么使用 rdb 是不错的选择; 如果你不能容忍一定数据的丢失,使用 rdb 就不是一个很好的选择。
    • 由于需要经常操作磁盘, RDB 会分出一个子进程。如果你的 redis 数据库很大的话,子进程占用比较多的时间,并且可能会影响 Redis 暂停服务一段时间(millisecond 级别),如果你的数据库超级大并且你的服务器 CPU 比较弱,有可能是会达到一秒。

(2)AOF

Append-only File(AOF), Redis 每次接收到一条改变数据的命令时,它将把该命令写到一个 AOF 文件中(只记录写操作,读操作不记录),当 Redis 重启时,它通过执行 AOF 文件中所有的命令来恢复数据。

  • append-only 文件是另一个可以提供完全数据保障的方案;
  • AOF 文件会在操作过程中变得越来越大。比如,如果你做一百次加法计算,最后你只会在数据库里面得到最终的数值,但是在你的 AOF 里面会存在 100 次记录,其中 99 条记录对最终的结果是无用的;但 Redis 支持在不影响服务的前提下在后台重构 AOF 文件,让文件得以整理变小
  • 可以同时使用这两种方式,redis默认优先加载 aof文件(aof数据最完整);

5. Redis集群保证了CAP中的什么?

CAP原则又称CAP定理,指在一个分布式系统不可能同时满足 Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性)。由于分区容错性是分布式系统中必须要保证的,因此我们只能在A和C之间进行权衡

  • 一致性(C):在分布式系统中的所有数据备份,在同一时刻是否同样的值。(等同于所有节点访问同一份最新的数据副本)
  • 可用性(A):在集群中一部分节点故障后,集群整体是否还能响应客户端的读写请求。(对数据更新具备高可用性)
  • 分区容错性(P):以实际效果而言,分区相当于对通信的时限要求。系统如果不能在时限内达成数据一致性,就意味着发生了分区的情况,必须就当前操作在C和A之间做出选择。

Redis集群是通过牺牲一致性来保证后两者(AP)。无法保证强一致性(主从服务器始终保持数据一致),只能保证最终一致性(最终从服务器与主服务器数据一致)

6. Redis主从复制原理

  1. 当启动一个 从结点 的时候,它会发送一个 PSYNC 命令给 主结点

  2. 如果这是 从结点 初次连接到 主结点,那么会触发一次全量复制:此时 主结点 会启动一个后台线程,开始生成一份 RDB 快照文件,同时还会将从客户端新收到的所有写命令缓存在内存中。RDB 文件生成完毕后, 主结点 会将这个 RDB 发送给 从结点从结点 会先写入本地磁盘,然后再从本地磁盘加载到内存中,接着 主结点 会将内存中缓存的写命令发送到 从结点从结点 也会同步这些数据。

  3. 从结点 如果跟 主结点 有网络故障,断开了连接,会自动重连,连接之后 主结点 仅会复制给 从结点 部分缺少的数据。

7. Redis集群会有写操作丢失吗?为什么?

Redis并不能保证数据的强一致性,这意味这在实际中集群在特定的条件下可能会丢失写操作。

更多:面了BAT,我总结了他们会问的Redis基础知识
Redis面试题(2020最新版)
Redis持久化与集群 (Redis高频面试点: AOF,RDB,CAP,主从复制,哨兵,集群)

六、Linux

1. 什么是管道?

管道就是用“|”连接两个命令,将前面一个命令的输出作为后面命令的输入,用于把管道左边的输出作为右边的输入。

2. 查看cpu,内存占用的命令

Linux top命令详解
Linux 常用命令-- top

七、Spring

1. IoC

IoC 就是控制反转,是指创建对象的控制权的转移,以前创建对象的主动权和时机是由自己把控的,而现在这种权力转移到 Spring 容器中,并由容器根据配置文件去创建实例和管理各个实例之间的依赖关系,对象与对象之间松散耦合,也利于功能的复用。DI 依赖注入,和控制反转是同一个概念的不同角度的描述,即 应用程序在运行时依赖 IoC 容器来动态注入对象需要的外部资源。

2. Aop

AOP,一般称为面向切面,作为面向对象的一种补充,用于将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块,这个模块被命名为 “切面”(Aspect),减少系统中的重复代码,降低了模块间的耦合度,同时提高了系统的可维护性。可用于权限认证、日志、事务处理。AOP 实现的关键在于 代理模式.

3. 解释一下 AOP 里面的几个名词:

  1. 切面(Aspect):被抽取的公共模块(类),可能会横切多个对象。例如银行系统,先检测是否安全,再执行核心(业务)层,再记录日志,再清空缓存记录,除了核心层都是切面

  2. 连接点(Join point):指方法(可以被切面织入的具体方法),通常业务接口中的方法均为连接点。例如上面的银行系统中,核心类(目标业务方法)就是连接点

  3. 通知(Advice):Advice也叫增强。切面为类,而切面里的方法叫通知。有前置通知,后置通知,环绕通知等等。

  4. 切入点(Pointcut):切入点就是一个或多个连接点的集合。通过切入点表达式,来获取满足条件的连接点的集合。

  5. 目标对象(Target Object): 目标对象指被增强了的对象(包含主业务逻辑的类的对象),即被一个或者多个切面所通知的对象。这个对象永远是一个被代理(proxied) 对象。

  6. 织入(Weaving):指把增强应用到目标对象来创建新的代理对象的过程(将通知与连接点连接起来的过程)。

4. Aop在项目中用在什么地方?

Aop一般用来处理权限控制,日志记录,加载事务等非主要业务逻辑的事情。底层是采用cglib和jdk的动态代理实现的。

5. IoC和Aop有什么作用?

  • IoC 这个指的就是我们获取对象的方式进行反转了。不使用IoC时,对象是需要手动new出来的,是我们主动获取的。使用IoC之后,是将这个获取的过程交给spring来管理,我们只需要告诉spring你需要什么就行了,它就会把东西给你。
  • Aop 就是用来增强功能,在执行主业务的时可以顺带执行其他业务。

Sring 的AOP和IoC都是为了解决系统代码耦合度过高的问题。使代码重用度高、易于维护。

6. AspectJ的Aop和Spring的Aop有什么区别?

  1. Spring AOP采用的是动态织入(运行时织入),而Aspectj是静态织入(编译时期就织入),通过修改代码来实现。

  2. AspectJ由于是在编译时期就织入,故性能更优。

  3. Spring AOP的通知是基于该对象是SpringBean对象才可以,而AspectJ可以在任何Java对象上应用通知。

  4. Spring AOP仅支持方法切入点,而AspectJ支持所有切入点

7. Spring如何实现事务的控制?

事务就是对一系列的数据库操作(比如插入多条数据)进行统一的提交或回滚操作,如果插入成功,那么一起成功,如果中间有一条出现异常,那么回滚之前的所有操作。这样可以防止出现脏数据,防止数据库数据出现问题。开发中为了避免这种情况一般都会进行事务管理。

Spring 提供了声明式事务和编程式事务。

  • 声明式事务:通常是指在配置文件中对事务进行配置声明,其中包括了很多声明属性,它是通过AOP实现的,自己不用额外的写代码,只要在Spring配置文件中声明即可;通常用在数据库的操作里面;
  • 编程式事务:就是指通过硬编码的方式做事务处理,这种处理方式需要写代码,事务中的逻辑可以自己定制;可以是数据库的操作,也可以是其他的操作。

Spring中也有自己的事务管理机制,一般是使用TransactionMananger进行管理,可以通过Spring的注入来使用此功能。

Spring面试总结(全)

八、SpringMVC

1. SpringMVC执行流程

2020年三、四月java面试题汇总_第3张图片

  1. 浏览器提交请求到中央调度器
  2. 中央调度器直接将请求转给处理器映射器。
  3. 处理器映射器会根据请求,找到处理该请求的处理器,并将其封装为处理器执行链后返回给中央调度器。
  4. 中央调度器根据处理器执行链中的处理器,找到能够执行该处理器的处理器适配器。
  5. 处理器适配器调用执行处理器。
  6. 处理器将处理结果及要跳转的视图封装到一个对象 ModelAndView 中,并将其返回给处理器适配器。
  7. 处理器适配器直接将结果返回给中央调度器。
  8. 中央调度器调用视图解析器,将 ModelAndView中的视图名称封装为视图对象。
  9. 视图解析器将封装了的视图对象返回给中央调度器
  10. 中央调度器调用视图对象,让其自己进行渲染,即进行数据填充,成响应对象。
  11. 中央调度器响应浏览器。

2. @RequestParam和@PathVariable

  • @RequestParam:将请求参数绑定到控制器的方法参数上,同时可以给参数一些限定,比如说指定参数的默认值,是否允许参数为空等等。
  • @PathVariable:是将Rest风格的请求中{}中的值取出来

3. @ResponseBody和@RequestBody

  • @ResponseBody:作用在方法上,表示该方法的返回结果直接写入响应体中,如果不使用@ResponseBody,则返回值会解析为跳转路径。
  • @RequestBody:作用在形参列表上,用于将前台发送过来固定格式的数据【xml 格式或者 json等】封装为对应的 JavaBean 对象,封装时使用到的一个对象是系统默认配置的 HttpMessageConverter进行解析,然后封装到形参上。

4. 如何拦截请求?

  1. 使用过滤器方式拦截请求(实现javax.servlet.Filter接口)
  2. 使用SpringMVC提供的拦截器(实现HandlerInterceptor接口)
  3. 利用Spring的切面(AOP)实现拦截器。

九、MyBatis

1. #{}和${}都用过吗?有什么区别?

#{}是预编译处理,${}是字符串替换

  • #{}Mybatis在处理#{}时,会将sql中的#{}替换为?号,对sql语句预编译后,再调用PreparedStatementset方法来赋值占位符,告诉 mybatis 使用实际的参数值代替。可以有效的防止SQL注入,提高系统安全性

  • ${}:字符串替换, 告诉 mybatis 使用$包含的“字符串”替换所在位置。使用的是 Statement 对象,有SQL注入的风险。

2. 模糊查询怎么使用?

<select id="selectLikeTwo" resultType="com.bjpowernode.domain.Student">
    select * from student where name like "%" #{name} "%"
select>

3. 动态查询的sql怎么写?

MyBatis学习记录(3)之动态SQL

4. MyBatis的缓存机制

MyBatis提供查询缓存,用于减轻数据库压力,提高数据库性能,MyBatis提供了一级缓存和二级缓存

  • 一级缓存:SqlSession级别的缓存

    在操作数据库时,需要构造SqlSession对象,在对象中有一个数据结构(HashMap)用于存储缓存数据

    不同的SqlSession之间的缓存区域是互相不影响的。

  • 二级缓存:mapper级别的缓存

    多个SqlSession去操作同一个mapper的sql语句,多个SqlSession可以共用二级缓存,所得到的数据会存在二级缓存区域,二级缓存是跨SqlSession

    二级缓存默认是没有开启的。需要在setting全局参数中配置开启二级缓存

二级缓存的具体流程:

  1. 当一个SqlSession执行了一次select后,在关闭此session的时候,会将查询结果缓存到二级缓存

  2. 当另一个SqlSession执行select时,首先会在他自己的一级缓存中找,如果没找到,就回去二级缓存中找,找到了就返回,就不用去数据库了,从而减少了数据库压力提高了性能

  3. 如果SqlSession执行了DML操作(insert、update、delete),并commit了,那么mybatis就会清空当前mapper缓存中的所有缓存数据,这样可以保证缓存中的存的数据永远和数据库中一致,避免出现脏读

十、SpringBoot

1. SpringBoot配置文件加载顺序

在 Spring Boot 里面,可以使用以下几种方式来加载配置,他们的加载优先级从高到低为:

  1. properties文件;
  2. YAML文件;
  3. 系统环境变量;
  4. 命令行参数;

不同目录下的配置文件(properties文件与yaml文件)优先级从高到低顺序:(高优先级配置会覆盖低优先级配置,多个配置文件互补

  1. file:./config/ - 优先级最高(项目根路径下的config
  2. file:./ - 优先级第二 -(项目根路径下
  3. classpath:/config/ - 优先级第三(项目resources/config下
  4. classpath:/ - 优先级第四(项目resources根目录

2. SpringBoot的核心注解?由哪些子注解组成?

启动类上面的注解是@SpringBootApplication,它也是 Spring Boot 的核心注解,主要组合包含了以下 3 个注解:

  • @SpringBootConfiguration:组合了 @Configuration 注解,实现配置文件的功能。

  • @EnableAutoConfiguration:打开自动配置的功能

  • @ComponentScan:Spring组件扫描。

3. SpringBoot的自动配置

注解 @EnableAutoConfiguration@Configuration@ConditionalOnClass 就是自动配置的核心。

@EnableAutoConfiguration 给容器导入META-INF/spring.factories 里定义的自动配置类,并根据一定的条件筛选有效的自动配置类,然后每一个自动配置类结合对应的 xxxProperties.java 读取配置文件进行自动配置功能

Spring Boot面试杀手锏——自动配置原理

4. Spring Boot Starter

Starters可以理解为启动器,它包含了一系列可以集成到应用里面的依赖包,你可以一站式集成Spring及其他技术,而不需要到处找示例代码和依赖包。例如,你想使用Spring JPA访问数据库,只要加入springboot-starter-data-jpa启动器依赖就能使用了。Starters包含了许多项目中需要用到的依赖,它们能快速持续的运行,都是一系列得到支持的管理传递性依赖。

Starter简单来讲就是引入了一些相关依赖和一些初始化的配置。然后通过自动配置,完成这些bean的自动配置。

Spring Boot面试题(2020最新版)

十一、RabbitMQ

RabbitMQ的组成

2020年三、四月java面试题汇总_第4张图片

  1. Publisher:消息的生产者,也是一个向交换器发布消息的客户端应用程序。
  2. Broker:表示消息队列服务器实体(就是个服务器)。
  3. Virtual Host:虚拟主机,表示一批交换器、消息队列和相关对象。虚拟主机是共享相同的身份认证和加密环境的独立服务器域。每个 vhost 本质上就是一个 mini 版的 RabbitMQ 服务器,拥有自己的队列、交换器、绑定和权限机制。vhost 是 AMQP 概念的基础,必须在连接时指定,RabbitMQ 默认的 vhost 是 /
  4. Exchange:交换器,用来接收生产者发送的消息并将这些消息路由给服务器中的队列。
  5. Binding:绑定,用于消息队列和交换器之间的关联。一个绑定就是基于路由键将交换器和消息队列连接起来的路由规则,所以可以将交换器理解成一个由绑定构成的路由表。
  6. Queue:消息队列,用来保存消息直到发送给消费者。它是消息的容器,也是消息的终点。一个消息可投入一个或多个队列。消息一直在队列里面,等待消费者连接到这个队列将其取走。
  7. Connection:网络连接,比如一个TCP连接。
  8. Channel:信道,多路复用连接中的一条独立的双向数据流通道。信道是建立在真实的TCP连接内地虚拟连接,AMQP 命令都是通过信道发出去的,不管是发布消息、订阅队列还是接收消息,这些动作都是通过信道完成。因为对于操作系统来说建立和销毁 TCP 都是非常昂贵的开销,所以引入了信道的概念,以复用一条 TCP 连接。
  9. Consumer:消息的消费者,表示一个从消息队列中取得消息的客户端应用程序。

RabbitMQ使用场景

  1. 解耦:A 系统发送数据到 BCD 三个系统,通过接口调用发送。如果 E 系统也要这个数据呢?那如果C 系统现在不需要了呢?A 系统负责人几乎崩溃…A 系统跟其它各种乱七八糟的系统严重耦合,A 系统产生一条比较关键的数据,很多系统都需要 A 系统将这个数据发送过来。如果使用 MQ,A 系统产生一条数据,发送到 MQ 里面去,哪个系统需要数据自己去 MQ 里面消费。如果新系统需要数据,直接从MQ 里消费即可;如果某个系统不需要这条数据了,就取消对 MQ 消息的消费即可。这样下来,A 系统压根儿不需要去考虑要给谁发送数据,不需要维护这个代码,也不需要考虑人家是否调用成功、失败超时等情况。就是一个系统或者一个模块,调用了多个系统或者模块,互相之间的调用很复杂,维护起来很麻烦。但
    是其实这个调用是不需要直接同步调用接口的,如果用 MQ 给它异步化解耦。
  2. 异步:A 系统接收一个请求,需要在自己本地写库,还需要在 BCD 三个系统写库,自己本地写库要 3ms,BCD 三个系统分别写库要 300ms、450ms、200ms。最终请求总延时是 3 + 300 + 450 +200 = 953ms,接近 1s,用户感觉搞个什么东西,慢死了慢死了。用户通过浏览器发起请求。如果使用MQ,那么 A 系统连续发送 3 条消息到 MQ 队列中,假如耗时 5ms,A 系统从接受一个请求到返回响应给用户,总时长是 3 + 5 = 8ms。
  3. 削峰:减少高峰时期对服务器压力

如何保证消息必达?

数据丢失可能有下面几种情况:生产者丢失消息、消息列表丢失消息、消费者丢失消息;

  1. 生产者丢失消息:从生产者弄丢数据这个角度来看,RabbitMQ提供transactionconfirm模式来确保生产者不丢消息;

    transaction机制:同步,发送消息前,开启事务(channel.txSelect()),然后发送消息,如果发送过程中出现什么异常,事务就会回滚(channel.txRollback()),如果发送成功则提交事务(channel.txCommit())。然而,这种方式有个缺点:吞吐量下降;

    confirm模式(推荐):异步,一旦channel进入confirm模式,所有在该信道上发布的消息都将会被指派一个唯一的ID(从1开始),一旦消息被投递到所有匹配的队列之后;rabbitMQ就会发送一个ACK给生产者(包含消息的唯一ID),这就使得生产者知道消息已经正确到达目的队列了;如果rabbitMQ没能处理该消息,则会发送一个Nack消息给你,你可以进行重试操作。

  2. 消息队列丢数据:消息持久化。

处理消息队列丢数据的情况,一般是开启持久化磁盘的配置。

这个持久化配置可以和confirm机制配合使用,你可以在消息持久化磁盘后,再给生产者发送一个Ack信号。这样,如果消息持久化磁盘之前,rabbitMQ阵亡了,那么生产者收不到Ack信号,生产者会自动重发。

  1. 消费者丢失消息:关闭RabbitMQ的自动消息确认,使用手动确认消息

在代码中成功处理完消息后,使用手动回复确认消息,避免消费者丢失消息。

如何保证消息不被重复消费?

保证消息的唯一性,就算是多次传输,不要让消息的多次消费带来影响;比如:在写入消息队列的数据做唯一标示,消费消息时,根据唯一标识判断是否消费过;

假设你有个系统,消费一条消息就往数据库里插入一条数据,要是你一个消息重复两次,你不就插入了两条,这数据不就错了?但是你要是消费到第二次的时候,自己判断一下是否已经消费过了,若是就直接扔了,这样不就保留了一条数据,从而保证了数据的正确性。

更多:
面试官问我:什么是消息队列?什么场景需要他?用了会出现什么问题?
消息中间件MQ与RabbitMQ面试题(2020最新版)

十二、web编程

简单说一下HTTP

【HTTP协议】—HTTP协议详解

简单说一下http请求四次挥手的过程?

http三次握手四次挥手详解

什么是Cookie,什么是Session,有什么区别?

SessionCookie功能效果相同,都是记录一系列状态。

SessionCookie的区别在于:

  • Session是记录在服务端的内存中,而Cookie是记录在客户端的浏览器内存,硬盘中。
  • Cookie中数据名和数据,只能String,而Session中数据名必须是String,但是数据可以是Object类型

Session的工作原理

HTTP协议是非连接性的,取完当前浏览器的内容,然后关闭浏览器后,链接就断开了,而没有任何机制去记录取出后的信息。而当需要访问同一个网站的另外一个页面时(就好比如在第一个页面选择购买的商品后,跳转到第二个页面去进行付款)这个时候取出来的信息,就读不出来了。所以必须要有一种机制让页面知道原理页面的session内容。

Tomcat在创建一个Session对象时,自动为这个session对象提供一个唯一编号。开发人员将这个唯一编号称为【sessionId】。Tomcatsessionid保存到一个Cookie对象,在响应时将cookie写入到响应头交给当前客户的浏览器上。浏览器收到sessionId之后将其保存在浏览器内存中。

等到用户再次通过浏览器发起请求时,其拥有sessionId作为Cookie写入到请求头发送回服务端。此时Tomcat接收到命令之后,就可以根据用户请求协议包中请求头是否拥有sessionId,来判断用户在服务端是否存在Session

如果客户端Cookie被禁用,Session还能使用吗?

使用URL重写,就是通过发送请求时加入sessionId的参数

Tomcat是如何配置内存的?

  1. Windows下,在文件/bin/catalina.bat,Linux下,在文件/bin/catalina.sh的前面,增加如下设置:

    # 配置JAVA_OPTS,后面跟需要配置的参数,变量名必须是JAVA_OPS,例如:
    JAVA_OPTS='-Xms256m -Xmx512m'
    # 表示初始化内存为256MB,可以使用的最大内存为512MB。
    
  2. 环境变量中进行配置,例如(变量名必须是JAVA_OPS):
    变量名:JAVA_OPTS
    变量值:-Xms512m -Xmx512m

防止表单重复提交的方法有哪些?

  1. 通过JavaScript屏蔽提交按钮(不推荐):

    通过js代码,当用户点击提交按钮后,屏蔽提交按钮使用户无法点击提交按钮或点击无效,从而实现防止表单重复提交。但是如果用户通过刷新页面方式,或使用postman等工具绕过前段页面仍能重复提交表单。因此不推荐此方法。

  2. 给数据库增加唯一键约束(会增加数据库的负载):

    在数据库建表的时候在ID字段添加主键约束,用户名、邮箱、电话等字段加唯一性约束。确保数据库只可以添加一条数据。

  3. 利用Session防止表单重复提交(推荐):

    在服务器端响应表单页面之前,先生成一个唯一的标识符,将它存入session,同时将它写入表单的隐藏字段中,然后将表单页面发给浏览器,用户录入信息后点击提交,在服务器端,获取表单中隐藏字段的值,与session中的唯一标识符比较,相等说明是首次提交,就处理本次请求,然后将session中的唯一标识符移除;不相等说明是重复提交,就不再处理。

什么情况下会出现跨域问题,如何解决?

跨域问题的产生原因和相应的解决方式

十三、其它

1. maven配置文件有哪些主要的元素?

  1. 如果maven项目有继承关系(Spring Boot),用标签指明父项目gav坐标。
  2. 当前项目的gav坐标(//
  3. 当前项目产生的构件类型(
  4. 自定义一些变量,通过${}来引用(
  5. 依赖(
  6. 资源路径列表(
  7. 插件(

Maven配置文件POM属性最全详解

自我介绍+项目介绍(你用了那些技术,项目用了哪些技术)

还有什么想问的

  1. 工作地点
  2. 岗位职责
  3. 多久回复

你的期望薪资是多少?

你的离职原因是什么?

如果对方是大公司,你是小公司,你可以说,在小公司待的时间长了,想到大公司见识一下。

如果对方是小公司,你是大公司,你可以说,在大公司待的时间长了,职业天花板就在眼前,想在小公司试一下,有没有发挥自己才华的空间。

如果对方是外包公司,你可以说,自己还年轻,想进外包公司锻炼一下,多积累项目经验,提升自己的技术能力

如果对方不是外包公司,你就可以说上家公司是外包公司,每天都把自己外派到别的公司,感觉派来派去,没有归属感

如果对方是创业公司,你可以说自己想要更有挑战性的工作。

如果对方是国企,你可以说自己想有一份稳定一些的工作。

如果对方是外企,你可以说自己想要学习和感受一下国外的氛围。

  • 面试之你为什么从上家公司离职
  • 面试时被问及离职原因该怎么回答?

你可能感兴趣的:(总结)