JAVA面试基础

49 篇文章 2 订阅

java基础

Catched Exception/RuntimeException的区别

前者是代码中可预期的异常,需要用try-catch处理或者抛出,比如sql异常或者IOException。如果不catch的话会编译错误。
后者是程序运行时候的异常,这又分为两种,一种是可预期可捕获的,比如空指针NullPointer、数组越界ArrayIndexOutOf等异常。我们可以再可能出现的地方catch一下自己处理;另一种是不可预期不可补货的异常,比如线程死锁,内存溢出等。

try代码块中有return的话finally代码块中还会执行吗?

会,try代码块中如果有return的话,会先去执行finally中的代码,在进行返回。
但是,因为java中基本类型是值传递,那么finally中变更Integer值的时候并不会返回,比如
在这里插入图片描述

finally会打印13,但是返回值依旧是3
在这里插入图片描述

如果代码中是个对象,并且java对象的传递是引用传递,修改的是堆中的值向栈中的指向地址,所以,对象的值会返回finally中修改后的值,如图结果会打印 zhaoliu
在这里插入图片描述

Object类有哪些方法?

toString()、notify()唤醒、notifyAll()、wait()、hashCode()、equals()。
getClass() 获取运行时类型
toString() 转换为字符串,一般子类都会覆盖
equals() 一般要重写这个方法
notify()唤醒该对象上等待的某个线程
notifyAll()唤醒该对象上等待的所有线程
finalize()垃圾回收器释放对象所占据的内存
hashCode()用于hash查找,重写equals一般都要重写hashCode

hash表存储结构

首先是一个hash表,通过hash算出对象该存储在什么位置
在这里插入图片描述

但是不同对象的hash地址可能重复,如果过两个对象地址冲突的话,就要加上链表结构
在这里插入图片描述

为什么重写equals时要重写hashCode

当对象的值一致时,但是两个对象的hash地址可能会不一致

abstract class和interface的区别

abstract需要被继承extends 不一定实现所有方法,并且有自己的数据变量
interface需要被实现implements ,而且同一个类可以有多个implements,需要实现接口中所有方法,并且接口类中不能有自己的数据变量,除非是static final,一般都不会在接口中定义

Override,Overload,Overwrite的区别

Overload是重载,方法名一样,入参不一样,相当于两个不同的方法
Override是重写,意思是继承父类方法,方法名,入参出参都一样,但是原方法还可以用supr调用
Overwrite时重写,并且抛弃原本父类的方法,但是很多公开的权威著作都认为java中不存在Overwrite这种说法

final/finally/finalize的区别

final是修饰符,用于声明属性方法或者类表示为静态,不可变不可编辑不可覆盖不可继承
finally是异常处理结构中必须执行的代码块,用于一些必须关闭的链接之类
finalize是Object对象的默认方法,当对象回收时释放资源

简述public、protected、private和不加修饰符的访问权限

在这里插入图片描述

一致性哈希算法

一致性hash算法是将原来的hash取模之后,均匀分布的数据区,围绕成了一个圆环
那么hash取值的时候都是在这个圆环上取值,定位到某个点,顺时针行走,遇到的第一个节点,就是要存储的服务器。如图:
在这里插入图片描述

由于肯会有热点数据问题,导致大量数据涌入同一个节点,所以引入了虚拟节点的概念
在这里插入图片描述

是否用过序列化,序列化是怎么实现的

我们的电脑只能识别字节流的数据,所以我们将对象存储或者传输的时候都需要将其变为byte形式的字节数据。这个过程就是序列化,那么将字节数据转换为我们能使用打印的对象,就是反序列化
java原生序列化:Java原生流(InputStream和OutputStream之间的转化)的方式进行转化
Json序列化:一般会使用jackson包,通过ObjectMapper类来进行一些操作,比如将对象转化为byte数组或者将json串转化为对象
fastjson:是由阿里巴巴开发的一个性能很好的Java 语言实现的 Json解析器和生成器。特点速度快

什么是IO

首先要了解IO原理,我们的程序和主机系统交互的时候,肯定是需要进行数据交换的,那么这个在java或者linux系统的开发叫做输入input和读取output的处理,简称为IO读写。
这个IO读写会用到两大方法,read&write,可能操作系统不同叫法会不一样,但基本功能一样。
read系统调用是把数据从内核缓冲区复制到进程缓冲区;write系统调用是把数据从进程缓冲区复制到系统内核缓冲区。
在这里插入图片描述

在这里插入图片描述

什么是NIO

一般认为是同步非阻塞IO或者多路复用IO,他是一种基于通道channel通道和buffer缓冲区的方式。数据总是从通道Channel通道读取到Buffer缓冲区或者从缓冲区写入通道,而selector选择区可以监听多个通道的事件,所以可以使用单线程监听多个数据通道

Buffer:缓冲区,是一个用于储存特定基本类型的数据容器,除了Boolean以外,其余每一种基本类型都有一个对应的Buffer类,Buffer的子类有ByteBuffer, CharBuffer, DoubleBuffer, FloatBuffer, IntBuffer, LongBuffer, ShortBuffer 。

Channel:通道,表示到实体,比如硬件设备、文件、网络套接字或可以执行一个或多个IO操作的程序组件的开放链接。Channel常见的实现接口有FileChannel对应文件IO,DategramChannel对应UDP,SocketChannel和ServerSocketChannel对应TCP的客户端和服务器端,Channel和IO中的Stream差不多一个等级,只不过Stream是单向的比如InputStream、OutputStream。而Channel是双向的,既可以读又可以写

selector:选择器,用于监听多个通道事件,因此单个线程可以监听多个数据通道,这样就可以借助单个线程实现对数量庞大的IO通道实施监控和维护

数组

ArrayList和LinkedList的区别

ArrayList是基于动态数组的数据结构,linkedList是基于链表的数据结构
对于随机访问get和set,ArrayList优于linkedList,因为arrayList get数据的时候是直接从对应的位置上返回数据,而linkedList需要循环链表查找数据,虽然现在已经做了优化,如果大于半数的值,则从右边开始遍历,但还是比ArrayList要慢
但是add和remove操作的时候ArrayList会比linkedList慢一些。因为ArrayList主要耗时是要将所有的数据index后移,而linkedList是直接遍历找到index然后插入数据。

Array和Arrays的区别

Array是基本的存储结构,而Arrays是专门用来操作array的静态类,有equals和 Arrays.asList(array): 等方法

List和Set的区别

list允许重复的对象,set不允许重复
list是有序的容器,set是无序的,但是TreeSet也可以是有序的

ArrayList的实现原理

将数据按照下表排列,放在一个容器中,add的时候就是这样

删除数据的时候,是删除指定下标位置的数据,然后把后面的数据全都前移一位

插入数据的时候也是,先将指定位置往后的数据全都后移一位,再将数据放进去

HashMap和HashTable的区别

在这里插入图片描述

我觉得主要不同就是hashMap是线程不安全的,允许有null作为键或值。HashTable是线程安全的,不允许有null作为键或值,虽然是线程安全的,但是他的性能不如ConcurrentHashMap

ConcurrentHashMap和HashTable的区别

最大的区别就是HashMap实现线程安全是基于synchronized关键字,每次操作数据的时候都要锁住整张表,而ConcurrentHashMap实现线程是分段式锁,将整个链表按照hash算法地址分为16个桶,每次操作哪个桶就锁住哪个桶,其他桶还是可以访问操作的。

HashMap的实现原理

数组加链表结构,通过key来计算hash值,算出要存储的数组位置,然后如果相同位置上已经有数据存在了,就按照链表方式排列。
1.8之后引入了红黑树,如果一个hash值的位置上链表长度超过了8,就会将链表转为红黑树方式排列,防止链表过长
在这里插入图片描述

对多线程的理解

什么是线程,线程是cpu运行的基本单位,而进程由线程组成。比如班级进行大扫除,那么大扫除的全体同学就是一个进程,而一个个同学做擦玻璃、洒水、扫地等等就是线程。
并发和并行
并发指多个线程同时执行;并行指多个线程交替执行

Java的线程生命周期说一下,每种状态对应的java方法是什么

当创建一个线程就到线程的退出销毁死亡就是线程的声明周期,周期中有5个状态
通过实现Runnable或者继承Thread来得到一个线程,这时候是初始状态
调用start方法或者sleep方法结束,位于可运行线程池中,进入可运行状态
当线程获得了cpu的运行时间片,则进入运行状态
如果调用了线程的sleep方法则进入阻塞状态

sleep()和wait()的区别

sleep()是线程的等待方法,并不会释放锁和资源,所以即使阻塞状态其他线程也不会执行
wait()是对象的方法,会将锁释放,并且进入对象等待集合中等待唤醒,这时候其他线程可以执行

notify()和notifyAll()的区别

notifyAll会将所有wait状态的线程都唤醒,并且全部进入锁池中等待竞争对象锁,没有竞争到的等现有线程执行完再次竞争
notify只会随机唤醒一个wait状态的线程,并且将其移动到锁池中等待获取对象锁

一个主线程开启10个子线程,10个子线程执行完毕后主线程继续,如何实现这样一个线程同步?

使用join()方法,使主线程进入阻塞状态(可以设置超时时间),等待子线程执行完毕

ThreadLocal的作用

为了解决多线程并发问题共用变量的问题,比如数据库连接变量,如果我们多个线程同时创建多个连接的话,肯定会造成数据库压力过大,如果共用同一个连接的话,sql语句会排队等待,效率低下。这时候就可以使用ThreadLocal,只进行一次连接,复制这个变量,让线程之间运行互不影响,还不会造成链接过多。

volatile关键字的意义和使用场景

保证被这个关键字修饰的变量,只要被修改了,就会被其他所有变量知道
原理就是所有变量每次读写的时候都是操作主内存中的数据,当一个变量修改了,那其他变量肯定也看得到;
线程使用没有被volatile关键字修饰的变量的时候,都是从主内存中拷贝到cpu缓存中,在本线程内使用

Java自带juc多线程包里用过哪些工具

volatile关键字、Lock同步锁、Condition 控制线程通信、ReadWriteLock读写锁
Executors 线程池:参数有核心线程数,最大线程数、线程空闲时间

SynchronizedMap和ConcurrentHashMap有什么区别?

SynchronizedMap是定义在Collections中的静态内部类,实现了Map接口,并且对其中的每一个方法实现通过synchronized关键字进行了同步控制

ConcurrentHashMap采用锁分段机制,用不同的锁来锁住不同的一段数据,这样多线程去访问容器中不同数据时就不用竞争同一把锁了。

对锁的理解,各种锁

乐观锁和悲观锁;独占锁和共享锁;互斥锁和读写锁;公平锁和非公平锁;可重入锁;自旋锁;分段锁

synchronized作用于静态方法和非静态方法的区别

静态方法是给类加锁,非静态方法是给创建的实例对象加锁

什么是可重入锁(ReentrantLock)

功能与synchronized锁差不多,但是可以实现公平锁,也就是说,可以让线程排队获得锁

当一个线程进入一个对象的synchronized方法A之后,其它线程是否可进入此对象的synchronized方法B?

不能,只能访问该对象的非同步方法

线程同步的几种方式

synchronized修饰
volatile实现同步(只能保证可见性,不能保证原子性)
使用局部变量ThreadLocal
使用原子类(AtomicInteger、AtomicBoolean……)
使用Lock
使用容器类(BlockingQueue、ConcurrentHashMap)

现在有个统计1千万条数据的需求,你要开10个线程去统计,主线程如何知道子线程统计完成了。

共享内存,定义全局变量使用volatile来定义,这是线程之间比较常用的通信方式。

有3个线程,如何让三个线程按照顺序完成执行

最简单的方式就是后一个线程调用前一个线程的join()方法。
当T2线程调用T1线程的T1.join(),那么T2线程会阻塞等待T1执行完毕。
该方法底层还是由wait()实现的。

jvm虚拟机

说一下你对jvm的理解

jvm内存结构包含 方法区(常量池)、java堆(变量存储的地方,GC执行垃圾回收的重点区域)、还有线程私有的(本地方法栈、java虚拟机栈、程序计数器)

什么是类加载器

就是根据全限定名称将class文件加载到JVM内存中转为Class对象

什么是双亲委派机制

当一个类加载器手动了类加载请求,会先判断是否有父类,有的话就向上抛
父类还有父类的话继续向上抛
如果父类完成加载可以成功返回,如果父类无法加载,子类才会去尝试自己加载

创建一个对象的过程

1.判断对象对应的类是否加载、链接、初始化
2.为对象分配内存
3.处理并发安全问题
4.初始化分配到空间
所有属性设置默认值,保证对象实例字段在不赋值时可以直接使用
5.设置对象的对象头
6.执行init方法进行初始化

Spring

spring是一种轻量级的开发框架,核心模块就是控制翻转依赖注入IOC,依赖注入AOP,事务管理等

mvc原理

全称Model-View-Controller,模型-视图-控制器模式
简单来说就是
用户请求控制器,发出请求
控制器按照请求选择一个模型
模型处理请求,并返回相应数据到控制器
控制器根据请求选取视图,
视图拿到数据并渲染,返回给用户

完整一些要记录这个图:
在这里插入图片描述

IOC

控制反转依赖注入,这是一种设计思想。
在原本的在程序中手动创造对象控制权,变为交给spring来管理,这就是控制反转。
将对象直接的相互依赖关系交给spring容器来管理,这就是依赖注入。
以前简单的spring需要我们将需要注入的bean文件写入配置文件,每创建一个bean就在配置文件中加一条配置,后来的springBoot可以通过注解来自自动读取文件夹中需要注入的bean。这样就越来越解放了我们的双手

AOP

面向切面编程,意思就是将那些与业务无关的,但是确实涉及到所有的模块的逻辑,比如日志管理,权限管理,事务处理,封装起来。插入到所有的业务逻辑中。好像在水管中插入一个网,来控制水流。

切面编程的通知有哪些类型

前置通知(Before Advice):在连接点(Join point)之前执行的通知。
后置通知(After Advice):当连接点退出的时候执行的通知(不论是正常返回还是异常退出)。
环绕通知(Around Advice):包围一个连接点的通知,这是最强大的一种通知类型。 环绕通知可以在方法调用前后完成自定义的行为。它也可以选择是否继续执行连接点或直接返回它们自己的返回值或抛出异常来结束执行。
返回后通知(AfterReturning Advice):在连接点正常完成后执行的通知(如果连接点抛出异常,则不执行)
抛出异常后通知(AfterThrowing advice):在方法抛出异常退出时执行的通知

Spring事务管理方式有几种

编程式事务:在代码中硬编码(不推荐使用)
声明式事务:在配置文件中配置(推荐使用),分为基于XML的声明式事务和基于注解的声明式事务。

Spring用到了java哪些设计模式

1.工厂设计模式:Spring使用工厂模式通过BeanFactory和ApplicationContext创建bean对象。
2.代理设计模式:Spring AOP功能的实现。
3.单例设计模式:Spring中的bean默认都是单例的。
4.模板方法模式:Spring中的jdbcTemplate、hibernateTemplate等以Template结尾的对数据库操作的类,它们就使用到了模板模式。
5.包装器设计模式:我们的项目需要连接多个数据库,而且不同的客户在每次访问中根据需要会去访问不同的数据库。这种模式让我们可以根据客户的需求能够动态切换不同的数据源。
6.观察者模式:Spring事件驱动模型就是观察者模式很经典的一个应用。
7.适配器模式:Spring AOP的增强或通知(Advice)使用到了适配器模式、Spring MVC中也是用到了适配器模式适配Controller。

垃圾回收

垃圾回收的全称是 Garbage Collection 通常被简称为GC。是对内存中堆区域进行清理的一种技术。

回收什么

堆是主要回收对象所在地,方法区一般不会有失效对象并且内存不大,栈一般线程结束就会将自己的栈帧销毁所以局部变量也会释放,程序计数器只是存了地址不需要回收

java引用类型

强引用-不回收,new一个对象就是强引用,我们开发过程中99%用的都是强引用,所以容易导致内存泄漏
软引用-当内存不足时会被回收,比如将对象存入缓存中SoftReference
弱引用-只要发生了GC就会被回收,WeakReference
虚引用-不能单独使用,也无法通过虚引用来获取对象,唯一的做旧在于跟踪垃圾回收过程,比如对象被垃圾回收时可以收到一个系统通知
判断对象活性
引用计数法- 对象每被引用一次就+1 ,引用放弃了就-1,引用为0时就可以被回收(无法解决循环依赖)
可达性分析-从对象集合为起点,根据引用目标遍历所有引用链路,没有被遍历到的就是可回收对象

回收算法

标记-清除:非常基础和常见的算法,从根节点遍历,并且标记所有被引用对象,遍历后没有被标记为可达对象的,就将其回收。需要注意的是并不是真的删除对象,而是将对象地址存在空闲地址列表中,如果有新的对象
标记-复制:将内存区分出来一份,将不是垃圾对象复制到这个分区中,将原来区域的垃圾对象全部释放
标记-整理:删除垃圾对象后内存碎片太多,就将所有的正常对象移动到一起
分代回收:对象区分为 新生代和老年代,新生代又分为伊甸区、生存区1、生存区2
对象诞生于伊甸区,第一轮GC扫描之后会将依旧存货的对象复制进生存区;
两个生存区是为了更好的施展复制算法;
当对象在生存区进行了若干次扫描拷贝之后还存活,就将其拷贝到老年代;
老年代的对象都是存活时间比较长的,并且扫描的周期也会长。

Redis

为什么要使用缓存

主要为了实现两方面,高性能和高并发
因为缓存是使用内存作为存储区,而且内存的读写性能天然的要比磁盘高很多,可以扛得住大量的并发请求,那么系统的性能自然就更好了。

什么场景下使用redis缓存的

要结合自己的系统业务需要来讲,并且这部分数据并不是核心数据,或者是并不会经常更新的数据。
比如导航栏,常用的系统参数,用户操作日志轨迹等等。

为什么redis的单线程可以支撑高并发

因为redis是基于内存的轻量级数据库,处理请求的事件处理器是纯内存操作,并且IO多路复用非阻塞监听请求

redis数据类型

String、hash、list、set、sorted set

redis过期策略

每100ms,redis会自己随机抽取一批设置了过期时间的key,判断是否过期了,是则删除。

淘汰策略

noeviction:当内存不足以容纳新的数据时,写入的时候直接报错,这个应该没有人会用吧allkeys-lru:当内存不足,写入新的数据时,会移除最近最少使用的key(这个是最常使用的)
allkeys-random:当内存不足,写入新的数据时,会随机移除某个key(这个不会有人用吧)
volatile-lru:当内存不足,写入新的数据时,会在设置了过期时间的key中移除最近最少使用的那个(这个使用的也比较少)
volatile-random:当内存不足,写入新的数据时,会在设置了过期时间的key中随机移除一个
volatile-ttl:当内存不足,写入新的数据时,有更早过期时间的key更早移除
原文链接:https://blog.csdn.net/weixin_41011482/article/details/118195862

redis缓存雪崩和穿透

雪崩:同一时间缓存中大量数据失效,或者缓存突然崩溃,导致数据库承受过大压力
穿透:用户恶意访问缓存中不存在的数据,导致请求直接打到数据库,比如id=-1之类的

解决:雪崩,将key的过期时间均匀分布,使其不会再同一时间大量过期。并且设置查询降级和熔断策略,如果缓存崩溃则使一部分请求先查询java缓存或者直接拦截,不至于让大量请求直接打到数据库,导致数据库并发过高而崩溃
穿透,当检测到用户这种恶意请求,数据库中无返回值时,在缓存中设置一个固定值供其查询,这样请求就不能达到数据库

redis一条命令变慢了,有可能是哪些原因导致的

redis原因:命令过于复杂、数据过大(value值过大)、集中过期、AOF或者RDB影响了性能
系统原因:数据太多,内存压力过大,网络原因

redis+数据库读写分离

将热点数据缓存进数据库中,用户查询是先查询缓存,不存在了再去数据库中查询,并且将查询到的数据存入

mysql数据库

数据库的4种事务隔离级别,Mysql默认级别

读未提交:此时select语句不加锁,一致性最差,会造成脏读
读已提交:读取的数据是另一个进程提交事务后的最终数据
可重复读:这个是针对update语句出现的不可重复读,当一个update事务开启是,其他事务不能再修改数据。这个也是mysql的默认隔离级别
串行化:这个是针对读数据时的幻读,让一个个请求阻塞排队等待执行,脏读幻读不可重复读都能避免,但是效率极差,所以基本很少人用这个

数据结构

二叉树:从根节点延伸,每个子节点分出大小两个子节点,缺点就是有可能单边增长,导致一边特别长,失去了原本的作用。
红黑树:又名平衡二叉树,在增加节点的时候自动调整节点大小位置,避免单边增长过长的情况,缺点就是红黑树会特别高大,查询时候依旧耗时
B树:将所有的关键字分布在节点中,搜索的时候依旧是从根部进行二分查找,直到命中
B+树:每个叶子节点也做标记,并且做成链表,查询的时候可以从叶子节点倒查,这个链表就是稠密索引
B*树:将所有的节点都做标记,做成链表,这样查询的提高了节点的利用率

Mysql的索引采用什么数据结构

InnoDB引擎使用B+树

mysql执行时,索引选错了

可能是sql语句导致全表扫描之类的,这时候会放弃索引
有可能是对数据行数预估错误,数据量过大可能会导致放弃索引

我们可以使用force index强行选择一个索引,不够灵活不推荐使用
优化sql语句、或者根据语句新建更合适的索引

慢查如何分析和解决

使用explain等工具分析sql语句使用哪些索引,针对sql语句和索引做优化
比如不能使用*查询,模糊查询,is null ,!= 、<>、数据量大的表进行join,这些都会导致放弃索引进行全表扫描

回表查询

比如select a from T where b=?
如果所查询的a字段没有索引的话,会先拿到b数据所在行数,然后根据行数找到数据行,再拿到a字段的值。就是回表查询
一个sql语句尽量不要使用*,查询字段尽量加上索引

读写分离

当我们想要数据库承受更大的并发量时,可以考虑读写分离。
将数据复制到不同的库中,一个库专门处理写的请求,并且将数据同步到其他数据库,其他库专门处理读的请求,这样多个从库可以承受更大的并发量,并且能不断添加从库。

分库分表

分库:将数据平均拆分到不同的库中,这样并发量也拆分了,可以承受更大的并发量
分表:将数据分成多份放在不同的表中,这样每个表数据少些,承受压力也少些

分库分表如何管理id

数据库自增id:所有的插入请求都在同一个库中拿同一个自增序列,缺点就是单库可能有并发量瓶颈
本地生成uuid:没有并发量瓶颈,但是数据过长可能会影响查询性能
获取系统当前时间:可以将系统当前时间与其他字段拼接之类的,缺点有重复的风险
snowflake算法:twitter开源的算法,就是一个64位的long型id,1个bit是不用的,41个bit作为毫秒数,10个bit作为工作机器id,12个bit作为序列号。

提升mysql性能方式

数据库是有缓存区的,我们每次操作和查询的数据都是缓存区的数据,并不会直接修改磁盘中的真实数据。
所以我们要注意将热点数据集中分布,这样的话加载缓存的时候就可以一次多加载一些热点数据,少加载几次,也能提高性能
提前预热数据,可以再访问高峰期之前定时自己访问一些热点数据,保证并发高峰期来临时热点数据已经在缓存区。

其他

自己如何实现分页的

首先分为前端分页和后端分页

前端分页自然就是将所有数据返回给前端,由前端做缓存和分页,这种一般不会使用,因为数据量大的话会给前端造成很大压力;
后端分页又分为 借助数组分页、sql分页、拦截器分页

借助数组分页,从数据库中查询出所有的数据,通过数组截取要查询页的数据;
sql分页,sql语句后面加limit来实现分页。需要注意的是,limit分页,如果数据量大的话,会造成效率低下问题,最好结合自增id来分页
插件分页page Helper,底层还是将sql语句拼接了limit

让你设计一个秒杀系统,说说你的想法

秒杀,比如12306抢票,电商系统发售新品之类的。瞬时会有大量的用户请求涌入,如果我们不拦截处理,大量的请求直接打到数据库,那么肯定会瞬间崩溃,所以我们主要做的就是控制用户流量。

秒杀系统说起来也挺简单,就是大量用户涌入,少部分用户成功,成功之后下订单减少库存就好了。

那么设计理念自然就是控制流量:限流、削峰、异步处理、缓存、可拓展

所以这样来设计
前端:活动页面尽量不要有太多动态元素,否则会多次请求后端;限制用户只能提交一次,某段时间同一个IP只能提交一次之类的。
后端:
用消息队列缓存请求,比如有1000台手机,来了100w的请求,没有必要全部放行,通过数据库订阅这些消息慢慢处理,其他的都返回失败就好了
利用缓存,比如在redis中让用户写入一个list,秒杀用户达上限是拒绝请求就可以了,然后我们后台线程读取这些用户list,下订单减库存。

当然,一个真正的秒杀系统要复杂的多的多,这里只是大概说了一下思路,真实遇到的各种状况没办法一一说明。

排序会哪些

冒泡排序:重复的遍历数据,对比相邻的两个数据,大的数据不断的上浮到顶端
选择排序:重复遍历数据,将最小值放在前端,比如第一次找到1放在前端,那么第二次就是将2放在1后面。
插入排序:在数据中取出一条,在已有序列中判断该放在什么位置,然后插入进去
希尔排序:比较复杂,但是效率也更高。

  • 比如现在有10条数据[8,9,1,7,2,3,5,4,6,0]
  • 首先将十个数据分组gap=length/5=5,则意味整个数据分为5组

在这里插入图片描述

  • 对这5组数据分别对比,小的数据放在左边 则变成了[3,5,1,6,0,8,9,4,7,2]

  • 然后缩小增量,将这10个数据分为两组
    在这里插入图片描述

  • 此次完成后则变成[0,2,1,4,3,5,7,6,9,8]

  • 最后将数据进行冒泡排序,遍历一到两次就可以了

如何判断一个链表是否有环

两次遍历链表,如果有重复节点就是存在环
定义两个指针,一个每次走一个节点,一个每次走两个节点,如果有环,那么两个节点一定会相遇

一个springBoot的jar启动后,将该jar包删除,会产生什么影响

jvm是懒加载

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

木小同

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值