Java面试总结篇

引用介绍

1.线程安全不安全的概念

​ 线程安全: 指多个线程在执行同一段代码的时候采用加锁机制,使每次的执行结果和单线程执行的结果都是一样的,不存在执行程序时出现意外结果。

​ 线程不安全: 是指不提供加锁机制保护,有可能出现多个线程先后更改数据造成所得到的数据是脏数据

2.结构化数据的概念

​ 结构化数据:有规律的一类数据;例如:人的信息,动物信息,考试信息
​ 非结构化数据:海量的不具备任何共同特性的数据集合;例如:网页,日志;图片

3.关系型和非关系型数据库的概念
 关系型数据库:体现不同类结构化数据之间的关系的数据,例如ORACLE mysql

非关系型数据库:存储的是非结构化的海量数据;无法体现数据的关系;例如 mongoDB red

4.什么是序列化

序列化的定义

**序列化:**把对象转化为可传输的字节序列过程称为序列化。

**反序列化:**把字节序列还原为对象的过程称为反序列化。

列化最终的目的是为了对象可以跨平台存储,和进行网络传输。而我们进行跨平台存储和网络传输的方式就是IO,而我们的IO支持的数据格式就是字节数组。

https://zhuanlan.zhihu.com/p/40462507


Java基础篇

1.Java核心思想

抽象、继承、封装、多态

2. Java语言的特点

​ ①:面向对象(使用计算机模拟现实世界)(Java最重要的特性,让程序耦合度更低,内聚性更高)

​ ②:简单易学、有丰富的类库

​ ③:跨平台(JVM是Java跨平台使用的根本)

​ ④:支持多线程

​ ⑤:安全可靠

3. Java运行机制

①:编译型

源码–>编译器–>编译–>机器码(编译一次,运行效率高)

②:解释型

​ 源码–>解释器–>逐渐解释运行(跨平台)

先编译后解释:源码–>编译–>字节码(class文件)–>解释器–>逐渐解释运行

③:Java运行环境

JVM(Java虚拟机):屏蔽底层操作系统差异

​ Java虚拟机包括一套字节码指令集、一组寄存器、一个栈、一个垃圾回收堆和一个存储方法域

JRE(Java运行环境,也叫java平台) = JVM+解释器

JDK(是程序开发者用来来编译、调试java程序用的开发工具包。JDK的工具也是Java程序,也需要JRE才能运行) = JRE+编译器+类库+工具包(Java开发工具包)

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

常见的DOS命令:

​ 切换盘符,例如 D:

​ 进入目录:cd目录名

​ 返回上一级目录:cd …

Java最小组成单位是类,使用class定义类

4. 数据类型

​ 对象类型(引用类型)(有无数种)

​ 基础类型 基础类型又分为4类,共有8种(1代表1个字节,2代表2个字节)

1、整数型 byte [1]、short[2] 、int[4] 、long[8]

2、浮点型 float [4]、 double[8]

3、字符型 char[2],存放单个字符,如’a’、‘男’,单个字母占1个字节,单个汉字占2个字节

4、布尔型 boolean[1],存放true、false

八种基本类型

类型字节大小取值范围封装器类说明
byte1字节-128~127Byte字符型,数据存储在内存中的最原始形态
short2字节-32768~32767Short短整型
int4字节-2147483648~2147483647Integer整型
long8字节-9223372036854775808~223372036854775807Long长整型
float4字节-±3.40282347E+38F(有效位数为6-7位)Float浮点型,用于存储单精度数字
double8字节±1.79769313486231570E+308(有效位数为15位)Double双精度型,用于存储双精度数字,默认声明的小数
char2字节分为两种,一种是无符号整型数据类型(unsigned char),另一种是有符号整型数据类型(signed char)Character字符型,用于存储字符,以 Unicode 编码方式
boolean1字节布尔类型只有俩个值,false和true,用来逻辑判断Boolean布尔型,用于存储真值或假值
4. 逻辑运算符

&与&&的比较

共同点:两个条件都为true,结果才是true。

不同点:

  • 短路与&&:如果第一个条件为false,则第二个条件就不会再判断,最终结果为false,效率高。(所以实际开发中&&用的较多)
  • 逻辑与&:不管第一个条件是否为false,第二个条件都要判断,效率低。

逻辑或 I” 和 “短路或 II

共同点:两个条件都为false,结果才是false。

不同点:

  • 短路或II:如果第一个条件为true,则第二个条件就不会再判断,最终结果为true。效率高。
  • 逻辑或I:不管第一个条件是否为true,第二个条件都要判断,效率低。
6. 条件分支和循环结构

​ 1. 条件分支

​ ①:if和if else

​ ②:switch

​ 2. 循环结构

​ ①:while和do while

​ ②:for循环

7. 函数

函数三要素:返回值类型,函数名,形参(函数内部有效的的局部变量,多个形参用逗号隔开)

函数是一组代码,完成特定的任务

函数的作用:

​ ①:避免冗余代码

​ ②:提高程序的可维护性

​ ③:提高程序的灵活性

​ ④:提高程序的重用性

8. 数组

数组的概念:一次性定义多个同类型的变量,而且数组空间在内存中必然是连续的;数组的长度是固定的,要扩充,必须创建新数组,把原先的数组copy到新数组中。

①:一维数组

如:

// 分配数组长度
int[] a = new int[3];
// 数组长度的显式初始化
int[] a = {1,2,3};
// 数组扩容有自带的方法
java.util.Arrays.copyOf(数组名,数组长度(如a.length*2)

②:二维数组

 // 分配数组长度
// [4]行数  [3]列数
int[][] a=new int[4][3]列数;
String[][] b=new String[4][3];
// 数组长度的显式初始化
int[][] a={{1,2,3,4},{4,5,6,7},{8,9,10,11}}
9. 面向对象内容

编程思想:需求–>建立思路–>目标代码,自顶而下,逐步求精

面向对象的关键:各司其职、弱耦合性、可扩展性

①:方法的重载(@OverLoad)与方法的覆盖(@Override)

​ 重载 @OverLoad:

​ 方法名相同,形参不同

​ 1.参数个数不同

​ 2.参数类型不同

​ 3.参数类型排列不同

​ 4.重载不一定在自身的类中,继承也可以

​ 覆盖@Override:

​ 1.参数名称相同,参数值相同

②:构造方法的特点

​ 1.没有返回值

​ 2.方法名必须和类名相同

​ 3.不允许手工调用,在对象构造过程中自动调用一次

​ 如果在一个类中没有任何构造方法,系统会默认一个无参的构造方法

③:对象创建的过程

​ 1.分配空间(所有实际变量被赋默认值)

​ 2.初始化属性(所有的实例变量被赋初始值)

​ 3.调用构造方法(实例变量常常被三次赋值)

④:类加载的时机(何时发生类加载)

​ 1.创建类的对象

​ 2.访问类的静态成员

​ 3.加载子类引发加载父类

​ 4.反向声明引用,不会发生类加载

10. 集合

Java 集合可分为 CollectionMap 两种体系

  • Collection(所有元素必须是对象类型)

    • List(元素有序有下标,元素内容可以重复)
      • ArrayList :基于数组实现的,是线程不安全的,查询快,因为是以数组为下标查询,增删慢是因为ArrayList是数组,所以在其中进行增删操作时,会对操作点之后所有数据的下标索引造成影响,需要进行数据的移动
      • Vertor:基于数组实现,线程安全,但是效率慢
      • LinkedList:基于链表实现的,线程不安全,增删快,查询慢,因为LinkedList是线性的数据存储方式,所以需要移动指针从前往后依次查找,比ArrayList 更占内存
        • 常用方法: add,remove,get,indexof,set,subList
    • Set(元素无序,无下标,元素内容不可以重复,初始容量为16,负载因子0.75倍,扩容量增加1倍
      • HashSet:储存唯一元素为空值,不保持插入顺序,线程不安全,内容不可以重复
      • LinkedHashSet:数组加链表实现,可以保证元素的获取顺序和添加的顺序一致(如果要保证自定义元素类型不重复,必须重写hashcode和equals方法)
      • TreeSet(红黑树,可以根据元素内容自动进行升序排列)有序的且没有重复元素的集合,自然排序或者根据提供的Comparator进行排序,TreeSet是基于TreeMap实现的
        • 常用方法: add,remove,contains,size,toArray
  • Map(双列数据,保存具有映射关系“key-value对”的集合),key和value都是泛型的,所以Map的key和value可以任意类型的

    • HashMap 线程不安全,key,value存储,key唯一,允许空值,但是key只能有一个空值
    • LinkdHashMap:继承自HashMap的,多线程不安全的、用于存储K、V键值对的,有序集合类,在HashMap基础上,对存储的元素节点添加了前后指针、额外维护了一个双向链表维持顺序。由于增加了前后指针,当进行Put、Get、Replace等操作时,会有维护双向链表的额外开销。(要求输入顺序和输出顺序相同,是有序的)
    • TreeMap:TreeMap是一个有序的key-value集合,它内部是通过红-黑树实现的,它支持序列化,增删改查的平均和最差时间复杂度均为O,最大特点时Key有序(实现了sortMap接口,能够把保存的记录按照键排序(默认升序),也可以指定排序比较器,遍历时得到的数据是排过序的)
    • Hashtable: Key不可为空、多线程安全、无序。底层采用数组 + 链表 的结构
11.HashMap如何用对象作为key

​ key对象的要求:

​ 1.该对象不可变

​ 2.重写equals()方法

​ 3.重写hashCode()方法

​ 使用HashMap的key作为对象的时候,不满足上述条件会导致用key取到的对象为null,地址变了,不能保证hash值和equals结果还是一样。所以取不到对应的value。

	1 两个对象要equals()==true,那么该两个对象指向的一定是同一块内存空间。
	2 HashMap在比对Map中是否存在key是,就是使用的equals方法
	3 HashMap在向Map中增加Key时,是通过hashCode()方法计算散列值,将其插入到指定的位置
	4 结合2,如果两个对象一样,所以只要对应的属性值一样。那么就可以equals等于true,不需要他们指向同一块内存空间,所以必须重写对	象的equals()方法
	5 结合hashCode的常规协定,重写了equals()方法最好重写hashCode()方法
	6 如果对象改变了,那么上述两个方法结果就改变了,所以一般key对象的不可变
12.HashMap的特点

1 :非线程安全 (注:如果需要满足线程安全,可以用 Collections的synchronizedMap方法使HashMap具有线程安全的能力,或者使用ConcurrentHashMap)

2:有很快的访问速度,但遍历顺序却是不确定的

3:HashMap最多只允许一条记录的键为null,但是允许多条记录的值为nulll

13.HashMap的实现原理
①:实现原理

1.HashMap基于hashing原理,通过put(key,value)和get(key)方法存储和获取对象。
2.当存储对象时,将键值传递给put(key,value)方法时,它调用键对象的key的hashCode()方法来计算hashCode,然后找到bucket位 置,来存储值对象value
3.当获取对象时,也是先计算key的hashCode,找到数组种对应位置的bucket位置,然后通过key的equals()方法找到正确的键值对key- value,然后返回值对象value
4.HashMap使用链表来解决碰撞问题,当发生碰撞了,对象会将存储在链表的下一个节点中。Hash每个链表节点中,存储键值对key- value对象,也就是当两个不同的键对象key
的hashcode相同时,它们会存储在同一个bucket位置的链表(JDK8链表长度大于8变红黑树)中,取数据可通过键对象key的equas()方 法来找到正确的键值对key-value

②:HashMap的底层数据结构

​ jdk1.8之前 HashMap的实现是数组+链表,它之所以查询速度快主要是因为它是通过key计算hashCode来决定一维数组中存储的位置,而增删速度靠的是链表保证
​ jdk1.8中 用数组+链表+红黑树的结构来优化,链表长度大于8同时满足HashMap中元素个数大于64则变红黑树,长度小于6变回链表

③:什么是hash表

​ 散列表(Hash Table,也叫哈希表),是根据关键码值(key,value)而直接访问的数据结构,也就是说,它通过关键码值映射到表中一个位置来访问记录,以加快查找的速度。
​ 这个映射函数叫做散列函数,存放记录的数据叫做散列表。

​ hash表里面可以存储元素的位置称为桶(bucket)

④:什么是Hash冲突

​ 不同的key产生产生相同的Hash地址
​ 解决方案:
​ 1.开放地址法:探测序列,查找一个空的单元插入。 方法有线性探测、再平方、伪随机
​ 2.链地址法:对于相同的值,使用链表进行连接。使用数组存储的每一个链表。HashMap中使用方案
​ 3.公共溢出区法:建立一个特殊的存储空间,专门存放冲突的数据。此方法适于数据和冲突较少的情况
​ 4.再散列法:准备若干个hash函数,如果使用第一个hash函数发生了冲突,就是使用第二个hash函数,依次类推

14.equals与 == 的区别

答:== 比较的是栈内存中存放对象的内存地址是否相同,equals比较的是内容是否相同,如果是比较数字的话,可以用==,比较内容用equals

15:String常用的方法
  • isEmpty()
  • equals()
  • equalsIgnoreCase()
  • hashCode()
  • indexOf()
  • substring()
  • concat()
  • replace()
  • contains()
  • split()
  • trim()
  • toString()
  • format()
  • valueOf()
public boolean isEmpty() {
    return value.length == 0;
}
public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        if (anObject instanceof String) {
            String anotherString = (String)anObject;
            int n = value.length;
            if (n == anotherString.value.length) {
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                while (n-- != 0) {
                    if (v1[i] != v2[i])
                        return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }

public boolean equalsIgnoreCase(String anotherString) {
     return (this == anotherString) ? true
           : (anotherString != null)
            && (anotherString.value.length == value.length)
             && regionMatches(true, 0, anotherString, 0, value.length);
    }
    
        //按字典顺序比较两个字符串
        //首先取出两个字符串的长度,比较较小的长度内,两者是否相等。
	//若不相等,则直接返回该位置字符的ASCII码相减后的值。
	//若各位置都相等,则将两个字符串长度的差值返回。
            public int compareTo(String anotherString) {
        int len1 = value.length;
        int len2 = anotherString.value.length;
        int lim = Math.min(len1, len2);
        char v1[] = value;
        char v2[] = anotherString.value;

        int k = 0;
        while (k < lim) {
            char c1 = v1[k];
            char c2 = v2[k];
            if (c1 != c2) {
                return c1 - c2;
            }
            k++;
        }
        return len1 - len2;
    }
public int hashCode() {
        int h = hash;
        if (h == 0 && value.length > 0) {
            char val[] = value;

            for (int i = 0; i < value.length; i++) {
                h = 31 * h + val[i];
            }
            hash = h;
        }
        return h;
    }
    public int indexOf(int ch) {
        return indexOf(ch, 0);
    }
    
    
public String substring(int beginIndex, int endIndex) {
        if (beginIndex < 0) {
            throw new StringIndexOutOfBoundsException(beginIndex);
        }
        if (endIndex > value.length) {
            throw new StringIndexOutOfBoundsException(endIndex);
        }
        int subLen = endIndex - beginIndex;
        if (subLen < 0) {
            throw new StringIndexOutOfBoundsException(subLen);
        }
        return ((beginIndex == 0) && (endIndex == value.length)) ? this
                : new String(value, beginIndex, subLen);
    }
    
 public String concat(String str) {
        if (str.isEmpty()) {
            return this;
        }
        int len = value.length;
        int otherLen = str.length();
        char buf[] = Arrays.copyOf(value, len + otherLen);
        str.getChars(buf, len);
        return new String(buf, true);
    }
public String replace(char oldChar, char newChar) {
        if (oldChar != newChar) {
            int len = value.length;
            int i = -1;
            char[] val = value; /* avoid getfield opcode */

            while (++i < len) {
                if (val[i] == oldChar) {
                    break;
                }
            }
            if (i < len) {
                char buf[] = new char[len];
                for (int j = 0; j < i; j++) {
                    buf[j] = val[j];
                }
                while (i < len) {
                    char c = val[i];
                    buf[i] = (c == oldChar) ? newChar : c;
                    i++;
                }
                return new String(buf, true);
            }
        }
        return this;
    }
public boolean contains(CharSequence s) {
        return indexOf(s.toString()) > -1;
    }
       public String replace(CharSequence target, CharSequence replacement) {
        return Pattern.compile(target.toString(), Pattern.LITERAL).matcher(
                this).replaceAll(Matcher.quoteReplacement(replacement.toString()));
    }
 public String[] split(String regex, int limit) {}
 
 
 public static String join(CharSequence delimiter, CharSequence... elements) {
        Objects.requireNonNull(delimiter);
        Objects.requireNonNull(elements);
        // Number of elements not likely worth Arrays.stream overhead.
        StringJoiner joiner = new StringJoiner(delimiter);
        for (CharSequence cs: elements) {
            joiner.add(cs);
        }
        return joiner.toString();
    }
    
public String trim() {
        int len = value.length;
        int st = 0;
        char[] val = value;    /* avoid getfield opcode */

        while ((st < len) && (val[st] <= ' ')) {
            st++;
        }
        while ((st < len) && (val[len - 1] <= ' ')) {
            len--;
        }
        return ((st > 0) || (len < value.length)) ? substring(st, len) : this;
}
public String toString() {
        return this;
    }
public static String format(String format, Object... args) {
   return new Formatter().format(format, args).toString();
}
public static String valueOf(Object obj) {
   return (obj == null) ? "null" : obj.toString();
}
16.重写和重载的区别

​ 答:重写 1.发生在父类与子类之间
​ 2.方法名,参数列表,返回类型(除过子类中方法的返回类型是父类中返回类型的子类)必须相同
​ 3.访问修饰符的限制一定要大于被重写方法的访问修饰符(public>protected>default>private)
​ 4.重写方法一定不能抛出新的检查异常或者比被重写方法申明更加宽泛的检查型异常

​ 重载: 1.重载Overload是一个类中多态性的一种表现
​ 2.重载要求同名方法的参数列表不同(参数类型,参数个数甚至是参数顺序)
3.重载的时候,返回值类型可以相同也可以不相同。无法以返回型别作为重载函数的区分标准

17抽象类和接口的区别

1.抽象类中可以有构造方法,接口中不能有构造方法

2.抽象类中可以有普通成员变量,接口中没有普通成员变量

3.抽象类中可以包含非抽象的普通方法,接口中的所有方法都必须是抽象的,不能有非抽象的普通方法

4.抽象类中可以包含静态方法,接口中不能包含静态方法

5.抽象类中的抽象方法的访问类型可以是public、protected和(默认类型default),但接口中的方法只能是public类型的,并且默认为public static final类型

18.String Stringbuffer Stringbuilder 区别

​ String是只读字符串,被final修饰,一旦定义,无法增删改,每次对String操作都会产生一个新的String对象,它是不可变的,是定长的,适用于少量字符串拼接,如果使用大量字符串拼接会造成空间浪费
​ Stringbuffer是线程安全的,被同步锁给修饰
​ StringBuild没有被同步锁修饰,是线程不安全的(适用于单线程下字符缓冲区进行大量操作的情况(单线程下线程安全,多线程下线程不安全))
​ StringBuffer和StringBuilder他们两都继承了AbstractStringBuilder抽象类

19.Collection包结构,与Collections的区别

​ Collection是集合类的上级接口,子接口有 Set、List
​ Collections是集合类的一个帮助类, 它包含有各种有关集合操作的静态多态方法,用于实现对各种
​ 集合的搜索、排序、线程安全化等操作。此类不能实例化,就像一个工具类,服务于Java的Collection框架。

20.Java创建对象有几种方式?

​ java中提供了以下四种创建对象的方式:
​ new创建新对象
​ 通过反射机制
​ 采用clone机制
​ 通过序列化机制

21.final有哪些用法

​ 被final修饰的类不可被继承
​ 被final修饰的方法不可以被重写
​ 被final修饰的变量不可以被改变.如果修饰引用,那么表示引用不可变,引用指向的内容可变.
​ 被final修饰的方法,JVM会尝试将其内联,以提高运行效率
​ 被final修饰的常量,在编译阶段会存入常量池中.

22.try catch finally,try里有return,finally还执行么?

​ 执行,并且finally的执行早于try里面的return
​ 结论:
​ 1、不管有木有出现异常,finally块中代码都会执行;
​ 2、当try和catch中有return时,finally仍然会执行;
​ 3、finally是在return后面的表达式运算后执行的(此时并没有返回运算后的值,而是先把要返回的
​ 值保存起来,不管finally中的代码怎么样,返回的值都不会改变,任然是之前保存的值),所以函数
​ 返回值是在finally执行前确定的;
​ 4、finally中最好不要包含return,否则程序会提前退出,返回值不是try或catch中保存的返回值。

23.简述线程、程序、进程的基本概念。以及他们之间关系是什么?

进程>线程

​ 线程与进程相似,但线程是一个比进程更小的执行单位。一个进程在其执行的过程中可以产生多个线程。与进程不同的是同类的多个线程共享同一块内存空间和一组系统资源,所以系统在产生一个
​ 线程,或是在各个线程之间作切换工作时,负担要比进程小得多,也正因为如此,线程也被称为轻量级进程。

程序是含有指令和数据的文件,被存储在磁盘或其他的数据存储设备中,也就是说程序是静态的码。

进程是程序的一次执行过程,是系统运行程序的基本单位,因此进程是动态的。系统运行一个程序即是一个进程从创建,运行到消亡的过程。简单来说,一个进程就是一个执行中的程序,它在计算机中一个指令接着一个指令地执行着,同时,每个进程还占有某些系统资源如 CPU 时间,内存空
间,文件,输入输出设备的使用权等等。换句话说,当程序在执行时,将会被操作系统载入内存中。 线程是进程划分成的更小的运行单位。线程和进程最大的不同在于基本上各进程是独立的,而各线程则不一定,因为同一进程中的线程极有可能会相互影响。从另一角度来说,进程属于操作系统的范畴,
主要是同一段时间内,可以同时执行一个以上的程序,而线程则是在同一程序内几乎同时执行一个以上的程序段
24.说说List,Set,Map三者的区别?

​ List(有序): 元素有序有下标,元素内容可以重复
​ Set(注重独一无二的性质): 元素无序,无下标,元素内容不可以重复,初始容量为16,负载因子0.75倍,扩容量增加1倍
​ Map(用Key来搜索的专家): 使用键值对存储。Key不能重复,典型的Key是String类型,但也可以是任何对象。

25.反射的实现方式:

​ (1)Class.forName(“类的路径”);

​ (2)类名.class

​ (3)对象名.getClass()

​ (4)基本类型的包装类,可以调用包装类的Type属性来获得该包装类的Class对象

26.请简单说一下集合

​ Java 集合可分为 CollectionMap 两种体系
​ ①:Collection有子接口List和Set,
​ List常用的实现类有ArrayList,LinkedList,Vector。
​ ArrayList:数组实现,查询快,增删慢,查询快因为是根据下标进行查询,增删慢是因为每次删除之后,会对操作点之后所有数据的下标索引造成影响,需要进行数据的移动
​ LinkedList:链表实现的,线程不安全,增删快,查询慢,因为LinkedList是线性的数据存储方式,所以需要移动指针从前往后依次查找,比ArrayList 更占内存
​ Vertor:基于数组实现,线程安全,但是效率慢
​ 常用的方法有add,remove,get,set等方法
​ ②:Map常用的实现类有HashMap、LinkdHashMap、TreeMap、HashTable常用的方法有add,remove,size,contains等方法
​ HashMap: (1)特点: 1.key-value存储形式存在的,存取是无序的,它是线程不安全的
​ 2.键和值都可以为null,但是键位置只能是一个null
​ 3.键位置是唯一的
​ 4.JDK1.8之前:数组+链表 JDK1.8之后:数组+链表+红黑树
​ 5.阈值>8且数组长度>64,才将链表转换为红黑树,目的是为了更高效的查询
​ (2)默认16: 1. 使用位与运算计算效率高

						2. 设置容量为2的幂指数,避免了hash碰撞产生链表,使得结果可以均匀分布
						3. 提高了查询效率
			LinkdHashMap:(1):继承自HashMap的,多线程不安全的、用于存储K、V键值对的,有序集合类,在HashMap基础上,对存储的元素节点添加了前后指针、额外维护了一个双向链表维持顺序。由于增加了前后指针,当进行Put、Get、Replace等操作时,会有维护双向链表的额外开销。
			使用迭代器遍历时比HM要快。使用场景是:当设定为按访问顺序存储时,适合做LRU缓存		
			TreeMap:(1):TreeMap是一个有序的key-value集合,它内部是通过红-黑树实现的,它支持序列化,增删改查的平均和最差时间复杂度均为O,最大特点时Key有序

			HashTable:(1):Key不可为空、多线程安全、无序。底层采用数组 + 链表 的结构
27.hastMap和hashtable的区别

​ 1、两者父类不同,HashMap是继承自AbstractMap类,而Hashtable是继承自Dictionary(dɪkʃənri)类。不过它们都实现了同时实现了map、Cloneable(可复制)、Serializable(可序列化)这三个接口。
​ 2、对外提供的接口不同,Hashtable比HashMap多提供了elments() 和contains() 两个方法。
​ elments() 方法继承自Hashtable的父类Dictionnary。elements() 方法用于返回此Hashtable中的value的枚举。
​ contains()方法判断该Hashtable是否包含传入的value。它的作用与containsValue()一致。事实上,contansValue() 就只是调用了一下contains() 方法。
​ 3、对null的支持不同
​ Hashtable:key和value都不能为null。
​ int[] a = new int[4];//推介使用int[] 这种方式初始化
​ int c[] = {23,43,56,78};//长度:4,索引范围:[0,3]
​ HashMap:key可以为null,但是这样的key只能有一个,因为必须保证key的唯一性;可以有多个key
​ 值对应的value为null。
​ 4、安全性不同
​ HashMap是线程不安全的,在多线程并发的环境下,可能会产生死锁等问题,因此需要开发人员自己处理多线程的安全问题。
​ Hashtable是线程安全的,它的每个方法上都有synchronized 关键字,因此可直接用于多线程中。
​ 虽然HashMap是线程不安全的,但是它的效率远远高于Hashtable,这样设计是合理的,因为大部分的
​ 使用场景都是单线程。当需要多线程操作的时候可以使用线程安全的ConcurrentHashMap。
​ ConcurrentHashMap虽然也是线程安全的,但是它的效率比Hashtable要高好多倍。因为
​ ConcurrentHashMap使用了分段锁,并不对整个数据进行锁定。
​ 5、初始容量大小和每次扩充容量大小不同
​ 6、计算hash值的方法不同

28.线程安全的map,SynchronizedMap和ConcurrentHashMap有什么区别

​ SynchronizedMap()和Hashtable一样,实现上在调用map所有方法时,都对整个map进行同步。而ConcurrentHashMap的实现却更加精细,它对map中的所有桶加了锁。所以,只要有一个线程访问
​ map,其他线程就无法进入map,而如果一个线程在访问ConcurrentHashMap某个桶时,其他线程,仍然可以对map执行某些操作。
​ 所以,ConcurrentHashMap在性能以及安全性方面,明显比Collections.synchronizedMap()更加有优势。同时,同步操作精确控制到桶,这样,即使在遍历map时,如果其他线程试图对map进行数据修改,也不会抛出ConcurrentModificationException。

29.高并发的map之ConcurrentHashMap

​ 它的数据结构和HashMap一样,采用数组+链表+红黑树,默认分为16段,每次操作都会对桶进行加锁,支持任意线程的并发读写(它使用可重入锁(ReentrantLock)进行加锁,),

30:arrayList和LinkedList的区别

​ ArrayList:数组实现,查询快,增删慢,查询快因为是根据下标进行查询,增删慢是因为每次删除之后,会对操作点之后所有数据的下标索引造成影响,需要进行数据的移动
​ LinkedList:链表实现的,线程不安全,增删快,查询慢,因为LinkedList是线性的数据存储方式,所以需要移动指针从前往后依次查找,比ArrayList 更占内存

31.都有哪几种锁
(https://blog.csdn.net/qq_39380737/article/details/105081827)

​ Lock,读写锁,重入锁(轻量级ReentrantLock和重量级synchronized),同步锁,悲观锁,乐观锁
读写锁(读-读能共存,读-写不能共存,写-写不能共存)适用场景:对一些共享资源的读和写操作,且写操作没有读操作那么频繁。在没有写操作的时候,两个线程同时读一个资源没有任何问题,所以应该允许多个线程能在同时读取共享资源。但是如果有一个线程想去写这些共享资源,就不应该再有其它线程对该资源进行读或写
乐观锁:总是认为不会产生并发问题,每次去取数据的时候总认为不会有其他线程对数据进行修改,因此不会上锁,但是在更新时会判断其他线程在这之前有没有对数据进行修改,一般会使用版本号机制或CAS操作实现。
悲观锁:总是假设最坏的情况,每次取数据时都认为其他线程会修改,所以都会加锁(读锁、写锁、行锁等),当其他线程想要访问数据时,都需要阻塞挂起。可以依靠数据库实现,如行锁、读锁和写锁等,都是在操作之前加锁,在Java中,synchronized的思想也是悲观锁

**同步锁:**具有互斥性

mysql

  • 表级锁:开销小,加锁快,不会出现死锁,锁定粒度大,发生锁冲突的概率最高,并发度最低(表锁就是一锁锁一整张表,在表被锁定期间,其他事务不能对该表进行操作,必须等当前表的锁被释放后才能进行操作。表锁响应的是非索引字段,即全表扫描,全表扫描时锁定整张表,sql语句可以通过执行计划看出扫描了多少条记录。
    处理非索引字段时通常是锁表)
  • 行级锁:开销大,加锁慢,会出现死锁,锁定粒度最小,发生锁冲突的概率最低,并发度也最高(行级锁在使用的时候并不是直接锁掉这行记录,而是锁索引
    如果一条sql用到了主键索引(mysql主键自带索引),mysql会锁住主键索引;
    如果一条sql操作了非主键索引,mysql会先锁住非主键索引,再锁定主键索引.)
  • 页面锁:开销和加锁时间界于表锁和行锁之间,会出现死锁,锁定粒度界于表锁和行锁之间,并发度一般
  • 共享锁:select * from tableName where … + lock in share more
  • 排他锁:select * from tableName where … + for update

加锁的方式:自动加锁。对于UPDATE、DELETE和INSERT语句,InnoDB会自动给涉及数据集加排他锁;对于普通SELECT语句,InnoDB不会加任何锁;当然我们也可以显示的加锁:

共享锁:select * from tableName where … + lock in share more

排他锁:select * from tableName where … + for update

32:单例 懒汉式和饿汉式的区别

​ 饿汉式天生就是线程安全的,可以直接用于多线程而不会出现问题,
​ 懒汉式本身是非线程安全的

饿汉式:

优点:

  • 饿汉式单例是最简单的一种单例形式,它没有添加任何的锁,执行效率最高
  • 线程安全

缺点:

某些情况下,造成内存浪费,因为对象未被使用的情况下就会被初始化,如果一个项目中的类多达上千个,在项目启动的时候便开始初始化可能并不是我们想要的。

public class SingletonEH {
    /**
     *饿汉式
     *是否 Lazy 初始化:否
     *是否多线程安全:是
     *实现难度:易
     *描述:这种方式比较常用,但容易产生垃圾对象。
     *优点:没有加锁,执行效率会提高。
     *缺点:类加载时就初始化,浪费内存。
     *它基于 classloder 机制避免了多线程的同步问题,
     * 不过,instance 在类装载时就实例化,虽然导致类装载的原因有很多种,
    * 在单例模式中大多数都是调用 getInstance 方法,
     * 但是也不能确定有其他的方式(或者其他的静态方法)导致类装载,
     * 这时候初始化 instance 显然没有达到 lazy loading 的效果。
     */
    private static SingletonEH instance = new SingletonEH();
    private SingletonEH (){}
    public static SingletonEH getInstance() {
        System.out.println("instance:"+instance);
        System.out.println("加载饿汉式....");
        return instance;
    }
}

懒汉式:

public class SingletonLH {
    /**
     *懒汉式
     *是否 Lazy 初始化:是
     *是否多线程安全:否
     *实现难度:易
     *描述:这种方式是最基本的实现方式,这种实现最大的问题就是不支持多线程。因为没有加锁 synchronized,所以严格意义上它并不算单例模式。
     *这种方式 lazy loading 很明显,不要求线程安全,在多线程不能正常工作。
     */
    private static SingletonLH instance;
    private SingletonLH (){}

public static SingletonLH getInstance() {
    if (instance == null) {
        instance = new SingletonLH();
    }
    return instance;
  }

}

双重检查锁

既能兼顾线程安全又能提升程序性能呢?有,这就是双重检查锁

public class SingletonLH {
    /**
     *双重检查锁
     *需要添加 volatile 关键字
     */
    // 需要添加 volatile 关键字
    private volatile static SingletonLH instance;
    private SingletonLH (){}

    public static SingletonLH getInstance() {
    //一重检查:检查实例,如果不存在,进入同步区块
    if (instance == null) {
   		 synchronized (LazyDoubleCheck.class) {
   		 //双重检查: 进入同步区块后,再检查一次,如果仍然是null,才创建实例
    		 if (instance == null) {
     			  instance = new SingletonLH();
     		}
    	}
    }
    return instance;
  }

}

https://blog.csdn.net/weixin_42208686/article/details/105078021

https://zhuanlan.zhihu.com/p/426672787

33.线程创建的几种方式

​ ①:继承Thread类创建线程类
​ ②:通过Runnable接口创建线程类
​ ③:实现Callable接口通过FutureTask包装器来创建Thread线程;
​ ④:通过线程池来创建线程

34:ThreadLocal 的作用以及适用场景以及底层实现

​ ThreadLocal比直接使用synchronized同步机制解决线程安全问题更简单,更方便,且结果程序拥有更高的并发性,
​ 订单处理包含一系列操作:减少库存量、增加一条流水台账、修改总账,这几个操作要在同一个
​ 事务中完成,通常也即同一个线程中进行处理,如果累加公司应收款的操作失败了,则应该把前面
​ 的操作回滚,否则,提交所有操作,这要求这些操作使用相同的数据库连接对象,而这些操作的代码
​ 分别位于不同的模块类中
​ 1、ThreadLocal类用于存储以线程为作用域的数据,线程之间数据隔离。

	2、ThreadLocalMap类是ThreadLocal的静态内部类,通过操作Entry来存储数据。
	3、Thread类比较常用,线程类内部维持一个ThreadLocalMap类实例(threadLocals)。
35:get和post的区别

​ ①:Get是用来从服务器上获得数据,而Post是用来向服务器上传递数据
​ ②:get拼接参数,post是封装了对象
​ ③:get请求是可以缓存的,post请求不可以缓存
​ ④:get一般传输数据大小不超过2k-4k(根据浏览器不同,限制不一样,但相差不大)post请求传输数据的大小根据php.ini 配置文件设定,也可以无限大

36:cookie session区别
1 作用范围不同,Cookie 保存在客户端(浏览器),Session 保存在服务器端。
2 存取方式的不同,Cookie 只能保存 ASCII,Session 可以存任意数据类型,一般情况下我们可以在 Session 中保持一些常用变量信息,比如说 UserId 等。
3 有效期不同,Cookie 可设置为长时间保持,比如我们经常使用的默认登录功能,Session 一般失效时间较短,客户端关闭或者 Session 超时都会失效。
4 隐私策略不同,Cookie 存储在客户端,比较容易遭到不法获取,早期有人将用户的登录名和密码存储在 Cookie 中导致信息被窃取;Session 存储在服务端,安全性相对 Cookie 要好一些。
5 存储大小不同, 单个 Cookie 保存的数据不能超过 4K,Session 可存储数据远高于 Cookie。
37:类加载过程

​ 1、加载 2、验证 3、准备 4、解析 5、 初始化 6、使用 7、卸载。

38:Thread 类中的start() 和 run() 方法有什么区别?

​ start()方法被用来启动新创建的线程,而且start()内部调用了run()方法,这和直接调用run()方法的效果
​ 不一样。当你调用run()方法的时候,只会是在原来的线程中调用,没有新的线程启动,start()方法才会
​ 启动新线程。

39:http1.1和1.0区别

​ 1.可扩展性
​ 2 缓存
​ 3 带宽优化
​ 4 长连接
​ 5 消息传递
​ 6 Host头域
​ 7 错误提示
​ 8 内容协商

40: 网络传输七层协议

​ https://blog.csdn.net/aaqingying/article/details/116853873

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

物理层 ---->数据链路层---->网络层---->传输层---->表示层 ---->应用层

41: io的用法

​ InputStreamReader:将一个字节的输入流转为字符的输入流
​ OutputStreamWriter:将一个字符的输出路转为字节的输出流

42:jdk1.8的新特性

一、 Lambda 表达式
​ 使用Lambda必须有接口,并且接口中有且仅有一个抽象方法。
​ 只有当接口中的抽象方法存在且唯一时,才可以使用Lambda,但排除接口默认方法以及声明中覆盖Object的公开方法。
​ 使用Lambda必须具有上下文推断。
​ 也就是方法的参数或局部变量类型必须为Lambda对应的接口类型,才能使用Lambda作为该接口的实例。
二、 方法引用
三、函数式接口(有且仅有一个抽象方法的接口)
​ 如:修饰符 interface 接口名称 {
​ public abstract 返回值类型 方法名称(可选参数信息);
// 其他非抽象方法内容
​ }
四、 接口允许定义默认方法和静态方法
五、Stream API
六、日期/时间类改进
七、Optional 类
八、 Java8 Base64 实现

43:hashMap的实现原理:

​ 数组+链表实现的
​ 主干是一个Entry数组,Entry是hashMap的基本组成单元,每一个Entry包含一个key-value键值对

44:juc(countdownlatch)并发包

​ CountDownLatch是JDK提供的一个同步工具,它可以让一个或多个线程等待,一直等到其他线程中执行完成一组操作。可以用作线程同步
​ 方法有countDown方法、await方法等

45:单元测试中的断言

​ 作用:是用于对程序进行调试的,对于执行结构的判断,而不是对于业务流程的判断。(相当于一个if ()语句,如果满足断言的执行程序,如果不满足则抛错误)
​ 使用情形:断言只适用复杂的调式过程。(如果不复杂完全可以用log或者debug代替)

46:时间复杂度:
常见复杂度量级
分类记作
常量阶O(1)
对数阶O(logn)
线性阶O(n)
线性对数阶O(nlogn)
n方阶O(nⁿ)
指数阶O(2ⁿ)
阶乘阶O(n!)
https://blog.csdn.net/qq_41297896/article/details/104223612
47:TCP的三次握手

​ TCP三次握手是浏览器和服务器建立连接的方式,目的是为了使二者能够建立连接,便于后续的数据交互传输。
​ 第一次握手:浏览器向服务器发起建立连接的请求
​ 第二次握手:服务器告诉浏览器,我同意你的连接请求,同时我也向你发起建立连接的请求
​ 第三次握手:浏览器也告诉服务器,我同意建立连接。
​ 至此,双方都知道对方同意建立连接,并准备好了进行数据传输,也知道对方知道自己的情况。接下来就可以传输数据了

48:java中的nio

​ NIO (New lO)也有人称之为java non-blocking lO是从Java 1.4版本开始引入的一个新的IO API,可以替代标准的Java lO API。NIO与原来的IO有同样的作用和目的,但是使用的方式完全不同,NIO支持面向缓冲区的、基于通道的IO操作。NIO将以更加高效的方式进行文件的读写操作。NIO可以理解为非阻塞IO,传统的IO的read和write只能阻塞执行,线程在读写IO期间不能干其他事情,比如调用socket.read()时,如果服务器一直没有数据传输过来,线程就一直阻塞,而NIO中可以配置socket为非阻塞模式。

NIO相关类都被放在java.nio包及子包下,并且对原java.io包中的很多类进行改写。
NIO有三大核心部分:Channel(通道),Buffer(缓冲区), Selector(选择器)
Java NlO的非阻塞模式,使一个线程从某通道发送请求或者读取数据,但是它仅能得到目前可用的数据,如果目前没有数据可用时,就什么都不会获取,而不是保持线程阻塞,所以直至数据变的可以读取之前,该线程可以继续做其他的事情。非阻塞写也是如此,一个线程请求写入一些数据到某通道,但不需要等待它完全写入,这个线程同时可以去做别的事情。
通俗理解:NIO是可以做到用一个线程来处理多个操作的。假设有1000个请求过来,根据实际情况,可以分配20或者80个线程来处理。不像之前的阻塞IO那样,非得分配1000个。
49:HashSet的底层原理

底层是用hashMap 实现的

HashSet底层使用了哈希表来支持的,特点:存储快
往HashSet添加元素的时候,HashSet会先调用元素的HashCode方法得到元素的哈希值,然后通过元素的哈希值经过异或移位等运算,就可以算出该元素在哈希表中的存储位置。

50.https协议

HTTP协议(超文本传输协议)是一种网络通信协议,它允许将超文本标记语言(HTML)文档从Web服务器传送到客户端的浏览器。默认端口:80

1、支持客户/服务器模式。

2、简单快速:客户向服务器请求服务时,只需传送请求方法和路径。请求方法常用的有GET、POST。每种方法规定了客户与服务器联系的类型不同。由于HTTP协议简单,使得HTTP服务器的程序规模小,因而通信速度很快。

3、灵活:HTTP允许传输任意类型的数据对象。正在传输的类型由Content-Type加以标记。

4、无连接:无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。

5、无状态:HTTP协议是无状态协议。无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。

HTTPS协议的主要特点:

主要目的是提供对网站服务器的身份认证,同时保护交换数据的隐私与完整性。默认端口:443

1、内容加密:采用混合加密技术,中间者无法直接查看明文内容

2、验证身份:通过证书认证客户端访问的是自己的服务器

3、保护数据完整性:防止传输的内容被中间人冒充或者篡改

4、SSL证书需要购买申请,功能越强大的证书费用越高

5、SSL证书通常需要绑定IP,不能在同一IP上绑定多个域名,IPv4资源不可能支撑这个消耗

6、HTTPS连接缓存不如HTTP高效,流量成本高

7、HTTPS协议握手阶段比较费时,对网站的响应速度有影响,影响用户体验。

https://www.cnblogs.com/YouJeffrey/p/15334068.html

HTTP协议的主要特点:

1、支持客户/服务器模式。

2、简单快速:客户向服务器请求服务时,只需传送请求方法和路径。请求方法常用的有GET、POST。每种方法规定了客户与服务器联系的类型不同。由于HTTP协议简单,使得HTTP服务器的程序规模小,因而通信速度很快。

3、灵活:HTTP允许传输任意类型的数据对象。正在传输的类型由Content-Type加以标记。

4、无连接:无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。

5、无状态:HTTP协议是无状态协议。无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。

51:freemarker 使用场景

FreeMarker是一款模板引擎: 即一种基于模板和要改变的数据, 并用来生成输出文本(HTML网页、电子邮件配置文件源代码等)的通用工具。 它不是面向最终用户的,而是一个Java类库,是一款程序员可以嵌入他们所开发产品的组件。

按照官方的说法,使用FreeMarker实现逻辑和视图分离,

FreeMarker不同于JSP的地方。

FreeMarker支持模板,可以在不改变业务逻辑的情况下动态调整视图。

是的,JSP确实也能做到模板化。但是JSP本身不支持模板化,如果要做到FreeMarker的模板功能,恐怕要有很多开发工作量。

可能又有人会说:如果用户要改界面,多写几套JSP代码供用户选择不就完了吗?

是,这样确实也能一定程度上支持模板化。但是这种模板必须要写代码、编译,然后发布版本。而FreeMarker的很重要的一点是定义一个新的视图,不需要写代码,不需要发版本,只需要定义一套新的模板即可。

所以总结下来,FreeMarker在类似于淘宝店的商品展示场景下使用,会比JSP更方便。

因为用户要把产品放到淘宝店的展台上,展示的商品会经常发生变化,有时候要对UI做一些美化、内容展示位置的调整等等。在这种情况下,只需要使用一个新的模板,就可以把UI进行改变。而无需对淘宝的平台代码进行修改。

这种情况下,用JSP就会没有多少优势了。

52.TCP协议

主要特点: 1.三次握手(建立可靠链接) 2.确认机制(应答接受) 3.端口号(多路复用) 4.序列号(丢失检测、乱序重排)5.完整性校验(差错检测) 6.窗口机制(流量控制)

https://blog.csdn.net/c_study__c/article/details/128961381

53.单线程为什么快

1.减少上下文开销
2.没有其他竞争资源

54:什么是事务

用户定义的一个操作序列

https://blog.csdn.net/m0_73878473/article/details/127439889

55:浮点型精度丢失

浮点十进制值通常没有完全相同的二进制表示形式。 这是 CPU 所采用的浮点数据表示形式的副作用。 因此,你可能会遇到一些精度丢失,并且某些浮点运算可能会产生意外的结果。

导致此行为的原因是下面之一:

  • 十进制数的二进制表示形式可能不精确。
  • 使用的数字之间存在类型不匹配(例如,混合使用浮点和双精度)。

为解决此行为,大多数程序员要么确保值大于或小于所需的值,要么获取并使用可以维护精度的二进制编码的十进制 (BCD) 库

https://www.cnblogs.com/straybirds/p/6295036.html

56.自定义注解是什么时候执行的

自定义注释注解其实就是一种标记,可以在程序代码中的关键节点(类、方法、变量、参数、包)上打上这些标记,然后程序在编译时或运行时可以检测到这些标记从而执行一些特殊操作

57.mysql的死锁问题

https://blog.csdn.net/qq_37436172/article/details/128067682

多线程&并发篇

1.多线程的创建方式

​ ①:继承Thread类创建线程类
​ ②:通过Runnable接口创建线程类
​ ③:实现Callable接口通过FutureTask包装器来创建Thread线程;
​ (1)创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,并且有返回值。
​ (2)创建Callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返 回值。
​ (3)使用FutureTask对象作为Thread对象的target创建并启动新线程。
​ (4)调用FutureTask对象的get()方法来获得子线程执行结束后的返回值
​ ④:使用ExecutorService、Callable、Future实现有返回结果的多线程(也就是使用了ExecutorService来管理前面的三种方式)。

2.线程池共同操作同一个对象的,如何解决

示例:

三个窗口同时出售20张票。

程序分析:

1、票数要使用一个静态的值。

2、为保证不会出现卖出同一张票,要使用同步锁。

3、设计思路:创建一个站台类Station,继承THread,重写run方法,在run方法内部执行售票操作!

售票要使用同步锁:即有一个站台卖这张票时,其他站台要等待这张票卖完才能继续卖票!

package com.multi_thread; //站台类
public class Station extends Thread {
    // 通过构造方法给线程名字赋值
    public Station(String name) {
        super(name);// 给线程起名字
    }     // 为了保持票数的一直,票数要静态
    static int tick = 20;
    // 创建一个静态钥匙
    static Object ob = "aa";// 值是任意的     
    @Override
    public void run() {
        while (tick > 0) {
            // 这个很重要,必须使用一个锁,进去的人会把钥匙拿在手上,出来后把钥匙让出来
            synchronized (ob) {
                if (tick > 0) {
                    System.out.println(getName() + "卖出了第" + tick + "张票");
                    tick--;
                } else {
                    System.out.println("票卖完了");
                }
            }
            try {
                // 休息一秒钟
                sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    } }

package com.multi_thread; 
public class MainClass {
    // java多线程同步所的使用
    // 三个售票窗口同时出售10张票
    public static void main(String[] args) {
        // 实例化站台对象,并为每一个站台取名字
        Station station1 = new Station("窗口1");
        Station station2 = new Station("窗口2");
        Station station3 = new Station("窗口3");
        // 让每一个站台对象各自开始工作
        station1.start();
        station2.start();
        station3.start();
    }
}

程序运行结果:

窗口1卖出了第20张票
窗口3卖出了第19张票
窗口2卖出了第18张票
窗口2卖出了第17张票
窗口3卖出了第16张票
窗口1卖出了第15张票
窗口1卖出了第14张票
窗口3卖出了第13张票
窗口2卖出了第12张票
窗口1卖出了第11张票
窗口3卖出了第10张票
窗口2卖出了第9张票
窗口1卖出了第8张票
窗口3卖出了第7张票
窗口2卖出了第6张票
窗口1卖出了第5张票
窗口3卖出了第4张票
窗口2卖出了第3张票
窗口3卖出了第2张票
窗口1卖出了第1张票

https://www.shuzhiduo.com/A/QW5YQP3e5m/

3.分布式锁有哪几种

​ ①:基于数据库实现的乐观锁 version

​ ②:基于redis实现的分布式锁

​ https://blog.csdn.net/weixin_46129192/article/details/126010250

​ ③:基于zookeeper实现的分布式锁

​ https://blog.csdn.net/weixin_52851967/article/details/127575486

4.为什么使用线程池

​ 线程池是一种线程使用模式。线程过多会带来额外的开销,其中包括创建销毁线程的开销、调度线程的开销等等,同时也降低了计算机的整体性能。线程池维护多个线程,等待监督管理者分配可并发执行的任务。这种做法,一方面避免了处理任务时创建销毁线程开销的代价,另一方面避免了线程数量膨胀导致的过分调度问题,保证了对内核的充分利用。
使用线程池,有几点好处:

  • 降低资源消耗:通过池化技术重复利用已创建的线程,降低线程创建和销毁造成的损耗。
  • 提高响应速度:任务到达时,无需等待线程创建即可立即执行。
  • 提高线程的可管理性:线程是稀缺资源,如果无限制创建,不仅会消耗系统资源,还会因为线程的不合理分布导致资源调度失衡,降低系统的稳定性。使用线程池可以进行统一的分配、调优和监控。

线程池参数(例ThreadPoolTaskExecutor)

corePoolSize(核心线程数)、

maxPoolSize(最大线程数)、

keepAliveSeconds(线程空闲时长)

queueCapacity(任务队列容量)

threadNamePrefix (线程名称前缀)

5.线程的生命周期

线程的生命周期包含5个阶段,包括:新建、就绪、运行、阻塞、销毁。

  • 新建:就是刚使用new方法,new出来的线程;
  • 就绪:就是调用的线程的start()方法后,这时候线程处于等待CPU分配资源阶段,谁先抢的CPU资源,谁开始执行;
  • 运行:当就绪的线程被调度并获得CPU资源时,便进入运行状态,run方法定义了线程的操作和功能;
  • 阻塞:在运行状态的时候,可能因为某些原因导致运行状态的线程变成了阻塞状态,比如sleep()、wait()之后线程就处于了阻塞状态,这个时候需要其他机制将处于阻塞状态的线程唤醒,比如调用notify或者notifyAll()方法。唤醒的线程不会立刻执行run方法,它们要再次等待CPU分配资源进入运行状态;
  • 销毁:如果线程正常执行完毕后或线程被提前强制性的终止或出现异常导致结束,那么线程就要被销毁,释放资源;

完整的生命周期图如下:

img

Spring&&SpringMvc篇

1.对spring的理解

​ Spring是一个轻量级的IoC/DI和AOP容器的开源框架,
​ 优点: 1.Spring 能帮我们低侵入/低耦合地根据配置文件创建及组装对象之间的依赖关系 //解耦
​ 2.Spring 面向切面编程能帮助我们无耦合的实现日志记录,性能统计,安全控制等 //提高代码重用性
​ 3.Spring 能非常简单的且强大的声明式事务管理 //使用xml或注解的方法,底层用到动态代理
​ 4.Spring 提供了与第三方数据访问框架(如JPA)无缝集成,且自己也提供了一套JDBC模板来方便数据库访问
​ 5.Spring 提供与第三方Web框架无缝集成,且自己也提供了一套 SpringMVC 框架,来方便 Web 层搭建
​ ①.1spring底层用到了哪几种设计模式
​ 1.工厂设计模式 : Spring使用工厂模式通过 BeanFactory 、ApplicationContext 创建 bean 对象
​ 2.代理设计模式 : Spring AOP 功能的实现
​ 3.单例设计模式 : Spring 中的 Bean 默认都是单例的
​ 4.模板方法模式 : Spring 中 jdbcTemplate 、hibernateTemplate 等以Template 结尾的对数据库操作类,
​ 它们就使用到了模板模式
​ 5.包装器设计模式 : 我们的项目需要连接多个数据库,而且不同的客户在每次访问中根据需要会去访问不同的数据库,
​ 这种模式让我们可以根据客户的需求能够动态切换不同的数据源。
​ 6.观察者模式: Spring 事件驱动模型就是观察者模式很经典的一个应用。
​ 7.适配器模式:Spring AOP的增强或通知(Advice)使用到了适配器模式,spring MVC中也是用到了适配器模式适配 Controller

缺点:

  • 使用了大量的反射机制,比较占内存,而且效率不如直接调用高

  • Spring包含很多的组件,使用的组件越多,Spring的启动就会越慢

  • 配置比较复杂,不过后来引入了注解配置,简化了配置过程

  • 没有做到依赖管理,比如控 制各依赖之间的版本兼容关系,非常麻烦。后续SpringBoot这一点做得很不错。

  • 启动Spring的IOC容器,需要依赖第三方的Web服务器,自身不能启动

  • 使用Spring做出来的程序,逻辑不太清晰,代码不直观,需要时不时去查看配置才能理解代码

  • 调试阶段不直观,不容易定位到Bug的问题所在
    原文链接:https://blog.csdn.net/qq_17300595/article/details/123678241

2.spring和springboot的区别

​ · 1、Spring Boot提供极其快速和简化的操作,让 Spring 开发者快速上手

2、Spring Boot提供了 Spring 运行的默认配置。

3、Spring Boot为通用 Spring项目提供了很多非功能性特性,例如:嵌入式 Serve、Security、统计、健康检查、外部配置等等。

3.事务的隔离级别

一、五大事务隔离级别

  • 1、DEAULT(默认属性),这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别。另外四个与JDBC的隔离级别相对应。

  • 2、READ_UNCOMMITTED (读未提交),这是事务最低的隔离级别,它允许另外一个事务可以看到这个事务未提交的数据。这种隔离级别会产生脏读,不可重复读和幻像读,是不使用的。

  • 3、READ_COMMITTED (读已提交),保证一个事务修改的数据提交后才能被另外一个事务读取,另外一个事务不能读取该事务未提交的数据。这种事务隔离级别可以避免脏读出现,但是可能会出现不可重复读和幻像读。默认的事务隔离级别。

  • 4、REPATEBLE_READ(可重复读),这种事务隔离级别可以防止脏读、不可重复读,但是可能出现幻像读。它除了保证一个事务不能读取另一个事务未提交的数据外,还保证了不可重复读。

  • 说一下什么是不可重复读。比如一个事务中两次查询一条数据,中间有另一个事务修改了数据,就出现了,不可重复读的情况。

  • 5、 SERIALIZABLE(串行化),这是花费最高代价但是最可靠的事务隔离级别,事务被处理为顺序执行。除了防止脏读、不可重复读外,还避免了幻像读。一般不使用,效率太低。

    说一下什么是幻读。比如,比如一个事务中两次查询所有数据,中间有另一个事务插入了一条数据,这样导致出现了幻读的情况。

4.事务的七大传播机制与五大隔离级别

REQUIRED(0), //使用当前事务,如果当前没有事务,则自己新建一个事务,子方法是必须运行在一个事务中的,如果当前存在事务,则加入这个事务,成为一个整体(多用于增,删,改,什么都不写的时候是默认是它)
SUPPORTS(1),//如果当前有事务,则使用事务,如果当前没有事务,则不使用事务
mandatory(2)(英文发音:mændətəri),//该属性强制必须存在一个事务,如果当前没有事务,就抛出异常
REQUIRES_NEW(3),//如果当前有事务,则挂起该事务,并且自己创建一个新的事务给自己使用;如果没有事务,同REQUIRED
NOT_SUPPORTED(4),//以非事务方式执行操作,如果当前存在事务,就把当前事务挂起
never(5),//如果当前存在事务,则抛出异常
nested(6)(英文发音:nestɪd);/如果当前有事务,则开启子事务(嵌套事务),嵌套事务是独立提交或者 回滚;如果当前没有事务,则同REQUIRED,但是如果主事务提交,则会携带子事务一 起提交。

​ 如果主事务回滚,则子事务一起回滚,相反,子事务异常,则父事务可以混滚或者不回滚。*/

5.springmvc的运行原理

1、 用户发送请求至前端控制器DispatcherServlet。
2、 DispatcherServlet收到请求调用HandlerMapping处理器映射器。
3、 处理器映射器找到具体的处理器(可以根据xml配置、注解进行查找),生成处理器对象及处理器拦截
器(如果有则生成)一并返回给DispatcherServlet。
4、 DispatcherServlet调用HandlerAdapter处理器适配器。
5、 HandlerAdapter经过适配调用具体的处理器(Controller,也叫后端控制器)。
6、 Controller执行完成返回ModelAndView。
7、 HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet。
8、 DispatcherServlet将ModelAndView传给ViewReslover视图解析器。
9、 ViewReslover解析后返回具体View。
10、DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)。
11、 DispatcherServlet响应用户。

6.SpingIOC/DI的底层实现原理

​ springIoc:将对象创建和属性赋值的权利交给spring工厂来管理,实现解耦合的效果,以及集中管理的效果
​ 底层原理是:工厂设计模式

​ SpringAop:面向切面编程。在不修改目标代码的情况下,为目标类增强额外的功能,是功能代码解耦合的效果
​ 应用场景:事务控制,日志,性能监控,权限
​ 符合AOP思想的技术:SpringAOP, Filter,拦截器
​ 静态代理
​ 1. 优点: 事务功能代码的解耦和.
​ 2. .缺点: 产生大量的代码冗余
​ 底层原理:JDK动态代理(默认):①:基于接口生成的
​ ②:不足:如果目标对象没有接口,则无法通过JDK动态代理生成代理类
​ 实现目标接口
​ ②:实现目标接口方法
​ ③:开启事务
​ ④:调用目标对象的目标方法
​ 基于继承的动态代理:cglib:实现思路①:目标对象
​ ②:增强功能
​ ③:将目标类作为父类
​ ④:组合增强功能和目标对象

7.Springmvc的执行流程:


​ 1、用户向服务器发送请求,请求被SpringMVC的前端控制器DispatcherServlet(分发器)截获。
​ ​ 2、DispatcherServlet对请求的URL(统一资源定位符)进行解析,得到URI(请求资源标识符),然后根据该URI,调用HandlerMapping获得该Handler配置的所有相关的对象,包括Handler(处理者)对象以及Handler对象对应的拦截器,这些对象都会被封装到一个HandlerExecutionChain对象当中返回。
​ ​ 3、DispatcherServlet根据获得的Handler,选择一个合适的HandlerAdapter。HandlerAdapter的设计符合面向对象中的单一职责原则,代码结构清晰,便于维护,最为重要的是,代码的可复制性高。HandlerAdapter会被用于处理多种Handler,调用Handler实际处理请求的方法。
​ ​ 4、提取请求中的模型数据,开始执行Handler(Controller)。
​ ​ //在填充Handler的入参过程中,根据配置,spring将帮助做一些额外的工作
​ ​
​ ​ 消息转换:将请求的消息,如json、xml等数据转换成一个对象,将对象转换为指定的响应信息。
​ ​
​ ​ 数据转换:对请求消息进行数据转换,如String转换成Integer、Double等。
​ ​
​ ​ 数据格式化:对请求的消息进行数据格式化,如将字符串转换为格式化数字或格式化日期等。
​ ​
​ ​ 数据验证:验证数据的有效性如长度、格式等,验证结果存储到BindingResult或Error中。
​ ​
​ ​ 5、Handler执行完成后,向DispatcherServlet返回一个ModelAndView对象,ModelAndView对象中应该包含视图名或视图模型。
​ ​ 6、根据返回的ModelAndView对象,选择一个合适的ViewResolver(视图解析器)返回给DispatcherServlet。
​ ​ 7、ViewResolver结合Model和View来渲染视图。
​ ​ 8、将视图渲染结果返回给客户端。
​ ​ 以上8个步骤,DispatcherServlet、HandlerMapping、HandlerAdapter和ViewResolver等对象协同工作,完成SpringMVC请求—>响应的整个工作流程,这些对象完成的工作对于开发者来说都是不可见的,开发者并不需要关心这些对象是如何工作的,开发者,只需要在Handler(Controller)当中完成对请求的业务处理。

8.工厂模式、简单工厂模式、抽象工厂模式三者的区别

​ 1、创建对象不同。创建对象时,“工厂模式”使用Factory模式替代使用new创建对象;“简单工厂模式”使用fw模式建立对象;“抽象工厂模式”则使用迭代模式创建对象。
​ 2、定义变量不同。“工厂模式”不用事先定义变量,使用时随时引用便可。“简单工厂模式”,使用参数或者配置文件等事先定义好的变量,然后利用分支判断初始化具体产品类并返回。“抽象工厂模式则”不符合“开发-封闭”原则,每次增加产品,都需要修改类方法。
​ 3、接口数量不同。“工厂模式”有两至三个接口。“简单工厂模式”只有一个接口。抽象工厂模式理论上具有无限个接口。

9.Spring循环依赖如何解决

spring是通过缓存来解决循环依赖的问题的

1.一级缓存,用于存放完全初始化好的bean

2.二级缓存,存放原始的bean对象(尚未填充属性),用于解决循环依赖

3.三级缓存,存放bean工厂对象,用于解决循环依赖

10.Spring框架的七大模块
  1. Spring Core
    框架的最基础部分,提供了核心类库以及 IoC 容器,对 bean 进行管理。
  2. Spring Context
  3. 基于 bean,提供上下文信息,扩展出JNDI、EJB、电子邮件、国际化、校验和调度等功能。(Context模块提供框架式的Bean访问方式,其他程序可以通过Context访问Spring的Bean资源,相当于资源注入)
  4. Spring DAO
    提供了JDBC的抽象层,它可消除冗长的JDBC编码和解析数据库厂商特有的错误代码,还提供了声明性事务管理方法。(Spring 提供对JDBC的支持,对JDBC进行封装,允许JDBC使用Spring资源,并能统一管理JDBC事物,并不对JDBC进行实现。(执行sql语句))
  5. Spring ORM
    提供了常用的“对象/关系”映射APIs的集成层。 其中包括JPA、JDO、Hibernate、MyBatis 等。(Spring 的ORM模块提供对常用的ORM框架的管理和辅助支持,Spring支持常用的Hibernate,ibtas,jdao等框架的支持,Spring本身并不对ORM进行实现,仅对常见的ORM框架进行封装,并对其进行管理)
  6. Spring AOP
    提供了符合AOP Alliance规范的面向方面的编程实现。(AOP模块是Spring的AOP库,提供了AOP(拦截器)机制,并提供常用的拦截器,供用户自定义和配置。)
  7. Spring Web
    提供了基础的 Web 开发的上下文信息,可与其他 web 进行集成。
  8. Spring Web MVC
    提供了 Web 应用的 Model-View-Controller 全功能实现。
11.Spring 组件
  1. core 层 – Core Container :

基础层,核心功能 Bean–Context管理,由这一层提供,是Spring项目的基础依赖

spring-core: Spring核心包,包含 IOC 和 DI 。
spring-beans: Bean包,核心是 BeanFactory,通过 IOC 将Bean的依赖与实际配置分离,实现Bean的动态注入。
spring-context:相当于注册表,维护了Bean的上下文信息,可以理解为Spring的IOC容器。
ApplicationContext是上下文的Base接口。通过分析的父类,看看它具有哪些功能

public interface ApplicationContext extends 
EnvironmentCapable,        //读取环境信息
ListableBeanFactory,       //读取当前容器Bean信息
HierarchicalBeanFactory,   //读取父容器的Bean
MessageSource,             //国际化接口
ApplicationEventPublisher, //事件发布接口
ResourcePatternResolver    //资源解析
 {
    @Nullable
    String getId();

String getApplicationName();

String getDisplayName();

long getStartupDate();

@Nullable
ApplicationContext getParent();

AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException;

}

spring-context-support提供将第三方库集成到 Spring-context 的支持,包括缓存,邮件,调度 和 模板引擎,如下图:

spring-expression: EL支持,运行时灵活查询和操作对象

  1. ext 层:

拓展层,提供额外的框架支持,按需选择引入

spring-aop:基于动态代理的切面编程,通过定义切入点和切面,进行切面管理。
spring-aspects:与 AspectJ 集成,支持编译时织入、编译后织入、加载时织入。

  1. DA 层 :

spring-jdbc:JDBC的封装,简化jdbc操作。

spring-tx:基于AOP的事务支持。

spring-orm: Object/Relational mapping API 。

spring-oxm:Object/XML mapping API。

spring-jms:Java Messaging Service Java消息服务API 。

  1. WEB 层:

spring-web:web应用集成包,提供基本的 web 远程服务支持

spring-webmvc:也称 Web-Servlet ,基于spring-web,是对 Servlet的增强

  1. Test :

spring-test: 支持Spring组件与JUnit或TestNG的单元测试和集成测试。它提供了Spring 的ApplicationContext 缓存,和可用于单独测试代码的模拟对象。

Mybatis篇

1.mybatis的注解有哪些
	@Mapper @Param @Repository
2.mybatis的缓存

https://blog.csdn.net/Progran_ape/article/details/104181926

​ Mybatis的一级缓存是基于sqlSession实现的,是系统默认的

​ 1、不需要配置,默认开启。

​ 2、无法管理一级缓存

​ 3、不得不用,无法剔除

​ Mybatis的二级缓存机制是基于key,value形式存储的,key就是namespace,value中有个map,map中以namespace作为key,查询到的结果作为结果

​ 1、查出的数据首先放在一级缓存中,只有一级缓存被关闭或者提交以后,一级缓存数据才会转移到二级缓存

​ 2、可以关闭

​ 3、基本上都是关闭的,被redis等其他缓存所替代

二级缓存实现:https://blog.csdn.net/qq_59025975/article/details/121929522

3.#{} 和 ${} 的区别是什么?

​ 两者在 MyBatis 中都可以作为 SQL 的参数占位符,在处理方式上不同
​ #{}:在解析 SQL 的时候会将其替换成 ? 占位符,然后通过 JDBC 的 PreparedStatement 对象添加参数值,这里会进行预编译处理,可以有效地防止 SQL 注入,提高系统的安全性
​ ${}:在 MyBatis 中带有该占位符的 SQL 片段会被解析成动态 SQL 语句,根据入参直接替换掉这个值,然后执行数据库相关操作,存在 SQL注入 的安全性问题

SpringBoot篇

1.springboot的优点

优点:一、独立运行
Spring Boot而且内嵌了各种servlet容器,Tomcat、Jetty等,现在不再需要打成war包部署到容器中,
Spring Boot只要打成一个可执行的jar包就能独立运行,所有的依赖包都在一个jar包内。
二、简化配置
spring-boot-starter-web启动器自动依赖其他组件,简少了maven的配置。
三、自动配置
Spring Boot能根据当前类路径下的类、jar包来自动配置bean,如添加一个spring-boot-starter-web启
动器就能拥有web的功能,无需其他配置。
四、无代码生成和XML配置
Spring Boot配置过程中无代码生成,也无需XML配置文件就能完成所有配置工作,这一切都是借助于
条件注解完成的,这也是Spring4.x的核心功能之一。
五、应用监控
Spring Boot提供一系列端点可以监控服务及应用,做健康检测。

2、运行Spring Boot有哪几种方式?

​ 1.打包用命令或者放到容器中运行
​ 2.用 Maven/Gradle 插件运行
​ 3.直接执行 main 方法运行
​ 三、Spring Boot 的核心注解是哪个?它主要由哪几个注解组成的?
​ 启动类上面的注解是@SpringBootApplication,它也是 Spring Boot 的核心注解,主要组合包含了以下
​ 3 个注解:
​ @SpringBootConfiguration:组合了 @Configuration 注解,实现配置文件的功能。
​ @EnableAutoConfiguration:打开自动配置的功能,也可以关闭某个自动配置的选项,如关闭数据源
​ 自动配置功能: @SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })。

3.SpringBoot的三大核心注解

Spring Boot框架本质上就是通过组合注解的方式实现了诸多Spring注解的组合**,从而极大地简化了Spring框架本身的繁琐配置,实现快速的集成和开发。

@Configuration:
提到@Configuration就要提到他的搭档@Bean。使用这两个注解就可以创建一个简单的spring配置类,可以用来替代相应的xml配置文件。 这个注解类标识这个类可以使用Spring IoC容器作为bean定义的来源。@Bean注解告诉Spring,一个带有@Bean的注解方法将返回一个对象,该对象应该被注册为 在Spring应用程序上下文中的bean。

@EnableAutoConfiguration:
能够自动配置spring的上下文,试图猜测和配置你想要的bean类,通常会自动根据你的类路径和你的bean定义自动配置。

@ComponentScan:
会自动扫描指定包下的全部标有@Component的类,并注册成bean,当然包括@Component下的子注解@Service,@Repository,@Controller。
https://blog.csdn.net/u010797364/article/details/123555101

分布式篇(springcloud和dubbo以及ZooKeeper)

1.yml的优先级

​ 当properties、yaml和yml三种文件路径相同时,三个文件中的配置信息都会生效,但是当三个文件中有配置信息冲突时,加载顺序是:

yml > yaml > properties
bootstrap.yml跟application.yml加载顺序
  • bootstrap.yml(bootstrap.properties)用来在程序引导时执行,应用于更加早期配置信息读取,如可以使用来配置application.yml中使用到参数等
  • application.yml(application.properties) 应用程序特有配置信息,可以用来配置后续各个模块中需使用的公共参数等。

bootstrap.yml 先于 application.yml 加载

2.SpringCloud微服务的拆分粒度

​ 原则1:划分为<10个微服务模块
​ 一个传统的单体应用,在进行划分的时候最好不要超过10个微服务模块。注意这里指的业务功能模块不包括底层的系统管理,流程引擎等技术模块。业务功能流程之间基本没有任何耦合性的多拆分也不影响
​ 原则2:强数据关联模块不要拆分
​ 简单来说就是有些系统基本就是围绕一个核心数据或业务对象展开的管理系统,比如我们说的资源管理系统,资产管理系统等。
​ 原则3:以数据聚合驱动业务功能聚合
​ 这个理解起来困难,简单来说就是微服务划分不仅仅是上层业务功能模块划分,而且包括了数据库的拆分,因此在划分微服务的时候首先要考虑数据域的拆分,基于数据和功能的CRUD分析来考虑数据的聚合关联要求。先拆分好数据域,再来考虑数据域里面有哪些业务功能。
​ 原则4:从纵向功能划分思路到横向分层思路转变
​ 在微服务划分里面,同样需要结合SOA的横向分层思想,即:
​ 传统的单体应用你会发现在进行微服务划分的时候,本身微服务也体现出分层属性,即有些属于底层的业务对象,实体,数据提供类微服务;有些数据业务功能和规则类微服务;而还有些属于上层的流程和服务功能组合类微服务。
​ 因此在进行微服务划分的时候应该从数据-》功能规则-》流程的分层模型维度综合考虑。
​ 原则5:高内聚,松耦合的基础原则
​ 在进行微服务拆分的时候高内聚,松耦合的原则不变。而如何确保拆分的数据库,拆分的业务功能模块满足高内聚松耦合的原则。

3.Eureka集群的孤岛效应
4.springcloud负责均衡的策略

​ 负载均衡:分为服务端负载均衡和客户端负债均衡
​ 服务端负载均衡器的问题是,它提供了更强的流量控制权,但无法满足不同的消费者希望使用不同负载均衡策略的需求,而使用不同负载均衡策略的场景确实是存在的,
​ 所以客户端负载均衡就提供了这种灵活性
​ 客户端负载均衡Ribbon(睿笨)为springclond
​ 1.轮询策略 第一次调用服务 1,第二次调用服务 2,第三次调用服务3
​ 2.权重策略 响应时间越长,权重越低,选中概率就越低
​ 3.随机策略 随机分配
​ 4.最小连接数策略 取连接数最小的一个服务实例。如果有相同的最小连接数,那么会调用轮询策略进行选取
​ 5.重试策略 按照轮询策略来获取服务,如果获取的服务实例为 null 或已经失效,则在指定的时间之内不断地进行重试来获取服务,如果超过指定时间依然没获取到服务实例则返回 null
​ 6.可用性敏感策略 过滤掉非健康的服务实例,然后再选择连接数较小的服务实例
​ 7.区域敏感策略 根据服务所在区域(zone)的性能和服务的可用性来选择服务实例,在没有区域的环境下,该策略和轮询策略类似

5.springcloud的核心组件

https://blog.csdn.net/web18334137065/article/details/125244487
springcloud的五大组件包括Netflix Eurek,Netflix Ribbon,Netflix Hystrix,Netflix Zuul和Spring Cloud Config。
五个组件分别对应(1)服务发现(2)客服端负载均衡(3)断路器(4)服务网关(5)分布式配置。各组件分工有别,协调统一。

6…dubbo: 其核心部分包含:

​ 服务提供者(Provider):暴露服务的服务提供方,服务提供者在启动时,向注册中心注册自己提供的服务。
​ 服务消费者(Consumer): 调用远程服务的服务消费方,服务消费者在启动时,向注册中心订阅自己所需的服务,服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
​ 注册中心(Registry):注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者
​ 监控中心(Monitor):服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心
​ Container:服务运行容器

7.Dubbo 和 Spring Cloud 有什么区别?

​ (1).Dubbo 使用的是 RPC 通信,而 Spring Cloud 使用的是 HTTP RESTFul 方式。
​ (2):dubbo的服务注册中心使用的是Zookeeper,springclond使用的是Eureka
​ (3):dubbo分布式配置和分布式网关相比于springclond都没有
​ (4):dubbo的短路器不完善,springcloud的比较完善

8.Dubbo默认使用的是什么通信框架,还有别的选择吗?

​ Dubbo 默认使用 Netty 框架,也是推荐的选择,另外内容还集成有Mina、Grizzly。(dubbo不支持分布式事务)

Mysql篇

1.事务的特性

​ MySQL 事务具有四个特性:原子性、一致性、隔离性、持久性,这四个特性简称 ACID 特性
​ (1)、原子性(Atomicity ):一个事务是一个不可再分割的整体,要么全部成功,要么全部失败
​ (2)、一致性(Consistency ):事务执行前后属于一致性状态
​ (3)、隔离性(Isolution ):一个事务不受其他事务的影响,并且多个事务彼此隔离
​ (4)、持久性(Durability ):一个事务一旦被提交,在数据库中的改变就是永久的,提交后就不能再回滚

2.数据库的隔离级别

​ 分别是:读未提交(read uncommitted )、读已提交(read committed)、可重复读(repeatable read)、序列化\串行化(serializable)。
​ (1) 读 未提交(read uncommitted):脏读
​ 假如A事务和B事务对同一张表进行操作,B事务进行了DML语句,但没有提交(commit)或者回滚(rollback),此时A事务便可以查看到B事务还没有提交的数据,就是保存在事务性活动的日志文件中的数据。这样可能会造成脏读。何为脏读:脏读就是B事务还没有提交的数据,A事务便可以查看到,在用户的眼里,A用户就会以为这条数据已经存在了,其实还没有提交,如果提交失败,便会回滚,这条数据并没有添加成功,而A用户却以为成功了
​ (2)读 已提交(read committed):不可重复读
​ 假如A事务和B事务对同一张表进行操作,B事务进行了DML(数据操作)语句,但没有进行提交,那么A事务是读取不到的,它解决了脏读,但是出现了不可重复读。
​ (3) 可重复读(repeatable read): 幻读
​ A事务读取不到B事务还没有提交的数据;解决了不可重复读,但会造成幻影读(幻读),数据不够真实。mysql中默认的事务隔离级别就是这个。
​ (4)序列化\串行化(serializable):
​ 这个隔离级别是最高级别,效率最低,但是非常安全,不能并发,俩个事务不能对同一张表进行操作,需要等待A事务操作完之后,B事务才能进行操作。每次读取的数据都是最真实的。

3.索引的分类以及索引的底层原理

​ 索引的分类
​ (1) 普通索引:最基本的索引,它没有任何限制。
​ (2) 唯一索引:与普通索引类似,不同的就是索引列的值必须唯一,但允许有空值。如果是组合索引,则列值的组合必须唯一。
​ (3) 主键索引:它是一种特殊的唯一索引,用于唯一标识数据表中的某一条记录,不允许有空值,一般用 primary key 来约束。
​ (4) 联合索引(又叫复合索引):多个字段上建立的索引,能够加速复合查询条件的检索。
​ (5) 全文索引:老版本 MySQL 自带的全文索引只能用于数据库引擎为 MyISAM 的数据表,新版本 MySQL 5.6 的 InnoDB 支持全文索引。默认 MySQL 不支持中文全文检索,可以通过扩展 MySQL,添加中文全文检索或为中文内容表提供一个对应的英文索引表的方式来支持中文。
​ 索引的底层: B+
​ (1):二叉树(Binary Search Trees)
​ 二叉树是每个结点最多有两个子树的树结构。通常子树被称作“左子树”(left subtree)和“右子树”(right subtree)。二叉树常被用于实现二叉查找树和二叉堆。二叉树有如下特性:
​ 1、每个结点都包含一个元素以及n个子树,这里0≤n≤2。
​ 2、左子树和右子树是有顺序的,次序不能任意颠倒。左子树的值要小于父结点,右子树的值要大于父结点。

4.Mysql优化

https://blog.csdn.net/iiiiiiiiiooooo/article/details/123583743
在设计上:字段类型,存储引擎,范式
在功能上:索引,缓存,分库分表
在架构上:集群,主从复制,负载均衡,读写分离
2.

​ 1.在读表的时候,尽可能的避免全表扫描,合理的根据业务需求,在where及order by涉及的列上建立索引。

​ 2.应尽量避免在where字句中使用!= 或 <> 操作符,否则将引擎会放弃索引而走全表扫描。

​ 3.尽量避免where字句中对字段进行null值判断,否则也会导致引擎放弃索引而走全表扫描。可以用0代替判断,前提是保证字段不能为null。

​ 4.尽量避免在where字句中用or拼接,否则也会走全表扫描。可以通过union all 拼接代替。

​ 5.尽量不适用Like做搜索查询,诺要提高效率,可以采用全文检索。

​ 6.尽量不适用In 或 Not in查询,否则会导致全表扫描。对于连续的数字,可以用between 代替 in。比如:select id from t where num between 1 and 3

5.mysql的模糊查询,mysql自带的函数,mysql的去重查重
6.Sql注入 是怎么实现的

​ 攻击者通过web 应用程序利用SQL语句或字符串将非法的数据插入到服务端数据库中,获取数据库的用户管理权限,然后将数据库管理用户权限提升至操作系统管理用户权限,控制服务器操作系统,获取重要的信息和机密文件

7.MySQL存储引擎InnoDB和MyISAM的区别

​ InnoDB是事务型数据库的首选引擎,是目前MYSQL的默认事务型引擎,是目前最重要、使用最广泛的存储引擎。支持事务安全表(ACID),支持行锁定和外键。
​ MyISAM是MySQL 的引擎之一,不支持数据库事务,也不支持行级锁和外键。
​ 如何选择InnoDB和MyISAM:
​ 1.InnoDB:如果需要对事务的完整性要求比较高(比如银行),要求实现并发控制(比如售票),那选择InnoDB有很大的优势。如果需要频繁的更新、删除操作的数据库,也可以选择InnoDB,因为支持事务的提交和回滚。
​ 2.MyISAM:读取数据快,空间和内存使用比较低。如果表主要是用于读取记录,那么选择MyISAM能实现处理高效率。如果应用的完整性、并发性要求比较低,也可以使用。

8.InnoDB和MyISAM的区别

​ 1.InnoDB是MySQL默认的存储引擎。
​ 2.只有 InnoDB 支持事务,MyISAM不支持事务。
​ 3.MyISAM不支持行级锁和外键, InnoDB支持。
​ 4.InnoDB表的大小更加的大,用MyISAM可省很多的硬盘空间。
​ 5.InnoDB 引擎的索引和文件是存放在一起的,找到索引就可以找到数据,是聚簇式设计。
​ 6.MyISAM 引擎采用的是非聚簇式(即使是主键)设计,索引文件和数据文件不在同一个文件中。

9.行锁:

​ 比如事务 A 更新了一行,而这时候事务 B 也要更新同一行,则必须等事务 A 的操作完成后才能进行更新

10.聚集索引和非聚集索引的区别

区别:
1.聚集索引一个表只能有一个,而非聚集索引一个表可以存在多个
2.聚集索引存储记录是物理上连续存在,而非聚集索引是逻辑上的连续,物理存储并不连续
3.聚集索引:物理存储按照索引排序;聚集索引是一种索引组织形式,索引的键值逻辑顺序决定了表数据行的物理存储顺序。
4.非聚集索引:物理存储不按照索引排序;非聚集索引则就是普通索引了,仅仅只是对数据列创建相应的索引,不影响整个表的物理存储 顺序。
5.索引是通过二叉树的数据结构来描述的,我们可以这么理解聚簇索引:索引的叶节点就是数据节点。而非聚簇索引的叶节点仍然是索 引节点,只不过有一个指针指向对应的数据块。

​ 优势与缺点:
​ 聚集索引插入数据时速度要慢(时间花费在“物理存储的排序”上,也就是首先要找到位置然后插入),查询数据比非聚集数据的速度快

11.mysql索引有几种类型

1.唯一索引:索引列的值必须唯一,可以允许空值

2.主键索引:主键索引是特殊的唯一索引,不允许有空值

3.普通索引:没有限制,值可以为Null,加快查询条件

4.联合索引

12.mysql查询重复数据

select 字段名,count(*) from employee group by employee_name having count(employee_name)>1;

13.EXPLAIN关键字的解释

reids篇

1.redis介绍

​ 基于内存读写操作的数据库. 简介: Redis是使用C语言编写的数据库, NoSQL数据库, Redis采用了非阻塞式IO,使用key-value的形式保存数据 not only sql: (非关系型数据,二维表,) 存: key— value 取: key— 特点:

​ 1.基于内存操作,速度最快.

​ 2.非关系型数据库, 没有table column,row数据概念, key-value保存数据

​ 3.独立服务器的内存 空间. (不会竞争tomcat内部的jvm空间)

​ 4.内部的命令执行: 单线程.(线程安全)

2.单线程为什么快

1.就是它本身是单线程的,单线程避免了操作共享变量,上下文切换的开销,而且本身基于内存操作,速度很快

2.就是它利用I/O多路复用机制,拥有高效的线程模型,可以让主线程在执行过程中不会有网络I/O的阻塞

https://www.jianshu.com/p/05d348eda81f

2.数据结构

redis是一个基于key-value的存储,key是string类型,value可以是string(字符串)、哈希表(hash),列表(list)、集合(set)、有序集合(sorted set)等

3.持久化方式

一种是快照(RDB),一种是日志增量备份(AOF),快照是某一时间的瞬时记录,我可以在5秒钟进行一次存储,也可以10秒进行一次存储,优点是效率高,缺点是如果还没有到时间数据就遗失了,会造成数据丢失
日志增量备份(AOF):就是你写一条我保存一条,好处是数据不容易遗失,坏处是效率低

4.淘汰策略与过期策略

过期策略:
定时删除+惰性删除
定时删除:可以通过redis的配置文件hz配置,1s执行几次定期删除
惰性删除:惰性删除不是去主动删除,而是在你要获取某个key 的时候,redis会先去检测一下这个key是否已经过期,如果没有过期则返回给你,如果已经过期了,那么redis会删除这个key,不会返回给你
内存淘汰策略:
1.noeviction
不进行数据淘汰,也是Redis的默认配置。这时,当缓存被写满时,再有写请求进来,Redis不再提供服务,直接返回错误。
2.volatile-random
缓存满了之后,在设置了过期时间的键值对中进行随机删除。
3.volatile-ttl
缓存满了之后,会针对设置了过期时间的键值对中,根据过期时间的先后顺序进行删除,越早过期的越先被删除。
4.volatile-lru
缓存满了之后,针对设置了过期时间的键值对,采用LRU算法进行淘汰。
5.volatile-lfu
缓存满了之后,针对设置了过期时间的键值对,采用LFU的算法进行淘汰。
6.allkeys-random
缓存满了之后,从所有键值对中随机选择并删除数据。
7.allkeys-lru
缓存满之后,使用LRU算法在所有的数据中进行筛选删除。
8.allkeys-lfu
缓存满了之后,使用LFU算法在所有的数据中进行筛选删除。

4.应用场景

​ 如:计数器(点赞或者阅读量,缓存或者是分布式锁)

5.redis实现缓存的时候,怎么保证mysql和redis数据一致

​ 1.增删改的时候,查一下redis是否缓存上,然后删除redis,再插入数据库,再更新redis

6.为什么要用redis:

​ 第一、redis是基于内存存储计算,性能速读远超mysql等数据库,计算速度很快,所以在使用的时候数据响应很快,
​ 第二、redis支持多种多样的数据结构,如字符串、tree、ztree、map、等,这些丰富的数据结构,可以满足我们在开发工作大部分常见数据结构,进行存储。
​ 第三、redis丰富的api支持,让我们在使用的时候,常见的查询存储都能够很方便的使用,支持自定的查询的api等等
​ 第四、redis的生态比较成熟,很多家大型公司都在使用,很多相关的知识扩展以及分析
​ 第五、redis分布式集群化扩展性极高,而且稳定,能够支撑大量的数据吞吐,只要硬件支持。

7.哨兵和集群

https://blog.csdn.net/qq_39230153/article/details/121524461

哨兵的由来,redis主从切换技术:当主机宕机后,需要手动把一台从(slave)服务器切换为主服务器,这就需要人工干预,费时费力,还回造成一段时间内服务不可用,所以推荐哨兵架构(Sentinel)来解决这个问题.

哨兵模式是一种特殊的模式,首先Redis提供了哨兵的命令,哨兵是一个独立的进程,作为进程,它独立运行。其原理是哨兵通过发送命令,等待Redis服务器响应,从而监控运行的多个Redis实例。

简而言之:redis主从复制的主机宕机时,哨兵会自动的会把从机换成主机,一个哨兵可能会有问题,可以多哨兵进行监控,这就是多哨兵模式,哨兵1认为主机不可用时主观下线,当后面的哨兵也认为不可用时,数量达到一定时,会进行投票,进行failover故障转移操作,转移成功后,就会发布订阅模式,让各个哨兵把自己监控的从服务器实现切换主机,这一过程称为 客观下线

8.redis的缓存机制

9.Redis的击穿、雪崩和穿透

雪崩>击穿>穿透

https://zhuanlan.zhihu.com/p/469677754
(1):缓存雪崩 是指在短时间内,有大量缓存同时过期,导致大量的请求直接查询数据库,从而对数据库造成了巨大的压力,严重情况下可能会导致数据库宕机的情况叫做缓存雪崩
解决方法:加锁排队,相当于单例模式的双重加锁机制(如果是分布式情况下,需要用到分布式锁)
(2):缓存穿透 说白了就是查询一个一定不存在的数据,由于缓存是未命中从数据库去查询,查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到数据库去查询,造成缓存穿透
解决方法:1.布隆过滤器:我们可以使用布隆过滤器来解决,可以将布隆过滤器想象成一个map,请求一个不存在的数据,我们就把它放到这个map中,每次请求前先通过map过滤一遍,如果map中存在这个值就直接将请求拦截掉。
2.缓存空结果:我们可以把每次从数据库查询的数据都保存到缓存中,我们可以将空结果的缓存时间设置得短一些,例如 1-3 分钟。
(3):缓存击穿指的是某个热点缓存,在某一时刻恰好失效了,然后客户端访问数据的时候,redis中没有数据,mysql中有数据,相当于直接跳过了redis。
解决方法:
1.加锁排队
在查数据库时进行加锁,缓冲大量请求, 以减少数据库压力

​ 2.设置永不过期
​ 对于某些热点缓存,我们可以设置永不过期,这样就能保证缓存的稳定性,但需要注意在数据更改之后,要及时更新此热点缓存,不然就会造成查询结果的误差。
​ (4):缓存预热
​ 缓存预热并不是一个问题,而是使用缓存时的一个优化方案,它可以提高前台用户的使用体验。
​ 缓存预热指的是在系统启动的时候,先把查询结果预存到缓存中,以便用户后面查询时可以直接从缓存中读取,以节约用户的等待时间。

10.redis读写分离+主从复制
11.什么时候用memcached,跟redis比,有啥好处

​ (1) memcached所有的值均是简单的字符串,redis作为其替代者,支持更为丰富的数据类型
​ (2) redis的速度比memcached快很多
​ (3) redis可以持久化其数据
​ Memcached支持的数据结构很单一,仅支持string类型的操作。并且对于value的大小限制必须在1MB以下,过期时间不能超过30天
​ 由于 Redis 只使用单核,而 Memcached 可以使用多核,所以平均每一个核上 Redis 在存储小数据时比 Memcached 性能更高。而在 100k 以上的数据中,Memcached 性能要高于 Redis。虽然 Redis 最近也在存储大数据的性能上进行优化,但是比起 Memcached,还是稍有逊色。

12.什么是哨兵

​ Sentinel(哨兵)是用于监控Redis集群中Master状态的工具,是Redis高可用解决方案,哨兵可以监视一个或者多个redis master服务,以及这些master服务的所有从服务。 某个master服务宕机后,会把这个master下的某个从服务升级为master来替代已宕机的master继续工作。
​ (顺带提一句,即使后来之前的master重启服务,也不会变回master了,而是作为slave从服务)

13.redis分布式锁,应用在哪些方面

基于数据库的分布式锁
悲观锁乐观锁【version】。
基于缓存实现分布式锁
用redis(setnx, redlock…)
基于zookeeper实现的分布式锁
之前的理解,zookeeper是用来为管理集群的,比如做load balancing之类的。用于管理协调大量服务器集群。但是实际 zookeeper是一个为分布式应用提供一致性服务的开源组件。它内部是一个分层的文件系统目录树结构,规定同一个目录下只能有一个唯一文件名。它也可以实现分布式锁

mq 解耦、异步、削峰。

rabbitmq

1.如何保证消息队列的高可用?

RabbitMQ 有三种模式:单机模式、普通集群模式、镜像集群模式。

2.如何保证消息不被重复消费?或者说,如何保证消息消费的幂等性?

加全局唯一的 id,看是否被消费过

3.如何保证消息的可靠性传输?或者说,如何处理消息丢失的问题?

确保说写 RabbitMQ 的消息别丢,可以开启 confirm 模式,在生产者那里设置开启 confirm 模式之后,你每次写的消息都会分配一个唯一的 id,
然后如果写入了 RabbitMQ 中,RabbitMQ 会给你回传一个 ack 消息,告诉你说这个消息 ok 了。如果 RabbitMQ 没能处理这个消息,会回调你的一个 nack 接口,
告诉你这个消息接收失败,你可以重试。而且你可以结合这个机制自己在内存里维护每个消息 id 的状态,如果超过一定时间还没接收到这个消息的回调,那么你可以重发
开启 RabbitMQ 的持久化

4.mq之间的比较

img

Linux篇

1.linux的最小执行单元是进程
2.nginx

​ 简介:Nginx是一个 轻量级/高性能的反向代理Web服务器,用于 HTTP、HTTPS、SMTP、POP3 和 IMAP 协议。他实现非常高效的反向代理、负载平衡,他可以处理2-3万并发连接数,官方监测能支持5万并发,现在中国使用nginx网站用户有很多,例如:新浪、网易、 腾讯等
​ Nginx 有哪些优点?
​ 跨平台、配置简单。
​ 非阻塞、高并发连接:处理 2-3 万并发连接数,官方监测能支持 5 万并发。
​ 内存消耗小:开启 10 个 Nginx 才占 150M 内存。
​ 成本低廉,且开源。
​ 稳定性高,宕机的概率非常小。
​ 内置的健康检查功能:如果有一个服务器宕机,会做一个健康检查,再发送的请求就不会发送到宕机的服务器了。重新将请求提交到其他的节点上
​ Nginx 负载均衡的算法怎么实现的?策略有哪些?
​ 1.1 .轮询(默认)
​ 2.权重 weight
​ 3.ip_hash( IP绑定)

3.shell是如何传递参数的

ehco

ELK篇(Elasticsearch , Logstash, Kibana)

ELK(Elasticsearch , Logstash, Kibana)
1.1、Elasticsearch是个开源分布式搜索引擎,提供搜集、分析、存储数据三大功能。它的特点有:分布式,零配置,自动发现,索引自动分片,索引副本机制,restful风格接口,多数据源,自动搜索负载等。
1.2、Logstash 主要是用来日志的搜集、分析、过滤日志的工具,支持大量的数据获取方式。一般工作方式为c/s架构,client端安装在需要收集日志的主机上,server端负责将收到的各节点日志进行过滤、修改等操作在一并发往elasticsearch上去。
1.3、Kibana 也是一个开源和免费的工具,Kibana可以为 Logstash 和 ElasticSearch 提供的日志分析友好的 Web 界面,可以帮助汇总、分析和搜索重要数据日志。
es使用了倒排索引:而倒排索引,是通过分词策略,形成了词和文章的映射关系表,这种词典+映射表即为倒排索引。有了
倒排索引,就能实现 o(1)时间复杂度的效率检索文章了,极大的提高了检索效率
1.es是什么:
elasticsearch简写es,es是一个高扩展、开源的全文检索和分析引擎,它可以准实时地快速存储、搜索、分析海量的数据

JVM篇

1.jvm数据结构和内存调优 jvm的回收机制

​ ①:堆、栈、程序计数器、方法区、本地方法栈

​ (1)堆内存是JVM中最大的一块由年轻代和老年代组成,而年轻代内存又被分成三部分,Eden空间、From Survivor空间、To Survivor空间,默认情况下年轻代按照8:1:1的比例来分配
​ (2)方法区存储类信息、常量、静态变量等数据,是线程共享的区域
​ (3) 栈又分为java虚拟机栈和本地方法栈主要用于方法的执行。
​ 控制参数
​ -Xms设置堆的最小空间大小。
​ -Xmx设置堆的最大空间大小。
​ -XX:NewSize设置新生代最小空间大小。
​ -XX:MaxNewSize设置新生代最大空间大小。
​ -XX:PermSize设置永久代最小空间大小。
​ -XX:MaxPermSize设置永久代最大空间大小。
​ -Xss设置每个线程的堆栈大小。
​ (1.1)Java堆是垃圾收集器管理的主要区域,因此很多时候也被称做“GC堆”。如果从内存回收的角度看,由于现在收集器基本都是采用的分代收集算法,所以Java堆中还可以细分为:新生代和老年代;再细致一点的有Eden空间、From Survivor空间、To Survivor空间等。
​ (2.1)方法区(Method Area)与Java堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。虽然Java虚拟机规范把方法区描述为堆的一个逻辑部分,但是它却有一个别名叫做Non-Heap(非堆),目的应该是与Java堆区分开来。
​ 对于习惯在HotSpot虚拟机上开发和部署程序的开发者来说,很多人愿意把方法区称为“永久代”(Permanent Generation),本质上两者并不等价,仅仅是因为HotSpot虚拟机的设计团队选择把GC分代收集扩展至方法区,或者说使用永久代来实现方法区而已。
​ ②:内存调优
​ 1.参数调优 2.调优工具之jps(Java Virtual Machine Process Status Tool)
​ ③:标记清除算法 2.标记复制算法

2.类加载机制

​ 双亲委派模式,即加载器加载类时先把请求委托给自己的父类加载器执行,直到顶层的启动类加载器.父类
​ 加载器能够完成加载则成功返回,不能则子类加载器才自己尝试加载.

https://blog.csdn.net/weixin_59262008/article/details/125676787

3.Java的垃圾回收机制

https://blog.csdn.net/weixin_53975556/article/details/125916780

https://blog.csdn.net/qq_35246620/article/details/80522720?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522166256320316782417032003%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=166256320316782417032003&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allfirst_rank_ecpm_v1~hot_rank-1-80522720-null-null.142v47body_digest,201v3control_2&utm_term=Java%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E6%9C%BA%E5%88%B6&spm=1018.2226.3001.4449

​ 垃圾回收机制分为两种,自动回收垃圾机制

自动垃圾回收机制

在 Java 世界中,几乎所有的对象实例都在堆中存放,所以垃圾回收也主要是针对堆来进行的。通过垃圾判断算法(引用计数法、可达性分析法)判断堆内存中是否存在垃圾,如果存在,则使用垃圾回收算法(标记-清除算法、标记-整理算法、复制算法、分代收集算法)来进行垃圾回收

23种设计模式:https://zhuanlan.zhihu.com/p/575645658

创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。

结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。

行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

设计模式的六大原则:

1>开闭原则: 开闭原则指的是对扩展开放,对修改关闭。在对程序进行拓展时,不能去修改原有的代码,使用抽象类或接口。

2>依赖倒转原则:依赖倒转原则是开闭原则的基础,系统面向接口编程,依赖于抽象而不依赖于具体。

3>里氏替换原则:里氏替换原则指的是任何基类出现的地方,子类一定可以出现。里氏替换原则是对开闭原则的补充,实现开闭原则的关键是抽象化,而基类与子类的继承关系就是抽象化的具体实现,所以里氏替换原则是对实现抽象化的具体步骤的规范。

4>接口隔离原则:使用多个隔离的接口比使用单个接口好,降低接口之间的耦合度和依赖。

5>迪米特原则:最少知道原则。一个类应当尽量减少与其他实体类相互作用,使得系统功能模块相对独立,降低耦合度。

6>合成复用原则:尽量使用组合/聚合的方式,而不是继承。

MQTT与EMQX

MQTT属于是物联网的通信协议,在MQTT协议中有两大角色:客户端(发布者/订阅者),服务端(Mqtt broker);针对客户端和服务端需要有遵循该协议的的具体实现,EMQ/EMQ X就是MQTT Broker的一种实现。

EMQ X 是开源百万级分布式 MQTT 消息服务器(MQTT Messaging Broker),用于支持各种接入标准 MQTT协议的设备,实现从设备端到服务器端的消息传递,以及从服务器端到设备端的设备控制消息转发。从而实现物联网设备的数据采集,和对设备的操作和控制

https://www.emqx.io/docs/zh/v4.3/backend/backend.html

https://cloud.tencent.com/developer/article/1860436

Docker

https://blog.csdn.net/weixin_42592282/article/details/121783396

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值