一、java基础

1.1this和super的区别

  1. this指当前类对象,用在此类中调用当前类对象的属性和方法。并且可以调用构造函数,但是只能在构造函数中,只能写在代码的第一行。
  2. super指父类的类对象,用在子类中来调用父类的属性和方法,并且可以用来调用父类的构造方法,但是必须写在代码的第一行。

1.2抽象类和接口的区别

  1. 抽象类:被abstract关键字修饰的类成为抽象类,抽象类中包含抽象方法,也包含非抽象方法。
  2. 接口:被interface关键字修饰的类成为接口,接口中只能存在抽象方法。
  3. 二者的区别:
  4. 1)抽象类中的变量包含publicprivateprotect,接口中的变量只能是final类型的;
  5. 2)抽象类中的方法可以是抽象的,也可以是非抽象的,接口中的方法只能是抽象的;
  6. 3)抽象类中的方法可以被子类重写,也可以不被重写,接口中的方法都要被实现类重写;
  7. 4)一个类只能去继承一个父类,但能实现多个接口;

1.3多态的三种形式

  1. 多态体现了现实事物中所具备的多种形态,在java中代表同一个对象既属于子类也属于父类,指代父类引用指向子类对象。
  2. 多态的三个必要条件:
  3. 1)必须存在继承关系;
  4. 2)必须有方法的重写;
  5. 3)父类的引用指向子类
  6. 多态的三种形式:
  1. 1 普通类多态定义的格式
  2. 父类 变量名 = new 子类();
  3. class Fu {}
  4. class Zi extends Fu {}
  5. //类的多态使用
  6. Fu f = new Zi();
  7. 2 抽象类多态定义的格式
  8. abstract class Fu {
  9. public abstract void method();
  10. }
  11. class Zi extends Fu {
  12. public void method(){
  13. System.out.println(“重写父类抽象方法”);
  14. }
  15. }
  16. //类的多态使用
  17. Fu fu= new Zi();
  18. 3、接口多态定义的格式
  19. interface Fu {
  20. public abstract void method();
  21. }
  22. class Zi implements Fu {
  23. public void method(){
  24. System.out.println(“重写接口抽象方法”);
  25. }
  26. }
  27. //接口的多态使用
  28. Fu fu = new Zi();
  29. ————————————————
  30. 版权声明:本文为CSDN博主「好吃不过炸酱面」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
  31. 原文链接:https://blog.csdn.net/lingang1991/article/details/69905944

1.4static的用法和区别

用法:可以用来修饰类、变量、方法、代码块。

区别:用来修饰类的时候该类是静态内部类;

  1. 用来修饰变量的时候,该变量可以被可以被所有的实例化对象所共享;
  2. 用来修饰方法的时候,该方法只能用类名来调用;
  3. 用来修饰代码块的时候,该代码块被称为静态代码块,静态代码块在整个类加载的时候只会执行一次;

1.5final的用法和作用

用法:final可以用来修饰类、方法、成员变量、普通变量、引用

作用:用来修饰类的时候此类不可以被继承;

  1. 用来修饰方法的时候此方法不可以被重写;
  2. 用来修饰成员变量的时候此成员变量必须初始化,并且一旦赋值便不可再改变;
  3. 用来修饰局部变量的时候,可以不初始化,但是一旦进行赋值便也不可再改变;
  4. 用来修饰引用的时候,表示引用不可改变,但是引用指向的内容可以改变;

1.6ArrayList的初始容量和扩容过程

使用无参构造创建一个arrayList对象的时候底层会创建一个初始容量是0的数组,当使用add方法的时候会将数组的容量扩到10,当添加的元素超过10的时候会创建一个新的数组,容量是前一个数组的1.5倍,并把原来的数组复制到新的数组中。

1.7HashMap的底层结构

HashMap是一种依据数组+单向链表+红黑树实现存在的键值对的数据结构。底层结构是数组。对于要存的键值对,先通过Hash函数把键作为参数计算出相应的值,再根据计算出来的值指定数据存储的位置。当通过Hash函数计算出来的值有冲突时,先对键通过equal进行比较,相同时进行覆盖,不同则添加到链表上,链表如果过长效率就会大大降低,在jdk1.8中,当链表的长度超过8是,就会转为红黑树,效率就会得到大大的提升。

1.8HashMap的头插法和尾插法的区别?

头插法是JDK1.7时使用的插入方法,头插法时操作速度时最快的,每次插入时,头结点都会发生改变位最新,因为底层使用的是单链表的纵向延伸,但是头插法在多线程下回忆起链表的死循环。
尾插法是JDK1.8是使用的插入方法,1.8使用的红黑树采用的尾插法,插入时插入到链表的最后一节点之后,当链表的长度大于8时就会转换成红黑树,链表的长度不会大于8,能够有效的避免出现逆序和链表死循环的问题。

1.9ConCurrentHashMap的底层实现原理?

在JDK1.7版本中,ConcurrentHashMap的数据结构是由一个Segment数组和多个HashEntry组成,Segment数组的意义就是将一个大的table分割成多个小的table来进行加锁,也就是锁分离技术,而每一个Segment元素存储的是HashEntry数组+链表,这个和HashMap的数据存储结构一样。
JDK1.8的实现已经摒弃了Segment的概念,而是直接用Node数组+链表+红黑树的数据结构来实现

1.10请写出4种创建线程的方式

a.实现Runnable接口,重写里面的run方法。由于Runnable是函数式接口,所以可以使用lamda表达式。
b. 创建一个子类实现Callable接口,重写里面的call方法;
使用FutureTask 接收 Callable接口的实现类; 通过Thread将线程启动 通过FutureTask 中的get方法,获取子线程的返回值
c.继承Thread 重写run方法
d.通过线程池创建子线程

  1. public class Thread4 {
  2. public static void main(String[] args) {
  3. ExecutorService executorService = new ThreadPoolExecutor(
  4. // 核心线程数
  5. 3,
  6. // 最大线程数
  7. 5,
  8. // 空闲线程的等待时间
  9. 1L,
  10. // 时间单位
  11. TimeUnit.SECONDS,
  12. // 等待队列
  13. new ArrayBlockingQueue<>(3),
  14. // 线程工厂,用来创建线程
  15. Executors.defaultThreadFactory(),
  16. // 拒绝策略
  17. new ThreadPoolExecutor.AbortPolicy());
  18. for (int i=0;i<20;i++){
  19. executorService.execute(()->{
  20. System.out.println(Thread.currentThread().getName()+"===>正在执行");
  21. });
  22. }
  23. executorService.shutdown();
  24. }
  25. }

1.11聊聊线程的生命周期

新建状态(New):至今尚未启动的线程的状态。线程刚被创建,但尚未启动。如:Thread t = new MyThread();
就绪状态(Runnable):当调用线程对象的start()方法(t.start();),线程即进入就绪状态。处于就绪状态的线程,只是说明此线程已经做好了准备,随时等待CPU调度执行,并不是说执行了t.start()此线程立即就会执行;
运行状态(Running):当CPU开始调度处于就绪状态的线程时,此时线程才得以真正执行,即进入到运行状态。注:就 绪状态是进入到运行状态的唯一入口,也就是说,线程要想进入运行状态执行,首先必须处于就绪状态中;
阻塞状态(Blocked):处于运行状态中的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,此时进入阻塞状态,直到其进入到就绪状态,才有机会再次被CPU调用以进入到运行状态。
死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

1.13谈谈sleep和wait的区别

sleep是Thread类的方法,wait是Object类的方法;
从使用角度看sleep可以在任何地方使用,而wait只能在同步方法或者同步块中使用;
从CPU及资源锁释放方面来看,sleep,wait调用后都会暂停当前线程并让出cpu的执行时间,但不同的是sleep不会释放当前持有的对象的锁资源,到时间后会继续执行,而wait会放弃所有锁并需要notify/notifyAll后重新获取到对象锁资源后才能继续执行。

二、常用框架

2.1 谈谈对微服务的理解

微服务是一种分布式系统解决方案,把复杂的系统设计与开发的问题拆分为几个简单功能的子系统,把一个大的项目拆分为几个子模块,每个模块导入各自需要的依赖,每个项目有自己的项目名,有自己的端口号,可以独自运行,被浏览器访问。

2.2 请写出Nacos的作用

服务的注册发现
作为配置中心

2.3 请写出gateway的好处和作用

请求的接入,一种有效的统一的API路由管理方式

2.4 SpingCloud框架中的各种O

VO(View Object):视图对象,用于展示层,它的作用是把某个指定页面(或组件)的所有数据封装起来。
DTO(Data Transfer Object):数据传输对象,这个概念来源于J2EE的设计模式,原来的目的是为了EJB的分布式应用提供粗粒度的数据实体,以减少分布式调用的次数,从而提高分布式调用的性能和降低网络负载,但在这里,我泛指用于展示层与服务层之间的数据传输对象。
DO(Domain Object):领域对象,就是从现实世界中抽象出来的有形或无形的业务实体。
PO(Persistent Object):持久化对象,它跟持久层(通常是关系型数据库)的数据结构形成一一对应的映射关系,如果持久层是关系型数据库,那么,数据表中的每个字段(或若干个)就对应PO的一个(或若干个)属性。

三、数据库

3.1请谈谈sql优化

sql优化需要先开启慢查询日志对sql语句的等级进行判定,需要在逻辑上以及硬件上对sql进行优化。
sql优化需要遵从三点:
最大化利用索引,尽量避免全表扫描,减少无效数据的查询。

3.2事务的三种注入方式

1、编码式,直接创建一个事务对象。
2、通过AOP进行注入
3、通过注解的方式

四、Redis

JVM

内存模型(1.7 和1.8),每个区域做什么

程序计数器(线程私有):
是当前线程锁执行字节码的行号治时期,每条线程都有一个独立的程序计数器,这类内存也称为“线程私有”的内存。正在执行java方法的话,计数器记录的是虚拟机字节码指令的地址(当前指令的地址)。如果是Natice方法,则为空。

java 虚拟机栈
也是线程私有的。
每个方法在执行的时候也会创建一个栈帧,存储了局部变量,操作数,动态链接,方法返回地址。
每个方法从调用到执行完毕,对应一个栈帧在虚拟机栈中的入栈和出栈。
通常所说的栈,一般是指在虚拟机栈中的局部变量部分。
局部变量所需内存在编译期间完成分配,如果线程请求的栈深度大于虚拟机所允许的深度,则StackOverflowError。
如果虚拟机栈可以动态扩展,扩展到无法申请足够的内存,则OutOfMemoryError。
本地方法栈(线程私有)
和虚拟机栈类似,主要为虚拟机使用到的Native方法服务。也会抛出StackOverflowError 和OutOfMemoryError。

Java堆(线程共享)
被所有线程共享的一块内存区域,在虚拟机启动的时候创建,用于存放对象实例。
对可以按照可扩展来实现(通过-Xmx 和-Xms 来控制)
当队中没有内存可分配给实例,也无法再扩展时,则抛出OutOfMemoryError异常。
方法区(线程共享)
被所有方法线程共享的一块内存区域。
用于存储已经被虚拟机加载的类信息,常量,静态变量等。
这个区域的内存回收目标主要针对常量池的回收和堆类型的卸载。

堆内存组成(新生代和老年代)

类的加载过程

类加载器

双亲委派模型?如何打破?

内存溢出和内存泄漏

JVM相关参数-调优

JVM相关的命令-调优