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。

数据类型转换

  1. 自动转换:从小到大,byte-short-char-->int-->long-->float-->double
  2. 强制转换:从大到小,浮点到整数可能会发生精度损失,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

创建线程的方法

  1. 定义Runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。
  2. 创建Runnable实现类的实例,并以此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。
  3. 调用线程对象的start()方法来启动线程。

Thread 和 Runnable 的区别

实现 Runnable 接口比继承 Thread 类更好:

  1. 适合多个相同的程序代码的线程去共享同一个资源。
  2. 可以避免java中的单继承的局限性。
  3. 增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程独立。
  4. 线程池只能放入实现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 用于暂停执行
  • 都可以暂停线程的执行

使用线程池的步骤

  1. 创建线程池
  2. 创建 Runnable 接口子类对象
  3. 提交 Runnable 接口子类对象
  4. 关闭线程池

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 数据库默认是手动提交事务,需要先开启事务,再提交

事务的四大特征:

  1. 原子性:是不可分割的最小操作单位,要么同时成功,要么同时失败。
  2. 持久性:当事务提交或回滚后,数据库会持久化的保存数据。
  3. 隔离性:多个事务之间。相互独立。
  4. 一致性:事务操作前后,数据总量不变

隔离级别:

  1. read uncommitted:读未提交,产生的问题:脏读、不可重复读、幻读
  2. read committed:读已提交 (Oracle)产生的问题:不可重复读、幻读
  3. repeatable read:可重复读 (MySQL默认)产生的问题:幻读
  4. serializable:串行化,可以解决所有的问题

批量将数据库中的某个字段的部分字符串更新

UPDATE T SET images = REPLACE(images,'#','*')

数据表 A 操作三题

name class grade
张三 英语 31
张三 语文 49
李四 数学 90
王五 英语 45
王五 语文 57
李四 化学 51
王五 化学 70
李四 语文 81
  1. 使用 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
    );
  1. 使用 MySQL 语法。用一个 sql 查出出现次数大于 3 次的人名
SELECT DISTINCT
    NAME
FROM
    A
WHERE
    NAME IN (
    	SELECT
    		NAME
    	FROM
    		A
    	GROUP BY
    		NAME
    	HAVING
    		COUNT(NAME) >= 3
    );
  1. 使用 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 = "张三";

实体类注解

  1. 表名默认使用类名,驼峰转下划线(只对大写字母进行处理),如UserInfo默认对应的表名为user_info
  2. 表名可以使用@Table(name = "tableName")进行指定,对不符合第一条默认规则的可以通过这种方式指定表名.
  3. 字段默认和@Column一样,都会作为表字段,表字段默认为Java对象的Field名字驼峰转下划线形式.
  4. 可以使用@Column(name = "fieldName")指定不符合第3条规则的字段名
  5. 使用@Transient注解可以忽略字段,添加该注解的字段不会作为表字段使用.如果你的实体类中包含了不是数据库表中的字段,你需要给这个字段加上@Transient注解,这样通用Mapper在处理单表操作时就不会将标注的属性当成表字段处理!
  6. 建议一定是有一个@Id注解作为主键的字段,可以有多个@Id注解的字段作为联合主键.
  7. 默认情况下,实体类中如果不存在包含@Id注解的字段,所有的字段都会作为主键字段进行使用(这种效率极低).
  8. 实体类可以继承使用,可以参考测试代码中的tk.mybatis.mapper.model.UserLogin2类.
  9. 由于基本类型,如int作为实体类字段时会有默认值0,而且无法消除,所以实体类中建议不要使用基本类型.
  10. 主键回显:@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中的InsertListMapperInsertUseGeneratedKeysMapper

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 工作流程

  1. 从远程仓库克隆项目至本地:
git clone https://github.com/luhexyz/site-Blog.git
  1. 在本地仓库中checkout代码并修改;
  2. 将代码提交到暂存区:
git add .
  1. 提交修改到本地仓库:
git commit -m "message"
  1. 将改动推送到远程仓库:
git push
  1. 从远程仓库拉取并合并代码:
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