Java
Java
Java 概述
环境介绍
- JVM(java virtual machine)
- 是一个虚拟的计算机,具有指令集并使用不同的存储区域。负责执行指令、管理数据、内存、寄存器,包含在 JDK 中。
- 对于不同的平台,有不同的虚拟机。
- Java 虚拟机机制屏蔽了底层运行平台的差别,实现了“一次编译,到处运行”。
- JDK(java development kit, java 开发工具包)
- JDK = JRE + java 开发工具(java, javac, javadoc, javap 等)
- JDK 是提供给 java 开发人员使用的,其中包含了 java 的开发工具,也包括了 JRE。
- JRE(java runtime environment, java 运行时环境)
- JRE = JVM + java 核心类库
- 如果想要运行一个开发好的 java 程序,计算机只需要安装 JRE 即可。
- test.java(源文件) –> 编译 javac –> test.class(字节码文件) –> 运行 java –> JVM
注意事项和细节说明
- java 应用程序的执行入口是 main() 方法,有固定的书写格式: public static void main(String [] args)
- 一个源文件中最多只能包含一个 public 类(其他类的个数不限制, 也可以将 main 方法写在非 public 类中,然后制定运行非 public 类),同时文件名必须按该类名命名。
- 编译后,每一个类都对应一个 .class 文件。
变量
数据类型
- 每一种数据都定义了明确的数据类型,在内存中分配了不同大小的内存空间。
基本数据类型
数值型
整数类型
- java 没有无符号数
- byte(1字节)
- short(2字节)
- int(4字节): java 的整型常量默认是 int 类型,声明 long 类型常量加 l / L。
- long(8字节)
浮点类型
- float(4字节): java 的浮点型常量默认是 double 类型,声明 float 类型常量加 f / F。
- double(4字节)
字符型
- char(2字节)
- 字符类型可以直接存放一个数字(char 的本质是一个整数,在输出时,是 unicode 码对应的字符)
- 是可以用于运算的,相当于一个整数
字符编码表
- ASCII: 一个字节表示
- Unicode: 固定大小的编码,使用两个字节来表示字符,字母和汉字统一都占用两个字节。(兼容 ASCII 码)
- utf-8: 大小可变的编码(可以使用 1-6 个字节表示一个符号),字母使用 1 字节,汉字使用 3 字节。
- gbk: 大小可变的编码,字母使用 1 字节,汉字使用 2 字节。
布尔型
- boolean(1字节): 存放 true / false
- 不可以 0 或非 0 的整数代替 false 和 true,这点和 C 语言不同。
自动类型转换
- 赋值或运算时,精度小的类型自动转换为精度大的数据类型。
- char -> int -> long -> float -> double
- byte -> short -> int -> long -> float -> double
- 有多种类型的数据混合运算时,系统首先自动将所有数据转换成容量最大的那种数据类型,然后再进行计算。
- 把精度大的数据类型赋值给精度小的数据类型时会报错,反之会进行自动类型转换。
- int a = 1.1; // error
- (byte, short) 和 char 之间不会相互自动转换。
- 当把具体数赋给 byte 时,先判断该数是否在 byte 范围内,如果是就可以
- byte b = 1; // yes
- int a = 1; byte b = a; // error
- byte, short, char 三者之间可以运算,在运算时首先转换为 int 类型。(即使只使用 byte 进行运算也会先转为 int)
- byte a = 1; byte b = a + a; // error
- boolean 不参与类型转换。
强制类型转换
- 自动类型转化的逆过程,将容量大的数据类型转换为容量小的数据类型。使用时加上强制转换符 (),可能造成精度降低或溢出。
- int n = (int)1.9;
基本数据类型和 String 类型的转换
- 基本数据类型 -> String: 基本类型的值 + “”
- int a = 100; String s = a + “”;
- String -> 基本数据类型: 通过基本类型的包装类调用 parseXX 方法
- String s = “123”; int n = Integer.parseInt(s);
- 如果格式不正确,就会抛出异常。(编译能过,运行报错)
- String s = “hello”; int n = Integer.parseInt(s);
引用数据类
类(class)
接口(interface)
数组([])
运算符
算术运算符
加法
- 当左右两边都是数值类型时,做加法运算。
- 当左右两边有一方为字符串时,做拼接运算。(从左到右运算)
- “1” + 1 输出 “11”
- 100 + 3 + “hello” 输出 “103hello”
- “hello” + 100 + 3 输出 “helle1003”
除法
- System.out.println(10 / 4); // 2
- System.out.println(10.0 / 4); // 2.5
- System.out.println(10 / 4.0); // 2.5
- double d = 10 / 4; // 2.0
取模
- a % b = a - a / b * b;
- 10 % 3; // 1
- -10 % 3; // -1
- 10 % -3; // 1
- -10 % -3; // -1
赋值运算符
基本赋值运算符
- int a = 10;
复合赋值运算符
- a += b;
- 也会进行类型转换
1
2
3byte a = 1;
a += 2; // 等价于 a = (byte)(a + 2);
// a = a + 2; // 这样写是错的
关系运算符(比较运算符)
- ==, !=, <, >, <=, >=, instanceof
逻辑运算符
- 用于连接多个条件,最终的结果也是一个 boolean 值。
- &&, ||, !
- &, |, ^
- & 和 && 作用相同,| 和 || 作用相同,只不过 && 和 || 第一个条件能得出结果时,后面的条件不会执行;而 & 和 | 会执行所有条件。
位运算符
- (>>, <<) 算术移位
- (>>>) 逻辑右移(没有逻辑左移)
三元运算符
基本语法
- 条件表达式? 表达式1: 表达式2;
控制结构
顺序控制
分支控制(同 C++)
- switch(表达式)中表达式的返回值必须是: byte, short, int, char, enum, String; case 子句中的值必须是常量或者常量表达式
循环控制
- for: 同 C++
- while: 同 C++
break
continue
return
数组、排序和查找
数组
- 数组可以存放多个同一类型的数据。
- 数组也是一种数据类型,是引用类型。
初始化
动态初始化1
1 | int[] a = new int[3]; |
动态初始化2(先声明后定义)
1 | int[] a; |
静态初始化
1 | int[] a = {1, 2, 3}; |
- java 中数组元素都是在堆中,不论是静态还是动态初始化。
默认值
- int/short/byte/long: 0
- float/double: 0.0
- char: \u0000
- boolean: false
- String: null
赋值
- 数组在默认情况下是引用传递,赋的值是地址,赋值方式为引用传递(是一个地址)
1
2int[] arr1 = {1, 2, 3};
int[] arr2 = arr1; // arr2 的变化会影响 arr1
二维数组
1 | int [][] arr = new int[2][3]; |
排序
查找
面向对象编程(基础)
类与对象
1 | public class Hello{ |
java 内存结构分析
- 栈: 一般存放基本数据类型(局部变量)
- 堆: 存放对象(Cat cat, 数组等)
- 方法区: 常量池(常量,比如字符串),类加载信息
成员方法
1 | class Cat{ |
成员方法传参机制
- 对于基本数据类型,传递的是值(值拷贝),形参的任何改变不影响实参。
- 对于引用数据类型,传递的是地址(传递的也是值,但是值是地址),可以通过形参影响实参。
重载 overload
- 方法名: 必须相同
- 形参列表: 必须不同(形参类型或个数或顺序,至少有一样不同,参数名无要求)
- 返回类型: 无要求
可变传参
- java 允许将同一个类中多个同名同功能但参数个数不同的方法,封装成一个方法。
- 可变参数的实参可以为 0 个或任意多个
- 可变参数的实参可以为数组
- 可变参数的本质就是数组
- 可变参数可以和普通类型的参数一起放在形参列表,但必须保证可变参数在最后
- 一个形参列表中只能有一个可变参数
1
2
3
4
5
6// 1. int... 表示接受的是可变参数,类型是int,即可以接收多个int(0-多)
// 2. 使用可变参数时,可以当做数组来使用,即 nums 可以当作数组
public int sum(int... nums) {
System.out.println("可变参数的长度:" + nums.length);
return 0;
}
作用域
- 全局变量: 也就是属性,作用域为整个类体。(属性在定义时,可以直接赋值)
- 全局变量可以不赋值,直接使用,因为有默认值
- 局部变量必须赋值后,才能使用,因为没有默认值
- 全局变量和局部变量可以重名,使用时遵循就近原则
- 全局变量可以加修饰符
构造器(构造函数)
1 | public class Hello{ |
- 构造器的修饰符可以默认
- 构造器没有返回值
- 方法名和类名必须一样
- 参数列表和成员方法一样的规则
- 构造器的调用由系统完成
构造器使用细节
- 一个类可以定义多个不同的构造器,即构造器重载
- 构造器是完成对象的初始化,并不是创建对象
- 如果没有定义构造器,系统会给类生成一个默认无参构造器
- 一旦定义了构造器,默认的构造器就覆盖了,就不能再使用默认的无参构造器,除非显示的定义一下
对象创建的流程分析
- 加载类信息(方法区,只会加载一次)
- 在堆中分配空间
- 完成对象的初始化
- 3.1 默认初始化
- 3.2 显示初始化
- 3.3 构造器初始化
- 返回对象的地址
this
1 | class Cat{ |
- this 关键字可以用来访问本类的属性、方法、构造器
- this 用于区分当前类的属性和局部变量
- 访问成员方法的语法: this.方法名(参数列表)
- 访问构造器语法: this(参数列表); 只能在构造器中使用(即只能在构造器中访问另一个构造器,且必须放在构造器的第一条语句)
- this 不能在类定义的外部使用,只能在类定义的方法中使用
面向对象编程(中级)
包
- 包的本质实际上就是创建不同的文件夹/目录来保存类文件
- 三大作用
- 区分相同名字的类
- 当类很多时,可以很好地管理类
- 控制访问范围
包基本语法
- package 包名;
- package 的作用是声明当前类所在的包,需要放在类的最上面,一个类最多只能有一句 package
- import 指令放在 package 的下面,在类定义的前面,可以有多句且没有顺序要求
命名
- com.公司名.项目名.业务模块名
常用的包
- java.lang.*: lang 包是基本包,默认引入,不需要再引入
- java.util.*: util 包,系统提供的工具包,工具类。(使用 Scanner)
- java.net.*: 网络包,网络开发
- java.awt.*: 做 java 的界面开发,GUI
引入包
- import java.util.Scanner; // 只会引入 java.util 包下的 Scanner
- import java.util.*; // 将 java.util 包下的所有类都引入
访问修饰符
- 用于控制方法和属性的访问权限
- 只有默认级别和 public 才能修饰类
- public > protected > 默认 > private
public
- 对外公开
protected
- 对子类和同一个包的类公开
默认级别
- 没有修饰符号,向同一个包的类公开
private
- 只有类本身可以访问,不对外公开
封装
- 把抽象出来的数据和对数据的操作封装在一起
继承
1 | class Father { |
- 子类继承了父类所有的属性和方法,非私有的属性和方法可以直接访问,但是私有属性不能在子类直接访问,要通过公共的方法去访问
- 子类必须调用父类的构造器,完成父类的初始化。(super() 默认调用父类的无参构造器)
- 当创建子类时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无参构造器,则必须在子类的构造器中用 super 去指定使用父类的哪个构造器完成对父类的初始化工作,否则,编译不通过
- 如果希望指定去调用父类的某个构造器,则显式的调用一下: super(父类构造器参数列表)
- super 在使用时,必须放在构造器的第一行(super 只能在构造器中使用)
- super() 和 this() 都只能放在构造器的第一行,因此这两个方法不能共存在一个构造器
- java 所有类都是 Object 类的子类,Object 类是所有类的基类
- 父类构造器的调用不限于直接父类,将一直往上追溯直到 Object 类
- 子类最多只能继承一个父类(直接继承),即 java 中是单继承机制
- 不能滥用继承,子类和父类之间必须满足 is-a 的逻辑关系
Super
- 用于访问父类的属性、方法和构造器
- 不能访问父类的私有属性及私有方法
- 当子类中有和父类中的属性和方法重名时,为了访问父类的属性和方法,必须通过 super。如果没有重名,使用 super、this、直接访问是一样的效果
- super 的访问不限于直接父类,如果爷爷类和本类中有同名的成员,也可以使用 super 去访问爷爷类的成员;如果多个基类(上级类)中都有同名成员,使用 super 访问遵循就近原则
override
- 子类有一个方法和父类的某个方法的名称、返回类型、参数都一样,那么就说子类的这个方法覆盖了父类的方法
- 子类的方法的参数、方法名称,要和父类方法的参数、方法名称完全一样
- 子类方法的返回类型和父类方法返回类型一样,或者是父类返回类型的子类
- 子类方法不能缩小父类方法的访问权限,可以扩大父类的访问权限
多态
- 方法或对象具有多种形态,是建立在封装和继承基础之上的。
- 一个的编译类型和运行类型可以不一致。
- 编译类型在定义类型时就确定了,不能改变。
- 运行类型是可以变换的。
- 编译类型看定义时 = 号的左边,运行类型看 = 号的右边。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28public class Hello{
public static void main(String [] args) {
Father fs = new Son(); // fs 编译类型是 Father, 运行类型是 Son
fs.say(); // Son!
fs = new Girl(); // fs 编译类型是 Father, 运行类型是 Gril
fs.say(); // Gril!
}
}
class Father {
public void say() {
System.out.println("Father!");
}
}
class Son extends Father {
public void say() {
System.out.println("Son!");
}
}
class Girl extends Father {
public void say() {
System.out.println("Girl!");
}
}
向上转型
- 父类的引用指向了子类的对象
- 可以调用父类的所有成员(需遵循访问权限),但是不能调用子类的特有成员(因为在编译阶段,能调用哪些成员,是由编译类型决定的),最终的运行效果看子类的具体实现(即调用方法时,按照从子类开始查找方法,然后调用)
向下转型
- 子类类型 引用名 = (子类类型)父类引用;
- 只能强转父类的引用,不能强转父类的对象
- 要求父类的引用必须指向的是当前目标类型的对象
- 当向下转型后,可以调用子类类型中的所有成员
属性重写
- 属性没有重写一说,属性的值看编译类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14public class Hello{
public static void main(String [] args) {
Father fs = new Son(); // fs 编译类型是 Father, 运行类型是 Son
System.out.println(fs.cnt); // 1, 成员变量的访问看编译类型
}
}
class Father {
int cnt = 1;
}
class Son extends Father {
int cnt = 2;
}
instanceof
- 用于判断对象的运行类型是否为 xx 类型或 xx 类型的子类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18public class Hello{
public static void main(String [] args) {
Son s = new Son();
System.out.println(s instanceof Son); // true
System.out.println(s instanceof Father);//true
Father fs = (Father)s;
System.out.println(fs instanceof Father);//true
System.out.println(fs instanceof Father);//true
}
}
class Father {
int cnt = 1;
}
class Son extends Father {
int cnt = 2;
}
动态绑定机制
- 当调用对象方法的时,该方法会和该对象的内存地址 / 运行类型绑定
- 当调用对象属性时,没有动态绑定机制,哪里声明,哪里使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33public class Hello{
public static void main(String [] args) {
A a = new B();
System.out.println(a.sum()); // 40
System.out.println(a.sum1()); // 30
}
}
class A {
public int i = 10;
public int sum() {
return getI() + 10;
}
public int sum1() {
return i + 10;
}
public int getI() {
return i;
}
}
class B extends A {
public int i = 20;
public int sum() {
return i + 20;
}
public int sum1() {
return i + 10;
}
public int getI() {
return i;
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33public class Hello{
public static void main(String [] args) {
A a = new B();
System.out.println(a.sum()); // 30
System.out.println(a.sum1()); // 20
}
}
class A {
public int i = 10;
public int sum() {
return getI() + 10;
}
public int sum1() {
return i + 10;
}
public int getI() {
return i;
}
}
class B extends A {
public int i = 20;
// public int sum() {
// return i + 20;
// }
// public int sum1() {
// return i + 10;
// }
public int getI() {
return i;
}
}
多态参数
- 方法定义的形参类型为父类类型,实参类型允许为子类类型
Object 类详解
== 运算符
- 既可以判断基本类型,也可以判断引用类型
- 判断基本类型,判断的是值是否相等
- 判断引用类型,判断的是地址是否相等,即判断是不是同一个对象
equals 方法
- Object 类中的方法,只能判断引用类型,默认判断的是地址是否相等,子类中往往会重写该方法,用于判断内容是否相等
hashCode 方法
- 提高具有哈希结构的容器的效率
- 两个引用,如果指向同一个对象,则哈希值肯定是一样的
- 两个引用,如果指向的是不同对象,则哈希值是不一样的(有可能出现地址冲突而导致哈希值相同)
- 一般是通过将该对象的内部地址转换为一个整数来实现的
1
2
3
4
5
6
7
8
9
10
11
12public class Hello{
public static void main(String [] args) {
A a1 = new A();
A a2 = new A();
A a3 = a1;
System.out.println(a1.hashCode() == a2.hashCode()); // false
System.out.println(a1.hashCode() == a3.hashCode()); // true
}
}
class A {
}
toString 方法
- 默认返回 全类名 + @ + 哈希值的十六机制
1
2Object o = new Object();
System.out.println(o.toString()); // java.lang.Object@7181ae3f - 重写 toString 方法,打印对象或拼接对象时,都会自动调用该对象的 toString 形式
- 直接输出一个对象时,toString 方法会被默认的调用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16public class Hello{
public static void main(String [] args) {
A a = new A();
System.out.println(a); // A{name='yzy'}
}
}
class A {
String name = "yzy";
public String toString() {
return "A{" +
"name='" + name + '\'' +
'}';
}
}
finalize 方法(析构函数)(deprecated)
- 当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用该方法
- 当对象被回收时,系统自动调用该对象的 finalize 方法。子类可以重写该方法,做一些释放资源的操作
面向对象编程(高级)
类变量和类方法
类变量(静态类属性)
- 同一个类所有对象共享
- 在类加载的时候就生成(没有创建对象实例,也能使用类变量)
- 生命周期随类的加载开始,随着类的消亡而销毁
- 访问: 类名.类变量名 / 对象名.类变量名 (满足访问修饰符的访问权限和范围)
1
2
3
4
5
6
7
8
9
10
11public class Hello{
public static void main(String [] args) {
System.out.println(A.cnt);
A a = new A();
System.out.println(a.cnt);
}
}
class A {
public static int cnt = 0;
}
类方法(静态方法)
- 访问: 类名.类方法名 / 对象名.类方法名 (满足访问修饰符的访问权限和范围)
- 当方法中不涉及到任何和对象相关的成员,则可以设计成类方法
- 和普通方法一样都是随着类的加载而加载,将结构信息存储在方法区
- 类方法中无 this 参数
- 类方法只能访问类变量和类方法
1
2
3
4
5
6
7
8
9
10
11
12
13public class Hello{
public static void main(String [] args) {
A.test();
A a = new A();
a.test();
}
}
class A {
public static void test() {
System.out.println("A.test()");
}
}
理解 main 方法语法
- main 方法是虚拟机调用的
- java 虚拟机需要调用类的 main 方法,所以 main 方法的访问权限必须是 public
- java 虚拟机在执行 main 方法时不必创建对象,所以 main 方法必须是 static
- main 方法接收 String 类型的数组参数,该数组中保存执行 java 命令时传递给所执行的类的参数
- java 执行的程序 参数1 参数2 参数3 …
代码块
- 又称为初始化块,属于类中的成员,类似于方法,将逻辑语句封装在方法体中,通过 {} 包围起来
- 和方法不同,没有方法名,没有返回,没有参数,只有方法体,而且不用通过对象或类显示调用,而是加载类或创建对象时隐式调用
- 基本语法
1
2
3
4
5[修饰符]{
代码
};
修饰符可选,要写的话,只能写 static
使用 static 修饰的叫静态代码块,不修饰的叫普通代码块 - static 代码块,作用就是对类进行初始化,随着类的加载而执行,并且只会执行一次;若是普通代码块,每创建一个对象都执行
- 静态代码块只能调用静态成员(属性和方法),普通代码块可以调用任意成员
- 使用场景: 如果多个构造器中都有重复语句,可以抽取到代码块中,提高代码的重用
- 不管调用哪个构造器创建对象,都会先调用代码块的内容
- 代码块调用顺序优先于构造器
- 构造器的最前面其实隐含了 super() 和普通代码块的调用(先 super 加载父类)
类加载时机
- 创建对象实例时
- 创建子类对象实例时,父类也会被加载
- 使用类的静态成员(属性或方法)时
创建对象时类的调用顺序
- 调用静态代码块和静态属性初始化(这俩初始化优先级是一样的,按定义的顺序调用)
- 调用 super()、普通代码块和普通属性的初始化(这俩优先级也一样)
- 调用构造方法
创建子类对象时类的调用顺序
- 父类的静态代码块和静态属性(优先级一样,按定义顺序执行)
- 子类的静态代码块和静态属性(优先级一样,按定义顺序执行)
- 父类的 super()、普通代码块和普通属性初始化(优先级一样,按定义顺序执行)
- 父类的构造方法
- 子类的 super()、普通代码块和普通属性初始化(优先级一样,按定义顺序执行)
- 子类的构造方法
单例设计模式
步骤
- 构造器私有化
- 类的内部创建对象
- 向外暴露一个静态的公共方法
饿汉式
- 未使用对象也会在类加载时创建
1
2
3
4
5
6
7
8
9class SingleTon {
private static SingleTon tmp = new SingleTon();
private SingleTon() {
}
public static SingleTon getInstance() {
return tmp;
}
}
懒汉式
- 目前懒汉式存在线程安全问题
1
2
3
4
5
6
7
8
9
10
11
12class SingleTon {
private static SingleTon tmp;
private SingleTon() {
}
public static SingleTon getInstance() {
if (tmp == null) {
tmp = new SingleTon();
}
return tmp;
}
}
final 关键字
- 可以修饰类、属性、方法和局部变量
- final 修饰的属性又叫常量,一般用大写命名
- final 修饰的属性在定义时必须赋初值,并且以后不能修改,赋值可以加在如下位置之一
- 定义时
- 在构造器中
1
2
3
4
5
6class A {
final int CNT;
public A() {
CNT = 0;
}
} - 在代码块中
1
2
3
4
5
6class A {
final int CNT;
{
CNT = 0;
}
}
- 如果 final 修饰的属性是静态的,则初始化的位置只能是定义时或者在静态代码块中,不能在构造器中赋值
- final 类不能继承,但是可以实例化
- 如果类不是 final 类,但是含有 final 方法,则该方法虽然不能重写,但是可以被继承
- 如果一个类已经是 final 类了,就没有必要再将方法修饰成 final 方法
- final 不能修饰构造方法
- final 和 static 往往搭配使用,效率更高,这样不会导致类加载(单独使用 static 会加载类)
- 包装类(Integer,Double,Float,Boolean等都是 final 类),String 也是 final 类
使用场景
- 当不希望类被继承时
1
2final class A {}
class B extends A {} // This should cause a compile-time error - 当不希望父类的某个方法被子类覆盖 / 重写时
1
2
3
4
5
6
7class A {
public final void test() {}
}
class B extends A {
public void test() {} // compile error
} - 当不希望类的某个属性被修改时
1
2
3
4
5
6
7
8
9
10public class Hello{
public static void main(String [] args) {
A a = new A();
a.cnt = 1; // compile error
}
}
class A {
public final int CNT = 0;
} - 当不希望某个局部变量被修改时
1
2
3
4
5
6class A {
public void test() {
final int CNT = 0;
cnt = 1; // error
}
}
抽象类
- 当父类的某些方法,需要声明,但是又不确定如何实现时,可以将其声明为抽象方法,那么这个类就是抽象类
- 当一个类中存在抽象方法时,需要将该类声明为 abstract 类
1
2
3abstract class A {
abstract public void test();
} - 抽象类不能被实例化
- 抽象类不一定要包含 abstract 方法,
- 一旦类包含了 abstract 方法,则这个类必须声明为 abstract 类
- abstract 只能修饰类和方法,不能修饰属性和其他的
- 抽象类可以有任意成员
- 抽象方法不能有主体,即不能实现
- 如果一个类继承了抽象类,则该类必须实现抽象类的所有抽象方法,除非该类也声明为 abstract 类
- 抽象方法不能使用 private、final 和 static 来修饰,因为这些关键字都是和重写相违背的
模板设计模式
1 | abstract class Template { |
接口
- 接口就是给出一些没有实现的方法,封装到一起,到某个类要使用的时候,再根据具体情况把这些方法写出来
1
2
3
4
5
6
7
8
9interface 接口名 {
// 属性
// 方法
}
class 类名 implements 接口 {
// 自己属性
// 自己方法
// 必须实现的接口的抽象方法
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22interface Ainterface {
// 属性
public int n = 0;
// 方法
// 在接口中,抽象方法,可以省略abstract关键字
public void test();
// 在 jdk8 之后,接口中允许定义默认方法
default public void test1() {
System.out.println("test1");
}
// 在 jdk8 之后,接口中允许定义静态方法
static public void test2() {
System.out.println("test1");
}
}
class A implements Ainterface {
// 实现接口中的所有抽象方法
public void test() {
System.out.println("实现接口中的抽象方法");
}
} - 接口不能被实例化
- 接口中所有的方法是 public 方法,接口中的抽象方法,可以不用 abstract 修饰
- 一个普通类实现接口,就必须将该接口的所有方法都实现
- 抽象类实现接口,可以不用实现接口的方法
- 一个类同时可以实现多个接口
- 接口中的属性,只能是 final 的,而且是 public static final 修饰的
- 接口中属性的访问形式: 接口名.属性名
- 接口不能继承其他类,但是可以继承多个别的接口
- 接口的修饰符只能是 public 和默认
接口 vs 继承
- 接口是对 java 单继承机制的补充
- 接口比继承更加灵活,继承是满足 is-a 关系,而接口只需满足 like-a 关系
- 接口在一定程度上实现代码解耦(接口规范性 + 动态绑定机制)
接口和继承解决的问题不同
- 继承的价值主要在于: 解决代码的复用性和可维护性
- 接口的价值主要在于: 设计好各种规范,让其他类去实现这些方法
接口多态特性
- 接口引用可以指向实现了接口类的对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33public class Hello{
public static void main(String [] args) {
// 接口类型的变量可以指向实现了接口的对象实例
Ainterface ifa = new A();
Ainterface ifb = new B();
A ifaa = new A();
Test.func(ifa); // A
Test.func(ifb); // B
Test.func(ifaa);// A
}
}
class Test {
public static void func(Ainterface inf) {
inf.test();
}
}
interface Ainterface {
public void test();
}
class A implements Ainterface {
public void test() {
System.out.println("A");
}
}
class B implements Ainterface {
public void test() {
System.out.println("B");
}
} - 接口多态数组
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23public class Hello{
public static void main(String [] args) {
Ainterface infs[] = new Ainterface[2];
infs[0] = new A();
infs[1] = new B();
}
}
interface Ainterface {
public void test();
}
class A implements Ainterface {
public void test() {
System.out.println("A");
}
}
class B implements Ainterface {
public void test() {
System.out.println("B");
}
} - 接口多态传递(接口与接口之间继承)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20public class Hello{
public static void main(String [] args) {
Ainterface a = new A();
Binterface b = new A();
}
}
interface Ainterface {
public void test();
}
interface Binterface extends Ainterface {
public void test();
}
class A implements Binterface {
public void test() {
System.out.println("A");
}
}
内部类
- 一个类的内部又完整的嵌套了另一个类结构
- 内部类最大的特点就是可以直接访问私有属性,并且可以体现类与类之间的包含关系
定义在外部类局部位置上
局部内部类(有类名)
1 | class Outer { |
匿名内部类(没有类名)
- 本质是类,同时还是一个对象(jdk 底层会分配类名)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46interface IA {
public void test();
}
class Father {
public String name;
public Father(String name) {
this.name = name;
}
public void test() {
System.out.println("father test");
}
}
class Outer {
private int x = 10;
public void func() {
// a 的编译类型是 IA,运行类型是匿名内部类
// 原理是编译器在编译时会生成一个类文件,类名为 Outer$1.class
IA a = new IA() {
public void test() {
System.out.println("inner IA test");
}
};
a.test();
// f 编译类型是 Father,运行类型是匿名内部类
// 原理同上,编译器会生成一个类文件,类名为 Outer$2.class
Father f = new Father("yzy") {
public void test() {
System.out.println("inner Father test");
}
};
f.test();
// 也可以不使用变量,直接调用匿名内部类的方法
new Father("yzy") {
public void test() {
System.out.println("inner Father test");
}
}.test();
}
}
定义在外部类的成员位置上
成员内部类(没用 static 修饰)
1 | class Outer { |
- 可以添加任意的访问修饰符(因为是外部类成员)
- 外部其他类也能使用成员内部类
1
2
3
4
5
6class Test {
public void test() {
Outer.Inner i = new Outer().new Inner();
i.test();
}
}
静态内部类(使用 static 修饰)
- 可以直接访问外部类的所有静态成员
- 可以添加任意的访问修饰符
枚举和注解
自定义类实现枚举
1 | class Season { |
enum 关键字实现枚举
- 当使用 enum 关键字开发一个枚举类时,默认会继承 Enum 类
- 如果使用无参创建枚举对象,则实参列表和小括号都可以省略
- 当有多个枚举对象时,使用逗号间隔,最后一个分号结尾
- 枚举对象必须放在枚举类的行首
- 使用了 enum 关键字之后,就不能继承其他类,因为 enum 会隐式继承 Enum,而 java 是单继承机制
- 枚举类和普通类一样可以实现接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22enum Gender {
BOY, GIRL;
}
enum Season {
SPRING("Spring", "The season of new beginnings"),
SUMMER("Summer", "The warmest season"),
AUTUMN("Autumn", "The season of harvest"),
WINTER("Winter", "The coldest season");
private String name;
private String desc;
private Season(String name, String desc) {
this.name = name;
this.desc = desc;
}
public String getName() {
return name;
}
public String getDesc() {
return desc;
}
}
Enum 成员方法
1 | public class Hello{ |
JDK 内置的基本注解类型
注解的理解
- 注解也被称为元数据,用于修饰解释包、类、方法、属性、构造器、局部变量等数据信息
- 和注释一样,注解不影响程序逻辑,但注解可以被编译或运行,相当于嵌入在代码中的补充信息
基础注解
- Override: 表示指定重写父类的方法,如果父类没有该方法,则会报错
- Deprecated: 表示某个程序元素已过时,可以修饰方法、类、字段、包、参数等
- SuppressWarnings: 抑制编译警告
元注解: 对注解进行注解
- Retention: 指定注解的范围,三种(SOURCE、CLASS、RUNTIME)
- Target: 指定注解可以在哪些地方使用
- Documented: 指定该注解是否会在 javadoc 中体现
- Inherited: 子类会继承父类注解
异常
常见的异常
- NullPointerException
- ArithmeticException
- ArrayIndexOutOfBoundsException
- ClassCastException
- NumberFormatException
异常处理方式
- 没有显式处理异常,默认是 throws
try-catch-finally
throws
1 | class Test { |
- 子类重写父类方法时,对于抛出异常的规定: 所抛出的异常类型要么和父类抛出的异常一样,要么为父类抛出异常的子类
异常处理分类
编译时异常
运行时异常
自定义异常
1 | class MyException extends Exception { |
throw 和 throws 对比
- throws: 异常处理的一种方式,在方法声明处使用,后面跟异常类型
- throw: 手动生成异常对象的关键字,在方法体中使用,后面跟异常对象
常用类
包装类
- 针对八种基本数据类型相应的引用类型–包装类
| 基本类型 | 包装类 |
|---|---|
| boolean | Boolean |
| char | Character |
| byte | Byte |
| short | Short |
| int | Integer |
| long | Long |
| float | Float |
| double | Double |
- 常用方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14public class Hello{
public static void main(String [] args) {
int a = 1;
Integer integer = a;
int b = integer;
System.out.println(b);
String str1 = integer + "";
String str2 = integer.toString();
String str3 = String.valueOf(integer);
string str4 = "12345";
Integer c = Integer.parseInt(str4);
Integer c = new Integer(str4);
}
}
String
- 实现了 Serializable 和 Comparable(可以串行化和比较大小)
- String 是 final 类
- 有属性 private final char value[]; 用于字符串内容(final 表示指向的地址不能修改,类似于 C++ 中 char* const)
创建剖析
直接赋值
- String str = “yzy”;
- 先从常量池查看是否有 “yzy” 数据空间,如果有,直接指向;如果没有则重新创建,然后指向。最终指向的是常量池的空间地址。
调用构造器
- String str = new String(“yzy”);
- 先在堆中创建空间,里面维护了 value 属性,指向常量池的 “yzy” 空间。如果常量池没有 “yzy”,重新创建,如果有,直接通过 value 指向。最终指向的是堆中的空间地址。
常用方法
StringBuffer
- String 保存的是字符串常量,里面的值不能更改,每次 String 类的更新实际上就是更改地址,效率较低 // private final char value[];
- StringBuffer 保存的是字符串常量,里面的值可以更改,每次的更新实际上可以更新内容,不用每次更新地址,效率较高
StringBuilder
- 一般用在单线程
Math
- 包含用于执行基本数学运算的方法
Arrays
- 包含了一系列的静态方法,用于管理或操作数组(比如排序和搜索)
1
2
3
4
5
6
7
8
9import java.util.Arrays;
public class Hello{
public static void main(String [] args) {
Integer[] integers = {1,2,3};
System.out.println(Arrays.toString(integers));
Arrays.sort(integers);
}
}
System
常用方法
1 | import java.util.Arrays; |
BigInteger、BigDecimal
BigInteger
- 加减乘除需要使用对应的方法
1
2
3
4
5
6
7
8
9
10
11
12import java.math.BigInteger;
public class Hello{
public static void main(String [] args) {
BigInteger b1 = new BigInteger("123456789012345678901234567890");
System.out.println(b1);
BigInteger b2 = b1.add(new BigInteger("987654321098765432109876543210"));
BigInteger b3 = b1.multiply(new BigInteger("2"));
BigInteger b4 = b2.subtract(b3);
BigInteger b5 = b4.divide(new BigInteger("3"));
}
}
BigDecimal
- 同 BigIntger
- 使用除法时可能除不尽,可以设置参数保留解读
Date、Calender、LocalDate
Date
- 精确到毫秒
1
2
3
4
5
6
7
8
9
10
11
12import java.util.Date;
import java.text.SimpleDateFormat;
public class Hello{
public static void main(String [] args) {
Date d1 = new Date();
System.out.println(d1); // Sat Aug 23 00:01:43 CST 2025
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String formattedDate = sdf.format(d1);
System.out.println(formattedDate); // 2025-08-23 00:01:43
}
}
Calender
1 | import java.util.Calendar; |
LocalDate
1 | import java.time.LocalDate; |
集合
集合框架体系
Collection
Collection 公共常用方法
- add
- remove
- contains: 查看元素是否存在
- size
- isEmpty
- clear
- addAll: 添加一个 Collection
- containsAll
- removeAll
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25import java.util.ArrayList;
public class Hello{
public static void main(String [] args) {
ArrayList list = new ArrayList();
list.add("Hello, World!");
list.add(123); // list.add(new Integer(123));
list.add(true);
System.out.println(list);
list.remove(0); // 删除索引为0的元素
list.remove(true); // 删除值为 true 的元素
System.out.println(list);
System.out.println(list.contains("yzy"));
System.out.println(list.size());
System.out.println(list.isEmpty());
list.clear();
System.out.println(list);
ArrayList list2 = new ArrayList();
list2.add("haha");
list.addAll(list2);
System.out.println(list);
list.removeAll(list2);
System.out.println(list);
}
}
迭代器遍历
1 | import java.util.ArrayList; |
增强 for
1 | import java.util.ArrayList; |
List
- List 集合类中元素有序、可重复
- 每个元素都有其顺序索引
List 接口方法
1 | import java.util.ArrayList; |
ArrayList
- 可以放 null 值
- 底层使用数组实现的
- 基本等同于 Vector
- 线程不安全的
- 维护了一个 Object 类的数组
- 当创建 ArrayList 对象时,如果使用的是无参构造器,则初始容量为 0,第一次添加,则扩容为 10,若需要再次扩容,则扩容为 1.5 倍
- 若使用的是指定大小的构造器,则初始容量为指定大小,若在扩容,还是扩 1.5 倍
Vector
- 底层也是数组
- 线程安全的
LinkedList
- 实现了双向链表和双向队列特点
- 可以添加任意元素,包括 null
- 线程不安全
Set
- 无序、没有索引
- 不允许有重复元素
- 可以使用迭代器、增强 for
1
2
3
4
5
6
7
8
9
10
11
12import java.util.HashSet;
public class Hello{
public static void main(String [] args) {
HashSet set = new HashSet();
set.add("Hello");
set.add("World");
set.add("Hello");
set.add(null);
System.out.println(set);
}
}
HashSet
- HashSet 实际上是一个 HashMap
LinkedHashSet
- LinkedHashSet 实际上是一个 LinkedHashMap,底层维护了一个数组 + 双向链表
- 根据元素的 hashCode 值来决定元素的存储位置,同时使用链表维护元素的次序
TreeSet
- 使用无参构造器仍然是无序的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18import java.util.TreeSet;
import java.util.Comparator;
public class Hello{
public static void main(String [] args) {
TreeSet ts = new TreeSet(new MyComparator());
ts.add("B");
ts.add("A");
ts.add("C");
System.out.println(ts);
}
}
class MyComparator implements Comparator {
public int compare(Object obj1, Object obj2) {
return -((String)obj1).compareTo((String)obj2);
}
}
Map
- Map 与 Collection 并列存在
常用方法
- put
- remove
- get
- size
- isEmpty
- clear
- containKey
遍历方式
1 | import java.util.HashMap; |
HashMap
- 底层是数组 + 链表 + 红黑树
- 线程不安全
HashTable
- key 和 value 都不能为 null
- 使用方法基本上和 HashMap 一样
- 线程安全
LinkedHashMap
TreeMap
Properties
- 继承自 HashTable 类并且实现了 Map 接口
- 使用特点和 HashTable 类似
- 用于从 xxx.propertise 文件中,加载数据到 Properties 类对象
Collections
- 一个操作 Set、List 和 Map 等集合的工具类
- 提供了一系列静态的方法对集合元素进行排序、查询和修改等操作
泛型
- 编译时检查添加元素的类型,提高了安全性
- 减少了类型转化的次数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33import java.util.ArrayList;
public class Hello{
public static void main(String [] args) {
ArrayList <Dog> dogs = new ArrayList<Dog>();
dogs.add(new Dog("Buddy", 3));
dogs.add(new Dog("Max", 5));
// dogs.add(new Cat("Whiskers", 2)); // This line will cause a compile-time error
for (Dog dog : dogs) {
System.out.println(dog.name + " is " + dog.age + " years old.");
}
}
}
class Dog {
String name;
int age;
Dog(String name, int age) {
this.name = name;
this.age = age;
}
}
class Cat {
String name;
int age;
Cat(String name, int age) {
this.name = name;
this.age = age;
}
}
泛型语法
- 给泛型指向数据类型时,要求是引用类型,不能是基本数据类型
1
2ArrayList<int> list = new ArrayList<int>(); // error
ArrayList<String> list = new ArrayList<String>(); // yes - 在给泛型指定具体类型后,可以传入该类型或者其子类类型
- 省略语法
1
2ArrayList<String> list = new ArrayList<>(); // 编译器会自动推断
ArrayList list = new ArrayList(); // 不指定类型时,默认是 Object
自定义泛型
- 普通成员可以使用泛型
- 使用泛型的数组,不能初始化
- 静态方法中不能使用类的泛型(因为静态是和类相关的,在类加载时,对象还没有创建,所以,如果静态方法或属性使用了泛型,JVM就无法完成初始化),但是能够定义和使用自己的泛型,成为静态泛型方法
- 泛型类的类型,是在创建对象时确定的
- 如果在创建对象时,没有指定类型,默认为 Object
泛型类
1 | public class Hello{ |
泛型接口
1 | interface Animal<T> { |
泛型方法
- 修饰符 <T, R…> 返回类型 方法名(参数列表) {}
- 可以定义在普通类中,也可以定义在泛型类中
泛型继承和通配符
- 泛型没有继承性
1
ArrayList<Object> list = new ArrayList<String>(); // error
- >: 支持任意泛型
- extends A>: 支持 A 类以及 A 类的子类,规定了泛型的上限
- super A>: 支持 A 类以及 A 类的父类,不限于直接父类,规定了泛型的下限
线程(基础)
线程使用
- main 线程结束后,其他线程不会被动结束,还会继续执行
继承 Thread 类
1 | public class Hello{ |
实现 Runnable 接口
- 避免单继承的限制
1
2
3
4
5
6
7
8
9
10
11public class Hello{
public static void main(String [] args) {
Thread t = new Thread(new Test(), "My Runnable");
t.start();
}
}
class Test implements Runnable {
public void run() {
System.out.println("Hello, World!");
}
}
线程方法
- setName: 设置线程名字
- getName
- start: 底层会创建新的线程,并调用 run
- run: run 就是一个简单的方法调用,不会启动新线程
- setPriority: 设置线程优先级
- getPriority
- sleep: 线程的静态方法,使当前线程休眠
- interrupt: 中断线程,但并没有真正结束线程。一般用于中断正在休眠的线程(唤醒?)
- yield: 让出 cpu,让其他线程执行(不一定礼让成功,看操作系统)
- join: 线程的插队,一旦插队成功,则肯定先执行完插入的线程所有的任务
用户线程
- 也叫工作线程,当线程的任务执行完或通知方式结束
守护线程
- setDaemon(true): 一般是为工作线程服务的,当所有的工作线程结束,守护线程自动结束
线程生命周期
- Thead.State
- NEW: 尚未启动的线程处于此状态
- RUNNABLE: 在 java 虚拟机中执行的线程处于此状态
- READY
- RUNNING
- BLOCKED: 被阻塞等待监视器锁定的线程处于此状态
- WAITING: 正在等待另一个线程执行特定动作的线程处于此状态
- TIMED_WAITING: 正在等待另一个线程执行动作达到指定等待时间的线程处于此状态
- TERMINATED: 已退出的线程处于此状态
Synchronized
同步代码块
1 | synchronized (对象) { // 得到对象的锁才能操作同步代码 |
同步方法
1 | public synchronized void func() { // 在同一时刻,只能有一个线程执行该方法 |
互斥锁
- 每个对象都对应一个可称为互斥锁的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象
- 关键字 synchronized 来与对象的互斥锁联系,当某个对象被 synchronized 修饰时,表明该对象在任一时刻只能由一个线程访问
- 同步方法(非静态的)的所可以是 this,也可以是其他对象(任意对象都可以)
- 同步方法(静态的)的锁为当前类本身(默认锁对象: 当前类.class)
死锁
IO 流
文件
概念
- 文件在程序中是以流的形式来操作的
- 输入流: 内存 –> 文件
- 输出流: 文件 –> 内存
常用操作
创建文件
- new File(String pathname) // 根据路径构建一个 File 对象
- new File(File parent, String child) // 根据父目录文件 + 子路径构建
- new File(String parent, String child) // 根据父目录 + 子路径构建
- createNewFile: 创建新文件
1
2
3
4
5
6
7
8
9
10
11
12
13import java.io.File;
public class Hello{
public static void main(String [] args) {
File f = new File("test.txt");
try {
f.createNewFile();
}
catch (Exception e) {
System.out.println(e);
}
}
}
获取文件信息
- getName
- getAbsolutePath
- getParent
- length
- exits
- isFile
- isDirectory
目录操作和文件删除
- mkdir: 创建一级目录
- mkdirs: 创建多级目录
- delete: 删除空目录或文件j
IO 流原理及流的分类
- 按操作数据单位不同: 字节流(InputStream, OutpuStream)、字符流(Reader, Writer)
- 按数据流的流向不同: 输入流、输出流
- 按流的角色不同: 节点流、处理流 / 包装流
节点流和处理流
输入流
InputStream()
FileInputStream
BufferedInputStream
ObjectInputStream
Reader
FileReader
BufferedReader
InputStreamReader
输出流
OutputStream
FileOutputReader
BufferedOutputReader
ObjectOutputReader
Writer
FileWriter
BufferedWriter
OutputStreamWriter
Properties 类
反射
- 通过外部文件配置,在不修改源码的情况下来控制程序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22import java.lang.reflect.Method;
public class Hello{
public static void main(String [] args) {
String className = "Cat";
String methodName = "hi";
try {
Class<?> cls = Class.forName(className);
Object obj = cls.getDeclaredConstructor().newInstance();
Method method = cls.getMethod(methodName);
method.invoke(obj);
} catch (Exception e) {
e.printStackTrace();
}
}
}
class Cat {
public void hi() {
System.out.println("Meow");
}
}
反射机制
- 反射机制允许程序在执行期间借助于 Reflection API 取得任何类的内部信息(比如成员变量、构造器、成员方法等),并能操作对象的属性及方法
- 加载完类之后,在堆中就产生了一个 Class 类型的对象(一个类只有一个 Class 对象),这个对象包含了类的完整结构信息。通过这个对象得到类的结构。
- 在运行时判断任意一个对象所属的类
- 在运行时构造任意一个类的对象
- 在运行时得到任意一个类所具有的成员变量和方法
- 在运行时调用任意一个对象的成员变量和方法
- 生成动态代理
反射相关类
- java.lang.Class: 代表一个类,Class 对象表示某个类加载后在堆中的对象
- java.lang.reflect.Method: 代表类的方法
- java.lang.reflect.Field: 代表类的成员变量
- java.lang.reflect.Constructor: 代表类的构造方法
Class类
- Class 也是类,因此也继承 Object 类
- Class 类对象不是 new 出来的,而是系统创建的
- 对于某个类的 Class 类对象,在内存中只有一份,因为类只加载一次
- 每个类的实例都会记得自己是由哪个 Class 实例所生成
- 通过 Class 可以完整地得到一个类的完整结构,通过一系列 API
- Class 对象是存放在堆中的
- 类的字节码二进制数据是放在方法区的,有的地方称为类的元数据
反射获取类的结构信息
Class
Field
Method
Constructor
访问属性
访问方法
哪些类有 Class 对象
- 外部类、成员内部类、静态内部类、局部内部类、匿名内部类
- interface
- 数组
- enum
- annotation(注解)
- 基本数据类型
- void
类加载
- 静态加载: 编译时加载相关的类,如果没有则报错,依赖性太强
- 动态加载: 运行时加载需要的类,如果运行时不用该类则不报错,降低了依赖性
类加载时机
- 当创建类时 new
- 当子类被加载时
- 调用类中的静态成员
- 反射(动态加载)
类加载流程(线程安全)
- java 源码
- 字节码文件
- 加载: 将类的 class 文件读入内存,并为之创建一个 java.lang.Class 对象,此过程由类加载器完成
- 连接: 将类的二进制数据合并到 JRE 中
- 验证: 对文件的安全进行校验
- 准备: 对静态变量进行默认初始化(0, null, false…)并分配内存
- 解析: 把符号引用转成直接引用
- 初始化: JVM 负责对类进行初始化,这里主要指静态成员
类加载后内存布局
- 方法区: 类的字节码二进制
- 堆区: 类的 Class 对象
反射调用性能优化
…
MySQL
安装
1 | sudo pacman -S mysql |
数据库
创建
1 | CREATE DATABASE test; # 默认 utf8 和 utf8_general_ci |
查看、删除数据库
1 | SHOW DATABASES; # 显示数据库语句 |
备份恢复数据库
备份数据库
1 | # 在 shell 中执行 |
恢复数据库
1 | # 在 mysql 命令行执行 |
表
创建
1 | create table table_name ( |
删除
修改
MySQL 数据类型
数值类型
- 整型
- tinyint
- smallint
- mediumint
- int
- bigint
- 小数类型
- float
- double
- decimal
文本类型(字符串类型)
- char
- varchar
- text
- longtext
二进制数据类型
- blob
- longblob
日期类型
- data(年月日)
- time(时分秒)
- datatime(年月日时分秒)
- timestamp(时间戳)
- year