面试整理八股
1、因为String类下的value数组是用final修饰的,final保证了value一旦被初始化,就不可改变其引用。此外,成员变量的访问权限为 private,同时没有提供方法将字段暴露出来,想要修改只能通过 String 提供的方法。而且,String类是用final修饰的,不可以被继承。
2、①安全性。 不可变性确保String对象的值在创建后不能被修改。所以可以经常用于存储一些敏感信息,例如用户名、密码、网络连接等。
②节省空间——字符串常量池 通过使用常量池,内容相同的字符串可以使用同一个对象,从而节省内存空间。
③线程安全 String 对象是不可修改的,如果线程尝试修改 String 对象,会创建新的 String,所以不存在并发修改同一个对象的问题
④性能 String 被广泛应用于 HashMap、HashSet 等哈希类中,当对这些哈希类进行操作时,例如 HashMap 的 get/put,hashCode 会被频繁调用。由于不可变性,String 的 hashCode 只需要计算1次后就可以缓存起来,因此在哈希类中使用 String 对象可以提升性能。
2、sychronized和locked的区别?
1、synchronized是一个关键字而lock是一个接口
2、synchronized是隐式的加锁,lock是显示的加锁。
3、synchronized可以作用在方法和代码块上,而lock只能作用在代码块上。
4、synchronized采用的是monitor对象监视器,lock的底层原理是AQS。
5、synchronized是非公平锁,而lock可以是公平锁也可以是非公平锁。
6、synchronized用object的notify / notifyAll方法进行唤醒,而lock用condition进行唤醒。
3、接口和抽象类的区别?
1、抽象类只能单继承而接口可以多实现。
2、抽象类可以有构造方法而接口中不能有构造方法。
3、抽象类中可以有成员变量而接口中没有成员变量只有常量(public static final)。
4、抽象类中可以包含非抽象方法;在Java7之前接口中的所有方法都是抽象的,在Java8之后接口支持非抽象方法:default方法、静态方法等。Java9支持私有方法、私有静态方法。
5、抽象类中的方法类型可以是任意修饰符;Java8之前接口中的方法只能是public,Java9支持private类型。
4、Redis的基本数据类型?
字符串String、哈希表Hash、列表List、集合Set、有序集合Zset。
(1)字符串String:可以用来做最简单的数据,可以缓存某个简单的字符串,也可以缓存某个json格式的字符串,Redis分布式锁的实现就利用了这种数据结构,还包括可以实现计数器、Session共享、分布式ID。
(2)哈希表Hash:可以用来存储一些key-value对,更适合用来存储对象。
(3)列表List:通过命令的组合,既可以当作栈,也可以当作队列来使用,可以用来缓存类似微信公众号、微博等消息流数据。
(4)集合Set(String字符串类型的无序集合):和列表类似,也可以存储多个元素,但是不能重复,集合可以进行交集、并集、差集操作,从而可以实现类似,我和某人共同关注的人、朋友圈点赞等功能。
(5)有序集合Zset(Zset是String类型的有序集合):不可重复,有序集合中的每个元素都需要指定一个分数,根据分数对元素进行升序排序。
5、Java的基础数据类型?
boolean、byte、char、int、short、long、float、double
6、SpringMVC的实现逻辑?
首先DispatcherServlet会接收到request请求信息,去头去尾得到映射地址,然后通过HandlerMapping查找到具体处理的Controller以及对应的方法。方法结束后会返回一个ModelAndView,里面包含的是返回的数据以及返回的视图。这个就会交给ViewResolver来处理。处理结束后就会跳转到相应的视图。
7、SpringBoot如何实现依赖注入?
1、使用 XML 配置依赖注入 在 Spring Boot 中,使用 XML 配置依赖注入(DI)时,需要使用<bean>元素来定义 bean,并使用<property>元素来为 bean 的属性注入值或依赖对象。
2、使用 Java 配置类实现依赖注入(推荐) 使用 Java Config 实现依赖注入可以通过@Configuration和@Bean注解来实现。
3、使用注解来进行依赖注入(推荐) 可以使用注解来进行依赖注入,常用的注解有@Autowired和@Qualifier。
8、为什么MySQL选择B+树索引?
1、阶数更多,路径更短。
2、磁盘读写代码B+树更低,非叶子节点只存储指针,叶子节点存储数据。
3、B+树便于扫库和区间查询,叶子节点是一个双向链表。
9、where和having的区别?
SELECT customer_id, SUM(total_price) AS total FROM orders GROUP BY customer_id HAVING SUM(total_price) > 100;
1、WHERE 子句用于在执行查询之前筛选行,而 HAVING 子句用于在执行聚合查询后筛选结果集。
2、WHERE 出现在 FROM 子句之后,GROUP BY 子句之前;而 HAVING 出现在 GROUP BY 子句之后、ORDER BY 子句之前。
3、WHERE 可以使用比较操作符和逻辑操作符来指定过滤条件,而 HAVING 不仅可以使用比较操作符和逻辑操作符,还可以使用聚合函数。
10、什么是线程池?
线程池是一种并发编程的机制,用于管理和复用线程,以提高程序的性能和资源利用率。在多线程环境中,频繁地创建和销毁线程会带来较大的开销,而线程池通过事先创建一组线程并将它们置于待命状态,有效地减少了这种开销。
11、hashmap扩容介绍,什么时候开始扩容?
默认初始容量是16。HashMap的容量必须是2的N次方,HashMap会根据传入的容量计算一个大于等于该容量的最小的2的N次方。
(1)当HashMap中的元素数量达到容量乘以加载因子时,就会触发扩容操作。加载因子默认为0.75,可以通过构造方法设置。
(2)当需要扩容时,HashMap 会创建一个新的数组,其大小为原数组的两倍。
(3)对于原数组中的每个非空位置(即存储了键值对的位置),会重新计算它们在新数组中的位置。重新计算的方法是通过对原位置的哈希值取模新数组长度。
(4)对于每个原数组的位置,如果它的链表长度小于等于阈值(默认为8),则直接将链表中的键值对按照顺序放入新数组的相应位置。如果链表长度超过阈值,会将该链表转化为红黑树,然后再将红黑树的节点按照顺序放入新数组的相应位置。
(5)当数组中的所有元素都被放入新的位置后,新数组取代了原来的数组,成为新的存储结构。
(6)扩容操作完成后,原数组会被垃圾回收。
12、Redis是单线程的,但是为什么还那么快?
1、完全基于内存的,C语言编写。
2、采用单线程,避免不必要的上下文切换可竞争条件。
3、Redis通过I/O多路复用程序来监听来自客户端的大量连接,它会将感兴趣的事件及类型注册到内核中并监听每个事件是否发生。I/O多路复用技术的使用让Redis不需要额外创建多余的线程来监听客户端的大量连接,降低了资源的消耗。
I/O多路复用是指利用单个线程来同时监听多个Socket,并在某个Socket可读、可写时得到通知,从而避免无效的等待,充分利用CPU资源。目前的I/O多路复用都是采用的epoll模式实现,它会在通知用户进程Socket就绪的同时,把已就绪的Socket写入用户空间,不需要挨个遍历Socket来判断是否就绪,提升了性能。
13、Redis内存淘汰策略?
(1)全局的键空间选择性移除。
(2)设置过期时间的键空间选择性移除 。
比如移除最近最少使用的key、随机移除某个key、优先移除更早过期的key呀(具体的看md文档)
14、类加载流程?
类加载的过程包括:加载、验证、准备、解析、初始化、使用、卸载,其中验证、准备、解析统称为连接。
(1)加载:通过一个类的全限定名来获取定义该类的二进制字节流,在内存中生成一个代表这个类的java.lang.Class对象。
(2)验证:确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。
(3)准备:为静态变量分配内存并设置静态变量初始值,这里所说的初始值“通常情况”下是数据类型的零值。
(4)解析:将常量池内的符号引用替换为直接引用。
(5)初始化:到了初始化阶段,才真正开始执行类中定义的Java初始化程序代码。主要是静态变量赋值动作和静态语句块(static{})中的语句。
(6)使用:JVM开始从入口方法开始执行用户的程序代码。
(7)卸载:当用户程序代码执行完毕后,JVM便开始销毁创建的Class对象。
15、jvm内存区域,虚拟机栈里面是什么,堆里面的数据什么时候进入老年代?
Java虚拟机栈: 线程私有的,每个线程都有一个私有的Java虚拟机栈,用于存储方法调用的局部变量、操作数栈、动态链接、方法出口等信息。每个方法的调用都会创建一个栈帧,方法执行完毕后栈帧会被销毁。如果线程请求的栈深度大于虚拟机允许的深度,将抛出栈溢出异常。 堆:用于存储对象实例和数组。
①Young GC后,如果对象太大无法进入Survivor区,则会通过分配担保机制进入老年代。
②对象每在Survivor区中“熬过”一次Young GC,年龄就增加1岁,当它的年龄增加到一定程度(默认为15岁,可以通过-XX:MaxTenuringThreshold设置),就将会被晋升到老年代中。
16、程序计数器是干什么的?
程序计数器:用于存储当前线程执行的字节码指令的地址。在多线程环境下,每个线程都有自己独立的程序计数器,以保证线程切换后能恢复到正确的执行位置。
17、方法执行的时候栈里面是怎么样的?
每个方法的调用都会创建一个栈帧,方法执行完毕后栈帧会被销毁。如果线程请求的栈深度大于虚拟机允许的深度,将抛出栈溢出异常。
18、方法区?
方法区:存放已被加载的类信息、常量、静态变量、即时编译器编译后的代码数据,即永久代。在jdk1.8中不存在方法区了,被元空间替代了,原方法区被分成两部分;1:加载的类信息,2:运行时常量池;加载的类信息被保存在元空间中,运行时常量池保存在堆中;
19、synchronize怎么用的,原理?
synchronized的底层实现主要区分:代码块和方法。
1、synchronized修饰代码块时,编译后会生成 monitorenter和monitorexit指令,分别对应进入同步块和退出同步块。会有两个monitorexit,这是因为编译时JVM为代码块添加隐式的try-finally,在finally中进行了锁释放,这也是为什么synchronized不需要手动释放锁的原因。
2、synchronized修饰方法时,编译后会生成ACC_SYNCHRONIZED标记,当方法调用时,调用指令将会检查方法的 ACC_SYNCHRONIZED访问标志是否被设置,如果设置了则会先尝试获得锁。 两种实现其实本质上没有区别,只是方法的同步是一种隐式的方式来实现,无需通过字节码来完成。
20、TCP的三次握手?
在建立TCP连接时,需要通过三次握手来建立,过程是:
(1)客户端向服务器端发送一个SYN;
(2)服务端接收到SYN后,给客户端发 送一个SYN_ACK;
(3)客户端接收到SYN_ACK后,再给服务端发送一个ACK。
21、TCP的四次挥手?
在断开TCP连接时,需要通过四次挥手来断开,过程是:
(1)客户端向服务端发送FIN;
(2)服务端接收FIN后,向客户端发送ACK,表示我接收到了断开连接的请求,客户端可以不发数据了,不过服务端这边可能还有数据正在处理;
(3)服务端处理完所有数据后,向客户端发送FIN,表示服务端现在可以断开连接;
(4)客户端接收到服务端的FIN,向服务端发送ACK,表示客户端也会断开连接。
22、浏览器输入URL的过程?
(1)DNS解析。
(2)建立TCP连接。
(3)发送HTTP请求。
(4)服务器处理请求并返回HTTP报文。
(5)浏览器解析渲染页面。
(6)连接结束。
23、hashmap用了哪些数据结构?
在JDK1.7 中,由“数组+链表”组成,数组是 HashMap 的主体,链表则是主要为了解决哈希冲突而存在的。 在jdk1.8版本后,底层是由“数组+链表+红黑树”组成。java对HashMap做了改进,在链表长度大于8的时候,将后面的数据存在红黑树中,以加快检索速度。
24、链表转红黑树的目的?
为了加快检索速率。红黑树虽然本质上是一颗二叉树,但它在二叉查找树的基础上增加了着色和相关的性质使得红黑树相对平衡,从而保证了红黑树的查找、插入、删除的时间复杂度最坏为O(log n)。
25、hashmap是线程安全的吗?
HashMap在并发场景中不是线程安全的。比如A希望插入一个key-value对到HashMap中,当获取到对应的链表结点位置时,此时线程A的时间片用完了,而此时线程B被调度得以执行,可能线程B占用了A计算得到的位置,插入了数值。而线程A被切换回来的时候,不知道B已经插入了元素,仍然将元素插入此前计算好的位置,这样就会将B线程的插入记录覆盖掉了,这对应了多线程的put可能导致元素的丢失;
JDK1.7 当扩容的时候,存在 Entry 链死循环和数据丢失问题(线程1的put和线程2的get并发,可能导致get为null) JDK 1.8 HashMap 采用数组 + 链表 + 红黑二叉树的数据结构,优化了 1.7 中数组扩容的方案,解决了 Entry 链死循环和数据丢失问题。但是多线程背景下,put 方法存在数据覆盖的问题。
26、hash的过程?
HashMap中采用的链地址法解决hash冲突。
存储索引是怎么计算的:首先根据key的值计算出hashcode的值,然后根据hashcode计算出hash值,最后通过hash &(length-1)计算得到存储的位置。
27、多线程下使用hashmap保证线程安全的方案?
1、HashTable 是线程安全的。HashTable 容器使用 synchronized 来保证线程安全,但在线程竞争激烈的情况下 HashTable 的效率非常低下。
2、①使用ConcurrentHashMap ,分段锁的思想,将 HashMap 进行切割,把 HashMap 中的哈希数组切分成小数组,每个小数组有 n 个 HashEntry 组成,其中小数组继承自ReentrantLock(可重入锁),这个小数组名叫Segment(JDK1.7)。
②JDK1.8中取消了Segment 分段锁,采用 CAS + synchronized 来保证并发安全,ConcurrentHashMap 中 synchronized 只锁定当前链表或红黑二叉树的首节点,只要节点 hash 不冲突,就不会产生并发。
28、聊一聊concurrentHashMap?
concurrentHashMap是一种线程安全的高效Map集合,它是【线程安全】的哈希表。它是通过“分段锁”来实现多线程下的安全问题。
1、底层数据结构 jdk1.7采用分段数组+链表实现 jdk1.8采用数组+链表/红黑树。
2、加锁的方式 jdk1.7采用segment分段锁,底层使用的是ReentrantLock。 Jdk1.8采用CAS添加新节点,采用synchronized锁定链表或红黑二叉树的首节点。
29、线程池的核心参数?
corePoolSize 核心线程数目
maximumPoolSize 最大线程数目
keepAliveTime 生存时间
unit 时间单位
workQueue
threadFactory 线程工厂
handler 拒绝策略
30、拒绝策略有哪些?
1、AbortPolicy:直接抛出异常,默认策略。
2、CallerRunsPolicy:用调用者所在的线程来执行任务。
3、DiscardOldestPolicy:丢弃阻塞队列中最靠前的任务,并执行当前任务。
4、DiscardPolicy:直接丢弃任务。
31、bean的生命周期?
首先会通过一个非常重要的类,叫做BeanDefinition获取bean的定义信息,这里面就封装了bean的所有信息,比如,类的全路径、是否是延迟加载、是否是单例等等这些信息。
(1)在创建bean的时候,第一步,调用构造函数实例化bean
(2)第二步,bean的依赖注入,比如一些set方法的注入、@Autowried自动注入等
(3)第三步,处理Aware接口,如果某一个bean实现了Aware接口,就会重写方法执行
(4)第四步,初始化前,执行bean的前置处理器,处理@PostConstrut注解
(5)第五步,初始化时,处理InitializingBean接口
(6)第六步,初始化后,执行bean的后置处理器,主要是对bean进行增强
(7)第七步,销毁bean。
32、MySQL的最左匹配原则?
最左匹配原则就是指在联合索引中,如果我们的 SQL 语句中用到了联合索引中的最左边的索引,那么这条 SQL 语句就可以利用这个联合索引去进行匹配。
33、索引失效的情况?
(1)<>不等于会导致索引失效。
(2)模糊查询中,使用like关键字,like查询以%开头就会失效,以%结尾不会失效;
(3)不符合最左匹配原则,索引的最左边那个条件必须有;
(4)索引列进行了类型转换;
(5)where后使用or导致索引失效;
(6)对索引列进行了计算或使用了函数;
(7)如果使用了复合索引,查询中包含范围条件,右侧的条件索引也会失效。
34、left join、right join和union join的区别?
(1)join(Inner Join)内连接 取两表的公共数据(交集)
(2) left Join左连接 返回左表所有的行,右表返回匹配行,不匹配的返回NULL
(3)right Join右连接 返回右表所有的行,左表返回匹配行,不匹配的返回NULL
(4)union 对两个结果集进行并集操作,重复数据只显示一次
(5)union All 对两个结果集进行并集操作,重复数据全部显示
35、springBoot的事务管理?
事务是指一组操作,这些操作要么全部成功,要么全部失败。如果在一组操作中有一个操作失败了,那么整个事务都应该回滚,即撤销已经执行的操作,从而保证数据的一致性和完整性。
在 Spring Boot 中,我们可以使用事务管理器来管理事务。事务管理器可以确保一系列操作要么全部成功,要么全部失败,从而保证数据的一致性和完整性。Spring Boot 中的事务管理器是通过 AOP 实现的,它可以拦截带有 @Transactional 注解的方法,并在方法执行前后自动开启和提交事务。如果方法执行过程中发生异常,事务管理器会自动回滚事务,从而保证数据的一致性和完整性。
如果需要自定义事务管理器,我们可以使用 @Bean 注解在配置类中定义一个事务管理器。
36、线程池的执行流程?
首先判断核心线程是否已满,如果没有满则创建核心线程执行,如果已经满了就判断任务队列是都已满,如果没有满则将任务放入到任务队列中,如果满了就判断最大线程数是否到达,如果没有到达则创建临时线程执行,如果已经达到则根据拒绝策略处理任务。最后,如果核心或临时线程执行完成任务后会检查任务队列中是否有需要执行的线程,如果有,则使用非核心线程执行任务。
37、对volatile关键字的理解?
volatile关键字详解:是Java语言提供的一种稍弱的同步机制,用来确保将变量的更新操作通知到其他线程。
-- 当把变量声明为volatile类型后,编译时与运行时都会注意到这个变量是共享的,因此不会将该变量上的操作与其他内存操作一起重排序。
-- volatile变量不会被缓存在寄存器或者对其他处理器不可见的地方,因此在读取volatile类型的变量时总会返回最新写入的值。
-- 在访问volatile变量时不会执行加锁操作,因此也就不会使执行线程阻塞,因此volatile变量是一种比sychronized关键字更轻量级的同步机制。
38、如何理解Java并发中的可见性?
-
Java并发可见性指的是多线程并发访问共享变量时,对变量的更改能够被其他线程及时感知, 即在一个线程修改变量后,其他线程能够立即看到这个变量的修改结果。
-
在Java中,可以使用volatile关键字来保证变量的可见性,对于加了volatile的变量,线程在读取该变量时会直接从内存中读取,再修改该变量时会同时修改CPU高速缓存和内存中的值。
39、Volatile和Synchronized的区别?
(1)作用的位置不同。 synchronized是修饰方法,代码块。volatile是修饰变量。
(2)作用不同。 synchronized,可以保证变量修改的可见性及原子性,可能会造成线程的阻塞;synchronized在锁释放的时候会将数据写入主内存,保证可见性。 volatile仅能实现变量修改的可见性,但无法保证原子性,不会造成线程的阻塞;volatile修饰变量后,每次读取都是去主内存进行读取,保证可见性。
40、hashcode和equals的问题?
1、对于hashcode方法,会返回一个哈希值,哈希值对数组的长度取余后会确定一个存储的下标位置。 不同的哈希值取余之后的结果可能是相同的,用equals方法判断是否为相同的对象,不同则在链表中插入。 则有hashCode()与equals()的相关规定: -- 如果两个对象相等,则hashcode一定也是相同的; -- 两个对象相等,对两个对象分别调用equals方法都返回true; -- 两个对象有相同的hashcode值,它们也不一定是相等的;
2、为什么重写 equals 方法必须重写 hashcode 方法 ? 判断的时候先根据hashcode进行的判断,相同的情况下再根据equals()方法进行判断。如果只重写了equals方法,而不重写hashcode的方法,会造成hashcode的值不同,而equals()方法判断出来的结果为true。 而在Java中的一些容器中,不允许有两个完全相同的对象,插入的时候,如果判断相同则会进行覆盖。这时候如果只重写了equals()的方法,而不重写hashcode的方法,Object中hashcode是根据对象的存储地址转换而形成的一个哈希值。这时候就有可能因为没有重写hashcode方法,造成相同的对象散列到不同的位置从而造成对象的不能覆盖的问题。
41、对反射机制的理解?
1、定义:
反射是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取信息以及动态调用对象的方法的功能称为 Java 语言的反射机制。
2、优缺点:
优点:能够运行时动态获取类的实例,提高灵活性;可与动态编译结合Class.forName('com.mysql.jdbc.Driver.class');,加载MySQL的驱动类。
缺点:使用反射性能较低,需要解析字节码,将内存中的对象进行解析。其解决方案是:通过setAccessible(true)关闭JDK的安全检查来提升反射速度;多次创建一个类的实例时,有缓存会快很多;ReflflectASM工具类,通过字节码生成的方式加快反射速度。
3、如何获取反射中的Class对象?
①Class.forName(“类的路径”);当我们知道该类的全路径名时,就可以使用该方法获取 Class 类对象。
②类名.class。这种方法只适合在编译前就知道操作的 Class。
③对象名.getClass()。
④如果是基本类型的包装类,可以调用包装类的Type属性来获得该包装类的Class对象。
42、JVM的垃圾回收算法?
(1)标记清除算法:
标记阶段:把垃圾内存标记出来;
清除阶段:直接将垃圾内存回收;
这种算法是比较简单的,但是有个很严重的问题,就是会产生大量的内存碎片。
(2)复制算法:为了解决标记清除算法的内存碎片问题,复制算法将内存分为大小相等的两半,每次只使用其中一半。垃圾回收时,将当前这一块的存活对象全部拷贝到另一半,然后当前这一半内存就可以直接清除。这种算法没有内存碎片,但是他的问题就在于浪费空间。而且,他的效率跟存活对象的个数有关。
(3)标记压缩算法:为了解决复制算法的缺陷,就提出了标记压缩算法。这种算法在标记阶段跟标记清除算法是一样的,但是在完成标记之后,不是直接清理垃圾内存,而是将存活对象往一端移动,然后将边界以外的所有内存直接清除。
43、HotSpot为什么要分为新生代和老年代?
HotSpot根据对象存活周期的不同将内存划分为几块,一般是把Java堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法。在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。而老年代中因为对象存活率高、没有额外空间对它进行分配担保,就必须使用“标记—清理”或者“标记—整理”算法来进行回收。
其中新生代又分为1个Eden区和2个Survivor区,通常称为From Survivor和To Survivor区。
44、springBoot的AOP和IOC、DI?
1、AOP是面向切面编程,在Spring中用于将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取公共模块复用,降低耦合,一般可以作为公共日志保存、事务处理等。 2、Spring 中的 IoC 的实现机制就是工厂模式加反射机制。
UserService service = new UserService(); //耦合度太高,维护不方便
引入IOC,将创建对象的控制权交给Spring的IOC,以前由程序员自己控制对象创建,现在交给Spring的IOC去创建,如果要去使用对象需要通过DI(依赖注入)比如@Autowried自动注入 就可以使用对象。
优点:(1)集中管理对象,方便维护。(2)降低耦合度。
优点:(1)IOC容器支持加载服务时的饿汉式初始化和懒加载。
(2)最小的代价和最小的侵入性使松散耦合得以实现。
3、依赖注入(Dependency Injection,DI)是一种设计模式,它通过将对象之间的依赖关系的创建和维护转移到外部容器中来,以减少对象之间的紧耦合性并提高可重用性。
45、sql优化?
(1)表的设计优化,数据类型选择根据需求来。
(2)索引优化(索引创建原则)。
(3)sql语句优化(不使用select *,避免索引失效,聚合查询多用union all(union会多一次过滤),表关联使用inner join,不使用left join,right join)。
(4)采用主从复制,读写分离的模式,让数据库的写入,不影响查询的效率。
(5)分库分表。
46、索引的创建原则
(1)被频繁查询的字段。
(2)被作为条件查询的字段。
(3)频繁需要排序的字段。
频繁需要分组的字段。
(4)字符串类型的字段使用前缀索引代替普通索引。
(5)不为NULL的字段。
(6)被经常频繁用于连接的字段。
(7)尽可能的考虑建立联合索引而不是单列索引。
(8)限制每张表上的索引数量。
(9)注意避免冗余索引,冗余索引指的是索引的功能相同,应该尽量扩展已有的索引而不是创建新索引。
(10)删除长期未使用的索引。MySQL 5.7可以通过sys库的schema_unused_indexes视图来查询哪些索引从未被使用。
47、SpringBoot中常用注解及其底层实现?
(1)@SpringBootApplication注解:这个注解标识了一个SpringBoot工程,它实际上是另外三个注解的组合,这三个注解是:
a. @SpringBootConfiguration:这个注解实际就是一个@Configuration,表示启动类也是一个配置类;
b. @EnableAutoConfiguration:向Spring容器中导入了一个Selector,用来加载ClassPath下SpringFactories中所定义的自动配置类,将这些自动加载为配置Bean;
c. @ComponentScan:标识扫描路径,因为默认是没有配置实际扫描路径,所以SpringBoot扫描的路径是启动类所在的当前目录。
(2)@Bean注解:用来定义Bean,类似于XML中的<bean>标签,Spring在启动时,会对加了@Bean注解的方法进行解析,将方法的名字做为beanName,并通过执行方法得到bean对象。
(3)@Controller、@Service、@ResponseBody、@Autowired。
48、HTTP状态码有哪些?
49、TCP和UDP的区别?
(1)UDP在传送数据之前不需要先建立连接。而TCP提供面向连接的服务,在传送数据之前必须先建立连接,数据传送结束后要释放连接。
(2)TCP是面向字节流的,UDP是面向报文的。
(3)TCP首部开销(20~60字节)比UDP首部开销(8字节)要大。
(4)由于使用TCP进行传输的时候多了连接、确认、重传等机制,所以TCP的传输效率要比UDP低很多。
(5)TCP只支持点对点通信,UDP支持一对一、一对多、多对一、多对多通信。
(6)远地主机在收到UDP报文后,不需要给出任何确认,并且不保证数据不丢失,不保证是否顺序到达。TCP提供可靠的传输服务,TCP在传递数据之前,会有三次握手来建立连接,而且在数据传递时,有确认、窗口、重传、拥塞控制机制。通过TCP连接传输的数据,无差错、不丢失、不重复、并且按序到达。
50、Spring的常见注解?
(1)第一类是:声明Bean。有@Component、@Service、@Controller、@Repository
(2)第二类是:依赖注入相关的。有@Autowired、@Qualifier、@Resourse
(3)第三类是:设置作用域的。有@Scope
(4)第四类是:Spring配置相关的。有@Configuration、@ComponentScan、@Bean
(5)第五类是:跟AOP相关做增强的注解。有@Aspect、@Before、@After、@Around、@Pointcut
51、SpringMVC的常见注解?
(1)@RequestMapping:用于映射请求路径
(2)@RequestBody:接收http请求的json数据,将json数据转换为java对象
(3)@RequestParam:指定请求参数的名称
(4)@PathViriable:从请求路径中获取请求参数(/user/{id}),传递给方法的形参
(5)@ReponseBody:将Controller方法返回对象转换为json对象响应给客户端
(6)@RequestHeader:获取指定的请求头数据
(7)@GetMapping:是一个组合注解, 通常用来处理get请求,常用于执行查询操作。
是@RequestMapping(value="这里写的是请求的路径",method = RequestMethod.GET)的缩写。
(8)@PostMapping:是一个组合注解, 通常用来处理post请求,常用于执行添加操作。
是@RequestMapping(value="这里写的是请求的路径",method = RequestMethod.POST)的缩写。
(9)@PutMapping:是一个组合注解,通常用来处理put请求,常用于执行更新操作。
是@RequestMapping(value="这里写的是请求的路径",method = RequestMethod.PUT)的缩写。
(10)@DeleteMapping:是一个组合注解。通常用来处理delete请求,常用于执行删除操作。
是@RequestMapping(value="这里写的是请求的路径",method = RequestMethod.DELETE)的缩写。
52、MyBatis的执行流程?
(1)读取MyBatis配置文件:mybatis-config.xml加载运行环境和映射文件
(2)构造会话工厂SqlSessionFactory,一个项目只需要一个,单例的,一般由Spring进行管理
(3)会话工厂创建SqlSession对象,这里面就包含了执行SQL语句的所有方法
(4)操作数据库的接口,Executor执行器,同时负责查询缓存的维护
(5)Executor接口的执行方法中有一个MappedStatement类型的参数,它封装了映射信息
(6)输入参数映射
(7)输出结果映射
53、操作系统虚拟内存的理解?
虚拟内存本质上来说它只是逻辑存在的,是一个假想出来的内存空间,主要作用是作为进程访问主存(物理内存)的桥梁并简化内存管理。
54、没有虚拟内存有什么问题?
如果没有虚拟内存的话,程序直接访问和操作的都是物理内存,看似少了一层中介,但多了很多问题。比如:
(1)用户程序可以访问任意物理内存,可能会不小心操作到系统运行必需的内存,进而造成操作系统崩溃,严重影响系统的安全。
(2)同时运行多个程序容易崩溃。比如你想同时运行一个微信和一个 QQ 音乐,微信在运行的时候给内存地址 1xxx 赋值后,QQ 音乐也同样给内存地址 1xxx 赋值,那么 QQ 音乐对内存的赋值就会覆盖微信之前所赋的值,这就可能会造成微信这个程序会崩溃。
(3)程序运行过程中使用的所有数据或指令都要载入物理内存,根据局部性原理,其中很大一部分可能都不会用到,白白占用了宝贵的物理内存资源。
55、什么是虚拟地址和物理地址?
物理地址是真正的物理内存中地址,更具体点来说是内存地址寄存器中的地址。程序中访问的内存地址不是物理地址,而是虚拟地址 。 也就是说,我们编程开发的时候实际就是在和虚拟地址打交道。 操作系统一般通过CPU芯片中的一个重要组件 MMU(Memory Management Unit,内存管理单元) 将虚拟地址转换为物理地址,这个过程被称为 地址翻译/地址转换
56、虚拟地址与物理内存地址是如何映射的?
内存管理单元MMU将虚拟地址翻译为物理地址的主要机制有3种:(1)分段机制(2)分页机制(3)段页机制。
①分段机制 以段的形式管理/分配物理内存。应用程序的虚拟地址空间被分为大小不等的段。
②分页机制 把主存(物理内存)分为连续等长的物理页,应用程序的虚拟地址空间划也被分为连续等长的虚拟页。现代操作系统广泛采用分页机制。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步