Java 笔记
JRE 和 JDK
- JRE:Java 程序的运行时环境,包含 JVM 和运行时所需要的核心类库 。
- JDK:Java 程序开发工具包,包含 JRE 和开发人员使用的工具。
编译和运行
- 编译:指将编写的 Java 源文件翻译成 JVM 能认识的 class 字节码文件,javac 编译器会检查程序中是否有错,有错误就会提示,无错误则编译成功。
- 运行:指将 class 文件 交给 JVM 运行。
基本数据类型
数据类型 | 关键字 | 内存占用 | 取值范围 |
---|---|---|---|
字节型 | byte | 1个字节 | -128~127 |
短整型 | short | 2个字节 | -32768~32767 |
整型 | int(默认) | 4个字节 | -2的31次方~2的31次方-1 |
长整型 | long | 8个字节 | -2的63次方~2的63次方-1 |
单精度浮点数 | float | 4个字节 | -3.4028E+38~3.4028E+38 |
双精度浮点数 | double(默认) | 8个字节 | -1.7977E+308~1.7977E+308 |
字符型 | char | 2个字节 | 0~65535 |
布尔型 | boolean | 1个字节 | true,false |
引用数据类型
除了八种基本数据类型,其他全都是引用数据类型,如 String。
数据类型转换
- 自动转换:从小到大,byte-short-char-->int-->long-->float-->double
- 强制转换:从大到小,浮点到整数可能会发生精度损失,int 转 short 可能会发生数据溢出(丢失)
格式:数据类型 变量名 = (数据类型) 被转数据值;
ASCII 编码表
字符 | 数值 |
---|---|
0 | 48 |
A | 65 |
a | 97 |
在 char 类型和 int 类型计算的过程中,char 类型的字符先查询编码表,得到 97,再和 1 求和,结果为 98。char 类型提升为了 int 类型。char 类型内存 2 个字节,int 类型内存 4 个字节。
重载和重写
- 重载:方法名相同,参数不同,返回类型可以相同也可以不同,方法体不同。一般用在构造器方法上,JVM 会根据传入参数的不同,选择不同的构造方法。
- 重写:方法名,参数,返回类型相同,方法体不同。一般用在子类对父类方法的重写,定义子类自己的行为。
Java 内存区域
JDK1.8 之后:图片丢失
类和对象
- 类:一类事物的属性和行为的集合,抽象的。
- 对象:一类事物的具体体现,类的一个实例,具体的。
类是对象的模板,对象是类的实体。
成员变量的默认值
数据类型 | 默认值 | |
---|---|---|
基本类型 | 整数 | 0 |
浮点 | 0.0 | |
字符 | '\u0000' | |
布尔类型 | false | |
引用类型 | 数组、类、接口 | null |
面向对象三大基本特征
- 封装:将类的属性和方法隐藏起来,提供一个安全的接口供外界调用,减少耦合。缺点是重复代码,要写很多 getter 和 setter。
- 继承:子类继承父类的属性和方法,减少代码重复,提高可维护性,程序更加有层次,缺点是增加了耦合。
- 多态:同一个接口或者类,使用不同的实例完成不同的操作。减少耦合,提高可维护性。三个必要条件:继承,重写,父类引用指向子类对象。缺点是无法调用子类特有的方法。
Scanner 和 Random
//Scanner
Scanner sc = new Scanner(System.in);
System.out.println("请输入第一个数据:");
int a = sc.nextInt();
//Random
Random r = new Random();
int i = r.nextInt();
Java 集合
- List:顺序结构,有序可以重复
- ArrayList:底层是 Object 数组,可以根据 index 快速访问
- LinkedList:底层是双向链表,无法快速访问
- Set:不允许重复
- HashSet:无序唯一,底层基于 HashMap,存储Key
- LinkedHashSet:底层基于 LinkedHashMap
- Map:键值对 key&value 形式,key 无法重复,value 可以
- HashMap:底层是数组+链表,链表用于存储哈希冲突产生的多个 value,JDK1.8 之后链表换成了红黑树,查询效率更高
- LinkedHashMap:在 HashMap 基础上增加了一条双向链表,来保证存取顺序一定
HashSet 检查重复:先计算对象的 hashcode 值,与集合中的其他对象的 hashcode 值比较,如果没有相同的则存储。如果有相同的,则使用 equals() 方法判断是否相同。而 hashcode 并不完美,两个不同的对象,有概率 hashcode 值也相同,所以在需要使用 HashSet 存储对象时,一定要重写 hashcode 和 equals。
== 与 equals
- 没有重写 equals() 方法,则两者相同:对基本类型比较值,对引用类型比较内存地址。
- 有重写 equals() 方法,则 equals() 比较的是对象的内容是否相等。
String 类
常用方法:equals,length,charAt,indexOf,toCharArray,replace,split
字符串类,一旦被创建赋值之后,无法改变,只能将变量指向其他字符串的内存地址。
static 关键字
可以修饰成员变量和成员方法,被修饰之后的变量和方法属于类,建议直接用类名调用。
静态代码块:在类加载之后只执行一次。
Arrays 类
常用方法:toString,sort,asList
Math 类
常用方法:abs(返回 double 的绝对值),ceil(返回大于等于参数的最小整数),floor(返回小于等于参数最大的整数),round(返回最接近参数的 long,相当于四舍五入)
final 关键字
可以修饰类、方法和变量
- 类:不能被继承
- 方法:不能被重写
- 变量:基础数据类型不能被重新赋值,引用类型不能再指向另一个对象
权限修饰符
public | protected | default(空) | private | |
---|---|---|---|---|
同类中 | ✔ | ✔ | ✔ | ✔ |
同一包中 | ✔ | ✔ | ✔ | |
不同包的子类 | ✔ | ✔ | ||
不同包中的无关类 | ✔ |
public 任意访问,private 只有同类能访问,default 只能在同包中访问,protected 在不同包子类中也能访问。
匿名内部类
本质是一个带具体实现父类或父接口的匿名的子类对象。
new 父类名或者接口名(){
// 方法重写
@Override
public void method() {
// 执行语句
}
};
Object 类
Java 中所有类的父类
常用方法:toString,equals
Objects 是一个工具类,其中的 equals 方法直接传递两个参数,可以防止空指针异常。
Date 类
无参构造自动设置系统当前时间的毫秒时刻(距 1970年1月1日00:00:00 GMT),有参构造传递一个 long 值可以自定义时刻。
常用方法:getTime(把日期对象转换成时间毫秒值)
DateFormat 类
抽象类,可使用子类 SimpleDateFormat,构造方法传递一个日期格式。
标识字母(区分大小写) | 含义 |
---|---|
y | 年 |
M | 月 |
d | 日 |
H | 时 |
m | 分 |
s | 秒 |
常用方法:format(将 Date 对象格式化为字符串),parse(将字符串解析为 Date 对象)
System 类
常用方法:currentTimeMillis(获取当前时间到 1970年1月1日00:00:00 GMT 的毫秒值),arraycopy(将数组中指定的数据拷贝到另一个数组中)
StringBuilder 类
为了解决 String 类无法修改造成的效率问题。
无参构造创建一个空的 StringBuilder 容器,有参构造创建一个StringBuilder 容器,传递一个字符串进去。
常用方法:append(在循环中效率比 + 更高),toString(将 StringBuilder 对象转换成 String 对象)
包装类
基本类型效率高,但是功能少,Java 提供了基本类型对应的包装类,实现更多功能。
基本类型 | 对应的包装类(位于java.lang包中) |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
char | Character |
boolean | Boolean |
- 装箱:从基本类型转换成对应的包装类对象
- 拆箱:从包装类对象转换成对应的基本类型
JDK1.5 开始,装箱和拆箱可以自动完成。
基本类型和字符串之间的转换
基本类型转换成字符串直接拼接:+""
字符串转换成对应的基本类型使用对应包装类的 praseXxx 方法
Collection 集合
所有单列集合(List 和 Set)的根接口
常用方法:add,remove,contains,isEmpty,size
Iterator 接口
构造方法获取一个集合对应的迭代器
常用方法:next(返回下一个元素),hasNext(返回是否还有元素)
增强 for 的原理是迭代器
for(元素的数据类型 变量 : Collection集合or数组){
//写操作代码
}
泛型
集合里可以存储不同类型的对象,但是没必要,一般都是同类型的。如果传递了不同的类型,取出时会发生强转异常。
可以在类或方法中预支地使用未知的类型,将运行时异常转移到了编译时期。
常见的数据类型
栈,队列,数组,链表,二叉树
Collections 工具类
常用方法:addAll,shuffle(打乱顺序),sort(排序,按指定规则排序)
comparable 和 comparator 的区别
- comparable 接口下有一个 compareTo(Object obj) 方法用来排序。用于在类中定义排序方法。
- comparator 接口下有一个 compare(Object obj1, Object obj2) 方法用来排序。用于在匿名内部类中重写排序方法。
Throwable 类
- Error:严重错误,无法通过修改代码处理的错误
- Exception:表示异常,可以通过修改代码纠正使程序继续运行
常用方法:printStackTrace,getMessage
Exception 分类
编译期异常(如日期格式化异常)和运行时异常(数学异常)
异常处理:
- throw 直接抛出给调用者
- throws 声明异常给调用者不处理
- try...catch 捕获异常
try{
编写可能会出现异常的代码
}catch(异常类型 e){
处理异常的代码
//记录日志/打印异常信息/继续抛出异常
}finally{
最终一定执行的代码
//当在 try 块或 catch 块中遇到 return 语句时,finally 语句块将在方法返回之前被执行。
//以下情况,finally 不会被执行:
//1.在 finally 语句块第一行发生了异常。 因为在其他行,finally 块还是会得到执行
//2.在前面的代码中用了 System.exit(int)已退出程序。 exit 是带参函数 ;若该语句在异常语句之后,finally 会执行
//3.程序所在的线程死亡。
//4.关闭 CPU。
}
可以捕获多个异常,子异常在父异常上方
并发和并行
- 并发:多个事件在同一个时间段发生
- 并行:多个事件在同一时刻发生
线程和进程
- 进程:是指程序的一次执行过程,是系统运行的基本单位。
- 线程:是进程中的一个执行单元,一个进程可以产生多个线程。
线程的几种状态
new,runnable,blocked,waiting,timewaiting,terminated
创建线程的方法
- 定义Runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。
- 创建Runnable实现类的实例,并以此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。
- 调用线程对象的start()方法来启动线程。
Thread 和 Runnable 的区别
实现 Runnable 接口比继承 Thread 类更好:
- 适合多个相同的程序代码的线程去共享同一个资源。
- 可以避免java中的单继承的局限性。
- 增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程独立。
- 线程池只能放入实现Runable或Callable类线程,不能直接放入继承Thread的类。
为什么我们调用 start() 方法时会执行 run() 方法,为什么我们不能直接调用 run() 方法?
调用 start 方法方可启动线程并使线程进入就绪状态,而 run 方法只是 thread 的一个普通方法调用,还是在主线程里执行。
synchronized 关键字
保证被它修饰过的方法或者代码块在同一时刻只有一个线程执行
synchronized(同步锁){
需要同步操作的代码
}
//锁对象可以是任意类型,多个线程对象要使用同一把锁
public synchronized void method(){
可能会产生线程安全问题的代码
}
//当有执行该方法的线程时,其他线程无法使用此方法
说说 sleep() 方法和 wait() 方法区别和共同点?
- sleep 方法不会释放锁,wait 方法会释放锁
- 除非使用 wait(long timeout) 方法传递一个时间,否则 wait() 无法自动苏醒,需要其他线程调用 notify
- wait 用于线程间通信,sleep 用于暂停执行
- 都可以暂停线程的执行
使用线程池的步骤
- 创建线程池
- 创建 Runnable 接口子类对象
- 提交 Runnable 接口子类对象
- 关闭线程池
File 类
- 绝对路径:从盘符开始的路径
- 相对路径:相对于项目目录的路径
常用方法:exist,isDirectory,isFile,createNewFile(当且仅当该名称的文件不存在时,创建一个新的空文件),delete,mkdir(创建此File表示的目录),mkdir(创建此File表示的目录以及任何必需但不存在的父目录),list(返回一个String数组,表示该File目录中所有的子文件或目录),listFiles(返回一个File数组,表示该File目录中所有的子文件或目录)
递归
指在当前方法内调用自己的这种现象
- 直接递归自身调用自身
- 间接递归可以A方法调用B方法,B方法调用C方法,C方法调用A方法
递归一定要有限定条件,保证递归能够停下,并且递归次数不能过多,否则会发生栈内存溢出。构造方法禁止递归。
public class DiGuiDemo {
//计算n的阶乘,使用递归完成
public static void main(String[] args) {
int n = 3;
// 调用求阶乘的方法
int value = getValue(n);
// 输出结果
System.out.println("阶乘为:"+ value); }
/*
通过递归算法实现.
参数列表:int
返回值类型: int
*/
public static int getValue(int n) {
// 1的阶乘为1
if (n == 1) {
return 1;
}
/*
n不为1时,方法返回 n! = n*(n‐1)!
递归调用getValue方法
*/
return n * getValue(n ‐ 1);
}
}
public class DiGuiDemo3 {
public static void main(String[] args) {
// 创建File对象
File dir = new File("D:\\aaa");
// 调用打印目录方法
printDir(dir);
}
public static void printDir(File dir) {
// 获取子文件和目录
File[] files = dir.listFiles();
// 循环打印
for (File file : files) {
if (file.isFile()) {
// 是文件,判断文件名并输出文件绝对路径
if (file.getName().endsWith(".java")) {
System.out.println("文件名:" + file.getAbsolutePath());
}
} else {
// 是目录,继续遍历,形成递归
printDir(file);
}
}
}
}
文件过滤器
public class DiGuiDemo4 {
public static void main(String[] args) {
File dir = new File("D:\\aaa");
printDir2(dir);
}
public static void printDir2(File dir) {
// 匿名内部类方式,创建过滤器子类对象
File[] files = dir.listFiles(new FileFilter() {
@Override
public boolean accept(File pathname) {
return pathname.getName().endsWith(".java")||pathname.isDirectory();
}
});
// 循环打印
for (File file : files) {
if (file.isFile()) {
System.out.println("文件名:" + file.getAbsolutePath());
} else {
printDir2(file);
}
}
}
}
字节流
字节输出流 OutputStream
常用方法:close(完成操作之后必须执行),flush,write
文件输出流 FileOutputStream
构造方法传递 File 对象或者 String 路径创建文件输出流,加一个 true 可追加数据
Windows 换行符号:\r\n
Unix 换行符号:\n
字节输入流 InputStream
常用方法:close(完成操作之后必须执行),read(读取字节时会自动提升为int类型)
文件输入流 FileInputStream
构造方法传递 File 对象或者 String 路径创建文件输入流
字符流
中文字符占用多个字节存储,所以使用字符流处理文本文件更方便。使用系统默认的字符编码和默认字节缓冲区。字符流无法处理图片视频等非文本文件。
FileWriter 与 FileOutputStream 不同, 如果不关闭,数据只是保存到缓冲区,并未保存到文件。
在 close 执行之后,流对象无法继续使用写出别的内容,所以必须在 close 之前执行 flush 方法。
在实际开发中,IO 异常一定要处理。
缓冲流
对四个基本流的增强,在创建流对象时,创建一个内置的默认大小的缓冲区数组,通过缓冲区读写,减少系统 IO 次数,提高读写效率。
字节缓冲流:BufferedInputStream,BufferedOutputStream
// 创建字节缓冲输入流
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("bis.txt"));
// 创建字节缓冲输出流
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("bos.txt"));
读取时 read 传递一个 byte 数组,效率更高。
字符缓冲流:BufferedReader,BufferedWriter
// 创建字符缓冲输入流
BufferedReader br = new BufferedReader(new FileReader("br.txt"));
// 创建字符缓冲输出流
BufferedWriter bw = new BufferedWriter(new FileWriter("bw.txt"));
特有方法:readLine(读取一行文字),newLine(写一行分隔符)
转换流
是字节与字符间的桥梁
InputStreamReader 类:Reader 的子类,读取字节解码为字符。
InputStreamReader isr = new InputStreamReader(new FileInputStream("in.txt")); InputStreamReader isr2 = new InputStreamReader(new FileInputStream("in.txt") , "GBK");
OutputStreamWriter 类:Writer 的子类,读取字符编码为字节。
OutputStreamWriter isr = new OutputStreamWriter(new FileOutputStream("out.txt")); OutputStreamWriter isr2 = new OutputStreamWriter(new FileOutputStream("out.txt") , "GBK");
序列化
对象序列化,用一个字节序列表示一个对象,字节序列中存储对象的数据,类型和属性等信息,可以写出到文件,持久化存储。反之,还可以从文件中读取字节序列,重构对象,称为反序列化。
ObjectOutputStream 类
FileOutputStream fileOut = new FileOutputStream("employee.txt"); ObjectOutputStream out = new ObjectOutputStream(fileOut);
一个对象想要序列化,必须实现 Serializable 接口,对象内部的所有属性必须可序列化,如果有某个属性不需要序列化,可使用 transient 关键字修饰。
常用方法: writeObject(将指定的对象写出)
ObjectInputStream 类:
FileInputStream fileIn = new FileInputStream("employee.txt"); ObjectInputStream in = new ObjectInputStream(fileIn);
常用方法:readObject
- 如果能找到类对应的 class 文件,则反序列化成功,如果没有,则会抛出 ClassNotFoundException 异常。
- 如果找到的 class 文件在序列化对象之后发生了修改,则反序列化失败,抛出 InvalidClassException 异常。
Serializable 提供了序列版本号的功能,验证序列化对象和类的版本是否一致。
private static final long serialVersionUID = 1L;
//指定1L,即使修改过类,反序列化也不会抛异常报错
TCP/IP 协议
应用层,传输层,网络层,数据链路层,物理层
TCP 面向连接,三次握手:
- 客户端向服务器发出请求,等待服务器确认
- 服务器向客户端发回一个相应,通知客户端已经收到了请求
- 客户端再次向服务器端发送确认信息,确认连接
UDP 面向无连接,直接传输不需要建立连接
网络编程三要素:协议,IP 地址,端口号
Socket 类
Socket:该类实现客户端套接字,套接字指的是两台设备之间通讯的端点。
构造方法
Socket client = new Socket("127.0.0.1", 6666);
常用方法:getInputStream(返回此套接字的输入流),getOutputStream,close,shutdownOutput(禁用此套接字的输出流)
ServerSocket 类:这个类实现了服务器套接字,该对象等待通过网络的请求。
ServerSocket server = new ServerSocket(6666);
常用方法:accept(侦听并接受连接,返回一个新的 Socket 对象,用于和客户端实现通信)
注解
注解是给计算机看的,注释是给程序员看的。也叫元数据,一种代码级别的说明。
反射
将类的各个组成部分封装为其他对象。
可以在程序运行过程中操作这些对象,可以解耦,提高程序可扩展性。
获取 class 对象的方式:
- Class.forName("全类名"),用于配置文件
- 类名.class,用于参数传递
- 对象.getClass(),用于对象获取字节码
同一个字节码文件在一次程序运行过程中只会被加载一次。
class 对象功能:获取成员变量 Field,获取构造方法 Constructor,获取成员方法 Method,获取全类名
如何解决无法使用最新的 springboot--2.2.6.RELEASE 下载 dependencies
到 maven 的 settings.xml 里注释掉 阿里云的 镜像, 让 maven 从官方库下载,就没问题了
@RestController 写在类上,导致类中的方法全部返回了 json 数据,无法通过视图解析器跳转到页面
@RestController 是包括 @Controller 和 @ResponseBody 的组合注解
@Repository 和 @Mapper 注解的区别
@Mapper 注解是 Mybatis 的注解,是用来说明这个是一个 Mapper,对应的 xxxMapper.xml 就是来实现这个 Mapper。然后在 service 层使用 @Autowired 注解注入。 @Repository 注解是 Spring 的注解,使用该注解把当前类注册成一个 bean,再使用 @Autowired注入。 接口上的 @Mapper 也是可以去掉的,但是要在启动类上加上 @MapperScan(value = {"cn.yltang.mapper"})。这句话的意思是在指定位置扫描 Mapper 类。
前置通知
切入点方法之前执行
MVC 设计模式
model模型层,封装数据,实体类;view视图层,jsp,html,展示数据;controller控制层,处理程序逻辑
SpringMVC 中返回 String 时 return 的写法
- return "main",返回视图。
- 需要携带request用forward,不需要则redirect。
- 相对路径不带/,绝对路径带/。
#{} 与 ${} 的区别
#{}
表示一个占位符号,通过#{}
可以实现 preparedStatement 向占位符中设置值,自动进行 java 类型和 jdbc 类型转换, #{}
可以有效防止 sql 注入。 #{}
可以接收简单类型值或 pojo 属性值。 如果 parameterType 传输单个简单类型值,#{}
括号中可以是 value 或其它名称。
${}
表示拼接 sql 串,通过${}
可以将 parameterType 传入的内容拼接在 sql 中且不进行 jdbc 类型转换, ${}
可以接收简单类型值或 pojo 属性值,如果 parameterType 传输单个简单类型值,${}
括号中只能是 value。
MyBatis 设计模式
工厂模式 SqlSessionFactory,代理模式 MapperProxyFactory,构建者模式 SqlSessionFactoryBuilder
MyBatis 自动提交事务的设置
@Test
public void testSaveUser() throws Exception {
User user = new User();
user.setUsername("mybatis user09");
//6.执行操作
int res = userDao.saveUser(user);
System.out.println(res);
System.out.println(user.getId());
}
@Before//在测试方法执行之前执行
public void init()throws Exception {
//1.读取配置文件
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.创建构建者对象
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
//3.创建 SqlSession 工厂对象
factory = builder.build(in);
//4.创建 SqlSession 对象
session = factory.openSession(); //传递一个 true 打开自动提交事务
//5.创建 Dao 的代理对象
userDao = session.getMapper(IUserDao.class);
}
@After//在测试方法执行完成之后执行
public void destroy() throws Exception{
//7.提交事务
session.commit(); //自动提交事务则此行不需要
//8.释放资源
session.close();
in.close();
}
Mybatis 延迟加载
就是在需要用到数据时才进行加载,不需要用到数据时就不加载数据。延迟加载也称懒加载
好处:先从单表查询,需要时再从关联表去关联查询,大大提高数据库性能,因为查询单表要比关联查询多张表速 度要快。
坏处:因为只有当需要用到数据时,才会进行数据库查询,这样在大批量数据查询时,因为查询工作也要消耗 时间,所以可能造成用户等待时间变长,造成用户体验下降。
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
Mybatis 缓存
- 一级缓存:是 SqlSession 级别的缓存,只要 SqlSession 没有 flush 或 close,它就存在。
- 二级缓存:是 mapper 映射级别的缓存,多个 SqlSession 去操作同一个 Mapper 映射的 sql 语句,多个 SqlSession 可以共用二级缓存,二级缓存是跨 SqlSession 的。 需要开启。
<settings>
<!-- 开启二级缓存的支持 -->
<setting name="cacheEnabled" value="true"/>
</settings>
Mybatis 接口绑定
与映射文件名相同,基于动态代理来生成对象执行sql
Mybatis 和 hebernate
h是通过实体映射模型全自动,m是写sql半自动
Mybatis 核心对象
SqlSessionFactory
MySQL5.7 之后修改默认密码
ALTER USER 'root'@'localhost' IDENTIFIED BY 'root密码';
增删改
insert into 表名(列名1,列名2,...列名n) values(值1,值2,...值n);
delete from 表名 [where 条件]
update 表名 set 列名1 = 值1, 列名2 = 值2,... [where 条件];
查询
select
字段列表
from
表名列表
where
条件列表
group by
分组字段
having
分组之后的条件
order by
排序
limit
分页限定
多个字段查询
select 字段名1,字段名2... from 表名;
去除重复
select distinct 字段名1,字段名2... from 表名;
where 条件
* > 、< 、<= 、>= 、= 、<>
* BETWEEN...AND
* IN( 集合)
* LIKE:模糊查询
* 占位符:
* _:单个任意字符
* %:多个任意字符
* IS NULL
* and 或 &&
* or 或 ||
* not 或 !
-- 查询年龄大于等于20 小于等于30
SELECT * FROM student WHERE age >= 20 && age <=30;
SELECT * FROM student WHERE age >= 20 AND age <=30;
SELECT * FROM student WHERE age BETWEEN 20 AND 30;
-- 查询年龄22岁,18岁,25岁的信息
SELECT * FROM student WHERE age = 22 OR age = 18 OR age = 25
SELECT * FROM student WHERE age IN (22,18,25);
-- 查询英语成绩为null
SELECT * FROM student WHERE english IS NULL;
-- 查询英语成绩不为null
SELECT * FROM student WHERE english IS NOT NULL;
-- 查询姓马的有哪些? like
SELECT * FROM student WHERE NAME LIKE '马%';
-- 查询姓名第二个字是化的人
SELECT * FROM student WHERE NAME LIKE "_化%";
-- 查询姓名是3个字的人
SELECT * FROM student WHERE NAME LIKE '___';
-- 查询姓名中包含德的人
SELECT * FROM student WHERE NAME LIKE '%德%';
排序分组
-- 排序 ASC:升序,默认的。DESC:降序。
order by 排序字段1 排序方式1 , 排序字段2 排序方式2...
-- 聚合函数
1. count:计算个数
1. 一般选择非空的列:主键
2. count(*)
2. max:计算最大值
3. min:计算最小值
4. sum:计算和
5. avg:计算平均值
* 注意:聚合函数的计算,排除null值。
解决方案:
1. 选择不包含非空的列进行计算
2. IFNULL函数
-- 分组查询
1. 语法:group by 分组字段;
2. 注意:
1. 分组之后查询的字段:分组字段、聚合函数
2. where 和 having 的区别?
1. where 在分组之前进行限定,如果不满足条件,则不参与分组。having在分组之后进行限定,如果不满足结果,则不会被查询出来
2. where 后不可以跟聚合函数,having可以进行聚合函数的判断。
-- 按照性别分组。分别查询男、女同学的平均分
SELECT sex , AVG(math) FROM student GROUP BY sex;
-- 按照性别分组。分别查询男、女同学的平均分,人数
SELECT sex , AVG(math),COUNT(id) FROM student GROUP BY sex;
-- 按照性别分组。分别查询男、女同学的平均分,人数 要求:分数低于70分的人,不参与分组
SELECT sex , AVG(math),COUNT(id) FROM student WHERE math > 70 GROUP BY sex;
-- 按照性别分组。分别查询男、女同学的平均分,人数 要求:分数低于70分的人,不参与分组,分组之后。人数要大于2个人
SELECT sex , AVG(math),COUNT(id) FROM student WHERE math > 70 GROUP BY sex HAVING COUNT(id) > 2;
SELECT sex , AVG(math),COUNT(id) 人数 FROM student WHERE math > 70 GROUP BY sex HAVING 人数 > 2;
-- 分页查询
1. 语法:limit 开始的索引,每页查询的条数;
2. 公式:开始的索引 = (当前的页码 - 1) * 每页显示的条数
-- 每页显示3条记录
SELECT * FROM student LIMIT 0,3; -- 第1页
SELECT * FROM student LIMIT 3,3; -- 第2页
SELECT * FROM student LIMIT 6,3; -- 第3页
3. limit 是一个MySQL"方言"
约束
* 概念: 对表中的数据进行限定,保证数据的正确性、有效性和完整性。
* 分类:
1. 主键约束:primary key 自增 auto_increment
2. 非空约束:not null
3. 唯一约束:unique
4. 外键约束:foreign key
-- 外键约束
foreign key,让表于表产生关系,从而保证数据的正确性。
1. 在创建表时,可以添加外键
* 语法:
create table 表名(
....
外键列
constraint 外键名称 foreign key (外键列名称) references 主表名称(主表列名称)
);
2. 删除外键
ALTER TABLE 表名 DROP FOREIGN KEY 外键名称;
3. 创建表之后,添加外键
ALTER TABLE 表名 ADD CONSTRAINT 外键名称 FOREIGN KEY (外键字段名称) REFERENCES 主表名称(主表列名称);
4. 级联操作
1. 添加级联操作
语法:ALTER TABLE 表名 ADD CONSTRAINT 外键名称
FOREIGN KEY (外键字段名称) REFERENCES 主表名称(主表列名称) ON UPDATE CASCADE ON DELETE CASCADE ;
2. 分类:
1. 级联更新:ON UPDATE CASCADE
2. 级联删除:ON DELETE CASCADE
多表查询
-- 内连接查询:
1. 从哪些表中查询数据
2. 条件是什么
3. 查询哪些字段
-- 隐式内连接:使用where条件消除无用数据
* 例子:
-- 查询所有员工信息和对应的部门信息
SELECT * FROM emp,dept WHERE emp.`dept_id` = dept.`id`;
-- 查询员工表的名称,性别。部门表的名称
SELECT emp.name,emp.gender,dept.name FROM emp,dept WHERE emp.`dept_id` = dept.`id`;
SELECT
t1.name, -- 员工表的姓名
t1.gender,-- 员工表的性别
t2.name -- 部门表的名称
FROM
emp t1,
dept t2
WHERE
t1.`dept_id` = t2.`id`;
-- 显式内连接:
* 语法: select 字段列表 from 表名1 [inner] join 表名2 on 条件
* 例如:
* SELECT * FROM emp INNER JOIN dept ON emp.`dept_id` = dept.`id`;
* SELECT * FROM emp JOIN dept ON emp.`dept_id` = dept.`id`;
-- 外链接查询:
1. 左外连接:
* 语法:select 字段列表 from 表1 left [outer] join 表2 on 条件;
* 查询的是左表所有数据以及其交集部分。
* 例子:
-- 查询所有员工信息,如果员工有部门,则查询部门名称,没有部门,则不显示部门名称
SELECT t1.*,t2.`name` FROM emp t1 LEFT JOIN dept t2 ON t1.`dept_id` = t2.`id`;
2. 右外连接:
* 语法:select 字段列表 from 表1 right [outer] join 表2 on 条件;
* 查询的是右表所有数据以及其交集部分。
* 例子:
SELECT * FROM dept t2 RIGHT JOIN emp t1 ON t1.`dept_id` = t2.`id`;
-- 子查询:
* 概念:查询中嵌套查询,称嵌套查询为子查询。
-- 查询工资最高的员工信息
-- 1 查询最高的工资是多少 9000
SELECT MAX(salary) FROM emp;
-- 2 查询员工信息,并且工资等于9000的
SELECT * FROM emp WHERE emp.`salary` = 9000;
-- 一条sql就完成这个操作。子查询
SELECT * FROM emp WHERE emp.`salary` = (SELECT MAX(salary) FROM emp);
* 子查询不同情况
1. 子查询的结果是单行单列的:
* 子查询可以作为条件,使用运算符去判断。 运算符: > >= < <= =
*
-- 查询员工工资小于平均工资的人
SELECT * FROM emp WHERE emp.salary < (SELECT AVG(salary) FROM emp);
2. 子查询的结果是多行单列的:
* 子查询可以作为条件,使用运算符in来判断
-- 查询'财务部'和'市场部'所有的员工信息
SELECT id FROM dept WHERE NAME = '财务部' OR NAME = '市场部';
SELECT * FROM emp WHERE dept_id = 3 OR dept_id = 2;
-- 子查询
SELECT * FROM emp WHERE dept_id IN (SELECT id FROM dept WHERE NAME = '财务部' OR NAME = '市场部');
3. 子查询的结果是多行多列的:
* 子查询可以作为一张虚拟表参与查询
-- 查询员工入职日期是2011-11-11日之后的员工信息和部门信息
-- 子查询
SELECT * FROM dept t1 ,(SELECT * FROM emp WHERE emp.`join_date` > '2011-11-11') t2
WHERE t1.id = t2.dept_id;
-- 普通内连接
SELECT * FROM emp t1,dept t2 WHERE t1.`dept_id` = t2.`id` AND t1.`join_date` > '2011-11-11'
事务
如果一个包含多个步骤的业务操作,被事务管理,那么这些操作要么同时成功,要么同时失败。
- 开启事务: start transaction;
- 回滚:rollback;
- 提交:commit;
事务提交的两种方式:
自动提交:mysql就是自动提交的,一条DML(增删改)语句会自动提交一次事务
手动提交:Oracle 数据库默认是手动提交事务,需要先开启事务,再提交
事务的四大特征:
- 原子性:是不可分割的最小操作单位,要么同时成功,要么同时失败。
- 持久性:当事务提交或回滚后,数据库会持久化的保存数据。
- 隔离性:多个事务之间。相互独立。
- 一致性:事务操作前后,数据总量不变
隔离级别:
- read uncommitted:读未提交,产生的问题:脏读、不可重复读、幻读
- read committed:读已提交 (Oracle)产生的问题:不可重复读、幻读
- repeatable read:可重复读 (MySQL默认)产生的问题:幻读
- serializable:串行化,可以解决所有的问题
批量将数据库中的某个字段的部分字符串更新
UPDATE T SET images = REPLACE(images,'#','*')
数据表 A 操作三题
name | class | grade |
---|---|---|
张三 | 英语 | 31 |
张三 | 语文 | 49 |
李四 | 数学 | 90 |
王五 | 英语 | 45 |
王五 | 语文 | 57 |
李四 | 化学 | 51 |
王五 | 化学 | 70 |
李四 | 语文 | 81 |
- 使用 MySQL 语法。用一个 sql 语句 查询出每门课都小于 60 分的学生姓名
SELECT
NAME
FROM
A
GROUP BY
NAME
HAVING
max(grade) < 60;
SELECT DISTINCT
NAME
FROM
A
WHERE
NAME NOT IN (
SELECT DISTINCT
NAME
FROM
A
WHERE
grade >= 60
);
- 使用 MySQL 语法。用一个 sql 查出出现次数大于 3 次的人名
SELECT DISTINCT
NAME
FROM
A
WHERE
NAME IN (
SELECT
NAME
FROM
A
GROUP BY
NAME
HAVING
COUNT(NAME) >= 3
);
- 使用 MySQL 语法。用一个 sql 查询以下结果,如果分数 0-59 展示不及格 , 60-79 展示良好,80-100 展示优秀
name | class | level |
---|---|---|
张三 | 英语 | 不及格 |
张三 | 语文 | 不及格 |
李四 | 数学 | 优秀 |
王五 | 英语 | 不及格 |
王五 | 语文 | 不及格 |
李四 | 化学 | 不及格 |
王五 | 化学 | 良好 |
李四 | 语文 | 优秀 |
SELECT name,class,
(
CASE
WHEN grade >= 80 THEN
'优秀'
WHEN grade <= 59 THEN
'不及格'
ELSE
'良好'
END
) as level
FROM
A;
合并两个数据表
select * from T1 union all select * from T2;
输出数据表中相同的字段
select * from T where name in (select name from T group by name having count(*)>1);
在已有的表中添加列(字段)
ALTER TABLE people ADD name VARCHAR(100);
删除数据表 A 中 name 字段为空的数据
delete from A where name is null;
查询科目语文分数大于 90 分的学生列表
SELECT * FROM A where id in(SELECT id from B WHERE score>90 and SUBJECT = "语文");
查询名字为张三的所有科目的成绩
SELECT * FROM B where id in(SELECT id from A WHERE name = "张三";
实体类注解
- 表名默认使用类名,驼峰转下划线(只对大写字母进行处理),如
UserInfo
默认对应的表名为user_info
。 - 表名可以使用
@Table(name = "tableName")
进行指定,对不符合第一条默认规则的可以通过这种方式指定表名. - 字段默认和
@Column
一样,都会作为表字段,表字段默认为Java对象的Field
名字驼峰转下划线形式. - 可以使用
@Column(name = "fieldName")
指定不符合第3条规则的字段名 - 使用
@Transient
注解可以忽略字段,添加该注解的字段不会作为表字段使用.如果你的实体类中包含了不是数据库表中的字段,你需要给这个字段加上@Transient
注解,这样通用Mapper在处理单表操作时就不会将标注的属性当成表字段处理! - 建议一定是有一个
@Id
注解作为主键的字段,可以有多个@Id
注解的字段作为联合主键. - 默认情况下,实体类中如果不存在包含
@Id
注解的字段,所有的字段都会作为主键字段进行使用(这种效率极低). - 实体类可以继承使用,可以参考测试代码中的
tk.mybatis.mapper.model.UserLogin2
类. - 由于基本类型,如int作为实体类字段时会有默认值0,而且无法消除,所以实体类中建议不要使用基本类型.
- 主键回显:@GeneratedValue(strategy = GenerationType.IDENTITY)
常用接口方法
基础接口
Select
接口:SelectMapper
方法:List select(T record);
说明:根据实体中的属性值进行查询,查询条件使用等号
接口:SelectByPrimaryKeyMapper
方法:T selectByPrimaryKey(Object key);
说明:根据主键字段进行查询,方法参数必须包含完整的主键属性,查询条件使用等号
接口:SelectAllMapper
方法:List selectAll();
说明:查询全部结果,select(null)方法能达到同样的效果
接口:SelectOneMapper
方法:T selectOne(T record);
说明:根据实体中的属性进行查询,只能有一个返回值,有多个结果是抛出异常,查询条件使用等号
接口:SelectCountMapper
方法:int selectCount(T record);
说明:根据实体中的属性查询总数,查询条件使用等号
Insert
接口:InsertMapper
方法:int insert(T record);
说明:保存一个实体,null的属性也会保存,不会使用数据库默认值
接口:InsertSelectiveMapper
方法:int insertSelective(T record);
说明:保存一个实体,null的属性不会保存,会使用数据库默认值
Update
接口:UpdateByPrimaryKeyMapper
方法:int updateByPrimaryKey(T record);
说明:根据主键更新实体全部字段,null值会被更新
接口:UpdateByPrimaryKeySelectiveMapper
方法:int updateByPrimaryKeySelective(T record);
说明:根据主键更新属性不为null的值
Delete
接口:DeleteMapper
方法:int delete(T record);
说明:根据实体属性作为条件进行删除,查询条件使用等号
接口:DeleteByPrimaryKeyMapper
方法:int deleteByPrimaryKey(Object key);
说明:根据主键字段进行删除,方法参数必须包含完整的主键属性
base 组合接口
接口:BaseSelectMapper
方法:包含上面Select的4个方法
接口:BaseInsertMapper
方法:包含上面Insert的2个方法
接口:BaseUpdateMapper
方法:包含上面Update的2个方法
接口:BaseDeleteMapper
方法:包含上面Delete的2个方法
CRUD 组合接口
接口:BaseMapper
方法:继承了base组合接口中的4个组合接口,包含完整的CRUD方法
Example 方法
接口:SelectByExampleMapper
方法:List selectByExample(Object example);
说明:根据Example条件进行查询
重点:这个查询支持通过Example
类指定查询列,通过selectProperties
方法指定查询列
接口:SelectCountByExampleMapper
方法:int selectCountByExample(Object example);
说明:根据Example条件进行查询总数
接口:UpdateByExampleMapper
方法:int updateByExample(@Param("record") T record, @Param("example") Object example);
说明:根据Example条件更新实体record
包含的全部属性,null值会被更新
接口:UpdateByExampleSelectiveMapper
方法:int updateByExampleSelective(@Param("record") T record, @Param("example") Object example);
说明:根据Example条件更新实体record
包含的不是null的属性值
接口:DeleteByExampleMapper
方法:int deleteByExample(Object example);
说明:根据Example条件删除数据
Example 组合接口
接口:ExampleMapper
方法:包含上面Example中的5个方法
Condition 方法
Condition方法和Example方法作用完全一样,只是为了避免Example带来的歧义,提供的的Condition方法
接口:SelectByConditionMapper
方法:List selectByCondition(Object condition);
说明:根据Condition条件进行查询
接口:SelectCountByConditionMapper
方法:int selectCountByCondition(Object condition);
说明:根据Condition条件进行查询总数
接口:UpdateByConditionMapper
方法:int updateByCondition(@Param("record") T record, @Param("example") Object condition);
说明:根据Condition条件更新实体record
包含的全部属性,null值会被更新
接口:UpdateByConditionSelectiveMapper
方法:int updateByConditionSelective(@Param("record") T record, @Param("example") Object condition);
说明:根据Condition条件更新实体record
包含的不是null的属性值
接口:DeleteByConditionMapper
方法:int deleteByCondition(Object condition);
说明:根据Condition条件删除数据
Condition 组合接口
接口:ConditionMapper
方法:包含上面Condition中的5个方法
RowBounds
默认为内存分页,可以配合PageHelper实现物理分页
接口:SelectRowBoundsMapper
方法:List selectByRowBounds(T record, RowBounds rowBounds);
说明:根据实体属性和RowBounds进行分页查询
接口:SelectByExampleRowBoundsMapper
方法:List selectByExampleAndRowBounds(Object example, RowBounds rowBounds);
说明:根据example条件和RowBounds进行分页查询
接口:SelectByConditionRowBoundsMapper
方法:List selectByConditionAndRowBounds(Object condition, RowBounds rowBounds);
说明:根据example条件和RowBounds进行分页查询,该方法和selectByExampleAndRowBounds完全一样,只是名字改成了Condition
RowBounds 组合接口
接口:RowBoundsMapper
方法:包含上面RowBounds中的前两个方法,不包含selectByConditionAndRowBounds
special 特殊接口
这些接口针对部分数据库设计,不是所有数据库都支持
接口:InsertListMapper
方法:int insertList(List recordList);
说明:批量插入,支持批量插入的数据库可以使用,例如MySQL,H2等,另外该接口限制实体包含id
属性并且必须为自增列
接口:InsertUseGeneratedKeysMapper
方法:int insertUseGeneratedKeys(T record);
说明:插入数据,限制为实体包含id
属性并且必须为自增列,实体配置的主键策略无效
MySQL 专用
接口:MySqlMapper
继承方法:int insertList(List recordList);
继承方法:int insertUseGeneratedKeys(T record);
说明:该接口不包含方法,继承了special中的InsertListMapper
和InsertUseGeneratedKeysMapper
Jsp 四个作用域九个内置对象
名称 | 作用域 |
---|---|
application | 在所有应用程序中有效 |
session | 在当前会话中有效 |
request | 在当前请求中有效 |
page | 在当前页面有效 |
out,request,response,session,application,pageContext,page,config,exception
常用快捷键
快捷键 | 功能 |
---|---|
Alt+Enter | 导入包,自动修正代码 |
Ctrl+Y | 删除光标所在行 |
Ctrl+D | 复制光标所在行的内容,插入光标位置下面 |
Ctrl+Alt+L | 格式化代码 |
Ctrl+/ | 单行注释 |
Ctrl+Shift+/ | 选中代码注释,多行注释,再按取消注释 |
Alt+Ins | 自动生成代码,toString,get,set等方法 |
Alt+Shift+上下箭头 | 移动当前代码行 |
IDEA 将包展开
右键项目目录左边竖向的Project
按钮,取消勾选Compact Middle Packages
。
IDEA 设置项目文件夹类型
在任意需要更改类型的文件夹上右键选择
Make Directory as
。File
-Project Structure
-Modules
。
IDEA设置背景图片
双击shift搜索
输入image
Cannot access org.springframework.context.ConfigurableApplicationContext
新导入项目,提示报 Cannot access org.springframework.context.ConfigurableApplicationContext的错,百度一番,需删除导入项目自动生成的xxx.iml ,然后刷新下右侧maven tab即可,这里记录一下。
创建版本库
在需要创建版本库的目录下执行:
git init
会创建一个隐藏的.git
目录。
Git 工作流程
- 从远程仓库克隆项目至本地:
git clone https://github.com/luhexyz/site-Blog.git
- 在本地仓库中
checkout
代码并修改; - 将代码提交到暂存区:
git add .
- 提交修改到本地仓库:
git commit -m "message"
- 将改动推送到远程仓库:
git push
- 从远程仓库拉取并合并代码:
git pull
Git 设置代理
Windows 下的命令行 CMD 设置代理:
set https_proxy=http://127.0.0.1:1080
在 Git 里设置代理:
git config --global https.proxy https://127.0.0.1:1080
取消代理:
git config --global --unset https.proxy
macOS 下的命令设置代理:
export https_proxy=socks5://127.0.0.1:7891
添加子模块
git submodule add <url> <path>
此时子模块文件夹里是空的,在项目根目录执行:
git submodule initgit submodule update
或者:
git submodule update --init --recursive
即可下载子模块代码。
若在整个项目clone
时添加--recursive
,也可以下载子模块代码:
git clone --recursive https://github.com/luhexyz/site-Blog.git
删除子模块
Kepler
为子模块名。
git rm --cached Kepler
rm -rf Kepler
删除.gitmodules
文件中的相关模块信息:
[submodule "Kepler"]
path = Kepler
url = https://github.com/AlanDecode/Maverick-Theme-Kepler.git
删除.git/config
中的相关子模块信息:
[submodule "Kepler"]
url = https://github.com/AlanDecode/Maverick-Theme-Kepler.git
删除.git
文件夹中的相关子模块文件:
rm -rf .git/modules/Kepler