什么是java序列化,什么时候需要序列化?
序列化是指将java对象转化成字节流的过程,反序列化是指将字节流转化成java对象的过程。
当java对象需要在网络上传输 或者 持久化到存储文件中,就需要对java对象进行序列化处理。
JVM的主要组成部分,描述一下类加载过程?
java代码在编译之后,不会直接编译成操作系统可以识别的机器码,而是编译成只有jvm可以识别的字节码。jvm充当翻译的作用,动态的将java代码翻译成不同操作系统可以识别的机器码。即实现了一次编译,处处运行--->跨平台
组成部分
1.类加载器(Class Loader):jvm将java代码 编译成字节码之后,jvm可以将字节码读入到内存里,从而进行解析运行等过程,该过程称为类加载机制。
2.运行时数据区(Runtime Data Area):jvm将管理的内存划分成不同的区域,即不同的数据放在不同的地方。分为:方法区,堆区,虚拟机栈,程序计数器,本地方法栈
3.执行引擎(Execution Engine):代码运行由执行引擎完成
类加载的过程
1.加载:查找并加载类的二进制数据,生成class对象实例
2.链接:当类加载到jvm内存区后,开始链接操作
2.1验证阶段,检查格式,语义,字节码,符号引用。
2.2准备阶段,为类的静态变量 分配内存并初始化为默认值
补充:静态变量和实例变量的区别:
- 静态变量:存储在方法区中,只有一份,属于类,所有的实例都共享同一个静态变量。
- 实例变量:存储在堆内存中,每创建一个实例,就会在堆内存中为该实例分配一块内存区域来存储实例变量,每个实例都有自己独立的实例变量。
2.3解析阶段,将类,接口,字段和方法的符号引用转为直接引用(即JVM可以直接使用内存地址来访问目标)
3.初始化:将静态变量初始化为指定的值,并执行静态代码块。该阶段是类装载的最后一个阶段,此时类才会开始执行java字节码。
4.使用:可以在程序中访问和调用静态成员,以及new创建对象实例
5.卸载:当该类的所有实例都被回收,该类就会被卸载
动态代理是什么,有什么应用,如何实现动态代理?
动态/静态 代理讲解
动态代理:动态代理:在运行时,创建目标类,可以调用和扩展目标类的方法。
例如:客户要找老板,但是客户多老板少,此时人力公司(JDK/CGLIB)动态派遣秘书解决客户的问题,客户只需根据需求动态获取秘书即可。
人力公司JDK需要目标类实现它的接口,而CGLIB需要继承目标类。
如果代理类实现了接口,就使用JDK代理,否则使用CGLIB代理
JDK底层通过反射调用实现动态代理,CGLIB是通过实时调用实现动态代理。
应用场景如:
-
统计每个 api 的请求耗时
-
统一的日志输出
-
Spring的 AOP 功能模块就是采用动态代理的机制来实现切面编程
数据库事务的隔离级别,请描述一下乐观锁和悲观锁?
先明确事务四大属性:原子性、一致性、隔离性、持久性。
原子性:要么全部成功要么全部失败回滚。
一致性:指事务状态的一致性,比如A和B互相转账的钱一共是1000,不管转几次,如何转,都要是1000
隔离性:不被其他事务干扰
持久性:对数据库的更改是永久的
在数据库操作中,并发的情况下可能出现如下问题:
更新丢失(Lost update),脏读(Dirty Reads),不可重复读(Non-repeatable Reads),幻读
更新丢失:后修改的覆盖前面修改的
脏读:A事务读取到B事务修改但未提交的数据,此时B执行回滚,那么A读到的数据就是经过B修改的脏数据
不可重复度:一个事务对同一行数据重复读取两次,但是却得到了不同的结果。
幻读:虽然查出来的数据结果可能是正确的,但是肯定会影响数据行和状态的一致性。这里的数据行指的是,我一开始预期是有10条记录,结果却有11条记录,并且由于事务的隔离,我看不到有11条记录,还以为10条记录。
事物的隔离级别以及对应可以解决的并发问题(隔离级别由低到高):
Read uncommitted(读未提交)-->解决更新丢失,可能出现脏读
举例:如果一个事务已经开始写数据,则另外一个事务则不允许同时进行写操作,但允许其他事务读此行数据
Read committed(授权读取、读提交)-->解决脏读,可能出现不可重复读
举例:读事务允许其他事务访问该行数据,但未提交的写事务禁止其他事务访问该行数据
Repeatable read(可重复读取)-->解决不可重复读取和脏读,可能出现幻读
举例:读取数据的事务将会禁止写事务(但允许读事务),写事务则禁止任何其他事务
Serializable(序列化)-->解决脏读、不可重复读,幻读
最严格的事务隔离,它要求事务序列化执行,事务只能一个接着一个地执行,不能并发执行
乐观锁和悲观锁
乐观锁:总是假设最好的情况,认为共享资源每次被访问的时候不会出现问题,线程可以不停地执行,无需加锁也无需等待,只在提交修改的时候去验证对应的资源(也就是数据)是否被其它线程修改了(具体方法可以使用版本号机制或 CAS 算法)。
悲观锁:总是假设最坏的情况,认为共享资源每次被访问就会出现问题(比如共享数据被修改),所以每次在获取资源的时候都会上锁,这样其他线程想拿到这个资源就会阻塞,直到锁被上一个持有者释放。也就是说,共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程。缺点:线程阻塞,可能出现死锁问题
springcloud五大组件是什么,它们是如何运行的?
一、服务治理组件
Nacos:是服务注册中心和配置中心的结合体,采用C/S架构,包含两大组件:
Nacos Server:可以作为服务注册中心、配置中心,帮助Nacos Client实现服务的注册、发现,配置的动态刷新。
Nacos Client:通过spring-cloud-starter-alibaba-nacos-discovery
,在Nacos Server中实现服务的注册和发现;通过spring-cloud-starter-alibaba-nacos-config
,在Nacos Server中实现配置的动态刷新。
二、负载均衡组件
Ribbon:客户端负载均衡和服务调用工具,不需要独立部署,几乎存在于每一个使用 Spring Cloud 构建的微服务中,是 Spring Cloud 体系中最核心、最重要的组件之一
三、熔断器组件
Hystrix:有效地阻止分布式微服务系统中出现联动故障,如雪崩,以提高微服务系统的弹性。Spring Cloud Hystrix 具有服务降级、服务熔断、线程隔离、请求缓存、请求合并以及实时故障监控等强大功能。
四、服务网关组件
GateWay:在微服务架构中,一个系统往往由多个微服务组成,而这些服务可能部署在不同机房、不同地区、不同域名下。这种情况下,客户端(例如浏览器、手机、软件工具等)想要直接请求这些服务,就需要知道它们具体的地址信息,例如 IP 地址、端口号等。
可以通过服务网关,直接请求到服务网关,再由服务网关根据不同的标识信息将请求转发到微服务实例。可以在服务网关中处理一些非业务功能的逻辑,例如权限验证、监控、缓存、请求路由等。
五、分布式配置组件
Config:在分布式微服务系统中,几乎所有服务的运行都离不开配置文件的支持,为了解决这些问题,通常我们都会使用配置中心对配置进行统一管理。可以为微服务架构中各个微服务提供集中化的外部配置支持。
什么是sql注入,如何防止sqI注入?
是一种常见的web应用安全漏洞,例如通过输入框输入非法的sql代码从而操纵数据库获得敏感数据。
是否发生sql注入可以通过查看日志和数据库记录进行确认,如果确实发生了sql注入,立即隔离受影响的系统减少风险。
使用参数化查询而不是字符串拼接,是预防sql注入最基础最有效的方法。
预编译:绑定变量,也就是相当于将数据和代码分离,把传入的参数绑定为一个变量而不是sql语句,用 ? 表示,达到无法改变sql结构。
说白话就是:不使用字符串拼接,而是将不确定数据部分用 ? 表示,不管输入什么,都只会作为参数而不是sql语句。
接口与抽象类区别,final关键字为什么不能修饰抽象类?
对于抽象类,时刻需要谨记: 抽象类是不能够直接实例化的, 如果要使用一个抽象类,就必须要有该抽象类的子类. 如果抽象类的子类不是抽象类的话,就一定要重写该抽象类的所有抽象方法.
java中一个类只能继承一个类,但是却可以实现(implements)多个接口. 如果实现接口的类不是抽象类的话,则该子类必须重写接口中所有的抽象方法.
具体两者的区别:抽象类和接口的区别
定义抽象类是让其他类继承的,如果定义为final 该类就不能被继承,这样彼此就会产生矛盾,所以final 不能修饰抽象类
详细描述一下HashMap的实现原理
HashMap底层是一个数组,数组的每个元素是一个链表或者红黑树,
添加元素时,计算得到该key的hash值。将hash值对数组的长度进行取模,以此来获取key要存在数组的哪一个位置。如果该位置没有key,直接插入。如果该位置有key,将这个key和要插入的key进行比对是否一致,如果一致,说明是同一个key,直接覆盖;如果不一致,就把该key的节点追加到这个链表上。jdk1.8之后为了减少链表搜索时间(jdk1.8引入红黑树的原因),当链表长度大于8的时候,将链表转为红黑树
白话总结:
1.计算插入的key的hash值
2.根据值进行算法判断插入的位置
3.如果该位置没有key,直接插入
4.如果该位置有key,并且是同一个key,则覆盖
5.如果不是同一个key,则追加到链表/红黑树
线程池有哪些状态
running: 线程池可以接受新任务并处理队列中的任务。
shutdown: 线程池不再接受新任务,但会继续处理队列中的任务。
stop: 线程池不再接受新任务,不再处理队列中的任务,并中断正在执行的任务。
tidying: 所有任务已终止,线程池中的线程数量为0,即将进入TERMINATED状态(该状态进行资源清理操作)。
terminated: 线程池已完全终止。
如何防止死锁
死锁:两个或两个以上的线程在执行的过程中,去争夺同一个共享资源,造成相互等待的现象。如果没有外部干预,线程会一直阻塞。
死锁产生的条件:
必须四个条件都满足才会产生死锁
1.互斥:共享资源只能被一个线程占用,其他线程无法干扰
2.请求(资源y)和保持(不释放资源x):线程1已经取得资源x,在等待资源y的时候,不释放x。
3.不可抢占:其他线程不能强行抢占线程1占有的资源
4.循环等待:线程1等待线程2占有的资源,线程2等待线程1占有的资源
如果防止死锁:
产生死锁只能人工干预或者kill该线程
防止死锁只需要破坏死锁四个条件任意一个,其中互斥条件不能被破坏,因为它是互斥锁的基本约束,只能破坏其他三个条件。
请求和保持-->一次性申请所有的资源,则不需要等待
不可抢占-->占用资源的线程在申请其他资源的时候,如果申请不到,则主动释放占用的资源
循环等待-->按序申请来预防,即线性化申请
mybatis支持延迟加载吗,延迟加载的原理
支持
延迟加载是相对于联表操作而言的,单表操作没有延迟加载。比如用户表和商品表,如果只需要用户信息,而不需要对应的商品信息,则可以使用延迟加载(懒加载),也就是在需要用到的数据才进行加载,不需要用到的时候就不加载,可以提高数据库性能,因为单表查询比关联查询要快。
延迟加载的原理
主要依赖于动态代理和动态sql,先为主查询对象(用户表)生成一个动态代理类类并返回,该类可以通过内部的方法判断是否开启了延迟加载,如果开启,则会执行事先存储好的关联对象的查询sql,并将结果通过反射赋值给关联对象。