一、.面向对象OOP
2.1 面向对象与面向过程
两者都是一种编程的思想
面向对象强调的是事情的结果,我们通过对象完成对应的功能
面向过程强调的是事情的过程,我们做任何事情,都要亲力亲为,经过每一个步骤
Java是一门面向对象的语言
2.2 类与对象
定义类通过关键字class来定义,类是一类事物的抽象,它是抽象的,它是模板
创建对象通过new关键字触发构造函数生成,对象是根据类创建出来的具体的内容
一个类可以创建出多个对象,对象是根据类的设计来创建的,所以对象具有类的所有属性与功能
对象之间是相互独立的,互不影响。我们把创建对象也称作“实例化”
2.3 面向对象的三大特性:封装
前提:为了保证数据的安全,也为了程序的使用者能够按照我们预先设计好的方式来使用资源
封装属性:用private修饰我们的属性
然后为属性提供对应的getXxx()【获取属性值】与setXxx()【设置属性值】
封装方法:用private修饰方法,被修饰的方法只能在本类中使用,所以我们在本类的公共方法里调用这个私有方法
外界如果想要使用这个私有方法的功能,只需要调用这个公共方法就可以了
2.4 面向对象的三大特性:继承
前提 :继承可以实现程序的复用性,减少代码的冗余
我们通过extends关键字建立子类与父类的继承关系:格式:子类 extends 父类
继承相当于子类把父类的功能复制了一份,包括私有资源
注意:虽然私有资源继承了,但是私有资源不可用,原因是被private限制了访问,私有资源只能在本类使用
注意:构造方法不能继承,原因是:构造方法要求名字是本类的类名,我们不能在子类中出现父类名字的构造方法
继承是可以传递的:爷爷的功能会传给爸爸,爸爸的功能会传给孙子
注意:爸爸从爷爷那里继承的功能,也会一起传给孙子
Java的类是单继承的:一个子类只能有一个父类,但是一个父类可以有多个子类
子类在继承了父类以后,如果对父类的功能不满意
可以在不修改父类功能的【满足OCP原则】前提下,在子类中,重写继承过来的这个方法
重写需要满足的规则:两同 两小 一大,我们可以在重写的方法上加@Override注解验证是否写对
继承是一种is a的关系,强耦合,关联性特别强,而且类的继承机会只有一次,要谨慎使用
子类可以直接使用父类的所有非私有资源
2.5 面向对象的三大特性:多态
前提:为了忽略子类型之间的差异,统一看作父类类型,写出更加通用的代码
比如:把Cat看作Animal,把Dog看作Animal,把Bird看作Animal,如果方法需要设置传入的参数,可以buy(Animal a)
比如:把算术异常、输入不匹配异常都看作是Exception,统一捕获处理,只写一个解决方案
概念:在同一时刻,同一个对象,代表的类型不同,拥有多种形态
6.多态的要求:继承 + 重写
多态的口诀1:父类引用指向子类对象:父类型的引用类型变量保存的是子类对象的地址值
多态的口诀2:编译看左边,运行看右边:
父类中定义的功能,子类才能使用,否则报错
多态中,方法的定义看的是父类的,方法的实现看的是子类重写后的功能;
7.多态中资源的使用:
1)成员变量:使用的是父类的
2)成员方法:对于方法的定义看的都是父类的,对于方法实现,重写后使用的是子类的
3)静态资源:静态资源属于类资源,不存在重写的概念,在哪个类中定义的,就属于哪个类
向上造型与向下造型
1)这两种都属于多态,只不过是多态的两种不同的表现形式
2)向上造型【最常用】
可以把不同的子类型都看作是父类型,比如Parent p = new Child();
比如:花木兰替父从军,被看作是父类型,并且花木兰在从军的时候,不能使用自己的特有功能,比如化妆
3)向下造型
前提:必须得先向上造型,才能向下造型
子类的引用指向子类的对象,但是这个子类对象之前被看作是父类类型,所以需要强制类型转换
Parent p = new Child(); 然后:Child c = (Child) p;
比如:花木兰已经替她爸打完仗了,想回家织布,那么这个时候,一直被看作是父类型的花木兰必须经历“解甲归田”【强制类型转换】这个过程,才能重新被看作成子类类型,使用子类的特有功能
为什么有向下造型:之前被看作是父类类型的子类对象,想使用子类的特有功能,那就需要向下造型
2.7 this与super
this代表的是本类,super代表的是父类
当本类的成员变量与局部变量同名时,我们可以通过this.变量名指定本类的成员变量
当父类的成员变量与子类的变量同名时,我们可以通过super.变量名指定父类的成员变量
我们可以在本类构造函数的第一行
使用this();调用本类的无参构造 / 使用this(参数); 调用本类对应参数的构造方法
构造函数的调用只有这一种方式,或者创建对象时被动触发,不能在外面自己主动调用
构造函数直接不能互相调用,否则会死循环
我们可以在子类构造函数的第一行
使用super();调用父类的无参构造 / 使用super(参数); 调用父类对应参数的构造方法
注意:子类默认调用super();父类的无参构造,如果父类没有无参构造,需要手动指定调用哪个含参构造
2.9 方法的重写与重载
方法的重写:
继承后,子类想在不改变父类代码的前提下,修改或者拓展功能
重写的规则:两同 两小 一大
1. 子类与父类的方法名要保持一致
2. 子类与父类的方法的参数列表要保持一致
3. 子类的返回值类型小于等于父类方法的返回值,这里说的是继承关系,不是值的大小
4. 子类的抛出异常类型小于等于父类方法抛出的异常类型
5. 子类方法的修饰符大于等于父类方法的修饰符
6. 注意:我们可以使用@Override注解标记这是一个重写的方法
方法的重载:
在同一个类中,存在两个或者两个以上的方法,方法名相同,但参数列表不同的现象
作用:重载提高程序的灵活性,只要用户调用这个方法,不管传入什么参数,都能匹配的上
注意:方法是否构成重载,除了前两项以外,看的是参数的个数与类型,与方法的参数名字没关系
3.7 接口与抽象类的区别
接口是一种用interface定义的类型
抽象类是一种用class定义的类型
接口中的方法都是抽象方法
抽象类中的方法不做限制
接口中的都是静态常量
抽象类中可以写普通的成员变量
接口没有构造方法,不可实例化
抽象类有构造方法,但是也不可以实例化
接口是先天设计的结果,抽象是后天重构的结果
接口可以多继承
抽象类只能单继承
3.7 异常
异常的继承结构
异常层次结构中的根是Throwable
Error:目前我们编码解决不了的问题
Exception:异常
编译异常:未运行代码就报错了, 那你
运行时异常RunTimeException:运行代码才报错,可以通过编译,不强制要求处理
异常的解决方案
捕获处理try-catch–自己解决
向上抛出throws–交给别人解决,在方法定义的两个小括号之间throws,可抛出多个异常,用逗号隔开
不能直接把异常抛给main(),因为调用main()是JVM,没人解决了
注意:是否抛出异常取决于自己的业务,比如暂时不处理或者处理不了需要交给别人处理
成员内部类
位置:类里方法外
1)被private修饰
被私有化的内部类在main()中是没有办法直接创建其对象的
可以在私有内部类所处的外部类中,创建一个公共的方法供外界调用,这个方法用来返回创建好的私有内部类对象
2) 被static修饰
静态内部类可以不创建外部类对象,直接创建静态内部类对象,格式:Outer3.Inner3 oi = new Outer3.Inner3();
如果静态内部类中还有静态方法,那么我们可以不创建对象
直接通过链式加载的方式调用:Outer3.Inner3.show2();//表示通过外部类名直接找到静态内部类,再找到静态方法
局部内部类
位置:方法里
直接创建外部类对象,调用局部内部类所处的方法,并不会触发局部内部类的功能
需要在外部类中创建局部内部类的对象并且进行调用局部内部类的功能,才能触发内部类的功能
匿名内部类
位置:可运行代码中,比如 main()中
匿名内部类通常与匿名对象【没有名字的对象】一起使用
格式:new Inter1(){ 我这个大括号其实是一个匿名内部类,我来实现方法 }.eat();
如果只是想使用一次接口/抽象类的某个功能,可以使用匿名内部类
匿名内部类+匿名对象的功能:创建实现类+实现方法+方法功能的一次调用【功能三合一】
3 基础API
1.equals和==的区别
等号比较的其实是地址值;
equals比较的是值是否相同,equals:1、先比较地址,通过返回true ,不通过再判断括号内的对象是不是string类型,如果不是string类型返回false,如果是string类型继续判断两个字符串的长度,如果长度不相等返回false,如果长度相等继续对字符串里面的每个字母进行对比,如果有一个字母不相同就返回false直到对比到最后一个字母都没有问题,然后返回true
3.2 String
String底层维护的是一个char[],而且String不可变,因为源码中的数组被final修饰了
创建方式:
char[] vlaues = {‘a’,‘b’,‘c’}; String s = new String(values);
String s = “abc”;有高效的效果,因为串存在堆中的常量池,第二次使用时就不再新建了
- 常用方法:
int hashCode() 返回此字符串的哈希码。
boolean equals(Object anObject) 将此字符串与指定的对象比较,比较的是重写后的串的具体内容
String toString() 返回此对象本身(它已经是一个字符串!)。
int length() 返回此字符串的长度。
String toUpperCase() 所有字符都转换为大写。
String toLowerCase() 所有字符都转换为小写
boolean startsWith(String prefix) 测试此字符串是否以指定的元素开头。
boolean endsWith(String suffix) 测试此字符串是否以指定的字符串结束。
char charAt(int index) 返回指定索引/下标处的 char 值/字符
int indexOf(int ch) 返回指定字符在此字符串中第一次出现处的索引。
int lastIndexOf(int ch) 返回指定字符在此字符串中最后一次出现处的索引。
String concat(String str) 将指定字符串连接/拼接到此字符串的结尾,注意:不会改变原串
String[] split(String regex) 根据给定元素来分隔此字符串。
String trim() 返回去除首尾空格的字符串
byte[] getBytes() 把字符串存储到一个新的 byte 数组中
String substring(int beginIndex) 返回一个新子串,从指定下标处开始,包含指定下标
String substring(int beginIndex, int endIndex) 返回一个新子串,从执定下标开始,到结束下标为止,但不包含结束下标
static String valueOf(int i) 把int转成String
3.3 StringBuilder与StringBuffer
String的:
特点:
创建之后长度内容是不可变的,每次拼接字符串,都会产生新的对象
优缺点:
优点:String类提供了丰富的关于操作字符串的方法,比如:拼接、获取对应下标处的字符、截取子串等等
缺点:在进行字符串拼接+=的时候,效率比较低
String转StringBuilder:
String s = “abc”; StringBuilder sb = new StringBuilder(s);
StringBuilder的:
特点:
StringBuilder是一个长度可变的字符串序列,在创建的时候,会有一个长度为16的默认空间
当拼接字符串的时候,是在原对象的基础之上进行拼接,如果长度不够就扩容
所以StringBuilder在创建之后,对应的操作一直是用一个对象
创建方式:
StringBuilder sb = new StringBuilder();//创建一个长度为16的StringBuilder对象
StringBuilder sb = new StringBuilder(“abc”);//以指定字符串内容为“abc”的方式创建一个StringBuilder对象
优缺点:
优点:在拼接的时候,不会产生新的对象,就避免了因为拼接频繁生成对象的问题,提高了程序的效率,使用的是append()
缺点:对于字符串的操作,不太方便
StringBuilder转String:
StringBuilder sb = new StringBuilder();
sb.append(“abc”);
String s = sb.toString();
总结一句话,拼接多用StringBuilder,用完转回String用String丰富的方法
在线程安全上 :
–StringBuffer是线程安全的。
–StringBuilder是线程不安全的。
2. 在执行效率上,StringBuilder > StringBuffer > String
3.源码体现:只不过Buffer把代码加了同步关键字,使得程序可以保证线程安全问题。
List接口的特点
-
- List集合是有下标的
- List集合是有顺序的
- List集合可以存放重复的数据
List接口的两个常用实现类
ArrayList的特点:
1)底层的数据结构是数组,内存空间是连续的
2)元素有下标,通常可以根据下标进行操作
3)增删操作比较慢,查询操作比较快【数据量大时】
- 内部是用数组结构存放数据,封装数组的操作,每个对象都有下标
- 内部数组默认的初始容量是10,如果不够会以1.5倍的容量增长
- 查询快,增删数据效率会低
LinkedList的特点:
1)底层的数据结构是链表,内存空间是不连续的
2)元素有下标,但是通常首尾节点操作比较多
3)增删操作比较快,查询操作比较慢【数据量大时】
注意:LinkedList查询慢也不是都慢,首尾操作还是比较快的
Map接口的特点
- map集合的结构是:键值对、KEY与VALUE、Map.Entry<K,V>的映射关系
- map中key值不允许重复,如果重复,对应的value会被覆盖
- map中的映射关系是无序的
- map没有自己的迭代器,所以迭代时通常需要转成set集合来迭代
Set接口的特点
- set集合没有重复的元素
- set集合的元素是无序的
- set集合可以存null值,并且null最多有一个
- 我们自定义对象如果想去重,需要在自定义类中添加重写的equals()与hashCode()
线程的几种状态以及线程状态之间的切换
1)新建状态:创建线程对象,申请PCB,对应的是new线程对象
2)就绪状态/可运行状态:万事俱备,只欠CPU,刚刚创建好的线程对象所有资源已经准备好,并且加入到了就绪队列之中
唯有等待操作系统的调度,只要分配了CPU,也就是时间片,当前线程可立即执行,对应的是start()
注意:调用start()并不会立即执行线程对象,这个是由OS的调度规则决定的。我们控制不了
3)执行/运行状态:就绪队列中的线程对象被OS选中,分配了时间片,正在执行
注意:只有就绪状态才能变成运行状态
4)阻塞状态:线程在执行过程中遇到了问题,比如锁阻塞、休眠阻塞、等待阻塞…
注意:我们的阻塞状态,等问题解决了以后/获取了临界资源【要抢占的公共资源】后
是加入到就绪队列中的,转为就绪状态,而不是转为运行状态直接执行
5)终止状态:线程成功执行完毕,释放资源,归还PCB
6)线程的挂起:正在运行中的线程,由于CPU分配的时间片已经用完,所以需要冻结当前线程运行的状态与各项信息
把它插入到就绪队列中,直到下次这个线程被调度执行时,重新恢复现场,继续执行
44. 创建线程池有哪几种方式?
线程池创建有七种方式,最核心的是最后一种:
newSingleThreadExecutor():它的特点在于工作线程数目被限制为 1,操作一个无界的工作队列,所以它保证了所有任务的都是被顺序执行,最多会有一个任务处于活动状态,并且不允许使用者改动线程池实例,因此可以避免其改变线程数目;
newCachedThreadPool():它是一种用来处理大量短时间工作任务的线程池,具有几个鲜明特点:它会试图缓存线程并重用,当无缓存线程可用时,就会创建新的工作线程;如果线程闲置的时间超过 60 秒,则被终止并移出缓存;长时间闲置时,这种线程池,不会消耗什么资源。其内部使用 SynchronousQueue 作为工作队列;
newFixedThreadPool(int nThreads):重用指定数目(nThreads)的线程,其背后使用的是无界的工作队列,任何时候最多有 nThreads 个工作线程是活动的。这意味着,如果任务数量超过了活动队列数目,将在工作队列中等待空闲线程出现;如果有工作线程退出,将会有新的工作线程被创建,以补足指定的数目nThreads;
newSingleThreadScheduledExecutor():创建单线程池,返回ScheduledExecutorService,可以进行定时或周期性的工作调度;
newScheduledThreadPool(int corePoolSize):和newSingleThreadScheduledExecutor()类似,创建的是个ScheduledExecutorService,可以进行定时或周期性的工作调度,区别在于单一工作线程还是多个工作线程;
newWorkStealingPool(int parallelism):这是一个经常被人忽略的线程池,Java 8 才加入这个创建方法,其内部会构建 ForkJoinPool,利用 Work-Stealing 算法,并行地处理任务,不保证处理顺序;
ThreadPoolExecutor():是最原始的线程池创建,上面 1-3 创建方式都是对 ThreadPoolExecutor 的封装。
补充:Java线程池七个参数
分别是corePoolSize、maximumPoolSize、keepAliveTime、unit、workQueue、threadFactory、handler。
一、corePoolSize 线程池核心线程大小
线程池中会维护一个最小的线程数量,即使这些线程处于空闲状态,他们也不会被销毁,除非设置了allowCoreThreadTimeOut。这里的最小线程数量即是corePoolSize。
二、maximumPoolSize 线程池最大线程数量
一个任务被提交到线程池以后,首先会找有没有空闲存活线程,如果有则直接将任务交给这个空闲线程来执行,如果没有则会缓存到工作队列(后面会介绍)中,如果工作队列满了,才会创建一个新线程,然后从工作队列的头部取出一个任务交由新线程来处理,而将刚提交的任务放入工作队列尾部。线程池不会无限制的去创建新线程,它会有一个最大线程数量的限制,这个数量即由maximunPoolSize指定。
三、keepAliveTime 空闲线程存活时间
一个线程如果处于空闲状态,并且当前的线程数量大于corePoolSize,那么在指定时间后,这个空闲线程会被销毁,这里的指定时间由keepAliveTime来设定。
四、unit 空闲线程存活时间单位
keepAliveTime的计量单位
五、workQueue 工作队列
新任务被提交后,会先进入到此工作队列中,任务调度时再从队列中取出任务。
六、threadFactory 线程工厂
创建一个新线程时使用的工厂,可以用来设定线程名、是否为daemon线程等等
七、handler 拒绝策略
当工作队列中的任务已到达最大限制,并且线程池中的线程数量也达到最大限制,这时如果有新任务提交进来,该如何处理呢。这里的拒绝策略,就是解决这个问题的。
RUNNING(运行):这是最正常的状态,接受新的任务,处理等待队列中的任务。
SHUTDOWN(关闭):不接受新的任务提交,但是会继续处理等待队列中的任务。
STOP(停止):不接受新的任务提交,不再处理等待队列中的任务,中断正在执行任务的线程。
TIDYING(tidying整理):所有的任务都销毁了,workCount 为 0,线程池的状态在转换为 TIDYING 状态时,会执行钩子方法 terminated()。
TERMINATED(terminated结束):terminated()方法结束后,线程池的状态就会变成这个。
46. 线程池中 submit() 和 execute() 方法有什么区别?
execute():只能执行 Runnable 类型的任务。
submit():可以执行 Runnable 和 Callable 类型的任务。
Callable 类型的任务可以获取执行的返回值,而 Runnable 执行无返回值。
注意Lambda表达式的前提:接口+接口中有且仅有一个抽象方法
BIO、NIO、AIO 有什么区别?
BIO:Block IO 同步阻塞式 IO,就是我们平常使用的传统 IO,它的特点是模式简单使用方便,并发处理能力低。
NIO:New IO 同步非阻塞 IO,是传统 IO 的升级,客户端和服务器端通过 Channel(通道)通讯,实现了多路复用。
AIO:Asynchronous IO 是 NIO 的升级,也叫 NIO2,实现了异步非堵塞 IO ,异步 IO 的操作基于事件和回调机制。
4、事务
事务是一个业务,也可以看成是一个逻辑工作单元,是为了保证业务的完整,数据的正确而推出的一种控制机制,原则上来讲,事务必须要满足ACID四个特性(原子性,一致性,隔离性,持久性)。
事务4个特性ACID
一般来说,事务是必须满足4个条件(ACID):原子性(Atomicity,或称不可分割性)、一致性(Consistency)、隔离性(Isolation,又称独立性)、持久性(Durability)。
原子性:一个事务(transaction)中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中如果发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。
一致性:在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设规则,这包含资料的精确度、串联性以及后续数据库可以自发性地完成预定的工作。
隔离性:数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。
持久性:事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。
简化版:
事务特性
- 原子性: 原子不可分割.事务处理要么同时成功,要么同时失败.
- 一致性: 多线程条件下,数据应该保持一致.
- 隔离性: 多线程条件下,操作数据时,操作是独立互不干扰.
- 持久性: 将数据最终处理之后,持久化到数据库中.
谐音:日本名字 “原一隔持”
隔离级别
事务隔离分为不同级别,包括
读未提交(Read uncommitted) 安全性最差,可能发生并发数据问题,性能最好
读提交(read committed) Oracle默认的隔离级别
可重复读(repeatable read)MySQL默认的隔离级别,安全性较好,性能一般
串行化(Serializable) 表级锁,读写都加锁,效率低下,安全性高,不能并发
SQL语句优化
- 索引是数据库优化
- 表的主键会默认自动创建索引
- 每个字段都可以被索引
- 大量降低数据库的IO磁盘读写成本,极大提高了检索速度
- 索引事先对数据进行了排序,大大提高了查询效率
SpringBoot
好处:
1.简化了Maven的操作,以前自己找jar包的坐标,现在直接创建springboot工程勾选你要的功能
2.SpringBoot项目可以快速启动/关闭,就是像服务器(Tomcat)一样的操作.被整合了
3.简单快速整合其他技术
@SpringBootApplication 的三个注解
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
SpringMVC框架
–1,概述
是Spring团队的产品,遵循MVC设计模式
MVC设计模式: 最终实现松耦合
M是Model是模型层,用来封装数据
V是View是视图层,用来展示数据
C是Controller是控制层, 接受浏览器发来的请求,并做出数据的响应
SpringMVC框架用来接受请求 + 做出响应
--2,工作原理
涉及五个组件:
1,前端控制器DispatcherServlet: 接受请求,并且调度
2,处理器映射器HandlerMapping: 根据地址栏的写法,找到能处理这次请求的类和方法
3,处理器适配器HandlerAdapter: 真正开始找到方法,执行方法体处理业务,并返回结果
4,视图解析器ViewResolver: 找到能够展示数据的页面
5,视图渲染View: 把数据展示在页面上
Spring框架
1,概述
Spring框架可以和其他技术无缝衔接
BeanFactory: bean工厂, spring框架认为所有类都是bean. 从bean工厂可以获取每个bean
IOC: 控制反转, 不需要程序员来创建对象了,交给Spring框架来管理对象(从初始化…销毁).程序员可以直接从 Spring框架中获取Bean的对象
DI: 依赖注入,使用Spring框架明确两个对象间的依赖关系
AOP: 面向切面编程,是一种思想,解决了OOP的不足
IOC的注解方式
常用的IOC注解有四个: 功能都一样,都是交给spring框架创建对象
@Component @Service @Controller @Repository
AOP
1, 切面Aspect: 就是一个类
2, 通知Advice: 类里的方法,分类:
前置通知, 是指调用业务方法前会被执行的功能(适用于权限管理,缓存管理)
后置通知, 是指调用业务方法后会被执行的功能(适用于释放资源)
环绕通知, 是指调用业务方法的前 后 会被执行的功能(适用于事务管理,性能分析)
返回后通知, 是指调用业务方法并返回了结果后 会被执行的功能
异常通知, 是指调用业务方法并抛出异常后 会被执行的功能
3,切点PointCut: 触发通知执行的那个方法的时间点
properties语法说明
语法说明:
1. 数据结构 key=value
2. value中前后不要有空格
3. properties文件 程序默认读取采用ISO-8859-1编码结构 中文必定乱码.
4. pro文件中key的名称不能复用.
YML语法说明
语法说明:
1. 数据结构 key:(空格)value
2. key的关键字有层级缩进效果, 注意缩进.
3. YML文件默认采用UTF-8编码格式 所以对中文友好.
4. value中不要有多余的空格
5. YML文件中key的前缀可以复用. 注意层级
注解说明:
@RestController = @Controller + @ResponseBody
效果: 1. @Controller 将当前类交给Spring容器管理
2. @ResponseBody 前后端交互时,将后端服务器返回的对象转化为JSON
前后端交互媒介 http协议 传输的数据都是字符串
JSON: 有特殊格式的字符串
@Value("${cgbname}")
用:从Spring容器中获取数据,需要指定key , $ springel spel表达式.
Spring容器如何理解
关于IOC的说明
IOC: 控制反转
具体含义: 将对象创建的权利交给Spring容器管理.
原因: 如果将对象自己管理,则必然出现耦合性高的现象. 不方便扩展
容器: 是一种数据结构类型 Map<K,V>集合
KEY: 类名首字母小写
Value: Spring为当前的类创建的对象.
只要程序启动成功,则Map集合中(容器),里边包含了所有的IOC管理的对象
@Bean注解
注解说明: @Bean注解是Spring 专门为管理自定义对象 研发的注解.
用法区域: 在配置类文件中使用
功能: 被@Bean修饰的方法,将方法名当做key--user,将返回值对象当做值value * 根据Key-value 存储到Map集合中 交给Spring容器管理 * Map<user,User对象>
Mybatis
MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。
总结: MyBatis 是一款优秀的持久层框架,利用ORM思想实现了数据库持久化操作.
补充说明: 也有人把mybatis称之为半自动化的ORM映射框架.
ORM思想
对象关系映射(英语:Object Relational Mapping,简称ORM,或O/RM,或O/R mapping),是一种程序设计技术,用于实现面向对象编程语言里不同类型系统的数据之间的转换。
关系映射:
1. 对象映射-------映射表
2. 对象中有属性-------表中有字段
总结: 以对象的方式操作数据库.
resultType:
要求: 对象的属性名称与表中的字段一一对应.
对象: User(id,name,age,sex)
表: demo_user(id,name,age,sex)
总结: resultType适合单表映射,并且属性名称一致.
resultMap:
功能:1. 如果发现表中的字段与属性名称不一致时,使用resultMap映射
对象: Dog(dogId,dogName)
表: dog(dog_id,dog_name) 属性不匹配.所以映射失败
2.做关联查询 实现数据封装时使用.
补充:
resultType:
1.适用与单表查询,同时要求属性名称与字段相同.
2.如果属性与字段满足驼峰命名规则,开启驼峰映射之后,
可以使用resultType
resultMap:
1.如果字段不一致时使用
2.多表关联查询时使用.
3.如果开启了驼峰映射规则, 则自动映射的属性可以省略,最好标识主键
4.如果使用驼峰规则映射时,需要映射封装对象时(一对一/一对多),默认条件下.驼峰规则失效.
可以使用: autoMapping="true" 要求开启驼峰映射.
5.默认条件下 一对一,一对多不会自动完成驼峰规则映射.
需要配置 autoMapping="true"才能自动映射
总结 :
Mybatis中映射规则: 结果集不允许出现重名字段.
1.Mybatis 的集合操作
1.1 数组 foreach collection=“array”
1.2 list集合 foreach collection=“list”
1.3 Map集合 foreach collection=“map中的KEY”
2. foreach 用法
1.collection 表示遍历的集合类型
1.1 数组 关键字 array
1.2 List集合 关键字 list
1.3 Map集合 关键字 Map中的key
2. open 循环开始标签
close 循环结束标签 包裹循环体
3. separator 分割符
4. item 当前循环遍历的数据的变量
- 动态Sql
- where-if
核心思想: 自动判断是否为null,
如果为null,该字段不参与sql
动态Sql规则:
1. <if test="写判断条件,可以直接获取属性值"> </if>
true: 会拼接 字段条件
false: 不会拼接字段条件
2. 多余的关键字
由于动态sql拼接必然会导致多余的and 或者 or
3. where标签说明 可以去除 where后边多余的and 或者 or
2. set-if set标签用法: 去除set条件中多余的,号
3. choose-when-otherwise 标识都是互斥事件,只有一个有效.
补充:
如果需要使用一级/二级缓存,则POJO对象必须实现序列化接口. 否则数据不可以被缓存.
代理方式介绍
1 JDK动态代理
特点:
1. 要求被代理者,必须有接口.
2. 默认条件下如果有接口,则使用JDK动态代理
2 CGLIB动态代理
特点:
1.不管被代理者是否有接口,都可以为其创建代理对象.
2. 代理对象是目标对象的子类. 继承关系.
结论:
1.Spring中如果有接口,默认使用JDK代理方式,如果没有接口,则默认使用CGLIB代理方式.
2.Spring5以后,自身接口对象创建代理对象时,使用cglib
什么是跨域
说明: 浏览器解析Ajax时 要求浏览器的网址,与Ajax请求的网址,必须满足三要素.
要素:
1. 协议相同
2. 域名相同
3. 端口号相同
如果上述的三要素都满足,则叫同域访问, 如果三要素有一项不满足,则称为跨域访问.
类型与请求的映射关系
- 新增操作 类型:POST 接收注解: @PostMapping
- 删除操作 类型:DELETE 接收注解: @DeleteMapping
- 更新操作 类型:PUT 接收注解: @PutMapping
- 查询操作 类型:GET 接收注解: @GetMapping
完成用户入库操作
* 用法: axios.post(url地址,对象名称)
* axios.put(url地址, 对象名称)
*
* axios.get(url地址,{params: 对象名称})
* axios.delete(url地址,{params: 对象名称})
Session总结
- Session 称之为 会话控制 技术
- Session生命周期, 会话结束 对象销毁.
- Session的数据存储在内存中.
- Session只可以临时存储数据.不能永久存储.
Cookie总结
Cookie,有时也用其复数形式 Cookies。类型为“小型文本文件”,是某些网站为了辨别用户身份,进行Session跟踪而储存在用户本地终端上的数据(通常经过加密),由用户客户端计算机暂时或永久保存的信息 [1] 。
特点:
1. 类型: 小型文本文件.
2. 文件通常是加密的.
3. cookie 可以临时或者永久存储.
Nginx介绍
Nginx是一款轻量级的Web 服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器,
Nginx-特点
- 占用内存少 不超过2M tomcat服务器占用内存 200M
- 并发能力强 3-5万次/秒 tomcat 支持的并发能力 220-260个/秒 调优1000个/秒
- 开发语言 C语言开发 tomcat是java写的
反向代理(nginx)
反向代理服务器位于用户与目标服务器之间,但是对于用户而言,反向代理服务器就相当于目标服务器,即用户直接访问反向代理服务器就可以获得目标服务器的资源。同时,用户不需要知道目标服务器的地址,也无须在用户端作任何设定。反向代理服务器通常可用来作为Web加速,即使用反向代理作为Web服务器的前置机来降低网络和服务器的负载,提高访问效率。
特点:
1. 反向代理服务器介于用户和目标服务器之间
2. 用户的资源从反向代理服务器中获取.
3. 用户不清楚真实的服务器到底是谁. 保护了服务器的信息. 称之为服务器端代理.
正向代理(扩展)
正向代理,意思是一个位于客户端和原始服务器(origin server)之间的服务器,为了从原始服务器取得内容,客户端向代理发送一个请求并指定目标(原始服务器),然后代理向原始服务器转交请求并将获得的内容返回给客户端。客户端才能使用正向代理。
特点:
1. 正向代理服务器介于用户和目标服务器之间
2. 用户的资源从正向代理服务器中获取.
3. 客户端通过正向代理服务器,指向目标服务器.(用户非常清楚的了解目标服务器的存在.) 服务器端不清楚到底是谁访问的服务器.以为只是代理服务器访问.
说明: 每一次请求服务器,都伴随着正向代理和反向代理.
SpringCloud Alibaba 微服务架构
核心组件:
①nacos:服务注册、发现及配置;
②RestTemplate,Feign:实现服务间的远程调用;
③Ribbon:实现服务调用时的负载均衡;
④Sentinel:实现服务的限流、降级;
⑤Gateway:实现API访问入口的统一管理;
注:分布式事务:添加全局事务@GlobalTransactional
nacos:
提供5秒心跳机制,连续15秒之内如果接收不到信息,处于亚健康状态,30秒后就会删除服务;
Nacos 配置管理模型由三部分构成,
其中:
- Namespace:命名空间,对不同的环境进⾏隔离,⽐如隔离开发环境和⽣产环境。
- Group:分组,将若⼲个服务或者若⼲个配置集归为⼀组。
- Service/DataId:某⼀个服务或配置集,一般对应一个配置文件。
@RefreshScope注解的应用
@RefreshScope的作用是在配置中心的相关配置发生变化以后,能够及时看到类中属性值的更新(底层是通过重新创建Controller对象的方式,对属性进行了重新初始化)
Feign应用过程分析(底层逻辑先了解):
1)通过 @EnableFeignCleints 注解告诉springcloud,启动 Feign Starter 组件。
2) Feign Starter 会在项目启动过程中注册全局配置,扫描包下所由@FeignClient注解描述的接口,然后由系统底层创建接口实现类(JDK代理类),并构建类的对象,然后交给spring管理(注册 IOC 容器)。
3) Feign接口被调用时,底层代理对象会将接口中的请求信息通过编码器创建 Request对象,基于此对象进行远程过程调用。
4) Feign客户端请求对象会经Ribbon进行负载均衡,挑选出一个健康的 Server 实例(instance)。
5) Feign客户端会携带 Request 调用远端服务并返回一个响应。
6) Feign客户端对象对Response信息进行解析然后返回客户端。
Sentinel
限流的目的防止恶意请求流量、恶意攻击,或者防止流量超过系统峰值。
- 你了解哪些限流算法?(计数器、令牌桶、漏斗算法,滑动窗口算法,…)
- 类似Sentinel的产品你知道有什么?(hystrix-一代微服务产品)
- Sentinel 默认的限流算法是什么?(滑动窗口算法)
熔断降级会在调用链路中某个资源出现不稳定状态时(例如调用超时或异常比例升高),对这个资源的调用进行限制,让请求快速失败,避免影响到其它的资源而导致级联错误。当资源被降级后,在接下来的降级时间窗口之内,对该资源的调用都自动熔断(默认行为是抛出 DegradeException)。
Sentinel热点规则分析(重点)
热点参数限流会统计传入参数中的热点数据,并根据配置的限流阈值与模式,对包含热点参数的资源调用进行限流。热点参数限流可以看做是一种特殊的流量控制,仅对包含热点参数的资源调用生效。其中,Sentinel会利用 LRU 策略统计最近最常访问的热点参数,结合令牌桶算法来进行参数级别的流控。
Gateway:
为微服务架构提供一种简单有效的统一的 API入口,负责服务请求路由、组合及协议转换,并且基于 Filter 链的方式提供了权限认证,监控、限流等功能。
- 优点:
- 性能强劲:是第一代网关Zuul的1.6倍。
- 功能强大:内置了很多实用的功能,例如转发、监控、限流等
- 设计优雅,容易扩展。
- 缺点:
- 依赖Netty与WebFlux(Spring5.0),不是传统的Servlet编程模型(Spring MVC就是基于此模型实现),学习成本高。
- 需要Spring Boot 2.0及以上的版本,才支持
springboot的核心配置文件有bootstrap.yml和application.yml或bootstrap.properties和application.properties。
SpringCloud 微服务
springcloud组件的作用和实现?
答:核心组件:Eureka、Ribbon、Feign、Hystrix、Zuul。
1. Eureka 是微服务架构中的注册中心,专门负责服务的注册与发现。
心跳间隔时间,默认 30 秒,最后一次心跳后,间隔多久认定微服务不可用,默认90。
eureka 的自我保护状态:心跳失败的比例,在15分钟内是否超过85%,如果出现了超过的情况,Eureka Server会将当前的实例注册信息保护起来,同时提示一个警告,一旦进入保护模式,Eureka Server将会尝试保护其服务注册表中的信息,不再删除服务注册表中的数据。也就是不会注销任何微服务
2. Feign 的一个关键机制就是使用了动态代理。
首先,如果你对某个接口定义了 @FeignClient 注解,Feign 就会针对这个接口创建一个动态代理。
接着你要是调用那个接口,本质就是会调用 Feign 创建的动态代理,这是核心中的核心。
Feign的动态代理会根据你在接口上的 @RequestMapping 等注解,来动态构造出你要请求的服务的地址。
最后针对这个地址,发起请求、解析响应。
3. Ribbon 的作用是负载均衡,均匀的把请求分发到各个机器上。
Ribbon 是和 Feign 以及 Eureka 紧密协作,完成工作的,具体如下:
首先 Ribbon 会从 Eureka Client 里获取到对应的服务注册表,也就知道了所有的服务都部署在了哪些机器上,在监听哪些端口号。
然后 Ribbon 就可以使用默认的 Round Robin 算法,从中选择一台机器。
Feign 就会针对这台机器,构造并发起请求。
4. Hystrix 是隔离、熔断以及降级的一个框架。
5. Zuul 也就是微服务网关,这个组件是负责网络路由的。做统一的降级、限流、认证授权、安全。
Spring Cloud 核心组件,在微服务架构中,分别扮演的角色:
Eureka:各个服务启动时,Eureka Client 都会将服务注册到 Eureka Server,并且 Eureka Client 还可以反过来从 Eureka Server 拉取注册表,从而知道其他服务在哪里。
Ribbon:服务间发起请求的时候,基于 Ribbon 做负载均衡,从一个服务的多台机器中选择一台。
Feign:基于 Feign 的动态代理机制,根据注解和选择的机器,拼接请求 URL 地址,发起请求。
Hystrix:发起请求是通过 Hystrix 的线程池来走的,不同的服务走不同的线程池,实现了不同服务调用的隔离,避免了服务雪崩的问题。
Zuul:如果前端、移动端要调用后端系统,统一从 Zuul 网关进入,由 Zuul 网关转发请求给对应的服务。
zuul网关四种过滤器:PRE, ROUT, POST, ERROR
执行过程:
1.浏览器请求网关先经过PRE验证请求,PRE验证请求是否带有token,如果没用token就没用权限访问
2.PRE转发给ROUT,通过映射ROUT调用具体微服务项目,然后项目返回结果给ROUT
3.ROUT把结果转发给POST
4.POST把结果返回给浏览器
5.在执行过程中,只要发生错误就会触发ERROR过滤器
执行流程图:
Redis:
两种缓存机制:
Rdb方式持久化
RDB:
Rdb方式是通过手动(save-阻塞式,bgsave-异步)或周期性方式保存redis中key/value的一种机制,Rdb方式一般为redis的默认数据持久化方式.系统启动时会自动开启这种方式的持久化机制。
RDB持久化机制有哪些优点?
第一:RDB会生成多个数据文件,每个数据文件都代表了某一个时刻中redis的数据,这种多个数据文件的方式,非常适合做冷备,可以将这种完整的数据文件发送到一些远程云服务上去,在国内可以是阿里云的ODPS分布式存储上,以预定好的备份策略来定期备份redis中的数据.
第二:RDB对redis对外提供的读写服务,影响非常小,可以让redis保持高性能,因为redis主进程只需要fork一个子进程,让子进程执行磁盘IO操作来进行RDB持久化即可。
第三:相对于AOF持久化机制来说,直接基于RDB数据文件来重启和恢复redis进程,更加快速。
RDB持久化机制有哪些缺点?
假如redis故障时,要尽可能少的丢失数据,那么RDB方式不太好,它都是每隔5分钟或更长时间做一次快照,这个时候一旦redis进程宕机,那么会丢失最近几分钟的数据。
Aof方式数据持久化
Aof方式是通过记录写操作日志的方式,记录redis数据的一种持久化机制,这个机制默认是关闭的。
AOF持久化机制有哪些优点?
第一:AOF可以更好的保护数据不丢失,一般AOF会每隔1秒,通过一个后台线程执行一次fsync操作,最多丢失1秒钟的数据.
第二:AOF日志文件通常以append-only模式写入,所以没有任何磁盘寻址的开销,写入性能非常高,并且文件不容易破损,即使文件尾部破损,也很容易修复。
第三:AOF日志文件过大的时候,出现后台重写操作,也不会影响客户端的读写。因为在rewrite log的时候,会对其中的日志进行压缩,创建出一份需要恢复数据的最小日志出来。再创建新日志文件的时候,老的日志文件还是照常写入。当新的merge后的日志文件ready的时候,再交换新老日志文件即可。
第四:AOF日志文件的命令通过易读的方式进行记录,这个特性非常适合做灾难性的误删除的紧急恢复。比如某人不小心用flushall命令清空了所有数据,只要这个时候后台rewrite还没有发生,那么就可以立即拷贝AOF文件,将最后一条flushall命令给删了,然后再将该AOF文件放回去,就可以通过恢复机制,自动恢复所有数据.
AOF持久化机制有哪些缺点?
第一:对于同一份数据来说,AOF日志文件通常比RDB数据快照文件更大。
第二:AOF开启后,支持的写QPS会比RDB支持的写QPS低,因为AOF一般会配置成每秒fsync一次日志文件,当然,每秒一次fsync,性能也还是很高的。
第三:AOF这种基于命令日志方式,比基于RDB每次持久化一份完整的数据快照文件的方式,更加脆弱一些,容易有bug。不过AOF为了避免rewrite过程导致的bug,因此每次rewrite并不是基于旧的指令日志进行merge的,而是基于当时内存中的数据进行指令的重新构建,这样健壮性会好很多。
如何选择redis的持久化方式?
第一:不要仅仅使用RDB,因为那样会导致你丢失很多数据。
第二:也不要仅仅使用AOF,因为AOF做冷备没有RDB做冷备进行数据恢复的速度快,并且RDB简单粗暴的数据快照方式更加健壮。
第三:综合使用AOF和RDB两种持久化机制,用AOF来保证数据不丢失,作为数据恢复的第一选择; 用RDB来做不同程度的冷备。
Redis主从复制
一个master node是可以配置多个slave node的。
redis的高并发可以基于主从架构与读写分离机制进行实现。
Redis的replication机制是怎样的?
(1)redis采用异步方式复制数据到slave节点。
(2)一个master node是可以配置多个slave node的。
(3)slave node做复制的时候,是不会block master node的正常工作的。
(4)slave node在做复制的时候,也不会block对自己的查询操作,它会用旧的数据集来提供服务; 但是复制完成的时候,需要删除旧数据集,加载新数据集,这个时候就会暂停对外服务了。
(5)slave node主要用来进行横向扩容,做读写分离,扩容的slave node可以提高读的吞吐量。
Redis哨兵模式
哨兵(Sentinel)是Redis的主从架构模式下,实现高可用性(high availability)的一种机制。
redis进行事务控制时,通常是基于如下指令进行实现
- multi 开启事务
- exec 提交事务
- discard 取消事务
- watch 监控,如果监控的值发生变化,则提交事务时会失败
- unwatch 去掉监控
哨兵工作原理分析
1):每个Sentinel以每秒钟一次的频率向它所知的Master,Slave以及其他 Sentinel 实例发送一个 PING 命令。
2):如果一个实例(instance)距离最后一次有效回复 PING 命令的时间超过 down-after-milliseconds 选项所指定的值(这个配置项指定了需要多少失效时间,一个master才会被这个sentinel主观地认为是不可用的。 单位是毫秒,默认为30秒), 则这个实例会被 Sentinel 标记为主观下线。
3):如果一个Master被标记为主观下线,则正在监视这个Master的所有 Sentinel 要以每秒一次的频率确认Master的确进入了主观下线状态。
4):当有足够数量的 Sentinel(大于等于配置文件指定的值)在指定的时间范围内确认Master的确进入了主观下线状态, 则Master会被标记为客观下线 。
5):在一般情况下, 每个 Sentinel 会以每 10 秒一次的频率向它已知的所有Master,Slave发送 INFO 命令 。
6):当Master被 Sentinel 标记为客观下线时,Sentinel 向下线的 Master 的所有 Slave 发送 INFO 命令的频率会从 10 秒一次改为每秒一次 。
7):若足够没有数量的 Sentinel 同意 Master 已经下线, Master 的客观下线状态就会被移除。
8): 若 Master 重新向 Sentinel 的 PING 命令返回有效回复, Master 的主观下线状态就会被移除。
Redis集群高可用
对于redis集群(Cluster),一般最少设置为6个节点,3个master,3个slave
Redis的高并发跟整个系统的高并发是什么关系?
第一:Redis你要搞高并发的话,不可避免,要把底层的缓存搞得很好。例如,mysql的高并发,是通过一系列复杂的分库分表,订单系统,事务要求的,QPS到几万,比较高了。
第二:要做一些电商的商品详情页,真正的超高并发,QPS上十万,甚至是百万,一秒钟百万的请求量,只有redis是不够的,但是redis是整个大型的缓存架构中,支撑高并发的架构里面,非常重要的一个环节。
第三:你的底层的缓存中间件,缓存系统,必须能够支撑的起我们说的那种高并发,其次,再经过良好的整体的缓存架构的设计(多级缓存架构、热点缓存),支撑真正的上十万,甚至上百万的高并发。
RabbitMQ 使用场景
RabbitMQ是一种消息中间件,用于处理来自客户端的异步消息。
1. 服务解耦
A服务只需要向消息服务器发送消息,而不用考虑谁需要这些数据;下游服务如果需要数据,自行从消息服务器订阅消息,不再需要数据时则取消订阅即可
2. 流量削峰
使用RabbitMQ来进行流量削峰,高峰情况下,瞬间出现的大量请求数据,先发送到消息队列服务器,排队等待被处理,而我们的应用,可以慢慢的从消息队列接收请求数据进行处理,这样把数据处理时间拉长,以减轻瞬时压力
3. 异步调用
我们引入RabbitMQ消息队列,订单数据可以发送到消息队列服务器,那么调用链路也就可以到此结束,订单系统则可以立即得到响应,整条链路的响应时间只有200毫秒左右
寻找外卖小哥的应用可以以异步的方式从消息队列接收订单消息,再执行耗时的寻找操作
rabbitmq六种工作模式
简单模式
工作模式
cookie、sessionStorage和localStorage的区别
注意:时刻注意XSS注入的风险,因为可以在控制台直接访问它们,所以不要存入敏感数据
XSS 攻击:即跨站脚本攻击,它是 Web 程序中常见的漏洞。原理是攻击者往 Web 页面里插入恶意的脚本代码(css 代码、Javascript 代码等),当用户浏览该页面时,嵌入其中的脚本代码会被执行,从而达到恶意攻击用户的目的,如盗取用户 cookie、破坏页面结构、重定向到其他网站等。预防 XSS 的核心是必须对输入的数据做过滤处理。
sessionStorage、localStorage和cookie的区别
1)相同点是都是保存在浏览器端、且同源的
2)cookie数据始终在同源的http请求中携带(即使不需要),即cookie在浏览器和服务器间来回传递,而sessionStorage和localStorage不会自动把数据发送给服务器,仅在本地保存。cookie数据还有路径(path)的概念,可以限制cookie只属于某个路径下
3)存储大小限制也不同,cookie数据不能超过4K,同时因为每次http请求都会携带cookie、所以cookie只适合保存很小的数据,如会话标识。sessionStorage和localStorage虽然也有存储大小的限制,但比cookie大得多,可以达到5M或更大
4)数据有效期不同,sessionStorage:仅在当前浏览器窗口关闭之前有效;localStorage:始终有效,窗口或浏览器关闭也一直保存,因此用作持久数据;cookie:只在设置的cookie过期时间之前有效,即使窗口关闭或浏览器关闭
5)作用域不同,sessionStorage不在不同的浏览器窗口中共享,即使是同一个页面;localstorage在所有同源窗口中都是共享的;cookie也是在所有同源窗口中都是共享的
6)web Storage支持事件通知机制,可以将数据更新的通知发送给监听者
7)web Storage的api接口使用更方便