JRE 和 JDK

  • JRE:Java 程序的运行时环境,包含 JVM 和运行时所需要的核心类库 。
  • JDK:Java 程序开发工具包,包含 JRE 和开发人员使用的工具。

编译和运行

  • 编译:指将编写的 Java 源文件翻译成 JVM 能认识的 class 字节码文件,javac 编译器会检查程序中是否有错,有错误就会提示,无错误则编译成功。
  • 运行:指将 class 文件 交给 JVM 运行。

基本数据类型

数据类型关键字内存占用取值范围
字节型byte1个字节-128~127
短整型short2个字节-32768~32767
整型int(默认)4个字节-2的31次方~2的31次方-1
长整型long8个字节-2的63次方~2的63次方-1
单精度浮点数float4个字节-3.4028E+38~3.4028E+38
双精度浮点数double(默认)8个字节-1.7977E+308~1.7977E+308
字符型char2个字节0~65535
布尔型boolean1个字节true,false

引用数据类型

除了八种基本数据类型,其他全都是引用数据类型,如 String。

数据类型转换

  1. 自动转换:从小到大,byte-short-char-->int-->long-->float-->double
  2. 强制转换:从大到小,浮点到整数可能会发生精度损失,int 转 short 可能会发生数据溢出(丢失)
    格式:数据类型 变量名 = (数据类型) 被转数据值;

ASCII 编码表

字符数值
048
A65
a97

在 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 关键字

可以修饰类、方法和变量

  • 类:不能被继承
  • 方法:不能被重写
  • 变量:基础数据类型不能被重新赋值,引用类型不能再指向另一个对象

权限修饰符

publicprotecteddefault(空)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包中)
byteByte
shortShort
intInteger
longLong
floatFloat
doubleDouble
charCharacter
booleanBoolean
  • 装箱:从基本类型转换成对应的包装类对象
  • 拆箱:从包装类对象转换成对应的基本类型

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 操作三题

nameclassgrade
张三英语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 展示优秀
nameclasslevel
张三英语不及格
张三语文不及格
李四数学优秀
王五英语不及格
王五语文不及格
李四化学不及格
王五化学良好
李四语文优秀
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
最后修改:2021 年 12 月 05 日
如果觉得我的文章对你有用,请随意赞赏