java后端工程师八股文合集

1、 SQL调优的基本步骤如下:

  1. 确认性能瓶颈:首先要确定数据库中哪些查询是慢的,哪些查询最需要优化。可以通过监控数据库的CPU、磁盘I/O、网络I/O、缓存等指标来确定性能瓶颈。

  2. 优化查询语句:如果查询语句本身存在问题,例如使用了不必要的子查询、重复的连接操作等,就需要对查询语句进行优化。

  3. 优化索引:索引是提高查询性能的关键因素之一。可以通过创建、修改、删除索引来优化查询性能。

  4. 优化数据结构:如果数据库中的表结构存在问题,例如表设计不合理、字段类型选择不当等,就需要对数据结构进行优化。

  5. 优化系统参数:系统参数对数据库性能也有很大的影响,可以通过调整系统参数来优化数据库性能。

  6. 测试优化效果:对于每一次优化操作,都需要进行测试验证,以确保优化操作能够真正提高数据库的性能。

spring cloud

Spring Cloud是一个构建分布式系统的框架,它为开发人员提供了一系列的组件和工具,用于快速搭建云原生应用、微服务和微服务架构。

以下是一些常用的Spring Cloud组件:

  1. Spring Cloud Netflix:Netflix是Spring Cloud中最重要的组件之一,它包括多个子项目,如Eureka、Hystrix、Zuul等。Eureka是一种服务注册和发现组件,Hystrix是一种容错和延迟处理工具,Zuul是一种网关组件,用于处理请求路由、过滤等。

  2. Spring Cloud Config:Config是一种分布式配置管理工具,它提供了集中式的配置管理服务,可以将应用的配置信息存储在远程仓库中,并将其分发到不同的应用中。

  3. Spring Cloud Stream:Stream是一种消息驱动的微服务框架,它提供了一套简单的编程模型,用于构建可扩展的事件驱动的微服务。

  4. Spring Cloud Data Flow:Data Flow是一种数据集成和工作流编排工具,它提供了一种可视化的方式来组合和编排各种批处理和实时数据处理任务。

  5. Spring Cloud Security:Security是一种安全框架,用于处理各种安全问题,例如身份验证、授权等。

  6. Spring Cloud Sleuth:Sleuth是一种分布式跟踪工具,它用于跟踪和记录微服务之间的请求和响应。

除了上述组件外,Spring Cloud还有很多其他的组件,如Spring Cloud Bus、Spring Cloud Consul、Spring Cloud Kubernetes等。开发人员可以根据自己的需求选择适合的组件来构建分布式系统。

基本功

  • 面向对象的特征

1. 封装(Encapsulation):封装是一种将数据和方法组合在一起的机制,通过这种机制可以限制外部访问对象的内部数据和方法。封装可以保护数据的安全性,防止非法访问和修改数据。
2. 继承(Inheritance):继承是一种通过现有类创建新类的机制,新类可以继承现有类的属性和方法,并可以添加自己的属性和方法。继承可以实现代码的重用,减少代码的重复。
3. 多态(Polymorphism):多态是一种对象的属性,它指的是同一个方法在不同的对象上具有不同的实现方式。多态可以使代码更加灵活,增强代码的可扩展性和可维护性。
4. 抽象(Abstraction):抽象是一种将对象的共同特征提取出来形成类的机制,通过抽象可以将对象的共性和个性分离,从而实现代码的复用性和可扩展性。
  • final, finally, finalize 的区别

final关键字:final可以用来修饰类、方法和变量。当用final修饰类时,表示这个类不能被继承;当用final修饰方法时,表示这个方法不能被重写;当用final修饰变量时,表示这个变量的值不能被修改。final关键字主要用于声明不变量,以提高程序的健壮性和安全性。
​
finally关键字:finally是一个Java中的关键字,它通常与try-catch语句一起使用。finally块中的代码会在try或catch块中的代码执行完毕后执行,无论是否抛出异常。finally块通常用来释放资源或进行一些清理操作。
​
finalize方法:finalize是Object类中的一个方法,它是Java虚拟机用来回收对象的一种机制。当一个对象不再被引用时,Java虚拟机会自动调用该对象的finalize方法进行垃圾回收。在finalize方法中,可以进行对象的清理和资源的释放等操作。但是,由于finalize方法的调用时机不确定,所以不建议在该方法中进行重要的操作,也不建议过多地依赖该方法来进行资源的释放。
  • int 和 Integer 有什么区别

int和Integer是Java中两种不同的数据类型,它们的主要区别如下:
​
int是Java的一种基本数据类型,而Integer是Java中的一个类,它是int类型的包装类。
int在内存中占用4个字节,表示范围是-2^31 ~ 2^31-1;而Integer类是一个对象,它在内存中占用8个字节(64位JVM),并且还要存储一些额外的信息,因此比int类型占用更多的内存空间。
int类型的变量可以直接进行数值运算,而Integer类型的变量需要先进行自动拆箱(即将Integer类型转换为int类型),再进行数值运算。
int类型的变量可以直接进行比较,而Integer类型的变量需要通过equals方法进行比较。
int类型的变量可以直接赋值,而Integer类型的变量需要通过new关键字进行创建对象。
总之,int是一种基本数据类型,它的操作速度更快、占用内存更小;而Integer是一种对象类型,它可以使用面向对象的操作和方法,但是由于要存储一些额外的信息,因此占用的内存空间相对更大。在实际开发中,应该根据具体的需求选择使用哪种类型。
  • 重载和重写的区别

重载指在同一个类中定义两个或以上的方法,它们具有相同的名称,但参数列表不同(包括参数个数、类型、顺序),重载方法的返回值可以不同。
​
重写指在子类中重新定义一个与父类具有相同名称、相同参数列表、相同返回类型的方法。重写方法必须与父类方法具有相同的方法签名,即方法名称、参数列表和返回类型必须相同。
​
重载和重写的区别主要有:
​
方法名相同,参数列表不同,即重载是两个或以上的方法名称相同,但参数列表不同;重写是子类重写父类的方法,方法名和参数列表都相同。
​
返回类型可以不同,即重载方法的返回类型可以不同;重写方法的返回类型必须相同。
​
重载对应的是同一个类中的方法,重写对应的是继承关系中父类和子类中的方法。
​
重载是编译时多态,重写是运行时多态。

  • 抽象类和接口有什么区别

抽象类可以包含普通方法和抽象方法,接口只能包含抽象方法。
类只能继承一个抽象类,但可以实现多个接口。
抽象类的方法可以有public、protected和default访问修饰符,接口的方法默认是public。
抽象类可以有实例变量,接口只能有静态常量。
抽象类可以有构造方法,接口不能有构造方法。
  • 说说反射的用途及实现

反射是Java的一个重要特性,它可以在运行时动态获取类的信息,并可以动态创建对象、调用方法、访问属性等。反射主要应用于框架开发、动态代理、单元测试等场景。实现反射可以使用Java自带的反射API,如Class类、Method类、Constructor类等。

  • 说说自定义注解的场景及实现

自定义注解是Java中的一种元编程技术,可以在代码中定义注解,并在程序运行时通过反射获取注解信息。自定义注解可以应用于很多场景,如框架开发、代码生成、测试框架等。实现自定义注解需要使用Java的注解API,如@Retention、@Target、@Inherited等。

  • HTTP 请求的 GET 与 POST 方式的区别

GET和POST是HTTP协议中常用的两种请求方式。GET请求会将参数放在URL中,而POST请求会将参数放在请求体中。因此,GET请求比较适合请求数据,而POST请求比较适合提交数据。GET请求的请求参数长度有限制,而POST请求没有长度限制。GET请求可以被缓存,而POST请求不能被缓存。

  • session 与 cookie 区别

session和cookie是Web应用中常用的状态管理技术。cookie是客户端存储状态的一种方式,它可以在浏览器端存储一些数据,如用户ID、用户名等,以便下一次访问时可以直接使用。session是服务器端存储状态的一种方式,它可以在服务器端存储一些数据,如用户信息、购物车信息等,以便用户在不同页面之间可以共享数据。session和cookie都可以实现用户状态的跟踪和管理,但session相对更安全,因为session数据存储在服务器端,不容易被窃取。

  • session 分布式处理

在分布式系统中,由于请求可能被多个服务器处理,因此session需要进行分布式处理,以保证不同服务器之间的session数据同步。常用的session分布式处理方案有以下几种:基于数据库的session共享、基于缓存的session共享、基于cookie的session共享、基于NFS的session共享等。

  • JDBC 流程

    • 加载数据库驱动程序

    • 建立数据库连接

    • 创建Statement对象

    • 执行SQL语句

    • 处理查询结果集

    • 关闭Statement和数据库连接

  • MVC 设计思想

MVC是一种常用的设计思想,它将应用程序分为三个部分:模型(Model)、视图(View)和控制器(Controller)。模型表示应用程序的数据和业务逻辑,视图表示用户界面,控制器协调模型和视图之间的交互。MVC设计思想的主要优点是代码分层明确、易于维护和重用。

  • equals 与 == 的区别

在Java中,==是比较两个对象的引用是否相等,而equals是比较两个对象的内容是否相等。具体来说,==比较的是两个对象的内存地址,即它们是否指向同一个对象;而equals比较的是两个对象的内容,即它们是否具有相同的属性和状态。对于基本数据类型,==比较的是它们的值是否相等,而对于引用类型,==比较的是它们的引用是否相等。通常情况下,我们应该使用equals来比较两个对象的内容是否相等。

集合

  1. List和Set区别: List是一个有序的集合,允许重复元素存在。Set是一个无序的集合,不允许重复元素存在。

  2. List和Map区别: List是一个有序的集合,可以包含重复元素,元素访问通过索引进行。Map是一个键值对集合,可以通过键来访问值,键不允许重复,值允许重复。

  3. ArrayList与LinkedList区别: ArrayList底层使用数组实现,支持快速随机访问,但在插入和删除元素时需要移动其他元素。LinkedList底层使用链表实现,插入和删除元素时只需要修改指针,但随机访问需要遍历链表。

  4. ArrayList与Vector区别: ArrayList和Vector都是动态数组,它们的主要区别在于线程安全性和效率。Vector是线程安全的,但效率相对较低,而ArrayList是非线程安全的,但效率相对较高。

  5. HashMap和Hashtable的区别: HashMap和Hashtable都是键值对映射的集合,但它们的主要区别在于线程安全性和效率。Hashtable是线程安全的,但效率相对较低,而HashMap是非线程安全的,但效率相对较高。此外,HashMap允许键和值为null,而Hashtable不允许。

  6. HashSet和HashMap区别: HashSet是基于HashMap实现的,它是一个不允许重复元素的集合。HashMap是一个键值对映射的集合,键不允许重复,但值允许重复。

  7. HashMap和ConcurrentHashMap的区别: HashMap和ConcurrentHashMap都是键值对映射的集合,但它们的主要区别在于线程安全性和效率。HashMap是非线程安全的,效率相对较高,适用于单线程环境。ConcurrentHashMap是线程安全的,效率相对较低,但适用于多线程环境。

  8. HashMap的工作原理及代码实现: HashMap底层使用哈希表实现,可以快速访问和修改元素。哈希表是由一个数组和一些链表或红黑树组成的。当元素被添加到HashMap中时,先根据元素的hashcode计算出它在数组中的位置,如果该位置已经有元素,就把新元素加入到链表或红黑树中,否则直接放入该位置。当访问元素时,根据元素的hashcode找到它在数组中的位置,然后在链表或红黑树中查找该元

线程

  • 创建线程的方式及实现: Java中有两种方式创建线程:继承Thread类和实现Runnable接口。继承Thread类需要重写run()方法,该方法体中的代码会在线程启动后执行。实现Runnable接口需要实现run()方法,并将其传入Thread类的构造方法中,然后调用Thread的start()方法启动线程。

  • sleep()、join()、yield()有什么区别: sleep()方法让当前线程休眠一段时间,期间不会释放锁;join()方法让当前线程等待另一个线程执行完毕后再继续执行;yield()方法让出当前线程的CPU时间片,给其他线程执行的机会。区别在于sleep()和join()方法会暂停当前线程的执行,而yield()方法只是让出一部分CPU时间片。

  • CountDownLatch原理: CountDownLatch是一个同步工具类,允许一个或多个线程等待其他线程完成操作后再执行。它通过一个计数器来实现,计数器初始化为一个正整数,每个线程完成操作时调用countDown()方法将计数器减1,当计数器变为0时,所有等待线程被唤醒。CountDownLatch的实现基于AQS(AbstractQueuedSynchronizer)同步器,通过维护一个同步状态和一个等待队列来实现线程的等待和唤醒。

  • CyclicBarrier原理: CyclicBarrier也是一个同步工具类,允许多个线程在某个屏障处等待,直到所有线程都到达该屏障后再继续执行。与CountDownLatch不同,CyclicBarrier的计数器可以重复使用。CyclicBarrier的实现也基于AQS同步器,每个线程到达屏障后调用await()方法进行等待,直到所有线程都到达屏障后才一起继续执行。

  • Semaphore原理: Semaphore是一个同步工具类,允许多个线程同时访问一定数量的资源。Semaphore通过一个计数器来实现,初始化时指定可访问资源的数量,每个线程访问资源时调用acquire()方法将计数器减1,当计数器为0时,所有访问资源的线程被阻塞。当一个线程释放资源时调用release()方法将计数器加1,唤醒一个被阻塞的线程。Semaphore的实现也基于AQS同步器

  • Exchanger 原理:Exchanger 是一个并发工具,可以用于两个线程之间交换数据。Exchanger 可以在两个线程之间提供一个同步点,当两个线程都到达这个同步点时,会交换它们持有的数据,并继续执行。

    Exchanger 的实现原理是使用了 LockSupport 类提供的 park 和 unpark 方法。当一个线程调用 Exchanger 的 exchange 方法时,它会被阻塞,直到另一个线程也调用 exchange 方法。这时,Exchanger 会将两个线程持有的数据进行交换,然后唤醒这两个线程,使它们继续执行。

  • CountDownLatch 与 CyclicBarrier 区别:CountDownLatch 和 CyclicBarrier 都是 Java 并发包中的同步工具,但是它们的使用场景不同。

    CountDownLatch 用于一个线程等待其他线程完成某个任务之后再继续执行。CountDownLatch 可以让一个线程等待多个线程完成,也可以让多个线程等待一个线程完成。它的核心方法是 await() 和 countDown()。

    CyclicBarrier 用于多个线程互相等待,直到所有线程都到达一个同步点之后再继续执行。CyclicBarrier 可以重复使用,即当所有线程都到达同步点之后,CyclicBarrier 可以被重置,重新开始下一轮的等待。它的核心方法是 await()。

  • ThreadLocal 原理分析:ThreadLocal 是一个线程本地变量,每个线程都可以独立地改变它的值,而不会影响其他线程的值。ThreadLocal 通常被用来解决线程安全问题。

    ThreadLocal 的原理是使用一个 ThreadLocalMap 类型的成员变量来存储每个线程的值。ThreadLocalMap 是一个类似于 Map 的数据结构,它的 key 是 ThreadLocal 实例,value 是该线程持有的值。每个线程都有自己的 ThreadLocalMap 实例,并且可以独立地修改其中的值。ThreadLocal 在获取值时,会根据当前线程获取该线程对应的 ThreadLocalMap 实例,并根据 ThreadLocal 实例获取对应的值。

  • 线程池的实现原理线程池是为了解决线程创建和销毁的开销,以及过多线程竞争系统资源的问题,而提出的一种解决方案。线程池中主要包含一个任务队列和一组线程,当任务到来时,线程池会分配一个空闲线程来处理该任务,当所有线程都处于忙碌状态时,新任务会被加入到任务队列中等待执行。线程池的实现主要涉及到线程池的初始化、任务提交、任务执行、线程池关闭等几个方面。线程池的实现可以使用 JDK 提供的 ThreadPoolExecutor 类,也可以手动实现线程池。

  • 线程池的几种方式Java 中常见的线程池有以下几种:

    (1) FixedThreadPool:固定大小线程池,线程数量固定,线程池中的线程数不会改变,适用于负载比较重的服务器。

    (2) CachedThreadPool:缓存线程池,线程数量不固定,根据任务数量动态调整线程池中的线程数,适用于执行短时间的任务。

    (3) SingleThreadExecutor:单线程线程池,只有一个线程在执行任务,适用于需要保证顺序执行的任务。

    (4) ScheduledThreadPool:定时任务线程池,可以定时执行任务,适用于周期性执行任务的场景。

  • 线程的生命周期线程的生命周期主要包括以下几个状态:

    (1) 新建状态(New):当线程对象创建后,它就处于新建状态。

    (2) 就绪状态(Runnable):当调用 start() 方法后,线程进入就绪状态,等待 CPU 分配时间片,开始执行任务。

    (3) 运行状态(Running):当线程获得 CPU 时间片后,进入运行状态,开始执行任务。

    (4) 阻塞状态(Blocked):当线程因为某些原因无法执行任务时,进入阻塞状态。

    (5) 等待状态(Waiting):当线程因为某些原因需要等待另一个线程执行完毕后再执行时,进入等待状态。

    (6) 超时等待状态(Timed Waiting):当线程因为某些原因需要等待一段时间后再执行时,进入超时等待状态。

    (7) 终止状态(Terminated):当线程执行完毕或出现异常时,进入终止状态。

锁机制

  • 线程安全问题:线程安全问题是多线程编程中常见的问题,由于多个线程同时访问同一个资源,可能会导致数据的不一致或者程序出现异常。常见的线程安全问题包括:竞态条件、死锁、饥饿等。

  • volatile 实现原理: volatile 是一种轻量级的同步机制,用来保证变量的可见性和禁止指令重排序优化。volatile 的实现原理是使用了 CPU 的缓存一致性协议,当一个变量被 volatile 修饰后,对这个变量的读写操作都会直接从内存中读取或者写入,不会使用 CPU 缓存,从而保证了变量的可见性。

  • synchronize 实现原理:synchronize 是一种重量级的同步机制,用来保证线程安全。synchronize 的实现原理是使用了对象的监视器,当一个线程获取了对象的监视器后,其他线程就不能再访问这个对象的 synchronized 方法和代码块,直到持有对象监视器的线程释放锁。

  • synchronized 与 lock 的区别

synchronized 和 lock 都可以用来保证线程安全,但是两者有一些区别:

synchronized 是 JVM 提供的关键字,而 lock 是一个接口,需要程序员自己实现。

synchronized 可以修饰方法和代码块,而 lock 只能用来修饰代码块。

synchronized 在程序执行完 synchronized 代码块或者方法后会自动释放锁,而 lock 必须手动释放锁。

  • CAS 乐观锁:CAS(Compare And Swap)是一种乐观锁机制,用来保证线程安全。CAS 操作包括三个操作数:内存位置、期望的原值和新值。当期望的原值和内存位置的值相同时,将内存位置的值更新为新值,否则什么也不做。CAS 操作是原子操作,因此可以保证线程安全。

  • ABA 问题:ABA 问题是 CAS 操作中的一个问题,当一个线程修改了某个变量的值,然后又将其修改为原来的值,此时另外一个线程也修改了该变量的值并将其修改为和原来一样的值,这样就出现了 ABA 问题。为了解决 ABA 问题,可以使用带有版本号的 CAS 操作。

  • 乐观锁的业务场景及实现方式: 乐观锁适用于多读少写的业务场景,通过在数据上加一个版本号(或者时间戳等),来实现对数据的同步控制。当读取数据的时候,先读取版本号,然后执行业务逻辑,最后更新数据的时候,检查版本号

核心篇

数据存储

  1. MySQL 索引使用的注意事项

- 在频繁进行查询、排序、分组、连接等操作的字段上建立索引。
- 避免使用过长的索引字段,建议使用整数类型。
- 对于经常修改的表,不宜建立太多的索引。
- 联合索引的顺序要考虑查询语句的where条件以及排序和分组的字段。
- 避免在索引字段上使用函数、表达式或类型转换。
- 注意对null值的处理,null值不会被索引。

  • 说说反模式设计: 反模式是指在软件设计和开发中,使用了被证实不良的做法和方法,可能会导致软件质量的下降、开发成本的增加、可维护性的降低等问题。一些常见的反模式包括:大类、大方法、复杂继承、复杂逻辑、破窗效应等。

  • 说说分库与分表设计

    分库与分表是指将一个大的数据库或表,拆分成多个小的数据库或表来存储数据。这种设计可以解决单表数据量过大的问题,提高数据库的并发能力和性能。
    ​
    分库与分表的设计需要考虑以下几点:
    ​
    - 划分原则:可以按照业务模块、数据的关联性、地域等因素进行划分。
    - 切分键选择:在划分的过程中需要选择一个或多个切分键,确保数据可以均匀地分布到各个库或表中。
    - 跨库查询:跨库查询可能会影响性能,需要根据具体情况进行优化。
    - 数据迁移:在数据量增大或业务变更的情况下,需要考虑数据的迁移问题。
    ​

  • 分库与分表带来的分布式困境与应对之策

    分库分表是常见的水平分割技术,它可以将一个大型的数据库分成多个小型的数据库或表,以便于提高数据库的扩展性、容量和性能等。但是,分库分表也带来了分布式困境,需要采取一些应对措施。
    ​
    数据一致性问题
    分库分表后,数据的分布变得分散,这样在数据同步、数据备份、数据恢复等方面都会带来挑战。因此,在设计分库分表的架构时,必须考虑到数据一致性的问题。常见的解决方案包括两阶段提交、消息队列等技术,保证数据的可靠性和一致性。
    ​
    跨节点查询问题
    在分库分表的架构中,查询数据的时候,需要跨多个节点进行查询,这可能会导致查询速度变慢。为了解决这个问题,可以采用数据路由和查询优化技术,将查询请求路由到正确的节点,从而提高查询速度。
    ​
    系统可用性问题
    分库分表架构中,由于多个节点参与,因此系统的可用性会变得更加脆弱。如果一个节点出现故障,可能会影响整个系统的可用性。为了解决这个问题,可以采用主从复制、数据备份和容错技术等,确保系统的高可用性。
    ​
    数据迁移问题
    分库分表后,数据的分布变得更加分散,如果需要进行数据迁移,可能会变得非常困难。为了解决这个问题,需要采用数据迁移和数据备份技术,确保数据的安全和可靠性。
    ​
    总之,分库分表架构是一种非常实用的技术,但是也需要考虑到分布式困境,从而采取相应的应对措施,保证系统的稳定和可靠性。

  • 说说 SQL 优化之道

    优化查询语句,尽量避免全表扫描;
    避免在 WHERE 子句中使用函数或表达式,会导致索引失效;
    尽量使用覆盖索引,减少回表操作;
    使用合适的索引,根据查询的列和条件进行创建;
    避免 JOIN 查询中的笛卡尔积,减少数据量;
    对大量数据的表进行分区,提高查询效率;
    限制结果集大小,避免一次查询过多的数据;
    使用数据库缓存,如 Memcached 或 Redis,提高查询速度;
    合理设计数据库架构,分表分库,避免单点故障。

  • MySQL 遇到的死锁问题

    MySQL 中的死锁问题指的是当两个或多个事务相互等待对方释放资源而导致的系统无法进行的情况。解决 MySQL 死锁问题的常用方法包括:
    设计合理的数据库结构,减少数据冲突;
    尽量使用索引,避免全表扫描;
    限制事务的持续时间,避

你可能感兴趣的:(spring,cloud,eureka,java,java-ee,spring,boot)