中山网站的优化,建公司网站哪家公司好,做ui设计一年后年薪多少,做公司网站的好处以及优势Java基础
本文章是作者的学习笔记#xff0c;帮助初学者快速入门#xff0c;内容讲的不是很细#xff0c;适合初学者#xff0c;不定时更新。 目录 Java基础数据类型1.基本类型(primitive type)1-1 整数类型1-2 浮点类型1-3 字符类型1-4 boolean类型 2.引用数据类型3.类型…Java基础
本文章是作者的学习笔记帮助初学者快速入门内容讲的不是很细适合初学者不定时更新。 目录 Java基础数据类型1.基本类型(primitive type)1-1 整数类型1-2 浮点类型1-3 字符类型1-4 boolean类型 2.引用数据类型3.类型转换3-1 内存溢出和强制转换3-2 自动类型转换 变量1.类变量(静态变量)2.实例变量3.局部变量4.常量 运算符4⭐三元运算符 顺序结构与循环1-1 if选择结构略 1-2 Switch选择结构2switch高级应用 1-3 While循环1-4 For循环详解0增强for循环1break2continue3goto 方法1-1 可变参数1-2 递归1-3 异常1-4 方法的调用1-4-1 静态方法1-4-2 非静态方法1-4-3 形参1-4-4 引用传递 1-5 构造方法 数组1-1 数组的创建方式1-2 数组的遍历方式1-3 冒泡排序1-4 稀疏数组 面向对象⭐本质1-1封装1-2继承1-2-1 super关键字 1-3多态1-3-1 方法重写 1-4 抽象1-5 接口1-6 内部类 集合1-1 迭代器1-2 List1-2-1 ArrayList1-2-2 Vector1-2-3 LinkedList底层逻辑添加删除 1-2-4 List集合选择 1-3 Set1-3-1 HashSet⭐面试题 1-4 Map1-4-1 HashMap1-4-2 HashMap和HashTable的区别和底层实现 泛型反射1-1 反射入门方法1-2 优点和缺点1-3 Class 类1-3-1 获取Class的几种方式 1-4 动态和静态加载1-5 获取类的结构信息1-6 通过反射爆破创建对象动态代理 IO1-1 IO流原理1-2 流的分类1-2-1 InputStream1-2-2 OutputStream 1-3 文件拷贝1-4 文件字符流1-5 节点流/处理流1-5-1 BufferedReader1-5-2 两者关系 1-6 BufferedReader 数据库Redis1.Nosql1-1 索引1-1-1.引入缓存1-2-1.分库分表水平拆分MYSQL集群 2.Nosql四大分类3.redis基本数据类型String类型List类型Set类型Hash类型Zset有序集合 4.redis特殊数据类型geospatial 地理位置 5.事务acidJedisLettce Java高级Spring1.IOC/DI/AOP(1)控制反转IOC(2)依赖注入DI JVM虚拟机1-1 JVM的位置1-2 类加载1-3 沙箱安全机制1-4 Native关键字1-5 PC寄存器和方法区1-5-1 PC寄存器1-5-2 方法区 1-6 栈与队列1-7 堆1-7-1 堆内存的组成代码演示 1-8 GC 线程1-1 创建线程的方式1-2 线程常用方法1-3 线程的种类1-4 线程的生命周期1-6 死锁 多线程/高并发1.并发基础1-1 互斥同步1-2 非阻塞同步1-3 指令重排1-4 synchronized重量级锁1-5 volatile1-6 关键字的比较 2.锁2-1 自旋锁2-2 偏向锁2-3 ReentrantLock可重入锁2-3-1 锁的操作 3.线程池4. 并发容器5.JUC5-1executor5-2collections5-3locks5-4 atomic5-5 tools 数据结构与算法1.线性结构和非线性结构设计模式1-1 单例模式1-1-1 饿汉式 1-2 工厂模式1-3原型模式 主流框架和项目管理微服务概念1.spring cloud2.Nacos注册中心3.Feign远程调用从这开始到Http Client结束4.Hystrix熔断器 5.RibbonNginxRabbitMQ中小型公司卡夫卡大型公司1-1流量消除峰值1-2 SSMspringspringMVCMyBatis 项目管理MavenGit 网络1.TCP/IP font colorred如font colororange果font color#FFEB05你font colorgreen也font colorskyblue想font colorblue学font colorpurple习font colorblack:黑客网络安全的零基础攻防教程在这里领取这个是我花了几天几夜自整理的最新最全网安学习资料包免费共享给你们其中包含以下东西1.学习路线职业规划2.全套体系课入门到精通3.黑客电子书面试资料4.漏洞挖掘工具和学习文档 ❤❣ ♥დღ♡❣❤❥❦❧♥
数据类型
1.基本类型(primitive type)
1-1 整数类型
byte占一个字节 short占两个字节
int占四个字节⭐常用
long占八个字节long类型要在数字后面加L
1-2 浮点类型
1float占四个字节float类型要在数字后面加F
2浮点数是约莫类型x过大时,结果无限接近但不等于x
因此用float和double比较会出现错误。
⭐1银行运算不用浮点数而使用BigDecimal等数学工具类
double占八个字节⭐常用
1-3 字符类型
char占两个字节
1-4 boolean类型
true 和 false
2.引用数据类型
⭐和equals的区别
String str1 hello;//常量池,String是用final修饰的常量类型
String str2 new String(hello);//实例化的String对象在堆内存引用在栈内存
String str3 str2;//引用传递传递的是引用地址栈
str1 str2;//false
str1 str3;//false
str2 str3;//true
//equals本身和没有区别但是String类重写了equals方法所以这个equals实际上是比较两个值的内容地址不比1类Class
2接口interface
3数组引用
3.类型转换
3-1 内存溢出和强制转换
1byte的取值范围-128~127
int i 128;
⭐byte b (byte)i;//此处内存溢出变成-128
2类型变量名强转高到低
3-2 自动类型转换
低到高的转换他会自动转换
⭐转换优先级
低 ------------------------------------------- 高
byteshortchar→int→long→float→double
⭐注意1不能对布尔值进行转换 2不能把对象转换为不相干的类型3精度丢失转换时默认向0取整比如int)23.7 23 (int)-45.89f -45变量
1.类变量(静态变量)
static关键词全局的静态的
2.实例变量
1从属于对象使用需要实例化类
2如果不进行初始化则它的值为这个类型的默认值00.0
3.局部变量
1必须声明和初始化值
4.常量
定义常量是一种特殊的变量初始化initialize之后不会变动
final 常量名 值;
运算符
1基本运算符略
2自增自减略
3位运算符略
4⭐三元运算符
x ? y : z
如果xtrue,则结果为y,否则结果为z
顺序结构与循环
1-1 if选择结构
略
1-2 Switch选择结构
2switch高级应用
多条件
switch(grade){case A:case B:System.out.println(shit);break;default:System.out.println(什么都没有)}1-3 While循环
while(i100){//计算123...100sumi;i;}do-while循环
⭐区别do-while至少执行一次因为他是先执行后判断。
1-4 For循环详解
for(初始化;布尔表达式;更新){//代码语句}0增强for循环
for局部变量:访问数组名){
}
for(int a;aList){System.out.println(aa)
}1break
强行退出此次循环。
2continue
只用于循环语句中。
用于终止某一次循环过程直接进行下一次循环。
3goto
在break或者continue后加label不要求掌握比较麻烦
方法
1-1 可变参数
在定义方法时在最后一个形参后加上三点 … 就表示该形参可以接受多个参数值多个参数值被当成数组传入。上述定义有几个要点需要注意
可变参数只能作为函数的最后一个参数但其前面可以有也可以没有任何其他参数由于可变参数必须是最后一个参数所以一个函数最多只能有一个可变参数Java的可变参数会被编译器转型为一个数组变长参数在编译为字节码后在方法签名中就是以数组形态出现的。这两个方法的签名是一致的不能作为方法的重载。如果同时出现是不能编译通过的。可变参数可以兼容数组反之则不成立
⭐简单来说你就把他看成数组就行了
1-2 递归
递归包含以下两个部分
⭐递归头表示什么时候不调用自身方法。如果没有头就会死循环
⭐递归体表示什么时候需要调用自身方法
public static void main(String[] args){System.out.println(f(4));
}
public static int fuck(int n){if(n1){//递归体表示什么时候需要递归return 1;}else{//递归头也就是递归的结束条件return n*f(n-1);}
}1-3 异常
https://www.runoob.com/java/java-exceptions.html
1-4 方法的调用
静态方法隶属于类非静态方法隶属于对象
1-4-1 静态方法
调用形式类名.方法名
1-4-2 非静态方法
先实例化
Student student new Student调用形式对象名.方法名
1-4-3 形参
形参不同于实参他只是一个做事的临时工具而并不是这件事的组成部分。
↓↓↓↓↓↓↓↓↓↓↓
1-4-4 引用传递
当形参是一个对象的时候传参传递的是引用的地址
public class Demo{public static void main(String[] args){Person person new Person();System.out.println(person.name);//nullDemo.change(person);//调用同类型方法需要用类名.方法名//因为都是静态方法可以直接调用非静态方法需要newSystem.out.println(person.name);//sb}public static void change(Person person){//person是一个对象指向的↓//Person person new Person();这是一个具体的人可以person.name sb;}
}
//定义了一个Person类有一个属性name
class Person{String name;//null
}1-5 构造方法
在类创建的时候默认会创建一个构造方法
特点1.必须和类名相同 2.必须没有返回类型也不能写voidPerson p new Person();//Person()就是构造方法作用1.实例化初始值
public Person()//无参构造this.name liubei;
}2.使用new关键字必须要有构造器public Person(String name){//有参构造this.name name;//this.name是对象的name。后面的name是传的形参。
}⭐注意一旦定义了有参无参也必须显示定义
数组
1-1 数组的创建方式
dataType[ ] arrayRefVar new dataType[ arraySize ];//⭐常用
dataType arrayRefVar[ ];
1-2 数组的遍历方式
1for-each循环
int[] arrays {1,2,3,4,5};
for(int array:arrays){//这里的数组不需要加[]System.out.println(array);//但这里没有带下标
}
//如果我们要打印数组的元素则用下面的方法
public static void printArray(int[] arrays){for(int i0;iarrays.length;i){System.out.print(array[i]);}
}1-3 冒泡排序
public static void main(String[] args){}
public static int[] pao(int[] array){//int临时变量int temp 0;for(int i0;iarray.length();i){for(int j0;jarray.length()-1-i;j){if(array[j]array[j1]){temparray[j];array[j]array[j1];array[j1]temp;}}}
}冒泡排序的时间复杂度为O(n2);//这里的n2指两次有关长度的操作
1-4 稀疏数组
处理方式 1.记录数组一共有几行几列有多少个不同的值2.把具有不同值的元素和行列及值记录在一个小规模的数组中面向对象
OOPObject-Oriented Programming
面向过程思想 1.步骤清晰简单第一步干啥第二步干啥2.面对过程适合处理一些简单的问题比如烧水..⭐面向对象思想 1.物以类聚**分类** 的思维模式。各部门分工合作大块分小块。2.面向对象适合处理复杂的问题适合处理需要多人合作的问题。⭐本质
面向对象编程的本质就是以类的方式组织代码以对象的组织封装数据。
高内聚低耦合。
创建对象内存分析
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Jzze5tb6-1631855103577)(https://edu-10121.oss-cn-beijing.aliyuncs.com/MyJavaStudyImg/A](D_T((E6)]6GCM[TG53%4.png)
栈相当于目录堆相当于内容。每当你new一个对象都是属于“目录”某个章节的一页。比如你new一个“男人”就是“人”这一章节的其中一页。对象是用过引用来操作的由栈指向堆
1-1封装
重点属性私有化。get、set
1-2继承
extends 子类继承父类的所有属性方法但是私有属性方法无法被子类访问
减少重复代码维护性提高提高代码复用性代码更简洁
继承的缺点提高了耦合性面向对象要求低耦合
1-2-1 super关键字
super只能出现在子类的方法或者构造方法中。
super.属性名/方法名 用来调用父类的属性或方法
子类默认会调用父类的无参构造函数super()在第一行调用)
如果没有无参构造调用有参构造也可以。
⭐注意不能与this同时用来调用构造方法
1-3多态
Student s1 new Student();
Person s2 new Student();
Object s3 new Student();
//引用类型可以是父类实际类型是确定的 new Student⭐多态是方法的多态属性和变量没有多态
⭐简单来说静态看左非静态看右静态方法不能被重写
原理向上转型里式代换原则(LSP)dd
ps向下转型则是强制转换 子类 子类的对象 (子类)父类的对象
1-3-1 方法重写
⭐重点父类的引用指向子类的对象
示例1个行为,不同的对象,他们具体体现出来的方式不一样, 比如: 方法重载 overloading 以及 方法重写(覆盖)override class Human{ void run(){输出 人在跑} } class Man extends Human{ void run(){输出 男人在跑} } 这个时候,同是跑,不同的对象,不一样(这个是方法覆盖的例子) class Test{ void out(String str){输出 str} void out(int i){输出 i} } 这个例子是方法重载,方法名相同,参数表不同 ok,明白了这些还不够,还用人在跑举例 Human ahumannew Man(); 这样我等于实例化了一个Man的对象,并声明了一个Human的引用,让它去指向Man这个对象 意思是说,把 Man这个对象当 Human看了. 比如去动物园,你看见了一个动物,不知道它是什么, 这是什么动物? 这是大熊猫! 这2句话,就是最好的证明,因为不知道它是大熊猫,但知道它的父类是动物,所以, 这个大熊猫对象,你把它当成其父类 动物看,这样子合情合理. 这种方式下要注意 new Man();的确实例化了Man对象,所以 ahuman.run()这个方法 输出的 是 男人在跑 如果在子类 Man下你 写了一些它独有的方法 比如 eat(),而Human没有这个方法, 在调用eat方法时,一定要注意 强制类型转换 ((Man)ahuman).eat(),这样才可以... 对接口来说,情况是类似的... 1-4 抽象
⭐抽象类只有方法的名字没有方法的实现。
继承了抽象类的子类都必须实现抽象类的所有方法。
1-5 接口
⭐接口Interface只有规范
接口中所有的方法都是**公共public且抽象abstract**的
只写返回值类型和方法名就行了。
⭐类可以实现接口
1.implements关键字实现
2.接口可以多继承extends
3.实现了接口的类需要实现接口中的所有方法。
1-6 内部类
public class hh{int a 0;class int xx{System.out.println(a);//内部类可以用外部的属性和方法}
}集合
⭐hashCode与equals的区别
hashCode相等对象值不一定相等 数组存在很多缺点比如
1.长度开始时必须指定而且一旦指定不能更改
2.保存的为同一类型的值 3.代码比较繁琐
于是集合他来了 Collection 接口的实现子类单列集合存放单个数据
Map 接口的实现子类 双列集合键值对形式
⭐Map的实现子类还有HashtableProperties是Hashtable的实现子类
⭐List的实现还有Vector
1-1 迭代器
Collection中的Iterator用于遍历集合
1hasNext()方法判断后面是否还有元素。
2next()方法下移并将下移之后集合位置上的元素返回。
⭐用next()之前必须用hasNext()不然会抛出异常NoSuchElementException
while(iterator.hasNext()){Object obj iterator.next();//下移并将下移之后集合位置上的元素返回。System.out.println(obj);//输出next()方法返回
}1-2 List
HashCode的特性
1HashCode的存在主要是用于查找的快捷性如HashtableHashMap等HashCode经常用于确定对象的存储地址
2如果两个对象相同 equals方法一定返回true并且这两个对象的HashCode一定相同
3两个对象的HashCode相同并不一定表示两个对象就相同即equals()不一定为true只能说明这两个对象在一个散列存储结构中。
4如果对象的equals方法被重写那么对象的HashCode也尽量重写。
本文原创转载请注明出处http://blog.csdn.net/seu_calvin/article/details/52094115
HashCode作用
Java中的集合有两类一类是List再有一类是Set。前者集合内的元素是有序的元素可以重复后者元素无序但元素不可重复。 equals方法可用于保证元素不重复但如果每增加一个元素就检查一次若集合中现在已经有1000个元素那么第1001个元素加入集合时就要调用1000次equals方法。这显然会大大降低效率。 于是Java采用了哈希表的原理。
哈希算法也称为散列算法是将数据依特定算法直接指定到一个地址上。这样一来当集合要添加新的元素时先调用这个元素的HashCode方法就一下子能定位到它应该放置的物理位置上。
1如果这个位置上没有元素它就可以直接存储在这个位置上不用再进行任何比较了
2如果这个位置上已经有元素了就调用它的equals方法与新元素进行比较相同的话就不存了
3不相同的话也就是发生了Hash key相同导致冲突的情况那么就在这个Hash key的地方产生一个链表将所有产生相同HashCode的对象放到这个单链表上去串在一起。这样一来实际调用equals方法的次数就大大降低了几乎只需要一两次。
int indexOf(obj);//返回obj在集合中第一次出现的位置int1-2-1 ArrayList 是线程不安全的可以看源码没有synchronized所以多线程不用ArrayList可以考虑用Vector 底层结构
(1) ArrayList中维护了一个Object类型的数组elementData.
transient Object[] elementData;//transient 表示瞬间的短暂的。表示该属性不会被序列化。(2) 当创建ArrayList对象时如果使用的是无参构造器则初始elementData容量为0第1次添加扩容为10如需要再次扩容则扩容为1.5倍
//0-第一次10-第二次15-第三次22以此类推(3) 如果使用的是指定int大小的构造器那么elementData初始容量为指定的int值如果要扩容则扩容为1.5倍
//你指定的int值-int*1.5源码
public boolean add(E e){//e:1//执行list.add先确定是否要扩容ensureCapacityInternal(size 1);//然后再执行赋值elementData[size] e;return true;
}private void ensureCapacityInternal(int minCapacity){//用来确认最小容量//首次确认最小容量if(elementData DEFAULTCAPACITY_EMPTY_ELEMENTDATA){minCapacity Math.max(DEFAULT_CAPACITY,minCapacity);//比较默认值与最小值1然后将其大值赋给minCapacity}ensureExplicitCapacity(minCapacity);//继续确认最小容量
}
private void ensureExplicitCapacity(int minCapacity){//确认完最小容量然后进行判断modCount;//记录集合被修改的次数if(minCapacity - elementData.length0)//判断容量够不够grow(minCapacity);//进行扩容使用的copyof
}1-2-2 Vector
Vector是线程安全的。它的方法带有synchronized 1-2-3 LinkedList
(1) LinkedList底层维护了一个双向链表
(2) LinkedList中维护了两个属性 first 和 last 分别指向 首节点 和 尾结点
(3) 每个结点里面又维护了三个属性prev,next,item,其中通过next指向后一个节点的地址prev指向前一个地址。数据的添加不是通过数组完成无需扩容所以相对效率高。
底层逻辑
首先是新建一个Node结点有三个属性。只有一个值得时候prev和next都是null
只有item值并且first和last都指向这个Node结点。
有多个结点时第一个加入的使它等于first最后一个使它等于last前后根据prev和next来指向地址形成双向链表
添加
void linkLast(E e){//Node添加的底层final NodeE l last;final NodeE newNode new Node(l,e,null);//新建一个Nodelast newNode;//管他三七二十一先把最新的这个Node设为lastif(l null)//如果这个Node是第一个(null)那就把他设为firstfirst newNode;elsel.next newNode;//如果这个Node不是第一个就吧上一个的next指向这个Nodesize;//结点1modCount;//运行计数1
}删除
private E unlinkFirst(NodeEf){//例子删除第一个final E element f.item;final NodeE next f.next;f.item null;f.next null;first next;//此时原先的第一个结点就变成了孤儿等待被垃圾回收if(next null)last null;elsenext.prev null;size--;//结点-1modCount;//运行计数1return element;
}1-2-4 List集合选择 1-3 Set
1.set接口对象接口的实现类的对象不能存放重复的元素可以放null
2.set接口对象存放数据是无序的
3.取出对象的顺序虽然不是添加的顺序但是他是固定的不会存在第一次第二次取出不一样顺序的情况
1-3-1 HashSet
add()方法执行时会返回一个boolean
set.add(liubei);//true
set.add(liubei);//false,常量池重复
set.add(new Dog(wangcai));//true
set.add(new Dog(wangcai));//true,开辟一个新地址,HashCode不一样⭐面试题
set.add(new String(lsp));//true
set.add(new String(lsp));//false
/*以这种方式赋值时JVM会先从字符串实例池中查询是否存在lsp这个对象若不存在则会在实例池中创建lsp对象同时在堆中创建lsp这个对象然后将堆中的这个对象的地址返回赋给引用str。若实例池存在则直接在堆中创建test这个对象然后将堆中的这个对象的地址返回赋给引用str。*/
底层HashMap而HashMap的底层是数组链表红黑树 为什么相同的元素就加不了呢
他有一个hash()和equals()两个方法用来比较相同的元素两个方法都成立时则不允许你的元素添加。
源码
1.add
(1)执行hashSet()public HashSet(){
map new HashMap()
}(2)执行add()public boolean add(E e){//E是泛型e是我们输入的对象return map.put(e,PRESENT)null;//PRESENT是固定对象
}(3)执行put(),该方法会执行hash(key)得到key对应的哈希值public V put(K key,V value){return putVal()
}(4)HahMap下面新建一个table数组是Node[]类型的然后将hash值key你输入的值value地址nextnull放入这个table对应的位置。执行前面两个if语句如果当前table是null或者大小为0.进行第一次扩容到16。
(5)添加失败的情况前面两个if语句是false然后执行else
final V putVal(int hash,K key,V value,boolean onlyIfAbsent,boolean evict){NodeK,V[] tab;NodeK,V p;int n, i;if((tab table)null||(ntab.length)0)n(tabresize()).length;if((ptab[i(n-1)hash])null)tab[i] newNode(hash,key,value,null);
else {NodeK,V e; K k;if (p.hash hash ((k p.key) key || (key ! null key.equals(k) )))//这里的p指向数组链表第1个位置上的结点如果这次要加的key对象和p指向的这个Node里面的key和hash值一样并且两个key是同一个对象或者equals返回true 那就不能加入。e p;//如果false再判断是不是红黑树是就调用putTreeValelse if (p instanceof TreeNode)e ((TreeNodeK,V)p).putTreeVal(this, tab, hash, key, value);//和对应那个链表的每个值比较如果没有这个值就丢在链表//在添加元素到链表之后立即判断是否有八个结点如果有八个就调用treeufyBin()对当前链表树化树化过程有很多判断)else {for (int binCount 0; ; binCount) {if ((e p.next) null) {p.next newNode(hash, key, value, null);if (binCount TREEIFY_THRESHOLD - 1) // -1 for 1sttreeifyBin(tab, hash);break;}if (e.hash hash ((k e.key) key || (key ! null key.equals(k))))break;p e;}}if (e ! null) { // existing mapping for keyV oldValue e.value;if (!onlyIfAbsent || oldValue null)e.value value;afterNodeAccess(e);return oldValue;}}1-4 Map
核心key-value键值对
1.Map中的key和value可以是任何引用类型的数据最终会封装到HashMap$Node对象中
HashMap$Node node new Node(hash,key,value,null);2.k-v为了方便程序员的遍历还会创建EntrySet集合该集合存放的元素类型是Entry一个Entry包含k,v EntrySetEntryK,V
3.entrySet中定义的类型是Map.Entry,但是实际上存放的是HashMap$Node.
为什么因为HashMap$Node实现了Map.Entry接口这就是接口的多态
父类的引用Map.Entry指向子类的对象HashMap$Node)
map.put(Number1,hh);
map.put(Number1,xx);
//输出xx因为有相同的key时value会替换注意Key可以为空但只能有一个nullvalue也可以为null。常用String类作为key
⭐总结HashMap里面有一个table一个entrySet集合table用来装Node结点而每个Node的key和value会被封装成entry对象放到entrySet集合中去而entrySet看似存放的是Map.Entry其实是HashMap N o d e 因为 H a s h M a p Node因为HashMap Node因为HashMapNode实现了Map.Entry,存的东西说白了就是table中Node结点的引用对象。
1-4-1 HashMap
HashMap的扩容机制
1.第一次添加需要扩容为16临界值为1216*0.7512
2.以后在扩容扩容table为原来的2倍临界值为之前的2倍24以此类推
3.java8以后如果一跳链表元素超过TREEIFY_THRESHOLD(默认8),并且table大小大于等于MIN_TREEIFY_CAPACITY(默认64)就会树化增加效率。
1-4-2 HashMap和HashTable的区别和底层实现
HashMap线程不安全HashTable线程安全。HashMap允许k和v为空HashTable不许。
HashTable相当于给所有方法加了synchronized关键字效率低于是乎我们现在一般用ConcurrentHashMap
底层实现
数组链表红黑树
JDK8开始链表高度到8数组长度超过64链表转化为红黑树元素以内部类结点Node形式存在。
1计算key的hash值然后二次hash对数组长度取模然后给他在数组安排位置没有hash冲突就直接将数据存为一个Node2如果产生冲突则进行equals比较相同则取代不同则判断链表高度插入链表链表高度达到8并且数组64则变为红黑树长度低于6又会变回链表3扩容因子为什么是0.75这里回答一下取其他值的缺点就行了如果是0.5 每次水加到一半就增加杯子的大小那杯子的空间利用率肯定会越来越小。 如果是1每次水装满再增加杯子的大小那肯定会浪费时间装满了就不能装了只能等你先增加杯子的大小
泛型
相当于给你限定一个数据类型E这个E可以是Stringint等一旦限定这个参数以后传的就只能是你限定的类型
反射
1-1 反射入门方法
反射java.lang.reflect)就是还没有new的时候就可以使用这个类相当于一个镜子 从这个简单的例子可以看出一般情况下我们使用反射获取一个对象的步骤
1.获取类的 Class 对象实例
Class clz Class.forName(com.zhenai.api.Apple);//Class对象java.lang.Class)表示某个类加载后在堆中的对象,就是这个com.zhenai.api.Apple2.根据 Class 对象实例获取 Constructor 对象
Constructor appleConstructor clz.getConstructor();3.使用 Constructor 对象的 newInstance 方法获取反射类对象
Object appleObj appleConstructor.newInstance();4.而如果要调用某一个方法则需要经过下面的步骤
获取方法的 Method 对象
Method setPriceMethod clz.getMethod(setPrice, int.class);利用 invoke 方法调用方法
setPriceMethod.invoke(appleObj, 14);1-2 优点和缺点
反射的优点
可以动态创建和使用对象也是框架底层核心使用灵活
反射的缺点
使用反射基本是解释执行而不是编译执行对执行速度有影响.编译快
//传统方法调用方法
public void hh(){Man man new Man();long start System.currentTimeMillis();for(int i0;i100000;i){Man.fuck();}long end System.currentTimeMillis();System.out.println(end-start);
}
//反射的方法
public void hh2() throws Exception{Class cls class.forName(com.yc.Man);Object o cls.newInstance();Method fuck cls.getMethod(fuck);long start System.currentTimeMillis();for(int i0;i100000;i){fuck.invoke(o);}long end System.currentTimeMillis();System.out.println(end-start);
}
//很明显普通方法0ms反射需要几百到几千ms
//如何优化用setAccessible禁用访问安全检查速度就会快很多
fuck.setAccessible(true);
fuck.invoke(o);//true表示禁用1-3 Class 类
//Class类对象不是new出来的而是系统创建的
不管是传统方法还是反射方式都是通过ClassLoader来加载类的Class对象
//某个类的Class类对象在内存中只有一个因为类只加载一次这个对象存在堆1-3-1 获取Class的几种方式
1.配置文件读取类全路径加载类
Class cls1 Class.forName(java.lang.xx);2.参数传递用反射获得构造器等
Class cls2 xx.class;3.通过创建好的对象获取Class对象
XX xx new XX();
Class cls3 xx.getClass();4.其他
四种类加载器去JVM看
1-4 动态和静态加载
1.静态加载普通编译时就加载相关的类如果没有则报错依赖性强
2.动态加载反射运行时并执行到相应代码的时候才加载相关的类
switch(key){case1:Dog dog new Dog();dog.cry();break;//静态加载//你只要运行就会报错因为没有Dog这个类case2;Class cls Class.forName(Person);Object o cls.newInstance();Method m cls.getMethod(hi);m.invoke(o);break;//动态加载//除非你代码走到case2不然不会报错default:
}1-5 获取类的结构信息
1-6 通过反射爆破创建对象
1.调用类中的public修饰的无参构造器
2.调用类中的指定构造器
3.Class类相关方法
newInstance调用类中的无参构造器获取对应类的对象
getConstructor根据参数列表获取对应public构造器的对象
getDecalaredConstructor根据参数列表获取所有对应构造器对象
⭐4.Constructor类相关方法
⭐setAccessible爆破
Constructor? c1 userClass.getDecalaredConstructor(int.class,String.class);
c1.setAccessible(true);//爆破暴力破解使反射可以访问private构造器
Object user2 c1.newInstance(100,张三丰);newInstance(Object…obj)调用构造器
动态代理
IO
文件保存数据的地方
File file new File(parentFile,fileName);
//相当于在内存创建了一个对象
file.createNewFile();
//真正把对象创建到硬盘中1-1 IO流原理
输入input 读取外部数据磁盘、光盘等存储设备的数据到程序内存中。
输出output 将程序内存数据输出到磁盘、光盘等存储设备中。
1-2 流的分类
抽象基类 字节流 字符流输入流 InputStream Reader输出流 OutputStream Writer
1-2-1 InputStream
read()方法用来输入
1-2-2 OutputStream
write()方法用来输出
FileOutputStream f null;
try{
f new FileOutputStream(filePath);// new FileOutputStream(filePath,true);这种方式会追加到文件后面而不是覆盖文件
String str你是个渣渣;
f.write(str.getBytes(),0,3);//getBytes可以将字符串转换为字符数组}
catch(IOException e){}
1-3 文件拷贝
//关键代码String firstPath e:\\1.png;String lastPath d:\\2.png;FileInputStream fileInputStream null;FileOutputStream fileOutputStream null;fileInputStream new FileInputStream(firstPath);fileOutputStream new FileOutputStream(lastPath);//定义一个字节数组提高效率byte[] buf new byte[1024];int readLen 0;while ((readLen fileInputStream.read(buf))! -1) {fileOutputStream.write(buf);//一定要写这个方法}System.out.println(拷贝完毕);if(fileInputStreamnull) {fileInputStream.close();}if(fileOutputStreamnull) {fileOutputStream.close();}1-4 文件字符流 输入流和字节流一样的玩法
1-5 节点流/处理流
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xmGbevhk-1631855103602)(…/Images/java/8.png)]
1-5-1 BufferedReader
BufferedReader中有属性Reader即可以封装一个节点流该节点流可以是任意。只要是Reader的子类。
public class BufferedReader extends Reader{private Reader in;private char cb[];
}1-5-2 两者关系
1.节点流是底层流/直接根数据源相连
2.处理流包装节点流即可以消除不同节点流的实现差异也可以提供更方便的方法来完成输入输出
3.处理流对节点流进行包装使用了修饰器设计模式不会直接与数据源相连
1-6 BufferedReader
String filePath e:\\a.java;
//创建bufferedReader
BufferedReader br new BufferedReader(new FileReader(filePath));
//读取
String line;//按行读取效率高
//readLine()是按行读取当返回null时表示文件读取完毕
while((line bufferedReader.readLine())!null){System.out.println(line);
}
//关闭流这里注意只需要关闭br因为底层会自动关闭节点流
bufferedReader.close();数据库
Redis
1.Nosql
刚开始所有机子都是用的单机mysql
后来为了减轻服务器压力开始改变
Nosql Not Only SQLredis是其中发展最好的
1-1 索引
索引是帮助MySql高效获取数据的排好序的数据结构
1-1-1.引入缓存
Memcached缓存垂直拆分MYSQL
1-2-1.分库分表水平拆分MYSQL集群
有些不会改动的数据把他放到A集群数据库中只需要读一次给他存到缓存里面要改的东西放在BC等集群数据库中。
MYISAM存储引擎索引实现表锁每次查东西要锁一个表效率很低
Innodb存储引擎索引实现行锁效率问题解决了
数据库不同的引擎存储的数据库表文件形式不同
以innoDB引擎举例
test.frm文件存表结构 innoDB.ibd文件存数据和索引文件
innoDB聚集索引因为他的数据和索引都存在ibd文件中而其他引擎是分开存的所以他遍历最方便的存储结构就是B树
为什么innoDB表必须有主键并且推荐自增
B树多叉平衡树
1非叶子节点不存储data只存储索引冗余可以放更多的索引
2叶子节点包含所有索引字段
3叶子节点用指针连接提高区间访问的性能
B树的叶子节点用指针连接提高区间访问的性能
我们知道HashMap的底层在最新的jdk里面加入了红黑树为什么我们数据库不用红黑树
首先红黑树根是黑色叶子是黑色每个红色节点必须有两个黑色子节点的底层数据结构是一种自平衡二叉查找树典型的用途是实现关联数组,对于插入密集型使用红黑树对于查找密集型使用AVL树
2.Nosql四大分类
KV键值对
*新浪redis
*美团redistair
*阿里redismemecache
文档型数据库bson格式和json一样
1MongoDB一般必须要掌握
是一个基于分布式文件存储的数据库由C编写用来处理大量的文档
是一个介于关系型数据库和非关系型数据库的中间产品它是非关系型数据库中功能最丰富最像关系型数据库的。
3.redis基本数据类型
redisRemote Dictionary Server远程字典服务
redis默认16个数据库0~15默认使用第0个
select x//切换为第x个数据库
keys * //查看所有key值
flushdb//删库别乱用
set 变量名 变量值//
exists name//检查name是否存在
move name//移出
expire name time//数据存放time秒
type name//查看name的类型incr name//使name1decr name//使name-1redis是单线程的因为它基于内存操作cpu不是redis性能瓶颈redis的瓶颈是内存和网络带宽。
redis是c语言写的官方提供的数据为十万的QPS每秒查询率比Memecache还厉害
String类型
# setex //(set with expire)设置过期时间
# setnx//(set if not exist)不存在则设置可用于分布式锁#msetnx //是一个原子性操作要么一起成功要么一起失败# getset xx yy//如果不存在xx则返回nil然后创一个xx值为yy//如果存在值则返回原来的值并设置新值yyList类型
# LPUSH list one//LPUSH 将一个值或多个值插入列表list头部
# RPUSH//插入在list尾部 #LRANGE list 0 -1//查询list中0位到-1位的值#LINDEX list 0//获取list中某一位的值两边插入效率高中间效率会低一点
Set类型
和List一样命令以S开头
Hash类型
就是Map集合key-value命令以H开头
Zset有序集合
在Set集合的基础上增加了一个值 Zset有多种排序的方法
4.redis特殊数据类型
geospatial 地理位置
看地理位置的
5.事务
事务的本质是一组命令的集合。一个事务中的所有命令都会被序列化在事务执行过程中会按照顺序执行。
编译型异常代码有问题命令有错事务中所有命令都不执行事务单个命令具有原子性事务不保证原子性因为事务异常发生后没有回滚
运行时异常命令语法错误
acid
一次性顺序性排他性
127.0.0.1:6379multi #multi开启事务
ok
127.0.0.1:6379set k1 v1#向事务加入语句
QUEUED
127.0.0.1:6379set k2 v2
QUEUED
127.0.0.1:6379set k3 v3
QUEUED
127.0.0.1:6379exec #执行事务
127.0.0.1:6379DISCARD #取消事务事务队列中的语句都不会执行127.0.0.1:6379watch money #监视money对象Jedis
以前用的
Lettce
现在用的
Java高级
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2o26uv2d-1644913012215)(https://segmentfault.com/img/remote/1460000022180990)]
Spring 1.IOC/DI/AOP
(1)控制反转IOC (2)依赖注入DI 步骤1
UserService userService new UserService;for(Field field : userService.getClass().getDeclaredFields()){//遍历获取对象所有的字段if(field.isAnnotationPresent(Autowired.class)){//判断遍历的对象是否有自动注入关键字field.set(userService1,??);//如果有则注入值}
}那么问题就来了这些对象是怎么创建的呢答案是spring用ApplicationContext.getBean为我们创建的是注入了值的。有值的称为bean对象了他是在实例化后将bean的引用和值注入到bean的属性中 而简单利用构造器无参构造是没值的 依赖注入之后把bean对象放入单例池Map中然后getBean就可以用了上面因为里面的名字一样相当于调用Map里面的同一个对象userService。
JVM虚拟机
面试题JDKJREJVM的区别 JDKjava开发工具包括JRE和java工具工具里面有javac JREjava运行时环境 包括jvm和apiapi用来解释.class文件 JVMjava虚拟机包含在JRE中
1-1 JVM的位置
JVM包括在JRE中在操作系统之上。 1-2 类加载 1.加载Loading
在该阶段主要目的是将字节码从不同的数据源可能是class文件jar包等转化为 二进制字节流加载到内存中 并生成一个代表该类的java.lang.Class对象
2.连接Linking
1验证阶段
为了确保Class文件中的字节流包含的信息符合当前虚拟机要求有元数据验证等
2准备阶段
JVM在该阶段对静态变量分配内存并默认初始化。这些变量所使用的内存都将在方法区中进行分配
class A{//n1是实例属性不是静态变量因此在准备阶段不会分配内存//n2是静态变量分配内存。n2默认为0而不是20//n3是final常量一旦赋值就不会变了所以默认为30public int n1 10;public static int n2 20;public sattic final int n3 30;
}3解析阶段
虚拟机将常量池内的符号引用替换为直接引用的过程
a的具体地址指向new出来的堆内存地址
3.Initialization初始化
1此阶段才真正开始执行类中定义的java程序代码此阶段是执行方法的过程。
2()方法按照语句在文件中出现的顺序收集整理赋值动作并进行合并
static{int num10;System.out.println(num);
}
class A{int num20;
}
//输出num203虚拟机会保证一个类的()方法在多线程环境中被正确的加锁同步如果多个线程同时去初始化一个类那么只会有一个线程去执行这个类的()方法其他线程都需要阻塞等待直到活动线程执行完().
⭐可以考虑使用-Xverifynone参数来关闭大多类验证措施
以Car为例子
⭐作用加载Class文件初始化生成相应Car Class相当于一个工厂用来制造Car类的相应Class就可以开始实例化对象了
⭐new关键字底层就是一个LoadClass方法
public Class? loadClass(String name)throws ClassNotFoundException{return loadClass(name,false);
}⭐反射1-3Class类中的forName方法
对象要变回Class用getClass方法Class要变回Class Loader用getClassLoader方法
引用在栈实体在堆栈存放的是实例堆存放的是内存地址空间
人住在客栈人的东西随便放一堆。
扩展rt.jar是什么为什么重要
⭐双亲委派机制安全
1.运行一个类之前类加载器会收到类加载的请求。他会将这个请求向上查找应用加载器-》扩展加载器-》根加载器
最终执行最靠近上边的类比如String类在根加载器你用String类就是调用的根加载器里面的String而不是你自己写的
1-3 沙箱安全机制
了解
沙箱机制就是将java代码限定在JVM特定的运行范围中并且严格限制代码对本地系统资源访问。保证对代码的有效隔离防止对本地系统造成破坏
组成
字节码校验器校验字节码在类加载过程中校验。
⭐扩展编译码校验在类加载之前校验。
1-4 Native关键字
⭐多线程的start0
凡是带了native关键字的说明java的库作用范围达不到了就会去调用底层c语言的库
会进入本地方法栈然后调用
上面1-1的 本地方法接口JNIJava Native Interface
而JNI的作用就是扩展Java的使用融合不同的编程语言CC为Java所用
所以说本地方法栈就是专门开辟用来调用CC其他语言的区域
⭐本地方法一般不用用的最多的本地方法System.currentTimeMillis();
1-5 PC寄存器和方法区
1-5-1 PC寄存器
程序计数器
每个线程都有一个程序计数器是线程私有的就是一个指向方法区中的方法字节码的指针在执行引擎读取下一条指令
1-5-2 方法区
方法区是被所有线程共享的所有定义的方法信息都保存在此
⭐静态变量常量类信息构造方法接口定义运行时的常量池存在方法区中但是实例变量存在于堆内存中和方法区无关
⭐扩展
元空间不等于方法区是jdk1.8之后方法区的实现之前的实现是永久带。字符串常量池以及运行时常量池逻辑上属于方法区实则属于堆。
1-6 栈与队列
⭐栈先进后出对应的线程
队列先进先出
栈内存主管程序的运行生命周期和线程同步main算作主线程
因此栈不存在垃圾回收问题程序结束栈就拜拜了
⭐栈帧
Java中的栈帧随着方法调用而创建随着方法结束销毁可以理解为分配给方法的一块栈空间每调用一个方法就创建一个栈帧。 因此一般我们的main方法的栈帧都在栈底由于栈是后进先出所以你现在应该理解为什么main方法最后结束了吧
因此main方法一般都在栈底不然会报错StackOverflow
而栈满了也会报此错误
⭐栈主要存放八大基本类型引用数据类型等…
⭐为什么java数据要分类型
引用类型在堆里基本类型在栈里。
栈空间小且连续往往会被放在缓存。引用类型cache miss率高且要多一次解引用。
对象还要再多储存一个对象头对基本数据类型来说空间浪费率太高
1-7 堆
Heap一个JVM只有一个堆内存堆内存的大小是可以调节的
存储的全部是对象每个对象包含一个与之对应的class信息–class的目的是得到操作指令。
对象在内存中的存储布局分为三部分
1.对象头 java里面用两个字来表示对象头一个是Mark Word一个是Klass pointerClass MetaData Address
1Mark Word包含了自身运行时数据 包括锁状态lockbkased_lock等,hashcode,GC分代年龄(age),线程持有的锁等… …
2Klass pointer就是一个指针 虚拟机通过这个指针来确定对象是哪个类的实例
64位虚拟机的对象组成有锁无锁
2.实例数据 就是你在对象里面写的东西
3.对齐填充 JVM要求对象起始地址必须是8字节的整数倍8字节对齐所以不够8字节就由这部分来补充。
OOM堆内存错误
⭐堆的分区有哪些分别是什么功能
堆内存分为三个区域 新生代Minor GC新生代采取淘汰机制活不下去的就被轻GC回收 a.伊甸园 b.幸存from区 淘汰机制就是在from和to去进行言赢算法15次活下来就可以走为什么是15次151111 刚好四个字节存在对象头中 c.幸存to区 老年代Full GC 永久代1.8后改为元空间 此区域常驻内存用来存放JDK自身携带的Class对象Interface元数据此区域不存在垃圾回收关闭JVM时永久代自动关闭 1.6之前永久代常量池在方法区 1.7永久代常量池在堆中 1.8无永久代
⭐堆内存调优
jvm分配的堆内存是电脑内存的1/4初始堆内存是1/64
VM options: -Xms1024m -Xmx1024m -XX:PrintGCDetails//Xms最小内存 Xmx最大内存堆内存原生代老年代的内存
1-7-1 堆内存的组成代码演示
//先在pom.xml里面引入jol依赖使得我们可以直观看到堆对象的组成
dependencygroupIdorg.openjdk.jol/groupIdartifactIdjol-core/artifactIdversion0.9/version
/dependencypublic class A{private String name;private int age;
}import org.openjdk.jol.info.ClassLayout;public class Test {static A a new A();public static void main(String[] args) {System.out.println(ClassLayout.parseInstance(a).toPrintable());}
}输出演示 1-8 GC
垃圾回收机制
⭐GC的算法有哪些
1.复制算法
2.标记整体算法
3.标记清除算法
4.分代收集算法
⭐轻GC和重GC分别在什么时候发生
线程
线程是由进程创建的一个实体线程可以继续创建线程
1.并发同一个时刻多个任务交替执行造成一种“好像是同时”的错觉单核cpu中多任务就是并发
2.并行同一时刻多个任务同时运行多核cpu可以并行
1-1 创建线程的方式
1继承Thread类Thread类已经实现了Runnable接口重写run方法
class T1 extends Thread{run(){}
}2实现Runnable接口重写run方法 run方法是Runnable接口的Thread是实现的Runnable的run方法
注意run()里面写你自己的业务逻辑
3用start()方法启动线程因为run()仅仅只是一个方法无法达成并行只能串行。启动后不一定马上执行要等待cpu
public synchronized void start(){start0();//实现多线程效果的是他而不是run这是个jvm控制的native方法
}⭐注意不论是继承Thread还是实现Runnable接口本质上都是调用start()里面的start0()方法
1-2 线程常用方法
setName,getName,start,run,setPriority更改优先级,getPriority获取优先级
interrupt,中断线程实际上用来唤醒正在睡觉的线程
sleep线程中的静态方法使线程休眠
// yield : 线程礼让方法,让其他方法先执行但不一定成功要看cpu调度
// join : 线程插队方法
Thread t1 new Thread();
Thread t2 new Thread();
t2.join();//t1的时候t2调joint2先插队完全执行完毕然后执行t1
1-3 线程的种类
用户线程也叫工作线程当线程的任务执行完或通知方式结束
守护线程为工作线程服务当所有用户线程结束守护线程自动结束
t1.setDaemon(true);//将t1设置为守护线程用户线程结束它就结束常见的守护线程垃圾回收机制
1-4 线程的生命周期 1-6 死锁
多线程/高并发
1.并发基础
1-1 互斥同步
1-2 非阻塞同步
1-3 指令重排
1-4 synchronized重量级锁
synchronized相信大家都看过或者用过synchronized是Java中的关键字synchronized可以保证方法或者代码块在运行时同一时刻只有一个方法可以进入到临界区同时它还可以保证共享变量的内存可见性Java中每一个对象都可以作为锁这是synchronized实现同步的基础。 synchronized 关键字可以保证原子性也可以保证可见性
它修饰的对象有以下几种
修饰一个代码块被修饰的代码块称为同步语句块其作用的范围是大括号{}括起来的代码作用的对象是调用这个代码块的对象修饰一个方法被修饰的方法称为同步方法其作用的范围是整个方法作用的对象是调用这个方法的对象修饰一个静态的方法其作用的范围是整个静态方法作用的对象是这个类的所有对象修饰一个类其作用的范围是synchronized后面括号括起来的部分作用主的对象是这个类的所有对象。 *synchronized使用详解 *
synchronized的实现过程 1Java代码中synchronized关键字 2编译过程字节码中monitorenter执行开始moniterexit执行完 3JVM执行过程中锁升级环节 4cpu中lock cmpechg
1-5 volatile
使用volatile 关键字可以强制的从公共内存中读取值。使用volatile关键字增加了实例变量在多个线程之间的可见性。但是volatile关键字的缺点是不支持原子性。 volatile 关键字可以禁止指令进行重排序优化也可以增加可见性 它修饰的对象是 变量
1-6 关键字的比较
1volatile 是线程同步的轻量级实现所以volatile性能肯定比synchronized要好但是volatile只能修饰变量而synchronized可以修饰方法代码块。
2多线程访问volatile 不会发生阻塞而synchronized 会发生阻塞。
3volatile 能保证数据的可见性但不能保证原子性而 synchronized可以保证原子性也可以保证可见性因为它会将私有内存和公共内存中的数据做同步。
2.锁
⭐锁的种类 new啥都没有---- 偏向锁 ---- 轻量级锁 ---- 重量级锁
第一次上锁为偏向锁一旦有线程竞争就立马升级为轻量级锁竞争激烈就变成重量级锁
轻量级锁假设两个线程抢先抢到的就把自己的Lock Record指针x贴到对象头上后来的看看Lock Record都变成这个x然后自旋直到前面线程走了后来的Lock Record就不等于这个x了就可以把自己的贴上去了。(x抢到了厕所y原地自旋等x上完厕所)
重量级锁用户态转内核态
JVM会检测到一直在给 str 这个对象重复加锁于是会粗化到while循环外面加锁只需加一次了。
2-1 自旋锁
CAS: compare and swap(比较和交换)
重量级锁用的是用户态和内核态的频繁切换导致重量级锁开销大损耗高而JVM的轻量级锁用的是CAS进行自旋抢锁。 CAS具体操作当前值和新值不相等就让其他线程改完再进行比较直到相等的时候更新。 底层用的JNI里面的unsafe ABA问题
你女朋友在分手状态时把你绿了后面他们又分了然后女朋友和你复合你复合后不知道他这段时间和别人好了。这就是ABA
如何解决ABA
离婚证相当于标记一下
⭐CAS在底层是通过
lock cmpxchg 指令实现的这个指令和synconized和voliate有关
2-2 偏向锁
因为经过HotSpot的作者大量的研究发现大多数时候是不存在锁竞争的常常是一个线程多次获得同一个锁因此如果每次都要竞争锁会增大很多没有必要付出的代价为了降低获取锁的代价才引入的偏向锁。
偏向锁的升级
当线程1访问代码块并获取锁对象时会在java对象头和栈帧中记录偏向的锁的threadID因为偏向锁不会主动释放锁因此以后线程1再次获取锁的时候需要比较当前线程的threadID和Java对象头中的threadID是否一致如果一致还是线程1获取锁对象则无需使用CAS来加锁、解锁如果不一致其他线程如线程2要竞争锁对象而偏向锁不会主动释放因此还是存储的线程1的threadID那么需要查看Java对象头中记录的线程1是否存活如果没有存活那么锁对象被重置为无锁状态其它线程线程2可以竞争将其设置为偏向锁如果存活那么立刻查找该线程线程1的栈帧信息如果还是需要继续持有这个锁对象那么暂停当前线程1撤销偏向锁升级为轻量级锁如果线程1 不再使用该锁对象那么将锁对象状态设为无锁状态重新偏向新的线程。
偏向锁的取消
偏向锁是默认开启的而且开始时间一般是比应用程序启动慢几秒如果不想有这个延迟那么可以使用-XX:BiasedLockingStartUpDelay0
如果不想要偏向锁那么可以通过-XX:-UseBiasedLocking false来设置
2-3 ReentrantLock可重入锁
在谈ReentrantLock 前我们先来了解一下另一种锁分类内置锁和显式锁我们前面讲到的synchronized就是内置锁很多小伙伴觉得synchronized已经很好用了啊为什么还要搞一个什么显式锁呢接下来就通过ReentrantLock来带大家了解
有些事情内置锁是做不了的我们只能搞一个新玩意ReentrantLock来做比如
1可定时:要加个超时等待时间超时了就停止获取锁这样就不会无限等待。
RenentrantLock.tryLock(long timeout, TimeUnit unit);2可中断:通过外部线程发起中断信号中断某些耗时的线程唤醒等待线程。
RenentrantLock.lockInterruptibly();3条件队列: 线程在获取锁之后可能会由于等待某个条件发生而进入等待状态内置锁通过Object.wait()方法显式锁通过Condition.await()方法进入等待状态的线程会挂起并自动释放锁这些线程会被放入到条件队列当中。synchronized对应的只有一个条件队列而ReentrantLock可以有多个条件队列
Condition.signal();
Condition.signalAll();//ReentrantLock通过这两种方法唤醒注意ReentrantLock 底层是AQS的实现使用内置锁时对象本身既是一把锁又是一个条件队列使用显式锁时RenentrantLock的对象是锁条件队列通过RenentrantLock.newCondition()方法获取多次调用该方法可以得到多个条件队列。
2-3-1 锁的操作
⭐锁升级 锁的4中状态无锁状态、偏向锁状态、轻量级锁状态、重量级锁状态级别从低到高
⭐锁消除
public void add(String str1,String str2){StringBuffer sb new StringBuffer();sb.append(str1).append(str2);
}我们都知道StringBuffer是线程安全的因为它的关键方法都是被synchronized修饰过得但我们看上面的代码可以发现sb这个引用只会在add方法中使用不可能被其他线程引用因此sb是不可能共享的资源。JVM会自动消除StringBuffer对象内部的锁。
⭐锁粗化
public String test(String str){int i0;stringBuffer sb new StringBuffer();while(i100){sb.append(str);i;}return sb.toString();
}3.线程池
池化思想线程池字符串常量池数据库连接池
这些都是为了提高资源的利用率
1.手动创建线程对象
2.执行任务
3.执行完毕释放线程对象
4. 并发容器
5.JUC
5-1executor
5-2collections
5-3locks
5-4 atomic
原子类许多都是native方法详情查看1-4 native关键字
5-5 tools
数据结构与算法
1.线性结构和非线性结构
线性元素之间存在一对一的线性关系比如数组队列链表栈
非线性比如二维数组多维数组树结构图结构
设计模式
1-1 单例模式
单例类一定要有并且只能有一个实例并且只能自己创造实例。
1-1-1 饿汉式
public class Hungry{//饿汉式会浪费内存private Hungry(){//构造器私有}private final static Hungry h new Hungry();public static Hungry getInstance(){//getInstance为创建一个唯一的实例和new不同return h;}
}1-2 工厂模式
实例化对象不用new用工厂代替买车要从车厂买
1静态工厂模式
public static void main(String[] args){//Car car new BenChi();普通方法new对象Car car CarFactory.getCar(奔驰);/* 优点:不用考虑过程。缺点:违反了开闭原则需要改进 */
}2方法工厂模式每个品牌一个类
3抽象工厂模式(强化版静态工厂模式也就是工厂的工厂)
//手机产品接口
public interface IphoneProduct{void fuck();void suck();
}
//小米手机
public class XiaoMi implements IphoneProduct{public void fuck(){System.out.println(cnm);}public void suck(){System.out.println(gun);}
}
//工厂接口
public interface IProductFactory{//小米手机IphoneProduct iphoneProduct();//小米叽叽IJJProduct ijjProdcut();
}
//抽象工厂
public XiaomiFactory implements IProductFactory{public IphoneProduct iphoneProduct(){return new XiaoMi();}
}1-3原型模式
其特点为复制复制的对象就是【原型】 创建复杂或耗时的对象时直接复制是最好的选择。
主流框架和项目管理
微服务概念
1微服务是一种架构风格
2有多个服务多个服务独立运行每个服务占用独立进程比如我们的商城模块和oss模块。
3为什么需要微服务因为我们之前的web项目是单体架构所有的东西都耦合在一块代码量大维护困难所以我们需要把一个单体拆分成多个独立运行的服务每个服务有特有的功能
4微服务可以方便多数据源
1.spring cloud
spring cloud不是一种技术是一系列框架技术的集合
spring cloud需要依赖spring boot 使用
2.Nacos注册中心
常见的注册中心还有ZooKeeper搭配dubbo使用
Eurekaspringboot原生
3.Feign远程调用从这开始到Http Client结束
Feign是网飞Netflix开发的声明式模板化的http客户端可以帮助我们快捷方便的使用html API
4.Hystrix
熔断器与服务雪崩
服务雪崩是指B调用A然后C和D调用B这时如果A服务不可用了那么会引起B不可用然后C和D因为B不可用所以也不可用这就叫服务雪崩。原因主要有
1.硬件故障 2.程序bug 3.缓存击穿 4.用户大量请求
熔断器
开关关闭时请求允许通过如果健康状况高于阈值开关保持关闭如果低于阈值开关打开开关打开时请求禁止通过禁止一段时间后只允许一个请求通过这一个请求成功时熔断器恢复关闭状态。
5.Ribbon
对请求做负载均衡比如生产者有一个集群Ribbon会将这些请求平均分摊到不同的服务器中
Nginx
反向代理服务器能实现很多功能
1.请求转发
什么是请求转发
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-q5cxhMfX-1644913012221)(C:\Users\86155\AppData\Roaming\Typora\typora-user-images\image-20211114094636893.png)]
2.负载均衡 将客户端的请求平均分摊到你的各个集群服务器中达到负载均衡
3.动静分离
把java代码动态资源和页面文件那些静态资源分离开根据请求申请动态或者静态资源
命令
到文件夹打开cmd
然后nginx.exe启动
nginx.exe -s stop关闭
RabbitMQ中小型公司卡夫卡大型公司
本质是个队列先入先出在系统高并发搞不定的时候引入中间件。
主要功能有
1-1流量消除峰值
在购买人数过多的时候把超出的一部分延迟下单虽然影响了用户体验但是至少是能够下单而不会崩溃。
1-2
SSM
spring
springMVC
MyBatis
项目管理
Maven
Git
网络
1.TCP/IP
TCP可靠协议传输数据时会进行检查没传输成功的他会重传并且保证数据顺序不会乱
UDP不可靠协议他不检查但是延时低占用资源少 如果你也想学习:黑客网络安全的零基础攻防教程
今天只要你给我的文章点赞我私藏的网安学习资料一样免费共享给你们来看看有哪些东西。
在这里领取 这个是我花了几天几夜自整理的最新最全网安学习资料包免费共享给你们其中包含以下东西
1.学习路线职业规划 2.全套体系课入门到精通 3.黑客电子书面试资料 4.漏洞挖掘工具和学习文档