个人笔记
2021-03-03 20:15:59 24 举报
AI智能生成
登录查看完整内容
为你推荐
查看更多
个人学习笔记
作者其他创作
大纲/内容
学习笔记
算法
设计模式
代理模式
定义
代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用
分类
静态代理
被代理对象与代理对象需要一起实现相同的接口或者是继承相同父类,因此要定义一个接口或抽象类.
代码
总结
动态代理
子主题
Cglib代理
代理对象不需要实现接口,目标对象也可以是单纯的一个对象。类似经纪人只为一个目标对象服务 当然也可多个
工厂模式
简单工厂/静态工厂
工厂方法
将创建的方法抽象由子类决定要抽象的类
抽象工厂
与工厂方法的区别
工厂方法模式针对的是一个产品等级结构;而抽象工厂模式则是针对的多个产品等级结构。
详情见设计模式笔记
单例模式
适配器
观察者
装饰者
数据库
操作系统
计算机网络
网络层IP
运输层TCP/UDP
应用层
面试
物理层
数据链路层
把0、1的信号组成数据包(帧),这是数据链路层的数据包
数据包的结构都是head+data。这里的head是数据包的说明项,比如发送者 接受者 数据格式等等,数据链路层的发送者和接受者的数据是MAC地址
网络层ip
网络层通过iip协议找到主机
运输层tcp/udp
处理进程之间的通信 端口就代表了进程
UDP 和 TCP 协议,讲过主要用来确定端口到端口的通信
UDP
无须建立连接
无连接状态
分组首部开销小
TCP
面向连接
提供可靠的交付服务
提供全双工通信
面向字节流
应用层http协议
通过应用进程间的交互来完成特定网络应用
数据包之间是包含关系的,所以 UDP 的数据包是放到 IP 数据包的“数据”部分的,IP 数据包又放在以太网数据包的“数据”部分的。
TCP 三次握手
客户端–发送带有 SYN 标志的数据包–一次握手–服务端
PS1:SYN=1,ACK=0表示该报文段为连接请求报文。PS2:x为本次TCP通信的字节流的初始序号。 TCP规定:SYN=1的报文段不能有数据部分,但要消耗掉一个序号。
服务端–发送带有 SYN/ACK 标志的数据包–二次握手–客户端
PS1:SYN=1,ACK=1表示该报文段为连接同意的应答报文。PS2:seq=y表示服务端作为发送者时,发送字节流的初始序号。PS3:ack=x+1表示服务端希望下一个数据报发送序号从x+1开始的字节
客户端–发送带有带有 ACK 标志的数据包–三次握手–服务端
为什么要三次握手
第一次握手:Client 什么都不能确认;Server 确认了对方发送正常,自己接收正常
第二次握手:Client 确认了:自己和对方发送和接收都正常;但服务端不能确认自己的发送是否正常 对方接收是否正常
第三次握手后双方才能知道一切正常
三次握手发生错误时的应对措施
第二次握手B发送SYN+ACK传输失败,A不会申请资源,B申请了资源,但收不到A的ACK,过一段时间释放资源。如果是收到了多个A的SYN请求,B都会回复SYN+ACK,但A只会承认其中它最早发送的那个SYN的回应,并回复最后一次握手的ACK
第三次握手ACK传输失败,B没有收到ACK,释放资源,对于后序的A的传输数据返回RST。实际上B会因为没有收到A的ACK会多次发送SYN+ACK,次数是可以设置的,如果最后还是没有收到A的ACK,则释放资源,对A的数据传输返回RST
双方通信无误必须是两者互相发送信息都无误。传了 SYN,证明发送方到接收方的通道没有问题,但是接收方到发送方的通道还需要 ACK 信号来进行验证。 这个时候syn在第一次就已经发了 不能确认数据是否正确 所以还需要ack
为什么建立连接是三次握手,关闭连接确是四次挥手呢?
网络编程
三要素
协议
计算机网络通信必须遵守的规则
IP地址
IP地址用来给一个网络中的计算机设 备做唯一的编号
端口号
说IP地址可以唯一标识网络中的设备,那么端口号就可以唯一标识设备中的进程(应用程序)了
协议 + IP地址 + 端口号 三元组合,就可以标识网络中的进程了,那么进程间的通信就可以利用这个标识与其 它进程进行交互
两端
通信步骤
两端实现通信
客户端: java.net.Socket 类表示。创建 Socket 对象,向服务端发出连接请求,服务端响应请求,两者建 立连接开始通信。 2. 服务端: java.net.ServerSocket 类表示。创建 ServerSocket 对象,相当于开启一个服务,并等待客户端 的连接。
命令
ipconfig
查看本机IP地址
ping 空格 IP地址
检查网络是否连通
本机IP地址: 127.0.0.1 、 localhost
java基础
java常识
虚拟机
执行java字节码的虚拟进程
JRE和JDK
平台无关性
编译性和解释性语言
编译性语言
一次编译 多次执行
语言代表
特点
不同平台对编译器影响大
16位
int 2字节16位 long4字节32位
32 64位
int4字节32位 long8字节64位
解释性语言
使用专门的解释器对源程序逐行解释成特定平台的机器码并立即执行。是代码在执行时才被解释器一行行动态翻译和执行,而不是在执行之前就完成翻译。
Python、JavaScript、Shell、Ruby、MATLAB等
运行效率一般相对比较低,依赖解释器,跨平台性好
关于java
java文件先编译成与平台无关的.class的字节码文件,然后.class的字节码文件既可以在Windows平台上的java虚拟机(JVM)上进行解释运行,也可以在Linux平台上的JVM上解释运行;而JVM的翻译过程时是解释性的,JVM从.class的字节码文件中读出一条指令,翻译一条指令,然后执行一条指令,这个过程就称为java的解释执行;
基础语法
基本类型
byte
1个字节 范围:-128(-2^7)~127(2^7-1)
short
2个字节 范围:-32768(-2^15)~32767(2^15)
int
4个字节 范围: -2^31~2^31-1
long
8个字节 范围: -2^63~2^63-1
float
4个字节
-2^128~2^128 尾数位23bit 7~8位十进制位
默认小数是double类型 所以声明float后面要加f
double
8个字节
-2^1024~2^1024 尾数位52bit 15~16位十进制位
char
2个字节 1个字符存储1个汉字
boolean
自动转换
范围小的向范围大的自动转换
int-long-float-double
包装类型
基本类型的包装类
Byte
Short
Integer
Long
Float
Double
Character
Boolean
类型转换(自动装拆箱)
基本-包装
int x=1;Integer y=new Integer(x)
包装-基本
Integer j=2;int x=j.intValue();
包装-字符串
Integer j=3;String s=j.toString()
字符串-包装
String s=\"4\";Integer j=Integer.valueOf(s)
字符串-基本
String s=\"4\";int j=Integer.valueOf(s)
基本-字符串
int c=12;String s=String.valueOf(c)
字符串类
概述
字符串类不属于8种基本类型 而是一种引用类
==与equals的区别
==指的是指向对象的地址是否相同。equal内部方法是把char[]的每一个值进行对比相同就返回true有一个不同就返回false。
String(不可变类)
类的结构
final类型的(不可以被继承)
通过char[]数组来保存字符串的
不同的创建方式
String str = new String(\"abc\");
String str1 = \"abc\";
不可变的好处
不可变以及执行最慢的原理
String str=“abc”;str=str+“de”;
String str=“abc”+“de”;
StringBuffer(可变类)
StringBuilder(可变类)
关于字符串常量池
关于 ==
三者的区别
运行速度
StringBuilder > StringBuffer > String(不是绝对)
StringBuffer则每次都需要判断锁,效率相对更低。
线程安全
String:适用于少量的字符串操作的情况StringBuilder:适用于单线程下在字符缓冲区进行大量操作的情况StringBuffer:适用多线程下在字符缓冲区进行大量操作的情况
常考面试:https://blog.csdn.net/qq_43499096/article/details/86744599
修饰符
访问控制修饰符
public
对所有类可见。使用对象:类、接口、变量、方法
protected
对同一包内的类和所有子类可见。使用对象:变量、方法。 注意:不能修饰类(外部类)。
default
在同一包内可见,不使用任何修饰符。使用对象:类、接口、变量、方法。
private
在同一类内可见。使用对象:变量、方法。 注意:不能修饰类(外部类)
非访问修饰符
static
用来修饰类方法和类变量。
final
用来修饰类、方法和变量,final 修饰的类不能够被继承,修饰的方法不能被继承类重新定义,修饰的变量为常量,是不可修改的
abstract
用来创建抽象类和抽象方法
synchronize
主要用于线程的编程
volatile
数组
创建
double[] myList; // 首选的方法或double myList[]; // 效果相同,但不是首选方法
arrayRefVar = new dataType[arraySize];
作为参数和返回值
public static int[] reverse(int[] list) {return reverse;}
多维数组
String s[][] = new String[2][];s[0] = new String[2];s[1] = new String[3];
Arrays类
ava.util.Arrays 类能方便地操作数组,它提供的所有方法都是静态的。
File
File类获取名称的方法名称
File类获取绝对路径的方法名称
File类获取文件大小的方法名称
File类判断是否是文件的方法名称
File类判断是否为文件夹
递归的用法
IO
数据的流向
输入流
把数据从其他设备读取到内存
输出流
把数据从内存写到其他设备
数据的类型
字节流
以字节为单位读取数据的流
字符流
以字符为单位读取数据的流
顶级父类
InputStream(字节输入流)
使用字节输入流读取数据到程序
OutputStream(字节输出流)
使用字节输出流写出数据到文件
FileReader(字符输入流)
使用字符输入流读取数据到程序
FileWriter(字符输出流)
使用字符输出流写出数据到文件
属性集Properties
IO进阶
缓冲流
BufferedInputStream (字节缓冲流)
BufferedOutputStream
BufferedReader(字符缓冲流)
readline的使用
BufferedWriter
newline的使用
基本原理
优势
默认缓存8192字节
BufferedOutputStream(OutputStrean out)
典例
关于使用缓冲流和自定义数组的区别
缓冲区
Buffer...FileWriter都带缓冲区 也可自定义数组
转换流
InputStreamReader
字节为字符解码
OutputStreamWriter
字符编码为字节
序列化
ObjectOutputStream(对象的序列化流)
writeObject(p) 得到字节
反序列化
ObjectInputStream(对象的反序列化流)
readObject() 得到Object对象
Serializable接口 (标记型接口 没有方法)
要进行序列化和反序列化就必须实线Serialzable
否则报 NotSerializable异常
transient瞬态关键字
被修饰的不能被序列化
打印流
PrintStream
异常
根类:java.lang.Throwable
子类:java.lang.Error
内存溢出 系统崩溃等等
子类:java.lang.Exception
编译时期异常:checked异常
IOException
ClassNotFoundException
ParseException
转换异常 format格式不对等等
SQLException
无法连接数据库
违反约束
列表不存在
运行时期异常:runtime异常
RubtimeException
NullPointerException
ArithmeticException(算术异常)
ArrayIndexOutOfBoundsException
异常的声明与捕获
声明异常
捕获异常
在方法中使用try-catch的语句块来处理异常。
异常的执行过程
异常注意事项 见文档
面向对象
三大特性
封装
概念
将类的某些信息隐藏在类内部,不允许外部程序直接访问,而是通过该类提供的方法来实现对隐藏信息的操作和访问
好处
属性的封装:使用者只能通过事先定制好的方法来访问数据,可以方便地加入逻辑控制,限制对属性的 不合理操作;
方法的封装:使用者按照既定的方式调用方法,不必关心方法的内部实现,便于使用; 便于修改,增强 代码的可维护性;
实现步骤
修改属性的可见性
改为private
创建getter和setter方法
用于属性的读写
在getter/setter方法中加入属性控制语句
对属性值得合法判断
继承
继承是从已有的类中派生出新的类,新的类能吸收已有类的数据属性和行为,并能扩展新的能力。
继承避免了对一般类和特殊类之间共同特征进行的重复描述,通过继承可以清晰地表达每一项共同特征所适应的概念范围
子类父类执行顺序
父类的构造器调用以及初始化过程一定在子类的前面 1、初始化父类再初始化子类 2、先执行初始化对象中属性,再执行构造方法中的初始化。 基于上面两点,我们就知道实例化一个子类,java程序的执行顺序是: 父类对象属性初始化---->父类对象构造方法---->子类对象属性初始化—>子类对象构造方法
多态
多态就是指多种状态,就是说当一个操作在不同的对象时,会产生不同的结果。
编译时多态
方法重载
有多个方法名相同的方法,但是这些方法有着不同的参数列表,在编译期我们就可以确定到底调用哪个方法
运行时多态
方法覆盖
实现的条件
:1.继承(包括接口的实现)2.方法的重写 3.父类的引用指向子类对象
实现多态的三个要素
类和对象的区别
java类是对具有相同特征事物的抽象,而对象时具体的个体
集合
Collection接口
List接口
List集合基础
实现了Collection接口
特性:可以重复,有序的
允许元素为null
有索引 可以使用for循环遍历
remove使用equals使用迭代器的remove set map可以直接remove 但用迭代器遍历时都使用迭代器的remove
如果使用 list.remove
ConcurrentModificationExceptionlist的remove是按照index下标进行
remove(Object o)
List在删除对象时,如果使用删除对象方法,应该最好重写equals方法,否则最好使用删除下标的方法。注:删除下标时一定要确保下标的类型是int类型,若是Integer类型,List会默认匹配remove(Object o)方法,而不是remove(int index)方法。
list的equals 比较的是object 也相当于==
注意与 string的equals的区别
Vector集合
基础
它是线程同步的,已被ArrayList取代
原因:线程安全但是效率太低
Vector和ArrayList,二者唯一的区别就是Vector每个方法都自带同步机制
ArrayList集合
底层为数组,初始容量为10(当添加第11个元素扩容),每次增长1.5倍
数组查询快 增删慢
但不绝对
拷贝 navite方法由c、c++实现 性能不错
线程非同步
LinkedList集合
底层为双向链表
链表查询慢 增删快
实现了Deque方法
使我们可以像操作队列和栈一样操作它
CopyOnWriteArrayList
原理
适合在读多写少的场景下使用
线程同步
Set接口
Set集合基础
底层大都是map结构的实现
常用的三个子类都是非同步的
HashSet
底层数据结构是hash表(元素为链表的数组)+红黑树
初始容量影响迭代性能
非同步
LinkedHashSet
底层数据结构由hash表(元素为链表的数组)和双向列表组成
父类是HashSet
实际上就是LinkHashMap
元素可以为null
迭代有序
性能比HashSet差一点 因为要维护双向链
初始容量与迭代⽆关,LinkedHashSet迭代的是双向链表
TreeSet
底层实际上是一个TreeMap
可以实现排序的功能
元素不能为null
Map
非线程安全
HashMap
底层
jdk8以前采用散列表(哈希表/数组+链表)
jdk8以后则是散列表+红黑树
链表转红黑树 条件
当链表长度大于8且数组长度大于64时 才能转化为红黑树
如果数组长度小于64 数组扩容(*2)
一直往一个链表插入元素 第9个时(数组初始容量为16) 扩容为32
同理 第10个时 扩容为64
第11个时才能 转为红黑树
扩容
默认初始化为16 扩容为原来的2倍 加载因子为0.75
当一个map填满了75%的bucket时候
容量为2的幂次的原因
使用2的幂数的数字 length-1的值所有低位二进制位都是1 这种情况下 index的值等同于HashCode的后几位
为什么不直接采用key的hash值 而是要用key.hashcode与高16为做异或
解决hash冲突
链地址法和红黑树
工作原理 过程
特性
允许null (key和value都可以)
存储无序
TreeMap
红黑树
复杂度log(n)
使用comparator和comparable可以进行排序
自然排序 手动排序 元素不能为null(value可以为null) null因为comparator设置了 可更改
LinkedHashMap
是HashMap的子类
put remove等方法调用Hashmap的
底层是hash链表(数组+链表)+双链表
因为链表
1个key被重新插入 顺序不变
可以为null
非同步 可以调用collection实现同步
设置accessorder为true
最常⽤的将其放在链表的最后,不常⽤的放在链表的最前
使⽤get⽅法也是结构性的修改
HashTable
key和value都不允许为null。
ConcurrentHashMap
底层是:散列表+红黑树,与HashMap是一样
为什么有了HashTable还要ConcurrentHashMap
Hashtable是在每个方法上都加上了Synchronized完成同步,效率低下。ConcurrentHashMap通过在部分加锁和利用CAS算法来实现同步。
Collection.synchronizedMap()
与数组的区别
长度可变
可以存储不同类型
只能存储引用类型
接口
一个方法的抽象集合
使用implement实现接口
一个类可以实现多个接口
接口可以被多继承
泛型
简介
Java泛型的设计原则
把类型明确的工作推迟到创建对象或者调用方法的时候才去明确的特殊的类型
相关术语
ArrayList<E> E称为类型参数变量
ArrayList<Integer> Integer称为实际类型参数
整个ArrayList<E> 称为泛型类型
ArrayList<Integer> 参数化的类型
引入泛型的原因
操作集合的时候,之前方法的定义都是Object类型,向集合中添加对象,都自动向上转型,加入的元素可以是任何类型但是,在取出元素的时候,通常想要使用对象的特有功能,就必须向下转型,此时就可能出现类型转换异常,存在安全隐患。
为什么需要泛型
早期使用object来代表任意类 但向下有强转的问题 程序不安全
泛型的使用
泛型类
泛型方法
某一个方法需要使用泛型 外界仅仅关心该方法 不关心类的其他属性
用户传递进来什么类型就返回什么类型
泛型类派生出的子类
子类泛型确定
泛型通配符
?可以表示匹配任意通用符
使用?的时候 只能调用对象与类型无关的方法 不能调用与类型有关的方法
PECS原则
设定通配符上限 <?extends Type>
设定通配符下限<?super Type>
通配符和泛型方法
泛型擦除
在编译阶段会进行所谓的“类型擦除”(Type Erasure),将所有的泛型表示(尖括号中的内容)都替换为具体的类型(其对应的原生态类型),就像完全没有泛型一样
泛型补偿
泛型补偿:当要本来具有泛型的方法在被调用的时候,根据实际类型的Class对象获取类型信息,自动的添加到原来的地方。
注解Annotation
什么是注解?
取代xml配置文件来告诉类如何运行 更加直观
基本注解
@Override
告诉编译器该方法是实现父类的 帮忙避免一些低级错误
@Deprecated
过时注解该注解也非常常见,Java在设计的时候,可能觉得某些方法设计得不好,为了兼容以前的程序,是不能直接把它抛弃的,于是就设置它为过时
@SuppressWarnings
抑制编译器警告注解该注解在我们写程序的时候并不是很常见,我们可以用它来让编译器不给予我们警告
@FunctionalInterface
用来指定该接口是函数式接口
自定义注解
注入普通属性其实就三步
1.反射出该类的方法
2.通过方法得到注解上的具体信息
3将注解上的信息注入到方法
注入对象
2.得到属性对应的写方法,通过写方法得到注解
3.获取注解的详细信息,将注解的信息注入到对象上
4.调用属性写方法,将已填充的对象注入到方法
元注解
反射
什么是反射
在运行时能够获取自身的信息。在java中,只要给定类的名字,那么就可以通过反射机制来获得类的所有信息
使用场合
在搭建框架的时候或编译的时候,有时候不知道需要什么类,什么方法,这个类有哪些属性。比如查询数据库之后的数据,我们就可以在运行时根据编译好的信息反射成对象
优缺点
优点
反射提高了Java程序的灵活性和扩展性,降低耦合性,提高自适应能力。它允许程序创建和控制任何类的对象,无需提前硬编码目标类(通过new来获取)
缺点
反射的操作
Class类主要api
成员变量Field
构造器Constructor
构造方法Method
获取反射中的Class对象
使用 Class.forName 静态方法Class c1 = Class.forName(“com.xxp.api.Phone”);
直接通过 类名.class 的方式得到,该方法最为安全可靠,程序性能更高Class clz = Phone.class;
通过对象调用 getClass() 方法来获取,通常应用在:比如你传过来一个 Object类型的对象,而我不知道你具体是什么类,用这种方法。new Phone().getClass() //对象.getClass()
创建类对象
通过 Class 对象的 newInstance() 方法。Class clz = Phone.class;Phone phone = (Phone)clz.newInstance();
通过 Constructor 对象创建类对象可以选择特定构造方法,而通过 Class 对象则只能使用默认的无参数构造方法
获取成员变量并使用
只能公有
1: 获取Class对象 2:通过Class对象获取Constructor对象 3:Object obj = Constructor.newInstance()创建对象 4:Field field = clz.getField(\"指定变量名\
还包括私有
1:Class.getDeclaredField()获取该成员变量对象 2:setAccessible() 暴力访问
通过反射调用成员方法
1:获取Class对象 2:通过Class对象获取Constructor对象 3:Constructor.newInstance()创建对象 4:通过Class对象获取Method对象 ------getMethod(\"方法名\"); 5: Method对象调用invoke方法实现功能Method method = clz.getMethod(\"setPrice\
1: getDeclaredMethod() 2: setAccessiable();
java8新特性
lambda表达式
Stream函数式编程
Optional类
接口中的默认方法
新的日期类
java进阶
NIO
多线程
线程与进程的基础知识
进程概述
线程概述
线程的引入:进程实现多处理⾮常耗费CPU的资源,⽽我们引⼊线程是作为调度和分派的基本单位(取代进程的部分基本功能【调度】)
在同⼀个进程内⼜可以执⾏多个任务,⽽这每⼀个任务我就可以看出是⼀个线程。
进程作为资源分配的基本单位线程作为资源调度的基本单位,是程序的执⾏单元,执⾏路径(单线程:⼀条执⾏路径,多线程:多条执⾏路径)。是程序使⽤CPU的最基本单位。
线程有3个基本状态:执⾏、就绪、阻塞线程有5种基本操作:派⽣、阻塞、激活、 调度、 结束
值得注意的是:多线程的存在,不是提⾼程序的执⾏速度。其实是为了提⾼应⽤程序的使⽤率,程序的执⾏其实都是在抢CPU的资源,CPU的执⾏权。多个进程是在抢这个资源,⽽其中的某⼀个进程如果执⾏路径⽐较多,就会有更⾼的⼏率抢到CPU的执⾏权
并行与并发
并行
并行性是指同一时刻内发生两个或多个事件
并行是在不同实体上的多个事件
并发
并发性
并发性是指同一时间间隔内发生两个或多个事件
并发是在同一实体的多个事件
并发编程三大特性及解决
原子性
使用synchronize可以保证
可见性
使用synchronize可以保证可见性
使用volatile保证可见性
有序性
程序的执行顺序按照代码的先后顺序执行
使用volatile保证有序性 屏蔽了指令重排序
JVM指令重排序
jvm在保证结果的前提下会打乱指令的顺序可能是因为性能的因素等等单线程是没问题的 多线程就得考虑这个问题
Java实现多线程需要注意的细节
run()和start()的区别
run()
仅仅是封装被线程执行的代码 直接调用是普通方法
start()
jvm虚拟机的启动是单线程的还是多线程的?
是多线程的。不仅仅是启动main线程,还⾄少会启动垃圾回收线程的,不然谁帮你回收不⽤的内存
守护线程
守护线程是为其他线程服务的如 垃圾回收线程
特点:当别的⽤户线程执⾏完了,虚拟机就会退出,守护线程也就会被停⽌掉了。也就是说:守护线程作为⼀个服务线程,没有服务对象就没有必要继续运⾏了
注意点
1. 在线程启动前设置为守护线程,⽅法是 setDaemon(boolean on)2. 使⽤守护线程不要访问共享资源(数据库、⽂件等),因为它可能会在任何时候就挂掉了。3. 守护线程中产⽣的新线程也是守护线程
方法
thread2.setDaemon(true);
线程优先级
线程优先级⾼仅仅表示线程获取的CPU时间⽚的⼏率⾼,但这不是⼀个确定的因素!线程的优先级是⾼度依赖于操作系统的,Windows和Linux就有所区别(Linux下优先级可能就被忽略了)
Java提供的优先级默认是5,最低是1,最⾼是10:
thread2.setPriority0(7);
线程join方法
如何使线程t1 t2 t3按顺序执行
A{B.join()} 到A线程的时候 会让B线程插队先执行 等待b结束在执行
线程的生命周期
图解
三个基本状态
重点:用来切换线程状态的方法
sleep()
调⽤sleep⽅法会进⼊计时阻塞,等时间到了,进⼊的是就绪状态⽽并⾮是运⾏状态!
yield()
调⽤yield⽅法会先让别的线程执⾏,但是不确保真正让出意思是:我有空,可以的话,让你们先执⾏
调用yield方法并不会让线程进入阻塞状态,而是让线程重回就绪状态,它只需要等待重新获取CPU执行时间不会释放锁
join()
调⽤join⽅法,会等待该线程执⾏完毕后才执⾏别的线程
join在线程里面意味着“插队”,哪个线程调用join代表哪个线程插队先执行——但是插谁的队是有讲究了,不是说你可以插到队头去做第一个吃螃蟹的人,而是插到在当前运行线程的前面,比如系统目前运行线程A,在线程A里面调用了线程B.join方法,则接下来线程B会抢先在线程A面前执行,等到线程B全部执行完后才继续执行线程A。
interrupt()
interrupt不会真正停止一个线程,它仅仅是给这个线程发了一个信号告诉它,它应该要结束了。interrupt方法压根是不会对线程的状态造成影响的,它仅仅设置一个标志位
使用多线程需要注意的问题
基础问题
线程安全问题(数据跟我们想象中运行的结果不一致)
性能问题(使用不当会降低效率,甚⾄会造成死锁)如加锁后性能降低
对象的发布与溢出
发布与逸出的定义
发布
使对象能够在当前作用域之外的代码中使用
逸出
当某个不应该发布的对象被发布了
常见的逸出的方式
静态域逸出public修饰的get⽅法⽅法参数传递隐式的this
安全发布的几种常见方式
在静态域中直接初始化
对应的引⽤保存到volatile或者AtomicReferance引⽤中
由final修饰
由锁来保护
解决线程安全性的办法
无状态(没有共享变量)
只要我们保证不要在栈(方法)上发布对象(每个变量的作用域仅仅停留在当前的方法上),那么我们的线程就是安全的
使用final使该引用变量不可变
不可变对象⼀定线程安全的。
加锁(内置锁,显示Lock锁)
原⼦性就是执⾏某⼀个操作是不可分割的
count++这个操作就不是一个原子性操作!
volatile是⼀种轻量级的同步机制volatile仅仅⽤来保证该变量对所有线程的可⻅性,但不保证原⼦性
保证该变量对所有线程的可⻅性
在多线程的环境下:当这个变量修改时,所有的线程都会知道该变量被修改了,也就是所谓的“可⻅性
不保证原⼦性
修改变量(赋值)实质上是在JVM中分了好⼏步,⽽在这⼏步内(从装载变量到修改),它是不安全的。
使⽤了volatile修饰的变量保证了三点
⼀旦你完成写⼊,任何访问这个字段的线程将会得到最新的值
在你写⼊前,会保证所有之前发⽣的事已经发⽣,并且任何更新过的数据值也是可⻅的,因为内存屏障会把之前的写⼊值都刷新到缓存
volatile可以防⽌重排序(重排序指的就是:程序执⾏的时候,CPU、编译器可能会对执⾏顺序做⼀些调整,导致执⾏的顺序并不是从上往下的。从⽽出现了⼀些意想不到的效果)。⽽如果声明了volatile,那么CPU、编译器就会知道这个变量是共享的,不会被缓存在寄存器或者其他不可⻅的地⽅。
volatile⼤多⽤于标志位上(判断操作)
修改变量时不依赖变量的当前值(因为volatile是不保证原⼦性的)
该变量不会纳⼊到不变性条件中(该变量是可变的)
在访问变量的时候不需要加锁(加锁就没必要使⽤volatile这种轻量级同步机制了)
线程封闭
在多线程的环境下,只要我们不使⽤成员变量(不共享数据)
如sevlet的不加锁所有的数据都是在⽅法(栈封闭)上操作的,每个线程都拥有⾃⼰的变量,互不⼲扰
有另⼀种⽅法:ThreadLocal
这个类的API就可以保证每个线程⾃⼰独占⼀个变量
使用JDK为我们提供的类来实现线程安全 未必要自己加锁
Java锁机制
公平锁与非公平锁
公平锁
线程将按照它们发出请求的顺序来获取锁
公平锁会来带⼀些性能的消耗的
非公平锁
线程发出请求的时可以“插队”获取锁
Lock和synchronize都是默认使⽤⾮公平锁的。如果不是必要的情况下,不要使⽤公平锁
公平模式下会去看FIFO队列线程是否是在队头,⽽⾮公平模式下是没有的
synchronized锁
synchronized是Java的一个关键字,它能够将代码块(方法)锁起来
synchronized是一种互斥锁:一次只能允许一个线程进入被锁住的代码块
synchronized⽤处
synchronized保证了线程的原⼦性。(被保护的代码块是⼀次被执⾏的,没有任何线程会同时访问)
synchronized还保证了可⻅性。(当执⾏完synchronized之后,修改后的变量对其他的线程是可⻅的)
synchronized底层
synchronized底层是是通过monitor对象,对象有⾃⼰的对象头,存储了很多信息,其中⼀个信息标示是被哪个线程持有。
类锁与对象锁
synchronized修饰静态⽅法获取的是类锁(类的字节码⽂件对象)
synchronized修饰普通⽅法或代码块获取的是对象锁。
获取了类锁的线程和获取了对象锁的线程是不冲突的
重入
可重入就是说某个线程已经获得某个锁,可以再次获取锁而不会出现死锁持有锁的是线程。
释放锁的时机
1. 当⽅法(代码块)执⾏完毕后会⾃动释放锁,不需要做任何的操作。2. 当⼀个线程执⾏的代码出现异常时,其所持有的锁会⾃动释放。
Lock显示锁
synchronized锁和Lock锁使⽤哪个
Lock锁两个主要的子类
ReentrantLock锁可重入锁(互斥锁)
比synchronized更有伸缩性(灵活)
使用时最标准用法是在try之前调用lock方法,在finally代码块释放锁
是互斥锁(⼀次只能有⼀个线程进⼊到临界区(被锁定的区域))synchronized也是
ReentrantReadWriteLock重入读写锁
是一个读写锁
在读取数据的时候,可以多个线程同时进⼊到到临界区(被锁定的区域) 在写数据的时候,⽆论是读线程还是写线程都是互斥的
读的线程⽐写的线程要多很多的话,那可以考虑使⽤它
读的时候可以共享,在写的时候是互斥的
读锁不能升级为写锁,写锁可以降级为读锁
在内部定义了两个内部类来代表读锁和写锁,本质上还是AQS实现
它使用state的变量高16位是读锁,低16位是写
相比synchronize增加的功能
trylock
使用synchronize没抢到锁的线程必须得等锁的释放干等 是用trylock当没抢到锁时可以做其他
支持读写锁
读锁和写锁互斥读锁和读锁共享
AQS(AbstractQueuedSynchronizer)⼀般我们叫AQS为同步器
juc 是包的缩写(java.util.concurrnt)
Lock子类实现都基于AQS(Lock锁底层原理()
含有三个抽象类
AbstractOwnableSynchronizerAbstractQueuedLongSynchronizerAbstractQueuedSynchronizer(简称aqs)
AQS其实就是一个可以给我们实现锁的框架
内部实现的关键是:先进先出的队列、state状态
定义了内部类ConditionObject
拥有两种线程模式
独占模式
共享模式
在LOCK包中的相关锁(常用的有ReentrantLock、 ReadWriteLock)都是基于AQS来构建
细节
修改state状态值时使用CAS算法(乐观锁)来实现
乐观锁,总是认为是线程安全的,不怕别的线程修改变量,如果修改了我就再重新尝试。悲观锁:总是认为线程不安全,不管什么情况都进行加锁,要是获取锁失败,就阻塞。
CAS算法:
其实就是拿副本中的预期值与主存中的值作比较,如果相等就继续替换新值,如果不相等就说明主存中的值已经被别的线程修改,就继续重试;
等待列被称为:CLH队列(三个名字组成),是一个双向队列
定义了框架,具体实现由子类来做(模板模式)
synchronized好⽤,简单,性能不差没有使⽤到Lock显式锁的特性就不要使⽤Lock锁了。
线程和进程的概念
创建线程的几种方法
线程的常用api
如何停止一个正在运行的线程
用户线程和守护线程
线程的优先级
什么是线程安全问题
JMM Java的内存模型产生安全问题的根本所在
Java并发编程三大特性
解决
底层原理
mointor 对象监控器
lock和synchronize的区别
synchronize可以修饰方法和代码块而lock只能修饰代码块
lock可以让等待的线程响应中断 synchronize只能死等lockInterruptibly()
lock可以知道有没有成功获取锁 trylock 而synchronize不行
lock可以提高多线程读的效率
synchronize大概实现原理
为什么使用wait方法时 要写synchronize里面
wait要释放掉锁 有锁才能释放
消费者与服务者代码
线程池
线程池可以看做是线程的集合
在没有任务时线程处于空闲状态,当请求到来:线程池给这个请求分配⼀个空闲的线程,任务完成后回到线程池中等待下次任务(⽽不是销毁)。这样就实现了线程的重⽤。
如果没有线程池 每次请求创建一个线程
线程⽣命周期的开销⾮常⾼ 创建和销毁线程花费的时间和资源
程序的稳定性和健壮性会下降,每个请求开⼀个线程。如果受到了恶意攻击或者请求过多(内存不⾜),程序很容易就奔溃掉了。
作用
降低资源消耗 提高响应速度 提高线程可管理性
实现Executor接⼝
ThreadPoolExecutor类用的最多的类 重点
常见线程池工具类
Callable和Future
Callable就是Runnable的扩展。Runnable没有返回值,不能抛出受检查的异常,而Callable可以!
当我们的任务需要返回值的时,我们就可以使⽤Callable
Future⼀般我们认为是Callable的返回值,但他其实代表的是任务的⽣命周期(当然了,它是能获取得到Callable的返回值的)
ThreadPoolExecutor类
常见参数重点 详情见笔记
corePoolSize
核心线程数
maximumPoolSize
最大线程数
keepAliveTime
保持存活时间
TimeUnit unit
时间单位
BlockQueue<Runnable> workQueue
任务队列
RejectedExecutionHandler handler
饱和策略
ThreadLocal
ThreadLocal提供了线程的局部变量,每个线程都可以通过 set() 和get() 来对这个局部变量进⾏操作,但不会和其他线程的局部变量进⾏冲突,实现了线程的数据隔离简要⾔之:往ThreadLocal中填充的变量属于当前线程,该变量对其他线程⽽⾔是隔离的。
常见的应用
最典型的是管理数据库的Connection
避免⼀些参数的传递的理解如cookie session中
实现原理
内存泄漏
由于ThreadLocalMap的⽣命周期跟Thread⼀样⻓,如果没有⼿动删除对应key就会导致内存泄漏,⽽不是因为弱引⽤。
目的
就是为了能够在当前线程中有属于⾃⼰的变量,并不是为了解决并发或者共享变量的问题
Automic原子类
为什么使用原子类
原子变量类
原子类可分为4种类型:基本数据类型、数组、引用类型、对象的属性
CAS
在多线程编程中实现不被打断的数据交换操作,从⽽避免多线程同时改写某⼀数据时由于执⾏顺序不确定性以及中断的不可预知性产⽣的数据不⼀致问题
实现
通过将内存中的值与指定数据进⾏⽐较,当数值⼀样时将内存中的数据替换为新的值。
CAS失败
重试(自旋)
失败后结束
CAS核心 原子性
即使对比后再修改看似多个操作 但还是原子性因为它是cpu指令
ABA
什么是ABA
解释1
线程A和线程B同时读到链表k1(符合内存和预期)但A先拿到执行权 将k2链表删除 这是链表C来了(没有k2符合c的预期) c在k2的位置加了kkk2的位置上有东西了 线程B以为那是k2(其实是kk 也符合B的预期) 就将kk删除
解释2
现在我有⼀个变量 count=10 ,现在有三个线程,分别为A、B、C线程A和线程C同时读到count变量,所以线程A和线程C的内存值和预期值都为10此时线程A使⽤CAS将count值修改成100修改完后,就在这时,线程B进来了,读取得到count的值为100(内存值和预期值都是100),将count值修改成10线程C拿到执⾏权,发现内存值是10,预期值也是10,将count值修改成11
解决ABA问题可以使用AtomicStampedReference和AtomicMarkableReference类
4LongAdder性能⽐AtomicLong要好
死锁
JVM
类加载机制
什么是类的加载
类的生命周期
如图
javac编译器生成的.class文件都会被直接加载到jvm中吗?
虚拟机严格规定了只有5中情况必须立即对类初始化
创建类的实例(new) 访问某个类或接口的静态变量或者为静态变量赋值 调用静态方法
反射的方式
初始化某个类的子类 父类也要被初始化
虚拟机被表明为启动类的类 包含main方法的那个类
jdk1.7动态语言支持时
所以说 java类的加载是动态的 而是保证基础类(像基类)完全加载到jvm中,其他类则在需要的时候加载
节省内存开销
.class通过类加载器加载到jvm
类加载过程
加载
查找并加载类的二进制数据(如.class文件等),在Java堆中也创建一个java.lang.Class类的对象
虚拟机要做的三件事
通过一个类的全限定名来获取其定义的二进制字节流。这个动作是在虚拟机外部 实现这个动作的代码被称为类加载器
引导类加载器
自定义加载器
比较两个类是否相等 只要他们的类加载器不同 两个类必不相等
相等指的是 equals isAssignFormisInstance方法的返回结果
将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
在内存中生成一个代表这个类的java.lang.Class对象,作为这个类的数据的访问入口
连接
验证
确保Class文件的字节流包含的信息符合当前虚拟机要求 保证不危害虚拟机的安全
准备
为类的静态变量分配内存,并将其初始化为默认值
解析
把类中的符号引用转换为直接引用
初始化
为类的静态变量赋予正确的初始值。
初始化就是执行类的构造器<clnit>方法的过程 注意与init实例构造器区别<clnit>是由编译器自动收集类中的所有类变量的赋值动作和静态语句块(static{})的语句合并产生如果没有上述动作 就不生成clnit
关于还有父类的情况
关于线程同步
类加载完后jvm干了什么?
jvm为新生对象分配内存
类的主动使用和被动使用
类的被动使用不会导致类的初始化
主动使用有七种情况会初始化
创建实例
访问某个类或接口的静态变量 或对静态变量赋值
调用静态方法
初始化一个类的子类
jvm启动被表明为启动类的类
jdk7的动态语言。。。
类加载器
虚拟机角度分类
启动类加载器
c++语言实现 虚拟机自身的一部分
其他所有的类加载器
所有派生于抽象类ClassLoader的加载器
独立存在于虚拟机外部
程序员角度分类
三种
比较两个类是否相等 只要他们的类加载器不同 两个类必不相等即使来自同一个Class文件
两个条件类的完整名一致 包括包名类加载器相同
类加载器的引用
jvm必须知道一个类型由启动加载器加载还是用户类加载器加载
如果是用户类加载器加载的 jvm这个类加载器的一个引用作为类型的信息保存在方法区中
双亲委派模型
过程
一个类加载器收到类加载请求,他不会自己去加载,会委派给父类,最终到达启动类加载器。只有当父加载器反馈无法完成这个加载请求,子类才去加载
避免类的重复加载
要不然不同加载器加载的类是不一样的 会创建多个同名的类
保护程序安全,防止api被随意篡改
自定义类java.lang.String
自定义类java.lang.zzz
归启动类加载器管 但核心库没有zzz所以会报错 SecurityException
沙箱安全机制:对java核心源代码的保护
案例
当自定义了一java.lang(包名).string(类名)高仿核心库的string
更加安全 不然被恶意写了个string整个过程的string都无法使用
如果自定义一个hello.you.world(放在lib\\ext目录下)
启动类加载器不能加载,就给子类扩展类加载器加载
运行时数据区域
程序计数器
也叫pc寄存器 对cpu寄存器的模拟
用来存储指向下一条指令的地址给执行引擎来执行
任何时间一个线程只有一个方法执行程序计数器会存储当前线程正在执行的Java方法或jvm指令如果是native方法 则是未指定值(undefined)
Java虚拟机栈
栈帧
方法执行的时候,jvm都会同步创建一个栈帧每一个方法被调用到执行完毕的过程,都对应栈帧入栈与出栈的过程
栈帧内部结构
局部变量表(local variables)
基本存储单元
slot槽
存放编译期可知的基本数据类型 引用类型 returnaddress类型
定义为一个数字数组 用于存放局部变量
也称为局部变量数组 或本地变量表
操作数栈
动态链接
将Class文件常量池里的符号引用转化为调用方法的直接引用
指向运行常量池的方法引用
方法返回地址
生命周期 和线程一致
作用 保存方法的局部变量 部分结果 并参与方法的调用和返回
快速有效分配存储方式访问速度仅次于程序计数器
可能存在栈溢出 不存在垃圾回收的问题
内存模型
垃圾回收
Jvm参数
-Xmx3550m
设置堆最大值
-Xms3550m
设置初始堆大小
-Xss128k
设置线程栈大小
-Xmn2g
-XX:NewSize=1024m
设置年轻代的初始值
-XX:PermSize=256m
设置持久带初始值
-XX:MaxPermSize=1024m
设置持久带最大值
-XX:NewRatio=4
设置年轻代(包括一个Eden区和两个Suvivor区)和老年代的比值
-XX:SurvivorRatio=4
设置Suvivor区和Edeb区的比值
-XX:MaxTenuringThreshold=7
表示一个对象在Survivor区移动了7次还没被回收就进入老年代
使用PC寄存器存储字节码指令地址的用处为什么使用pc寄存器记录当前线程执行地址
PC寄存器为什么被设定为线程私有
多个线程看似并行 实质还是并发
栈中常见的error
默认
采用固定大小的虚拟栈每一个线程的jvm栈是在线程创建时确定线程请求分配的栈容量超过最大容量抛 stackovererror
可以动态扩展,当尝试扩展时无法申请足够的内存创建新线程时没有足够的内存去创建对应的虚拟机栈抛outofmemoryerror 内存不足
java中需要使用的组件
java堆
使用存储java对象的内存区域
=Xmx表示堆的最大大小
-Xms表示堆的初始大小
一旦分配完成 内存不够时不能再申请
线程
每个线程创建时,都会分配一个栈堆
通常256kb和756kb之间
类和类加载器
默认的三个类加载器
Bootstrap ClassLoader
ExtClassLoader
AppClassLoader
nio
jni
使得本机代码可以调用java方法,native memory
javaweb
前端
HTTP
超文本传输协议客户端和服务器交互的一种通讯格式
Http协议是⽆状态的协议
⼀旦数据提交完后,浏览器和服务器的连接就会关闭,再次交互的时候需要重新建⽴新的连接
当点开链接就能打开网页
HTTP1.0和1.1的区别
HTTP1.0
客户端与web服务器建立连接后 只能获得一个资源短连接 获取资源就断开连接
HTTP1.1
在一个连接能获取多个资源保持连接
HTTP请求
请求行
请求方式 请求url 请求协议/版本\t\tGET /login.html\tHTTP/1.1
请求头
实现从浏览器向服务器**隐式**传递数据题
请求空行
空行,就是用于分割POST请求的请求头,和请求体的。
请求体
* 封装POST请求消息的请求参数的
HTTP响应
响应行
服务器对请求的处理结果
响应头
用于服务器向浏览器发送隐式数据
空行
响应报头之后有个空行,负责将 响应头 跟 响应体分隔开。
响应体
传输的数据响应正文中可以包含 字符数据,也可以包含 字节数据
Tomcat
运行java的一个网络服务器底层是Socket的⼀个程序,它也是JSP和Serlvet的⼀个容器。
.浏览器访问WEB资源的流程
域对象
可以在不同Servlet之间传递数据的对象,就是域对象
四个域对象
request域对象
request里面的数据只有当前的一个请求域内也就是请求转发前后的servlet可以共享
作用范围一次请求
ServletContext对象
作用域范围全局
所有Servlet都共享着⼀个ServletContext对象,所以Servlet之间可以通过ServletContext实现通讯。
Session对象
HttpSession
作用域范围一次会话
PageContext
PageContext对象是JSP页面中才有的对象。
当对JSP的请求开始时创建,当响应结束时销毁。
Servlet
Servlet概念
8080/d1 8080/d2 两个Servlet对象
Servlet细节
Servlet是单例的
浏览器多次对Servlet的请求,⼀般情况下,服务器只创建⼀个Servlet对象,也就是说,Servlet对象⼀旦创建了,就会驻留在内存中,为后续的请求做服务,直到服务器关闭。
每次访问请求对象和响应对象都是新的
对于每次访问请求,Servlet引擎都会创建⼀个新的HttpServletRequest请求对象和⼀个新的HttpServletResponse响应对象,然后将这两个对象作为参数传递给它调⽤的Servlet的service()⽅ 法,service⽅法再根据请求⽅式分别调⽤doXXX⽅法。
线程安全问题
当多个⽤户访问Servlet的时候,服务器会为每个⽤户创建⼀个线程。当多个⽤户并发访问Servlet共享资源的时候就会出现线程安全问题。
原则
1. 如果⼀个变量需要多个⽤户共享,则应当在访问该变量的时候,加同步机制synchronized (对象){}2. 如果⼀个变量不需要共享,则直接在 doGet() 或者 doPost()定义.这样不会存在线程安全问题
load-on-startup
1. 为web应⽤写⼀个InitServlet,这个servlet配置为启动时装载,为整个web应⽤创建必要的数据库表和数据2. 完成⼀些定时的任务【定时写⽇志,定时备份数据】
代表当前web的站点ServletContext对象可以被称之为域对象
ServletContext既然代表着当前web站点,那么所有Servlet都共享着⼀个ServletContext对象,所以Servlet之间可以通过ServletContext实现通讯。
ServletConfig获取的是配置的是单个Servlet的参数信息,ServletContext可以获取的是配置整个web站点的参数信息
利⽤ServletContext读取web站点的资源⽂件
实现Servlet的转发【⽤ServletContext转发不多,主要⽤request转发】
Servlet之间实现通讯
获取web站点配置的信息
要所有的Servlet都能够获取到连接数据库的信息,不可能在web.xml⽂件中每个Servlet中都配置
web.xml⽂件⽀持对整个站点进⾏配置参数信息【所有Servlet都可以取到该参数信息】
读取资源⽂件
四种方式
通过类装载器读取资源⽂件。我的⽂件放在了src⽬录下【也叫做类⽬录】
如果⽂件太⼤,就不能⽤类装载器的⽅式去读取,会导致内存溢出
添加包名即可
直接使用字节流
以前读取⽂件的时候,如果程序和⽂件在同⼀包名,可以直接通过⽂件名称获取现在却报500错误
原因很简单,以前我们写的程序都是通过JVM来运⾏的,⽽现在,我们是通过Tomcat来运⾏
Servlet编译后的class⽂件是存放在WEB-INF\\classes⽂件夹中的要进⼊classes⽬录中读取⽂件 只能通过绝对路径
常用 获取服务器路径真实地址
Request对象
HttpServletRequest对象代表客户端的请求
应用
防盗链
解决乱码的问题 详情见笔记
获得post传来的字符
request.setCharacterEncoding(\"UTF-8\");String name = request.getParameter(\"name\");
获得request传来的
//此时得到的数据已经是被ISO 8859-1编码后的字符串了,这个是乱码String name = request.getParameter(\"username\");//乱码通过反向查ISO 8859-1得到原始的数据byte[] bytes = name.getBytes(\"ISO8859-1\
或者更改 tomcat 的connect
所以提交数据还是用response好
转发
Servlet之间可以通过ServletContext实现通讯,ServletContext也能称之为域对象。⽽request也可以称之为域对象,只不过ServletContext的域是整个web应⽤,⽽request的域仅仅代表⼀次http请求
不要在转发之前写数据给浏览器
Response对象
response对象代表http响应,那么我们向浏览器输出数据,找response对象
解决中文乱码的问题
一步搞定 推荐response.setContentType(\"text/html;charset=UTF-8\");//获取到printWriter对象PrintWriter printWriter = response.getWriter();printWriter.write(\"看!\");
//原本浏览器是ISO 8859-1的编码,我设置成UTF-8response.setCharacterEncoding(\"UTF-8\");然后浏览器也要设置接收utf-8 默认是gbk//设置头信息,告诉浏览器我回送的数据编码是utf-8的response.setHeader(\"Content-Type\
重定向跳转
response.sendRedirect(\"/z/index.jsp\");
会话技术
会话
什么是会话
会话:一次会话中包含多次请求和响应。
解决的问题
客户端会话技术Cookie
Cookie流程
Cookie API
不可跨域名性,设置Cookie的域名
Cookie使用中文
Cookie的有效期
Cookie的修改和删除
Cookie的路径
Cookie的安全性
在浏览器保存的时间
Cookie的共享问题
setPath
setDomain
Cookie的应用
显示上次的访问情况
服务器端会话技术Session
⽤户使⽤浏览器访问服务器的时候,服务器把⽤户的信息以某种的形式记录在服务器
与Cookie对比
Session可以解决Cookie解决不了的事情【Session可以存储对象,Cookie只能存储字符串
cookie记录在浏览器 session记录在服务器
API
Session是依赖于cookie的
面试题
转发和重定向的区别
* 重定向的特点:redirect\t\t\t1. 地址栏发生变化\t\t\t2. 重定向可以访问其他站点(服务器)的资源\t\t\t3. 重定向是两次请求。不能使用request对象来共享数据\t\t* 转发的特点:forward\t\t\t1. 转发地址栏路径不变\t\t\t2. 转发只能访问当前服务器下的资源\t\t\t3. 转发是一次请求,可以使用request对象来共享数据
转发和重定向使用哪个
1. 转发: 访问 Servlet 处理业务逻辑,然后 forward 到 jsp 显示处理结果,浏览器⾥ URL 不变2. 重定向: 提交表单,处理成功后 redirect 到另⼀个 jsp,防⽌表单重复提交,浏览器⾥ URL 变了
转发是带着转发前的请求的参数的。重定向是新的请求
Servlet生命周期
get和post的区别
forward和redirect的区别
cookie和session的区别
tomcat容器是如何创建servlet类实例?⽤到了什么原理?
Servlet安全性问题
过滤器
过滤一些敏感的字符串
避免中文乱码
权限验证
详情见笔记
监听器
统计网站在线人数
监听Session是否被创建了如果Session被创建了,那么在Context的域对象的值就应该+1如果Session从内存中移除了,那么在Context的域对象的值就应该-1.
A发生了变化,B需要依赖A发生的变化做出处理
监听器就是一个实现特定接口的普通Java程序,这个程序专门用于监听一个Java对象的方法调用或属性改变,当被监听对象发生上述事件后,监听器某个方法将立即被执行。
JDBC
AJAX与JSON
SSM框架
Spring
IOC
IOC和DI
IOC控制反转
DI依赖注入
依赖注入是ioc的一种实现方式(还有依赖查找 不用)
IOC容器
Spring容器(Bean工厂)
BeanFactory 基础 面向Spring
ApplicationContext
在BeanFactory基础之上 面向spring框架开发
两者区别
应用中bean的实例化,获取,销毁等都是由这个bean工厂管理的
IOC容器装配Bean
装配Bean方式
xml ,注解, java配置
注入方式
属性注入 setter()方法
使用注解注入时 set可以不用
构造函数注入
工厂注入
Bean的作用范围和生命周期
作用域
单例Singleton
多例prototype
与web应用有关的Bean作用域
request
session
globalSession
使用与web有关的Bean需要手动设置代理
处理自动装配的歧义性
使用@Primary注解设置为首选的注入Bean
使用@Qualifier注解设置特定名称的Bean来限定注入
引用属性文件及Bean属性
引用配置文件的数据 ${}
引用Bean的属性使用 #{}
AOP
AOP原理
底层是动态代理
JDK动态代理(基于接口)
CBLib动态代理(基于类)
AOP术语
连接点(Join Point)
能够被拦截的地方
切点(Poincut)
joinpoint的集合 很多个连接点的集合
增强/通知(Advice)
作用到连接点的哪个地方 作用的内容是什么
织入(Weaving)
将增强/通知添加到目标类对策具体连接点的过程
引入/引介(Introduction)
允许我们向现有的类添加新方法或属性,是一种特殊的增强
切面(Aspect)
连接点 切点 通知所在的那个类 为切面
Spring对AOP的支持
@AspectJ注解驱动的切面:注解 方便
知识点
增强方式
前置 后置 环绕增强
异常抛出增强
引入/引介增强
切面类型
一般切面
切点切面
引介/引入切面
切点函数
方法切点函数 execution使用多
事务
事务隔离级别
TransactionDefinition.ISOLATION_DEFAULT
使用后端数据库默认的隔离级别
Mysql 默认采用的 REPEATABLE_READ隔离级别 Oracle 默认采用的 READ_COMMITTED隔离级别
读未提交
TransactionDefinition.ISOLATION_READ_UNCOMMITTED
最低的隔离级别
允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读
读已提交
TransactionDefinition.ISOLATION_READ_COMMITTED
允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生
可重复读
TransactionDefinition.ISOLATION_REPEATABLE_READ
对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
串行化
TransactionDefinition.ISOLATION_SERIALIZABLE
最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别
事务的传播行为
支持当前事务的行为
REQUIRED
如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。一般的选择(默认值)
SUPPORTS
支持当前事务,如果当前没有事务,就以非事务方式执行(没有事务)
其他
MANDATORY
如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。(mandatory:强制性)
Mybatis
sqlSession原理
缓存
SpringMvc
DisptachServlet
请求映射
参数绑定与转换
页面渲染
分支主题
自由主题
0 条评论
回复 删除
下一页