没问到不亏,问到血赚。
内存泄漏和内存溢出分别是什么意思?
- 内存泄漏(Memory Leak)指的是在程序中分配的内存空间没有被正确释放或回收的情况。当一个对象在不再被使用时,如果没有显式释放其占用的内存空间,或者存在引用循环导致对象无法被垃圾回收器回收,就会出现内存泄漏。随着时间的推移,内存泄漏会导致系统可用内存逐渐减少,最终可能导致系统性能下降或崩溃。内存泄漏通常是由于程序逻辑错误、资源管理错误或不当的对象生命周期管理引起的。
- 内存溢出(Memory Overflow)指的是程序在申请内存时超过了系统或进程可用的内存空间。当程序需要分配的内存超过了系统或进程的限制,无法满足分配请求时,就会发生内存溢出。内存溢出可能导致程序崩溃、运行异常或者系统崩溃。内存溢出通常是由于程序中存在递归调用、大量数据存储、内存泄漏等情况引起的。
对于内存泄漏,需要确保正确释放不再使用的内存资源,避免资源占用过多。
对于内存溢出,需要合理规划内存使用,优化算法和数据结构,避免申请过多的内存。此外,使用合适的内存管理工具和编程语言的垃圾回收机制,也能帮助检测和解决内存泄漏和内存溢出问题。
NoSQL数据库是什么?
NoSQL(NoSQL = Not Only SQL ),意即”不仅仅是SQL“,泛指非关系型的数据库。
NoSQL 不依赖业务逻辑方式存储,而以简单的key-value模式存储。因此大大的增加了数据库的扩展能力。
- 不遵循SQL标准。
- 不支持ACID
- 远超于SQL的性能。
- 数据无关联
- 储存在内存当中
常见的就是Radis、MongoDB。
Radis有哪些数据类型。
有好多,但常用的有String、list、set、Hashes、Short Set。
MySql中的日志有哪几种?
redo log、undo log、bin log等
- Redo Log(重做日志): Redo Log用于保证数据库的事务持久性和崩溃恢复。它记录了正在进行中的事务所做的更改,以便在数据库发生崩溃时重新应用这些更改。Redo Log是物理日志,以块为单位记录数据修改操作。
- Undo Log(撤销日志): Undo Log用于支持事务的回滚操作。它记录了正在进行中的事务对数据的旧值的修改,以便在事务回滚时恢复数据到之前的状态。Undo Log也用于MVCC(多版本并发控制)的实现。
- 重做缓冲(Redo Buffer): 重做缓冲是内存中的一个缓冲区,用于暂存事务的重做日志记录。它提高了事务提交时的性能,减少了写入磁盘的频率。
- Bin log(二进制日志): 二进制日志记录了数据库的逻辑更改,包括数据库中执行的所有语句和数据修改操作。它用于实现数据复制、恢复和数据库同步等功能。
- 查询重放日志(Query Rewrite Log): 查询重放日志是一种高级日志功能,用于记录和重放查询重写操作。它可用于实现查询重写和优化器的测试、调试和性能分析。
这些日志类型在MySQL中发挥着不同的作用,用于确保数据的一致性、持久性和崩溃恢复,以及支持数据库的复制、同步和高级功能。它们共同组成了MySQL的日志系统,保证了数据库的可靠性和可恢复性。
==和equals的区别
==和equals都是用来判断两个内容是否相等。
但==一般用于基本数据类型的判断,对于引用数据类型==是判断地址是否相等。
equals一般用于引用数据类型的判断,比如两个字符串或两个引用对象是否相等。
一般会对equals方法进行重写。然后自己写对类的比较规则。
hashcode和equals如何使用。
在往hashmap里面放数据的时候,是参照hashcode的hash值的。这个值也对应在hashmap的数组中的位置。对于相同的对象会在链表的位置进行追加。而需要先进行equals比较才能判断是否要放进去。如果key相等就不放了,如果key不相等就会进行追加。
对两个对象进行判断的时候使用equals效率太低,一般是直接使用hashcode的值进行取模运算。
但两个不同的对象可能生成相同的hashcode。但如果两个对象的内存对象相同,则生成的hashcode也一定相同。
如果只重写equals方法,会导致hashcode不相同。
这也是为什么重写equals也要重写hashcode。
可能在集合中存放了相同值的对象。
Java中线程的状态。
官方是6种:new、runable、bloaked、waiting、timed_waiting、terminated。
当执行sleep()的时候会到timed_waiting,直到时间到了会被自动唤醒,回到runable。而waiting需要手动notifi唤醒。
操作系统层面是5种:new、ready、running、waiting、terminated
在程序遇到了如join()、sleep()、wait()这一类的阻塞操作,则会到等待状态waiting,直到又到了ready状态。
Java中sleep和wait方法的区别。
sleep是Thread中的静态方法。而wait是object类中的方法。
sleep属于timed_waiting,休眠后会被自动唤醒。sleep在持有cpu的情况下不会释放cpu资源。也就是拿着锁进入timed_waiting。
wait属于waiting,休眠后需要被手动唤醒。wait在持有cpu的情况下会释放cpu资源。
Java中的四种引用类型。
强引用、弱引用、软引用、虚引用。
谈谈你对Spring的理解。
Spring是一个开源的Java应用开发框架,旨在简化企业级应用程序的开发。它提供了一个轻量级的、非侵入性的编程和配置模型,使得开发人员可以更专注于业务逻辑的实现,而不必过多关注底层的技术细节。
- 依赖注入(Dependency Injection):Spring通过依赖注入的方式管理应用程序中的组件之间的依赖关系。通过配置文件或注解,Spring容器负责将相应的依赖项自动注入到需要它们的地方,从而实现了松耦合和可测试性。
- 面向切面编程(Aspect-Oriented Programming):Spring支持面向切面编程,通过AOP可以将横切关注点(如日志记录、性能监控、事务管理等)从业务逻辑中分离出来,提高了代码的可维护性和可复用性。
- 容器管理(Container):Spring提供了一个IoC容器,负责创建和管理应用程序中的对象。通过配置文件或注解,开发人员可以定义Bean的生命周期、作用域和依赖关系,Spring容器会负责实例化、组装和管理这些对象。
- 数据访问支持:Spring提供了对各种数据访问技术的集成支持,包括JDBC、ORM框架(如Hibernate、MyBatis)和NoSQL数据库等。通过Spring的数据访问抽象层,开发人员可以更方便地使用这些数据访问技术,而不必过多关注底层实现细节。
- MVC框架:Spring提供了一个灵活的MVC框架,用于构建Web应用程序。通过配置和扩展Spring MVC,开发人员可以实现URL路由、请求处理、数据绑定、视图解析等功能,从而更高效地开发出结构清晰、可扩展的Web应用。
综上所述,Spring是一个强大而灵活的开发框架,它提供了丰富的特性和功能,帮助开发人员构建可维护、可扩展、高效的企业级Java应用程序。
上面都是些套话。不过现在Spring不单是个框架了,更多的是一个生态。不同的开发技术相互依赖融合,构成更好的开发生态。提供了更多可以快速构建的可靠的相关工具框架、覆盖微服务、数据访问、安全和企业集成等等。这使得开发人员能够更高效地构建复杂的应用程序,并且能够与其他Spring生态系统中的组件无缝集成。极大提高代码质量和软件质量。
JDK、JRE、JVM的区别
JDK是开发工具、包括了Java编译器、Java运行时环境、以及常用类库等。可以用于开发、编译、运行Java程序。
JRE是Java运行时环境、用于运行Java字节码文件。其中包括了JVM和JVM所需的类库。普通用户安装JRE即可运行Java程序。
JVM是Java虚拟机、是JRE的一部分,它是整个Java实现跨平台的最核心部分,负责运行Java字节码文件。
String、StringBuffer、StringBuilder的区别。
String是常量是不可变的。就是我们通常的字符串。
StringBuffer是早期的类,是可以变的而且是线程安全的。
StringBuilder是后面出现的类。是可以变的但是线程不安全的。
如果需要频繁修改字符串内容且在多线程环境下使用,应该选择StringBuffer;如果在单线程环境下需要频繁修改字符串内容,可以选择StringBuilder以获得更好的性能;如果字符串内容不需要修改,或者在多线程环境下使用,应该选择String类。
hashmap中的get方法。
get方法会先去判断hashcode。在hashcode相同的情况下才会去判断equals。
ArrayList和LinkedList有什么区别
ArrayList是数组,LinkedList是链表。这两者都实现了List接口,LinkedList还额外实现了Deque接口(双端队列的接口)。
前者更适合随机查找。后者更适合删除、插入操作。
B树和B+树的区别。为什么MySql使用B+树。
B树的特点是所有的节点都是排序过的。一个节点里面可以存储多个元素。
B+树的叶子节点之间有指针,因为数据库需要支持范围查找。
Java中有哪些集合数据类型?继承关系是什么样的?
Collection 接口是 List、Set 和 Queue 接口的父接口。
List(列表):按照元素插入的顺序存储对象,可以包含重复元素。常见的实现类有ArrayList、LinkedList、Vector。
Set(集合):存储独一无二的对象,不允许重复元素。常见的实现类有HashSet、TreeSet、LinkedHashSet。
Queue(队列):按照特定规则插入和删除元素。常见的实现类有LinkedList、PriorityQueue、ArrayDeque。
Deque 接口继承自 Queue 接口,它在队列的两端都可以插入和删除元素,支持双向操作。常见的实现类有LinkedList、ArrayDeque。
Map 接口提供了键值对的存储和检索功能,它是独立于 Collection 接口的,不继承自 Collection 接口。用于存储键值对(key-value)的对象。每个键只能出现一次,但值可以重复。常见的实现类有HashMap、TreeMap、LinkedHashMap。
事务的特性有哪些?
事务(Transaction)是数据库操作的基本单位,具有以下四个特性,通常被称为ACID特性:
- 原子性(Atomicity):原子性指事务是一个不可分割的操作单位,要么全部执行成功,要么全部回滚到事务开始前的状态,不允许出现部分执行的情况。如果事务中的任何一个操作失败,整个事务都会被回滚,确保数据的一致性。
- 一致性(Consistency):一致性指事务在执行前后,数据库的状态必须保持一致。事务执行前和执行后,数据库中的数据必须满足事务定义的业务规则、约束条件和完整性约束,以确保数据的有效性和完整性。
- 隔离性(Isolation):隔离性指多个并发执行的事务之间应该相互隔离,每个事务都应该感知不到其他事务的存在。隔离性确保每个事务在执行过程中不受其他事务的干扰,避免了并发执行时可能出现的数据不一致问题。
- 持久性(Durability):持久性指一旦事务提交成功,其对数据库的修改就是永久性的,即使在系统故障或重启后也能够恢复。系统需要将事务的结果持久地存储到稳定的存储介质(如磁盘)上,以防止数据的丢失。
这些事务特性确保了数据库的数据在并发操作下的一致性和可靠性。数据库管理系统(DBMS)通过使用日志记录、锁机制、并发控制和回滚/恢复等技术来实现这些特性。
需要注意的是,ACID特性对于某些特定场景可能过于严格,会降低系统的性能和并发性。因此,在一些分布式系统中,为了更好地满足性能和可扩展性的需求,可能会采用柔性事务或最终一致性的方案。这些方案可能会在一定程度上放宽ACID特性的要求,如BASE(Basically Available, Soft state, Eventually consistent)模型。
说说DI、IOC、AOP
DI(依赖注入),IOC(控制反转)和AOP(面向切面编程)是三个常见的设计模式和编程范式,它们在软件开发中起着重要的作用。
- 依赖注入(Dependency Injection,DI): DI是一种通过外部注入依赖对象的方式,来解耦和管理组件之间的依赖关系的设计模式。传统的开发模式中,对象通常负责自己创建和管理所依赖的对象,而在DI模式中,对象不再自己创建依赖对象,而是通过外部的机制将依赖对象注入进来。这样可以降低对象之间的耦合性,提高代码的可测试性、可维护性和可扩展性。
- 控制反转(Inversion of Control,IOC): IOC是一种软件设计原则,用于解耦对象之间的关系。在传统的编程模式中,对象通常通过直接创建和管理依赖对象来满足自身的需求,而在IOC模式中,对象不再自己创建和管理依赖对象,而是通过外部的容器(如Spring容器)来创建和管理对象。控制反转将对象的控制权交给了容器,容器负责创建、配置和管理对象及其依赖关系,使得对象之间的关系更加灵活、可配置和可扩展。
- 面向切面编程(Aspect-Oriented Programming,AOP): AOP是一种编程范式,用于解决分散在应用程序各处的横切关注点(Cross-cutting Concerns),例如日志记录、事务管理、安全性等。在传统的面向对象编程中,这些横切关注点往往散布在应用程序的各个模块中,导致代码的重复和难以维护。AOP通过将这些横切关注点从主业务逻辑中分离出来,形成可重用的切面(Aspect),使得关注点的修改和管理更加集中和方便。AOP通过在运行时动态地将切面织入到目标对象中,实现了横切关注点的模块化和复用。
综上所述,DI、IOC和AOP是三个常见的设计模式和编程范式。DI通过注入依赖对象来解耦组件之间的依赖关系,IOC通过控制反转将对象的控制权交给容器来解耦对象的创建和管理,而AOP则通过将横切关注点从主业务逻辑中分离出来,实现关注点的复用和集中管理。这些模式和范式可以提高代码。
题外话:IOC最早是由Spring的创始人提出的并系统化的整理,后面Java看到了IOC的发展,也融入了IOC的思想提出了DI的概念(为了不和IOC的名称重复)。
什么是数据库连接池?为什么使用数据库连接池?
数据库连接池(Database Connection Pool)是一种管理和维护数据库连接的技术。它在应用程序和数据库之间建立一组预先创建的数据库连接,并对这些连接进行管理和重用。
使用数据库连接池的主要原因包括以下几点:
- 提高性能:数据库连接的建立和销毁是一项开销较大的操作。通过使用连接池,可以避免频繁地创建和销毁连接,而是重复利用已经建立的连接。这样可以减少系统开销,提高数据库访问的性能。
- 资源管理:数据库连接是有限资源,每个连接都需要占用一定的内存和系统资源。连接池可以有效地管理这些资源,确保在需要时可用,并且能够限制同时打开的连接数,避免过多的连接对数据库造成压力。
- 连接的可复用性:数据库连接池允许多个线程同时从连接池中获取连接,并在使用完毕后归还给池,以便其他线程重复使用。这样可以提高并发性能和资源利用率。
- 连接的管理和监控:连接池可以提供一些管理和监控功能,例如连接的状态监测、空闲连接超时检测、连接泄漏检测等。这些功能可以帮助开发人员及时发现和解决连接相关的问题。
总之,使用数据库连接池可以提高数据库访问的性能、资源利用率和可维护性。它是一种常见的数据库优化技术,被广泛应用于各种类型的应用程序中。常见的Java数据库连接池包括Apache Commons DBCP、C3P0、HikariCP等。这些连接池框架提供了易于使用的接口和配置选项,方便开发人员集成到他们的应用程序中。
什么是负载均衡,如何解决负载均衡?可以使用什么框架?
负载均衡(Load Balancing)是一种分布式系统中常用的技术,旨在平衡服务器资源的负载,确保在高负载情况下,请求能够均匀地分布到多个服务器上,提高系统的性能、可靠性和可扩展性。
解决负载均衡问题的方法有多种,以下是几种常见的方法:
- 硬件负载均衡:通过使用专门的硬件设备(如负载均衡器)来分发和管理流量。这些硬件设备根据特定的算法(如轮询、加权轮询、最少连接数等)将请求分发到后端服务器上。
- 软件负载均衡:使用软件来实现负载均衡,通常在应用层或网络层实现。常见的软件负载均衡方案包括反向代理服务器(如Nginx、Apache HTTP Server)和应用服务器集群(如Tomcat、JBoss)。
- DNS负载均衡:通过DNS服务器将请求分发到多个服务器的不同IP地址上。DNS服务器返回一个IP地址列表,客户端根据列表中的IP地址选择其中一个进行访问。这种方式适用于较小规模的负载均衡需求。
- 负载均衡框架:还有一些专门的负载均衡框架,如Netflix的Zuul、Spring Cloud的Ribbon和Eureka等,它们提供了在微服务架构中进行负载均衡和服务发现的能力。
选择负载均衡框架通常取决于具体的需求和技术栈。如果你正在构建基于Java的微服务架构,可以考虑使用Spring Cloud的负载均衡组件(如Ribbon)和服务发现组件(如Eureka)来实现负载均衡功能。这些框架提供了简便的配置和集成,并且与Spring生态系统很好地配合使用。
需要注意的是,负载均衡不仅限于单一的技术或框架,还需要综合考虑系统的架构、性能需求、可扩展性要求等因素,选择最适合的负载均衡解决方案。
解释一下Spring MVC框架的工作原理。
Spring MVC(Model-View-Controller)是Spring框架中用于开发Web应用程序的一部分。它遵循MVC设计模式,将应用程序的逻辑分为模型(Model)、视图(View)和控制器(Controller)三个组件。
下面是Spring MVC框架的工作原理:
- 请求到达前端控制器(Front Controller):在Spring MVC中,DispatcherServlet是前端控制器。当客户端发送请求时,DispatcherServlet接收并处理请求。
- 处理器映射器(Handler Mapping):DispatcherServlet使用处理器映射器(Handler Mapping)来确定哪个控制器将处理请求。处理器映射器根据请求的URL或其他标识符来映射到相应的控制器。
- 处理器适配器(Handler Adapter):处理器适配器负责将请求分发给相应的控制器。它将请求和控制器进行适配,确保控制器能够正确处理请求。
- 控制器处理请求:控制器是实际处理请求的组件。它接收请求,并根据业务逻辑执行相应的操作,例如调用服务层方法、访问数据库等。控制器可以返回数据模型和逻辑视图名称。
- 视图解析器(View Resolver):视图解析器负责将逻辑视图名称解析为实际的视图对象。它根据视图名称查找并返回对应的视图对象,例如JSP页面或Thymeleaf模板。
- 视图渲染和响应:视图对象接收数据模型,并负责将数据填充到视图模板中。它生成最终的HTML或其他类型的响应,并将其返回给客户端。
- 响应返回给客户端:DispatcherServlet将最终的响应返回给客户端,完成请求-响应周期。
通过这种方式,Spring MVC框架将请求的处理逻辑进行了解耦,并提供了灵活的配置和扩展选项。开发人员可以定义控制器和视图,以及配置处理器映射器、视图解析器等来满足特定的业务需求。
总结起来,Spring MVC框架的工作原理是通过DispatcherServlet作为前端控制器,使用处理器映射器、处理器适配器、控制器、视图解析器和视图等组件来实现请求的路由、处理和响应。
在Java中,什么是序列化(Serialization)?如何实现对象的序列化和反序列化?
在Java中,序列化(Serialization)是将对象转换为字节流的过程,以便可以将其存储在文件中、通过网络传输或在内存中进行持久化。反序列化(Deserialization)则是将字节流转换回对象的过程。
要实现对象的序列化和反序列化,需要满足以下条件:
- 类必须实现
java.io.Serializable
接口:该接口是一个标记接口,表示该类可以进行序列化。它不包含任何方法,只是用于标识可序列化的类。 - 序列化过程:使用
ObjectOutputStream
类将对象序列化为字节流。可以通过创建ObjectOutputStream
对象,并使用其writeObject()
方法将对象写入输出流。 - 反序列化过程:使用
ObjectInputStream
类将字节流反序列化为对象。可以通过创建ObjectInputStream
对象,并使用其readObject()
方法从输入流中读取对象。
需要注意的是,被序列化的类的所有非静态字段都将被序列化。如果某个字段不需要序列化,可以使用transient
关键字进行标记。
另外,如果在序列化和反序列化过程中遇到不可序列化的字段,可以使用writeObject()
和readObject()
方法在类中自定义序列化和反序列化的逻辑。
总结起来,序列化是将对象转换为字节流的过程,反序列化是将字节流转换回对象的过程。通过实现java.io.Serializable
接口,并使用ObjectOutputStream
和ObjectInputStream
类,可以在Java中实现对象的序列化和反序列化。