JAVA面试圣经

一.DDD设计模型

1.把业务的操作写到实体中(这个Account就是一个充血模型)
在这里插入图片描述
这么做的好处:
在未来功能增多的情况下,可以知道对于这个账户的所有业务操作都可以从这一个Account实体中了解到。如果使用之前的思想MVC 把对Account的业务操作都写在service或者controller中的话,随着时间的推移,业务的迭代我们程序员不能很清楚的知道对Account类的所有业务操作

2.抽象出只与数据库打交道的repository层
在这里插入图片描述
3.防腐层 隔离外部服务
在这里插入图片描述

一.构造器可否被重写

答:不可以

一.什么是乐观锁,悲观锁,自旋锁,排他锁,共享锁,统一锁,分段锁

  • 乐观锁:乐观锁是一种并发控制策略,它假设事务之间不会产生冲突,因此在读取数据时不会上锁。当更新数据时,会先检查数据是否被其他事务修改过。如果没有修改,则可以直接更新;如果有修改,则需要回滚当前事务或重新尝试。
  • 悲观锁:悲观锁是一种并发控制策略,它假设事务之间会产生冲突,因此在读取数据时会上锁,阻止其他事务修改数据。只有当前事务完成后,其他事务才能获取锁并继续操作。
  • 自旋锁:自旋锁是一种基于忙等待的锁机制,当线程尝试获取锁时,如果锁已经被其他线程持有,该线程将循环(自旋)等待直到锁被释放。这种锁适用于短暂的临界区域和较少的竞争情况。
  • 排他锁:排他锁是一种独占锁,也称为写锁。它允许一个线程独占资源,在该线程释放锁之前,其他线程无法访问该资源。
  • 共享锁:共享锁是一种共享访问机制,也称为读锁。它允许多个线程同时访问资源,但不允许写操作。当一个线程持有共享锁时,其他线程也可以获取共享锁,但无法获取排他锁。
  • 统一锁:统一锁是一种综合了排他锁和共享锁的锁机制。它具有两种模式:共享模式和排他模式。在共享模式下,多个线程可以同时获得锁并共享资源;在排他模式下,只有一个线程可以获得锁。
  • 分段锁:分段锁是一种将数据划分成多个部分,并为每个部分分配一个锁的机制。这样可以提高并发性能,因为不同的线程可以同时访问不同的数据段而不会产生冲突。分段锁常用于数据库等需要处理大量事务的系统中。

二.写出java反射的3种方法

1.Class<?> class = Myclass.class();
2.Class<?> class = Class.forName("com.example.Myclass");
3.Class<?> class = obj.getClass();

三.如何决定使用HashMap还是TreeMap

1.数据结构

  • HashMap底层数据结构是哈希表:适用于快速的增加,删除和查询。他的存储和检索速度通常比TreeMap更快,平均时间复杂度为O(1)。但是不保证元素的有序性。

  • TreeMap底层是红黑树:它保证了元素的有序性,一般适用于有序遍历的场景。可以按照键的自然顺序或自定义比较器进行排序。插入、删除和查找操作的时间复杂度为O(log n)。

2.键的唯一性

  • HashMap允许键的重复,每个键对应一个值。
  • TreeMap要求键唯一,如果插入重复键的元素,后面的元素将会覆盖前面的。

3.内存消耗

  • HashMap使用哈希表,它需要额外的内存来存储哈希桶和链表(或红黑树)。相比之下,TreeMap使用红黑树来存储元素,需要更多的内存空间。

四.HashMap是怎么解决哈希冲突

链地址法

  • 将具有相同哈希值的键值对存储在同一个槽位的链表或红黑树中。这种方式可以高效地处理冲突,保证了HashMap的插入、查找和删除操作的性能。

在这里插入图片描述

五.常用的集合类有哪些,并展开讲讲

六.如何确保一个集合不能被修改

七.什么叫对象序列化,什么叫反序列化,实现对象序列化需要哪些工作

八.String,StringBuffer,StringBuilder的区别


如果有一个String “abc”
1.String是指向了一个"abc"的内存地址"abc"对象不可变,StringBuffer和StringBuilder是指向了一个数组
如果要在后面加英文字母,那么StringBuffer和StringBuilder效率就高
StringBuffer是线程安全的

九.springMVC的工作原理

在这里插入图片描述

十.ConcurrentHashMap和HashTable的区别

2个都是线程安全的
但是HashTable是只有一把锁,有十个线程竞争锁的时候,只要有一个线程的到锁,其他九个线程就需要阻塞等待ConcurrentHashMap是分段锁,降低了我们锁冲突的一个概率
ConcurrentHashMap对读操作不加锁,只对写操作进行加锁。

十一.volatile关键的的效果


在这里插入图片描述
对于成员变量i,它存储在堆内存中。每个线程在运行时都会有一个自己的线程栈,线程如果要访问类的成员变量i,会通过引用获取到堆中变量i实际的值10,然后把这个变量值拷贝到自己的栈内存中,作为一个变量副本,之后线程便不再会与堆中的变量有实际联系。每个线程都有一个自己的本地副本,相互隔离。线程访问自己栈内存的效率比访问堆的效率高。线程对变量i值的修改,只会修改自己线程副本中的值,修改结束后,在线程退出前,会把自己线程副本中的值,刷新到堆中。
大多数时候可以正常中断,但是一旦发生异常,便会导致线程死循环。所以需要在flag标志上加一个volatile关键字。对于加了volatile关键字的变量值,线程1修改了这个值的话,会强制将修改值直接写入堆内存中,其他线程各自线程栈中的变量副本无效,只能去堆中取最新的变量值。多个线程之间的内存可见得以保证。

值得注意的是,volatile关键字不能保证原子性。

十二.mysql锁

  1. 共享锁(Shared Lock):允许多个读操作并行执行,但不允许任何写操作,即其他线程不能修改此数据,直到共享锁被释放为止。
  2. 排它锁(Exclusive Lock):允许写操作进行,但不允许其他线程的任何读或写操作,即其他线程不能同时对同一数据进行修改,直到排它锁被释放为止。
  3. 行锁(Row Lock):锁定表中的单行数据,允许读/写操作,其他行不受影响,提高了并发性和效率。
  4. 间隙锁(Gap Lock):​

锁的就是两个值之间的空隙。Mysql默认级别是repeatable-read,有办法解决幻读问题吗?间隙锁在某些情况下可以解决幻读问题。
假设account表里数据如下:

在这里插入图片描述

那么间隙就有id为(3,10),(10,20),(20,正无穷)这三个区间,
在Session_1下面执行update account set name='zhuge’where id>8 and id<18;,则其他Session没法在这个范围所包含的所有行记录以及行记录所在的间隙里插入或修改任何数据,即id在(3,20]区间都无法修改数据,注意最后那个20也是包含在内的。
间隙锁是在可重复读隔离级别下才会生效。

如:

  1. 元数据锁(Metadata Lock):用于保护数据字典表,以防止操作不当造成损坏。
  2. 意向锁(Intention Lock):表示事务正在获取数据对象的共享锁或排它锁,并提高了锁定效率。
  3. 记录锁(Record Lock):用于对表中某一行/记录进行锁定,防止其他线程对该行数据进行修改操作。
  4. 插入意向锁(Insert Intention Lock):在一次事务中,先对索引上相应的间隙置上意向锁,再在该间隙内进行插入操作。
  5. 自适应锁(Adaptive Locking):根据访问数据的情况调整锁定策略,提高并发性能。

十二.mysql

1、读未提交内容(read-uncommitted)

在该隔离级别中,所有事务都可以看到其他未提交事务的执行结果。本隔离级别很少用于实际应用,因为它的性能也不比其他级别好多少。

该隔离级别会出现的问题是:脏读(Dirty Read),即读取到了未提交的数据。

2、读取提交内容(read-committed)

这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)。它满足了隔离的简单定义:一个事务只能看见已经提交事务所做的改变。

该隔离级别会出现的问题是:不可重复读(Nonrepeatable Read),即不可重复读意味着我们在同一个事务中执行完全相同的select语句时可能看到不一样的结果。

导致这种情况的原因可能有:

1)、有一个交叉的事务有新的commit,导致了数据的改变;

2)、一个数据库被多个实例操作时,同一事务的其他实例在该实例处理其间可能会有新的commit

3、可重复读(repeatable-read)

这是MySQL的默认事务隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行。

不过理论上,这会导致另一个棘手的问题:幻读 (Phantom Read)。

简单的说,幻读指当用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的“幻影” 行。

InnoDB和Falcon存储引擎通过多版本并发控制(MVCC,Multiversion Concurrency Control)机制解决了该问题。

4、可串行化(serializable)

这是最高的隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。在这个级别,可能导致大量的超时现象和锁竞争。

在这个级别,可能导致大量的超时现象和锁竞争。

十二.mysql索引优化原则

1.null值,<>或!=,%a%, 索引列上的sum()这种计算 会导致不走索引要尽量避免
2最左前缀匹配原则,Mysql会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配,
比如a=3 and b=4 and c>5 and d=6如果建立(a,b,c,d)顺序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引则都可以用到,a,b,d的顺序可以任意调整。
=和in可以乱序,比如a=1 and b=2 and c=3建立(a,b,c)索引可以任意顺序,mysql的查询优化器会帮你优化成索引可以识别的形式
3.索引字段最好小而且唯一,避免select * 的情况

十三.mysql如何让like的%%走索引


转载: https://blog.csdn.net/qq_43575674/article/details/109330779

十三.mysql 聚簇索引和非聚簇索引区别

在这里插入图片描述
在innodb中主键采用的就是聚簇索引,并且一个表只有一个聚簇索引
聚簇索引(主键索引):就是b+树的叶子节点存放的数据是表的所有的数据
辅助键索引:就是我们一般数据库中添加的索引,它的叶子节点存储的是索引的列值和主键id
所以 如果走辅助键索引的话,它要查询2次,直接走主键索引的话就只需要查询一次即可

聚簇索引的优点
1.当你需要取出一定范围内的数据时,用聚簇索引也比用非聚族索引好。
2.当通过聚族索引查找目标数据时理论上比非聚族索引要快,因为非聚族索引定位到对应主键时还要多一次目
标记录寻址,即多一次I/O。
3.使用覆盖索引扫描的查询可以直接使用页节点中的主键值。
聚簇索引的缺点
1插入速度严重依赖于插入顺序。
2更新主键的代价很高,因为将会导致被更新的行移动。
3.二级索引访问需要两次索引查找,第一次找到主键值,第二次根据主键值找到行数据。

在这里插入图片描述

十四.cookie和session的区别,以及如何进行用户验证的

区别:cookie是存在浏览器端的,session是存在服务器端的
用户登录的时候通过cookie将用户的认证信息(账号密码)发送给服务器,服务器通过对比数据库中用户的账号密码,将用户的信息存在session中,并把sessionId通过cookie传给浏览器。下次用户登录的时候,通过这个sessionId来查看用户是否登录

基于session的
在这里插入图片描述

基于Token的
在这里插入图片描述

BufferedReader属于哪种流,主要是用来干什么的?有哪些经典的方法

Java中流的超类主要有哪些?

sleep和wait的区别

sleep()是静态方法,用于将线程睡眠,将执行状态变成显示阻塞状态,并且不会释放锁
wait()需要在synchronized{}代码块中使用,他会释放锁,并且只有在notify(),notifyall()方法后才可以停止阻塞去争夺锁

线程池创建线程的七个参数是什么?

  • corePoolSize:核心线程数,也是线程池中常驻的线程数,线程池初始化时默认是没有线程的,当任务来临时才开始创建线程去执行任务

  • maximumPoolSize:最大线程数,在核心线程数的基础上可能会额外增加一些非核心线程,需要注意的是只有当workQueue队列填满时才会创建多于corePoolSize的线程(线程池总线程数不超过maxPoolSize)

  • keepAliveTime:非核心线程的空闲时间超过keepAliveTime就会被自动终止回收掉,注意当corePoolSize=maxPoolSize时,keepAliveTime参数也就不起作用了(因为不存在非核心线程);

  • unit:keepAliveTime的时间单位

  • workQueue:用于保存任务的队列,可以为无界、有界、同步移交三种队列类型之一,当池子里的工作线程数大于corePoolSize时,这时新进来的任务会被放到队列中

  • threadFactory:创建线程的工厂类,默认使用Executors.defaultThreadFactory(),也可以使用guava库的ThreadFactoryBuilder来创建

  • handler:线程池无法继续接收任务(队列已满且线程数达到maximunPoolSize)时的饱和策略,取值有AbortPolicy、CallerRunsPolicy、DiscardOldestPolicy、DiscardPolicy

线程池中的线程创建流程图?

在这里插入图片描述

Tomcat线程池和java原生线程池的区别是什么?

tomcat线程池如果当前运行的线程,少于corePoolSize,则创建一个新的线程来执行任务。如果线程数大于 corePoolSize了,Tomcat 的线程不会直接把线程加入到无界的阻塞队列中,而是去判断submittedCount(已经提交线程数)是否等于 maximumPoolSize。如果等于,表示线程池已经满负荷运行,不能再创建线程了,直接把线程提交到队列,如果不等于,则需要判断,是否有空闲线程可以消费

有3个线程T1,T2,T3,如何保证他们按照T2->T1->T3的顺序执行

try-catch-finally中,如果catch中return了。finally还会执行吗?


答:会

异常会影响性能吗?


答:会

如何停止中断运行中的线程?三大方法

  • 通过volatile关键字
  • 通过AtomicBoolean类型 下面的代码包括了AtomicBooleanvolatile
public class DeadLock {
   static volatile  boolean isStop = false;
   static AtomicBoolean atomicBoolean = new AtomicBoolean(false);  //原子布尔型
    public static void main(String[] args) throws Exception{

    new Thread(()->{
        while (true){
            if(isStop /*atomicBoolean.get()*/){
                System.out.println("线程1线程停止了");
                break;
            }
            System.out.println("线程1执行中---");
        }
    }).start();


    TimeUnit.SECONDS.sleep(1);

    new Thread(()->{  // 通过线程2修改可见的isStop来对线程1进行中断
        isStop=true;
        // atomicBoolean.set(true);
    }).start();
    }
}
  • 通过Thread类自带的中断api实现
public class DeadLock {
    public static void main(String[] args) throws Exception{
    Thread t1 = new Thread(()->{
        while (true){
            if(Thread.currentThread().isInterrupted()){
                System.out.println("线程1线程停止了");
                break;
            }
            System.out.println("线程1执行中---");
        }
    });
    t1.start();



    TimeUnit.SECONDS.sleep(1);

    new Thread(()->{  
        t1.interrupt();
    }).start();
    }
}

当前线程的中断标识为true,是不是线程就立刻停止了?

  • 答:不是

事例1:

public class DeadLock {
    public static void main(String[] args) throws Exception{
    Thread t1 = new Thread(()->{
            for (int i = 0; i < 300; i++) {
                System.out.println("------"+i);
            }
    });
    t1.start();
    
    TimeUnit.MILLISECONDS.sleep(1);
        System.out.println("线程1是否被打断情况"+t1.isInterrupted()); //false


    new Thread(()->{  // 通过线程2修改可见的isStop来对线程1进行中断
        t1.interrupt(); // true
        System.out.println("线程1的是否被打断情况"+t1.isInterrupted()); //true
    }).start();

        TimeUnit.MILLISECONDS.sleep(2000);
        System.out.println("线程1是否被打断情况"+t1.isInterrupted()); //false    中断不活动的线程不会产生任何影响

    }

事例2:

public class DeadLock {
    public static void main(String[] args) throws Exception {
        Thread t1 = new Thread(() -> {
            while (true) {
                System.out.println("程序运行中");
                if (Thread.currentThread().isInterrupted()) {
                    System.out.println("线程t1被中断了,中断标识位:" + Thread.currentThread().isInterrupted());
                    break;
                }

                try {
                    Thread.sleep(200); //sleep方法会在别的线程中断当前线程的时候报出InterruptedException,并将中断标识清空置为false。就会导致死循环
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();  //这行代码是为了防止线程报错 而没有修改中断标识位的值为true  而造成死循环
                    e.printStackTrace();
                }
            }

        });
        t1.start();

        TimeUnit.SECONDS.sleep(1);

        new Thread(() -> {  // 通过线程2修改可见的isStop来对线程1进行中断
            t1.interrupt(); // true
        }).start();
    }
}

@TOC

它的作用是:1.用来判断线程是否被中断2.将当前线程的中断状态清零并重新设为false,清除当前中断状态

代码演示

public class DeadLock {
    public static void main(String[] args) throws Exception {

        System.out.println(Thread.currentThread().getName()+"线程的中断状态:"+Thread.interrupted());  //false
        System.out.println(Thread.currentThread().getName()+"线程的中断状态:"+Thread.interrupted());  //false

        Thread.currentThread().interrupt();

        System.out.println(Thread.currentThread().getName()+"线程的中断状态:"+Thread.interrupted());  //true
        System.out.println(Thread.currentThread().getName()+"线程的中断状态:"+Thread.interrupted());  //false

}

}

Thread.interrupted()方法和isInterrupted()方法底层都调用了isInterrupted()方法(见p2,而是否清除中断状态取决于入参是否为true)

在这里插入图片描述
在这里插入图片描述

一.重载和重写的区别

对象什么时候可以被垃圾回收

根据根可达性算法 如过从根节点出发,一直查询相关的引用对象,没有被引用的对象就直接被垃圾回收

在Java语言中,可以作为GCRoots的对象包括下面几种:

(1). 虚拟机栈(栈帧中的局部变量区,也叫做局部变量表)中引用的对象。

(2). 方法区中的类静态属性引用的对象。

(3). 方法区中常量引用的对象。

(4). 本地方法栈中JNI(Native方法)引用的对象。

什么是java的堆

Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。

JVM双亲委派机制


在这里插入图片描述
BootStrap ClassLoader是核心jar包
Extension ClassLoader是

在加载,查找一个.class文件的时候首先会把这个任务委派给父加载器去加载,如果父加载失败,就再给下一级去加载。
这么做的好处:
1、避免类重复加载
2、安全性 如果你自己写了一个java.lang.String 就无法覆盖类库中的类

如何打破双亲委派机制


Tomcat实际上是一个java工程,在tomcat中有一个webApp的文件夹,可以存放多个项目运行的jar包。举例,如果项目a使用了spring5.1 项目b使用了spring6.0 那么这2个项目如果他们在加载spring相关类的过程中,如果采用双亲委派机制的话,项目a如果先加载了spring5.1的内容,那么项目b加载类的过程中只能加载到spring5.1的内容就会出现问题。所以tomcat在对于每个项目的私有jar包在类加载的过程中就会打破双亲委派不让父类去加载

一.重载和重写的区别

Spring中@Component,@Controller,@Repository,@Service的区别

哪些是重要的bean生命周期方法,你能重载他们吗?

比较SpringSecurity和Shiro的各自优缺点

SpringBoot中如何解决跨域问题

如何解决Post请求中文乱码的问题,Get的又如何处理

SpringMvc常用注解有哪些?

怎样在方法里得到Request或者Session

简述SpringCloud的优缺点

Ribbon有什么用

对高并发的接口做什么处理?

1.服务限流
2.使用消息队列
3.服务降级
4.热点数据,缓存在缓存中间件中(问卷信息)
5.读写分离
6.负载均衡

在微服务中如何保护服务

使用SpringBoot开发分布式微服务,我们面临什么问题

简述Redis与Memcached的区别

Redis是单线程的如何提高多核CPU利用率

Redis如何做缓存预热的

Redis如何保证数据的一致性


1.redis是单线程的,没有并发
2.哨兵模式,它是一个单独的线程,当主节点出现故障可以在剩下的节点中选举出一个节点当主节点

代码层面:
1.通过canal 异步同步数据
2.通过延时双删(先删除缓存,更新数据库,休眠一段时间,删除缓存)

Redis的数据类型

字符串(string) 缓存数据:将经常使用的数据存储在 Redis 中,可以提高访问速度。计数器:可以使用 Redis 的自增命令实现计数器功能。分布式锁:可以使用 Redis 的 SETNX 命令实现分布式锁。
哈希(hash) 存储对象:可以将对象的各个属性存储在哈希中,方便查询和修改。 用户对象
列表(list) 消息队列:可以使用列表实现简单的消息队列。
集合(set) 去重:可以使用集合存储重复的数据,然后使用 SREM 命令删除重复数据。标签系统:可以使用集合存储文章的标签,然后根据标签查询相关文章。 推荐系统:可以使用集合存储用户的喜好,然后根据相似度推荐相关内容。
有序集合(zset) :排行榜:可以使用有序集合存储用户的得分,然后根据得分排序。数据分析:可以使用有序集合存储数据的分值,然后根据分值查询相关数据。推荐系统:可以使用有序集合存储用户的喜好和权重,然后根据相似度推荐相关内容。

传统JDBC开发存在什么问题

Mybatis能执行一对多,一堆一的联系查询吗?有哪些实现方法

Mybatis都有哪些Executor执行器?他们之间的区别是什么

Hash索引和B+树索引有什么区别和优劣

数据库有什么优化方式

Mysql权限相关的表都有哪些?

mysql中varchar和char的区别

简述Socket通讯的过程

RabbitMQ消息如何分发

如何确保消息正确的发送至RabbitMQ?如何确确保消息接收方消费了消息

数据库有什么方法处理海量数据

数据库分库分表,何时分,如何分

如何设计一个秒杀/抢券系统?

如何提高抢券系统的性能?

如何提高系统的并发能力?

一.重载和重写的区别

1.重载是定义相同的方法名,参数不同;重写是子类重写父类的方法。

2.重载是编译时多态,重写是运行时多态

3.重载的入参和反参可以改变,重写的入参和反参要和父类保持一致

4.重载是在一个类中的,重写是在父类和子类中的

5.重载对访问的修饰没有要求,重写访问修饰符的限制一定要大于被重写方法的访问修饰符。(父类是public 子类可以是public和protected, 父类是protected子类重写可以用public或protected,但不可以使用private)

二.读写分离实现步骤

1.配置主从复制:建立主数据库(写库),建立从数据库(读库),并让读库去同步主库的binlog日志与主库数据保持一致

2.独写切分:在应用中明确读操作和写操作的分离。根据业务需求将读操作发送到从数据库,写操作发送到主数据库

三.MQ如何保证消息可靠性

1.消息持久化:消息在发送后即使在系统故障或重启后也能有所保留

2.ack机制:生产者把消息推送给MQ之后,要求MQ返回一个确认收到消息的信息给生产者,表示消息已经成功

3.事务机制

4.消息重试:消费者处理消息报错,MQ系统可以进行消息重试

5.高可用性和冗余:通过将MQ系统部署再多个节点上,实现数据的冗余存储和高可用性,及时某个节点发生故障,消息依然可以在其他节点进行处理

6.监控和报警

四.redis四大情况

1.预加载缓存:系统高峰或者发布生产上线的时候,提前将热点数据存储到缓存中

2.缓存击穿:redis的热点数据突然失效,大量请求直接访问数据库,数据库压力剧增
解决方案:
设置合理的缓存过期时间:将热点数据的过期时间设置为较长的时间,避免过快地过期。
使用互斥锁(Mutex Lock):当一个请求发现缓存失效时,可以尝试获取一个互斥锁,然后再从后端存储系统中加载数据并更新缓存。其他请求在这个过程中会等待,并共享加载后的数据。
预先加载热点数据:在系统启动时或非高峰期,可以预先加载一些热点数据到缓存中,避免在真正需要时才去加载。

3.缓存雪崩:redis大量缓存突然失效,导致大量查询需要访问数据库,数据库压力剧增
解决方案:
设置合理的缓存过期时间:将缓存的过期时间分散开,避免大量缓存同时失效。
引入随机过期时间:给缓存设置一个随机的过期时间,使得缓存不会同时失效。
使用缓存高可用方案:通过使用缓存集群、主从复制、哨兵或集群模式等方式,保证缓存系统的高可用性,避免单点故障导致的缓存失效问题。
数据预加载和异步更新:在缓存失效前,提前异步加载数据或者使用热点数据预加载策略,保证缓存的及时更新。
限流和熔断机制:在缓存失效时,对请求进行限流或熔断,避免请求直接访问后端存储系统的压力过大。

4.缓存穿透:整一个系统redis找不到数据,数据库也找不到数据,(恶意请求或无效数据请求绕过缓存直接访问后端存储系统)导致数据库压力剧增。
解决方案:
输入合法性验证:在接收到请求前,对请求参数进行校验,例如验证请求参数的格式、长度等。如果请求参数不合法,可以直接拒绝访问,避免查询无效数据。
布隆过滤器(Bloom Filter):使用布隆过滤器来过滤掉不存在于后端存储系统中的数据请求。布隆过滤器是一种空间效率很高的概率型数据结构,可以快速判断一个元素是否存在于集合中。

五.为什么SpringIOC要使用工厂设计模式创建Bean?

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
读者评论 前言 简介 第1章 对象导论 1.1 抽象过程 1.2 每个对象都有一个接口 1.3 每个对象都提供服务 1.4 被隐藏的具体实现 1.5 复用具体实现 1.6 继承 1.6.1 “是一个”(is-a)与“像是一个”(is-like-a)关系 1.7 伴随多态的可互换对象 1.8 单根继承结构 1.9 容器 1.9.1 参数化类型(范型) 1.10 对象的创建和生命期 1.11 异常处理:处理错误 1.12 并发编程 1.13 Java与Internet 1.13.1 Web是什么 1.13.2 客户端编程 1.13.3 服务器端编程 1.22 总结 第2章 一切都是对象 2.1 用引用操纵对象 2.2 必须由你创建所有对象 2.2.1 存储到什么地方 2.2.2 特例:基本类型 2.2.3 Java中的数组 2.3 永远不需要销毁对象 2.3.1 作用域 2.3.2 对象的作用域 2.4 创建新的数据类型:类 2.4.1 域和方法 2.4.2 基本成员默认值 2.5 方法、参数和返回值 2.5.1 参数列表 2.6 构建一个Java程序 2.6.1 名字可见性 2.6.2 运用其他构件 2.6.3 static 关键字 2.7 你的第一个J ava程序 编译和运行 2.8 注释和嵌入式文档 2.8.1 注释文档 2.8.2 语法 2.8.3 嵌入式HTML 2.8.4 一些标签示例 2.8.5 文档示例 2.9 编码风格 2.10 总结 2.11 练习 第3章 操作符 3.1 更简单的打印语句 3.2 使用Java操作符 3.3 优先级 3.4 赋值 3.4.1 方法调用中的别名问题 3.5 算术操作符 3.5.1 一元加、减操作符 3.6 自动递增和递减 3.7 关系操作符 3.7.1 测试对象的等价性 3.8 逻辑操作符 3.8.1 短路 3.9 直接常量 3.9.1 指数记数法 3.10 按位操作符 3.11 移位操作符 3.12 三元操作符 if-else 3.13 字符串操作符 + 和 += 3.14 使用操作符时常犯的错误 3.15 类型转换操作符 3.15.1 截尾和舍入 3.15.2提升 3.16 Java没有“sizeof” 3.17 操作符小结 3.18 总结 第4章 控制执行流程 4.1 true和false 4.2 if-else 4.3 迭代 4.3.1 do-while 4.3.2 for 4.3.3 逗号操作符 4.4 Foreach语法 4.5 return 4.6 break和 continue 4.7 臭名昭著的“goto” 4.8 switch 4.9 总结 第5章 初始化与清理 5.1 用构造器确保初始化 5.2 方法重载 5.2.1 区分重载方法 5.2.2 涉及基本类型的重载 5.2.3 以返回值区分重载方法 5.3 缺省构造器 5.4 this关键字 5.4.1 在构造器中调用构造器 5.4.2 static的含义 5.5 清理:终结处理和垃圾回收 5.5.1 finalize()的用途何在 5.5.2 你必须实施清理 5.5.3 终结条件 5.5.4 垃圾回收器如何工作 5.6 成员初始化 5.6.1 指定初始化 5.7 构造器初始化 5.7.1 初始化顺序 5.7.2. 静态数据的初始化 5.7.3. 显式的静态初始化 5.7.4. 非静态实例初始化 5.8 数组初始化 5.8.1 可变参数列表 5.9 枚举类型 5.10 总结 第6章 访问权限控制 第7章 复用类 第8章 多态 第9章 接口 第10章 内部类 第11章 持有对象 第12章 通过异常处理错误 第13章 字符串 第14章 类型信息 第15章 泛型 第16章 数组 第17章 容器深入研究 第18章 Java I/O系统 第19章 枚举类型 第20章 注解 第21章 并发 第22章 图形化用户界面 附录A 补充材料 可下载的补充材料 Thinking in C:Java的基础 Java编程思想 研讨课 Hands-on Java研讨课CD Thinking in Objects研讨课 Thinking in Enterprise Java Thinking in Patterns(with Java) Thinking in Patterns研讨课 设计咨询与复审 附录B 资源 软件 编辑器与IDE 书籍 分析与设计 Python 我的著作列表 索引

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值