表的描述

空属性

  • null(默认)和not null(不为空)
  • 注意:在开发过程中尽量避免字段不为空,数据为空是没有办法参与运算的。’’
1
2
3
mysql> create table myclass(
-> class_name varchar(20) not null,
-> class_room varchar(10) not null);

默认值

  • 某一种数据会经常出现某个具体的值,可以在一开始指定好,在需要帧数数据的时候,用户可以选择性的使用默认值。
1
2
3
4
mysql> create table tt10 (
-> name varchar(20) not null,
-> age tinyint unsigned default
-> sex char(2) default '男');

列描述

  • comment,没有实际含义,专门用来描述字段,在创建表时保存
1
2
3
4
mysql> create table tt12 (
-> name varchar(20) not null comment '姓名',
-> age tinyint unsigned default 0 comment '年龄',
-> sex char(2) default '男' comment '性别');

comment通过desc看不到注释信息,需要通过show关键字查询

1
2
3
4
5
6
7
8
9
10
mysql> show create table tt12/G;
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '/G' at line 1
mysql> show create table tt12\G;
*************************** 1. row ***************************
Table: tt12
Create Table: CREATE TABLE `tt12` (
`name` varchar(20) NOT NULL COMMENT '姓名',
`age` tinyint(3) unsigned DEFAULT '0' COMMENT '年龄',
`sex` char(2) DEFAULT '男' COMMENT '性别'
) ENGINE=InnoDB DEFAULT CHARSET=utf8

zerolfill

  • 相当于自动填充功能(格式化输出),假如指定一个字段的长度为5并且向该字段插入一个数据1后,当你在查询时,就会将宽度小于设定宽度的地方自动填充为0,显示为00001
1
2
3
4
5
6
mysql> select * from tt3;
+-------+------+
| a | b |
+-------+------+
| 00001 | 2 |
+-------+------+
  • 在查看时也可以通过hex函数来表示
1
2
3
4
5
6
mysql> select a, hex(a) from tt3;
+-------+--------+
| a | hex(a) |
+-------+--------+
| 00001 | 1 |
+-------+--------+

主键

primary key:用来唯一约束该字段里边的内容,不能重复,不能为空,一张表中最多只能有一个主键,并且主键所在的列通常是整数类型。

  • 在创建表时,直接在字段上指定主键
1
2
3
4
5
6
7
8
9
10
11
12
13
mysql> create table tt13(
-> id int unsigned primary key comment '学号不能为空',
-> name varchar(20) not null);
Query OK, 0 rows affected (0.04 sec)

mysql> desc tt13; 主键
+-------+------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+------------------+------+-----+---------+-------+
| id | int(10) unsigned | NO | PRI | NULL | |
| name | varchar(20) | NO | | NULL | |
+-------+------------------+------+-----+---------+-------+
2 rows in set (0.01 sec)
  • 复合主键,可以在创建表的最后,使用复合主键来设置多个字段
1
2
3
4
5
6
7
8
9
10
11
12
13
mysql> create table tt14(
-> id int unsigned,-> course char(10) comment '代码',
-> score tinyint unsigned default 60 comment '成绩',
-> primary key(id, course) -- id和course为复合
->);
mysql> desc tt14;
+--------+---------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------+---------------------+------+-----+---------+-------+
| id |int(10) unsigned | NO | PRI | 0 | |
| course | char(10) | NO | PRI | | |
| score | tinyint(3) unsigned | YES | | 60 | |
+--------+---------------------+------+-----+---------+-------+
  • 追加主键
1
alter table 表名 add primary key(字段列表)
  • 主键约束:主键对应的字段不能重复,一旦重复,操作失败
  • 删除主键
1
alter table 表明 drop primary key;

自增长

auto_increment:会自动在当前字段已有的最大值+1操作,得到一个新的不同值。通常与主键搭配使用,作为逻辑主键。

特点:

  • 任何一个字段要做自增长,前提本身是一个索引
  • 自增长字段必须为整数
  • 一张表最多有一个自增长
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
mysql> create table tt21(
-> id int unsigned primary key auto_increment,
-> name varchar(10) not null default ''
-> );
Query OK, 0 rows affected (0.03 sec)

mysql> insert into tt21(name) values('a');
Query OK, 1 row affected (0.01 sec)

mysql> insert into tt21(name) values('b');
Query OK, 1 row affected (0.00 sec)

mysql> select * from tt21;
+----+------+
| id | name |
+----+------+
| 1 | a |
| 2 | b |
+----+------+
2 rows in set (0.00 sec)

在插入后获取最近一次auto_increment的值(批量插入获取的是第一个值)

1
2
3
4
5
6
mysql> select last_insert_id();
+------------------+
| last_insert_id() |
+------------------+
| 3 |
+------------------+

唯一键(unique)

一张表中可以多个唯一键,唯一键与主键差不多,数据不能重复,但是唯一键允许为空,而且可以多个为空,空字段不做唯一性比较。

外键

外键用于定义主表与从表之间的关系:外键约束主要定义在从表上,主表则必须是有主键约束或unique约束。当定义外键后,要求外键列数据必须在主表的主键列存在或为空

语法

1
foreign key (字段名) references 主表(列)

举例:班级表与学生表

  • 创建主键表
1
2
3
4
mysql> create table class(
-> id int primary key,
-> name varchar(20) not null comment '班级名'
-> );
  • 创建从表
1
2
3
4
5
6
mysql> create table stu(
-> id int primary key,
-> name varchar(20) not null comment '班级名',
-> class_id int,
-> foreign key (class_id) references class(id) 外键约束
-> );
  • 插入数据
1
2
3
4
5
6
7
8
9
10
mysql> insert into class values(10,'16班'),(20,'15班');
Query OK, 2 rows affected (0.01 sec)
Records: 2 Duplicates: 0 Warnings: 0

mysql> insert into stu values(100,'张三',10),(101,'李四',20);
Query OK, 2 rows affected (0.01 sec)
Records: 2 Duplicates: 0 Warnings: 0

mysql> insert into stu values(102,'王五',null); 外键可以为空
Query OK, 1 row affected (0.00 sec)

Maven:项目管理

项目管理:

  1. 关于类库的管理(Jar包导入) 管理各种第三方jar
  2. 项目构建(打包发布) mvn package
  3. 项目标准结构

pom.xml – Maven配置文件

src – 放置所有源代码与测试代码

​ – main -源文件

​ – java文件

​ – test文件

1.Maven项目构建

  1. 通过命令行构建
1
2
3
mvn -B archetype:generate -DarchetypeGroupId=org.apache.maven.archetypes -
DarchetypeArtifactId=maven-archetype-quickstart -DarchetypeVersion=1.1 -
DgroupId=com.bittech.hello -DartifactId=hello-app -Dversion=1.0.

-B通过命令行一次性构建项目

  1. 通IDEA构建

2.Maven中三个关键配置

  • groupID:项目所属的公司或组织名称 – 用于后续包名称定义
  • artifactID:项目名称
  • version:项目版本号

3.pom.xml中关键配置

1
2
3
4
5
6
7
8
9
</dependencies>
<!-- 配置所有项目需要的第三方Jar包 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>

在内部导入第三方jar包,每个jar包都对应一个标签,通过groupID,artifactID,version来指定不同的jar包

ctrl+shift+T 创建单元测试

4.Maven生命周期

  1. compile

    mvn copmlie(*.java -> *.class)

  2. clean – 清空target目录

    mvn clean

  3. test – 执行测试用例

    mvn test

  4. package – 将项目打包

    mvn package

    打包生成的结果(jar—默认,war)命名按照 artifactID—version

    在执行package将项目打包过程中会默认执行test命令运行所有测试用例,只有当所有测试用例均通过项目才会打包成功。

mvn -DskippTests package

打包过程中不再执行测试用例直接将项目打包

  1. 构建可执行jar 重要

    1. 在pom.xml中添加打包插件并指明主类来使得打包生成额jar具备可执行能力
    2. 使用mvn package将项目打包
    3. java -jar 打包后的jar包
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
<build>
<!-- 所有maven生命周期中需要的插件在此配置,插件是一个特殊的maven构件 -->
<plugins>
<!-- 在此添加插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<!--设置版本号,版本号要和Plugins中jar版本号一致-->
<version>2.4</version>
<configuration>
<archive>
<manifest>
<!--依赖的jar包添加到classpath-->
<addClasspath>true</addClasspath>
<!--设置可执行jar的主类-->
<mainClass>com.bittech.hello.App</mainClass>
<addExtensions>true</addExtensions>
<!--指定可执行jar依赖包归档的目录前缀-->
<classpathPrefix>lib</classpathPrefix>
</manifest>
<manifestEntries>
<Implementation-Title>${project.name}</Implementation-Title>
<Implementation-Version>${project.version}</Implementation-Version>
<Implementation-Vendor-Id>${project.groupId}</Implementation-Vendor-Id>
</manifestEntries>
</archive>
</configuration>
</plugin>
</plugins>
</build>
  1. install —— 安装构件到本地仓库

    mvn install

    将项目打包放置在本地仓库,本机中的其他项目可以使用dependency来引入此项目。

    • maven寻找jar包顺序

      • 先从本地仓库查询是否存在,如不存在去中央仓库寻找,如中央仓库都没有找到报错。

      • 匹配jar包的规则:根据pom文件中指定的dopendency中三个关键配置

        groupID -> artifactID -> version

  2. deploy —— 安装构件到中央仓库

  3. site —— 生成网站

    mvn site

    将项目信息,依赖等生成网站

maven:

  1. 重点掌握IDEA创建Maven项目
  2. 使用pom文件添加依赖
  3. 会使用插件构建可执行jar包

邮箱选择 gmail/outlook/foxmail

复杂度分析

如何分析,统计算法的执行效率和资源消耗

  1. 测试结果非常依赖测试环境
  2. 测试结果受数据规模的影响很大

大O复杂度表示法

所有代码的执行时间T(n)与每行代码的执行次数n成正比

大O公式:T(n) = (f(n))

  • 大O时间复杂度实际上并不是具体表示代码真正的执行时间,而是表示代码执行时间随数据规模增长的变化趋势,所以也叫作渐进时间复杂度(asymptotic space complexity),简称为时间复杂度

时间复杂度分析

  1. 只关注循环执行次数最多的一段代码
  2. 加法法则:总复杂度等于量级最大的那段代码的复杂度
  3. 乘法法则:嵌套代码的复杂度等于嵌套内外代码复杂度的乘积

O(1)

  • 一般情况下,只要不存在循环语句,递归语句,即使有成千上万行的代码,其时间复杂度也是O(1)

O(logn),O(nlogn)

$$
对数之间可以相互转化的\log3_n 就等于\log3_2\log2_n,所以O(\log3_n)=O(C\log2_n),其中 C=\log3_2 是一个常量。
$$

在采用大O标记复杂度的时候,可以忽略系数,即O(Cf(n))=O(f(n))。
$$
因此,O(\log2_n)就等于O(\log3_n)。因此,在对数阶时间复杂度的表达方法里,我们忽略对数的“底”,统一表示为O(logn)
$$

  • 当一段代码的时间复杂度为O(logn),将循环执行n遍,时间复杂度就是O(nlogn),比如,归并排序,快速排序的时间都是O(nlogn)。

O(m+n),O(m*n)

  • m和n是表示两个数据规模。当无法事先评估m和n谁的量级大,所以我们在表示复杂度的时候,就不能简单利用加法法则而省略其中一个。所以,时间复杂就为O(m+n)。


时间复杂度最好,最坏,平均,均摊分析

最好,最坏情况时间复杂度

平均情况复杂度

均摊时间复杂度

  • 摊还分析法

JVM

JVM简介

  • JVM(Java Virtual Machine)
    • 虚拟机:通过软件模拟的具有完整硬件功能,运行在完全隔离环境中的计算机系统
    • 常见虚拟机:VMWare/Box
  • JVM是通过软件模拟Java字节码指令集,JVM中只保留了PC寄存器,而普通的虚拟机有很多寄存器
  • 从JDK1.3至今,HotSpot为默认JVM

Java内存区域划分 – 共六块内存区

线程私有内存:每个线程都有,彼此之间完全隔离

  1. 程序计数器(Program Counter Register) – 记录上一次执行到的地址

    程序计数器是比较小的内存空间,当前线程所执行的字节码的行号指示器

    • 若当前线程执行的是Java方法,计数器记录的是正在执行的JVM字节码指令地址
    • 若当前线程执行当是 Native本地方法时, 计数器为空

    程序计数器是唯一一块不会产生OOM(OutOfMemoryError)异常的区域

  2. 虚拟机栈(Virtual Machine Stacks) – Java方法执行的内存模型

    虚拟机栈描述了Java方法执行的内存模型:每个方法执行时都会创建一个栈帧存储局部变量表(决定了栈帧开辟内存空间的大小),操作数栈方法出口,动态链接等信息。每个方法从调用到执行完毕的过程就相当于对应一个栈帧从虚拟机的入栈的出栈过程。

    局部变量表:存放编译器可知的各种数据类型(8大基本数据类型),对象引用(大小固定为4个字节)。局部变量所需要的空间是在编译期间完成分配,当进入一个方法时,这个方法需要在帧中分配多大的局部变量是完全确定的,在执行期间不会再改变局部变量表的大小

    • 生命周期好线程相同:在创建线程同时创建此线程的虚拟机栈,线程执行结束,虚拟机栈会和线程一同被回收

    可能会产生两种异常:

    1. 若线程请求深度大于JVM允许的深度(-Xss设置栈容量),会抛出StackOverflowError异常。(发生情况一般在单线程模式下)
    2. 虚拟机在进行栈的动态扩展时,若没有申请到足够内存,会抛出OOM(OutOfMemoryError)异常。(发生情况一般在多线程模式下)

    注意:Java中可以大致的分为堆内存(Heap)和栈内存(Stack),这个概念很粗糙,这里的栈内存其实就是这里的虚拟机栈,也可以说是虚拟机栈中局部变量表的部分。

  3. 本地方法栈(Native Method Stack)

    • 本地方法与虚拟机栈的作用类似,但是虚拟机栈为虚拟机执行Java方法服务,本地方法为虚拟机使用的Native方法服务, 在HotSpot虚拟机中,本地方法个虚拟机栈是同一块内存区域,合二为一
    • 与虚拟机栈一样,本地方法也会抛出StackOverflowError和OutOfMemoryError异常

线程共有内存:所有线程共享此内存空间,此内存空间所有线程可见

  1. 堆(GC堆 Garbage Collected Heap)

    Java堆(Java Heap)时JVM管理的最大内存区域。在JVM启动时创建,所有线程共享此内存,该内存中存放的都是 对象实例以及数组

    • Java堆时垃圾回收器管理的最主要内存区域。Java堆对可以处于物理上不连续,只要是逻辑上连续的即可,就例如磁盘空间一般。在实现时,即可以实现成固定大小,也可以是可扩展的(-Xmx设置堆最大值)(-Xms设置堆的最小值)
    • 若堆中没有足够的内存完成对象实例分配并且堆无法再次扩展时,会抛出OOM(OutOfMemoryError)异常
  2. 方法区(Method Area)

    • 用于存储已经被JVM加载的类信息,常量,静态变量数据。JDK8以前,方法区也叫作 永久带(Permanent Generation),JDK8之后称之为 元空间(Meta Space)
    • 当方法区无法满足内存分配需求时,会抛出OOM(OutOfMemoryError)异常
  3. 运行时常量池(Runtime Constant Pool)

    运行时常量池是方法区的一部分,存放字面量与符号引用。

    • 字面量:字符串常量(JDK1.7移到堆中),final常量,基本数据类型的值。
    • 符号引用:类,字段,方法的完全限定名,方法的名称,描述符。

    运行时常量池具备动态性,java语言并没有要求常量一定只有编译期才能产生,也就是并非提前置入Class文件中常量池的内容才能进入方法区常量池,运行期间也可以将新的常量放入池中,就比如String类的intern()手动入池方法

直接内存

关于OOM产生的原因:
  • 内存溢出:内存中对象确是还应该存活,但是由于堆内存不够用产生的异常
  • 内存泄漏:无用对象无法被GC

那么如何区分OOM是那种原因产生

  • 比如:存在一个递归,并且递归没有出口,这时运行程序,就会产生OOM异常,当我们要判断该异常是是什么原因产生的时,只需要将堆内存的可扩展性扩大,如果不会产生OOM异常,则是由于内存溢出而导致,否则为内存泄漏,在该栗子中,很明显当我们将堆内存扩大时并不能解决OOM问题,所以该异常是由于内存泄漏引起的。

判断对象是否已死

引用计数法(Reference Counting)

算法思想:给每个对对象附加一个引用计数器,每当有一个地方引用此对象时,计数器+1;每当有一个引用失效时,计数器-1;在任意时刻,只要计数器值为0的对象就是不能再被使用,即对象已死。

  • 引用计数法实现简单,判定效率较高。Python就是采用引用计数法来管理内存。但是无法解决循环引用问题

可达性分析算法(Reachability Analysis)

Java采用可达性分析算法来判断对象是否存活(C#,Lisp)

算法思想:通过一系列“GC Roots”的对象作为起点,从这些节点开始向下搜索对象,搜索走过的路径,称为“引用链”,当一个对象到任意一个GC

Root对象没有任何的引用链相连时(从GC Roots到对象不可达),证明对象已死。

Java中能够作为GC Roots的对象包含一下四种 :

  1. 虚拟机栈(栈帧中的本地变量表)中引用对象
  2. 类静态变量引用的对象
  3. 常量引用的对象
  4. 本地方法栈中引用的对象