补充知识点

本文详细介绍了Java中的常用集合接口(如List、Set、Map)及其实现类,探讨了设计模式中的单例、工厂、代理等,并涵盖了数据库操作、异常处理、MyBatis使用、分布式系统中的主从复制和读写分离等内容。
摘要由CSDN通过智能技术生成

常用集合的分类

Collection 接口的接口 对象的集合(单列集合)
├——-List 接口:元素按进入先后有序保存,可重复
│—————-├ LinkedList 接口实现类, 链表, 插入删除, 没有同步, 线程不安全
│—————-├ ArrayList 接口实现类, 数组, 随机访问, 没有同步, 线程不安全
│—————-└ Vector 接口实现类 数组, 同步, 线程安全
│ ———————-└ Stack 是Vector类的实现类
└——-Set 接口: 仅接收一次,不可重复,并做内部排序
├—————-└HashSet 使用hash表(数组)存储元素
│————————└ LinkedHashSet 链表维护元素的插入次序
└ —————-TreeSet 底层实现为二叉树,元素排好序

Map 接口 键值对的集合 (双列集合)
├———Hashtable 接口实现类, 同步, 线程安全
├———HashMap 接口实现类 ,没有同步, 线程不安全-
│—————–├ LinkedHashMap 双向链表和哈希表实现
│—————–└ WeakHashMap
├ ——–TreeMap 红黑树对所有的key进行排序
└———IdentifyHashMap

时间复杂度与空间复杂度

设计模式

设计模式面试题(14道含答案)

1、单例模式

那些地方用到了单例模式

  • 网站的计数器,一般也是采用单例模式实现,否则难以同步。
  • 应用程序的日志应用,一般都是单例模式实现,只有一个实例去操作才好,否则内容不好追加显示。
  • 多线程的线程池的设计一般也是采用单例模式,因为线程池要方便对池中的线程进行控制
  • Windows的(任务管理器)就是很典型的单例模式,他不能打开俩个
  • windows的(回收站)也是典型的单例应用。整个系统运行过程中,回收站只维护一个实例。

单例创建方式(主要使用懒汉和懒汉式)

  1. 饿汉式:类初始化时,会立即加载该对象,线程天生安全,调用效率高。
  2. 懒汉式: 类初始化时,不会初始化该对象,真正需要使用时才会创建该对象,具备懒加载功能。

2、工厂模式

Spring IOC容器创建bean的过程是使用了工厂设计模式

3、代理模式

        1.什么是代理模式

                通过代理控制对象的访问,可以在这个对象调用方法之前、之后去处理/添加新的功能。

                代理在原有代码乃至原业务流程都不修改的情况下,直接在业务流程中切入新代码,增           加新功能,这也和Spring的(面向切面编程)很相似

        2.代理模式应用场景

                Spring AOP、日志打印、异常处理、事务控制、权限控制等

        3.代理的分类
                动态代理(动态生成代理类,也称为Jdk自带动态代理)
                Cglib 、javaassist(字节码操作库)

4、建造者模式

        StringBuilder就是建造者模式创建的

5、模板方法模式

        数据库访问的封装、Junit单元测试、servlet中关于doGet/doPost方法的调用等等

6、外观模式

7、策略模式

8、观察者模式

异常的类型

1、异常系列的超父类:Throwable

2、Throwable分为两大派别

(1)Error:是系统中的错误,程序员是不能改变的和处理的,一般是指与虚拟机相关的问题

(2)Exception: 一般的异常,可以通过判断、检验进行避免,或者使用try...catch进行处理

3、Exception又分为两大类

(1)运行时异常:

​    它是RuntimeException或它子类的对象。

​    这种类型的异常,编译器不会提醒你,要进行throws或try...catch进行处理,但是运行时可能导致崩溃。

(2)编译时异常:

​    异常除了运行时异常以外的都是编译时异常。

​    这种类型的异常,编译器是强制要求你,throws或try...catch进行处理,否则编译不通过。

MyBatis编程步骤是什么样的?

1. 创建SqlSessionFactory
2. 通过SqlSessionFactory创建SqlSession
3. 通过sqlsession执行数据库操作
4. 调用session.commit()提交事务
5. 调用session.close()关闭会话

Mybatis缓存

一级缓存(本地缓存)

sqlSession级别的缓存,一级缓存一直是开启的,它实质上就是sqlSession级别的一个Map。

1. 作用
        与数据库同一次会话期间查询到的数据会放在本地缓存中,以后如果需要获取相同的数据,直接从缓存中拿,没必要再去查询数据库。
2. 一级缓存失效的情况(没有使用到当前一级缓存的情况,效果就是还要再向数据库发出查询)
        sqlSession不同。
        sqlSession相同,查询条件不同(当前一级缓存中还没有这个数据)。
        sqlSession相同,两次查询之间执行了增删改操作(这次增删改可能对当前数据有影响),实际上,这个是因为每个增删改查都有标签flushCache,增删改默认为flushCache=“true”,即执行完后就清除一级缓存和二级缓存。
        sqlSession相同,手动清除了一级缓存(缓存清空,session.clearCache(),注意,该方法只清除当前session的一级缓存)。

二级缓存(全局缓存)

namespace级别的缓存,一个namespace对应一个二级缓存。

工作机制
        一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中。
        如果会话关闭或提交,一级缓存中的数据会被保存到二级缓存中,新的会话查询信息,就可以参照二级缓存中的内容。
        注意:不同的namespace查出的数据会放在自己对应的缓存(map)中。
        效果:数据会从二级缓存中取出。查出的数据都会被默认先放在一级缓存中,只有会话提交或者关闭以后,一级缓存中的数据才会转移到二级缓存中。

Mybatis标签

定义SQL语句:insert、delete、update、select
配置关联关系:collection、association
配置java对象属性与查询结果集中列名的对应关系:resultMap
控制动态SQL拼接:foreach、if、choose
格式化输出:where、set、trim
定义常量:sql
其他:include

数据库的主从复制与读写分离

主从复制

1、主服务器通过dump线程,把自己改变的数据记录到二进制日志文件中

2、从服务器通过I/O线程向主服务器请求同步,将主服务器中的二进制日志文件同步到自己的中继日志中

3、从服务器再通过SQL线程查看中继日志,并且重载中继日志实现同步

读写分离

读写分离的原理是让主数据库处理事务性增、改、删操作(INSERT、UPDATE、DELETE),而从数据库处理SELECT查询操作。数据库复制被用来把事务性操作导致的变更同步到集群中的从数据库。

分布式事务控制分段提交

参与者主要有:

1. 协调者: 四种状态: INITIAL, WAITING, DECIDED, COMPLETED

2. 参与者

两段式提交:

1. 第一阶段: 投票阶段。 协调者询问所有参与者是否准备好提交事务,如果有一个参与者投撤销票或超时无响应,则协调器通知所有参与者撤销事务。

2. 第二阶段: 决定阶段。 如果所有参与者都投票提交,那么协调者通知他们提交事务。

三段式提交:

1. 第一阶段: 投票阶段。 协调者询问所有参与者是否准备好提交事务,如果有一个参与者投撤销票或超时无响应,则协调器通知所有参与者撤销事务。

2. 第二阶段:预提交阶段。协调者在收到所有参与者的提交投票后,广播发送PRE-COMMIT消息。接受到全局预提交消息的所有参与者即知道其他所有参与者都已经投票COMMIT,此时参与者肯定提交,除非故障。

3. 第三阶段: 决定阶段。 每个收到PRE-COMMIT消息的参与者都发回收到通知,一旦协调器接收到所有通知,就发出全局提交消息。

一阶段提交:

        不像两阶段提交那样复杂,一阶段提交非常直白,就是从应用程序向数据库发出提交请求到数据库完成提交或回滚之后将结果返回给应用程序的过程。一阶段提交不需要“协调者”角色,各结点之间不存在协调操作,因此其事务执行时间比两阶段提交要短,但是提交的“危险期”是每一个事务的实际提交时间,相比于两阶段提交,一阶段提交出现在“不一致”的概率就变大了。但是我们必须注意到:只有当基础设施出现问题时(如网络中断,宕机等),一阶段提交才可能会出现“不一致”的情况,相比它的性能优势,很多团队都会选择这一方案。

zuul与gateway的区别

相同点:
        1、底层都是servlet

        2、两者均是web网关,处理的是http请求

不同点:

        1、内部实现:

                gateway对比zuul多依赖了spring-webflux,在spring的支持下,功能更强大,内部实现了限流、负载均衡等,扩展性也更强,但同时也限制了仅适合于Spring Cloud套件
          zuul则可以扩展至其他微服务框架中,其内部没有实现限流、负载均衡等。     

        2、是否支持异步
          zuul仅支持同步
          gateway支持异步。理论上gateway则更适合于提高系统吞吐量(但不一定能有更好的性能),最终性能还需要通过严密的压测来决定
        3、框架设计的角度
          gateway具有更好的扩展性,稳定性也是非常好的。

start()方法和run()方法

        主线程调用start()方法可以创建并启动新线程,新线程调用run()方法后线程才会去执行。
        创建线程时,会重写run()方法,run()方法可以理解为线程要做的任务,但是直接调用run()方法,只是main线程也就是主线程去执行的,是没有新线程产生的。
        如果要想让线程去执行run()方法里面的代码,就需要让创建线程的对象去调用start()方法,

排序算法

冒泡排序算法

(1)比较前后相邻的二个数据,如果前面数据大于后面的数据,就将这二个数据交换。
(2)这样对数组的第 0 个数据到 N-1 个数据进行一次遍历后,最大的一个数据就“沉”到数组第
N-1 个位置。
(3)N=N-1,如果 N 不为 0 就重复前面二步,否则排序完成

空间复杂度 O(1)、时间复杂度 O(n²)。

public static void bubbleSort1(int [] a, int n){ 
    int i, j;
    for(i=0; i<n; i++){//表示 n 次排序过程。
        for(j=1; j<n-i; j++){
            if(a[j-1] > a[j]){//前面的数字大于后面的数字就交换
                //交换 a[j-1]和 a[j]
                int temp; temp = a[j-1]; a[j-1] = a[j]; a[j]=temp;
            }
        }
    }
}

插入排序

插入排序不是通过交换位置而是通过比较找到合适的位置插入元素来达到排序的目的的

空间复杂度 O(1),时间复杂度 O(n²)。

public void sort(int arr[]) {
    for(int i =1; i<arr.length;i++) {
        //插入的数      
        int insertVal = arr[i];
        //被插入的位置(准备和前一个数比较)
        int index = i-1;
        //如果插入的数比被插入的数小
        while(index>=0&&insertVal<arr[index]) {
            //将把 arr[index] 向后移动               
            arr[index+1]=arr[index];
            //让 index 向前移动
            index--;
        }
        //把插入的数放入合适位置
        arr[index+1]=insertVal;
    }
}

无重复字符的最长子串

题目描述:给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。

解法

  • 定义一个哈希表存放字符及其出现的位置;
  • 定义 i, j 分别表示不重复子串的开始位置和结束位置;
  • j 向后遍历,若遇到与 [i, j] 区间内字符相同的元素,更新 i 的值,此时 [i, j] 区间内不存在重复字符,计算 res 的最大值。
class Solution {
    public int lengthOfLongestSubstring(String s) {
        int res = 0;
        Map<Character, Integer> chars = new HashMap<>();
        for (int i = 0, j = 0; j < s.length(); ++j) {
            char c = s.charAt(j);
            if (chars.containsKey(c)) {
                // chars.get(c)+1 可能比 i 还小,通过 max 函数来锁住左边界
                // e.g. 在"tmmzuxt"这个字符串中,遍历到最后一步时,最后一个字符't'和第一个
字符't'是相等的。如果没有 max 函数,i 就会回到第一个't'的索引0处的下一个位置
                i = Math.max(i, chars.get(c) + 1);
             }
         chars.put(c, j);
         res = Math.max(res, j - i + 1);
         }
    return res;
    }
}

  • 28
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值