JAVASE
JDK,JRE,JVM
- JDK 是 Java Development Kit,它是功能⻬全的 Java SDK。它拥有 JRE 所拥有的⼀切,还有编译器(javac)和⼯具(如 javadoc 和 jdb)。它能够创建和编译程序。
- JRE 是 Java 运⾏时环境。它是运⾏已编译 Java 程序所需的所有内容的集合,包括 Java 虚拟机(JVM),Java 类库,java 命令和其他的⼀些基础构件。但是,它不能⽤于创建新程序。 如果你只是为了运⾏⼀下 Java 程序的话,那么你只需要安装 JRE 就可以了。如果你需要进⾏⼀ 些 Java 编程⽅⾯的⼯作,那么你就需要安装 JDK 了。
- JVM 是Java 虚拟机,是运⾏ Java 字节码的虚拟机。
面向对象的特性及描述
三(四)大特性:封装、(抽象)、继承、多态
- 面向对象的封装性,即将对象封装成一个高度自治和相对封闭的个体,对象状态(属性)由这个对象自己的行为(方法)来读取和改变。张三这个人,他的姓名等属性,要有自己提供的获取或改变的方法来操作。private name setName getName
- 抽象就是找出一些事物的相似和共性之处,然后将这些事物归为一个类,这个类只考虑这些事物的相似和共性之处,并且会忽略与当前主题和目标无关的那些方面,将注意力集中在与当前目标有关的方面。 就是把现实生活中的对象,抽象为类。
- 继承:两个类如果是父子关系,可以使用继承,子类可以复用父类所定义的内容
- 多态是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量倒底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。多态存在的三个必要条件:继承、重写、父类引用指向子类对象(Animal a = new Cat())
重写与重载的区别
- 重写:
1.发生在父类与子类之间
2.方法名,参数列表,返回类型(除过子类中方法的返回类型是父类中返回类型的子类)必须相同
3.访问修饰符的限制一定要等于或大于被重写方法的访问修饰符(public>protected>default>private)
4.重写方法一定不能抛出新的检查异常或者比被重写方法申明更加宽泛的检查型异常
- 重载
1.重载Overload是一个类中多态性的一种表现
2.重载要求同名方法的参数列表不同(参数类型,参数个数甚至是参数顺序)
3.重载的时候,返回值类型可以相同也可以不相同。无法以返回型别作为重载函数的区分标准
String,StringBuilder,StringBuffer
- 在java中提供三个类String StringBuilder StringBuffer来表示和操作字符串。字符串就是多个字符的集合。
- String是内容不可变的字符串。String底层使用了一个不可变的字符数组(final char[])
- 而StringBuillder StringBuffer,是内容可以改变的字符串。StringBuillder StringBuffer底层使用的可变的字符数组(没有使用final来修饰)
- String是一个final修饰的类,final修饰的类不可以被继承。
- 拼接字符串不能使用String进行拼接,要使用StringBuilder或者StringBuffer
- StringBuilder是线程不安全的,效率较高.而StringBuffer是线程安全的,效率较低
字符串常量池
JVM为了减少字符串对象的重复创建,其维护了一个特殊的内存,这段内存被成为字符串常量池。
当代码中出现字面量形式创建字符串对象时(String s = "abc";),JVM首先会对这个字面量进行检查,如果字符串常量池中存在相同内容的字符串对象的引用,则将这个引用返回,否则新的字符串对象被创建,然后将这个引用放入字符串常量池,并返回该引用。
字符串常量池实现的前提条件就是Java中String对象是不可变的,这样可以安全保证多个变量共享同一个对象。
基本数据类型,最大最小值,默认值

注意:long默认为0L,int默认0,boolean默认false,int 最大最小算法,去除第一位符号位,所以是31次方
IntegerCache
在Integer类中的静态内部类
Integer i = 10;
该类的作用是将数值等于-128 到127(默认)区间的Integer实例缓存到cache数组中。
通过valueOf()方法很明显发现,当再次创建值在-128到127区间的Integer实例时,会复用缓存中的实例,也就是直接指向缓存中的Integer实例。注意,这里的创建不包括用new创建,new创建对象不会复用缓存实例。
1、 Byte、Short、Integer、Long、Character都是具有缓存机制的类。
缓存工作都是在静态块中完成,在类生命周期的初始化阶段执行。
2、缓存范围?
Byte,Short,Integer,Long为 -128 到 127。Character范围为 0 到 127
3、Integer可以通过jvm参数指定缓存范围,其它类都不行。
Integer的缓存上界high可以通过jvm参数-XX:AutoBoxCacheMax=size指定,取指定值与127的最大值并且不超过Integer表示范围,而下界low不能指定,只能为-128。
自动装箱与自动拆箱
包装类型:每一个基本的数据类型都会一一对应一个包装类型。
|
byte |
Byte |
|
short |
Short |
|
int |
Integer |
|
long |
Long |
|
float |
Float |
|
double |
Double |
|
char |
Character |
|
boolean |
Boolean |
装箱:把基本的数据类型转换成对应的包装类型.
拆箱:是把包装类型转换为基本数据类型.基本数据类型
在jdk1.5之后实现了自动装/拆箱,实际是发生在编译时:装箱调用valueOf方法,拆箱调用xxxValue方法
final关键字
final 关键字主要⽤在三个地⽅:变量、⽅法、类。
final int[] arr = {1,2,3};
arr[0]=8;
final char[] value = {'a','b','c'}; "abc";
- 对于⼀个 final 变量,如果是基本数据类型的变量,则其数值⼀旦在初始化之后便不能更
改,称之为常量;如果是引⽤类型的变量,则在对其初始化之后便不能再让其指向另⼀个对象。 - 当⽤ final 修饰⼀个类时,表明这个类不能被继承。final 类中的所有成员⽅法都会被隐式地
指定为 final ⽅法。 - 使⽤ final ⽅法的原因有两个。第⼀个原因是把⽅法锁定,以防任何继承类修改它的含义;
第⼆个原因是效率。在早期的 Java 实现版本中,会将 final ⽅法转为内嵌调⽤。但是如果⽅
法过于庞⼤,可能看不到内嵌调⽤带来的任何性能提升(现在的 Java 版本已经不需要使⽤
final ⽅法进⾏这些优化了)。类中所有的 private ⽅法都隐式地指定为 final。
static关键字
static关键字两个基本的用法:静态变量和静态方法.也就是被static所修饰的变量/方法都属于类的静态资源,类实例所共享。
除了静态变量和静态方法之外,static也用于静态块,多用于初始化操作。
此外static也多用于修饰内部类,此时称之为静态内部类.
switch支持的数据类型
int,char, short, byte以为他们对应的包装类型,jdk1.5之后添加枚举类型,jdk1.7之后添加String
什么是构造方法,有什么特性
构造方法是类中的一种特殊的方法,其主要作⽤是完成对类对象的初始化⼯作(创建对象)。
- 名字与类名相同。
- 没有返回值,但不能⽤ void 声明构造函数。
- ⽣成类的对象时⾃动执⾏,⽆需调⽤。
- 类中不显示的声明构造方法时,会默认带一个无参的构造方法,如果显示的声明了构造方法(无论是有参的还是无参的)默认的构造方法就不存在了。
- 调用子类的构造方法时,子类构造方法中默认会在方法的第一行带有super()这行代码,是为了先执行父类的构造方法。
抽象类与接口的异同
- 接⼝的⽅法默认是 public ,所有⽅法在接⼝中不能有实现(Java 8 开始接⼝⽅法可以有默认
实现),⽽抽象类可以有⾮抽象的⽅法。 - 接⼝中除了 static 、 final 变量(常量),不能有其他变量,⽽抽象类中则不⼀定。
- ⼀个类可以实现多个接⼝,但只能实现⼀个抽象类。接⼝⾃⼰本身可以通过 extends 关键
字扩展多个接⼝。 - 接⼝⽅法默认修饰符是 public ,抽象⽅法可以有 public 、 protected 和 default 这些修饰
符(抽象⽅法就是为了被重写所以不能使⽤ private 关键字修饰!)。 - 从设计层⾯来说,抽象是对类的抽象,是⼀种模板设计,⽽接⼝是对⾏为的抽象,是⼀种⾏
为的规范。 - 关于版本性的变化:
- 在 jdk 7 或更早版本中,接⼝⾥⾯只能有常量变量和抽象⽅法。这些接⼝⽅法必须由选择实
现接⼝的类实现。 - jdk 8 的时候接⼝可以有默认⽅法和静态⽅法功能。
- Jdk 9 在接⼝中引⼊了私有⽅法和私有静态⽅法。
==与equals的区别
== : 它的作⽤是判断两个对象的地址是不是相等。即,判断两个对象是不是同⼀个对象(基本数据类型==⽐的是值,引⽤数据类型==⽐的是内存地址)。
equals() : 它的作⽤也是判断两个对象是否相等。但它⼀般有两种使⽤情况:
情况 1:类没有覆盖 equals() ⽅法。则通过 equals() ⽐该类的两个对象时,等价于通过“==”⽐这两个对象。
情况 2:类覆盖了 equals() ⽅法。⼀般,我们都覆盖 equals() ⽅法来⽐两个对象的内容是否相等;若它们的内容相等,则返回 true (即,认为这两个对象相等)。
比如String类就重写过equals方法,因为 object 的 equals ⽅法是⽐的对象的内存地址,⽽ String 的equals ⽅法⽐的是对象的值。
线程和进程
进程:指在系统中正在运行的一个应用程序;程序一旦运行就是进程;或者更专业化来说:进程是指程序执行时的一个实例,即它是程序已经执行到课中程度的数据结构的汇集。从内核的观点看,进程的目的就是担当分配系统资源(CPU时间、内存等)的基本单位。
线程:系统分配处理器时间资源的基本单元,或者说进程之内独立执行的一个单元执行流。进程——资源分配的最小单位,线程——程序执行的最小单位。
线程无法独立运行,依赖于进程存在,一个进程至少包含一个线程。
线程的状态
- New 初始状态,线程被创建,但是还没调用start。
- Runnable 就绪状态
- Running 运行状态
- Blocked 阻塞状态
- Terminated 终止状态

创建多线程的几种方式以及区别
- 继承Thread类重写run方法
- 实现Runnable接口重写run方法
- 实现Callable接口重写call方法
- 线程池方式创建
通过继承Thread类或者实现Runnable接口、Callable接口都可以实现多线程,不过实现Runnable接口与实现Callable接口的方式基本相同,只是Callable接口里定义的方法返回值,可以声明抛出异常而已。因此将实现Runnable接口和实现Callable接口归为一种方式。
采用实现接口的方式创建线程的优缺点
优点:线程类只是实现了Runnable或者Callable接口,还可以继承其他类。这种方式下,多个线程可以共享一个target对象,所以非常适合多个相同线程来处理同一份资源的情况,从而可以将CPU、代码和数据分开,形成清晰的模型,较好的体现了面向对象的思想。
缺点:编程稍微复杂一些,如果需要访问当前线程,则必须使用 Thread.currentThread() 方法
采用继承Thread类的方式创建线程的优缺点优点:编写简单,如果需要访问当前线程,则无需使用Thread.currentThread() 方法,直接使用 this即可获取当前线程
缺点:因为线程类已经继承了Thread类,Java语言是单继承的,所以就不能再继承其他父类了。
Synchronized
此关键字解决的是多个线程之间访问资源的同步性, synchronized 关键字可以保证被它修饰的⽅法或者代码块在任意时刻只能有⼀个线程执⾏。 早期版本中synchronized属于重量级锁,效率低下。
因为监视器锁(monitor)是依赖于底层的操作系统的 Mutex Lock 来实现的,Java 的线程是映射到操作系统的原⽣线程之上的。如果要挂起或者唤醒⼀个线程,都需要操作系统帮忙完成,⽽操作系统实现线程之间的切换时需要从⽤户态转换到内核态,这个状态之间的转换需要相对⽐⻓的时间,时间成本相对⾼。
庆幸的是在 Java 6 之后 Java 官⽅对从 JVM 层⾯对 synchronized 较⼤优化,所以现在的synchronized 锁效率也优化得很不错了。JDK1.6 对锁的实现引⼊了⼤量的优化,如⾃旋锁、适应性⾃旋锁、锁消除、锁粗化、偏向锁、轻量级锁等技术来减少锁操作的开销。
用法:
- 修饰方法 作⽤于当前对象实例加锁,进⼊同步代码前要获得当前对象实例的锁
- 修饰静态方法 也就是给当前类加锁,会作⽤于类的所有对象实例 ,进⼊同步代码前要获得当前 class 的锁
- 修饰代码块 指定加锁对象,对给定对象/类加锁,比如synchronized(this)或者synchronized(User.class)
异常的分类

在 Java 中,所有的异常都有⼀个共同的祖先 java.lang 包中的 Throwable 类。 Throwable 类有两个重要的⼦类 Exception (异常)和 Error (错误)。 Exception 能被程序本身处理( trycatch ), Error 是⽆法处理的(只能尽量避免)。
受检查异常
Java 代码在编译过程中,如果受检查异常没有被 catch / throw 处理的话,就没办法通过编译。
除了 RuntimeException 及其⼦类以外,其他的 Exception 类及其⼦类都属于检查异常 。常⻅的受检查异常有: IO 相关的异常、 ClassNotFoundException 、 SQLException ...
不受检查异常
Java 代码在编译过程中 ,我们即使不处理不受检查异常也可以正常通过编译。RuntimeException 及其⼦类都统称为⾮受检查异常,例如: NullPointExecrption 、 NumberFormatException (字符串转换为数
字)、 ArrayIndexOutOfBoundsException (数组越界)、 ClassCastException (类型转换错误)、 ArithmeticException (算术错误)等。
try,catch,finally用法
try 块: ⽤于捕获异常。其后可接零个或多个 catch 块,如果没有 catch 块,则必须跟⼀个 finally 块。
catch 块: ⽤于处理 try 捕获到的异常。
finally 块: ⽆论是否捕获或处理异常, finally 块⾥的语句都会被执⾏。当在 try 块或catch 块中遇到 return 语句时, finally 语句块将在⽅法返回之前被执⾏。
注意: 当 try 语句和 catch 语句中有 return 语句时,在⽅法返回之前,finally 语句的内容将被执⾏,但是finally不会影响返回值的结果(注意引用类型返回的是地址)。但是finally中也有return时, finally 语句的返回值将会覆盖原始的返回值。
集合

Java中的集合分为:value(Conllection),key-value(Map)两种。
1、存储值有分为List 和Set.
List是有序的,可以重复的。Set是无序的,不可以重复的。
根据equals和hashcode判断,也就是如果一个对象要存储在Set中,必须重写equals和hashCode方法。
2、存储key-value的为map.
List,Set,Map
List (对付顺序的好帮⼿): 存储的元素是有序的、可重复的。
Set (注重独⼀⽆⼆的性质): 存储的元素是⽆序的、不可重复的。
Map (⽤ Key 来搜索的专家): 使⽤键值对(kye-value)存储,Key 是⽆序的、不可重复的,value 是⽆序的、可重复的,每个键最多映射到⼀个值。
ArrayList与LinkedList的区别
ArrayList底层使用的是数组。LinkedList使用的是链表。
数组查询具有所有查询特定元素比较快。而插入和删除和修改比较慢(数组在内存中是一块连续的内存,如果插入或删除是需要移动内存)。
链表不要求内存是连续的,在当前元素中存放下一个或上一个元素的地址。查询时需要从头部开始,一个一个的找。所以查询效率低。插入时不需要移动内存,只需改变引用指向即可。所以插入或者删除的效率高。
ArrayList使用在查询比较多,但是插入和删除比较少的情况,而LinkedList使用在查询比较少而插入和删除比较多的情况。
Vector与ArrayList
相同点:Vector与ArrayList本质上底层都是一个Object[] 数组
不同点:
1) Vector是线程安全的集合类,ArrayList并不是线程安全的类。
Vector类对集合的元素操作时都加了synchronized,保证线程安全。
2) Vector与ArrayList的扩容并不一样,Vector默认扩容是增长一倍的容量,
Arraylist是增长50%的容量,ArrayList就有利于节约内存空间。
Vector无论查询还是增删都很慢,所以被ArrayList替代了。
HashMap,HashTable,ConcurrentHashMap
相同点:HashMap和HasheTable都可以使用来存储key--value的数据。
区别:
1、HashMap是可以把null作为key或者value的,而HashTable是不可以的。
2、HashMap是线程不安全的,效率较高。而HashTable是线程安全的,效率较低。
我想线程安全但是我又想效率高?
通过把整个Map分为N个段(Segment:类似HashTable),操作前面的不影响后面的使用,可以提供相同的线程安全,但是效率提升N倍,默认提升16倍,这就是ConcurrentHashMap的策略。
HashMap树化与退化
树化规则:
当链表长度超过树化阈值 8 时,先尝试扩容来减少链表长度,如果数组容量已经 >=64,才会进行树化
退化规则
情况1:在扩容时如果拆分树时,树元素个数 <= 6 则会退化链表
情况2:remove 树节点时,若 root、root.left、root.right、root.left.left 有一个为 null ,也会退化为链表
HashMap,ArrayList,LinkedList扩容
1、ArrayList 的默认大小是10个元素。扩容点规则是,新增的时候发现容量不够用了,就去扩容。扩容大小规则是:1.5倍
2、HashMap 的默认大小是16个元素(必须是2的幂)。扩容因子默认0.75,扩容机制.(例如:初始大小为 16 ,扩容因子 0.75 ,当容量为12(16*0.75=12)的时候(这个值是size的值也就是put的key-value的数量),触发扩容,扩容后的大小为 32)
3、LinkedList 是一个双向链表,没有初始化大小,也没有扩容的机制,就是一直在前面或者后面新增就好。
遍历方式
- Set
因为Set无序,所以不可以使用for(int i =0;i<length;i++)的方式,可以使用for(String i:is)的方式。
另外可以使用对象.iterator()方法获取迭代器进行遍历
- List
List有顺序,所以加上for(int i =0;i<length;i++)的三种方式都可以。
- Map
对象.entrySet()获取一个键值对的set集合,每一个是一个entry键值对,再for,推荐用此方式。
对象.keySet()获取所有key的集合,再for,根据每个key获取值
对象.values()获取所有的值,此方式无法获取key的相关数据,一般不使用。
DB
存储引擎
- MYISAM:全表锁,拥有较高的执行速度,不支持事务,不支持外键,并发性能差,占用空间相对较小,对事务完整性没有要求,以select、insert为主的应用基本上可以使用这引擎
- Innodb:行级锁,提供了具有提交、回滚和崩溃回复能力的事务安全,支持自动增长列,支持外键约束,并发能力强,占用空间是MYISAM的2.5倍,处理效率相对会差一些
- 5.5版本前默认引擎为MYISAM,现在默认为Innodb
索引
索引相当于一本书的目录,通过目录我们可以迅速定位书中要找的内容。MySQL中的索引也是一样,它是一种帮助MySQL高效获取数据的数据结构(树)。
建索引的优缺点
优点:大大加快对数据的查询速度
缺点:占物理空间,而且对数据库进行增删改的时候也要动态的维护索引。
分类:
- 主键索引(PRIMAY KEY)
- 唯一索引(UNIQUE)
- 常规索引(INDEX)
- 全文索引(FULLTEXT)
使用索引时,有以下一些技巧和注意事项:
- 索引不会包含有NULL值的列
只要列中包含有NULL值都将不会被包含在索引中,复合索引中只要有一列含有NULL值,那么这一列对于此复合索引就是无效的。所以我们在数据库设计时不要让字段的默认值为NULL。
- 使用短索引
对串列进行索引,如果可能应该指定一个前缀长度。例如,如果有一个CHAR(255)的列,如果在前10个或20个字符内,多数值是惟一的,那么就不要对整个列进行索引。短索引不仅可以提高查询速度而且可以节省磁盘空间和I/O操作。
- 索引列排序
MySQL查询只使用一个索引,因此如果where子句中已经使用了索引的话,那么order by中的列是不会使用索引的。因此数据库默认排序可以符合要求的情况下不要使用排序操作;尽量不要包含多个列的排序,如果需要最好给这些列创建复合索引。
- like语句操作
一般情况下不鼓励使用like操作,如果非使用不可,如何使用也是一个问题。like “%a%” 不会使用索引而like “aaa%”可以使用索引。
- 避免在索引上使用计算
- 尽量避免使用in和NOT IN和<>操作,否则会导致全表扫描 因为逻辑判断会让索引失效。
- 应尽量避免在 where 子句中使用 or 来连接条件,否则将导致引擎放弃使用索引而进行全表扫描
索引失效的场景
(1)like 以%开头,索引无效;当like前缀没有%,后缀有%时,索引有效。
(2)or语句前后没有同时使用索引。当or左右查询字段只有一个是索引,该索引失效,只有当or左右查询字段均为索引时,才会生效
(3)组合索引,不是使用第一列索引,索引失效(最左匹配规则)。
(4)数据类型出现隐式转化。如varchar不加单引号的话可能会自动转换为int型(用select查询时),使索引无效,产生全表扫描。
(5)在索引列上使用 IS NULL 或 IS NOT NULL操作(在 where 子句中对字段进行 null 值判断)
(6)在索引字段上使用not,<>,!=。不等于操作符是永远不会用到索引的,因此对它的处理只会产生全表扫描。 优化方法: key<>0 改为 key>0 or key<0。
(7)对索引字段进行计算操作、字段上使用函数。
(8)当全表扫描速度比索引速度快时,mysql会使用全表扫描,此时索引失效。
@Transactional注解失效的场景
1.@Transactional 注解只能应用到 public 可见度的方法上。
2.默认情况下,Spring会对Error或者RuntimeException异常进行事务回滚,
其他继承自java.lang.Exception的异常:如IOException、TimeoutException等,不会回滚。
解决方案:Transactional注解加rollbackFor 属性,指定java.lang.Exception.class;
3.try-catch异常,事务不会生效
spring的事务是在调用业务方法之前开始的,业务方法执行完毕之后才执行commit or rollback,
事务是否执行取决于是否抛出runtime异常。如果抛出runtime exception 并在你的业务方法中没有catch到的话,事务会回滚。
(尽量不要写try-catch 如果要写的同时还要保证事务回滚可以尝试在catch最后一行throw一个runtimeException,类似这样)

4.@Transactional 注解属性 propagation 设置错误
TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。
TransactionDefinition.PROPAGATION_NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。
5.同一个类中方法调用,导致@Transactional失效
场景:开发中避免不了会对同一个类里面的方法调用,比如有一个类Test,它的一个方法A,A再调用本类的方法B(不论方法B是用public还是private修饰),但方法A没有声明注解事务,而B方法有。则外部调用方法A之后,
方法B的事务是不会起作用的。
原因:由于使用Spring AOP代理造成的,因为只有当事务方法被当前类以外的代码调用时,才会由Spring生成的代理对象来管理。
6.数据库引擎不支持事务。(仅InnoDB支持)
事务
- 事务是逻辑上的⼀组操作,要么都执⾏,要么都不执⾏。
- 事务最经典也经常被拿出来说例⼦就是转账了。假如⼩明要给⼩红转账1000元,这个转账会涉及
到两个关键操作就是:将⼩明的余额减少1000元,将⼩红的余额增加1000元。万⼀在这两个操
作之间突然出现错误⽐如银⾏系统崩溃,导致⼩明余额减少⽽⼩红的余额没有增加,这样就不对
了。事务就是保证这两个关键操作要么都成功,要么都要失败。
事务的特性
事务的特性:ACID
- 原⼦性(Atomicity): 事务是最⼩的执⾏单位,不允许分割。事务的原⼦性确保动作要么
全部完成,要么完全不起作⽤; - ⼀致性(Consistency): 执⾏事务前后,数据保持⼀致,多个事务对同⼀个数据读取的结
果是相同的; - 隔离性(Isolation): 并发访问数据库时,⼀个⽤户的事务不被其他事务所⼲扰,各并发
事务之间数据库是独⽴的; - 持久性(Durability): ⼀个事务被提交之后。它对数据库中数据的改变是持久的,即使数
据库发⽣故障也不应该对其有任何影响。
并发事务问题
在典型的应⽤程序中,多个事务并发运⾏,经常会操作相同的数据来完成各⾃的任务(多个⽤户对同⼀数据进⾏操作)。并发虽然是必须的,但可能会导致以下的问题。
脏读(Dirty read): 当⼀个事务正在访问数据并且对数据进⾏了修改,⽽这种修改还没有提交到数据库中,这时另外⼀个事务也访问了这个数据,然后使⽤了这个数据。因为这个数据是还没有提交的数据,那么另外⼀个事务读到的这个数据是“脏数据”,依据“脏数据”所做的操作可能是不正确的。
丢失修改(Lost to modify): 指在⼀个事务读取⼀个数据时,另外⼀个事务也访问了该数据,那么在第⼀个事务中修改了这个数据后,第⼆个事务也修改了这个数据。这样第⼀个事务内的修改结果就被丢失,因此称为丢失修改。 例如:事务1读取某表中的数据A=20,事务2也读取A=20,事务1修改A=A-1,事务2也修改A=A-1,最终结果A=19,事务1的修改被丢失。
不可重复读(Unrepeatableread): 指在⼀个事务内多次读同⼀数据。在这个事务还没有结束时,另⼀个事务也访问该数据。那么,在第⼀个事务中的两次读数据之间,由于第⼆个事务的修改导致第⼀个事务两次读取的数据可能不太⼀样。这就发⽣了在⼀个事务内两次读到的数据是不⼀样的情况,因此称为不可重复读。
幻读(Phantom read): 幻读与不可重复读类似。它发⽣在⼀个事务(T1)读取了⼏⾏数据,接着另⼀个并发事务(T2)插⼊了⼀些数据时。在随后的查询中,第⼀个事务(T1)就会发现多了⼀些原本不存在的记录,就好像发⽣了幻觉⼀样,所以称为幻读。
重点注意:
不可重复读和幻读区别:
不可重复读的重点是修改⽐如多次读取⼀条记录发现其中某些列的值被修改,幻读的重点在于新增或者删除⽐如多次读取⼀条记录发现记录增多或减少了。
隔离级别
READ-UNCOMMITTED(读取未提交): 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。
READ-COMMITTED(读取已提交): 允许读取并发事务已经提交的数据,可以阻⽌脏读,但是幻读或不可重复读仍有可能发⽣。
REPEATABLE-READ(可重复读):对同⼀字段的多次读取结果都是⼀致的,除⾮数据是被本身事务⾃⼰所修改,可以阻⽌脏读和不可重复读,但幻读仍有可能发⽣。
SERIALIZABLE(可串⾏化): 最⾼的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执⾏,这样事务之间就完全不可能产⽣⼲扰,也就是说,该级别可以防⽌脏读、不可重复读以及幻读。
mysql中Innodb引擎下默认的隔离级别为:REPEATABLE-READ(可重复读)
三范式
第一范式:列不可再分
第二范式:行可以唯一区分,主键约束
第三范式:表的非主属性不能依赖与其他表的非主属性外键约束且三大范式是一级一级依赖的,第二范式建立在第一范式上,第三范式建立第一第二范式上。
表连接哪几种,区别是啥
- 在SQL中常用的连接有:内连接,外连接,全连接
- mysql数据库不支持全连接
- 只返回两张表中匹配上的记录叫内连接(inner join)
- 返回匹配上的记录和主表的多余记录(outer join)
- 外连接分左外连接(left join)和右外连接(right join),分别表示左边或者右边的表为主表
连接池
数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个;释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放数据库连接而引起的数据库连接遗漏。这项技术能明显提高对数据库操作的性能。
JDBC的步骤
- 加载JDBC驱动程序:Class.forName("com.mysql.cj.jdbc.Driver")
- 创建数据库的连接: Connection conn;
- 创建一个preparedStatement : ps = conn.prepareStatement("select * from 表");
- 执行SQL语句 :ps.executeUpdate ps.executeQuery
- 遍历结果集 : ResultSet rs / rs.next()方法
- 处理异常,关闭JDBC对象资源:注意关闭顺序
Mysql实现分页方式
最简单的方式:使用limit关键字
语法:select * from 表 limit a,b
a表示查询当前开始的下标值,b表示一共要查询出几条数据
聚合函数
- count(col): 表示求指定列的总行数
- max(col): 表示求指定列的最大值
- min(col): 表示求指定列的最小值
- sum(col): 表示求指定列的和
- avg(col): 表示求指定列的平均值
注意:sum与count之间的区别
sum()函数求累加; 对符合条件的记录的数值列求和;
count()函数求个数; 对查询中符合条件的结果(或记录)的个数
数据库中对空值的处理:sum()不计算,count()认为没有此项;
where,having
- where:
where是一个约束声明,使用where来约束来自数据库的数据;
where是在结果返回之前起作用的;
where中不能使用聚合函数。
- having:
having是一个过滤声明;
在查询返回结果集以后,对查询结果进行的过滤操作;
在having中可以使用聚合函数。
JAVA EE
Servlet
servlet是Server Applet的简称,翻译过来就是服务程序。
简单地讲,这个servlet是运行在服务器上的一个小程序,用来处理服务器请求的.进一步讲,我们知道,一般的网页程序,是由我们通过浏览器访问来实现的,在这个过程中,我们的浏览器发送访问请求,服务器接收请求,并对浏览器的请求作出相应的处理.这就是我们熟悉的B/S模型(浏览器-服务器模型).而servlet就是对请求作出处理的组件,运行于支持Java的应用服务器中.
Servlet生命周期
主要分为以下几个阶段:
加载类—>实例化(为对象分配空间)—>初始化(为对象的属性赋值)—>请求响应(服务阶段)—>销毁
方法:
init()初始化阶段
service()处理客户端请求阶段
destroy()终止阶段

转发和重定向
1.重定向访问服务器两次,转发只访问服务器一次。
2.转发页面的URL不会改变,而重定向地址会改变
3.转发只能转发到自己的web应用内,重定向可以重定义到任意资源路径。
4.转发相当于服务器跳转,相当于方法调用,在执行当前文件的过程中转向执行目标文件,两个文件(当前文件和目标文件)属于同一次请求,前后页 共用一个request,可以通过此来传递一些数据或者session信息,request.setAttribute()和 request.getAttribute()。而重定向会产生一个新的request,不能共享request域信息与请求参数
5.由于转发相当于服务器内部方法调用,所以转发后面的代码仍然会执行(转发之后记得return);重定向代码执行之后是方法执行完成之后进行重定向操作,也就是访问第二个请求,如果是方法的最后一行进行重定向那就会马上进行重定向(重定向也需要return)。
6.无论是RequestDispatcher.forward方法,还是HttpServletResponse.sendRedirect方法,在调用它们之前,都不能有内容已经被实际输出到了客户端。如果缓冲区中已经有了一些内容,这些内容将被从缓冲区中移除。
Filter
Java EE 中的Filter过滤器,可以拦截请求和响应,进行一些业务逻辑需要的处理。
作用:
(1)在客户端的请求访问后端资源之前,拦截这些请求。
(2)在服务器的响应发送回客户端之前,处理这些响应。

过滤器与拦截器
过滤器 (Filter) 和 拦截器 (Interceptor)
1、实现原理不同 过滤器和拦截器底层实现方式大不相同,过滤器 是基于函数回调的,拦截器 则是基于Java的反射机制(动态代理)实现的。
2、使用范围不同 我们看到过滤器 实现的是 javax.servlet.Filter 接口,而这个接口是在Servlet规范中定义的,也就是说过滤器Filter 的使用要依赖于Tomcat等容器,导致它只能在web程序中使用。 而拦截器(Interceptor) 它是一个Spring组件,并由Spring容器管理,并不依赖Tomcat等容器,是可以单独使用的。不仅能应用在web程序中,也可以用于Application、Swing等程序中。
3、触发时机不同 过滤器Filter是在请求进入容器后,但在进入servlet之前进行预处理,请求结束是在servlet处理完以后。拦截器 Interceptor 是在请求进入servlet后,在进入Controller之前进行预处理的,Controller 中渲染了对应的视图之后请求结束。
4、拦截的请求范围不同 过滤器Filter执行了两次,拦截器Interceptor只执行了一次。这是因为过滤器几乎可以对所有进入容器的请求起作用,而拦截器只会对Controller中请求或访问static目录下的资源请求起作用。
5、注入Bean情况不同 这是因为加载顺序导致的问题,拦截器加载的时间点在springcontext之前,而Bean又是由spring进行管理。
6、控制执行顺序不同 过滤器用@Order注解控制执行顺序,通过@Order控制过滤器的级别,值越小级别越高越先执行。 拦截器默认的执行顺序,就是它的注册顺序,也可以通过Order手动设置控制,值越小越先执行
四大作用域
从小到大:
1.page作用域 :仅限于用户请求的当前页面
2.request作用域:一个HTTP请求的处理可能需要多个Servlet合作,而这几个Servlet之间可以通过某种方式传递信息,但这个信息在请求结束后就无效了。
3.session作用域 :同一浏览器对服务器进行多次访问,在这多次访问之间传递信息,就是session作用域的体现。
4.application 作用域:application作用域就是服务器启动到关闭的整段时间,在这个作用域内设置的信息可以被所有应用程序使用。
九大内置对象
1、request对象
request 对象是 javax.servlet.httpServletRequest类型的对象。 该对象代表了客户端的bai请求信息,主要用于接受通过HTTP协议传送到服务器的数据。(包括头信息、系统信息、请求方式以及请求参数等)。request对象的作用域为一次请求。
2、response对象
response 代表的是对客户端的响应,主要是将JSP容器处理过的对象传回到客户端。response对象也具有作用域,只在JSP页面内有效。
3、session对象
session 对象是由服务器自动创建的与用户请求相关的对象。服务器为每个用户都生成一个session对象,用于保存该用户的信息,跟踪用户的操作状态。session对象内部使用Map类来保存数据,因此保存数据的格式为 “Key/value”。 session对象的value可以使复杂的对象类型,而不仅仅局限于字符串类型。
4、application对象
application 对象可将信息保存在服务器中,直到服务器关闭,否则application对象中保存的信息会在整个应用中都有效。与session对象相比,application对象生命周期更长,类似于系统的“全局变量”。
5、out 对象
out 对象用于在Web浏览器内输出信息,并且管理应用服务器上的输出缓冲区。在使用 out 对象输出数据时,可以对数据缓冲区进行操作,及时清除缓冲区中的残余数据,为其他的输出让出缓冲空间。待数据输出完毕后,要及时关闭输出流。
6、pageContext 对象
pageContext 对象的作用是取得任何范围的参数,可以获取 JSP页面的out、request、reponse、session、application 等对象。pageContext对象的创建和初始化都是由容器来完成的,在JSP页面中可以直接使用 pageContext对象。
7、config 对象
config 对象的主要作用是取得服务器的配置信息。通过 pageConext对象的 getServletConfig() 方法可以获取一个config对象。当一个Servlet 初始化时,容器把某些信息通过 config对象传递给这个 Servlet。 开发者可以在web.xml 文件中为应用程序环境中的Servlet程序和JSP页面提供初始化参数。
8、page 对象
page 对象代表JSP本身,只有在JSP页面内才是合法的。 page隐含对象本质上包含当前 Servlet接口引用的变量,类似于Java编程中的 this 指针。
9、exception 对象
exception 对象的作用是显示异常信息,只有在包含 isErrorPage="true" 的页面中才可以被使用,在一般的JSP页面中使用该对象将无法编译JSP文件。excepation对象和Java的所有对象一样,都具有系统提供的继承结构。exception 对象几乎定义了所有异常情况。在Java程序中,可以使用try/catch关键字来处理异常情况;在JSP页面中出现没有捕获到的异常,就会生成 exception 对象,并把 exception 对象传送到在page指令中设定的错误页面中,然后在错误页面中处理相应的 exception 对象。
常见的Http状态码
200:请求成功
302:一般用于重定向
400:错误请求,服务器不理解请求的语法(请求地址正确,参数传递有问题)
404:未找到,请求的资源不存在
405:方法禁用,一般是请求方式不正确,比如后台只允许get方式请求此资源,前台使用的post方式
500:请求错误,服务器遇到错误,无法完成请求
请求方式get与post的区别
HTTP协议中的两种发送请求的方法,本质上都是在进行TCP连接.
- GET请求参数是通过URL进行传递的,POST请求的参数包含在请求体当中。
- GET请求比POST请求更不安全,因为参数直接暴露在URL中,所以,GET请求不能用来传递敏感信息。
- GET请求在url中传递的参数是有长度限制的(在HTTP协议中并没有对URL的长度进行限制,限制是特定的浏览器以及服务器对他的限制,不同浏览器限制的长度不同。),POST对长度没有限制。
- GET请求参数会完整的保留在浏览器的历史记录中,POST请求的参数不会保留。
- GET请求进行url编码(百分号编码),POST请求支持多种编码方式。
- GET请求产生的URL地址是可以被bookmark(添加书签)的,POST请求不可以。
- GET请求在浏览器回退的时候是无害的,POST请求会.再次提交数据。
- GET请求在浏览器中可以被主动cache(缓存),而POST请求不会,可以手动设置。
SSM
spring mvc的运行原理(工作流程)

1.客户端请求提交到DispatcherServlet
2.DispatcherServlet接收到请求后、将提交的信息交给处理器映射器(HandlerMapping)
3.HandlerMapping根据用户的url请求、匹配该url的Handler(Controller),并返回一个执行链
4.DispatcherServlet调用HandlerAdapter(处理器适配器)
5.HandlerAdapter经过适配调用具体的处理器(Controller)扫描
6.Controller扫描完成后返回一个ModelAndView
7.HandlerAdapter将Controller扫描结果(ModelAndView)返回给DispatcherServlet
8.DispatcherServlet将ModelAndView请求试图解析器(ViewReslover)进行解析
9.ViewReslover解析后返回具体的View给前端控制器DispatcherServlet
10.DispatcherServlet将view进行渲染试图(即将模型数据填充到视图中)
11.DispatcherServlet将页面响应给个用户
Springmvc中转发与重定向
- 转发到视图
在handler方法中,返回字符串,springmvc就会走视图解析器,转发到视图页面
或返回ModelAndView对象,通过setViewName的方式指定视图,也转发到视图
- 转发到Handler
在handler方法中返回值 为"forward:*****",springmvc会将请求转发到forward:后对应的请求地址的handler中,但是客户端只发了一次请求,浏览器地址栏不变
- 重定向到Handler
在handler方法中返回值为"redirect:*****",springmvc会将请求重定向到redirect:后对应的请求地址的handler中,客户端会再次发起新请求,地址栏会变
Springmvc常用注解
@RequestMapping
映射请求地址,可加在类和方法上,加在方法上时默认映射所有的请求方式,如果只限制一种可以使用 @GetMapping @PostMapping 等些类注解
@RestController
组合了@Controller和@ResponseBody,表示此handler中所有的方法都不进入视图,直接返回数据给前台
@RequestParam
用于将请求参数区数据映射到功能处理方法的参数上
@PathVariable
用于将请求URL中的模板变量映射到功能处理方法的参数上,即取出uri模板中的变量作为参数。
@ResponseBody
用于将Controller的方法返回的对象,通过适当的HttpMessageConverter转换为指定格式后,写入到Response对象的body数据区
@RequestBody
允许request的参数在request体中,而不是在直接链接在地址后面,一般可以用于请求参数json字符串与对象的自动转换。
Spring常用注解
- 声明bean
@Component 泛指各种组件,创建普通bean
@Controller 控制层 @Service 业务层 @Repository 数据访问层
- 注入bean
@Autowired 依赖注入
- 配置类
@Configuration 配置类
@Bean 用在方法上,把返回值创建一个bean
- aop
@Aspect 声明一个切面
@Before @After @Around @AfterReturning @AfterThrowing 通知注解
@PointCut 切点
IOC
Inversion of Control,控制反转
1.IOC不是一种技术而是一种设计思想,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。
2.在传统Java SE程序设计,我们直接在对象内部通过new进行创建对象,IoC是有专门一个容器来创建这些对象,即由Ioc容器来控制对 象的创建;
3.何为控制反转?
传统应用程序是由我们自己在对象中主动控制去直接获取依赖对象,也就是正转;而反转则是由容器来帮忙创建及注入依赖对象;因为由容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,所以是反转;哪些方面反转了?依赖对象的获取被反转了。
4.IOC是spring的核心,贯穿始终。所谓IoC,对于spring框架来说,就是由spring来负责控制对象的生命周期和对象间的关系。而Spring提供了IOC容器来帮我们生成所需要的对象。也就是说在我们原先的对象中有用到其他对象的地方Spring会帮我们来注入。不用我们再去考虑这些问题。
DI
Dependency Injection,依赖注入
Spring框架的核心功能之一就是通过依赖注入的方式来管理Bean之间的依赖关系。
当编写一个复杂的 Java 应用程序时,应用程序类应该尽可能独立于其他 Java 类来增加这些类重用的可能性,并且在做单元测试时,测试独立于其他类的独立性。依赖注入有助于把这些类粘合在一起,同时保持他们独立。
方式:
1、Constructor-based dependency injection:基于类构造函数的DI
当容器调用带有多个参数的构造函数类时,实现基于构造函数的 DI,每个代表在其他类中的一个依赖关系。
2、Setter-based dependency injection:基于 setter 方法的 DI
基于 setter 方法的 DI 是通过在调用无参数的构造函数或无参数的静态工厂方法实例化 bean 之后,容器调用 beans 的 setter 方法来实现的。
3、接口注入
依赖类必须要实现指定的接口,然后实现该接口中的一个函数,该函数就是用于依赖注入。
侵入性强,不建议使用。
4、反射注入
将注解添加在成员变量上,当bean初始化完成之后,通过反射机制注入些成员的值
依赖注入常用的注解@Autowired @Resource,区别是:
- 方式:@Autowired默认按byType自动装配,而@Resource默认byName自动装配。
- 参数:@Autowired只包含一个参数:required,表示是否开启自动准入,默认是true。而@Resource包含七个参数,其中最重要的两个参数是:name 和 type。
- 配置:@Autowired如果要使用byName,需要使用@Qualifier一起配合。而@Resource如果指定了name,则用byName自动装配,如果指定了type,则用byType自动装配。
- 用法:@Autowired能够用在:构造器、方法、参数、成员变量和注解上,而@Resource能用在:类、成员变量和方法上。
- 来源:@Autowired是spring定义的注解,而@Resource是JSR-250定义的注解。
AOP
AOP(Aspect Oriented Programming)是一种面向切面的编程思想。
面向切面编程是将程序抽象成各个切面,即解剖对象的内部,将那些影响了多个类的公共行为抽取到一个可重用模块里,减少系统的重复代码,降低模块间的耦合度,增强代码的可操作性和可维护性。
- 切点(PointCut): 可以插入增强处理的连接点。
- 通知(Advice): AOP 框架中的增强处理。通知描述了切面何时执行以及如何执行增强处理。通知分为前置通知,后置通知,异常通知,最终通知,环绕通知(切面要完成的功能)
- 切面(Aspect): 切面是通知和切点的结合。
- Joinpoint(连接点):所谓连接点是指那些被拦截到的点,在spring中,这些点指的是方法,因为spring只支持方法类型的连接点
Mybatis中${}与#{}区别
#{} 是占位符,做预编译处理,MyBatis在处理 #{ } 时,会将 SQL 中的 #{ } 编译为 ?,对应的变量自动加单引号,能够有效防止 SQL 注入;
${} 是拼接符,即字符串替换,MyBatis 在处理 ${ } 时,就是把 ${ } 替换成变量的值。
注意:尽量使用 #{ } ,不用或少用 ${ };当传的参数为表名时,或者使用 order by ${ }时 ,必须用 ${ }。
Mybatis动态SQL
动态 SQL 是 MyBatis 的强大特性之一 。它一般是根据用户输入或外部条件动态组合的SQL语句块。 动态SQL能灵活的发挥SQL强大的功能、方便的解决一些其它方法难以解决的问题。
Mybatis 动态 sql 可以让我们在 Xml 映射文件内,以标签的形式编写动态 sql,完成逻辑判断和动态拼接 sql 的功能。
常用的动态sql标签:
- if
判断语句,单条件分支判断
- choose(when,otherwise)
相当于java中的switch case语句,多条件分支判断
- where
辅助元素,用于处理一些sql拼装的问题,不确定是否需要加where时
- foreach
循环语句,一般在in语句等列举条件时常用
Mybatis高级关系映射
resultType:将查询结果按照sql列名pojo属性名一致性映射到pojo对象中
resultMap:使用association和collection完成高级映射(对结果有特殊的映射要求)
association:一对一关系映射 将关联查询信息映射到一个pojo对象中
collection:将关联查询信息映射到一个list集合中
该博客围绕Java开发展开,涵盖JAVASE、DB、JAVA EE、SSM等方面。介绍了JDK、JRE、JVM区别,面向对象特性,重写与重载差异等基础知识;还涉及数据库存储引擎、索引,Servlet生命周期,Spring MVC原理,MyBatis动态SQL等内容。
2266





