目录
- 项目面试题
- 一. 网络基础知识
- 二. HTTP相关
- 二. 会话跟踪技术
- 三. JDK1.8新特性
- 四. 重定向和转发的区别以及如何在框架里实现的:
- 进程与线程
- synchronized和lock区别(锁)
- 乐观锁和悲观锁
- 什么是虚拟内存?
- JVM的主要组成部分及作用
- List和HashMap底层实现
- Mysql和oracle区别
- 五. 数据库的事务和隔离级别
- 六. 数据库的索引
- SQL语句本身的优化以及创建表的注意事项
- 如何实现动态SQL的
- 七. 说一下JDBC,Mybatis底层实现原理
- 八. Spring中DI和IOC
- 九. SpringBoot
- Spring里如何实现事务的
- 说一下分布式锁
- 说一下分布式项目里如何解决跨域问题
- 在你的项目中,你的并发量是多少,如何解决并发问题
- 说一下是如何配置Nginx以及正反向代理
- 会Linux系统吗?常用命令有哪些?java项目是如何打包部署的
- 常用的数据库中间件,如何使用的,作用是什么?
- AOP面向切面
- SpringMVC调用流程
- Nginx
- Redis
- 实现单点登录系统(SSO)
- Dubbo?
- 微服务
- 面试总结话术
- == 和 equals 的区别是什么?
- java 容器都有哪些?
- ArrayList 和 LinkedList 及Vector的区别是什么?
- Set
- Map
- final在java中有什么用
- java 中的 Math.round(-1.5) 等于多少?
- RPC
- Spring
- String、StringBuffer与StringBuilder之间区别
- 在 Queue 中 poll()和 remove()有什么区别?
- 怎么确保一个集合不能被修改?
- Thread中的start和run方法的区别
- Thread和Runnable
- 数据库优化
- Explain(mysql之优化-重要)
- 线程池
- sleep和wait的区别
- notify和notfyAll区别
- Volatile关键字
- synchronized
- synchronized和ReentrantLock的区别
- CAS
- 并行和并发有什么区别?
- 什么是反射?
- 什么是序列化?
- String 类的常用方法都有那些?
- JAVA的设计模式
- 如何设计数据库
- 二插查找树
- MyISAM与InnoDB关于所方面的区别
- IO流
- 递归打印多级目录
项目面试题
项目介绍
javaGC
- GC回收机制:
堆是java虚拟机进行垃圾回收的主要场所,其次要场所是方法区,垃圾回收其实就是对那些“死亡的”对象进行回收,释放内存,让后续对象再能分配到内存中,堆分为 新生代,老年代,永久代。新生代有一个有一个Eden区和两个survivor区(默认比例为8:1,两个suivivor区,一个叫from ,一个叫to),首先将对象放入Eden区,如果空间不足就向其中一个survivor区上放,如果仍然放不下,就会引发一次在新生代的minor GC,将存活的对象放入另一个survivor去中,然后清空Eden和之前的那个survivor的内存,在某次GC过程中,如果发现仍然有放不下的对象,就将这些对象放入老年代内存中。保存在永久代的对象一搬不会被回收,回收的频率和速度比较慢
(因为年轻代中的对象基本都是朝生夕死的(80%以上),所以在年轻代的垃圾回收算法使用的是复制算法,复制算法的基本思想就是将内存分为两块,每次只用其中一块,当这一块内存用完,就将还活着的对象复制到另外一块上面。复制算法不会产生内存碎片。) - 大对象以及长期存活的对象直接进入老年区
- 新生代: 复制算法
老年代: 标记-清楚和标记-压缩算法
永久代: 存放在java中的类和加载类的类加载器本身 - 复制算法: (新生代采用)
原理:将内存空间分为两块,每次只使用其中一块,在垃圾回收时,将正在使用的内存中的存活对象复制到未使用的内存块中,之后,清除正在使用的内存中的所有对象,交换两个内存的角色,复制算法的效率是很高的,而且新的内存空间中可以保证是没有内存碎片的
缺点:复制算法的代价是将系统内存折半,一般情况下很难让人接受,存活对象较多的情况,效率不能保证。
Object obj = new Object()在内存中占多少字节
说明:在没有成员变量的情况下,在 markword(属于对象头部)中有 12 字节,在 class pointer(类型指针,属于对象头部) 中有4个,需要整除8,不够会补齐,也就是说会有 16 字节
markword: 包括锁信息,hashcode,gc信息
一个对象从出生到死亡
对象从伊甸区域,如果还存活着就到so<==>s1 之间进行循环复制直到年龄超过限制时,进入oldI(老年区)区
java的应用类型有哪些
怎么用Git发布第一版本项目
分布式事务
Seata AT : 独立运行的服务协调器,每个服务执行成功或者失败都要告诉事务协调器,来协调各个服务的运行状态,这个服务成为TC。
TCC SAGA
项目中遇见的问题,项目和谁做的
- 因为我们项目是分布式项目,会遇到分布式常见的解决方案问题,支付服务成功之后会调用积分服务增加积分,所以存在分布式事务问题
- 缓存和DB的数据不一致: (1)主库数据库还没有同步到从库时,redis从从库读取到旧数据库放入 cache ,(2)写操作过程中,在 淘汰cache 和 写数据库 操作之间,有请求从主库读取旧数据放入cache
解决方案: 删除 cache,在经过 “主从同步延时窗口时间” 后,再次发起一个 异步淘汰cache 的请求;
多线程的理解,如何创建多线程,线程池使用
多线程主要作用就是为了提高程序的效率,,为了能够快速的响应 Http 请求防止用户一直等待,然后接口中比较好损的代码,全部改为多线程异步操作,异步写入日志,还有发送短信等,线程安全(使用Lock锁和synchronized 或者原子类的 cas无锁机制),线程池(线程池主要作用就是一个复用机制,如果频繁的创建线程非常浪费 CUP 的资源,所以可以采用线程池统一管理,和数据库连接池一样的)
产品需求和技术性能优先考虑什么
站在开发人员角度考虑代码的性能嘛,当然首要条件就是根据项目的需求,对技术的性能进行优化,因为需求是甲方给的嘛,别人给了钱肯定是要做好嘛,当然要是设计到需求之外的,可以在代码上做一些文章嘛,甲方要是需要进行升级,这个时候就可以像甲方提出费用的问题嘛
拿到项目需求后,你自己操作流程
首先根据项目的需求,来进行技术选型,环境的搭建
分布式锁,死锁问题
通过Redis实现分布式锁,给Redis加锁,使用命令 Setnx 来对 Key进行标识,当线程任务执行完成再释放锁,可以直接执行 del 命令,释放锁之后其他线程就可以去获取锁对象了,但是要考虑到死锁的问题,所以必须设置一个超时时间,到了一定的时间后自动释放锁
JVM内存加载机制
一. 网络基础知识
OSI是一个定义良好协议的规范机,定义了开放系统的层次结构
OSI7层协议
1. 物理层: 以二进制数据形式在物理媒体上传输数据。机械,电子,定时接口通信信道上的原始比特流输出。主要定义了物理设备的标准,比如网线的类型,光纤的接口类型,主要传输比特流,也就是01 01二进制数据
2. 数据层: 定义了如何格式化数据,错误的监测功能。以进行传输,让控制对物理介质的访问,这层还提供错误检测和纠正,以确保数据传输的可靠性
3. 网络层: 将网络地址翻译成对应的物理地址,为数据包选择路由
4. 传输层: 传输协议,同时进行流量的控制,关注的协议就是TCP和UDP
5. 会话层: 建立和管理应用程序之间的通讯
6. 表示层: 解决不同系统之间的通信语法的问题,数据格式化,代码转换,数据解密。
7. 应用层: 文件传输,电子邮件,文件服务,虚拟终端。规定发送端后接收端必须使用一个固定长度的消息头,消息头必须使用某种规定的组成,还需记录消息长的等信息,以便接收方更容易解析
Socket简介
Socket是对TCP/IP协议的抽象,是操作系统对外开放的接口
1. TCP的三次握手
TCP简介
TCP是面向连接的通信,基于字节流的传输通信协议,即传输数据之前,需要客户端和服务器端建立逻辑连接,它提供了两台计算机之间可靠无差错的数据传输。客户端和服务器端必须经过3次握手,建立连接才能通信(安全的)。
第一次握手: 客户端主动(active open)去connect服务器,并且发送SYN 包假设序列号为J,服务器是被动打开(passive open)
第二次: 服务器在收到SYN包后,它会发送一个SYN以及一个ACK(应答)给客户,
ACK的序列号是 J+1表示是给SYN J的应答,新发送的SYN K 序列号是K
第三次: 客户在收到新SYN K, ACK J+1 后,也回应ACK K+1 以表示收到了,然后两边就可以开始数据发送数据了
- 为什么要三次握手才能建立连接?
以保证应用程序接收到的数据不会因为网络上的传输问题而乱序。
2. TCP的四次挥手
“挥手” 是为了终止TCP连接,在算开一个TCP连接时需要客户端和服务端总共发出4个包以确认连接的断开,执行CLOSE来完成。
3. TCP和UDP的区别
UDP简介 UDP是无连接通信协议,即在数据传输时,数据的发送端和接收端不用建立连接。由于是无连接的,所以不能保证数据的完整性,安全性。
- 区别:
- TCP和UDP是 OSI模型中的协议,TCP面向连接,UDP无连接。
- TCP是利用握手提供了可靠机制,UDP有可能会丢失。
- TCP具备有序性。
- TCP因为要创建连接,所以在传输速度相对较慢,一般用于网页的流浪,文件下载,UDP适用于音频,视频的传输。
- TCP属于重量级的(20个字节),UDP属于轻量级的(8个字节)。
二. HTTP相关
超文本传输协议HTTP,基于TCP的连接方式
简介
- 协议也就是规则,对数据的传输格式,传输速率,做了统一的规定,通信双方必须同时遵守才能完成数据交换
- HTTP是一个客户端终端(用户)和服务器端(网站)请求和应答的标准(TCP)。通过使用网页浏览器、网络爬虫或者其它的工具,客户端发起一个HTTP请求到服务器上指定端口(默认端口为80)
特点
- 支持客户/服务器模式,浏览器通过URL 向服务端发送请求。
- 简单快速,只需传送请求方法和路径,请求方法常用的有“GET” 和"POST"
- 灵活,HTTP允许传输任意类型的数据对象
- 每个请求连接一次,服务器响应成功后就断开连接。
请求/响应步骤
- 客户连接到Web服务器
- 发送HTTP请求
- 服务器接收请求并返回HTTP响应
- 释放TCP连接
- 客户端浏览器解析HTML内容
请求结构
- 请求方法 + URL + 协议版本 + 请求头部 + 请求行
URL请求流程如图:
HTTP常见状态码如图:
HTTP和HTTPS的区别
二. 会话跟踪技术
对同一个用户对服务器的连续的请求和接受响应的监视。(将用户与同一用户发出的不同请求之间关联,为了数据共享)
为什么需要会话跟踪:
HTTP一次响应完成之后连接就断开了,下一次的请求需要重新连接,这样就需要判断是否是同一个用户,所以才应会话跟踪技术来实现这种要求
四种会话跟踪技术:
1).UR重写: URL(统一资源定位符)是Web上特定页面的地址,URL地址重写的原理是将该用户Session的id信息重写 到URL地址中,以便在服务器端进行识别不同的用户。URL重写能够在客户端停用cookies或者不支持cookies的时候仍然能够发挥作用。
2).隐藏表单域: 将会话ID添加到HTML表单元素中提交到服务器,此表单元素并不在客户端显示,浏览时看不到,源代码中有。
3).Cookie: 浏览器第一次请求服务器时,服务器端创建cookie,绑定数据后通过响应发送cookie到浏览器,浏览器收到cookie后存储在本地,下次请求时将cookie又带回服务器,cookie的数据是保存在浏览器内存中,称为临时Cookie,浏览器关闭后 这个Cookie对象将消失。
注意:浏览器对单个cookie的大小有限制,一般为4kb(不同浏览器存在差异),并对同一个域名下cookie的数量也有限制(一般20个以内)
4).session: 每一个用户都有一个不同的session,各个用户之间是不能共享的,在服务器端会创建一个session对象,产生一个sessionID来标识这个session对象,Session创建后服务器将Session ID 通过Cookie发送到客服端浏览器,而浏览器则将该Session ID 保存在会话Cookie中。当浏览器再次向服务器发送HTTP请求时,会将Session ID 信息一起发送给服务器。服务器根据该Session ID 在服务器内存找到对应的Session对象,就可取出共享数据。
Cookie和Session的区别
(1)Session存储在服务器端,Cookie存储在浏览器端
(2)Session存储无大小限制,Cookie存储数据有大小限制
(3)Session相对Cookie更安全
三. JDK1.8新特性
- Lambda表达式
- 函数式接口
- 方法引用和构造调用
- Stream API
- 接口中的默认方法和静态方法
- 新时间日期API
Lambda表达式
lambda表达式本质上是一段匿名内部类,也可以是一段可以传递的代码,使用前提是必须有接口,并且接口中只有一个抽象方法
函数式接口
定义:“函数式接口”是指仅仅只包含一个抽象方法的接口,每一个该类型的lambda表达式都会被匹配到这个抽象方法。jdk1.8提供了一个@FunctionalInterface注解来定义函数式接口,如果我们定义的接口不符合函数式的规范便会报错。
四大核心内置接口
1. Consumer<T>,消费型接口
2. Supplier<T>,供给接口
3. Function<T,R>,函数型接口(传替参数T,返回R)
4. Predicate<T>,断言型接口(传替参数T,返回Boolean)
Steam流
数据源:集合,数组
1. sorted(排序)
2. allMatch(检查是否所有元素匹配)
3. anyMatch(检查是否至少匹配一个)
4. noneMatch(检查是否没有匹配所有元素)
5. findFirst(返回第一个元素)
6. count(返回流中元素个数)
7. max(返回流中最大值)
8. min(返回流中最小值)
9. reduce(从某元素开始,计算和 )
10.collect(收集)
方法引用和构造调用
四. 重定向和转发的区别以及如何在框架里实现的:
区别:
(1)请求重定向和请求转发都是web开发中资源跳转的方式。
(2)请求转发是服务器内部的跳转,地址栏发生变化,只有一个请求相应,可以通过request域跳转目标的请求
(3)请求重定向是浏览器自动发起对跳转目标的请求,地址栏会发生变化,两次请求相应,无法通过request域传递对象
重定向:
两次request,客户浏览器发送http请求,web服务器接受后发送302状态码响应及对应新的location给客户浏览器,客户浏览器发现是302响应,则自动再发送一个新的http请求,请求url是新的location地址,服务器根据此请求寻找资源并发送给客户。在这里location可以重定向到任意URL,既然是浏览器重新发出了请求,则就没有什么request传递的概念了。在客户浏览器路径栏显示的是其重定向的路径,客户可以观察到地址的变化的。重定向行为是浏览器做了至少两次的访问请求的。
转发: 客户浏览器发送http请求,web服务器接受此请求,调用内部的一个方法在容器内部完成请求处理和转发动作,将目标资源发送给客户;在这里,转发的路径必须是同一个web容器下的url,其不能转向到其他的web路径上去,中间传递的是自己的容器内的request。在客户浏览器路径栏显示的仍然是其第一次访问的路径,也就是说客户是感觉不到服务器做了转发的。转发行为是浏览器只做了一次访问请求。
它们的区别:
(1) 转发在服务器端完成的bai;重定向是在客户端完成的
(2) 转发的速度快;重定向速度慢
(3)转发的是同一次请求;重定向是两次不同请求
(4) 转发不会执行转发后的代码;重定向会执行重定向之后的代码
(5)转发地址栏没有变化;重定向地址栏有变化
(6) 转发必须是在同一台服务器下完成;重定向可以在不同的服务器下完成
如何在框架里实现
在SpringMVC框架中,控制处理器中处理方法的return语句默认就是转发实现,只不过实现的是转发到视图
进程与线程
进程
进程是在内存中开辟一片临时空间在其中运行的程序,一个应用程序可以同时运行多个进程。
线程
线程是进程的一个执行单元,负责当前京城中的执行,一个进程至少有一个线程,一个进程中可以有多个线程,这个应用程序称之为多线程
线程调度
-
分时调度
所有线程轮流使用CPU的使用权,平均分配每个线程占用CPU的时间 -
抢占式调度
优先让优先级高的线程使用CPU,如果线程的优先级相同,那么会随机选择一个(线程的随机性)CPU在线程当中作高速的切换,我们学的JAVA是抢占式
主线程
执行主方法的线程(main),单线程执行是从上往下依次执行。
多线程
实现步骤:
- 要执行多线程,就必须继承Thread
- 在子类中重写run(),然后设置线程任务,
- new一个Thread的子类对象,然后执行start(),开启线程,执行run()方法
Thread类当中常用的方法
- getName()–获取线程名称
- currentThread()这个方法返回值是静态的,可以直接被类名调用。 获取当前正在执行的线程,再使用getNmae()获取线程名称
- setName(名字)给线程设置名称
- sleep() 使正在执行的线程以指定的毫秒数暂停,时间结束后,线程继续执行。
Runnable接口
- 尽量使用Runnable接口来创建多线程
- 避免了单继承的局限性,一个类只能继承一个父类,而使用接口还可以继承其它类,实现其它接口
- 增强了程序的扩展性,降低了程序的耦合度(解耦)
多线程原理,内存图解
多线程并发与并行
并发: 指两个或多个事件在同一时间段内发生(CPU高速切换执行任务)
并行: 指两个或多个事件在同一时刻发生(两个CPU同时分别执行任务)
线程安全问题
单线程程序不会出现线程安全问题的
多线程程序没有访问共享数据,不会产生安全问题
多线程程序访问了共享数据就会产生安全问题
线程的生命周期
- 新建状态(new Thread)
- 就绪状态
- 运行状态
- 阻塞状态
- 死亡状态
线程同步问题解决安全问题
- 1. 同步代码块(synchronized是一个关键字):
同步技术原理: 使用了一个锁对象(同步锁),多个线程一起抢夺CUP执行权,谁抢到了run方法谁先执行,当一个线程获取到锁对象,其它线程就会进入阻塞状态,只有等待该线程执行完成将锁对象归还后才能执行。
优缺点: 同步保证了线程的安全问题,但程序频繁的判断锁,获取锁,释放锁,程序的效率会降低。
-
2. 同步方法
代码的写法如图:
-
3. 锁机制(lock是一个接口)
等待唤醒机制
多个线程之间的一种协作机制
线程间通信
多个线程处理同一个资源,但是处理的动作(线程的任务)却不同。也就是线程之间进行合作。所以多线程之间需要一些协调通信,以次达到多线程共同操作一份数据。
等待唤醒中的方法
1. wait: 线程不再活动,不在参与调度,进入wait set中,因此不会浪费CPU资源,这是线程是无线等待状态(waiting),需要其它线程执行一个特别的动作,也就是**通知(notify)**在这个对象上等待的线程从wait set中释放出来,进入到调度状态。
2. notify: 会先择所统治对象的wait set中的一个线程释放,谁等的久就先唤醒谁。
3. notifyAll: 则会释放所统治对象的wait set上的全部线程。
- 注意:
- wait方法与notify方法必须由同一个锁对象调用
- wait方法与notify方法是属于Object类的方法,因为锁对象可以是任意对象
- wait和notify方法必须要在同步代码块或者是同步函数使用。因为必须要用锁对象调用这两个方法。
sleep()和wait()的区别
sleep()是 Thread 类,程序进入阻塞状态,释放cpu资源,在调用sleep()方法的过程中,线程不会释放对象锁。当指定时间到了,就会自动恢复运行状态
wait()来自 Object 类,调用wait方法,让出锁对象,并进入线程等待池,其他线程可以运行,需要配合notify()或者notifyAll()来释放,
为什么要使用线程池
避免频繁地创建和销毁线程,达到线程对象的重用。另外,使用线程池还可以根据项目灵活地控制并发的数目
java中用到的线程调度算法是什么
抢占式。一个线程用完CPU之后,操作系统会根据线程优先级、线程饥饿情况等数据算出一个总的优先级并分配下一个时间片给某个线程执行。
synchronized和lock区别(锁)
synchronized 可以说是悲观锁的设计思想,lock 乐观锁思想
(1)lock 是一个接口,需要创建 ReentrantLock
对象去控制加锁(lock()方法获取锁)和释放(unlock()方法释放锁),synchronized是关键字,可以在代码块,方法上使用,由JDK实现的
(2)synchronized 修饰的代码块发生异常时,jdk 会自动释放锁,而 Lock 需要在 finally
代码块中释放锁,否则容易造成死锁 (3)Lock 可以让等待锁的线程响应中断,synchronized 只能一直等待下去
(4)Lock可以绑定条件,实现分组唤醒需要的线程;synchronized要么随机唤醒一个,要么唤醒全部线程。
(5)Lock锁的范围有局限性,仅适用于代码块范围,而synchronized可以锁住代码块、对象实例、类;
乐观锁和悲观锁
悲观锁
总是认为会产生并发问题,因此每次对某资源进行操作时,都会持有一个独占的锁,就像synchronized,不管三七二十一,直接上了锁就操作资源了
乐观锁:
乐观锁:
总是认为不会产生并发问题,每次去取数据的时候总认为不会有其他线程对数据进行修改,因此不会上锁,但是在更新时会判断其他线程在这之前有没有对数据进行修改,一般会使用版本号机制或CAS操作实现。
乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库如果提供类似于write_condition机制的其实都是提供的乐观锁。
CAS 实现:当需要更新时,判断当前内存值与之前取得值是否相等,若相等,则更新,若失败,就重试,一般情况下为一个自旋操作,就是不断重试。
悲 观 锁:
总是会假设最坏的情况,每次取数据时都认为其他线程会修改,所以都会加锁(读锁、写锁、行锁等),当其他线程想要访问数据时,都需要阻塞挂起。可以依靠数据库实现,如行锁、读锁和写锁等,都是在操作之前加锁,在Java中,synchronized的思想也是悲观锁
什么是虚拟内存?
虚拟内存是计算机系统内存管理的一种技术,它通常是被分割成多个物理内存碎片
JVM的主要组成部分及作用
- 堆: 堆是java对象的存储区域,任何用new字段分配的java对象实例和数组,都被分配在堆上,java堆可用-Xms和-Xmx进行内存控制,jdk1.7以后,运行时常量池从方法区移到了堆上
- 方法区: 用于存储已被虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码等数据。
- 虚拟机栈: 虚拟机栈中执行每个方法的时候,都会创建一个栈桢用于存储局部变量表,操作数栈,动态链接,方法出口等信息。
- 本地方法栈: 与虚拟机发挥的作用相似,相比于虚拟机栈为Java方法服务,本地方法栈为虚拟机使用的Native方法服务,执行每个本地方法的时候,都会创建一个栈帧用于存储局部变量表,操作数栈,动态链接,方法出口等信息。
- 程序计数器: 指示java虚拟机下一条需要执行的字节码指令。
List和HashMap底层实现
HashMap概述
HashMap基于Map接口实现,元素以键值对的方式存储,并且允许使用 null 建和 null
值, 因为key不允许重复,因此只能有一个键为null,另外HashMap不能保证放入元素的顺序,它是无序的,和放入的顺序并不能相同。HashMap是线程不安全的
HashMap的数据存储结构
- HashMap 是 Map 的一个实现类,它代表的是一种键值对的数据存储形式。Key 不允许重复出现,Value 随意。jdk 8 之前,其内部是由数组+链表来实现的,而 jdk 8 对于链表长度超过 8 的链表将转储为红黑树。HashMap 底层就是一个数组结构,数组中的每一项又是一个链表。当新建一个 HashMap 的时候,就会初始化一个数组,HashMap的数据结构:HashMap实际上是一个“链表散列”的数据结构,即数组和链表的结合体。当我们往Hashmap中put元素时,首先根据key的hashcode重新计算hash值,根绝hash值得到这个元素在数组中的位置(下标),如果该数组在该位置上已经存放了其他元素,那么在这个位置上的元素将以链表的形式存放,新加入的放在链头,最先加入的放入链尾.如果数组中该位置没有元素,就直接将该元素放到数组的该位置上。
- HashMap的初始容量为16,Hashtable初始容量为11,两者的填充因子默认都是0.75
- JDK1.8版本中,HashMap数据存储在Node数组中
红黑树
红黑树本质上是一种二叉查找树,但它在二叉查找树的基础上额外添加了一个标记(颜色),同时具有一定的规则。这些规则使红黑树保证了一种平衡,插入、删除、查找的最快时间复杂度都为 O(logn)
- JDK1.8 HashMap由数组和链表加红黑树来实现对数据的存储,当链表长度超过阈值(8)时,将链表转换为红黑树。在性能上进一步得到提升。HashMap采用Entry数组来存储key-value对,每一个键值对组成了一个Entry实体,Entry类实际上是一个单向的链表结构,它具有Next指针,可以连接下一个Entry实体,以此来解决Hash冲突的问题。
- 数组存储区间是连续的,占用内存严重,故空间复杂的很大。但数组的二分查找时间复杂度小,为O(1);数组的特点是:寻址容易,插入和删除困难;
- 链表存储区间离散,占用内存比较宽松,故空间复杂度很小,但时间复杂度很大,达O(N)。链表的特点是:寻址困难,插入和删除容易。
HashMap和Hashtable的区别
- Hashtable是线程安全的,它的每个方法中都加入了Synchronize方法。在多线程并发的环境下,可以直接使用Hashtable,不需要自己为它的方法实现同步,HashMap不是线程安全的,在多线程并发的环境下,可能会产生死锁等问题
- HashTable不允许储存null值(key和value都不可以),HashMap允许使用null值(key和value)都可以
- Hashtable默认的初始大小为11,之后每次扩充,容量变为原来的2n+1。HashMap默认的初始化大小为16。之后每次扩充,容量变为原来的2倍
List
List实现了 Collection 接口,它的数据结构是有序可以重复的结合。 三个实现类:ArrayList,LinkList,Vetor
**ArrayList:**底层数据结构使数组结构,查询速度快,增删改慢,ArrayList当初始化容量超过10时,会new一个50%de ,把原来的东西放入这150%中
LinkedList:
- 底层基于双向链表实现,查询慢,增删速度快。
- 链表里存放这有一个个的节点,一个节点分为三个部分,当添加元素时,LinkedList要判断链表里是否有节点,无论有没有都会创建一个节点,会把添加的元素放在尾部,等待下一个节点指向
Mysql和oracle区别
(1)对事务的提交
MySQL默认是自动提交,而Oracle默认不自动提交,需要用户手动提交,需要在写commit,指令或者点击commit 按钮
(2)对事物的支持
MySQL在innodb存储引擎的行级锁的情况下才可支持事务,而Oracle则完全支持事务
(3)分页查询
MySQL是直接在SQL语句中写"select… from …where…limit x, y",有 limit 就可以实现分页;而 Oracle 则是需要用到伪列 rownum 和嵌套查询
(4)事务隔离级别
(5)保存数据的持久性
MySQL是在数据库更新或者重启,则会丢失数据,Oracle 把提交的sql操作线写入了在线联机日志文件中,保持到了磁盘上,可以随时恢复
(6)并发性
Oracle 的并发性更好,Oracle 使用行级锁,对资源锁定的粒度要小很多,只是锁定sql需要的资源,并且加锁是在数据库中的数据行上,不依赖与索引
(7)分区表和分区索引
MySQL的分区表还不太成熟稳定。
Oracle 的分区表和分区索引功能很成熟,可以提高用户访问db的体验。
(8)最大的区别
MySQL是轻量型数据库,并且免费,没有服务恢复数据。
Oracle是重量型数据库,收费,Oracle公司对Oracle数据库有任何服务。
(9)
Mysql的字符串用双引号引起来,orcle 用单引号。
五. 数据库的事务和隔离级别
事务
原子性(Atomic):
事务中各相关操作,要么都执行,要么都不执行,任何一项操作的失败都会导致整个事务的失败。
一致性(Consistent):
事务结束后后系统状态是一致的。
隔离性(Lsolated):
并发执行的事务彼此无法看到对方的中间状态
持久性(Durable):
事务完成后所做的改动都会被持久化,即使发生灾难性的失败,通过日志和同步备份可以在故障发生后重建数据
隔离级别
- 读位提交(Read uncommitted):脏度,就是一个事务可以读取另一个未提交事务的数据
- 读已提交(Read Committed):可避免脏度,就是只能读到已经提交了的内容
- 可重复度(Repeated Read):就是专门针对“不可重复读”这种情况而制定的隔离级别,自然,它就可以有效的避免“不可重复读”。而它也是MySql的默认隔离级别。
- 串行读(序列化 Serializable):脏读,不可重复读,换读都可以避免
六. 数据库的索引
什么是索引
索引也是一张表,是数据库管理系统中一个排序的数据结构,以协助快速查询,更新数据库表中数据,索引的实现通常使用了B树及其变种B+树
索引的优点
- 建立索引的列可以保证行的唯一性,生成唯一的rowld
- 索引可以有效缩短数据的查询时间,减少I/O次数
- 索引可以加快表与表之间的连接
缺点
- 创建索引和维护索引要消耗时间,索引也会占用空间,并且随着数据量越大消耗的时间越多;
- 当对表中的数据进行增加、删除、修改时,索引也需要动态的维护,降低了数据
的维护速度;
索引分类
单例(普通索引,唯一索引,主键索引),组合索引,全文索引,空间索引
组合索引: 在表中的多个字段组合上创建的索引,只有在查询条件中使用了这些字段的左边的字段时,索引才会被使用,使用组合索引时遵循最左前缀集合。
全文索引: 只有在 myISAM 引擎才能使用,只能在 CHAR/VARCHAR/TEXT 类型字段上使用全文索引,全文索引其实就是在一堆文字中
空间索引: 空间索引是空间数据类型的字段建立的索引
创建原则
- 尽量选择区分度高的列做为索引
- 索引不能参与运算,尽量保持“干净”
- 尽可能的扩展索引,不要建立新索引
SQL语句本身的优化以及创建表的注意事项
(1) 在表中建立索引,优先考虑where、group by使用到的字段
(2) 尽量避免使用 select *,返回无用的字段会降低查询效率,使用具体的字段
(3) 尽量避免使用 in 和 not in,会导致数据库索引放弃索引进行全表扫描
如何实现动态SQL的
七. 说一下JDBC,Mybatis底层实现原理
八. Spring中DI和IOC
DI(Dependency Injection依赖注入)
DI也就是应用程序依赖于IOC容器,应用程序需要IOC容器来提供对象需要的外部资源
IOC(控制反转)
控制反转: 也就是如果A对象想使用C的对象,A不主动去创建对象C,而是等待IOC容器来获取一个C的实例,通过IOC容器来建立他们之间的关系。
IOC是一种思想,是Spring的核心,IOC容器来注入某个对象,就是诸如某个对象所需要的外部资源,对于spring框架来说,就是由spring框架来负责控制的生命周期和对象间的关系,这样就有效的降低了代码之间的耦合,有利于功能复用,使得程序的整个体系变得非常灵活。
说明:bean就是IOC
九. SpringBoot
- SpringBoot 内嵌Tomcat 和Jetty 服务器。可以直接打jar包 放在服务器上运行,
- 自动配好SringMVC
- 自动配好Web常见功能,如:字符编码问题
- 自动starter依赖,简化构建配置
- 启动类的@SpringBootApplication注解由@SpringBootConfiguration,@EnableAutoConfiguration,@ComponentScan三个注解组成,三个注解共同完成自动装配;
- @SpringBootConfiguration 注解标记启动类为配置类
- @ComponentScan
注解实现启动时扫描启动类所在的包以及子包下所有标记为bean的类由IOC容器注册为bean- @EnableAutoConfiguration通过 @Import 注解导入
AutoConfigurationImportSelector类,然后通过AutoConfigurationImportSelector
类的 selectImports
方法去读取需要被自动装配的组件依赖下的spring.factories文件配置的组件的类全名,并按照一定的规则过滤掉不符合要求的组件的类全名,将剩余读取到的各个组件的类全名集合返回给IOC容器并将这些组件注册为bean
- Springboot 简化了很多配置,例如工厂、整合SpringMVC 、 Mybatis 之类的固定配置 ,创建工程后可直接开发业务逻辑。另外,第三方工具的使用,也只需引入starter,配置文件配置一下账号密码,有种开箱即用的感觉。
- spring-boot-starter:核心Starter,包含自动配置的支持,日志以及YAML解析等。
- spring-boot-starter-aop:提供SpringAOP和AspectJ的面向切面编程支持。
- spring-boot-starter-data-jpa:提供Spring Data JPA支持(由Hibernate提供底层支持)。
- spring-boot-starter-data-mongodb:提供Spring Data MongoDB和MongoDB支持。
- spring-boot-starter-jdbc:提供了JDBC支持(由Tomcat JDBC连接池提供支持)。
- spring-boot-sarter-jersey:提供使用JAX-RS和Jersey构建RESTful风格的Web应用的支持。
- spring-boot-starter-web:提供使用Spring
- MVC构建Web(包含RESTful)应用的支持,使用Tomcat作为默认嵌入式容器。
- spring-boot-starter-webflux:提供使用Spring Framework的Reactive
- Web构建WebFlux应用的支持。 spring-boot-starter-actuator:SpringBoot的Actuator支持,其提供了生产就绪功能,帮助开发者监控和管理应用
SpringBoot源码分析
开箱即用
SpringBoot程序启动会依次执行多个启动项,当pom.xml文件中添加某个特定的jar包文件时, 启动项就会加载该文件,实例化对象.完成自动化的装配.从而实现了开箱即用的效果
spring
SpringBoot常用注解
-
@SpringBootApplication 常用在启动类上 包含(
@SpringBootConfiguration 标注当前类是配置类,这个注解继承自@Configuration并会将当前类内声明的一个或多个以@Bean注解标记的方法的实例纳入到srping容器中,并且实例名就是方法名。
@EnableAutoConfiguration 实现了SpringBoot开箱即用的特点,会自动扫描启动类包路径下面的所有类
@ComponentScan) -
@Repository 用于标注数据访问组件,即DAO组件
-
@Service 用于标注业务层组件
-
@RestController 用于标注控制层组件,处理http请求
包含(@Controller,@ResponseBody) -
@Component泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注
-
@Bean 相当于XML中的,放在方法的上面,而不是类,意思是产生一个Bean,并交给Spring管理
-
@AutoWired 它可以对类成员变量,方法及构造函数进行标注,完成自动装配(依赖注入)
-
@RequesMapping 是用来处理请求地址映射的注解,用在方法或者类上
-
@PathVariable 路径变量。方法与大括号里的名字一样相同
-
@RequesParam 用在方法的参数前面
SpringBoot自动配置的原理
在 spring 程序 main 方法中 添加@SpringBootApplication 或者@EnableAutoConfiguration 会自动去 maven 中读取每个 starter 中的 spring.factories 文件 该文件里配置了所有需要被创建 spring 容器中的 bean
Spring里如何实现事务的
说一下分布式锁
说一下分布式项目里如何解决跨域问题
在你的项目中,你的并发量是多少,如何解决并发问题
说一下是如何配置Nginx以及正反向代理
会Linux系统吗?常用命令有哪些?java项目是如何打包部署的
常用的数据库中间件,如何使用的,作用是什么?
AOP面向切面
AOP 是一种设计思想,面向切面编程,它是面向对象编程(OOP)的一种补充和完善,它可以通过预编译方式和运行期动态代理方式,在不改变目标对象源代码的情况下给程序统一添加额外功能的一种技术。异常处理/事务控制/日志收集
AOP的5个通知
- before 通知 在执行目标方法之前执行
- afterReturning 通知 在目标方法执行之后执行
- afterThrowing 通知 在目标方法执行之后报错时执行
- after 通知 无论什么时候程序执行完成都要执行的通知
上述的4大通知类型,不能控制目标方法是否执行.一般用来记录程序的执行的状态.
一般应用与监控的操作.
- around 通知(功能最为强大的) 在目标方法执行前后执行.
因为环绕通知可以控制目标方法是否执行.控制程序的执行的轨迹.
AOP切入点表达式
- bean(“bean的ID”) 粒度: 粗粒度 按bean匹配 当前bean中的方法都会执行通知.
- within(“包名.类名”) 粒度: 粗粒度 可以匹配多个类
- execution(“返回值类型 包名.类名.方法名(参数列表)”) 粒度: 细粒度 方法参数级别
- @annotation(“包名.类名”) 粒度:细粒度 按照注解匹配
JDK代理和CGLIB代理
代理对象有JDK代理和CGLIB代理,系统默认CGLIB代理,可以在配置文件中配置为JDK代理。使用JKD代理,那么代理对象与目标对象是兄弟关系,都实现了接口,使用CGLIB代理,则代理对象可以继承了目标对象,Aspect切面(aop)可以为目标对象增加一些特有功能,proxy代理对象去调用切面,然后切面去调用目标对象,实现目标对象的多功能
JDK代理: 使用 JDK 代理,代理对象与目标对象是兄弟关系,都实现了接口,优点降低代码的耦合对,可以有多个实现类。
CGLIB代理: 继承了目标对象,如果目标对象被 final 修饰了则不能继承
SpringMVC调用流程
SpringMVC如何做的拦截
拦截器: Spring MVC中的拦截器(Interceptor)类似于Servlet中的过滤器(Filter),它主要用于拦截用户请求并作相应的处理。例如通过拦截器可以进行权限验证、记录请求信息的日志、判断用户是否登录等。
拦截方法: 继承或者实现 HandlerInterceptor 接口
1. 前端控制器(DispatcherServlet): 用户发送请求 URL 到前端控制器 DispatcherServlet,所有请求的中转站
2. 处理映射器(HandlerMapping): (标识用户的请求路径/后端执行的方法的映射关系,kye-请求路径,vaule-方法)DispatcherServlet 收到请求调用 HanlderMapping 处理器映射器,HanlderMapping 处理器映射器根据请求 URL 地址找到具体的处理器,生成处理器以及处理器拦截器(如果有则生成)一并返回给前端控制器 DispatcherServlet;
3. 处理适配器(HandlerAdaptor): DispatcherServlet 根据获得的 Handler,选择一个合适的HandlerAdapter,HandlerAdapter 将 controller 执 行 结 果 ModelAndView 返 回 前 端 控 制 器DispatcherServlet
4. 视图解析器(ViewReslover): 前端控制器 DispatcherServlet 将 ModelAndView 传给 ViewReslover 视图解析器,器解析(拼接前后缀)后返回具体 View
5. 响应结果: 前端控制器 DispatcherServlet 对 View 进行渲染视图然后返回给用户
如图:
Nginx
概述: Nginx 是一个高性能的HTTP和反向代理web服务器,Nginx是一款轻量级的web服务器/反向代理服务器及电子邮件代理服务器
特点: 占有内存少,并发能力强,Nginx 的并发能力在同类型的网页服务器中表现较好
nginx: 3-5万/秒
Nginx和Apache之间的不同点
- 抗并发,Nginx 处理请求是异步非租塞的,而 Apache 则是阻塞型的,在高并发下 Nginx 能保持 低资源 低消耗 高性能
- Nginx 比 Apache占用更少的内存资源
- Nginx 所有请求都有一个线程,多个连接可以对应一个进程, Apache 单个线程处理单个请求,一个连接对应一个进程。
Nginx为什么不使用多线程?
采用单线程来异步非阻塞处理请求(管理员可以配置Nginx主进程的工作进程的数量)(epoll),不会为每个请求分配cpu和内存资源,节省了大量资源,同时也减少了大量的CPU的上下文切换。所以才使得Nginx支持更高的并发。
使用反向代理服务器的优点
- 提高访问速度
- 防火墙作用,反向代理机制保护了真实的服务器信息
- 通过代理服务器访问不能访问的目标站点
负载均衡
Nginx
起到了一个代理服务器的角色,为了避免单独一个服务器压力过大,提升后台服务器的处理能力,可以增加服务器,实现负载均衡的策略,将来自用户的请求转发给不同的服务器。
负载均衡的策略:
- **轮询策略:**会按照 Nginx.conf 中配置文件的顺序依次访问
- 权重策略(weight): 权重设置的越高,分配到的请求就会越多
- ip_hash策略: 当用户发送请求IP,是通过ip_hash算法进行服务器的选择
正向代理服务器和返向代理服务器
正向代理服务器: 正向代理隐藏了用户的信息,用户的请求被代理服务器接收代替,到了服务器
反向代理: 用户发送请求到服务器,其实访问的是反向代理服务器,由反向代理服务器将数据返回给用户,但是用户并不知道,保护了真实服务器的信息。
Redis
概述: Redis 是一个使用 C 语言编写的,开源的(BSD许可)高性能非关系型(NoSQL)的键值对数据库,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件
nginx: 3-5万/秒
redis: 读: 11.2万/秒 写: 8.6万/秒 平均10万/秒
吞吐量: 50万/秒
Redis有哪些优缺点
优点:
- 读写性能优异, Redis能读的速度是110000次/s,写的速度是81000次/s。
- 支持数据持久化,支持AOF和RDB两种持久化方式。
- 支持事务,Redis的所有操作都是原子性的,同时Redis还支持对几个操作合并后的原子性执行。
- 数据结构丰富,除了支持string类型的value外还支持hash、set、zset、list等数据结构。
- 支持主从复制,主机会自动将数据同步到从机,可以进行读写分离。
缺点:
- Redis 不具备自动容错和恢复功能,主机从机的宕机都会导致前端部分读写请求失败,需要等待机器重启或者手动切换前端的IP才能恢复
- 数据库容量受到物理内存的限制,不能用作海量数据的高性能读写
- Redis 较难支持在线扩容,在集群容量达到上限时在线扩容会变得很复杂
Redis和memcached有哪些优势
- memcached所有的值均是简单的字符串,redis 作为替代者,支持更为丰富的数据类型
- redis 的速度比 memcached 快很多
- redis 支持数据的备份
Redis官方为什么不提供 Windows版本?
应为 Redis 是单线程高性能的,所以Redis需要单线程轮询,Linux轮询用 epoll,Windows 用 selector,性能方面 epoll 是高于 selector的
Redis支持的数据类型
- String(字符串) String 类型是一个二进制安全的,一个Key 对应一个Value ,一个值最大能储存512MB,Redis 的String 可以包含任何数据(比如 图片或者 序列化的对象)
- List(列表) Redis 列表是简单的字符串列表,按照插入顺序排序,常用命令(lpush、rpush、lpop、rpop、lrange等)
- Sets(集合) Redis的Set是string类型的无序集合,集合是通过哈希表实现的
- Sorted Set(有序集合) Sorted Set和Set一样也是string类型元素的集合,且不允许重复的成员
- hashes(哈希) 通常用来存储一个用户信息的对象数据,例如记录网站每个用户个人主页的访问量,常用命令(hget、hset、hgetall)
Redis为什么要把所有的数据放到内存中
如果不将数据放到内存中,磁盘的I/O速度会严重影响redis的性能。Redis为了达到最快的读写速度将数据都读到内存中,并通过异步的方式将数据写入磁盘。所以Redis具有快速和数据持久化的特性
为什么要用 Redis /为什么要用缓存
主要从“高性能”和“高并发”这两点来看待这个问题
Redis为什么这么快
- 数据结构简单,对数据操作也简单
- 采用单线程,避免了不必要的上下文切换和竞争条件
- 使用多路I/O复用模型,非阻塞IO
Redis由那些适合的场景
- 会话缓存: 可以使用 Redis 来统一存储多台应用服务器的会话信息
- 全页缓存: 除基本的会话token之外,Redis还提供很简便的FPC平台。
- 消息队列: set数据类型操作,使得Redis能作为一个很好的消息队列平台来使用
- 排行榜/计数器: 集合(Set)和有序集合(Sorted Set)使得对数据进行地增减的操作实现非常简单。
什么是Redis持久化?
持久化就是把内存的数据写到磁盘中去,防止服务宕机了内存数据丢失。
Redis 提供了两种持久化方式:RDB(默认) 和 AOF
Redis中的事物
说明:命令 Multi 开启事物, 命令 Exec 执行事物,命令 discard 结束当前组队时候的事物
1. 当开启事物组队期间有错误的命令,执行事物时都不执行
2. 当组队时没有错误命令,执行时候报错,组队中的事物只有错误命令任务不执行,其他都执行
Redis事物冲突
悲观锁解决: 每次去拿数据都会上锁,这样别人就只有进入阻塞状态,直到释放了锁(列如表级锁,读锁,写锁)
乐观锁解决: 每次去拿数据的时候都认为别人不会修改,所以不上锁,但是在更新的时候会判断此期间别人有没有去更新(使用版本号机制),乐观锁适用于多读的操作,提高吞吐量。
- 在执行 multi 之前,先执行 watch key(这里可以监视多个 key),在事物执行之前这个 key 被其他命令改动,那么事物将会被结束
Redis 举例 秒杀项目
说明:当并发情况下秒杀会存在 超卖 和 连接超时问题
解决超卖: 通过事物 + 乐观锁(watch 来监视 key)来解决
解决连接超时: 设置连接池,写一个连接池工具类,然后通过连接池得到 redis 的对象
RDB模式
RDB模式是Redis 默认策略,能够定期持久化,记录的是内存数据的快照,持久化效率较高,但可能会导致数据的丢失,常用命令(save:主动操作,bgsave:将内存数据采用后台运行的方式)
AOF模式
AOF默认条件下是关闭的,需要手动开启,记录的是用户实时操作,可以实现实时持久化操作
面试题: 在公司中不小心执行了 flushAll 命令怎么办 答:可以找到 “ aof ”文件之后,去删除 flushAll 命令之后重启 redis 就可
缓存穿透?如何避免?什么是缓存雪崩?何如避免?**
缓存穿透: 用户频繁访问数据库中不存在的数据,可能出现缓存穿透的现象,如果该操作是高并发操作,则可能直接威胁数据库服务器
解决方案:
- 采用IP限流的方式:降低用户访问服务器次数. IP动态代理(1分钟变一次)
- 微服务的处理方式:利用断路器返回执行的业务数据即可不执行数据库操作 从而保护了数据库.
- 微服务处理方式:API网关设计. 不允许做非法操作
缓存击穿: key数据存在,当key对应的数据在缓存中过期.同时用户高并发访问该数据,则可能导致数据库宕机.该操作称之为
缓存击穿
解决方案: 调整key的过期时间.
缓存雪崩: key数据存在,但在某一时刻,缓存中大量key数据过期,而此时大量高并发请求访问,可能导致数据库服务器宕机. 这种现象称之为缓存雪崩.
解决方案:
- 采用多级缓存
- 设定不同的超时时间
- 静止执行 flushAll 等敏感操作
Redis如何做内存优化?
说明:由于redis在内存中保存数据.如果一直存储,则内存数据必然溢出.所以需要定期维护内存数据的大小.
维护策略:删除旧的不用的数据,保留新的常用的数据
内存优化方案
- LRU算法: 即最近最少使用,是一种常用的页面置换算法,选择最近最久未使用的页面予以淘汰。计算维度是 “ 时间” 是目前内存中最好用的数据置换算法
- LFU算法 : 即最不经常使用页置换算法,要求在页置换时置换引用计数最小的页,使用次数
- 随机算法: 随机
- TTL算法: 根据剩余的存活时间,将马上要超时的数据提前删除.
Redis分片机制(多级缓存)
如果需要Redis存储海量的内存数据,使用单台redis不能满足用户的需求,所以可以采用Redis分片机制实现数据存储
一致性Hash算法
- 是一种特殊的哈希算法,目的是解决分布式缓存的问题,在移除或者添加一个服务器时,能够尽可能小地改变已存在的服务请求与处理请求服务器之间的映射关系
- 常规hash由多少位16进制数组成??? 8位16进制数组成 2^32次方
- 如果对相同的数据进行hash计算问结果是否相同??? 结果必然相同的。
哨兵机制
启动命令: redis-sentinel sentinel.conf
- 当哨兵启动时,会链接redis主节点,同时获取所有节点的状态信息
- 当哨兵连续3次通过心跳检测机制(PING-PONG),如果发现主机宕机,则开始选举.
- 哨兵内部通过随机算法筛选一台从机当选新的主机.其他的节点应该当新主机的从
Redis集群
需求:Redis 集群既可以实现内存数据的扩容,同时实现高可用机制(不用第三方)。
采用redis集群,可以保证数据分散存储,同时保证数据存储的一致性.并且在内部实现高可用的机制.实现了服务故障的自动迁移
Redis集群面试问题
- redis 集群中一共可以存储16384个KEY? 答:不对的,16384只是槽位的数量 只负责规划这个数据归谁管理的问题.至于数据如何存储,是由redis内存决定的. hash(key1) = 3000, hash(key2)= 3000;
-
- Redis 集群中最多可以有多少台主机? 16384台主机
- Redis 中如果遇到多线程操作,是否有线程安全性问题 ? 答:没有,因为 redis 服务器是单进程单线程操作.,每次操作都是由一个线程执行,所以不会有线程安全性问题.
- Redis 如何实现内存数据的优化? 答:LRU/LFU/随机算法/TTL
JSONP跨域
同源策略
协议名称 / 域名地址 / 端口号,三个如果相同则满足同源策略,浏览器可以正常解析返回值,如果有一个不同,则违反了同源策略,浏览器不会解析返回值。
什么是跨域
浏览器解析 Ajax 页面,违反了同源策略时,称之为跨域请求
JSONP跨域访问
JSONP是JSON的一种使用模式 利用 JavaScript 中的 src属性进行跨域请求。(还要自定义回调函数(function()),将返回值进行特殊格式封装 (callback (json)))。
注意 :由于是利用 src 属性进行调用,所以只支持 get 类型的请求
CORS跨域
CORS 是当前实现跨域的主流方式,现在所有的主流浏览器都支持该功能,需要在服务器端配置是否允许跨域的配置,只要配置了(在响应头中添加允许跨域的标识)则同源策略不生效,则可以实现跨域。
HttpClient(工具API)
- 实现了所有 HTTP 的方法(GET、POST、PUT、HEAD、DELETE、HEAD、OPTIONS 等)
- 支持 HTTPS 协议
- 支持代理服务器(Nginx等)等
- 支持自动(跳转)转向
用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包,并且它支持 HTTP 协议最新的版本和建议。JSONP, CORS跨域做一些简单的,不需要处理业务逻辑的跨域(不是很安全,JS 只支持一些微调,业务逻辑不在JS 当中写),使用 HttpClient 在业务层发起请求。
实现单点登录系统(SSO)
session会话是不共享的,zookeeper+Dubbo实现 SSO,核心机制就是用cookie的方式来保留用户的数据,SSO服务器端生成一个秘钥,将秘钥(ticket)信息存到Redis,web服务器也要将秘钥保存在cookie,一般会设置超时时间(7天内有效)。当下次访问,就会拿着 tiket 与Redis中的 tiket 进行校验,如果有交易信息,还会进行二次校验登录。
2. 购物车的实现
3. 拦截器实现用户单点登录权限控制
4. 判断是用户否登录–判断是否有cookie + 判断redis中是否有记录
Dubbo?
什么是Dubbo
说明:默认端口 20880
Dubbo 是一款高性能,轻量级的开源Java RPC服务框架,它提供了三大核心功能:面向接口的远程方法调用,智能容错和负载均衡,以及自动注册和发现。底层实现就是对 TCP/IP进行了包装。
为什么要用Dubbo?
主流的微服务框架,服务越来越多,服务之间的调用和依赖关系也越来越复杂,诞生了面向服务的框架体系(SOA)
**特性: **
Dubbo的节点角色
在 Provider 上可以配置的 Consumer 端的属性有哪些?
1. timeout: 方法调用超时
2. retries: 失败重试次数,默认重试两次
3. loadbalance: 负载均衡算法,默认是随机的
4. actives : 消费者端,最大并发调用限制
SOA思想说明
面向服务的架构(SOA)是一个组件模型,将应用程序不同的功能单元(称为服务)进行拆分,透过这些服务之间定义良好的接口和协议联系起来,接口是采用中立的方式进行定义的。
Dubbo 和 SpringCloud 有什么区别?
Dubbo: 具有调度、发现、监控、治理等功能,支持相当丰富的服务治理能力。Dubbo架构下,注册中心对等集群,并会缓存服务列表已被数据库失效时继续提供发现功能,本身的服务发现结构有很强的可用性与健壮性,足够支持高访问量的网站。
SpringCloud: Spring Cloud由众多子项目组成,如SpringCloudConfig、SpringCloudNetflix、SpringCloudConsul 等
Dubbo 都支持什么协议,推荐用哪种?
dubbo
Dubbo启动需要Web容器吗?
Dubbo 服务容器是一个 standalone 的启动程序,后台不需要 Tomcat 或 Web 容器的功能,如果硬要用 Web 容器,只会增加复杂性,也浪费资源。
Dubbo负载均衡策略
- @Reference(loadbalance=“leastactive”) //挑选压力最小的服务器访问
- @Reference(loadbalance=“consistenhash”) //一致性Hash算法
- @Reference(loadbalance=“roundrobin”) //轮询算法
- @Reference(loadbalance=“random”) //默认的条件就是随机算法
注册了多个同一样的服务,如果测试指定的某一个服务呢?
可以配置环境点对点直连,绕过注册中心,将以服务接口为单位,忽略注册中心的提供者列表
Dubbo支持多协议吗?
Dubbo 允许配置多协议,在不同服务上支持不同协议或者同一服务上同时支持多种协议。
Dubbo 服务之间的调用是阻塞的吗?
默认是同步等待结果阻塞的,支持异步调用。 Dubbo 是基于 NIO 的非阻塞实现并行调用,客户端不需要启动多线程即可完成并行调用多个远程服务,相对多线程开销较小,异步调用会返回一个 Future 对象。
Dubbo支持分布式事务吗?
不支持,后续可能采用基于 JTA/XA 规范实现
Dubbo如何优雅停机
Dubbo 是通过 JDK 的 ShutdownHook 来完成优雅停机的,所以如果使用kill -9 PID 等强制关闭指令,是不会执行优雅停机的,只有通过 kill PID 时,才会执行
服务读写推荐的容错策略是怎样的的?
读操作建议使用 Failover 失败自动切换,默认重试两次其他服务器。 写操作建议使用 Failfast 快速失败,发一次调用失败就立即报错
Dubbo在使用过程中遇到的问题
- 同时配置了XML和properties文件,则properties中的配置无效
- 无法连接到注册中心
- Dubbo 的设计目的是为了满足高并发小数据量的 rpc 调用,在大数据量下的性能表现并不好,建议使用 rmi 或 http 协议。
你觉得用 Dubbo 好还是 Spring Cloud 好?
扩展性问题,根据业务来使用,只有合不合适,我更倾向于 Dubno,SpringCloud版本升级太快,组件多,配置繁琐。
微服务
- 为了降低代码的耦合性,将项目进行了拆分,按照功能模块拆分为若干个项目,该项目称之为服务(分布式思想)
- 如果采用微服务的结构,要求服务器如果出现了故障应该实现自动化的故障的迁移(高可用HA)。
优点
微服务的好处有:服务独立、扩展性好、可靠性强
缺点
比如运维复杂性,分布式复杂性、监控复杂性等等
SpringCloud
- SpringCloud是基于SpringBoot的一整套实现微服务的框架。它提供了微服务开发所需的配置管理、服务发现、断路器、智能路由、微代理、控制总线、全局锁、决策竞选、分布式会话和集群状态管理等组件。最重要的是,基于SpringBoot,会让开发微服务架构非常方便。
- Spring cloud是一个工具集,集成多个工具集,来解决微服务中的各种问题
SpringCloud技术组成
eureka: 微服务治理,服务注册和发现
ribbon: 负载均衡,请求重试
hystrix: 断路器,服务降级、熔断
feign: ribbon + hystrix 集成,并提供声明式客户端
zuul: API 网关,提供微服务的统一入口,并提供统一的权限验证
config: 配置中心
bus: 消息总线,配置刷新
sleuth+zipkin: 链路跟踪
hystrix dashboard 和 turbine: hystrix 数据监控
面试总结话术
== 和 equals 的区别是什么?
== 是关系运算符 :
== 在基本数据类型中比较两个值的内容是否相等
== 在引用类型型中比较的是两个对象的地址是否相等
equals()是Object类中的方法 :
- 基本数据类型无法使用equals()方法
- 在引用类型中若是没有重写Object类时,则默认使用Object类的equals方法,则仍然利用“==”比较两个对象的内存地址,若是重写Object类的equals方法,则调用子类重写后 的方法可以进行内容或值的比较
java 容器都有哪些?
List、Set、Map
ArrayList 和 LinkedList 及Vector的区别是什么?
1. ArrayList的底层数据结构为数组,增删慢、查询快,线程不安全,效率高,每次扩容为原来数组的1.5倍
2. LinkedList的底层数据结构为链表,增删快、查询慢,线程不安全,效率高
3. Vector底层数据结构为数组,增删慢、查询快,线程安全,效率低,每次扩容为原来数组的2倍
Set
1.hashset底层是hashmap,保证元素的唯一性,不能有重复元素
2.底层是二叉树,保证元素的排序,自然顺序,实现了comparable进行排序
Map
hashMap数组+链表+红黑树: 线程不安全超过阀值,就把链表转成红黑树,如果链表长度低于6,会把红黑树转回链表
hashTable线程安全,锁住整个对象+链表: 实现线程安全方式是:是在修改数组时,锁住整个hashtable,效率低
ConcurrentHashMap数组+链表+红黑树,优化了HashTable,效率提高: 通过将锁细腻度到每个元素来提升效率,CAS+synchronized使锁更细化。
扩容机制: 数组中元素的数量 >= 16 * 0.75 并且所在位置元素不等于 null 就会扩容为原来的 2 倍(创建一个新的数组,将老数组的元素放入新数组,然后把新数组给老数组),扩容的同时会从新算一下哈希码和位置
hash冲突: 通过key调用hashCode算出哈希码,然后再算出元素在数组中的位置,当数组节点索引有元素(key相同,就覆盖,key不相同会以链表的形式存储),1.7是头插法,1.8后是尾插法。
负载因子: 如果为1,空间利用率得到了很大的满足,但很容易发生碰撞,就会引入链表,查询效率变低,如果为0.5,碰撞的几率低,查询效率高,但空间利用率低,所以折中为0.75
final在java中有什么用
1. 修饰变量只能一次赋值以后值不能被修改(常量)
2. 修饰方法不能被重写
3. 修饰类不能被继承
java 中的 Math.round(-1.5) 等于多少?
Math.round(-1.5)的返回值是-1。四舍五入的原理是在参数上加0.5然后做向下取整,固为 -2
RPC
- 网络通信。dubbo的网络通信是 tcp socket的长连接,每次调用不需要重新连接,直接调用就可以了,服务器一般都是走的内网,速度足够快(springcloud是短连接,feign内部做了优化,在http调用的底层其实也是 TCP Socket长连接来处理的)
- 代理。微服务框架都是通过代理的方式吧调用远程服务封装起来,出餐入参的序列化,线程池的调用,负载均衡的选择等等
Spring
IOC: A依赖于B,B依赖于C,C依赖于D,对D的成员变量进行修改,就会改变所有类
实现方式,setter,通过接口Interface,Construtor,通过注解。
String、StringBuffer与StringBuilder之间区别
String的值是不可变的,这就导致每次对String的操作都会生成新的String对象,不仅效率低下,而且浪费大量优先的内存空间
** StringBuffer 多线程操作字符串,线程安全的,是可变类,当字符串大小超过容量时,会自动增加容量**
StringBuilder 单线程操作字符串,线程不安全,速度最快,是可变类
在 Queue 中 poll()和 remove()有什么区别?
poll() 和 remove() 都是从队列中取出一个元素,但是 poll() 在获取元素失败的时候会返回空,但是 remove() 失败的时候会抛出异常。
怎么确保一个集合不能被修改?
使用Collections.unmodifiableMap();
Thread中的start和run方法的区别
- 调用start()方法会创建一个新的子线程并启动
- run()方法只是Thread的一个普通方法的调用
Thread和Runnable
- thread是实现了Runnable接口的类,是的run支持多线程
- 因类的单一继承原则,推荐使用runnable接口
数据库优化
索引(两大功能查找和排序): 排好序的快速查找数据接口就是索引,索引本身也很大,索引一般以文件的形式存在磁盘当中
索引优势:
- 提高数据查找的效率,降低数据库的 IO 成本
- 通过索引列对数据进行排序,降低数据排序成本,降低CPU的消耗
索引的劣势:
3. 索引也是一张表,该表保存了主键与索引字段,并指向实体表的记录,所以索引列也是要占用空间
4. 虽然提高了查询效率,但会降低更新表的速度
索引分类: 单利索引,唯一索引,复合索引,全文索引
基本语法:
Btree索引:
什么时候创建索引:
5. 主键自动创建索引
6. 频繁作为查询条件的字段
7. 查询中与其它变关联的字段,外键
8. 频繁更新的字段不适合创建索引,数据更新同时会更新索引
9. 表记录少不合适
10. 经常增删的不合适
11.查询中排序的字段,排序字段若通过索引去访问将大大提高排序速度 (order by 后面字段也按照索引顺序)
Explain(mysql之优化-重要)
id:
- id相同,执行顺序由上至下
- id不同,如果是子查询,id的序号递增,则id值越大优先级越高,越先被执行
- id相同不同,同时存在(id如果相同,可以认为是一组,从上往下执行,在所有组中,id值越大,越先执行)
select_type:
- simple,简单的 select 查询,查询不包含子查询或者 UNION
- primary,最外层的,最后执行
- subquepy,子查询
- derived,衍生,在子查询表被标记为其它名字
- union
- union result
table: 表
type:
说明:一般来说,sql执行的效率得保证至少达到 range 级别,最好能达到 ref
system(表示执行结果只查到一条) > const (表示通过索引一次就找到了)> eq_ref ()> ref > range(一般就是 where 后面有 between,>,<,in 等) > index(全索引扫描) > All(全表扫描)
possoble_keys: 查询涉及到的字段上若存在索引,就会被列出
key: 实际上走了那些索引,查询中若使用了覆盖索引,则该索引晋出现在key列表中
key_len: key_len的值为索引字段的最大可能长度,并非实际使用长度。
ref: 显示索引的那一列被使用了,如果可能的话,是一个常数,哪些列或敞亮被用于查找索引列上的值
rows: 根据表统计信息及索引选用情况,大致估算出找到所需的记录所需要读取的行数(越小越好)
Extra:
- Using Filesort(文件排序) 出现是最坏的情况
- Using join buffer (使用了连接缓存)
- Using index (好,从索引取值)
- using temporary(最坏的情况) mysql在对查询结果排序时使用了临时表,常见于排序 order by 和 group by(一般都要排序,会建一个临时表),遵循索引顺序一致原理提高效率
索引的优化
- 左连接索引加右表,右连接索引加左表
索引失效:
- 全职匹配
- 复合索引要遵循最佳左前缀法则(范围之后全失效)
- 不在索引列上做任何操作(计算,函数,(自动or手动)类型的转换)会导致索引失效
- 存储引擎不能使用索引中范围条件右边的列
- 尽量使用覆盖索引(只访问索引的查询(索引列和查询列一致),减少 select *) 不用 * 区别就是,Extra 字段中有一个 Using index,性能会更好
- 在使用不等于(!= 或者 <>)的时候索引失效变成全表扫描
- is null,is not null 也无法使用索引
- like 以通配符开头索引失效变成全表扫描
- 字符串不加单引号索引失效
- 少用 or,用它来连接时会索引失效 ,创建索引 or 连接的字段必须都有索引(必须是单列索引),否则索引就会失效,当然建议使用 union 替换 or
- 字符串没有加单引号 导致索引失效
- not in 不走索引
- 尽量使用连接查询,不要使用子查询
- limit 优化 (主键自增且不断层可以使用 主键> xx 的方法),(order by + 回关联查询)
选择性的走某一个索引
- use index(索引名字) 表示只走这个索引
- ignore index(索引名字) 表示忽略该索引
- force index(索引名字) 表示强制走该索引
sql优化
覆盖索引:尽量将查询的字段都包含在索引当中
- 小表驱动大表
- order by 优化(复合索引遵循最左前缀,提高效率),要么都是升序,要么都是降序
方法:order by 大忌不要用 select * ,常识数据库调优提高 sort_buffer_size(单路排序算法) 参数,尝试提高 max_length_for_sort_data
举例如图:
group by优化:
- 使用 group by 时,会文件内排序并且进行分组,如果不用排序则可以如下写法,(加上 order by null)
慢查询日志
命令: show variables like ‘%slow_query_log%’,默认是关闭(off)的;
批量插入数据脚本
show profile 进行分析优化
说明:show profile查询 SQL 在 Mysql 服务器里面的执行细节和生命周期情况
SQL 数据库服务器参数调优
线程池
说明:java中的线程池使用 Executor 框架实现的
常用的线程池:
- Executors.newFixedThreadPool(int): 执行长期的任务,性能好很多
- Executors.newSingleTreadExecutor(): 一个任务一个任务执行场景
- Excutors.newCachedThreadPool(): 执行很多短期异步的小程序或者负载较轻的服务,不够会新增
线程池底层
2个核心数,最大线程数5个,现2个人进入2个核心数执行,当有3 4 5,会进入阻塞队列进行等待,当有6 7 8进入,会开启最大线程数,6 7 8就抢占线程立刻执行任务(如下图)
所有线程池底层(七大参数如图):
- corePoolSize: 线程池中的常驻核心线程数,当线程池中的线程数目达到 corePoolSize 后,就会把达到的任务数放到缓存队列当中
- maximumPoolSize: 线程池能够容纳同时执行的最大线程数,此值必须大于等于 1
- keepAliveTime: 多余的空闲线程的存活时间,当线程池数量超过 corePoolSize 时,空闲时间达到 keepAliveTime值时,多余空闲线程会被销毁直到剩下 corePoolSize 个线程为止
- unit: keepAliveTime的单位
- workQueue: 阻塞队列,任务队列,被提交但尚未被执行的任务
- threadFactory: 表示生成线程池中工作线程的线程工厂,用于创建线程一般用默认的即可
- handler: 拒绝策略,表示当队列满了并且工作线程大于等于线程池的最大线程数(maximumPoolSize)
sleep和wait的区别
- sleep是thread类的方法,wait是object类中定义的方法
- sleep可以在任何地方使用
- wait方法只能在synchronized方法或者synchronized块中使用
- thread.sleep只会让出cpu,不会导致锁行为的改变
- object.wait让出cpu,还会释放已经占有的同步资源锁
notify和notfyAll区别
notfyAll会唤醒让所有线程
Volatile关键字
- 保证内存的可见性 (任何线程对它的写操作都会立即刷新到主内存中):线程A对一个volatile变量的修改,对于其它线程来说是可见的,即线程每次获取volatile变量的值都是最新的
- 禁止指令重排序
synchronized
互斥锁的特性:
- 互斥性: 在同一时间只允许一个线程持有对象锁,用过这种特性来实现多线程的协调机制。互斥锁也称为操作的原子性。
- 可见性: 必须保证在锁释放之前,对共享变量所做的修改,对于随后获得的宁一个线程是可见的。
synchronized和ReentrantLock的区别
- synchronized是关键字,底层monitorenter(进入)/monitorexit(退出)(底层是通过monitor对象来完成的,wait/notify等方法也依赖于monitor),lock是一个类
- synchronized 不需要手动释放锁,执行完成会自动让线程释放锁对象,ReentrantLock 需要受用释放锁,否则会出现死锁对象配合try/finally语句块来完成
- synchronized 是不可以中断的,除非执行完成或者抛出异常,reentrantLock 可以中断
- synchronized 非公平锁, reentrantLock 两者都可以,默认非公平锁
- synchronized 不能实现精确唤醒,只有随机唤醒和全部唤醒,reentrantLock (Condition对象)可以实现精确唤醒
CAS
并行和并发有什么区别?
- 并发:是指多个线程任务在同一个CPU上快速地轮换执行,由于切换的速度非常快,给人的感觉就是这些线程任务是在同时进行的,但其实并发只是一种逻辑上的同时进行;
- 并行:是指多个线程任务在不同CPU上同时进行,是真正意义上的同时执行。
什么是反射?
反射机制是在指程序在运行过程中,对任意一个类都能获取所有属性和方法,并且对任意一个对象都能调用其任意一个属性和方法,这种动态获取类和对象信息,以及动态调用对象的方法的功能被称之为java反射机制
什么是序列化?
把java对象转换为字节序列
String 类的常用方法都有那些?
1. indexOf():返回指定字符的索引。
2. charAt():返回指定索引处的字符。
3. replace():字符串替换。
4. trim():去除字符串两端空白。
5. split():分割字符串,返回一个分割后的字符串数组。
6. getBytes():返回字符串的 byte 类型数组。
7. length():返回字符串长度。
8. toLowerCase():将字符串转成小写字母。
9. toUpperCase():将字符串转成大写字符。
10. substring():截取字符串。
11. equals():字符串比较
JAVA的设计模式
创建模式: 单利模式,工厂模式,原型模式,建造者模式
结构型模式: 适配器模式,桥接模式,装饰模式,组合模式,外观模式,享元模式,代理模式
行为型模式: 模板方法模式,命令模式,访问者模式,迭代器模式,观察者模式,中介者模式,备忘录模式,解释权模式,状态模式,策略模式,职责链模式
单例模式(jdk当中的Runtime就是使用的饿汉式)
说明:单利模式保证了系统内存中该类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使用单例,模式可以提高系统性能
应用场景:需要频繁创建和销毁的对象,创建对象时耗时过多或消耗资源过多
-
饿汉式(两种)
优点:写法简单,就是在类装载的时候就完成实例化。避免了线程同步问题
缺点:在类装载的时候完成实例化,如果没用使用这个实例,可能会造成内存浪费
-
懒汉式(三种)
优点:需要时候才创建
缺点:线程不安全,单线程下使用,可以加入同步锁synchronized,效率相对较低
-
双重检查(在千万级并发下不一定线程安全,需要用到Volatile来禁止指令重排)(多线程开发下的双重检查,两次判断对象是否等于null) 可以解决线程安全问题,还能解决线程安全问题,推荐使用
-
静态内部类(多线程并发问题) 也可以实现线程安全
装载类的时候JVM保证了线程的安全性
-
枚举(推荐使用)
工厂模式
如何设计数据库
存储管理,缓存机制,sql解析。日志管理,权限划分,容灾机制,索引管理,锁管理
二插查找树
MyISAM与InnoDB关于所方面的区别
MyISAM莫人用的是表级锁,不支持行级锁
InnoDB莫人用的是行级锁,也支持表级锁
表级锁: 当数据被查询的时候,会将表加一个表级的读锁(共享锁),当对数据进行增删改时候,会加上一个写锁(排它锁),当读锁未被释放的时候,写锁会进入阻塞状态,直到所有的读锁被释放(select一个大数据的表,同时进行update,这时update需要等到select技术才能成功)
行级锁:
索引模块
IO流
I : input 输入(读取)
O:output输出(写入)
流:(字节流,字符流),1个字符 = 2个字节,1个字节 = 8个二进制位(bit)
注意事项:while无限循环时候读取文件时要用一个变量接收(记录每次读取到的字节),举例(文件复制),字节流缓冲读取用一个 byte[] 数组存储,字符流缓冲使用 char [] 数组存储
BufferedOutputStream(字节缓冲输出流):
使用BufferedInputStream减少IO交互次数能大量提升IO性能,通过缓冲区读写,较少系统IO次数,从而提高读写的效率