前言
由于这段时间经常性的找工作找工作找工作,然后面试题问的也比较多,我就想着对这个进行一些整合,基本都是我面试的时候问过的一些问题,三年经验的JAVA开发,可能答案有些不太准确,请多多包涵和见谅;
线上面试题
基础类型判断
public static void main(String[] args) {
// 使用new关键字显式创建一个Integer对象,值为2
Integer i = new Integer(2);
// 利用Integer的缓存(int类型),实际上对于-128到127之间的值,Java会返回缓存中的对象
Integer j = 2;
// i和j虽然都表示整数值2,但它们是两个不同的对象引用。i引用的是通过new创建的全新对象,而j引用的是Integer缓存中的一个对象。
System.out.println(i == j); // false
// Integer类重写了equals方法,以基于值而不是引用进行比较。因此,即使i和j引用不同的对象,它们的值都是2,所以equals方法返回true。
System.out.println(i.equals(j)); // true
// hashCode方法返回的是对象的哈希码,通常用于在哈希表中快速定位对象。对于Integer对象,如果它们的值相同,那么它们的哈希码也相同。
System.out.println(i.hashCode() == j.hashCode()); // true
/**
* 对于普通的Object对象(没有重写equals和hashCode方法),
* ==会进行引用比较,equals方法默认也是进行引用比较,
* 而hashCode方法可能会返回不同的值(尽管对于某些对象,默认的hashCode实现可能会产生相同的哈希码,但这并不意味着它们是相等的)。
*/
Object o = new Object();
Object b = new Object();
System.out.println(o == b); // false
System.out.println(o.equals(b)); // false
System.out.println(o.hashCode() == b.hashCode()); // false
/**
* HashMap使用键的hashCode来定位桶(bucket)
* 并在该桶中使用equals方法来查找具体的键值对
* 由于i和j的哈希码相同,并且它们通过equals方法是相等的
* 所以当我们使用j作为键从HashMap中获取值时,可以成功地检索到与i关联的值。
*/
HashMap<Integer, Object> m = new HashMap<>();
m.put(i, 1);
System.out.println(m.get(j)); // 1
}
递归
题目:我现在有一个node
,下面有一个类型为Object
的ID
、还有一个类型为List<Node>
的children,请给出一个方法findById()
,传入的参数为List<Node>
和targetId
,请用他来查找出该ID所对应的目标节点;
public Node findById(List<Node> roots, Object targetId) {
// 遍历所有根节点
for (Node root : roots) {
// 递归搜索从当前根节点开始的子树
Node result = findByIdRecursive(root, targetId);
if (result != null) {
// 如果找到目标节点,返回它
return result;
}
}
// 如果没有在根节点列表中找到,返回null
return null;
}
// 递归辅助函数
private Node findByIdRecursive(Node node, Object targetId) {
if (node == null) {
// 如果没有节点,返回null
return null;
}
if (node.getId().equals(targetId)) {
// 如果当前节点的id与目标id匹配,返回当前节点
return node;
}
// 递归检查子节点
for (Node child : node.getChildren()) {
Node result = findByIdRecursive(child, targetId);
if (result != null) {
// 如果在子树中找到,返回找到的节点
return result;
}
}
// 如果没有在任何子树中找到,返回null
return null;
}
线下面试题
一个微服务框架 最重要的组成部分是哪些东西?
- 服务组件:一般用于服务注册与发现;
- 通信机制:例如消息队列等;
- 服务网关:将多个组件合并到一个端口进行访问,将请求路由到相应的服务实例;
- 监控组件:收集服务的运行指标、如请求量,响应时间、错误率等;
- 容器化技术:如
Docker
,用于服务的打包和部署,确保服务在不同环境的一致性和可移植性;
HTTP和HTTPS,以及他们的组成部分
http
:超文本传输协议,明文传输信息,端口号为80;https
:由SSL+HTTP
协议构建的加密传输的网络协议,端口号为443;- 组成部分:
http
:客户端->互联网->服务器https
:客户端->使用SSL
证书加密->互联网->服务器->使用SSL
证书解密(非对称加密)
- 缺点:
https
容易消耗硬件资源,并且SSL
证书的成本较高;http
的安全性不高,容易被窃听,服务器收到的数据直接默认上传数据有效
多线程与并发中 线程与线程怎么交互
- 使用
wait()
、notify()
、notifyAll()
; - 使用
volatile
关键字来修饰字段,通过共享内存中获取**(备注:会浪费CPU资源)**;
如何创建线程池,线程池的几个参数需要怎么设置
// 创建线程池
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5,10,60L,TimeUnit.SECONDS);
// 设置核心线程数[corePoolSize]、最大线程数[maximumPoolSize]、线程空闲时间[keepAliveTime]、时间单位[unit]
mybatis大批量插入数据设计技术方案
- 首先需要考虑到服务器的压力情况,需要选择服务器压力小的时候进行数据插入;
- 其次需要考虑分批次,多线程处理,mybatis有一个执行器,可以使用里面的批处理模式;
- 然后还要考虑插入报错的容错率,是否是必须全部成功,或者允许部分失败;
Redis作用拿来干什么?怎么设计他的内存淘汰策略?分布式锁怎么做?
- 作用:缓存数据,一般用于缓存用户登录信息,防止重复查询/导出,减少服务器负载;
- 内存淘汰策略可以通过配置最大可用内存和淘汰算法来实现,如
volatile-lru
、allkeys-lru
、volatile-random
和volatile-ttl
等; - 分布式锁,Redis提供了
setnx
命令和lua
脚本来实现,也可以考虑使用Redis
的RedLock
算法;
Redis除了做缓存还能做什么
- 分布式锁
- 限流
- 消息队列
- 延时队列
- 分布式Session
- 复杂业务场景维护
Redis的优点
- 基于内存,内存的访问速度比磁盘快
- 基于Reactor模式开发了一套高效的事件处理模型(单线程事件循环和IO多路复用)
- 内置了多种优化过后的数据类型/结构实现,性能非常高。
- 通信协议实现简单且解析高效。
RabbitMQ在你们业务怎么体现?怎么保证幂等性,消费失败怎么办?如何设置重试机制?
- 一般使用rabbitMQ来发送公告,或者某个用户的账户过期/下发失败等通知;
- 保证幂等性一般用于在消息中添加唯一标识,并在消费端进行去重处理;
- 消费失败可以设置rabbitMQ的重试机制,包括设置重试次数,重试间隔等;
了解哪些Map和List,TreeMap
Map
:HashMap
、HashTable
- 区别:
HashMap
不是线程安全的,HashTable
是线程安全的。 HashMap
允许空键和空值,HashTable
不允许空键和空值。HashMap
在单线程环境下的性能通常优于HashTable
,因为HashTable
使用synchronized
关键字实现同步,导致性能较差。HashMap
的迭代器是快速失败的(fail-fast)
,而HashTable
的迭代器不是。
- 区别:
List
:ArrayList
、LinkedList
- 区别:
ArrayList
基于动态数组,LinkedList
基于双向链表。 ArrayList
通过索引可以快速访问元素,LinkedList
需要遍历链表才能访问元素;ArrayList
适用于频繁访问和遍历元素的场景;LinkedList
适用于频繁插入和删除元素的场景。
- 区别:
Spring Cloud和SpringBoot的区别是什么?
Spring Boot
用于快速开发,简化了Spring Mvc
的XML
配置;Spring Cloud
是基于Spring Boot
的服务框架,简化了微服务应用的开发;Spring Cloud
需要在Spring Boot
的基础上使用,他提供了对微服务的综合管理;
MySql索引优缺点?
- 优点:
- 使用索引可以大大加快数据的检索速度,减少
IO
次数; - 通过创建唯一的索引,可以保证数据库表中每一行数据的唯一性;
- 使用索引可以大大加快数据的检索速度,减少
- 缺点:
- 创建/维护索引需要耗费许多时间,当对表中的数据进行增删改的时候,如果数据包括了索引,那么索引也需要进行动态的修改,会降低
SQL
执行效率; - 索引需要使用物理文件存储,也会耗费一定空间;
- 创建/维护索引需要耗费许多时间,当对表中的数据进行增删改的时候,如果数据包括了索引,那么索引也需要进行动态的修改,会降低
微服务中有哪些组件?
- 注册中心组件/服务配置管理组件:Nacos
- 远程调用组件:Fegin
- 网关组件:Gateway
- 日志监控:Log
- 消息队列:RabbitMq
- 缓存:Redis
在微服务中,网关的具体作用是什么?
- 统一入口:为所有的微服务提供唯一的一个入口点,简化客户端与服务的交互;
- 动态路由:根据需要,网关可动态的将请求路由映射到不同的后端集群中;
- 降低耦合度:网关层做映射,将客户端与服务端解耦,减少两者的依赖;
- 日志统一记录:在微服务网关中实现日志的统一记录,有助于监控和追踪系统的运行情况;
- 用户权限认证操作:微服务网关可以用作用户权限认证的统一入口,对进入系统的用户进行身份验证和授权;
在微服务中,如何实现远程调用?
- 实现一个接口,并使用
SpringCloud
自带的@FeginClient()
注解,并设置如下内容: contextId
:为该远程调用设置一个自定义的上下文ID,用于区分多个相同服务的不同客户端实例;value
:指定远程服务的名称,我这边使用自定义枚举来指定远程服务的名称,例如:business-log
、business-web
、cloud-tools
等;fallbackFactory
:指定一个回退工厂类,在远程调用失败后,将会在该方法中进行异常处理;- 在接口中使用
@PostMapping()
将调用的路径写好 并定义好远程调用的入参与出参(备注:出入参需要与远程调用接口的出入参一致 最好是放到一个统一的 可直接调用的模块中)
在什么情况下使用数据库作为字典,什么情况下使用枚举作为字典
- 使用数据库作为字典的前提是该数据未使用到业务逻辑,仅仅需要在数据库中进行存值的时候就可以直接使用数据库作为字典(举例:性别[男、女])。使用枚举作为字典的话就是需要维护的,在系统中有用到业务逻辑的(举例:订单类型[已发货、未发货、已签收等])。
MySQL性能优化
- 使用缓存 对于需要经常访问的结果使用缓存技术
- 避免使用
select *
- 创建索引
- 优化表结构 尽量使用小型的数据类型
- 定期进行数据清理,减缓数据库负载
Mysql索引 在什么情况下会失效?
- 数据太少:如果表中的数据太少,使用索引的效益不明显,甚至可能比全表扫描更慢
- 函数操作:如果在查询中使用了函数操作,索引将无法使用
LIKE
查询:如果在查询中使用LIKE操作符,以通配符开头的查询(例如:%abc
)索引将无法使用。- 非前缀查询:如果在查询中使用了非前缀查询,如
WHERE column LIKE '%abc'
,索引将无法使用。 - 数据类型不匹配:如果索引列和查询条件的数据类型不匹配,索引将无法使用。
- 隐式转换:如果在查询中使用了隐式转换,如
WHERE char_column = 1
,索引将无法使用。 - 多列索引:如果在查询中只使用了多列索引中的一部分列,索引将无法使用。
- 不等式查询:如果在查询中使用了不等式操作符,如
WHERE column <> 100
,索引将无法使用。 - 大量重复值:如果索引列中有大量重复的值,索引的效率将大降低。
- 内存不足:如果索引数据无法完全加载到内存中,索引的效率将大降低。
ORDER BY
、GROUP BY
、DISTINCT
查询:如果在查询中使用了ORDER BY
、GROUP BY
、DISTINCT
等操作符,索引将根据操作符的顺序进行排序,可能会导致索引失效。- 如果在查询中涉及到多表关联查询,并且没有正确地设置索引,可能会导致索引失效。
- 存在隐含条件:如果在查询中存在隐含条件,如
WHERE column IS NOT NULL
,索引将无法使用。 - 数据分布不均匀:如果索引列中数据的分布不均匀,如某些值的数据量特别大,索引的效率将大降低。
- 索引列过长:如果索引列的长度过长,可能会导致索引失效。
- 更新频繁:如果索引列经常需要更新,索引的效率将大降低。
- 索引类型不匹配:如果索引类型和查询条件的类型不匹配,索引将无法使用。
- 并发访问:如果同一时间有多个并发访问请求,可能会导致索引失效。
- 查询语句过于复杂:如果查询语句过于复杂,可能会导致索引失效。因为
MySQL
在执行查询时需要进行优化和解析,如果查询语句过于复杂,可能会导致MySQL
无法正确地使用索引。 - 索引列类型不一致:如果多个索引列的类型不一致,可能会导致索引失效。
- 索引列顺序不正确:如果多个索引列的顺序不正确,可能会导致索引失效。
- 字符集不一致:如果索引列的字符集和查询条件的字符集不一致,可能会导致索引失效。
- 存在大量
NULL
值:如果索引列中存在大量NULL
值,可能会导致索引失效。 - 存在大量重复的索引值:如果索引列中存在大量重复的索引值,可能会导致索引失效。
- 存在大量的更新操作:如果索引列经常需要更新,可能会导致索引失效。
在JAVA-OBJECT中,有些方法是使用native来修饰的,native修饰的方法有什么用?
- 有时候JAVA应用需要与java外面的环境交互,当我需要调用非java代码的接口的时候,native方法用于确定:该方法的实现由非java语言实现,比如C。
- 与java环境外交互:有时java应用需要与java外面的环境交互。这是本地方法存在的主要原因,你可以想想java需要与一些底层系统如操作系统或某些硬件交换信息时的情况。本地方法正是这样一种交流机制:它为我们提供了一个非常简洁的接口,而且我们无需去了解java应用之外的繁琐的细节。
- 与操作系统交互:JVM支持着java语言本身和运行时库,它是java程序赖以生存的平台,它由一个解释器(解释字节码)和一些连接到本地代码的库组成。然而不管怎 样,它毕竟不是一个完整的系统,它经常依赖于一些底层系统的支持。这些底层系统常常是强大的操作系统。通过使用本地方法,我们得以用java实现了jre的与底层系统的交互,甚至JVM的一些部分就是用C写的,还有,如果我们要使用一些java语言本身没有提供封装的操作系统的特性时,我们也需要使用本地方法。
在java中 什么去看看下可以用到super关键字?
- 访问父类的成员:
- 当子类与父类有同名的成员(包括变量和方法)时,子类可以通过
super
关键字来访问父类中被隐藏的成员。 - 示例:如果子类重写了父类的方法,那么在子类的方法中,你可以通过
super.methodName()
来调用父类中被重写的方法。
- 当子类与父类有同名的成员(包括变量和方法)时,子类可以通过
- 调用父类的构造器:
- 在子类的构造器中,可以通过
super()
来调用父类的构造器(无论是无参构造器还是有参构造器)。 - 注意:
super()
调用必须是子类构造器的第一条语句(如果父类没有无参构造器,并且你没有显式地调用父类的某个构造器,那么编译器会报错)。 - 示例:
super(parameter1, parameter2);
// 调用父类的有参构造器
- 在子类的构造器中,可以通过
- 在子类的方法中:
- 除了上述的调用父类方法外,
super
关键字还可以在子类的任何方法中作为对当前对象的父类引用的隐式引用。尽管这种用法不常见,但在某些复杂的继承关系中,它可以帮助你明确你正在引用的是父类版本的方法或变量。
- 除了上述的调用父类方法外,
- 在初始化块中:
- 类似于构造器,初始化块(无论是静态初始化块还是实例初始化块)也可以使用
super()
来调用父类的构造器(但通常只在实例初始化块中这样做,因为静态初始化块与类相关,而不是与类的实例相关)。
- 类似于构造器,初始化块(无论是静态初始化块还是实例初始化块)也可以使用
- 解决隐藏问题:
- 当子类继承父类,并且子类定义了与父类同名的字段时,子类的字段会隐藏父类的字段。此时,可以通过super关键字来访问父类中被隐藏的字段。
- 在子类的静态方法中:
- 需要注意的是,
super
关键字不能用于静态上下文中直接引用父类的静态成员(如静态变量或静态方法)。静态成员属于类级别,而不是实例级别,因此不需要通过super
来访问。你可以直接使用类名来访问它们。
- 需要注意的是,
在JAVA中 List和Set的区别
- 元素唯一性
- Set:Set接口的实现类(如HashSet、LinkedHashSet、TreeSet等)中的元素是无序的且唯一的。尝试向Set中添加一个已经存在的元素,则该元素不会被添加,集合的大小也不会改变。
- List:List接口的实现类(如ArrayList、LinkedList、Vector等)可以包含重复的元素。List集合可以包含多个相同的元素,元素的顺序与添加顺序一致。
- 元素的顺序
- Set:如上所述,Set集合不保证元素的顺序(虽然LinkedHashSet按添加顺序遍历元素,但这仍然被认为是Set的一个特殊实现)。
- List:List集合是一个有序的集合,它可以按元素被添加到集合中的顺序来遍历这些元素。
- 功能和方法
- Set:主要提供了add(E e)、remove(Object o)、contains(Object o)等方法,由于集合无序,所以没有直接用于访问元素的索引方法(尽管某些实现如LinkedHashSet可能允许按添加顺序遍历)。
- List:除了提供Set中已有的方法外,List还提供了按索引访问元素的方法,如get(int index)、set(int index, E element)等。List还支持在列表中任意位置插入或删除元素,以及使用ListIterator进行向前或向后遍历和修改元素。
- 应用场景:
- Set:适用于不需要考虑元素顺序,且需要保证元素唯一性的场景。
- List:适用于需要保留元素添加顺序,或者集合中可能存在重复元素的场景。
- 性能
- 性能方面的差异主要取决于具体的实现类及其内部机制。例如,HashSet通常比ArrayList在插入和查找操作上提供更快的性能,因为它基于哈希表实现,而ArrayList基于动态数组实现。然而,在需要按索引访问元素时,ArrayList将提供更快的性能。
在线程开发中 我们可能会出现几个线程需要对统一的属性做变更 有什么手段能够让A线程知道B线程修改了某个属性的值?
在线程开发中,当多个线程需要访问和修改共享资源(如某个属性的值)时,确保线程安全和数据一致性是非常重要的。有几种机制可以帮助线程间同步和通信,以便一个线程(如B线程)能够通知其他线程(如A线程)它已经修改了某个属性的值。以下是一些常用的方法:
- 锁(Locks):使用锁(如Java中的synchronized关键字或ReentrantLock)可以确保在任一时刻只有一个线程可以访问共享资源。当一个线程(B线程)修改了共享属性后,其他线程(如A线程)在重新访问该属性之前需要等待锁被释放。这确保了A线程在访问属性时会看到最新的值。
- 条件变量(Condition Variables):条件变量通常与锁一起使用,用于线程间的显式通信。一个线程(B线程)在修改共享属性后,可以通过条件变量来通知等待该条件的其他线程(如A线程)。等待的线程会阻塞在条件变量上,直到它们被另一个线程显式地唤醒。
- 原子变量(Atomic Variables):对于简单的变量修改,可以使用原子变量(如Java中的AtomicInteger、AtomicReference等)来确保操作的原子性。虽然原子变量本身不提供直接的线程间通知机制,但它们确保了变量修改的原子性和可见性,减少了锁的需求。
- 显式通知(Explicit Notification):在某些情况下,可以使用专门的同步机制(如Java中的wait()和notify()或notifyAll()方法)来显式地通知其他线程。当一个线程(B线程)修改了共享属性后,它可以调用notifyAll()来唤醒所有等待该锁的线程,这些线程(如A线程)在重新获取锁后会检查共享属性的值。
- 读写锁(Read-Write Locks):如果多个线程经常读取但很少修改共享资源,可以使用读写锁来提高性能。读写锁允许多个线程同时读取共享资源,但只允许一个线程写入。当一个线程(B线程)写入资源后,其他等待读取或写入的线程会收到通知。
- 消息队列(Message Queues):使用消息队列是另一种线程间通信的方式。一个线程(B线程)可以将修改通知作为消息发送到队列中,其他线程(如A线程)可以监听队列以接收这些通知。这种方法适用于更复杂的场景,其中线程间通信不仅限于简单的属性修改。
- 使用事件或监听器(Events or Listeners):在一些框架中,可以通过注册事件监听器来实现线程间的通信。当一个线程(B线程)修改了共享资源时,它可以触发一个事件,其他线程(如A线程)通过监听这些事件来响应。
在JAVA中 在项目启动时我需要执行一些自定义方法 我可以怎么操作?
- 在
main
方法中编写一些需要执行的内容; - 如果使用的是Spring框架 可以在bean类上使用@PostConstruct注解来标记一个方法,这个方法会在Spring容器创建该Bean之后、属性被设置之后被自动调用。
import javax.annotation.PostConstruct; @Component public class StartupBean { @PostConstruct public void init() { // 在这里执行初始化代码 } }
Spring支持哪些事务?
- 编程式事务管理
通过 TransactionTemplate
或者TransactionManager
手动管理事务,实际应用中很少使用,但是对于你理解 Spring 事务管理原理有帮助。
使用TransactionTemplate
进行编程式事务管理的示例代码如下:
@Autowired
private TransactionTemplate transactionTemplate;
public void testTransaction() {
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
try {
// .... 业务代码
} catch (Exception e){
//回滚
transactionStatus.setRollbackOnly();
}
}
});
}
使用 TransactionManager
进行编程式事务管理的示例代码如下:
@Autowired
private PlatformTransactionManager transactionManager;
public void testTransaction() {
TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
try {
// .... 业务代码
transactionManager.commit(status);
} catch (Exception e) {
transactionManager.rollback(status);
}
}
- 声明式事务管理
推荐使用(代码侵入性最小),实际是通过 AOP 实现(基于@Transactional
的全注解方式使用最多)。
使用 @Transactional
注解进行事务管理的示例代码如下:
@Transactional(propagation = Propagation.REQUIRED)
public void aMethod {
//do something
B b = new B();
C c = new C();
b.bMethod();
c.cMethod();
}
在Mybatis的xml文件中 #{}和${}有什么区别?
#{}
(预处理占位符)
- SQL注入防护:
#{}
是预处理(Prepared Statement)的占位符,MyBatis会将其替换为?
,并使用PreparedStatement的set方法来设置参数值。这种方式可以有效防止SQL注入攻击,因为参数值在SQL执行前就已经被设置,并且作为SQL语句的一部分发送给数据库,而不是直接拼接在SQL语句中。 - 类型处理:MyBatis会根据参数的类型自动进行必要的类型转换和引用处理。例如,如果参数是Java对象,MyBatis会尝试调用其getter方法来获取属性值;如果参数是基本类型或包装类型,MyBatis会直接使用该值。
- 用法:在XML映射文件中,
#{}
后面通常跟的是参数名(可以是#{parameterName}
,也可以是#{0}
、#{1}
等索引形式,取决于mapper接口方法的参数类型)。
${}
(字符串替换)
- SQL注入风险:
${}
是字符串替换的占位符,MyBatis会直接将参数值拼接到SQL语句中。这种方式存在SQL注入的风险,因为如果参数值中包含SQL关键字或特殊字符,它们可能会被数据库错误地解释和执行。因此,在使用${}
时需要格外小心,确保参数值是可信的或经过适当的清理。 - 类型处理:由于
${}
是直接进行字符串替换,因此不会进行任何类型处理或转换。参数值将直接作为字符串拼接到SQL语句中。 - 用法:在XML映射文件中,
${}
后面跟的是参数名或表达式。它常用于动态表名、动态列名等场景,因为这些信息通常不是通过预处理语句来设置的。
HTTP常用状态码
状态码区间 | 类别 | 原因短语 |
---|---|---|
1XX | Infomational(信息性状态码) | 接收的请求正在处理 |
2XX | Success(成功状态码) | 请求正常处理完毕 |
3XX | Redirection(重定向状态码) | 需要进行附加操作以完成请求 |
4XX | Client ERROR(客户端错误状态码) | 服务器无法处理请求 |
5XX | Server ERROR(服务器错误状态码) | 服务器处理请求出错 |
常用状态码
- 2XX 成功状态码
状态码 | 状态码原因 |
---|---|
200 | ok(请求成功) |
204 | no content (请求成功,但是没有结果返回) |
206 | partial content (客户端请求一部分资源,服务端成功响应,返回一范围资源) |
- 3XX 重定向状态码
状态码 | 状态码原因 |
---|---|
301 | move permanently (永久性重定向) |
302 | found (临时性重定向) |
303 | see other (示由于请求对应的资源存在着另一个 URI,应使用 GET方法定向获取请求的资源) |
304 | not modified (表示在客户端采用带条件的访问某资源时,服务端找到了资源, 但是这个请求的条件不符合。跟重定向无关) |
307 | temporary redirect (临时性重定向) |
- 4XX 客户端错误状态码
状态码 | 状态码原因 |
---|---|
400 | bad request (请求报文存在语法错误) |
401 | unauthorized (需要认证(第一次返回)或者认证失败(第二次返回)) |
403 | forbidden (请求被服务器拒绝了) |
404 | not found (服务器上无法找到请求的资源) |
- 5XX 服务器错误状态码
状态码 | 状态码原因 |
---|---|
500 | internal server error (服务端执行请求时发生了错误) |
503 | service unavailable (服务器正在超负载或者停机维护,无法处理请求) |
Mysql在什么情况下会用到Having?
在MySQL中,HAVING
子句主要用于对GROUP BY
语句产生的分组结果进行过滤。HAVING
子句的作用与WHERE
子句相似,都是用于设置条件过滤记录,但它们在应用上存在关键区别:
WHERE
子句:在数据分组前对原始记录进行过滤,即先过滤后分组。它不能用于对聚合函数(如COUNT()
,SUM()
,AVG()
,MAX()
,MIN()
等)的结果进行过滤。HAVING
子句:在数据分组后对分组结果进行过滤,即先分组后过滤。它主要用于对聚合函数的结果进行条件过滤。
因此,在以下情况下,你会使用到HAVING
子句:
- 当你需要基于聚合函数的结果来过滤数据时:比如,你想查询销售记录,但只关心那些销售额超过某个特定值的销售区域或产品。
- 当你使用
GROUP BY
语句对数据进行分组,并希望进一步筛选这些分组时:例如,你可能想要查询每个部门的平均销售额,但只关心那些平均销售额超过一定水平的部门。 - 当
WHERE
子句不能满足你的过滤需求时:比如,你的过滤条件涉及到了聚合函数的结果。
MySQL 中的 BLOB
和 TEXT
类型有什么区别?
BLOB 类型
BLOB
(Binary Large OBject)类型用于存储二进制数据,如图片、音频、视频等文件。BLOB
类型有几个变种,根据能够存储的数据量大小不同,它们分别是:
TINYBLOB
:最大长度为 255 字节。BLOB
:最大长度为 65,535 字节(64KB)。MEDIUMBLOB
:最大长度为 16,777,215 字节(16MB)。LONGBLOB
:最大长度为 4,294,967,295 字节(4GB)。
特点:
- 存储的是二进制数据,不进行字符集编码转换。
- 适用于存储图片、音频、视频等文件。
TEXT 类型
TEXT
类型用于存储非二进制字符串(文本),如文章、长文本描述等。TEXT
类型也有几个变种,以适应不同长度的文本数据:
TINYTEXT
:最大长度为 255 字节。TEXT
:最大长度为 65,535 字节(64KB)。MEDIUMTEXT
:最大长度为 16,777,215 字节(16MB)。LONGTEXT
:最大长度为 4,294,967,295 字节(4GB)。
特点:
- 存储的是文本数据,根据字符集进行编码转换。
- 适用于存储长文本,如文章、邮件等。
BLOB 与 TEXT 的主要区别
- 数据类型:
BLOB
存储的是二进制数据,而TEXT
存储的是非二进制字符串。 - 字符集:
BLOB
不进行字符集编码转换,而TEXT
类型的数据会根据字符集进行编码转换。 - 用途:
BLOB
更适合存储图片、音频、视频等文件,而TEXT
更适合存储文本数据,如文章、长文本描述等。 - 排序和比较:由于
TEXT
类型的数据会进行字符集编码转换,因此TEXT
类型的列可以进行排序和比较操作,而BLOB
类型的列则不能。
总结
选择 BLOB
还是 TEXT
类型主要取决于你要存储的数据类型和用途。如果你需要存储二进制数据(如文件),那么应该选择 BLOB
类型;如果你需要存储长文本数据(如文章),那么应该选择 TEXT
类型。