面向对象整理
面向过程和面向对象的区别
面向对象编程思想不考虑封装,继承,多态,直接定义数据为静态变量,用静态函数操作数据,基于过程编程,面向过程编程,代码不具有可复用性和可扩展性;
面向对象编程思想,需要先抽象出实体的结构,并进行封装,用成员变量表达实体的属性,成员方法封装对属性的操作,提供构造方法构造对象,基于对象变成,面向对象编程,代码具有可复用性和可扩展性。
面向对象的三大特性:封装、继承、多态
第一特性:封装
用private进行私有化封装,不对外暴露对象的属性,防止外部对属性的误操作,会对外部暴露一个典型的成员方法setter和getter方法,供外部的设值、读值操作。
第二特性:继承
继承的关键字是extends
继承是发生在类与类之间的关系
发生继承的子类,被继承的是父类
Java不支持多继承,只支持但继承,但支持多级继承,一个类只能继承一个父;但是一个父类可以有多个子类
子类可以继承父类的非私有(非private修饰)的成员
父类的构造方法子类不能继承,但可以使用super()调用
如果一个类没有显示的继承父类,则隐式继承object类
如果父类中有抽象方法,子类必须重写,除非子类也是抽象类,让子类的子类去重写
父类的静态成员与继承无关
第三特性:多态
先有继承而后有多态,多态的反义词是单态
指针的多态(数组的多态,参数的多态,返回值的多态归根到底都是指针的多态)
如果指针是具体的子类类型,则指针是单态指针,只能指向具体的子类对象
如果指针是父类类型,则指针是多态指针,可以指向任意的子类对象
方法的多态(1.基于重载实现2.基于重写实现)
重载是编译时多态的体现
重写是运行时多态的体现
重载和重写的区别
重载和重写都是方法多态的体现
重载(overload)是编译时多态的体现
重载发生在同一个类中,java允许一个类中的多个方法同名,但必须满足重载的要求
方法名相同但是方法的参数列表不同(可以是参数个数不同,或者是参数类型不同)
静态方法,构造方法,成员方法都可以重载
重写(override)是运行时多态的体现
重写发生在子类与父类之间,子类重写父类的方法,其目的是,当使用父类型的指针调用子方法的时候,可以无需做向下转型
子类方法与父类方法同名
访问修饰符要大于等于父类方法
参数的个数必须与父类一样,参数类型可以小于父类方法的参数类型
返回值类型小于等于父类方法的返回值类型
抽象类和抽象方法的关系
抽象类和抽象方法的需要用abstract修饰
抽象方法必须出现在抽象类中
抽象类中可以有抽象方法,也可以没有
普通类和抽象类的区别
普通类可以实例化,也可以被继承
抽象类不可以实例化,只能被继承
(实例化就是new构造方法()创建对象)
向上转型和向下转型的区别
向上转型
将子类对象指针赋给父类对象指针的语法称为“向上转型”,“向上转型”通常是隐式的
继承是向上转型的前提,向上转型的目的是为了实现多态的指针
向上转型的副作用:指针无法访问下级对象中的成员(除非发生重写)
向下转型
将父类型指针赋给子类型指针的语法称为“向下转型”,需要显示强制转换,通常会伴随instanceof做类型判断,否则可能会出现ClassCastException类型转换异常
指针向下转型为子类类型,就可以访问子类中特有成员了(非重写的方法)
访问权限修饰符
修饰符 |
| 当前类 | 同包 | 子类 | 其他包 |
private | 私有的 | 可见 | 不可见 | 不可见 | 不可见 |
(default) | 默认的 | 可见 | 可见 | 不可见 | 不可见 |
protected | 受保护的 | 可见 | 可见 | 可见 | 不可见 |
public | 公有的 | 可见 | 可见 | 可见 | 可见 |
private的特性:只有本类可见
(default)的特点:只有同包可见
protected:子类可见
public:任何地方可见
抽象类和接口的区别
相同点:
都可以作为指针的类型,实现指针的多态
都不可以实例化
不同点:
抽象类用abstract class定义,接口用interface定义
抽象类用extends继承,接口用implements实现
类只能单继承,接口可以多实现
虽然两者都不可以被实例化,但是抽象类可以有构造方法,接口不可以有构造方法
抽象类可以有抽象方法,也可以有具体方法,接口只能有抽象方法,而且接口中所有的方法默认是”public abstract”修饰的
抽象类可以有成员变量,接口不能有成员变量,只能有静态常量
如何理解接口?如何理解面向接口编程
代码中存在依赖和调用关系,应该面向接口编程
(被调用方:只要是其他人写的代码被你拿来调用了,都被称为被调用方)
调用--------------依赖关系------------>被调用方(依赖API,API即接口和类,目的是为了调用对方的方法)
调用方关心什么:
方法名
参数列表
返回值
调用方不关心什么:
方法的实现过程
异常体系
Java把异常也封装成了对象
关键字throw和throws
Throw的作用与return很像
return的作用是方法返回,并且根据返回值类型返回相应的变量
throw的作用也是方法返回,但是只能返回异常对象,也就是说throw后面的对象必须是Throwable创建的子类
throw和throws的区别
Throw是用于返回异常对象的,通常throw后面跟随的对象必须是throwable的子类对象
而throws只是声明异常,把异常往上层抛,不会返回异常对象
try-catch-finally中那一部分可以省略
Try不可以省略
Catch可以省略
Finally可以省略
Catch和finally不可以同时省略
Try-catch:try中发生的异常被catch捕捉,程序可以继续往后运行
Try-finally:try中发生的异常没有被程序捕捉,程序会立即退出,但退出之前会执行finally
try-catch-finally:try中发生异常,异常代码后面的代码不会执行,直接进入catch,try中没有发生异常,不会进入catch,无论是否发生异常,finally都会执行
一般在finally中做什么
在try-catch-finally语法中,无论try执行哪个catch,最终都会执行finally,所有我们一般把在try中打开的资源放在finally中进行释放
try-catch-finally中,如果catch中return了,finally还会执行吗?
Finally会在return之前执行,但是finally中无法改变返回值的结果
可以理解为先执行finally的时候,先确定返回值,但是方法没有改变
常见的异常有哪些
Error错误(不受检) | Exception编译时异常(受检) | RuntimeException运行时异常(不受检) |
OutMerroyError内存溢出 | SQLException数据库异常 | NullPointerException空指针异常 |
StackOverflowError栈溢出 | ClassNotFoundException类找不到异常 | ClassCastException类型转换异常 |
| FileNotFoundException文件找不到异常 | IndexOutBoundsException下表越界异常 |
|
| InputMismatchException输入不匹配异常 |
|
| NumberFormatException数字转换异常 |
Final修饰符最终的
修饰变量称为常量 值不可以修饰
修饰方法称为最终方法方法不可以被子类重写
修饰类称为最终类,类不可以被继承
Final修饰的变量真的不能改变吗?
如果final修饰的变量是基本数据类型,指向常量池,那么值不可以修改
如果final修饰的变量是引用数据类型,指向堆,那么指针不可以修改,但是指针指向的对象内的成员可以修改
Abstract关键字是否可以与final同时出现,能不能同时用abstract和final修饰方法?
答:不能,原因是
abstract修饰的类称为抽象类,抽象类不能被实例化,只能被继承,而final是阻止类被继承
abstract修饰的方法称为抽象方法,抽象方法必须让子类重写,而final是阻止子类重写的
双向链表封装类-LinkeList
底层:Node对象
-增 add
-删 remove
-改 set
-查 get
链表在中间插入的速度要优于数组,只需要找到要插入位置的节点,创新节点,然后修改指针即可
数组在尾部插入较快,但是在首部或者中间插入,比较慢,因为需要数据后移
数组:遍历数组,指定位置后面的元素要后移
链表:遍历数组,指定位置的节点修改指针
增删改链表快,查数组快
(中间的插入、删除,和修改,链表优于数组;查询数组优于链表)
链表不存在扩容和转移,数组存在
LinkeList类中的一个静态内部 Node - 节点
next:下一个 prev:上一个
遍历ArrayList和LinkedList的几种方式
普通for循环
增强型for循环
forEach循环
迭代器
哈希表(HashMap)
同一个字符串,使用同样的哈希函数,计算出来的哈希码必定一样
不同的字符串,使用同样的哈希函数,计算出来的哈希码大概率是不一样的,小概率是一样的
什么是hash冲突/碰撞
如果有两个不同的字符串通过同样的哈希算法计算出来的哈希码是一样的,则称他们发生看哈希碰撞/冲突
如何解决哈希冲突?
1.开放地址法
2.拉链法(链地址法)HashMap默认使用的就是这种
当某一个链表的长度超过阈值8的时候,此时链表会转化为红黑树结构
链表树化,为了减少遍历的次数
哈希=散列
数组和链表是线性结构,特点:有序,不唯一
哈希(散列)是非线性结构,特点:无序,唯一的
HashSet类(无序,唯一):HashSet的底层是HashMap
存储在HashSet集合中的元素,实际上存储在HashMap中的key的位置
需要一个元素唯一的集合
存储键值对K,V的话用HashMap
只存储单个元素的话用HashSet
遍历HashMap和HashSet的几种方式
不可以用普通for循环,非线性结构不支持用索引号遍历
不可以用增强型for循环
可以用forEach循环
可以用迭代器
先获取Entry集合,然后再获取Entry集合的迭代器
先获取key的集合,然后再获取key集合的迭代器
遍历HashSet的几种方式
测试唯一性
测试无序性
遍历
进程和线程
进程(process)是操作系统的任务单元,每一个程序启动以后,操作系统都会为其分配进程编号PID
线程(Thread)是进程中的任务单元,程序启动的时候,首先会创建主线程,可以在主线程中开辟子线程,每一个线程都对应一个虚拟机栈,栈是线程私有的,堆和方法区是线程共享的
串行(hang)
在一台机器上单线执行
并行
并发:在同一台机器上多线程并行执行(存在资源竞争关系)
并行:在多台机器上并行执行(不存在资源竞争关系)
Java中实现多线程的方式有四种
1.继承Thread类,重写run方法
不能给主线程提供返回值,子线程异常,主线程捕捉不到
2.实现Runnable接口,重写run方法
当一个类已经有父类了,此时不方便继承Thread,可以采用Runnable接口的方式
3.实现Callable接口,重写call方法
实现线程的方式1和方式2都无法实现子线程运行结束后返回一个结果,因为run方法的返回值类型是void,而Callable可以给主线程提供返回值,子线程异常,主线程可以捕捉
4.使用线程池类
提升系统的性能和使用率
线程的五个生命周期
1.新建(new):新创建了一个线程对象。
2.(等待)可运行(runnable):线程对象创建后,当调用线程对象的start()方法,该线程处于就绪状态,等待被线程调度选中,获取cpu的使用权。
3.运行(running):可运行状态(runnable)的线程获得了qpu时间片(timeslice) ,执行程序代码。注:就绪状态是进入到运行状态的唯一入口, 也就是说,线程要想进入运行状态执行,首先必须处于就绪状态中;
4.阻塞(block):处于运行状态中的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,此时进入阻塞状态,直到其进入到就绪状态,才有机会再次被CPU调用以进入到运行状态。
阻塞的情况分两种:
1.阻塞时候释放锁
2.阻塞时候不释放锁
5.死亡(dead):线程run()、 main()方法执行结束,或者因异常退出了run()方法,则该线程结束生命周期。死亡的线程不可再次复生。
调用start()方法和run()方法的区别
调用start()会开辟新的路线,然后在新的线程中执行run()
直接调用run()不会开辟新的路线,直接在原来的线程中执行run()
锁和死锁
锁是java中用来保证线程操作原子性的一种机制
锁是数据库中用来保证事物操作原子性的一种机制
Java中的锁有synchronized和lock锁
synchronized是关键字,可以锁代码块,也可以锁方法
Lock是类(官方推荐),只能锁代码块
我们把数据类型分为线程安全和线程不安全类型
如果一个数据类型需要我们手动的加锁来保证其操作的原子性,那么它就是线程不安全的数据类型。
如果一个数据类型自己在方法中加锁来保证其操作的原子性,那么它就是线程安全的数据类型。
线程不安全 | 线程安全 |
ArrayList | 1.vector 2.CopyOnWriteArrayList |
HashMap | 1.Hashtable 2.ConcurrentHashMap |
String,StringBuilder | StringBuffer |
Int,Integer | AtomicInteger |
.................................................... |
并不是说线程安全就比线程不安全好,在不同环境就下每个都有自己的好
例如:单线程,不共享的情况下,上锁不仅会给自己带来麻烦,而且上锁还会烧性能;
但是在多线程,共享的情况下,不上锁就会无法保证操作的原子性,容易发生脏读,数据丢失一小部分
只有在多线程共享数据的前提下,才需要考虑加锁的问题
产生死锁的原因
线程A 锁住对象a,请求对象b的锁
线程B锁住对象b,请球对象a的锁
两个线程的手上各自持有一个对象的锁,在没有释放的情况下,去请求对方手上的锁
如何解决死锁
产生死锁条件是什么(死锁产生的原因)
互斥条件:锁要具有排他性,在同一时刻,锁只能被一个线程持有
请求与保持条件:一个线程因为请求其他资源被阻塞的时,对已获取的资源保持不放
不剥夺条件:一个线程没有主动开锁释放之前,是不能被其他线程强行剥夺的
循环等待条件:A线程持有资源a的锁,B线程持有资源b的锁,在互相不释放自己锁的情况下,去请求对方持有的锁,这时候会形成双方循环等待,造成永久阻塞。
解决死锁
破坏任意一个条件即可
破坏互斥条件:用共享锁,在同一时刻,锁可以被多个线程持有(共享锁只能用于读,不可以改).
破坏请求与保持条件:—次性申请所有的资源.
破坏不剥夺条件:一个线程因为请求其他资源被阻塞时,主动释放已获得的资源.
破坏循环等待条件:靠按序申请资源来预防。按某一顺序申请资源,释放资源则反序释放.
集合容器的整理
Collection和Map的区别
Collection和Map是官方提供的集合容器的两大体系的顶层接口
Collection代表单元素集合体系
Map代表KV键值对集合体系
Collection接口继承了iterable迭代器接口,所有的子类都提供了迭代器的实现,而Map体系没有
List,Set,Queue的区别
List,Set,Queue都是Collection下的接口,分别代表三个体系
List体系的特点,有序,不唯一
Set体系的特性,无序,唯一
Queue体系的特点是先入先出
队列(Queue)和栈(Stack)的区别
队列是一种FIFO(First in First Out)先入先出的结构
栈是一种FILO(First In Last Out)先入后出的结构
Java集合体系中LoinkedList类可以实现队列和栈结构
在链表头部插入,尾部取出或者尾部插入,头部取出就是队列(插入和取出在不同方向上进行)
在链表头部插入,头部取出或者尾部插入,尾部取出就是栈(插入和取出在相同方向上进行)
这里的栈是先入后出的数据结构
而虚拟机栈是栈数据结构的一种应用
ArrayList和Vector的区别(带一下CopyOnWriteArrayList)
ArrayList是线程不安全的,Vector是线程安全的,ArrayList中所有的方法都没有加同步锁,Vector中所有的方法加了synchronized同步锁,官方在JDK1.5版本中又推出了一个CopyOnWriteArrayList,使用Lock锁实现线程安全,然后弃用了Vector,因为Lock锁的性能比synchronized锁的性能更好
在并发编程中,如果多个线程共享一个ArrayList,那么必须考虑线程安全的问题,可以自己在代码中对ArrayList操作代码加锁,或者直接用线程安全的CopyOnWriteArrayList类
在不考虑线程安全的环境下,用ArrayList性能更好,因为加锁开锁是很消耗性能的
Array怎么转换为ArrayList,ArrayList怎么转换成Array
官方提供的数组工具类Arrays中提供了一个静态方法asList()可以把数组转换为List,参数是数组,返回值是List
ArrayList类中提供了toArray()方法,可以把ArrayList转换为Array后进行返回
ArrayList和LinkedList的区别是什么?
1.底层数据结构实现︰ArrayList底层数据结构是动态数组,而 LinkedList的底层数据结构是双向链表
2.随机访问(即读)效率∶ArrayList比LinkedList在随机访问的时候效率要高,因为ArrayList底层是数组,可以通过索引号快速访问,LinkedList是通过二分查找法遍历链表节点进行查找的
3.增加和删除效率∶在非首尾的增加和删除操作,LinkedList 要比 ArrayList 效率要高,因为ArrayList增删操作需要大量的前移或后移,这个过程中涉及到大量的赋值操作比较耗时间,LinkedList只需要修改节点对象的左右指针即可。
4.内存空间占用:LinkedList 比 ArrayList更占内存,因为 LinkedList的节点除了存储数据,还存储了两个引用,一个指向前一个元素,一个指向后一个元素。
5.综合来说,在需要频繁读取集合中的元素时,更推荐使用ArrayList,而在插入和删除操作较多时,更推荐使用 LinkedList。
HashSet和TreeSet的区别
HashSet和TreeSet都是Set接口下面的子类
HashSet的底层是HashMap,他将数据都存储在HashMap的key中
HahsSet是无序的,唯一的,因为HashMap的key是无序,唯一的
TreeSet的底层是TreeMap,他将数据存储在TreeMap的key中
TreeSet是有序的,唯一的,因为TreeMap的key是有序,唯一的
HashMap和Hashtable的区别(带一下ConcurrentHashMap)
HashMap是线程不安全的,Hashtable是线程安全的,HashMap中所有的方法都没有加同步锁,Hashtable中所有的方法加了synchronized同步锁,官方在JDK1.5版本中又推出了一个ConcurrentHashMap,使用Lock锁实现线程安全,然后弃用了Hashtable,因为Lock锁的性能比synchronized锁的性能更好
在并发编程中,如果多个线程共享一个HashMap,那么必须考虑线程安全的问题,可以自己在代码中对HashMap操作代码加锁,或者直接用线程安全的ConcurrentHashMap类
在不考虑线程安全的环境下,用HashMap性能更好,因为加锁开锁是很消耗性能的
对Null key和Null value支持:
HashMap支持key为null,但只能有一个
,Hashtable不支持,直接抛NPE,
HashMap和Hashtable支持value为空,不限制个数
ConcurrentHashMap的key和value都不支持Null
HashMap在1.8之后,设置了阈值为8,当链表长度超过阈值的时候,直接转换为红黑树减少探索时间,Hashtable被抛弃,没有更新
初始容量大小和扩容容量大小的区别:
HashMap默认初始容量是16,扩容策略是原来的2倍
Hashtable默认初始容量是11,扩容策略是原来的2n+1
HashMap如果手动指定了初始容量,不是2的n次方,他会找到一个最近的2的n次方作为初始容量
Hashtable如果手动指定了初始容量,会直接使用给定的大小
Hashtable采用的锁全表的机制,ConcurrentHashMap采用的分段锁的设计,锁粒度更好,性能更好
HashMap和TreeMap的区别
HashMap的底层是数组+链表/红黑树,key是无序的,唯一的
TreeMap的底层是红黑树,key是有序的,唯一的
HashMap的性能比TreeMap更好,但如果需要一个有序的key的集合,需要使用TreeMap
HashMap的底层原理(数据结构+put()流程+resize()流程)
根据key的hashCode计算出数组index
2.落槽时
1.如果数组中节点为null,创建新的节点对象,把k,v存储在节点对象中,把节点对象存储在数组中
2.如果数组的节点不为null,判断节点的key与插入元素的key是否相等
1.相等,直接用新的k ,v覆盖原节点中的k,v
2.不相等,判断此时节点是否为红黑树
1.是红黑树,创建红黑树节点对象存储k,v,插入到红黑树中
2.不是红黑树,创建链表节点对象存储k,v,插入到链表中,判断链表长度是否大于
阈值8
1.大于阈值8,链表转换为红黑树
3.判断++size是否大于阈值,是就扩容
HashMap为什么不直接使用hashCode()处理后的哈希值直接作为table的下标?
答:`hashCode()`方法返回的是int整数类型,其范围为-(2 ^ 31)~(2 ^ 31 - 1),约有40亿个映射空间,而HashMap的容量范围是在16(初始化默认值)~2 ^ 30,HashMap通常情况下是取不到最大值的,并且设备上也难以提供这么多的存储空间,从而导致通过`hashCode()`计算出的哈希值可能不在数组大小范围内,进而无法匹配存储位置;
**那怎么解决呢?**
1. HashMap自己实现了自己的`hash()`方法,通过两次扰动使得它自己的哈希值高低位自行进行异或运算,降低哈希碰撞概率也使得数据分布更平均;
2. 在保证数组长度为2的幂次方的时候,使用`hash()`运算之后的值与运算(&)(数组长度 - 1)来获取数组下标的方式进行存储,这样一来是比取余操作更加有效率,二来也是因为只有当数组长度为2的幂次方时,h&(length-1)才等价于h%length,三来解决了“哈希值与数组大小范围不匹配”的问题;
==和equals()的区别
==和equals()都可以用于比较,语法是a==b或者a.equals(b)
==比较的是内存地址
equals()方法是Object类中方法,可以被任意继承,通过看官方object类源码可以知道equals()方法默认也是用==比较内存地址
如果需要修改equals()方法的比较规则,可以重写equals()方法
String类就重写equals()方法的比较规则,有默认的比较两个字符串对象的内存地址,修改为比较字符串中每个字符串是否相等
因为堆区中可能出现两个一模一样的字符串对象,但内存地址不一样,所以字符串的比较必须用equals()方法,否则可能会出现两个内容一模一样的字符串,因为地址不一样比较后出现不相等的情况
为什么重写了hashcode()方法,必须也要重写equals()方法?
HashMap的底层采用了key的Hashcode()来计算数组的索引index,如果数组[index]为空说明key不存在,直接落槽插入
如果数组[index]部位null说明该位置有key存在,此时应该进一步用equals方法比较已存在的key要插入key是否相等。如果相等就说明一定是重复的,应该覆盖,如果不相等,说明发生了哈希碰撞,那么应该插入链表中
重写equals方法的目的是为了不去比较两个对象的内存地址,改为比较对象内容,如果应该类重写了equals,没有hashcode,就可能出现地址不同的对象,equals比较相等,但是hashcode比较不相等,这样会为发HashMap的唯一性,因此,重写了equals方法必须也要重写hashcode方法,且必须满足两个对象equals相等,hashcode也必须相等
IO流
网络编程(机器有65536个端口)
通信协议分层模型
三次握手四次挥手
TCP协议通过三次握手建立链接,通过四次挥手断开链接,而UDP协议没有
TCP协议如果握手失败,是不会发送数据包的,UDP直接发
三次握手刚好信息对等,次数再多(>3)也没有意义,但是次数少了(<3)就会造成信息不对等
四次挥手第1和2次是断开机器A的链接,3和4次是断开机器B的链接
TCP和UDP的区别
TCP和UDP都属于传输层的协议
TCP是可靠的,UDP不可靠的(UDP速度快一些)
TCP有重传机制和拥塞机制,UDP都没有
拥塞机制指的是根据当前网络状况动态调整传输速率
如果TCP协议在传输过程中发生了丢包,TCP会重传保证数据包的完整性
UDP因速度快,可用于视频会议,直播等。(过程丢包不理会)
单工、半双工、全双工
TCP是全双工
单工是单方面传输信息
半双工:双方功能一个一个实现,即A在进行发信息功能的时候,A不能收消息,B只能收,不能发消息,等A发完了才可以进行下一个功能
全双工:双方的功能都可以同时实现,双方是平等的,即A在进行发的时候可以收,B可以收也可以发
长连接和短连接
长链接更耗资源
长链接:即使不用也一直在链接,信息发送即时看见
短链接:不用接断开链接,用就连接链接,即用即连,不用就关
CS架构和BS架构
CS client-Server客户端服务器架构模型
CS架构的软件需要安装,客服端和服务器都需要更新
BS Browser-Server浏览器服务器架构模型
BS架构的软件不需要安装,只需要一个浏览器即可,更新只需服务端更新,客服端不需要
MySQL---重点部分(必须掌握)
数据类型
float和double的区别
float是单精度的double是双精度的,但是他们两个都不是最精确的
char和varchar的区别
char支持的是0-255 ,varcahr支持的长度是0-6553 (超过5000的字节将不再使用Varcahr
,因为超过5000以后varchar的性能就开始卡了,性能没有那么好了)
Char是定长字符串,varcahr是变长字符串
Char是空间换时间,varcahr是时间换空间
因为在插入的时候char不会去计算实际的长度,varcahr要去计算实际长度
所以char的性能比varchar的性能好
char(32)和varchar(32)括号里面的长度是字节还是字符
Mysql 5.0之前是字节,5.0之后是字符
假设字符集是UTF
有说明类型存储金额
严禁用float、double存储金额
解决方案
使用定点数decimal类型
使用长整型存储到金额的最小单位
超长的文本(超过5000的字节将不再使用Varcahr )和博客文章怎么存储
严禁使用varchar存储长文本,应该使用Blob和Test类型来存储文本
并且应该把该字段拆分出去,应该用独立文本存储,并且设置主外键关联,关联链接来跟我主文本建立关系,防止影响我们文本的便利性
二进制文件(图片、音频,视频)怎么存储
Mysql可以存二进制数据,但是性能很差
解决方案
二进制数据以文件的形式存储在磁盘里面,然后在数据库里面创建一个文件路径的字段来存储地址,后面通过这个文件路径来访问这个二进制文件
7.MySQL的整型支持无符号,使用关键字UNSIGNED
Age tinyint --age字段取值范围是 -128 - 127
Age tinyint unigned --age字段的取值范围是0-255
约束
非空约束NOT NULL
检查约束CHECK (MySQL不支持,应该在应用程序里面做,在MySQL做浪费性能)
唯一约束UNIQUE
主键约束PRIMARY KEY
外键约束FOREIGN KEY
SQL分类 - 语法整理(DDL | DQL | DML | DCL)
数据查询语言DQL(Data Query Language)
SELECT,FROM,WHERE,GROUP BY,ORDER BY
这个较为好理解即查询操作,以select关键字。各种简单查询,连接查询等 都属于DQL。
数据操纵语言DML(Data Manipualtion Language)
INSERT,UPDATE,DELETE
主要为以上操作即对数据进行操作的,对应上面所说的查询操作DQL与DML共同构建了多数初级程序员常用的增删改查操作。而查询是较为特殊的一种 被划分到DQL中。
事务控制语言TCL(Transaction Control Language)
COMMIT,ROLLBACK
用于操作事物,用于事物的提交和回滚操作
数据定义语言DDL(Data Ddefinition Language)
CREATE,DROP,ALTER
主要为以上操作即对逻辑结构等有操作的,其中包括表结构,视图和索引。
数据控制功能DCL(Data Control Language)
REVOKE,GRANT
用来对数据库用户授予或回收访问数据库的某种权限
连表查询的方式
关系类型和三范式、反三范式
数据库表的关系
一对一,一对多(多对一),多对多
三范式-以时间换空间(增加关系来减少冗余数据,节省空间)
反(打破)三范式-以空间换时间(允许适量的冗余数据,减少关系,不考虑节省空间,只注重性能)
事物四大特性和四种隔离级别
原子性(Atomicity):事务是最小的执行单位,不允许分割。事务的原子性确保动作要么全部完成,要么完全不起作用;
一致性(Consistency ):执行事务前后,数据保持一致,多个事务对同一个数据读取的结果是相同的;
隔离性(Isolation ):并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的;
持久性(Durability ):一个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有任何影响。
为了达到事务的四大特性,数据库定义了4种不同的事务隔离级别,由低到高依次为Read uncommitted、Read committed、Repeatable read、Serializable,这四个级别可以逐个解决脏读、不可重复读、幻读这几类问题。
隔离级别 | 脏读 | 不可重复读 | 幻读 |
READ-UNCOMMITTED | √ | √ | √ |
READ-COMMITTED | × | √ | √ |
REPEATABLE-READ | × | × | √ |
SERIALIZABLE | × | × | × |
SQL 标准定义了四个隔离级别:
READ-UNCOMMITTED(读取未提交): 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。
READ-COMMITTED(读取已提交): 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生。
REPEATABLE-READ(可重复读): 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
SERIALIZABLE(可串行化): 最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。
这里需要注意的是:Mysql 默认采用的 REPEATABLE-READ(可重复读)隔离级别 Oracle 默认采用的 READ-COMMITTED(读取已提交)隔离级别
事务隔离机制的实现基于锁机制和并发调度。其中并发调度使用的是MVVC(多版本并发控制),通过保存修改的旧版本信息来支持并发一致性读和回滚等特性。
因为隔离级别越低,事务请求的锁越少,所以大部分数据库系统的隔离级别都是READ-COMMITTED(读取提交内容):,但是你要知道的是InnoDB 存储引擎默认使用 REPEATABLE-READ(可重读)并不会有任何性能损失。
InnoDB 存储引擎在分布式事务的情况下一般会用到SERIALIZABLE(可串行化)隔离级别。
锁的种类
从锁的类别上来讲,有共享锁和排他锁。
共享锁: 又叫做读锁。 当用户要进行数据的读取时,对数据加上共享锁。共享锁可以同时加上多个。
排他锁: 又叫做写锁。 当用户要进行数据的写入时,对数据加上排他锁。排他锁只可以加一个,他和其他的排他锁,共享锁都相斥。
行级锁 行级锁是Mysql中锁定粒度最细的一种锁,表示只针对当前操作的行进行加锁。行级锁能大大减少数据库操作的冲突。其加锁粒度最小,但加锁的开销也最大。行级锁分为共享锁 和 排他锁。
特点:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。
表级锁 表级锁是MySQL中锁定粒度最大的一种锁,表示对当前操作的整张表加锁,它实现简单,资源消耗较少,被大部分MySQL引擎支持。最常使用的MYISAM与INNODB都支持表级锁定。表级锁定分为表共享读锁(共享锁)与表独占写锁(排他锁)。
特点:开销小,加锁快;不会出现死锁;锁定粒度大,发出锁冲突的概率最高,并发度最低。
页级锁 页级锁是MySQL中锁定粒度介于行级锁和表级锁中间的一种锁。表级锁速度快,但冲突多,行级冲突少,但速度慢。所以取了折中的页级,一次锁定相邻的一组记录。
特点:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般
锁的粒度取决于具体的存储引擎,InnoDB实现了行级锁,页级锁,表级锁。
他们的加锁开销从大到小,并发能力也是从大到小。
MySQL中InnoDB引擎的行锁是怎么实现的?
答:InnoDB是基于索引来完成行锁
例: select * from tab_with_index where id = 1 for update;
for update 可以根据条件来完成行锁锁定,并且 id 是有索引键的列,如果 id 不是索引键那么InnoDB将完成表锁,并发将无从谈起
InnoDB存储引擎的锁的算法有三种
Record lock:单个行记录上的锁
Gap lock:间隙锁,锁定一个范围,不包括记录本身
Next-key lock:record+gap 锁定一个范围,包含记录本身
死锁、如何防止死锁
死锁是指两个或多个事务在同一资源上相互占用,并请求锁定对方的资源,从而导致恶性循环而出现的
-- 1. 我们如何从代码逻辑层面避免死锁的出现
. 尽量让所有的事务获取资源的顺序保持一致
. 可以在事务中一次性锁定所有需要的资源
. 提供锁的粒度, 行锁提升为表锁 (不建议使用, 会严重影响数据库的并发性能)
. 使用乐观锁
-- 2. MySQL数据库自带死锁检测和快速失败机制,当某个事务进入锁等待的时候, MySQL会检测是否会产生死锁
-- 如果产生了死锁, MySQL会立即结束当前事务, 并释放当前事务内占用的所有资源上的锁
-- 悲观锁和乐观锁
-- 数据库管理系统(DBMS)中的并发控制的任务是确保在多个事务同时存取数据库中同一数据时不破坏事务的隔离性和统一性以及数据库的统一性。
-- 乐观并发控制(乐观锁)和悲观并发控制(悲观锁)是并发控制主要采用的技术手段。
-- 悲观锁:比较悲观, 假定会发生并发冲突,屏蔽一切可能违反数据完整性的操作。在查询完数据的时候就把事务锁起来,直到提交事务。
-- 实现方式:使用数据库中的锁机制, 也就是说数据库默认就是悲观锁
-- 乐观锁:比较乐观, 假设不会发生并发冲突,只在提交操作时检查是否违反数据完整性。在修改数据的时候把事务锁起来,通过version版本的方式来进行锁定。
-- 实现方式:乐观锁一般会使用版本号机制或CAS算法实现。
-- 两种锁的使用场景
-- 从上面对两种锁的介绍,我们知道两种锁各有优缺点,不可认为一种好于另一种
-- 像乐观锁适用于写比较少,读比较多的场景,即冲突真的很少发生的时候,这样可以省去了锁的开销,加大了系统的整个吞吐量。
-- 但如果是写比较多, 读比较少的情况,一般会经常产生冲突,这就会导致上层应用会不断的进行retry重试,这样反倒是降低了性能,所以这种场景下用悲观锁就比较合适。
索引
索引是一种特殊的文件(InnoDB数据表上的索引是表空间的一个组成部分),它们包含着对数据表里所有记录的引用指针。
索引是一种数据结构。数据库索引,是数据库管理系统中一个排序的数据结构,以协助快速查询、更新数据库表中数据。索引的实现通常使用B树及其变种B+树。
更通俗的说,索引就相当于目录。为了方便查找书中的内容,通过对内容建立索引形成目录。索引是一个文件,它是要占据物理空间的。
索引的优点
可以大大加快数据的检索速度,这也是创建索引的最主要的原因。
通过使用索引,可以在查询的过程中,使用优化隐藏器,提高系统的性能。
索引的缺点
时间方面:创建索引和维护索引要耗费时间,具体地,当对表中的数据进行增加、删除和修改的时候,索引也要动态的维护,会降低增/改/删的执行效率;
空间方面:索引需要占物理空间。
索引使用场景(重点)
当有Where 、order by、join等语句的时候,我们就需要用到索引
如果我们对该字段建立索引alter table 表名 add index(字段名),那么由于索引本身是有序的,因此直接按照索引的顺序和映射关系逐条取出数据即可。而且如果分页的,那么只用取出索引表某个范围内的索引对应的数据,而不用像上述那取出所有数据进行排序再返回某个范围内的数据。(从磁盘取数据是最影响性能的)
索引覆盖
如果要查询的字段都建立过索引,那么引擎会直接在索引表中查询而不会访问原始数据(否则只要有一个字段没有建立索引就会做全表扫描),这叫索引覆盖。因此我们需要尽可能的在select后只写必要的查询字段,以增加索引覆盖的几率。
这里值得注意的是不要想着为每个字段建立索引,因为优先使用索引的优势就在于其体积小。
索引有哪几种类型?
主键索引: 数据列不允许重复,不允许为NULL,一个表只能有一个主键。
唯一索引: 数据列不允许重复,允许为NULL值,一个表允许多个列创建唯一索引。
可以通过ALTER TABLE table_name ADD UNIQUE (column); 创建唯一索引
可以通过ALTER TABLE table_name ADD UNIQUE (column1,column2); 创建唯一组合索引
普通索引: 基本的索引类型,没有唯一性的限制,允许为NULL值。
可以通过ALTER TABLE table_name ADD INDEX index_name (column);创建普通索引
可以通过ALTER TABLE table_name ADD INDEX index_name(column1, column2, column3);创建组合索引
全文索引:是目前搜索引擎使用的一种关键技术。
可以通过ALTER TABLE table_name ADD FULLTEXT (column);创建全文索引
索引的基本原理
索引用来快速地寻找那些具有特定值的记录。如果没有索引,一般来说执行查询时遍历整张表。索引的原理很简单,就是把无序的数据变成有序的查询
把创建了索引的列的内容进行排序
对排序结果生成倒排表
在倒排表内容上拼上数据地址链
在查询的时候,先拿到倒排表内容,再取出数据地址链,从而拿到具体数据
索引设计的原则?
适合索引的列是出现在where子句中的列,或者连接子句中指定的列
2. 基数较小的类,索引效果较差,没有必要在此列建立索引
3. 使用短索引,如果对长字符串列进行索引,应该指定一个前缀长度,这样能够节省大量索引空间
4. 不要过度索引。索引需要额外的磁盘空间,并降低写操作的性能。在修改表内容的时候,索引会进行更新甚至重构,索引列越多,这个时间就会越长。所以只保持需要的索引有利于查询即可。
创建索引的原则(重中之重)
索引虽好,但也不是无限制的使用,最好符合一下几个原则
1) 最左前缀匹配原则,组合索引非常重要的原则,mysql会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配,比如a = 1 and b = 2 and c > 3 and d = 4 如果建立(a,b,c,d)顺序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引则都可以用到,a,b,d的顺序可以任意调整。
2)较频繁作为查询条件的字段才去创建索引
3)更新频繁字段不适合创建索引
4)若是不能有效区分数据的列,不适合做索引列(如性别,男女未知,最多也就三种,区分度实在太低)
5)尽量的扩展索引,不要新建索引。比如表中已经有a的索引,现在要加(a,b)的索引,那么只需要修改原来的索引即可。
6)定义有外键的数据列一定要建立索引。
7)对于那些查询中很少涉及的列,重复值比较多的列不要建立索引。
8)对于定义为text、image和bit的数据类型的列不要建立索引。
创建索引的三种方式
第一种方式:在执行CREATE TABLE时创建索引
第二种方式:使用ALTER TABLE命令去增加索引
ALTER TABLE用来创建普通索引、UNIQUE索引或PRIMARY KEY索引。
其中table_name是要增加索引的表名,column_list指出对哪些列进行索引,多列时各列之间用逗号分隔。
索引名index_name可自己命名,缺省时,MySQL将根据第一个索引列赋一个名称。另外,ALTER TABLE允许在单个语句中更改多个表,因此可以在同时创建多个索引。
第三种方式:使用CREATE INDEX命令创建
CREATE INDEX可对表增加普通索引或UNIQUE索引。(但是,不能创建PRIMARY KEY索引)
删除索引
根据索引名删除普通索引、唯一索引、全文索引:alter table 表名 drop KEY 索引名
删除主键索引:alter table 表名 drop primary key(因为主键只有一个)。这里值得注意的是,如果主键自增长,那么不能直接执行此操作(自增长依赖于主键索引):
需要取消自增长再行删除:
但通常不会删除主键,因为设计主键一定与业务逻辑无关。
什么是最左前缀原则?什么是最左匹配原则
顾名思义,就是最左优先,在创建多列索引时,要根据业务需求,where子句中使用最频繁的一列放在最左边。
最左前缀匹配原则,非常重要的原则,mysql会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配,比如a = 1 and b = 2 and c > 3 and d = 4 如果建立(a,b,c,d)顺序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引则都可以用到,a,b,d的顺序可以任意调整。
=和in可以乱序,比如a = 1 and b = 2 and c = 3 建立(a,b,c)索引可以任意顺序,mysql的查询优化器会帮你优化成索引可以识别的形式
创建索引时需要注意什么?
非空字段:应该指定列为NOT NULL,除非你想存储NULL。在mysql中,含有空值的列很难进行查询优化,因为它们使得索引、索引的统计信息以及比较运算更加复杂。你应该用0、一个特殊的值或者一个空串代替空值;
取值离散大的字段:(变量各个取值之间的差异程度)的列放到联合索引的前面,可以通过count()函数查看字段的差异值,返回值越大说明字段的唯一值越多字段的离散程度高;
索引字段越小越好:数据库的数据存储以页为单位一页存储的数据越多一次IO操作获取的数据越大效率越高。
B树和B+树的区别
在B树中,你可以将键和值存放在内部节点和叶子节点;但在B+树中,内部节点都是键,没有值,叶子节点同时存放键和值。
B+树的叶子节点有一条链相连,而B树的叶子节点各自独立。
索引的优缺点
时间:
可以大大加快数据的检索速度,
在增删改的时候,为了保证索引的有序性,需要动态的维护索引,牺牲了增删改的速度
空间方面:
索引需要占物理空间。
索引的类型
4大类
主键索引:关键字:primary key
唯一索引:关键字:unique
普通索引(非唯一索引)关键字:index
全文索引:关键字:fulltext
创建删除索引的语法
(聚簇索引=主键索引是表自带的,我们自己创建的都是非聚簇索引)
(如果表没有主键,那么会选择隐藏系列rowid来创建聚簇索引)
创建
在create table建表的时候添加索引
在create table建表成功以后,使用alter语句添加索引
ALTER TABLE 表名ADD INDEX表索引(字段名)
在create table 建表成功以后,只要create index语句添加
CREATE INDEX 索引名ON 表名(字段名)
删除
使用alter语句删除索引
ALTER TABLE 表名 DROP INDEX索引名(字段名)
使用drop语句删除索引
DROP INDEX索引名ON表名(字段名)
聚簇索引=主键索引=一级索引
非聚簇索引=(唯一索引,普通索引)=二级索引
MySQL----了解部分
存储引擎的区别
InnoDB
MyIsam
Memory
关系型数据库和NoSQL(关系型数据库)数据库的区别
主流的关系型数据库:MySQL Oracle
主流的NoSQL 数据库Redis
MySQL和Redis的关系不是谁替代谁,而是在数据量非常大,数据库并发操作非常频繁的情况下,可以通过Redis来搭建缓存服务器分担MySQL的压力