2020-12-20

2020/12/06

冒泡排序 选择排序

class A {
	public static void main (String[] args) {
		int[] arr = {2, 3, 4, 1, 7, 35, 15};
		
        // 冒泡排序 相邻两个数据排列 最大数在最下边 for i是需要循环比较的次数
        for (int i = 0; i < arr.length - 1; i++) {
            // for j 是真正执行比较的代码
            for (int j = 0; j < arr.length - 1; j++) {
            	// 相邻两数比较
                if (arr[j] > arr[j + 1]) {
                    int temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
                }
            }
        }
        System.out.println(Arrays.toString(arr));
        
	// <---------------我是分割线-------------------->
	
		// 选择排序  第一个数依次和后边的数进行比较
		// i 与后边所有的数进行比较 并换位 
        for (int i = 0; i < arr.length-1; i++) {
            // j=i+1 索引开始比较,起始位置是1 
            for (int j = i+1; j < arr.length; j++) {
                if (arr[i] > arr[j]) {
                    int temp = arr[i];
                    arr[i] = arr[j];
                    arr[j] = temp;
                }
            }
        }
        System.out.println(Arrays.toString(arr));
	}
}

Arrays工具类

- Arrays.sort();  // 数组排序
- Arrays.toString(); 	// 打印数组
- Arrays.asList(arr);  //根据数组创建ArrayList<>集合;
- ArrayUtils.addAll(arr1,arr2); //  合并两个数组
- list.toArray(arr);  //集合转为数组;**加粗样式**
- ArrayUtils.reverse(arr) ;  //数组元素反转 
- ArrayUtils.removeElement(arr,index) ;  //移除元素,创建新的数组 
JDK7之前使用的是**快速排序**;
JDK8之后使用的是**归并排序**;

二分查找

1. 使用二分查找之前需要先对数组进行排序。
	Arrays.sort();

2. 代码实现,定义方法实现二分查找 binarySearch();
	// 传入参数为排序之后的arr[] + 搜索值  ;
    public static int binarySearch(int[] arr, int target) {
        // 二分查找 定义左右指针索引 left right
        int left = 0;
        int right = arr.length-1;
        // 当right在left左边的时候(left > right),表示数组搜素完毕 停止搜索,所以搜索条件是left <= right;
        while (left <= right) {
            // 定义中值索引 mid ,防止溢出
            int mid = left + (right - left) / 2;
            if (arr[mid] == target) {
                return mid;
            } else if (arr[mid] < target) {
                left = mid + 1;
            } else if (arr[mid] > target) {
                right = mid - 1;
            }
        }
        return -1;
    }

异常及异常的处理

Throwable是所有异常的父类;
    |-- exception 异常,可以使用throw抛出/try...catch进行捕获,异常一旦产生便不会继续执行下面的代码。	
        |-- runtimeException 运行时异常,程序会继续运行
        |-- exception 编译时异常,此时程序不会继续编译
    |-- error 错误,无法挽回

1. 系统会在出现异常的地方创建一个异常对象 new ArrayIndexOutOfBoundsException(),将其抛给方法调用者位置;如果调用者也没有处理这个异常,就会将异常抛给JVM。
2. JVM此时会将异常信息输出到控制台或者停止程序。
        
// 异常的抛出关键字 
1. throw  手动向外抛出异常对象;throw new 异常类名();
2. object 通过null调用任何非静态方法或属性都会引发空指针异常;  requireNonNull(T obj)
3. throws 进行方法的异常声明;在方法后面 + throws 异常类名(可以包含多个异常类名)
4. try...catch try{ 可能出现异常的代码 } catch ( 异常类名 变量名 ){};
	|-- 如果try中没有任何异常,就会跳过catch()向下执行
    |-- try中代码有异常,catch()捕获到了异常,此时会执行catch()代码中的代码(需要try中抛出的异常和catch中的异常类一致)
        |--catch情况下,那个catch先捕获到异常,就执行那个catch语句,其他的catch块则不会执行,然后再执行其他语句。
        |-- 在多catch处理异常情况下,父类异常不能放在子类异常的前边(可以在catch最后使用Exception来捕获其他异常)
    |-- try中代码有异常但是catch()没有捕获到异常,那么代码还是会向外抛出异常。
5. finally try{} catch(){} finally{ 一定会执行的内容,可以在里面添加释放资源的操作 }  

// 编译时异常和运行时异常区别
- 编译时异常:如果编译时期会出现异常就必须处理,try...catch/throws
- 运行时异常: 编译时期出现
    |-- 父类方法抛出了多个异常,子类重写父类方法,只能抛出相同异常或是它的子异常。
    |-- 父类方法不抛出异常的话,子类方法重写父类方法也不能抛出该异常,只能进行try...catch处理
    |-- 当多异常进行捕获处理时,前边抛出的异常不可是后边异常的父类;
    	|-- 多异常捕获使用比较多的是一次捕获,多次处理方式;
    |-- try...catch中添加finally代码块,finally代码块中可以添加释放资源的操作;
// 捕获异常信息 
        -- 需要使用捕获到的异常对象来调用方法 e = new Exception()
void printStackTrace() -开发者看- 使用标准的错物流将异常信息输出到console System.err.println()    
String getMessage() -给用户看- 获取异常信息的字符串,简短的异常信息

// 自定义异常
        

多线程

// 最终目的是:提高CPU利用率
进程:在内存中运行的程序就是进程,一个程序中至少有一个线程;
线程:程序中的执行单元就是线程,每个线程可以单独执行一个任务。
单线程程序:程序中只有一个线程,只能做一件事;
多线程程序:程序中有多个线程,可以同时做多个事;    
线程需要由CPU调度才会去执行,一个CPU只能调度一个线程,CPU切换速度很快,可以看成是同时;
    |-- 抢占式调度:线程随机抢占,主动权在CPU。
// 并发和并行
    并发:同一时间,多个线程一起执行,
    并行:真正意义上的多线程,同一时间点,多个线程一起执行。(必须有多个CPU的支持)
     // main线程执行main方法
// Thread类 完成多线程操作
    栈内存是私有的,每一个线程都有自己的栈内存来运行自己的方法;方法是由哪个线程执行的,就在哪个线程的栈内存中开辟空间。
    堆内存:所有的线程都共同使用一个堆内存
// Thread中的方法
    1.定义类继承Thread类,重写Thread类中的run()方法;
    2.在另外一个类中通过创建继承Thread类的对象,来调用它的start()方法实现。
    Thread();
	Thread("线程名字")
   	getName();
	setName();
	currentThread(): 获取当前执行线程
	sleep(long millis): 线程休眠
// Runnable接口
   1.类实现Runnable接口,重写run(),在run()中定义要实现的内容;
   2.创建 new Thread(new MyThread()).start();  new Thread()中传入实现Runnable接口的类的对象;调用线程的start()方法。
// 匿名内部类 某个类的子类只使用了一次
	new Thread(new Runnable(){
        重写run()方法
    }).start();		        

2020/12/07

volatile

只能保证可见性,不能保证原子性。
被volatile关键字修饰的变量,在被其他线程修改时,对于其他线程来说也是可见的。    

synchronized

1.修饰同步代码块
synchronized(锁对象){
    ... 
}
锁对象是一个普通的java对象,可以是任何类型的。只起到一个标记的作用;
会牺牲效率;
2.修饰方法
    |-- 如果同步方法是非静态的,锁是this,表示本类的一个对象;
	|-- 如果是静态的,锁对象是 当前类名.class

Lock接口

Lock接口需要使用实现类ReentrantLock。
    Lock lock = new ReentrantLock();
	在需要加锁的前边:lock.lock()进行加锁;
        走完方法之后需要释放锁:lock.unlock();

java内存模型

JMM java memory model;
定义了线程对于共享变量的访问规则;
    |-- 主内存:线程共享的数据是保存在主内存中
    |-- 线程的工作内存:线程会将主内存复制一份到自己的工作内存,线程无法直接操作主内存数据;之后再将工作内存数据保存到主内存中;
线程内的数据是线程私有的,保存到主内存的数据是共享内存 -- > 堆区

原子性

多个操作是不可分割的整体。
    count++ 不具备原子性。 ++ 并不是一个整体; 其中的某些操作会被插队。
    可以使用synchronized关键字来对多线程操作的步骤进行同步,保证只有一个线程能拿到锁对工作空间以及主空间操作。    

原子类

AtomicInteger 整数原子类  需要new AtomicInteger()之后调用下面的方法。
    |-- get() 获取当前值
    |-- getAndIncrement() 返回自增前的值 (可以保证保证原子性)  -- count++
    |-- incrementAndGet() 返回自增后的值
    |-- getAndDecrement() (可以保证保证原子性)  -- count--
    
// 类实现Runnable接口,需要将实现类的对象传到new Thread( 实现类对象 ).start();
// 继承Thread类,需要通过该类对象调用start()方法实现多线程。    

CAS机制

compare and Swap 先比较再交换;
    比较旧的预期值与主内存中的值是否相同,如果不是则表示主内存数据被修改。
    内存地址保存值:V
    旧的预期值:A
    新的修改值:B
乐观锁/悲观锁:synchronized认为每次都会引发线程安全问题,只让一个线程去访问;可以看成是悲观锁;
    CAS认为每次都不会发生线程安全问题,只有将数据保存到主内存中才会去验证该数据是否被其他线程操作,可以看成是乐观锁;

ConcurrentHashMap

HashMap线程不安全;
Hashtable是线程安全的,对方法加了synchronized关键字来保证同步;效率低;==表锁==
ConcurrentHashMap线程安全,内部使用了CAS+分段锁。分段锁对桶进行的加锁,不会对其他桶加锁,可以实现多线程修改;    

CountDownLatch 线程等待

# CountDownLatch(int count)
await() 线程等待,直到计数器(count)的值到0;
countDown() 计数器值-1;
# 通过构造方法传入同一个参数,保证使用的是同一个CountDownLatch对象。
类需要继承Thread类;
需要同时在类中定义构造方法,并且传入(CountDownLatch 对象),在main方法中创建CountDownLatch对象,并设置计数器的值,然后new Task(countdownlatch).start; 传入countdownlatch对象。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aCLLa7eD-1608212449615)(C:\Users\76734\AppData\Roaming\Typora\typora-user-images\image-20201208210233152.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FKAcsR0Z-1608212449620)(C:\Users\76734\AppData\Roaming\Typora\typora-user-images\image-20201208210252946.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QGxc7Dsn-1608212449621)(C:\Users\76734\AppData\Roaming\Typora\typora-user-images\image-20201208210314232.png)]

CyclicBarrier

让一组线程达到一个同步点时被阻塞,知道最后一个线程到达的时候,阻塞才会消失;
CyclicBarrier(int count,Runnable barrierAction),当有count个线程到达同步点的时候,会执行barrierAction方法
int await()  线程等待,会通知barrierAction已经到达同步点
count 是线程数; barrierAction 是需要执行的方法,需要实现Runnable接口
需要使用构造方法传参,使得变量都是一致的。
使用await()方法来告诉barrierAction,线程到齐了,可以执行方法了。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kY8No7EP-1608212449624)(C:\Users\76734\AppData\Roaming\Typora\typora-user-images\image-20201208214341344.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xXLBOtpf-1608212449627)(C:\Users\76734\AppData\Roaming\Typora\typora-user-images\image-20201208214413404.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Q5IHKxNt-1608212449629)(C:\Users\76734\AppData\Roaming\Typora\typora-user-images\image-20201208214427686.png)]

Semaphore

控制线程的并发数量;
Semaphore(int permits) 允许几个线程共同执行某段代码
acquire() 获取凭证
release() 释放凭证

线程池

Executor是根接口,一般使用ExecutorService,提供了线程工具类池:管理线程池的方法
线程池工具类:Executors,获取线程池
# ExecutorService newFixedThreadPool(int nThreads)	创建定长线程池
submit() 提交线程任务并执行
Future submit(Callable task) 
shutdown() 销毁线程池
使用步骤:
	|-- 通过Executors工具类获取线程池
	|-- 使用submit(),提交线程任务
	|-- 销毁线程池,一般不做。
	线程池中的线程序号是从1开始的,pool-1-thread-1

Callable + 线程池

Executors中的submit(Callable task)返回值是Future类型的,封装了结果,最后需要调用Future中的get()来取到结果;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LUExdaur-1608212449631)(C:\Users\76734\AppData\Roaming\Typora\typora-user-images\image-20201209081344458.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MGXgQt0y-1608212449632)(C:\Users\76734\AppData\Roaming\Typora\typora-user-images\image-20201209081358184.png)]

a = a + b和a+=b

a=a+b,需要我们进行强制类型转换,否则会编译错误;
a+=b,则不需要

java面向对象解释

1. 封装:将类的某些信息隐藏在类内部,不允许外部直接访问,而是需要通过该类提供的方法来实现对类中属性的访问。private setter/gettrt
	|-- 例如java的内部类
		内部类的方法可以直接访问外部类的所有数据,包括私有数据;
		内部类提供了更好的封装,不允许同一个包中的其他类访问该类。
2. 继承:继承是单继承,只有一个顶级父类  
	final修饰 不可被继承,
	|-- 继承初始化顺序
			1.初始化顺序:父类 子类
		2.初始化对象中的属性,执行构造方法中的初始化
3. 多态:  抽象类
	创建本类对象时,调用的方法为本类方法;
	创建子类对象时,调用的方法为子类重写的方法/继承的方法;
4. 抽象 使用abstract修饰类
	public abstract class Telephone(){
		// 可以没有抽象方法
		public void call(){
			System.out.println("抽象方法");
		}
		
		// 抽象方法
		public abstract void call();
		
	}
	继承抽象类的子类需要重写抽象方法。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SryswQp3-1608212449633)(C:\Users\76734\AppData\Roaming\Typora\typora-user-images\image-20201210161212508.png)]

面试题集合

#{} ${} 的区别

#{}是预编译处理,${}是字符串替换。 
预编译就是提前对SQL语句进行预编译,而其后注入的参数将不会再进行SQL编译。
SQL注入发生在编译的过程中,因为恶意注入了某些特殊字符,最后被编译成恶意的执行操作,预编译机制可以很好的防止SQL注入。
mybatis在处理#{}时,会将SQL中的#{}替换为?号,调用PreparedStatement的set方法来赋值;
mybatis在处理${}时,会将${}替换成变量的值;但是表名只能用${}来替换。

String

String中的split(",")方法, 会将字符串进行分割,最后到一个数组中; --> 可以分割
	但是split(".")会返回一个空数组[],原因是"."会跟正则的符号冲突,此时需要进行转译操作;
	详细操作如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VXCCQOww-1608212449634)(C:\Users\76734\AppData\Roaming\Typora\typora-user-images\image-20201210164002539.png)]

TCP UDP

  1. TCP是有连接的(三次握手),UDP是无连接的;
  2. TCP传输数据有重传机制,如果在传输数据的过程中发生了数据丢失事件,发送方会重传丢失的分组,保证一定能到达接收方,因此是可靠的。UDP不能保证数据传输的可靠性;
  3. UDP有较好的实时性,工作效率比TCP高;
  4. TCP连接是点对点,UDP支持一对一、一对多、多对多;
  5. TCP对系统资源要求较多(为连接分配发送缓存和接受缓存,维护拥塞控制变量),UDP要求较小;
    1. TCP应用:
      1. FTP文件传输协议;
      2. SSH:安全登录,文件传送SCP和端口重定向;
      3. Telnet:不安全文本传送;
      4. SMTP:简单邮件传输协议
      5. HTTP:超文本传输协议
    2. UDP应用:
      1. 流媒体:
      2. 实时游戏:
      3. 物联网
      4. QQ 语音 视频

三次握手

  1. 第一次握手:建立连接时,客户端发送syn包到服务器,进入SYN_SEND状态,等待服务器确认;

  2. 第二次握手:服务器收到syn包,必须确认客户的SYN,同时自己也发送一个SYN包,SYN+ACK,此时服务器进入SYN_RECV状态;

  3. 第三次握手:客户端收到服务器的SYN+ACK包,想服务器发送确认包ACK,发送完毕之后,客户端和服务器进入ESTABLISH状态,完成三次握手;

    三次握手最主要目的是为了保证连接是双工的,可靠更多的靠的是重传机制。

四次挥手

// 客户端与服务器需要请求响应一次才可以关闭连接

  1. 客户端发送一个FIN,用来关闭客户A到服务器B 的数据传输;
  2. 服务器B收到这个FIN,他发回一个ACK,确认序号为收到的序号+1,跟SYN一样,一个FIN占用一个序号;
  3. 服务器B关闭与客户端A的连接,发送一个FIN给客户端A
  4. 客户端A返回ACK报文确认,并将确认序号设置为收到的序号加1

redis(重点)

  1. redis全称:Remote Dictionary Server 远程数据字典服务器,K-V类型的内存数据库,可以对K-V设置expire过期时间;
  2. redis会将整个数据库加载在内存中进行操作,定期通过异步操作把数据库数据flush到硬盘上保存;
  3. 纯内存操作
  4. 支持多种数据结构:String 、 Set(无序集合) 、 List 、Hash 、 Sorted Set(有序集合)
    1. 使用List来做FIFO的双向链表,实现一个轻量化的高性能消息队列服务;
    2. 使用Set来做高性能的tag分类系统等;(关键词标记,用于搜索查找。也可以将文章中没有的关键词作为tag来标记)
  5. redis主要消耗内存资源;
  6. redis有哪几种淘汰策略 六种:(内存占用满了,需要清除内存中数据)
    1. no-enviction: 禁止删除数据, 直接抛出out fo memory;
    2. 从已设置过期时间的数据集
      1. 最近最少使用的数据
      2. 随意淘汰的数据
      3. 将要过期的数据
    3. 从数据集中
      1. 最近最少使用
      2. 随意淘汰
  7. 一个字符串容量的值能存储最大容量是512M
  8. redis为达到最大读写速度,将数据都读到内存中,并通过异步的方法将数据写入磁盘。如果不将数据放入内存中,磁盘的I/O速度会严重影响redis性能。
  9. redis应用场景:
    1. 会话缓存 Session Cache :redis提供持久化
    2. 全页缓存 FPC:即使重启了redis实例,因为有磁盘的持久化,用户也不会看到页面加载速度的下降;
    3. 队列 FIFO :list set
    4. 排行榜/计数器:
    5. 发布/订阅 :
  10. redis没有使用一致性hash,引入了哈希槽;redis一共有16384个哈希槽,每个key通过CRC16(循环冗余校验)校验后对16384取模来决定放在哪个槽,集群的每个节点负责一部分hash槽。

redis集群的主从复制模型

为了使在部分节点失效或者大部分节点无法通信的情况下集群仍可用,所以集群使用了主从复制模型,每个节点都会有N-1个复制品;

redis集群之间的复制方式

异步复制。

redis集群最大节点个数是多少

16384个。

redis集群如何选择数据库

目前无法做选择,默认在0数据库。

redis事务

redis事务是一个单独的隔离操作:事务中的所有命令都会被序列化、按顺序的执行。事务在执行的过程中,不会被其他客户端发送来的请求打断。事务是一个原子性操作:要么同时成功,要么同时失败;

redis key的过期时间和永久有效分别怎么设置?

expire(设置过期时间)和persist(持久化)。

redis如何做内存优化

尽可能使用散列表(hashtable),使用的内存非常小;可以将用户所有信息存储到一张散列表里面;

redis的持久化机制

RDB和AOF。

开启AOF:appendonly no —> 修改为 appendonly yes,之后会生成一个appendonly.aof文件

设置持久化时机:appendfsync everysec

RDB是在指定的时间间隔内将内存中的数据集写到磁盘中,是默认的持久化方式,文件名为dump.rdb。可以恢复大数据,恢复速度快,但是恢复完整性没有AOF那么好。

AOF机制:会记录每一个写命令,保存到.aof文件中,所以文件会比较大;为了压缩AOF文件,redis提供了bgrewriteaof命令。每秒一次同步。

缓存穿透(一个KEY)

故意查询一个一定不存在的key,请求量很大,会对后端DB造成很大压力,即缓存穿透;

如何避免:如果一个查询返回的数据为空,就把这个空结果缓存,设置一个过期时间,最长不超过五分钟;

缓存雪崩(多个key)

设置缓存时设置了相同的过期时间,导致缓存在同一时间同时失效,请求全部转发到DB,DB崩溃。

解决方法:可以在原有的失效时间上增加一个随机值,比如1-5分钟随机,expire不在要同一个点上。

缓存击穿(一个KEY)

对于一些设置了过期时间的key,如果这些key可能会在某些时间点被超高并发访问,成为热点数据。

解决方法:设置热点数据永不过期

​ 物理不过期:redis不设置过期时间即可;

​ 功能不过期:将过期时间存在key的value中,如果发现快要过期了,通过后台的异步线程进行缓存的构建;

Jedis

SpringCloud

1. SpringCloud服务间的通讯方式

有两种。通过REST接口调用服务的http接口,参数和结果默认都是通过Jackson序列化和反序列化

  1. RestTemplate方式
  2. Feign方式

2. 创建SpringCloud微服务步骤

  1. 创建普通Springboot项目
  2. 添加依赖pom.xml,spring-cloud-starter-eureka和spring-cloud-starter-feign
  3. 添加注解:在工程的入口类上添加开启Spring Cloud Feign的支持功能:@EnableFeignClient
  4. 声明服务:通过@FeignClient注解来指定服务名并绑定服务;
  5. Controller中调用服务
  6. 属性配置:在配置文件中指定服务注册中心,配置端口号等;

3. 服务注册形式

  1. 客户端注册:zookeeper 服务自身要负责注册与注销工作。register(“服务名”,地址和端口号),心跳机制
  2. 第三方注册:Eureka注册中心,服务启动后以某种方式通知Registar,然后Registar向注册中心注册。

API网关

可以做:授权、监控、分发路由、负载均衡、缓存、请求分片、管理、静态响应处理等;

API 网关负责请求转发、合成和协议转换。所有来自客户端的请求都要先经过API gateway,然后路由这些请求到对应的微服务。

配置中心

一般用作系统的参数配置,需要满足:高效获取、实时感知、分布式访问。

zookeeper配置中心:将数据加载到内存方式解决高效获取的问题,借助zookeeper的节点监听机制来实现实时感知。(Watcher)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OH8vLCdA-1608212449635)(C:\Users\76734\AppData\Roaming\Typora\typora-user-images\image-20201211150351060.png)]

事件调度(kafka)

消息服务和事件的统一调度,常用kafka,mq

服务跟踪 Starter-sleuth

实现请求从一个微服务到下一个微服务的传播过程,Spring Cloud Sleuth在日志中引入唯一ID,保证微服务调用之间的一致性,通过此方式来跟踪请求的传递路线。

请求到达分布式系统的入口端点时,需要为其创建一个唯一的跟踪标识,同时在分布式系统内部流转的时候,会一直保持该值的传递,知道返回给请求放为止。TraceID

服务熔断Hystrix

三种状态:全开 半开 关闭

微服务框架中通常会有多个服务层调用,基础服务的故障可能导致级联故障,进而造成整个系统不可用的情况,即服务雪崩效应:因服务提供者不可用导致服务消费者的不可用,并将不可用逐渐放大的过程。

熔断器可以实现快速失败,如果在一段时间内侦测到许多类似的错误,会强迫其以后的多个调用快速失败,不再访问远程服务,从而防止应用程序不断的尝试执行可能会失败的操作。

**断路器机制:**当请求后端服务失败数量超过一定比例(默认50%)断路器会切换到开路状态Open,此时所有的请求会直接失败而不会发送到后端服务。

断路器保持开路状态一段时间后(默认5秒)自动切换到半开路状态,此时会判断下一次请求的返回情况,如果请求成功,断路器切回闭路状态Closed,否则重新切换到开路状态。

API管理

SwaggerAPI 管理工具。

SpringBoot

特点

1. 内置Tomcat,无需部署war文件;
2. 简化Maven配置;
3. 创建独立的Spring应用程序
4. 自动配置Spring

Mybatis缓存

分为一级缓存和二级缓存:
	默认情况下一级缓存是开启的且不能关闭。
	一级缓存指SQLSession级别的缓存(本地缓存,缓存使用的数据结构是一个map)。当在同一个SQLSession中进行相同的SQL语句查询时,第二次之后的查询不会从数据库查询,可以直接从缓存中获取,一级缓存最多缓存1024条SQL。当出现commit操作时,需要将SQLSession中的一级缓存区域全部清空,下次查询需要从数据库查,之后写入本地缓存。
	|-- key:MapperID + offset + limit +Sql + 所有入参
		value:用户信息
	二级缓存指可以跨SQLSession的缓存(全局缓存),是mapper级别的缓存。mapper以命名空间为单位创建缓存数据结构,结构是map。mybatis的二级缓存是通过CacheExecutor实现的。
	配置:mybatis全局配置中启用二级缓存配置;
		在对应的Mapper.xml中配置cache节点;
		在对应的select查询节点中添加uesCache = true;

Tomcat

Spring

事务

事务的四种特性

ACID:原子性Atomic ,一致性(Consistency) 隔离性(Isolation) 持久性(Durability)

事务的传播特性 7种

  1. required:如果当前没有事务就新建一个事务;如果已存在一个事务就加入到这个事务中,Spring默认选择
  2. supports:支持当前事务。如果当前没有事务,就以非事务方法执行;
  3. mandatory:使用当前事务。如果当前没有事务,就抛出异常;
  4. required_new:新建事务。如果当前存在事务,把当前事务挂起。
  5. not_supported:以非事务方式执行操作。如果当前存在事务,就把当前事务挂起;
  6. never:以非事务方式执行。如果当前事务存在,则抛出异常;
  7. nested:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与required类似的操作;

事务隔离级别

  1. 读未提交:read uncommitted
  2. 读已提交:read committed
  3. 可重复读:repeatable read,mysql默认事务隔离级别。
  4. 串行化:serializable,事务被处理为顺序执行。

Spring支持的事务模式

  1. 编程式事务:对基于pojo的应用来说是唯一选择,需要在代码中调用beginTransaction() commit() rollback()等事务管理相关方法;(比较落伍) – 需要在代码中调用方法;
  2. 声明式事务:基于@Transaction注解的声明式事务管理
  3. 基于Aspectj AOP配置事务
  4. 基于TransactionProxyFactoryBean的声明式事务管理

Spring常用注解

  1. @Controller :标注控制层组件。用于标记类,声明该类是一个SpringMVC Controller对象;
  2. @RestController: @Controller + @Response
  3. @Component:声明该类是一个组件类;
  4. @Repository:用于注解dao层,在daoImpl类上面注解
  5. @Service:标注业务层组件
  6. @ResponseBody:异步请求,将Controller方法返回的对象,通过适当的HTTPMessageConverter转换为指定格式后,写入到Response对象的body数据区;返回的是json xml
  7. @RequestMapping:用来处理请求地址映射的注解。用于类或者方法上;
  8. @Autowired:对类成员、方法和构造函数进行标注,完成自动装配的工作。通过@Autowired的使用来消除set get 方法;
  9. @PathVariable:将请求URL中的模板变量映射到功能处理方法的参数上,取出rul模板中的变量作为参数;
  10. @RequestParam:用于在SpringMVC后台控制层获取参数 == request.getParameter(“name”)
  11. @ModelAttribute:
  12. @Valid:实体数据校验,结合hibernate validator一起使用

AOP

面向切面编程,将影响多个类的公共行为封装到一个可重用模块,将其命名为Aspect。将与业务无关,但是被业务模块共同调用的逻辑封装起来,减少代码重复率,降低模块之间的耦合。比如权限认证、日志。

bean注入和装配的方式

-- xml getter/setter 构造函数 注解方式来实现,最简单的是注解方式

Spring依赖注入四种方式

  • 构造器注入:
  • setter方法注入
  • 静态工厂注入
  • 实例工厂

Spring与第三方的整合

  • 权限:shiro 认证 授权 加密 会话管理 缓存 与web集成
  • 缓存:redis 高性能的缓存数据库;Ehcache 纯java的进程内缓存框架,Hibernate中默认的CacheProvider
  • 持久层框架:
    • Hibernate 全自动的ORM框架,封装了JDBC,将POJO于数据库表建立映射关系;
    • mybatis:支持普通SQL查询,存储过程和高级映射的优秀持久层框架;
  • 定时任务:crontab 是Linux中查看定时任务的命令;
    • quartz:作业调度框架,由java编写
    • Spring-Task:支持注解和配置文件两种形式
  • 校验框架:
    • Hibernate validator :验证bean的字段,基于注解
    • Oval:可扩展的java对象数据验证框架,验证规则可以通过配置文件;Annotation、pojos进行设定。

SpringIOC

IOC容器管理bean的创建以及实例化,降低类与类之间的耦合性,当想要对象的时候可以直接去IOC容器中获取;

  1. Spring通过一个配置文件来描述Bean和Bean之间的依赖关系(XML @Configuration注解的java类 @Autowired注解),IOC先读取配置文件获取bean的配置信息;
  2. 利用反射功能实例化Bean并建立Bean之间的关系。根据bean定义注册表来实例化bean;(BeanDefinitionRegistry注册表提供了向容器手工注册BeanDefinition对象的方法,BeanDefinition对象描述了Bean的配置信息)
  3. 将bean实例化对象放入Spring容器中;
  4. 然后应用程序可以从Bean缓存池中取想要的bean对线;(bean缓存池的底层实现是HashMap)
  • BeanFactory:主要方法是getBean(String beanName) ,从容器中返回特定名称的Bean。

Spring中的设计模式

  • 工厂模式:IOC就是有BeanFactory来创建对线;
  • 单例模式:默认Spring创建的对象就是单例;
  • 代理模式:AOP的实现就是基于代理模式(jdk动态代理 CGlib代理)
  • 模板方法:RestTemplate JMSTemplate
  • 观察者模式:一对多的依赖关系,当一个对象状态发生改变,所有依赖它的对象都会得到通知;

Spring中Bean的作用域

  • singleton:IOC容器中只会存在一个共享的bean实例。

    <bean id="userDao" class="com.ioc.UserDaoImpl" scope="singleton"> </bean>
    
  • prototype:原型模式,每次通过Spring容器获取prototype定义的bean时,容器都将创建一个新的Bean实例,每个Bean实例都有自己的属性和状态。对有状态的bean使用prototype作用域,对无状态的bean使用singleton作用域;

  • Request:一个request一个实例。不同Http请求会产生新的Bean,仅在当前http请求有效,请求结束,则bean的实例也会被销毁;

    <bean id="loginAction" class="com.cn.Login" scope="request" />
    
  • session:

  • global Session:

java基础

乐观锁 悲观锁

  • CAS 乐观锁: AutomicalInteger 保证可见性单个变量的原子性 (自旋锁)
  • synchronized 悲观锁 :

获取对象的3中方法

  1. 调用对象的getClass();

    Person p = new Person();
    Class clazz = p.getClass();
    
  2. 调用类的class属性来获取该类对应的Class对象; Class clazz = Person.class;

  3. 使用Class类中的forName()静态方法(安全性/性能最好)

    Class clazz = Class.forName("类的全路径");  -- 最常用的方法
    

创建对象的2种方法

// .newInstance()
Class clazz = Class.forName("类的全路径");
Person p = clazz.newInstance();  -- 类对象调用newInstance()来创建对象

// 获取构造方法来和创建对象
    constructor c = clazz.getDeclaredConstructor(String.class,String.class,int.class)  -- 获取类的构造函数,构造参数
    Person p = (Person)c.newInstance("张三","男",32);

匿名内部类

(new Bird(){
    @Override // 重写方法
    public int fly(){
        return 2;
    }
})
// 

网络

7层模型

  1. 物理层
  2. 数据链路层
  3. 网络层
  4. 传输层
  5. 会话层
  6. 表示层
  7. 应用层

TCP/IP原理

因特网整个TCP/IP协议族。TCP/IP由四个层次组成:网络接口层,网络层,传输层、应用层。

你可能感兴趣的:(java)