一、java基础
1、jdk、jre、jvm的区别
jdk:Java程序开发工具包。
jre:Java程序运行环境。
jvm:Java虚拟机。
2、一个Java源文件中是否可以包含多个类有什么限制
解:可以包含多个类但是只有一个类生命成public并且要和文件名一致
3、Java的优势
跨平台, 面向对象性 安全性高 简单 高性能
4、Java是否存在内存溢出和内存泄漏 如何解决
存在 内存溢出是指不被使用的对象或者变量一直存在于内存中 理论上说不被使用的对象都会被GC垃圾回收机制给自动回收掉 如果长生命周期的对象持有短生命周期的对象的引用就会出现内存泄漏。因为短生命周期的对象不在需要但是因为长生命周期的对象持有它的引用导致不会被回收掉
5、如何看待Java是半编译半解释语言
Java源码通过编译器生成字节码文件在同个类的加载器进行字节码验证。可以通过解释器直接执行也可以通过JIT编译器编译执行
6、如何高效的计算2*8的值
使用 <<
7、&和&&的区别
两者都是与 &是运算符两边都是true整个表达式才会执行
&&是运算饭左边如果是false右边的表达式会直接短路不进行运算
8、Java中的基本类型有哪些?String 是最基本的数据类型吗
byte short int long float double char boolean
9、Java开发中计算金额时使用什么数据类型
不能使用double和float 因为天涯吗不很精确 可以使用 BigDecimal来进行运算
因为不能实现每个十进制小数都对于一个二进制小数 IEEE745标准
10、char变量可不可以存储一个中文汉字
可以的 因为char使用的unicode编码 包含了世界范围的字符
11、== 和equals的区别
==基本类型:比较的是数据
==引用类型:比较的是地址值
equals默认比较的是是引用比较 只是很多类重写了equals方法变成了值比较
12、两个对象的 hashCode() 相同,他们一定相同吗?
hashCode相同不一定相同 equals相同hashcode一点相同
13、final在Java中的作用
final修饰的变量就说常量
final修饰的方法不能被重写
final修饰的类不能被继承
15、++i 和 i++的区别
++i 变量先自增1在运算 i++ 变量先运算在+1
16、面向对象的三大特征
封装 继承 多态
封装: 就是把一个对象私有化,同时提高一些可以对外访问属性的一些方法
继承:
* 子类拥有父类非private的方法和属性
* 子类可以扩展自己的属性和方法
* 子类可以用自己的方式实现父类的方法
多态:
父类引用指向子类对象
17、创建对象的几种方式
通过new 一个对象的实例
使用反射通过class.newInstance创建对象
克隆 实现Cloneable接口重写clone方法
使用序列化和反序列化
18、lambda表达式
19、Stream流
20、单例模式
- 懒汉:
//直接序列化: 私有化构造方法, 自行创建并静态赋值,可以对外提供这个实例
public class A{
private static final A TE=new A();
private A(){}
}
//枚举 :限定为一个就成了单例
public enum A{
TE
}
//静态代码块实现单例模式, 这种方式可以通过配置文件来进行数学的赋值,只需要修改配置文件的内容就可以了.
public class A{
private static final A TE;
static{
TE=new A();
}
private A(){
}
}
- 饱汉:
二、java基础常用的算法
时间复杂度:分析关键字的比较次数和记录移动次数
由小到大常见的算法时间复杂度由小到大依次为:
O(1) < O(logn) < O(n) < O(nlogn) < O(n^2) < O(n^3) < O(2^n) < O(n!) < O(n^n)
1、冒泡排序
> 比较相邻的两个值 如果第一个比第二个大就升序,交换他们两个。
对每一对相邻的元素做同样的工作,从开始到结束 最后的元素就说最大的,持续每次越来越少的上面的步骤,
public static void main(String[] args){
int[] arr = {6,9,2,9,1};
for(int i=1; i<arr.length; i++){
for(int j=0; j<arr.length-i; j++){
//希望的是arr[j] < arr[j+1]
if(arr[j] > arr[j+1]){
//交换arr[j]与arr[j+1]
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
//完成排序,遍历结果
for(int i=0; i<arr.length; i++){
System.out.print(arr[i]+" ");
}
}
2、优化后的冒泡排序
public static void main(String[] args) {
int[] arr = {1, 3, 5, 7, 9};
//从小到大排序
for (int i = 0; i < arr.length - 1; i++) {
boolean flag = true;//假设数组已经是有序的
for (int j = 0; j < arr.length - 1 - i; j++) {
//希望的是arr[j] < arr[j+1]
if (arr[j] > arr[j + 1]) {
//交换arr[j]与arr[j+1]
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
flag = false;//如果元素发生了交换,那么说明数组还没有排好序
}
}
if (flag) {
break;
}
}
//完成排序,遍历结果
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
}
3、 快速排序
从数列中挑选出一个元素为基准重新排序所有元素比基准小的在前面,比基准大的在后面,然后递归的把小于基准的元素和大于基准的元素进行排序
三、反射
1、反射的机制
在程序运行期间对于任何一个类都能知道这个类的所有属性和方法,并能任意的调用
2、 反射的优缺点和应用场景
优点:
提高了Java程序的灵活性和扩展性,降低了耦合性,提高自适应能力
允许程序创建和控制任何类的对象,无需提前硬编码目标类
缺点:
反射的性能较低。
反射机制主要应用在对灵活性和扩展性要求很高的系统框架上
反射会模糊程序内部逻辑,可读性较差。
场景
Jdbc数据库连接池的时候class.forName()加载数据库的驱动
xml配置 spring通过xml配置装载bean的过程
** 获取反射的方式**
- 类名.class
Class class1=String.class;
- 对象.getClass()方法
Class classStu = stu.getClass();
- Class.forName(“”)
Class clazz = Class.forName("java.lang.String");
- 其他方式 使用类加载器进行加载
ClassLoader cl = this.getClass().getClassLoader();
Class class4 = cl.loadClass("类的全类名");
3、类加载的过程
- 加载 、链接 (验证 准备 解析 )、初始化
加载: 将类的class文件读入内存并创建java.lang.Class对象
验证: 确保加载的类信息符合jvm规范
准备: 正式为类的变量分配内存并设置类变量的初始值
解析: 虚拟机常量池的符合引用(常量名)替换成直接引用(地址)的过程
初始化: 执行类构造器clint()方法的过程
4、类加载器
分类: 引导类加载器和自定义加载器:
启动类加载器 扩展类加载器 应用程序类加载器
5、双亲委派机制
jvm是通过双亲委派机制进行的类的加载,
当一个类收到类加载请求,先不会尝试自己去加载,而是委派父类去完成,当父类加载器反馈自己不能加载,子类加载器才会自己去加载。
6、使用反射获取配置文件信息
//此时默认的相对路径是当前module的src目录
InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("info1.properties");
7、使用反射自定义注解
-
内置注解:
@Override @Deprecated 表示废弃的不建议用的 @SuppressWarnings 镇压警告 -
元注解
负责注解其他注解
@Target:用于描述注解的使用范围
@Retention:表示需要在什么级别保存该注释信息 用于描述注解生命周期
(source源代码 class class runtime 运行的时候)
@Document:说明该注解被包含在javadoc中 是不是在文档中
@inherited:说明之类可以基础父类中的该注解 -
自定义注解
@interface 会继承 java.long.annotation.Annotation -
通过反射获取注解信息
四、集合
1、常用的集合有那些
分为collection和map
collection分为set和list
** list**
一个有序元素可以重复可以插入多个null元素常用的实现类有arrayList、linkedList、vector
-
Arraylist: Object数组 有序可以重复、线程不安全、初始化的数组长度是10扩容机制是原来的1.5倍
-
Vector: Object数组,存在同步化机制线程安全、有序可以重复、默认扩容2倍
-
LinkedList: 双向循环链表、线程不安全、有序可以重复
set
一个无序不可以存储重复元素,只允许插入一个null元素常用的实现类有有hashSet、TreeSet、linkedHashSet
-
HashSet(无序,唯一):基于 HashMap 实现的,hashset存储的时候会调用hashcode方法 计算哈希值 然后看里面有没有该哈希值的元素发现没有后,会存到set集合里面 ,如果调用hashcode方法 计算哈希值,发现有当前哈希值的元素会产生哈希冲突,调用equals方法进行比较,如果为true会认定两个元素相同,然后不会存储到set集合里面 如果调用equals方法进行比较 如果为false 则两个元素不同 然后会存到当前哈希值下面 一链表形式存在。
-
LinkedHashSet: 是hashset的子类 底层是哈希表(数组加链表、红黑树)加链表 :比hashset多了一条链表 (记录元素的存储顺序) 保证元素有序; 有序 不允许重复
-
TreeSet(有序,唯一): 红黑树(自平衡的排序二叉树。)
map
一个键值对的集合key无序,唯一,value不要求有序可以重复常用的实现类有hashMap、treeMap、hashtable、LinkedHashMap、ConcurrentHashMap
-
HashMap: 底层是基于数组加链表的形式进行存储,当存储一个键值对的时候,根据key的hashcode值进行hash算法算出对于的下标如果对于的下标存在值,则会出现hash碰撞,进行equals比较如果是true则覆盖value的值,如果为false则以链表的形式进行存储,jdk1.8后当链表的长度达到8的时候则转化成红黑二叉树的形式进行存储当降为6的时候又转换成链表进行存储而且为了避免出现逆序且链表死循环问题链表的插入由头擦法转换成1.8后的尾插法,hashcode的初始容量是16加载因子是0.75,当hashmap的的容量达到之前容量的0,75倍后,会二倍扩容。
-
LinkedHashMap:LinkedHashMap 继承自 HashMap,所以它的底层仍然是 基于拉链式散列结构即由数组和链表或红黑树组成是一个有序的集合,存储元素和取出元素的顺序一致。
-
HashTable: 数组+链表组成的,数组是 HashMap 的主体,链表则是主要为 了解决哈希冲突而存在的、实现map接口 哈希表结构 不允许存储null;线程安全 ,线程同步,单线程 速度慢
-
TreeMap: 红黑树(自平衡的排序二叉树)
2、数据结构:计算机存储,组织数据的方式
-
栈:先进后出 压栈
-
队列: 先进先出
-
数组 :查询快增删慢 查询通过索引定位 查询效率高 添加数据和删除数据的时候 效率低
链表: 结点 结点存储数据和下个结点的地址 增删快 查询慢 从头结点开始查询 -
树:
**二叉树:**节点 每一个节点由父节点地址, 值 , 左节点地址, 右节点地址,每一个节点的子节点数量为度 任意一个节点的度小于等于二
二叉查找树:每一个节点上最多有两个子节点,每一个左子节点都要小于父节点 右子节点要大于父节点的值(二叉查找树 二叉排序树);
平衡二叉树:(高度平衡)左右两个子树的高度差不能超过1,任意一个节点的两个子树是平衡二叉树
红黑树:()每一个节点智能是红或黑,根据自己的规则进行旋转父节点地址,左节点地址,右节点地址,值,颜色。
红黑规则:根节点是黑色的,如果一个节点没有子节点或者父节点,则该节点指针属性为nil 这些nil视为叶节点 每个叶节点是黑色的,如果某个节点是黑色的下一个节点是红色的。
对每一个节点,该节点到其所有后代叶节点的简单路径上,均包含相同数目的黑色节点
五、JUC
1、并发和并行
2、创建线程的方式
-
实现 Thread类 重写里面的run()方法
-
实现runnable接口重写run方法
-
实现callable接口
-
线程池
3、线程的生命周期
-
创建状态(new的时候 ) 调用start方法会 变成 就绪状态(runnable) 通过cpu调度 会变成运行状态
-
运行状态 ()等待线线程自然咨询完毕会变成死亡状态 如果用户进行干扰进行休眠就好到阻塞状态
-
创建状态new 就绪状态runnable 运行状态(Running) 阻塞状态(BLOCKED)WAITING等待) 死亡状态 (TERMINATED)死亡
-
线程对象一旦创建就会进入到新生状态 ,当调用start方法的时候线程立即进入就绪状态,但不意味着立即调度执行, 进入运行状态 线程才真正的执行线程体的代码块,当调用sleep,walt或同步锁定时,线程进入阻塞状态,阻塞事件解除后重新进入就绪状态等待cpu调度。 线程中断或者接受,一旦进入死亡状态就不能再次启动。
-
阻塞状态(Blocked): 阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
(01) 等待阻塞 – 通过调用线程的wait()方法,让线程等待某工作的完成。
(02) 同步阻塞 – 线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态。
(03) 其他阻塞 – 通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态
4、synchoronized和lock的区别
- synchronized是一个java关键字 lock是一个类
- synchronized无法判断获取锁的状态 lock可以判断是否获取了锁
- synchronized会自动释放锁 lock要手动释放解锁 不释放锁就好造成死锁。
- synchronized 可重入锁 不可以中断 ,非公平 lock 可重入锁 可以判断锁 (可以自己设置公平和非公平锁)
- synchronized适合锁少量同步代码 lock适合大量同步代码
锁是什么,如何判断锁的是谁 -
synchronized 锁的对象是方法的调用者!两个方法用的是同一个锁,谁先拿到谁执行
5、线程池
不推荐用executors 推荐原生的
threadpollexecutor
1、五大方法 七大参数 四种拒绝策略
五大方法
-
newSingleThreadExecutor():只有一个线程的线程池,任务是顺序执行,适用于一个一个任务执行的场景
-
newCachedThreadPool():线程池里有很多线程需要同时执行,60s内复用,适用执行很多短期异步的小程序或者负载较轻的服务
-
newFixedThreadPool():拥有固定线程数的线程池,如果没有任务执行,那么线程会一直等待,适用执行长期的任务。
-
newScheduledThreadPool():用来调度即将执行的任务的线程池
-
newWorkStealingPool():底层采用forkjoin的Deque,采用独立的任务队列可以减少竞争同时加快任务处理
七大参数
-
int corePoolSize, 核心线程数
int maximumPoolSize, 最大线程数
long keepAliveTime, 线程超过核心线程数的空闲线程存活时间
TimeUnit unit, 时间单位
BlockingQueue workQueue, 阻塞队列
ThreadFactory threadFactory, 线程工厂
RejectedExecutionHandler handler 拒绝策略
四种拒绝策略
AbortPolicy:直接抛出异常,阻止系统正常运行。可以根据业务逻辑选择重试或者放弃提交等策略。
CallerRunsPolicy :只要线程池未关闭,该策略直接在调用者线程中,运行当前被丢弃的任务。
不会造成任务丢失,同时减缓提交任务的速度,给执行任务缓冲时间。
DiscardOldestPolicy :丢弃最老的一个请求,也就是即将被执行的任务,并尝试再次提交当前任务。
DiscardPolicy :该策略默默地丢弃无法处理的任务,不予任何处理。如果允许任务丢失,这是最好的一种方案。
六、mysql
七、Spring
1、ioc容器
- 控制反转是一种思想 降低了程序的耦合度
- DI依赖注入
- 指spring创建对象的过程中,将对象依赖属性通过配置注入
- 注入的方式有两种
- set注入
- 构造注入
- Bean管理说的是:Bean对象的创建,以及Bean对象中属性的赋值(或者叫做Bean对象之间关系的维护)
2、注解定义bean
3、注入的注解
** @Autowired**
-
该注解有一个required属性,默认值是true,表示在注入的时候要求被注入的Bean必须是存在的,如果不存在则报错。如果required属性设置为false,表示注入的Bean存在或者不存在都没关系,存在的话就注入,不存在的话,也不报错
** @Resource**
-
@Resource注解属于JDK扩展包,所以不在JDK当中,需要额外引入以下依赖:【如果是JDK8的话不需要额外引入依赖。高于JDK11或低于JDK8需要引入以下依赖。
<dependency>
<groupId>jakarta.annotation</groupId>
<artifactId>jakarta.annotation-api</artifactId>
<version>2.1.1</version>
</dependency>
4、aop
代理模式
- 静态代理
- 动态代理
jdk的代理 :有接口的时候使用jdk代理,生成接口实现类的代理对象。
和cglib代理:没有接口,使用cglib代理,生成子类的代理对象。
相关术语
1、横切关注点
2、通知
前置通知:@Before在被代理的目标方法前执行
返回通知:@After在被代理的目标方法成功结束后执行
异常通知:@AfterReturning在被代理的目标方法异常结束后执行
后置通知 @AfterThrowing在被代理的目标方法最终结束后执行
环绕通知:@Around使用try…catch…finally结构围绕整个被代理的目标方法,包括上面四种通知对应的所有位置
3、切面
4、目标
5、代理
6、连接点
7、切入点
切入点表达式语法
- 用*号代替“权限修饰符”和“返回值”部分表示“权限修饰符”和“返回值”不限
- 在包名的部分,一个“*”号只能代表包的层次结构中的一层,表示这一层是任意的。
- 在包名的部分,使用“*…”表示包名任意、包的层次深度任意
- 在类名的部分,类名部分整体用*号代替,表示类名任意
- 在类名的部分,可以使用*号代替类名的一部分
- 在方法名部分,可以使用*号表示方法名任意
- 在方法名部分,可以使用*号代替方法名的一部分
- 在方法参数列表部分,使用(…)表示参数列表任意
- 在方法参数列表部分,使用(int,…)表示参数列表以一个int类型的参数开头
- 在方法参数列表部分,基本数据类型和对应的包装类型是不一样的
- 在方法返回值部分,如果想要明确指定一个返回值类型,那么必须同时写明权限修饰符
八、事务
1、事务的特性 ACID
A:原子性(Atomicity)
- 一个事务(transaction)中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。
C:一致性(Consistency)
- 事务的一致性指的是在一个事务执行之前和执行之后数据库都必须处于一致性状态。如果事务成功地完成,那么系统中所有变化将正确地应用,系统处于有效状态。如果在事务中出现错误,那么系统中的所有变化将自动地回滚,系统返回到原始状态。
I:隔离性(Isolation)
- 指的是在并发环境中,当不同的事务同时操纵相同的数据时,每个事务都有各自的完整数据空间。由并发事务所做的修改必须与任何其他并发事务所做的修改隔离。事务查看数据更新时,数据所处的状态要么是另一事务修改它之前的状态,要么是另一事务修改它之后的状态,事务不会查看到中间状态的数据。
D:持久性(Durability)
- 指的是只要事务成功结束,它对数据库所做的更新就必须保存下来。即使发生系统崩溃,重新启动数据库系统后,数据库还能恢复到事务成功结束时的状态。
2、事务属性:
1、只读
Transactional(readOnly = true)
2、超时
//超时时间单位秒
@Transactional(timeout = 3)
3、回滚策略:
可以通过@Transactional中相关属性设置回滚策略
@Transactional(noRollbackFor = ArithmeticException.class)
//@Transactional(noRollbackForClassName = “java.lang.ArithmeticException”)
rollbackFor属性:需要设置一个Class类型的对象
rollbackForClassName属性:需要设置一个字符串类型的全类名
noRollbackFor属性:需要设置一个Class类型的对象
rollbackFor属性:需要设置一个字符串类型的全类名
3、事务的隔离级别
隔离级别一共有四种:
-
读未提交:READ UNCOMMITTED
允许Transaction01读取Transaction02未提交的修改。
读已提交:READ COMMITTED、
要求Transaction01只能读取Transaction02已提交的修改。
可重复读:REPEATABLE READ
确保Transaction01可以多次从一个字段中读取到相同的值,即Transaction01执行期间禁止其它事务对这个字段进行更新。
串行化:SERIALIZABLE
确保Transaction01可以多次从一个表中读取到相同的行,在Transaction01执行期间,禁止其它事务对这个表进行添加、更新、删除操作。可以避免任何并发问题,但性能十分低下。
@Transactional(isolation = Isolation.DEFAULT)//使用数据库默认的隔离级别
@Transactional(isolation = Isolation.READ_UNCOMMITTED)//读未提交
@Transactional(isolation = Isolation.READ_COMMITTED)//读已提交
@Transactional(isolation = Isolation.REPEATABLE_READ)//可重复读
@Transactional(isolation = Isolation.SERIALIZABLE)//串行化
4、事务的传播行为
一共有七种传播行为:
@Transactional(propagation = Propagation.REQUIRED)
REQUIRED:支持当前事务,如果不存在就新建一个(默认)【没有就新建,有就加入】
SUPPORTS 支持当前事务,如果当前没有事务,就以非事务方式执行 【有就加入,没有就不管了】
MANDATORY 必须运行在一个事务中,如果当前没有事务正在发生,将抛出一个异常 【有就加入,没有就抛异常】
REQUIRES_NEW 开启一个新的事务,如果一个事务已经存在,则将这个存在的事务挂起【不管有没有,直接开启一个新事务,开启的新事务和之前的事务不存在嵌套关系,之前事务被挂起】
NOT_SUPPORTED 以非事务方式运行,如果有事务存在,挂起当前事务**【不支持事务,存在就挂起】**
NEVER 以非事务方式运行,如果有事务存在,抛出异常【不支持事务,存在就抛异常】
NESTED 如果当前正有一个事务在进行中,则该方法应当运行在一个嵌套式事务中。被嵌套的事务可以独立于外层事务进行提交或回滚。如果外层事务不存在,行为就像REQUIRED一样。【有事务的话,就在这个事务里再嵌套一个完全独立的事务,嵌套的事务可以独立的提交和回滚。没有事务就和REQUIRED一样。】