答:面向对象的特征主要有以下几个方面:
1.toString () 方法
2.equals 方法
3. finalize () 方法
[4.hashCode ()方法]
8 种
1、整型:byte、short、int、long
2、字符型:char
3、浮点型:float、double
4、布尔型:boolean
那么,让我们来看看ConcurrentHashMap是如何实现线程安全的,它能实现高效率的线程安全吗?
这里我们就不看ConcurrentHashMap的源码了,简单说说线程安全的原理,其实ConcurrentHashMap实现线程安全也是通过synchronized关键字来控制代码同步来实现的,不同于HashTable的是ConcurrentHashMap在线程同步上更加细分化,它不会像HashTable那样一把包揽的将所有数据都锁住。
采用分段锁思路
我们都知道ConcurrentHashMap的底层数据结构实现原理其实和HashMap没什么两样,都是数组+链表+红黑树。那么我们就从HashMap入手,来理解。
具体HashMap的实现原理剖析请看个人博客:https://blog.csdn.net/zhanglei082319/article/details/87872156
怎么将HashMap数据结构使用分段锁的思想进行细分化。在jdk1.7及以前,是这样实现的:
比如容器HashMap中存在1000个元素,各个元素都放置到HashMap数组的链表或者红黑数中,最后得到的数组大小可能只有128,ConcurrentHashMap会根据这128个数组,对其分段,比如以16个数组为一段,可以分为8段。在实际获取元素,添加元素时,会根据元素的索引(如何获取元素在HashMap数组中的索引请看https://blog.csdn.net/zhanglei082319/article/details/87872156)找到该元素所处的段位,然后只将该段位锁住,并不影响其他段位的数据操作。这样如果按照HashTable的效率为基本单位来计算,ConcurrentHashMap在jdk1.7及以前的效率会提高8倍,当然数据量越大,提高的效率将越多。
jdk1.8后,ConcurrentHashMap依旧使用分段锁的思想来实现线程安全,不同于jdk1.7及以前,jdk1.8将锁的粒度更加细分化,以每个数组索引为锁来进行实现。比如HashMap数组中长度有128,那么就会存在128个锁将每个索引锁住。这样相比于jdk1.7之前在效率上有了很大的改进。
总结一下
HashTable和ConcurrentHashMap都是线程安全的容器。
HashTable: 线程安全,效率和容器的大小成正比。容器数据量越大,效率越慢
ConcurrentHashMap: 线程安全,效率相对于不如HashMap,但是和HashTable相比,效率得到很大的提升。
综合考虑,如果使用线程安全容器,推荐使用ConcurrentHashMap
1.Switch支持String类型
3.泛型实例创建可以通过类型推断简化,new对象后边的泛型可以不用写,直接< >
4.HashMap性能优化:
jdk1.8 当每个链表长度 >8 ,并且数组元素个数 ≥64时,会调整成红黑树,目的是提高效率
jdk1.8 当链表长度 <6 时 调整成链表
jdk1.8 以前,链表时头插入,之后为尾插入
原因:头插法在并发情况下会出现链表成环的问题,当然我们知道它不是线程安全的类,不会用它,并发编程时用的是Concurrent包下的ConcurrentHashMap
5.Annotation注解:支持多重注解
想了解更多请看这:Java 多重注解
6.永久代移除,把类的元数据保存在元空间(本地内存区域,也就是堆外内存)
7.实现了异步非阻塞IO(AIO)和Profactor
IO分为BIO(同步阻塞),NIO(同步非阻塞)和AIO(异步非阻塞)
8.Lambda表达式,(例如: (x, y) -> { return x + y; } ;λ表达式有三部分组成:参数列表,箭头(->),以及一个表达式或语句块。)
9.栅栏:允许两个或多个线程在某个集合点同步,当一个线程到达集合点时,它将调用await()方法等待其它的线程。线程调用await()方法后,CyclicBarrier将阻塞这个线程并将它置入休眠状态等待其它线程的到来。等最后一个线程调用await()方法时,CyclicBarrier将唤醒所有等待的线程然后这些线程将继续执行。CyclicBarrier可以传入另一个Runnable对象作为初始化参数。当所有的线程都到达集合点后,CyclicBarrier类将Runnable对象作为线程执行。
jdk 1.7 数组加链表
jdk8中添加了红黑树,当链表长度大于等于8的时候链表会变成红黑树
链表新节点插入链表的顺序不同(jdk7是插入头结点,jdk8因为要把链表变为红 黑树所以采用插入尾节点)
hash算法简化 ( jdk8 )
resize的逻辑修改(jdk7会出现死循环,jdk8不会)
public(公共) 所有类都可以调用
protected (受保护的) 在本类、本包和子类中可以使用
默认 本类和本包中可以使用
private (私有的)只有本类中可以使用
错误 s1 + 1,s1是short类型,1是int型,s1会自动转换为int型的1,与1相加后,得到int型的2,要向左侧的short类型的s1看齐,即需要通过强制类型转换。正确写法:s1 = (short) (s1 + 1);
正确 执行s1+=1;其实执行的是s1 = (short) (s1 + 1); 其中会有一个强制转换的过程。
1.&&具有短路功能,而&不具有短路功能
2.& 进行判断的时候 只有条件一个为真 结果集为true ; && 需要两个条件都为真
==:
基本数据类型,比较的是值是否相等(注:基本数据类型有:(byte,short,char,int,long,float,double,boolean))
引用数据类型,比较的是地址是否相等
equals:
没有重写的情况下,比较的是两个对象的地址是否相等,此时等价于 ==。
重写的情况下,按重写的方式进行比较。
属性 方法 构造都不同 tihs是调用本类 super 调用父类
do-while 语句至少执行一次循环体内的代码;
while 条件不成立 一次都不会执行
break,continue,return方式
嵌套循环最外层设置一标记,然后break 标记,就能跳出多层嵌套循环
用移位运算的方式运算:int a=2<<3;
1、值传递
在方法的调用过程中,实参把它的实际值传递给形参,此传递过程就是将实参的值复制一份传递到函数中,这样如果在函数中对该值(形参的值)进行了操作将不会影响实参的值。因为是直接复制,所以这种方式在传递大量数据时,运行效率会特别低下。
2、引用传递
引用传递弥补了值传递的不足,如果传递的数据量很大,直接复过去的话,会占用大量的内存空间,而引用传递就是将对象的地址值传递过去,函数接收的是原始值的首地址值。在方法的执行过程中,形参和实参的内容相同,指向同一块内存地址,也就是说操作的其实都是源数据,所以方法的执行将会影响到实际对象。
java是值传递
数组没有length()方法,有length 的属性。String 有length()方法。、
java集合分三种,List、Set、Map,这三种集合适用于不同的场景
List:适用于有序,可重复的集合
Set:适用于不可重复集合
Map:适用于键值对的存储
注:通常List与Map最为常用
每个集合常用的实现类有哪些?
List: ArrayList与LinkedList
Set: HashSet与TreeSet
Map: HashMap与TreeMap与HashTable
我们可以看出将一个key-value对放入HashMap中时,首先根据key的hashCode()返回值决定该Entry的存储位置,如果两个key的hash值相同,那么它们的存储位置相同。如果这个两个key的equals比较返回true。那么新添加的Entry的value会覆盖原来的Entry的value,key不会覆盖。且HashSet中add()中 map.put(e, PRESENT)==null 为false,HashSet添加元素失败。因此,如果向HashSet中添加一个已经存在的元素,新添加的集合元素不会覆盖原来已有的集合元素。
1.数据结构不同 ArrayList底层结构是数组,LinkedList底层结构是链表;
2.数据结构决定了,ArrayList在查询上的效率较高,而LinkedList在删除和添加上的效率更高;
3.两者都可以为null
1.底层其实是一个Object类型的数组变量elementData
transient Object[] elementData;
2.new一个无参ArrayList,底层创建的一个长度为0的空数组
3.开始第一个元素,执行add方法 ,代码如下
首先size变量目前还未赋值,因此大小为系统给的初始值0,
size其实代表的就是我们实际放入集合中的元素个数(而非elementData.length)。
接下来执行 ensureCapacityInternal(size + 1)这个方法,就是去检测以下容器(elelmentData数组)长度是否够用,
将size+1的值作为是否触发扩容的标准,这个值满足内在的设计条件,则扩容,不满足就不扩容,表示elementData数组容量还凑合,暂时不加长了。具体代码在下面,将在注释中解释这个过程。
private int size;
public boolean add(E e) {
ensureCapacityInternal(size + 1); // 将size+1的值作为判断接下来要不要扩容的标准,具体代码就在下一个方法,
elementData[size++] = e;//扩容后的长度为10的数组elementData,因为是Object类型,因此里面是系统给的初始值全部都为null,
//并将下标为size:0处的null改为元素e:"嘿嘿",然后size+1,即有效元素为1
return true;//添加完毕,返回true
}
//private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//检测size+1的长度是否需要扩容
private static final int DEFAULT_CAPACITY = 10;
private void ensureCapacityInternal(int minCapacity) {//此时传入的minCapacity=size+1=0+1=1
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {//此时的elementData={},因此满足这个条件,
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);//DEFAULT_CAPACITY = 10,所以10和1最大值为10赋值给minCapacity变量
}
ensureExplicitCapacity(minCapacity);//minCapacity=10;
}
protected transient int modCount = 0;
//将10这个长度再拿过来检测
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
if (minCapacity - elementData.length > 0)//此时elementData依然是一个空数组,因此10-0>0 条件成立,
grow(minCapacity); //开始执行核心扩容方法
}
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
private void grow(int minCapacity) {//minCapacity=10
int oldCapacity = elementData.length;//此时elementData依然是空数组,长度自然为0,因此oldCapacity=0
int newCapacity = oldCapacity + (oldCapacity >> 1);```
**>>1 这个表示是二进制右移一位,0右移一位依然是0,因此newCapacity=0+0=0,后面当添加到11个元素时,也会在此扩容,扩容后的长度就是这样子算的, 10+10>>1 右移1位相当于乘以0.5 ,因此newCapacity=10+10*0.5=15 ,也就是说,扩容倍数为1.5倍**
```java
if (newCapacity - minCapacity < 0)//minCapacity:传入方法参数值10, 0-10=-10<0 为true,if成立
newCapacity = minCapacity; //将10赋值给新的数组长度 newCapacity=10
if (newCapacity - MAX_ARRAY_SIZE > 0)// MAX_ARRAY_SIZE =int的4个字节一共32位bit的最大值大概21亿多减去8的值,显然不成立
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);//通过Arrays工具类将老数组内容复制到一个新的数组,并指定此数组的长度为newCapacity:10 ,因此grow完成了扩容,返回的是长度为10的数组,里面存了系统给的默认值10个null。
}
对于ArrayList ,new无参对象时,底层是一个空数组,当添加第一个元素时,会进行扩容,将底层数组长度扩为10,
其中扩容触发的条件是:存元素时,即先让bucketensureCapacityInternal(size+1)的值判断是否大于底层elementData.length的长度,如果大于,则先扩容再添加,
扩容的倍数为1.5倍。
巴克特show 趴cti 英特no
流向分,可以分为输入流和输出流;
单元划分,可以划分为字节流和字符流;
角色划分为节点流和处理流。
Files.exists():检测文件路径是否存在。
Files.createFile():创建文件。
Files.createDirectory():创建文件夹。
Files.delete():删除一个文件或目录。
Files.copy():复制文件。
Files.move():移动文件。
Files.size():查看文件个数。
Files.read():读取文件。
Files.write():写入文件。
1.Stringbuilder线程不安全,Stringbuffer线程安全
2.Stringbuilder效率更快,因为它不需要加锁,不具备多线程安全,相对来说Stringbuffer就效率慢一些
5 个 ; 两个字符串常量 两个堆对象 一个stringbuilder
不可以 可以被多个类继承
1.内部类
2.接口
3.多层继承
在编译时不知道加载的是哪一个类,在运行时可以动态获取类的属性调用对象的方法的机制就叫做反射。
就是为了保证资源安全,确保一次仅有一个线程对共享资源进行修改。
Collection 和 Map 两大类 又有很多子类继承
Collection
List
map
Set
Java里 Java hashmap采用的是链表,每个节点都是单向链表,有冲突就往链表尾部放
1.在JDK1.7及之前,是用数组加链表的方式存储的。
2.JDK1.8 把它设计为达到一个特定的阈值之后,就将链表转化为红黑树。
HashMap基于hashing原理,我们通过put()和get()方法储存和获取对象。当我们将键值对传递给put()方法时,它调用键对象的hashCode()方法来计算hashcode,让后找到bucket位置来储存值对象。当获取对象时,通过键对象的equals()方法找到正确的键值对,然后返回值对象。HashMap使用链表来解决碰撞问题,当发生碰撞了,对象将会储存在链表的下一个节点中。 HashMap在每个链表节点中储存键值对对象。
当两个不同的键对象的hashcode相同时会发生什么? 它们会储存在同一个bucket位置的链表中。键对象的equals()方法用来找到键值对。
key,value 形式存储
实际上的数组+链表+红黑树
put : 比如说数组长度16 存储数据 张三 内部调用键对象中的hashcode()方法 (将张三做一个hash)
然后根据16取模 返回一个下标 根据这个下标找到bucket位置存储对象
get : 根据equals()方法去找到正确的键值对,返回键值对对象
HashMap对象的key、value值均可为null。
HahTable对象的key、value值均不可为null。
1.hasmap 是线程不安全
2.hastable 是线程安全
**1、put(K key,V value) **赋值
**2、get(Object key) **取值
**3、size() **返回HashMap集合中的元素数量
**4、clear() **清空HashMap集合
5、isEmpty () 判空
6、remove(Object key) 根据key删除数据
**7、values() ** 返回HashMap集合中所有value组成的以Collection数据类型格式数据
不是必须的
1.Collection:
是集合类的上层接口。本身是一个Interface,里面包含了一些集合的基本操作。
Collection接口是Set接口和List接口的父接口
2.Collections
Collections是一个集合框架的帮助类,里面包含一些对集合的排序,搜索以及序列化的操作
1.调用 start() 方法是用来启动线程的,轮到该线程执行时,会自动调用 run();直接调用 run() 方法,无法达到启动多线程的目的,相当于主线程线性执行 Thread 对象的 run() 方法。
2.一个线程对线的 start() 方法只能调用一次,多次调用会抛出 java.lang.IllegalThreadStateException 异常;run() 方法没有限制
进程:是指一个内存中运行的应用程序(已经在内存中运行的程序). 一个进程都有一个独立的内存空间,一个电脑(手机)可以同时运行多个进程;进程也是程序的一次执行过程,是系统运行程序的基本单位;
线程:是进程中的一个执行单元(线程是依赖于进程的),负责当前进程中程序的执行,一个进程中至少有一个线程(单线程程序)。一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序。
第一种: 通过继承Thread类创建线程 重写run()
第二种: 通过实现Runnable接口创建线程 重写run()
第三种: 实现Callable接口 重写 call( )
1、sleep是线程中的方法,但是wait是Object中的方法。
2、sleep方法不会释放lock,但是wait会释放,而且会加入到等待队列中。
3、sleep方法不依赖于同步器synchronized,但是wait需要依赖synchronized关键字。
4、sleep不需要被唤醒(休眠之后推出阻塞),但是wait需要(不指定时间需要被别人中断)。
JVM就是java虚拟机,它是一个虚构出来的计算机,可在实际的计算机上模拟各种计算机的功能。JVM有自己完善的硬件结构,例如处理器、堆栈和寄存器等,还具有相应的指令系统
Spring是一个轻量级的IoC和AOP容器框架。是为Java应用程序提供基础性服务的一套框架,目的是用于简化企业应用程序的开发,它使得开发者只需要关心业务需求
1.单例设计模式 spring bean 默认都是单例
2.代理设计模式 spring aop 的实现
3.工厂设计模式 spring 使用工厂设计模式创建bean对象
4.模板方法模式 spring 中 jdbcTemplate redisTemplate 等以Template结尾的对数据库操作的类 采用模板设计模式
1.减少开发,测试时间
2.使用JavaConfig 有助于避免使用XML。
3.避免大量的Maven 导入和各种版本冲突。
4.提供意见发展方法。
一.用于创建对象的注解
作用:和在xml配置文件中编写一个标签实现的功能一样。
1.@Component : 用于把当前类对象存入Spring容器中。
属性:value — 用于指定bean的id。如果不写该属性,id的默认值是当前类名,且首字母改为小写。
2.@Controller : 一般用在表现层。
3.@Service : 一般用在业务层。
4.@Repository : 一般用在持久层(dto层)。
备注:其中2,3,4注解的作用和属性与@Component 一模一样,他们三个是Spring框架为我们提供的三层架构使用的注解,使我们对三层对象更加清晰。
二.用于注入数据的注解
作用:和在xml配置文件中的标签中写一个标签的功能一样。
1.@Autowired : 自动按照类型注入。只要容器中有唯一的一个bean对象类型和要注入的变量类型匹配,就可以注入成功。可以作用在变量或者方法上。
2.@Qualifier : 在按照类型注入的基础之上再按照名称注入,它在给类成员注入时要和@Autowired配合使用,但是在给方法参数注入是可以单独使用。
3.@Resource : 直接按照bean的id注入,可以独立使用。
属性:name — 用于指定bean的id。
备注:以上三个注入都只能注入其他bean类型的数据,而基本类型和String类型无法使用上述注解实现。另外,集合类型的注入只能通过xml来实现。
4.@Value : 用于注入基本类型和String类型的数据。
属性:value — 用于指定数据的值,它可以使用Spring中的SpEL(也就是Spring中的el表达式)。SpEL的写法:${表达式}
三.用于改变作用范围的注解
作用:和在xml配置文件中的标签中使用scope属性实现的功能一样。
1.@Scope : 用于指定bean的作用范围。
属性:value — 指定范围的取值。常用取值:singleton(单例)和prototype(多例)。
四.和生命周期相关的注解
作用:和在xml配置文件中的标签中使用init-method和destory-method属性实现的功能一样。
1.@PreDestory : 用于指定销毁方法。
2.@PostConstruct : 用于指定初始化方法。
五.Spring新注解
1.@Configuration : 用于指定当前类是一个配置类。
2.@ComponentScan : 用于通过注解指定Spring在创建容器时要扫描的包。
3.@Bean : 用于把当前方法的返回值作为bean对象存入Spring的IOC容器中。
属性:name — 用于指定bean的id。当不写时,默认值为当前方法的名称。
4.@Import : 用于导入其他的配置类。
属性:value — 用于指定其他配置类的字节码。当我们使用@Import注解时,有@Import注解的类就是父配置类。
5.@PropertySource : 用于指定properties文件的位置。
@Autowired 是Spring提供的,@Resource 是J2EE提供的。
@Autowired只按type装配,@Resource默认是按name装配。
(1)@Autowired默认按类型装配。如果想使用名称装配可以结合@Qualifier注解进行使用。
(2)@Resource,默认按照名称进行装配,名称可以通过name属性进行指定,当找不到与名称匹配的bean时才按照类型进行装配。
1.非侵入式设计
2.支持AOP
3.支持声明式事务处理
4.方便程序的测试
6.方便集成各类框架
spring apo
spring jdbc
spring web
spring test
控制反转,是一种设计思想。spring中提供了一种IOC容器,来控制对象的创建,无论是你创建对象,处理对象之间的依赖关系,对象的创建时间还是对象的创建数量,都是spring提供IOC容器上配置对象的信息就可以了
ioc的核心就是依赖注入
spring 帮我们统一 管理对象 比如
1.简单来说就是在程序当中 对象 与 对象的依赖变得复杂
2.使用和维护对象之间的依赖 维护成本比较高
3.就比如我操作a对象 它要依赖b对象 或者说两个对象依赖了共同的模板
Spriing Ioc依赖注入,控制反转;
1.手动注入 get(),set()方法
2.xml形式自动注入
3.标注@Autowire注解的属性或方法都是注入点
1.set 方法注入
2.构造器注入
3.通过index设置参数位置
4.工厂注入 静态工厂和实例化工厂
当 bean 在 Spring 容器中组合在一起时,它被称为装配或 bean 装配。
1.no-手动配置
2.byName 通过bean的属性名称自动装配
3.byType 通过bean的类型进行自动装配
4.constructor 通过bean的类型与构造器参数相同进行装配
有两种
声明式事务
基于annotation
基于application.xml 配置文件
编程式事务
1.经典的jdbc事务
注意:spring 只做事务支持 说的简单点 aop
1.发生自调 类中使用this方法调用本类 但是this对象本身代理类
2.方法不是public
3.数据库不支持事务
4.没有被spring管理
5.异常没有被做处理
面向切面编程,用于将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块,这个模块被命名为“切面”(Aspect),减少系统中的重复代码,降低了模块间的耦合度,同时提高了系统的可维护性。aop是基于ioc
在不改变原有代码逻辑的前提下,从某个切入点(代码之前之后)动态添加代码(核心是动态代理),实现业务增强。常见的应用有spring声明式事务,仅仅需要一个注解实现事务支持。
cglib(哥赖 b)
Interceptor 拦截器 (英特塞科特)
BeforeAdviseInterceptor (比佛特爱的sein 塞克特)
AfterAdviseInterceptor (安抚特爱的sein 塞克特)
1.其于代理实现
2.原生的<[aop]:config> 切面
3.@AspectJ注解驱动的切面
1.实例化bean
2.注入属性
3.调用init方法
4.bean可以使用
5.销毁时调用销毁方法
利用缓存处理未通过初始化的bean
三级缓存
1、创建A,放⼊三级缓存
2、A注⼊属性,发现依赖B,去实例化B
3、实例化B,B注⼊属性要依赖A,找找找,三级缓存中找到A,找到A了,B创建完成,把B放入⼀级缓存;
删除三级缓存中的A,放入放⼊⼆级缓存
4、A继续属性赋值,从⼀级缓存拿到创建好B对象;
A对象创建也完成,删除⼆级缓存中的A,将A放⼊⼀级缓存
5、最后,⼀级缓存中保存着实例化、初始化都完成的A、B对象
因为实例化和属性赋值是分开的,所以里面有操作的空间
singleton(单例):只有一个共享的实例存在,所有对这个bean的请求都会返回这个唯一的实例。
prototype(多例):对这个bean的每次请求都会创建一个新的bean实例,类似于new。
spring 是单例
可以多例:
在加有@componet之类注解(@service、@bean…等)受spring管理的类上再加上注解@scope(“prototype”)那么被spring管理的类就是多例的。
有线程安全问题
SOAR 小米开源的sql编写优化工具
MySQL 在 Windows 环境下是大小写不敏感的
MySQL 在 Linux 环境下是大小写敏感的
数据库名、表名、表别名、字段名、字段别名等都小写
SQL 关键字、函数名、绑定变量等都大写
连接符或运算符or、in、and、=、<=、>=, +,- 等前后宜加上一个空格
语句关键字段全部使用小写
性能优化
\1. 查询时应尽量减少多余数据的读取,通过使用where子句来减少返回的记录数。
\2. 如果在语句中有not in(in)操作,应尽量用not exists(exists)来代替。特别对大数据量的两者检索速度有很明显的区别。
\3. 不宜使用外连接。外连接效率低。
\4. 一条SQL语句中不宜使用3层以上的嵌套查询。如果超过,则应在Java等应用服务器程序中处理。
\5. 一条SQL语句中不得从4个及以上表中同时取数。仅作关联或过滤条件而不涉及取数的表不参与表个数计算;如果必须关联4个或4个以上表,应在Java等应用服务器程序中处理。
\6. 应尽量避免使用order by和group by排序操作,如必须使用排序操作,尽量建立在有索引的列上。因为大量的排序操作影响系统性能。
\7. 对索引列的比较,应尽量避免使用not 或 !=,可拆分为几个条件。因为“not”和“!=”不会使用索引。如col1 是索引列,条件col1 !=0 可以拆分为col1 >0 or col2 <0。
\8. 应尽量将数据库函数、计算表达式写在逻辑操作符右边。因为这些对列的操作会将导致表扫描,影响性能。
\9. 在where子句中,如果有多个过滤条件,应将索引列或过滤记录数最多的条件放在前面。
共享锁 排他锁 意向锁 乐观锁 悲观锁
四大特性即ACID特性:原子性
、一致性
、隔离性
、持久性
1 创建主键索引
查询数据库的内容,按主键查询是最快的,每个表只能有一个主键,但是可以有多个普通索引列,主键列要求所有内容必须唯一,而索引列不要求内容唯一。
2.对字段的前n个字符创建普通索引
3.为多个字段创建联合索引
作用
如果我们需要将两个select语句的结果作为一个整体显示出来,我们就需要用到union或者union all关键字。union(或称为联合)的作用是将多个结果合并在一起显示出来。
区别
union和union all的区别是,union会自动压缩多个结果集合中的重复结果,而union all则将所有的结果全部显示出来,不管是不是重复。
“子查询是一种常用计算机语言SELECT-SQL语言中嵌套查询下层的程序模块。当一个查询是另一个查询的条件时,称之为子查询。”
使用场景
1.显示数据的表不存在 但是可以通过对数据加工获得
2.子查询可以出现在select where form子句中 dml子句中
3.在select delete update insert 命令中任意一个下使用 甚至可以在其他子查询语句中使用
1.sql语句优化
2.建立合适的索引(主键索引,组合索引,函数索引)
3.建立存储过程或者函数
1.普通索引
MySQL中基本索引类型,没有什么限制,允许在定义索引的列中插入重复值和空值,纯粹为了查询数据更快一点;
2.唯一索引
索引列中的值必须是唯一的,但是允许为空值;
3.主键索引
是一种特殊的唯一索引,不允许有空值;
1.列经常在where条件当中
2.列中有大量的空值
3.表几乎没有被修改过
4.数据过于庞大
1.脏读: 一个事务会读取到另一个未提交的事务
2.幻读: 一个事务多次读取到的数据不一样 比如 第一次读取2条数据 第二次读取3条数据 针对 insert delete
3.不可重复读: 一个事务多次读取到的数据不一样 比如 a事务读取到1 b事务把1修改为2 并且提交了 a事务也读取到2 针对于 update
①因为b+树是把数据都存放在叶子节点中的(在innodb存储引擎中一个b+树的节点是 一页(16k)),那么在固定大小的容量中 B+树的非叶子节点中就可以存放更多的索引列数据,也就意味着B+树的非叶子节点存储的数据的范围就会更大,那么树的层次就会更少,IO次数也就会更少;
②b+树的叶子节点维护了一个双向链表,它更有利于范围查询
③b+树中的叶子节点和非叶子节点的数据都是分开存储的,分别存放在叶子节点段和非叶子节点段,那么进行全表扫描的时候,就可以不用再扫描非叶子节点的数据了,并且这是一个顺序读取数据的过程(顺序读比随机读的速度要快很多很多),扫描的速度也会大大提高;
约定优于配置是一种软件设计的范式,他的核心思想是减少软件开发人员,对于配置项的维护,从而让开发人员能更聚焦在业务逻辑上;
基于传统的Spring框架,开发web应用的时候,我们需要做很多于业务无关,且只需要做一次的配置项
Spring Boot 中我们不需要做这些繁琐的配置,因为Spring Boot已经自动帮我们完成了,而完成这样一个动作的前提,就是基于约定优于配置这样一个思想
(1)Spring Boot Starter 启动依赖,他能够帮助我们管理所有的jar包版本;
(2)如果当前的应用依赖了web这样一个jar包,Spring Boot会内置Tomcat容器来运行web应用,我们不需要单独进行应用部署;
(3)Spring Boot的自动装配机制的实现中,通过扫描约定路径下的spring.factories文件,去识别配置类,从而去实现Bean的自动装载;
(4)Spring Boot默认会加载resource目录下的,application.properties文件
1.Windows 每个进程中的线程数不允许超过 2000
2. Linux 每个进程中的线程数不允许超过 1000
项目中springboot读取配置文件的三种方式:
1.使用Environment
2.使用@Value
3.使用@ConfigurationProperties注解映射到bean中,定义一个User对象里面有name和age属性,在配置文件里使用spring.user作为prefix,为User增加对应注解即可
spring boot 专注于快速开发单个个体微服务,springcloud 是关注微服务协调政治框架
@SpringBootApplication注解是Spring Boot的核心注解,它其实是一个组合注解,
查看它的源代码发现它重要的只有如下3个注解组成
@SpringBootConfiguration
@EnableAutoConfiguration
:@ComponentScan
@EnableAutoConfiguration
借助AutoConfigurationImportSelector
,AutoConfigurationImportSelector
又借助Spring框架原有的一个工具类SpringFactoriesLoader,加载spring.factories
文件中的完整类名,在利用java中的反射将类装载到虚拟机,并这些类中所有符合条件的@Configuration配置都加载到当前SpringBoot,并创建Bean和使用IoC容器。
如此:@EnableAutoConfiguration可智能的自动配置功效才得以大功告成!
mian 入口类的核心注解
spring 加载上下文
加载并初始化各种bean
如果是wbe 项目 根据classpath 包含的类判断 启动 embedded服务器 默认tomcat
nginx 是一个轻量级高性能力的反向代理服务web服务器 可以实现非常高效的反向代理和负载均衡 官方测试为5万条
异步非阻塞事件处理机制:运用了epoll模型,提供了一个队列,排队解决
简单来说所谓的负载均衡就是把很多请求进行分流,将他们分配到不同的服务器去处理。比如我有3个服务器,分别为A、B、C,然后使用Nginx进行负载均衡,使用轮询策略,此时如果收到了9个请求,那么会均匀的将这9个请求分发给A、B、Cf服务器,每一个服务器处理3个请求,这样的话我们可以利用多台机器集群的特性减少单个服务器的压力。
Nginx 默认提供的负载均衡策略:
每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务器 down 掉,能自动剔除。
每个请求按访问 ip 的 hash 结果分配,这样每个访客固定访问一个后端服务器,可以解决 session 共享的问题。
当然,实际场景下,一般不考虑使用 ip_hash 解决 session 共享。
下一个请求将被分派到活动连接数量最少的服务器
weight的值越大分配到的访问概率越高,主要用于后端每台服务器性能不均衡的情况下,达到合理的资源利用率。
修改config 配置文件 给Nginx服务器配置响应的header参数即可
分布式锁
分布式事务
分布式缓存(击穿、雪崩、穿透、不一致)
分布式分库分表
分布式任务调度
分布式配置中心
分布式Session
基于redis分布式锁的实现
两阶段提交(Two-phase Commit,2PC),通过引入协调者(Coordinator)来协调参与者的行为,并最终决定这些参与者是否要真正执行事务。
协调者询问参与者事务是否执行成功,参与者发回事务执行结果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NUuFhrk7-1657109152371)(C:/Users/阿琪/Desktop/4-SpringCloud/imgs/分布式事物-1.png)]
如果事务在每个参与者上都执行成功,事务协调者发送通知让参与者提交事务;否则,协调者发送通知让参与者回滚事务。
需要注意的是,在准备阶段,参与者执行了事务,但是还未提交。只有在提交阶段接收到协调者发来的通知后,才进行提交或者回滚。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1MUr145x-1657109152371)(C:/Users/阿琪/Desktop/4-SpringCloud/imgs/分布式事物-2.png)]
TCC事务是Try、Commit、Cancel三种指令的缩写,其逻辑模式类似于两阶段提交,但是实现方式是在代码层面来人为实现。它分为三个阶段:
- Try 阶段主要是对业务系统做检测及资源预留
- Confirm 阶段主要是对业务系统做确认提交,Try阶段执行成功并开始执行 Confirm阶段时,默认 Confirm阶段是不会出错的。即:只要Try成功,Confirm一定成功。
- Cancel 阶段主要是在业务执行错误,需要回滚的状态下执行的业务取消,预留资源释放。
例子,假入 Bob 要向 Smith 转账,思路大概是: 我们有一个本地方法,里面依次调用
- 首先在 Try 阶段,要先调用远程接口把 Smith 和 Bob 的钱给冻结起来。
- 在 Confirm 阶段,执行远程调用的转账的操作,转账成功进行解冻。
- 如果第2步执行成功,那么转账成功,如果第二步执行失败,则调用远程冻结接口对应的解冻方法 (Cancel)。
优点: 跟2PC比起来,实现以及流程相对简单了一些,但数据的一致性比2PC也要差一些
缺点: 缺点还是比较明显的,在2,3步中都有可能失败。TCC属于应用层的一种补偿方式,所以需要程序员在实现的时候多写很多补偿的代码,在一些场景中,一些业务流程可能用TCC不太好定义及处理。
1.开发 需要考虑分布式的复杂性,比如事务和系统容错等。
2.运行 通信成本较高,增加网络延迟。
3.维护 运维难度提升,需要做流量调度、链路监控等。
可以将目标主机返回的数据存储在代理服务器中,下一次再访问相同的站点数据时,直接从代理服务器的硬盘中读取。
所有的客户机请求都必须通过代理服务器访问远程站点,避免直接与目标服务器接触。
类似防火墙,可以在代理服务器上设限,过滤掉某些不安全信息。
同理:在访问其他人的服务器时,也可以用多层代理服务器,防止被访问者查出直接攻击对象~~
互联网上有许多开发的代理服务器,客户机在访问受限时,可通过不受限的代理服务器访问目标站点。
例如:VPN代理,VPN服务器。
通过分布式系统有序的对一个共享资源进行操作 通过互斥保证一致性
一共五种
持久化:Redis是内存数据库,数据都是存储在内存中,为了避免进程退出导致数据的永久丢失,需要定期将Redis中的数据以某种形式(数据或命令)从内存保存到硬盘;当下次Redis重启时,利用持久化文件实现数据恢复。除此之外,为了进行灾难备份,可以将持久化文件拷贝到一个远程位置
**RDB **是Redis默认的持久化方式。按照一定的时间周期策略把内存的数据以快照的形式保存到硬盘的二进制文件。即Snapshot快照存储,对应产生的数据文件为dump.rdb,通过配置文件中的save参数来定义快照的周期。( 快照可以是其所表示的数据的一个副本,也可以是数据的一个复制品)
主从模式 采用读写分离的方式
1.读操作 : 主库,从库 都可以接收
2.写操作 : 首先到主库执行,然后,主库将写操作同步给从库
某个master服务宕机后,会把这个master下的某个从服务升级为master来替代已宕机的master继续工作。
将字符串值 value 关联到 key 。
如果 key 已经持有其他值, SET 就覆写旧值,无视类型。
将 key 的值设为 value ,当且仅当 key 不存在。
若给定的 key 已经存在,则 SETNX 不做任何动作。
SETNX 是『SET if Not eXists』(如果不存在,则 SET)的简写。
设置成功,返回 1 。
设置失败,返回 0 。
RabbitMQ 即一个消息队列,主要是用来实现应用程序的异步和解耦,同时也能起到消息缓冲,消息分发的作用。
高频访问数据 分布式会话 分布式锁 字典数据 带有时效性的数据和验证码
1.基于Nginx的ip_hash 负载均衡
2.基于Tomcat的session复制
3.我们服务器将所有的session都保存到缓存库(redis)中
1.读写分离 设置超时时间
2.先写 MySQL,通过 Binlog,异步更新 Redis
利用MySQL 的 Binlog,然后通过异步的方式,将数据更新到 Redis,这种方案有个前提,查询的请求,不会回写 Redis。
1、Mybatis是一个半ORM(对象关系映射)框架,它内部封装了JDBC,开发时只需要关注SQL语句本身,不需要花费精力去处理加载驱动、创建连接、创建statement等繁杂的过程。程序员直接编写原生态sql,可以严格控制sql执行性能,灵活度高。
2、MyBatis可以使用XML或注解来配置和映射原生信息,将POJO映射成数据库中的记录,避免了几乎所有的JDBC代码和手动设置参数以及获取结果集。
3、通过xml文件或注解的方式将要执行的各种statement配置起来,并通过java对象和statement中sql的动态参数进行映射生成最终执行的sql语句,最后由mybatis框架执行sql并将结果映射为java对象并返回。(从执行sql到返回result的过程)。
sql注入原理就是用户输入动态的构造了意外sql语句,造成了意外结果,是攻击者有机可乘
1.参数验证
2.特殊字符过滤
3.使用参数化语句,不要拼接sql
4.编码输出
5.平台过滤
答:
Mybatis在处理#{}时,会将sql中的#{}替换为?号,调用PreparedStatement的set方法来赋值;
Mybatis在处理 时 , 就 是 把 {}时,就是把 时,就是把{}替换成变量的值。
1、Mybatis 动态 sql 可以让我们在 Xml 映射文件内,以标签的形式编写动态 sql,完成逻辑判断和动态拼接 sql 的功能。
2、Mybatis 提 供 了 9 种 动 态 sql 标 签 : trim|where|set|foreach|if|choose|when|otherwise|bind。
其执行原理为,使用 OGNL 从 sql 参数对象中计算表达式的值,根据表达式的值动态拼接 sql,以此来完成动态 sql 的功能。
返回 第一种是使用 resultMap 标签,逐一定义列名和对象属性名之间的映射关系。 第二种是使用sql列的别名功能,将列别名书写为对象属性名。
后端仅开发接口、处理业务逻辑、提供数据;
前端关注交互,调用接口仅做渲染逻辑处理,尽量避免业务逻辑的处理;
前端渲染逻辑禁止跨多个接口调用;
1.将java程序打成一个jar包
2.创建一个文件夹 将jar下载到linux上
3.部署环境变量 修改的配置文件
4.使用java -version 命令检查是否安装成功
ifconfig
pwd
rm
ps
ps-a
docker logs
pull
cd
ls
ll
mkdir
less
find
sh
kill
grep
docker run
docker exec -it
reboot
1.axios
2.Element UI
3.比如说编辑器 表单 或者说文件上传 比如文件上传组件vue-upload-component
4.还有自定义组件
http协议是一种无状态的协议(客户端和服务端互相不认识)
两者都是缓存
session 是服务器端生成的
第一次请求 服务器 创建session对象 生成唯一的session id 保存在浏览器的cookie中
服务器关闭即销毁 tomcat 中 默认20分钟的生命周期
session是存储于服务器端的特殊对象,创建一个唯一的session。这个session是服务器端共享,每个游览器(客户端)独享的
1.session类似于一个Map,是以key-value进行存放的。key必须是一个字符串,value是一个对象。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qHruQYmt-1657109152373)(C:\Users\阿琪\AppData\Roaming\Typora\typora-user-images\image-20220702111415487.png)]