一、Java基础
1.从数据库中查询出来的数据除了用对象接收以外,还可以用什么接收?
1)用hashMap<String,String>存储,然后将map存入list中
2)用对象封装
2.设计模式–单例设计模式
1.懒汉式
2.饿汉式
3.集合
Collection是接口
Collection接口
-- List接口 : 数据有序,可以重复。
-- ArrayList子类
-- LinkedList子类
-- Set接口 : 数据无序,不可以存重复值
-- HashSet子类
-- Map接口 : 键值对存数据
-- HashMap
Collections是工具类
Collections.sort(List<> list):根据元素的自然顺序 对指定列表按升序进行排序。
Collections.max():根据元素的自然顺序,返回给定 collection 的最大元素。
Collections.min():根据元素的自然顺序 返回给定 collection 的最小元素。
Collections.swap(List,i,j):在指定列表的指定位置处交换元素。
Collections.addAll():
3.1.List接口:有序的集合
特点:
1)数据有序
2)允许存放重复数据
3)元素都有索引
4) 可以存放多个null
List集合的几种迭代/遍历,如下:
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
public class collection {
public static void main(String[] args) {
List list = new ArrayList();
list.add(111);
list.add(222);
list.add(333);
list.add('a');
list.add("bbb");
System.out.println(list);//[111, 222, 333, a, bbb]
//第一种遍历:用Collection接口提供的iterator()
Iterator it = list.iterator();
while (it.hasNext()){
Object o = it.next();
System.out.print(o+" ");
}//111 222 333 a bbb
//第二种遍历:用List接口提供的listIterator()
ListIterator it2 = list.listIterator();
while (it2.hasNext()){
System.out.print(it2.next()+" ");
}//111 222 333 a bbb
//第三,增强for循环
for (Object object : list) {
System.out.print(object+" ");
}//111 222 333 a bbb
//第四,下标遍历
for (int i = 0; i < list.size(); i++) {
System.out.print(list.get(i)+" ");
}//111 222 333 a bbb
}
}
1.ArrayList
1}底层使用数组存放数据,每个对象都有下标。
2)查询快,但是数据的增删的效率会降低。
3)new ArrayList():初始容量是10,如果不够会以1.5倍容量增长。
**ArrayList扩容**
当第一次插入元素时才分配10(默认)个对象空间。假如有20个数据需要添加,那么会分别在第一次的时候,将ArrayList的容量变为10,
之后扩容会按照1.5倍增长。也就是当添加第11个数据的时候,Arraylist继续扩容变为10*1.5=15;当添加第16个数据时,继续扩容变为15 * 1.5 =22个
对比HashMap扩容:
成长因子:
static final float DEFAULT_LOAD_FACTOR = 0.75f;
前面的讲述已经发现,当你空间只有仅仅为10的时候是很容易造成2个对象的hashcode 所对应的地址是一个位置的情况。这样就造成 2个 对象会形成散列桶(链表)。这时就有一个加载因子的参数,值默认为0.75 ,如果你hashmap的 空间有 100那么当你插入了75个元素的时候 hashmap就需要扩容了,不然的话会形成很长的散列桶结构,对于查询和插入都会增加时间,因为它要一个一个的equals比较。但又不能让加载因子很小,如0.01,这样显然是不合适的,频繁扩容会大大消耗你的内存。这时就存在着一个平衡,jdk中默认是0.75,当然负载因子可以根据自己的实际情况进行调整。
- LinkedList
1)可以重复,有索引,但是乱序的。
2)底层是一个链表的实现。适用于增删业务,不适用于查询。
3.2.Set接口:没有下标所以数据无序、不包含重复元素,常用来去重
1.HashSet实现类
底层是哈希表,包装了HashMap,相当于向HashSet中存入数据时,会把数据作为K,存入内部的HashMap中。当然K仍然不许重复。
允许使用null元素。
2. TreeSet: 底层就是TreeMap,也是红黑树的形式,便于查找数据。
Map接口也是集合的一种形式,但是和Collection接口没有关系
1.Map接口的特点
1)Map里的数据都是K,V的格式
2)Map里的key不能重复,如果key重复,会把value覆盖掉
3)Map也是无序
2.HashMap实现类的特点
1)同Map的特点
2)HashMap的原理:HashMap底层是一个Entry数组,当存放数据时会根据hash算法计算数据的存放位置。算法:hash(key)%n,n就是数组的长度。当计算的位置没有数据时,就直接存放,当计算的位置有数据时也就是发生hash冲突的时候/hash碰撞时,采用链表的方式来解决的,在对应的数组位置存放链表的头结点。对链表而言,新加入的节点会从头结点加入。
3.(重要)Map怎么遍历
工具: Set<Map.Entry<K,V>> entrySet()
返回此映射所包含的映射关系的 Set 视图。
public class Map {
public static void main(String[] args) {
java.util.Map map = new HashMap();
map.put(100, "刘德华");
map.put(101, "梁朝伟");
map.put(102, "古天乐");
map.put(103, "周润发");
//遍历方式1:keySet()
Set set1 = map.keySet();
Iterator it1 = set1.iterator();
while (it1.hasNext()){
Object key = it1.next();
Object value = map.get(key);
System.out.println(key+" = "+value);
}
//遍历方式2:entrySet()
Set set2 = map.entrySet();
Iterator it2 = set2.iterator();
while (it2.hasNext()){
java.util.Map.Entry e = (java.util.Map.Entry)it2.next();
Object key = e.getKey();
Object value = e.getValue();
System.out.println(key+" = "+value);
}
}
}
线程
1.创建线程的几种方式
第一种:通过继承Thread类创建线程类
步骤如下:
1、定义一个类继承Thread类,并重写Thread类的run()方法,run()方法的方法体就是线程要完成的任务,因此把 run()称为线程的执行体;
2、创建该类的实例对象,即创建了线程对象;
3、调用线程对象的start()方法来启动线程;
public class ExtendThread extends Thread{
private int i;
@Override
public void run() {
for (;i<100;i++){
//当通过继承Thread类的方式实现多线程时,可以直接使用this获取当前执行的线程
System.out.println(this.getName()+" "+i);
}
}
public static void main(String[] args) {
for (int j = 0; j < 50; j++) {
//调用Thread类的currentThread()方法获取当前线程
System.out.println(Thread.currentThread().getName()+" "+j);
if (j==10){
//创建当前类并启动一个线程
new ExtendThread().start();
//创建当前类并启动第二个线程
new ExtendThread().start();
}
}
}
}
部分结果如下 :
Thread-0 70
Thread-0 71
Thread-0 72
Thread-1 0
Thread-1 1
Thread-1 2
第二种 : 通过实现Runnable接口创建线程类
步骤:
1、定义一个类实现Runnable接口;
2、创建该类的实例对象obj;
3、将obj作为构造器参数传入Thread类实例对象,这个对象才是真正的线程对象;
4、调用线程对象的start()方法启动该线程;
public class ImplRunnable implements Runnable{
private int i;
@Override
public void run() {
for (; i < 50; i++) {
//当线程类实现Runnable接口时,要获取当前线程对象只有通过Thread.currentThread()获取
System.out.println(Thread.currentThread().getName()+" " +i);
}
}
public static void main(String[] args) {
for (int j = 0; j < 30; j++) {
System.out.println(Thread.currentThread().getName()+" " +j);
if (j==10){
ImplRunnable thread_target = new ImplRunnable();
//通过new Thread(target,name)的方式创建线程
new Thread(thread_target,"线程1").start();
new Thread(thread_target,"线程2").start();
}
}
}
}
结果如下:
线程1 42
线程1 43
线程1 44
线程2 44
线程2 46
线程2 47
线程2 48
线程2 49
线程1 45
第三种:通过Callable和Future接口创建线程
步骤:
1、创建Callable接口实现类,并实现call()方法,该方法将作为线程执行体,且该方法有返回值,再创建Callable实现类的实例;
2、使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值;
3、使用FutureTask对象作为Thread对象的target创建并启动新线程;
4、调用FutureTask对象的get()方法来获得子线程执行结束后的返回值。
2.线程的声明周期和状态
线程创建之后它将处于新建(NEW)状态,调用start() 方法后开始执行,线程这时候会处于可运行(READY)状态。可运行状态的线程被cpu调用后就处于运行(RUNNING)状态。当线程执行wait() 方法之后,线程进入等待(waitting)状态。进入等待状态的线程需要依靠其他线程的通知才能够返回到运行状态,而超时等待(TIME_WAITING)状态相当于在等待状态的基础上增加了超时限制,⽐如通过 sleep(long millis) ⽅法或 wait(long millis) ⽅法可以将 Java线程置于 超时等待状态。当超时时间到达后线程会重新回到可运行状态。当线程调用同步方法时,在没有获取到锁的情况下,线程会进入阻塞(BLOCKED)状态。线程在执行Runnable的run() 方法之后将进入终止(TERMINATED)状态。
3.说说 sleep() ⽅法和 wait() ⽅法区别和共同点?
1) 两者最主要的区别在于:sleep ⽅法没有释放锁,⽽ wait ⽅法释放了锁 。
2) Wait 通常被⽤于线程间交互/通信,sleep 通常被⽤于暂停执⾏。
3) wait() ⽅法被调⽤后,线程不会⾃动苏醒,需要别的线程调⽤同⼀个对象上的 notify() 或者
notifyAll() ⽅法。sleep() ⽅法执⾏完成后,线程会⾃动苏醒。或者可以使⽤ wait(long
timeout)超时后线程会⾃动苏醒。
4) **共同点:**两者都可以暂停线程的执行。
二、SSM框架
SpringMVC的五大组件
三、MySQL
MySQL的优化
大表优化
当MySQL单表记录数过大时,数据库的CRUD性能会明显下降,优化措施如下:
1、限定数据的范围
务必禁⽌不带任何限制数据范围条件的查询语句。
⽐如:我们当⽤户在查询订单历史的时候,我们可以控制在⼀个⽉的时间范围内;
2、读写分离
经典的数据库拆分⽅案,主库负责写,从库负责读;
3、垂直分区
简单来说垂直拆分是指数据表列的拆分,把⼀张列⽐较多的表拆分为多张表。
如下图所示,
MySQL⾼性能优化规范建议
- 禁止使用 SELECT * 必须使用 SELECT <字段列表> 查询
- 禁止使用不含字段列表的 INSERT 语句
如:
insert into values ('a','b','c');
应使用:
insert into t(c1,c2,c3) values ('a','b','c');
- 对应同一列进行 or 判断时,使用 in 代替 or
in 的值不要超过 500 个,in 操作可以更有效的利用索引,or 大多数情况下很少能利用到索引。 - 如果知道查询结果只有一条或者只要最大/最小一条记录,建议用limit 1。
- 应尽量避免在where子句中使用or来连接条件。
- Inner join 、left join、right join,优先使用Inner join,如果是left join,左边表结果尽量小
Inner join 内连接,在两张表进行连接查询时,只保留两张表中完全匹配的结果集
- 索引不宜太多,一般5个以内。
- 尽可能使用varchar 代替 char。
因为首先变长字段存储空间小,可以节省存储空间。
其次对于查询来说,在一个相对较小的字段内搜索,效率更高。
索引
1、索引是什么
1.1 索引是表的目录,类似于字典中的目录,查找字典内容时可以根据目录查找到数据的存放位置,以此快速定位查询数
据。对于索引,会保存在额外的文件中。
1.2索引可以提高查询速度,会减慢写入速度,索引的缺点是创建和维护索引需要耗费时间。
日后再整理:
https://blog.csdn.net/u012954706/article/details/81241049
事务与隔离级别
-
什么是事务?
事务是逻辑上的一组操作,要么都执行,要么都不执行。 -
事务的四大特性(ACID)
原子性 (Atomicity):事务是最小的执行单位,不允许分隔。事务的原子性确保动作要么全部完成,要么完全不起作用。
一致性 (Consistency):执行事务前后,数据保持一致,多个事务对同⼀个数据读取的结果是相同的。比如A向B转账100元,转账之前他们各有1000元,那么转账之后他们的钱的总和也应该是2000元。
隔离性(Isolation):并发访问数据库时,⼀个⽤户的事务不被其他事务所⼲扰,各并发事务之间数据库是独⽴的。
持久性(Durability): ⼀个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发⽣故障也不应该对其有任何影响。 -
并发事务会带来哪些问题
脏读:A事务正在访问数据并且进行修改,而这种修改还没有提交到数据库,这时B事务也访问了这个数据,然后使用了这个数据,因为这个数据还没有提交,所以B事务读到的数据是“脏数据”。
丢失修改:事务1读取某表中的数据A=20,事务2也读取A=20,事务1修改A=A-1,事务2也修改A=A-1,最终结果A=19,事务1的修改被丢失。
不可重复读:A事务需要多次读取同一数据,但是A事务还没结束时,B事务对这个数据进行了修改,进而导致了A事务两次读到的数据不一样。
幻读:和不可重复读类似。A事务读取了几行数据,接着B事务插入了一些数据。在随后的查询中,A事务就会发现多了一些原本不存在的记录,就好像发生了幻觉。 -
事务隔离级别有哪些?MySQL的默认隔离级别是?
有四个隔离级别:
READ-UNCOMMITTED(读取未提交): 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。
READ-COMMITTED(读取已提交): 允许读取并发事务已经提交的数据,可以阻⽌脏读,但是幻读或不可重复读仍有可能发⽣。
REPEATABLE-READ(可重复读): 对同⼀字段的多次读取结果都是⼀致的,除⾮数据是被本身事务⾃⼰所修改,可以阻⽌脏读和不可重复读,但幻读仍有可能发⽣。
SERIALIZABLE(可串⾏化): 最⾼的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执⾏,这样事务之间就完全不可能产⽣⼲扰,也就是说,该级别可以防⽌脏读、不可重复读以及幻读。
MySQL InnoDB 存储引擎的默认⽀持的隔离级别是 第三等 REPEATABLE-READ(可重读)。