笔试+面试题

文章目录

  • 笔试
    • 设计模式
      • 代理模式
      • 单例模式
      • 策略模式
    • 排序算法
      • 冒泡排序
      • 选择排序
      • 插入排序
      • 希尔排序
      • 快速排序
      • 归并排序
      • 堆排序
      • 二分法排序
      • 大小根堆
      • 栈,堆,队列
      • 递归输出斐波那契数列
      • 递归实现阶乘
      • 递归输出单链表
      • 数组中是否有重复元素
      • 字符串中重复元素的个数
      • 字符串中不重复子串中的最大长度
  • 面试
    • Java 基础
      • 各种比较
      • 设计模式
      • 面向对象
      • 数据类型
    • java8新特性
      • Java8代替匿名内部类:
    • 数据库
      • MySql
      • Mongo
      • Mongo和Mysql的区别
    • Linux
    • Docker
    • Jenkins
    • Git
    • 框架
      • Spring
      • Spring MVC
      • Spring Boot
      • Spring Cloud
      • MyBatis
    • Redis
    • RabbitMQ
    • Maven
    • 定时任务
    • Nginx
    • 并发处理办法
      • 跨域
      • 数据量大的处理办法
    • 缓存穿透,击穿,雪崩
    • 分布式锁
    • 分布式Session
    • 分库,分表
    • 事务
      • 分布式事务
    • 一致性HASH
    • CAP
    • BASE理论
    • 线程
      • volatile
      • Synchronized
    • 线程池
    • 线程并发
    • 微服务
    • 其他

笔试

  1. window64和32位的差异:是cpu运算方式的差异
  2. 32位,4G内存的效用,假如安装了大于内存为4G的应用,那么大于4G的部分不能很好的应用
  3. 有,家庭版16G,专业和旗舰版192G
  4. 遍历ArrayList遇到的坑
    只遍历没有问题
    forEach(String id,List)形式:边遍历,编修改会抛出异常。因为这种形式是通过Iterator来访问的,修改的时候,ArrayList会调用remove(),进而调用fastRemove():modCount+1,每次forEach的时候,都会调用checkForComodification,校验modConunt与ExpectedModCount不一致,会抛出异常
    for:边遍历边修改无异常,但是结果有错,因为它不经过迭代器,直接访问ArrayList,不会引入fail-fast机制,
    综上,使用集合类对象的时候,需要考虑fail-fast因素
    当使用Iterator进行遍历的时候,里面的remove()方法会重置expectedModCount,重置动机:保证多个迭代器同时遍历时的数据一致
  5. 数据结构里的散列表高速索引机制
    长度为n的线性表,存放无序数据,平均查找时间为n/2
    hash函数,即散列函数,关联数据和存储位置,hashMap来源于散列表
  6. 散列表里解决冲突的方法:hash值可能相同,HashMap里面用到了”链地址法“,即链表里存放冲突数据
  7. 在HashMap里存储不带HashCode和equals对象(自定义的对象)时:会调用Object里的hashCode(返回对象的地址)和equals(根据地址进行判断)
    此时拿不到具体数据
    重写hashCode ,不重写equals,同样拿不到数据
    重写equals方法和散列表里的冲突有关
    重写规则:根据类的唯一主键来判断hashCode,是否一致,两对象是否该equals
    如介绍项目时,可以说用到了HashMap,其中的键时自定义模块
    当使用ArrayList对象时,频繁的查找其中的数据成为性能瓶颈,候用HashMap对象重构
  8. 短路现象:
    && :左边为false,右边不会执行,结果为false
    || : 左边为true,右边不会执行,结果为true
    注意:尽量别在条件判断里进行运算
    条件判断的语句不要太复杂,可嵌套改写
    注意起始条件和退出条件,测试案例中覆盖边界值
    技巧:重构代码时,或代码重审时,会着重检查短路现象,或避免条件分支太复杂
    编写测试案例时,着重看下边界条件有没有覆盖

设计模式

  1. 设计模式的根本原因是提高项目的可维护性
  2. 注意设计模式后蕴含的思想,如开闭原则
  3. 设计模式不仅能给出具体解决方案,还能提供优化系统架构思路,所以在项目里,一般不会只用某个而会根据一些原则来优化代码,如解耦合和面向接口编程
  4. 不是为了好看才用的设计模式,而是为了解决特定类型的实际问题,从此意义上讲,更关心模式背后包含的原则思想。
  5. 设计模式背后包含的设计原则
    1,依赖倒转原则:模块或类间的依赖关系,如相互调用,是通过interface和abstract类发生,实现类之间不发生或尽量少发生依赖关系。减少修改所影响的范围。即interface或abstract类不依赖于实现类,相反依赖于interface或abstract
    2,单一职责:能让类更稳定,每个类或模块应该只具有单一职责,即只实现一种功能,多的进行拆分
    3,里氏替换原则:子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法,子类要扩展功能,可以增加自己特有的方法
    违背该原则的后果:同一方法在3层里有不同的实现,维护困难,增加多态调用时的复杂度
    4,通过合成复用原则优化继承的使用场景
    合成服用原则的核心:优先使用组合和聚合,只有当父子类之间存在逻辑上的从属关系时,才考虑extends
    聚合:表示整体和部分的弱关系,组合表示强关联关系
    技巧:
    a.在项目里,定义类之间的关系时,会遵循合成复用原则,只有具有从属关系的类之间采用继承,否则会使用聚合或组合,这样类之间耦合度低,如果修改其中一个类,不会影响其他类
    b.在……模块,专门讨论了编码规范,其中包含……原则,然后展开……
    c.在详细设计阶段,会根据合成复用原则定义父子类之间的关系,或使用……原则,达到……效果

代理模式

降低了业务使用和业务提供者的耦合度,如处于数据安全的考虑,无法直接调用某服务,可用代理模式
比如在项目里把一些机密数据放在了secret的机器上,同时该机器上启动一个起到安全代理的服务类,该代理类会检查发起请求的模块是否有访问权限,如参数是否正确,通过这个代理,能有效保护数据。

  1. 图示
    客户 ----------------------------------->对象
    客户—————代理—————>对象
    抽象对象角色:声明了目标对象和代理对象的共同接口,这样就可以在任何可以使用目标对象的地方使用代理对象
public abstract claa AbstractObject{
	//操作
	public abstract void operation();
}

目标对象角色:定义了代理对象可代表的目标对象

public class RealObject extends AbstractObject{
		@Override
		operation()~;
}

代理对象角色:代理对象内部含有目标对象的引用,从而可以在任何时候操作目标对象,代理对象提供一个与目标对象相同的接口,以便在任何时候替代目标对象,代理对象通常在客户端调用传递给目标对象之前或之后,执行某个操作,而不是单纯的将调用传递给目标对象

public class ProxyObject extends AbstractObject{
	private RealObject realObject = new RealObject();
	@Override
	public void operation(){
		realObject.operation();
		}
}

测试:代理对象是对象的结构模式

ProxyObject proxyObject = new ProxyObject();
proxyObject.operation();

单例模式

说明:确保实例化对象只有一个,所以要构造函数私有,通过静态方法向外提供对象
改进1,多线程时也要保证一个对象,所以在同一时间段内只有一个线程调用,其他线程排队等待进入synchronized代码块
改进2,不应等待,双重检查,对象存在时不等待

//饿汉模式
public class Singleton{
		private final static Singleton instance = new Singleton();
		private Singleton(){}
		public static Singleton getInstance(){
		return instance();
}
}
//线程安全的懒汉模式:会先判断是否为null
public class Singleton {  
    private static Singleton instance;  
    private Singleton (){}  
    public static synchronized Singleton getInstance() {  
    if (instance == null) {  
        instance = new Singleton();  
    }  
    return instance;  
    }  
}
//线程安全,延迟初始化。这种方式采用双锁机制,安全且在多线程情况下能保持高性能。
public class Singleton {  
    private volatile static Singleton singleton;  
    private Singleton (){}  
    public static Singleton getSingleton() {  
    if (singleton == null) {  
        synchronized (Singleton.class) {  
        if (singleton == null) {  
            singleton = new Singleton();  
        }  
        }  
    }  
    return singleton;  
    }  
}

策略模式

行为模式
游客旅游举例说明

排序算法

内部排序:数据在内存中进行排序
外部排序:数据很大的时候,一次不能容纳所有记录,在排序的过程中需要访问外存,大文件的排序
内部排序的有,冒泡,选择,插入,希尔,快速,归并
稳定的有,冒泡,插入,归并

冒泡排序

说明:通过每一次的排序获取最大值或最小值,放在头部或者尾部,后除去排好的数据,继续排序

For(int i = 0;i<a.length;i++){
For(int j = 0;i<a.length-i-1;i++){
   if(a[j]>a[j+1]){
    t = a[j]  a[j] = a[j+1]  a[j+1] =t;
}
}
}
平均:O() 最好:O(n) 最坏:O() 空间复杂度:O(1)

选择排序

说明: 将第一个看成最小值,与后续进行比较,找出最小值和下标,与起始值进行交换

For(i= 0;i<a.length;i++){
	Min = a[i]; Index = I;
	For(j = i+1;j<a.length;j++){
		If(min>a[j]){
		Min = a[j]; 
		Index = j;
	}
}
	T =a[i]  a[i] = min ; a[index]=t
}
平均:O() 最好:O() 最坏:O() 空间复杂度:O(1)

插入排序

说明:从第二个数据进行比较,如果2比1小,则交换,再用第三个数据比较,如果比前面的小,则交换,否则,退出循环

For(i=1;i<a.length;i++){
	For(j = i;j>0;j--){
 	if(a[j]<a[j-1]){
	T = a[j];a[j]=a[j-1];a[j-1]=t;
	}else{
  	break;
	}
	}
}
平均:O() 最好:O(n) 最坏:O() 空间复杂度:O(1)

希尔排序

说明:与插入排序相同,不同的是,每次循环的步长不同,通过减半来实现

For(i=a.length/2;i>0;i=i/2){
	For(j = i ;j<a.length;j++){
		For(k = j;k>0&&k-i>0;k=k-i){
			If(a[k]<a[k-i]){
				t = a[k-i];a[k-i] = a[k];a[k] =t;
			}else{
				Break;
			}
		}
	}
}
平均:O(N1.3) 最好:O(n) 最坏:O() 空间复杂度:O(1)

快速排序

说明:冒泡排序的改进,选择一个基准数,分成左右两个部分,>=位于右侧,<=位于左侧,左侧又选择一个分界值,同上划分,右侧同理,左右两个排好序后,整个数组也排好序了
temp
i——> <——j
j选中小于temp的,存入a[i]中,移动i
i移动同理,直到i,j相遇,然后将temp值存入a[i]中

public class QuickSort {
    public static void main(String[] args) {
        int[] num = new int[]{6,1,2,7,9,3,4,5,10,8};
        for(int a:quickSort(num, 0, num.length-1)) {
            System.out.print(a+" ");
        }
    }
    public static int[] quickSort(int[] num, int leftPos, int rightPos) {        
        if(rightPos < leftPos)
            return num;
        else {
        //将数列最左边第一个数字作为基准数
        int initLeftPos = leftPos;
        int initRightPos = rightPos;
        int baseNum = num[leftPos];
                
        while(rightPos > leftPos) {
            //第二步:右边指针找到小于基准数的就停下
            while(num[rightPos] >= baseNum & rightPos > leftPos) {
                rightPos--;
            }
            
            //第二步:左边指针找到大于基准数的就停下
            while(num[leftPos] <= baseNum & rightPos > leftPos) {
                leftPos++;
            }
            //交换两个指针最终标记的数字
            if(rightPos > leftPos)
                swap(num,leftPos,rightPos);
        }
        //当左右两边指针重合时,将基准数与指针指向数字交换
        swap(num,leftPos,initLeftPos);
        
        //指针左半边递归,以进来的数组的左边为界,右边是左右指针相同时左边一个
        quickSort(num, initLeftPos, leftPos-1);
        
        //右边同理
        quickSort(num, rightPos+1, initRightPos);

        return num;
        }
    }
    
    //swap方法:将数组中leftPos和rightPos上的两个数值进行交换
    public static void swap(int[] num,int leftPos,int rightPos) {
        int temp = num[leftPos];
        num[leftPos] = num[rightPos];
        num[rightPos] = temp;
    }
}

平均:O(nlog2n) 最好:O(nlog2n) 最坏:O() 空间复杂度:O(nlog2n)
O(nlog2n)

归并排序

平均:O(nlog2n) 最好:O(nlog2n) 最坏:O(nlog2n) 空间复杂度:O(n)

堆排序

平均:O(nlog2n) 最好:O(nlog2n) 最坏:O(nlog2n) 空间复杂度:O(1)

时间复杂度如何计算

  1. 找到执行次数最多的语句,通常为循环体,加法常数用1代替
  2. 执行语句的数量级,忽略系数
  3. 用O表示

二分法排序

说明:在插入第i个元素时,对前面的0~i-1元素进行折半,先跟他们
中间的那个元素比,如果小,则对前半再进行折半,否则对后半
进行折半,直到left>right,然后再把第i个元素前1位与目标位置之间
的所有元素后移,再把第i个元素放在目标位置上。
二分法排序最重要的一个步骤就是查找要插入元素的位置,也就是要在哪一个位置上放我们要准备排序的这个元素。
当我们查找到位置以后就很好说了,和插入排序一样,将这个位置以后的所有元素都向后移动一位。这样就实现了二分法排序。
  然后是怎么查找着一个位置呢,就是不断的比较已排序的序列中的中间元素和要排序元素,如果大于的话,说明这个要排
序的元素在已排序序列中点之前的序列。

public static void DichotomySort(int[] array)
        {
            for (int i = 0; i < array.Length; i++)
            {
                int start, end, mid;
                start = 0;
                end = i - 1;
                mid = 0;
                int temp = array[i];
                while (start <= end)
                {
                    mid = (start + end) / 2;
                    if (array[mid] > temp)//要排序元素在已经排过序的数组左边
                    {
                        end = mid - 1;
                    }
                    else
                    {
                        start = mid + 1;
                    }
                }
                for (int j = i - 1; j > end; j--)//找到了要插入的位置,然后将这个位置以后的所有元素向后移动

                {
                    array[j + 1] = array[j];
                }
                array[end + 1] = temp;


            }
        }

大小根堆

                           A[0]
                 B[1]-------------C[2]
                 A>=BC:大根堆   升排序
                 A<=BC:小根堆   降排序

如,一组数字,5,11,7,2,3,17
首先建立一棵完全二叉树,从左到右,左右节点都填上,再根据上述规则,进行大小根堆排序
5
11 -------------7
2-------3 ------- —17

栈,堆,队列

  1. :是一棵完全二叉树,大根堆,小根堆,顺序随意
    在程序运行时非编译时,申请某个大小的内存空间,即动态分配内存
  2. :一种受限的线性表,又名堆栈,后进先出
    受限是指在top端进行插入和删除操作,编译的时候,为线程或进程建立存储区域,push,pop操作
  3. 区别
    栈的空间分配是OS自动分配,存储函数参数值,局部变量
    缓存方式:一级缓存,调用完立即释放
    数据结构:先进后出
    堆:空间分配由程序员分配,若不释放,结束时由OS回收,分配方式类似于链表
    缓存方式:二级缓存,生命周期由JVM垃圾回收来决定
    数据结构:一棵树
  4. 队列线性表,前出后进,先进先出,队尾进,队头出

递归输出斐波那契数列


int f(int n)
{
	if(n==1||n==2)return 1;
	else return f(n-1)+f(n-2);
}

递归实现阶乘

public static int factorial(int n){
		if(n == 1){
			return 1;
		}
		else{
			return n*factorial(n-1);
		}
	}

递归输出单链表

从尾到头打印链表每个节点的值。

 首先定义一个节点类
 class ListNode {
		int val;
		ListNode next = null;
		ListNode(int val) {
			this.val = val;
		}
}
利用递归返回类似栈的形式,实现递归算法

public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
    	ArrayList<Integer> arrayList=new ArrayList<Integer>();
			if(listNode!=null){
		    	this.printListFromTailToHead(listNode.next);
		        arrayList.add(listNode.val);
		    }
		return arrayList;
}

数组中是否有重复元素

将数组中的元素放入set中,看set的最终结果的长度是否等于数组的大小

字符串中重复元素的个数

  1. 先用split()方法将字符串分割,存入数组中
  2. 数组循环放入map中,key为字符,value为个数

字符串中不重复子串中的最大长度

todo

面试

Java 基础

  1. Java创建对象的方式
    (1)new 语句
    (2)反射手段,调用java.lang.Class或者Java.lang.reflect.Constructor类的newInstance()
    (3)调用对象的clone()
    (4)反序列化手段
    说明:1.2显示的调用构造函数,3.对内存上已有对象的影印,4.从文件中还原类的对象
  2. Md5:单向散列函数的加密算法
  3. && 短路运算,& 按位与
  4. this:指这个对象,当前对象,表示类的成员变量而非函数参数,this不能用在static方法中,因为static无对象之说
    a. 局部变量与成员变量重命名时
    b.返回return this,或传递当前对象
    c.构造器中调用其他构造器
  5. java中的方法氛围静态方法和非静态方法
  6. 垃圾收集管理器
    :定义的基本数据类型的变量,对象的引用,函数调用的保存现场,堆heap:new和构造器创建的对象,是垃圾收集管理的主要区域
    垃圾收集器采用分代收集算法,分为新生代和老生代,各个线程共享的内存区域:方法区,堆,
    100,hello等和常量是放在常量池种的,常量池是方法区的一部分,
    当栈用光的时候,会报StackOverflowError,当堆和常量空间不足的时候,会报OutofMemoryError错误
    垃圾回收机制
    JVM用于在空闲时间以不定时的方式动态回收无任何引用的对象占据的内存空间
    方式:手动System.gc(),自动
    GC:垃圾收集,GC的功能可自动监测对象是否超过作用域,从而达到自动回收内存的目的
    对象什么时候被回收
    1,引用计数器法:给每个对象加一个,被引用一次+1,为0的时候回收
    2,可达性分析法:向下搜索所走过的路径,称为引用链。当一个对象无任何引用链时,即这个对象不可达
    综上,当这个对象对当前使用这个对象的应用程序变得不可触及时,这个对象就被回收了
  7. J2SDK:是Sun公司开发的编程工具
    java api:应用程序接口
    JAR:java Archive Java归档文件
    Unicode:是用16位来表示一个字的
  8. 2<<3:左移3位,相当于2的3次幂
  9. Java类加载过程
    包含加载,验证,准备,解析,初始化,使用【根据程序定义的行为执行】,卸载(由GC完成)
    加载,连接(验证[文件格式,元数据,字节码,符号引用的验证],准备【为变量分配内存并设置类变量的初始化】,解析【将常量池内的符号引用替换成直接引用。主要包括四种类型引用的解析。类或接口的解析、字段解析、方法解析、接口方法解析。】),初始化 【初始化类的变量和其他资源,如static块,构造函数,父类初始化等】
    通过一个类的全限定名来获取定义此类的二进制字节流。
    将这个字节流所代表的静态存储结构转化为方法区域的运行时数据结构。
    在Java堆中生成一个代表这个类的java.lang.Class对象,作为方法区域数据的访问入口

java文件从编码完成到运行,包括编译,运行
编译:java文件通过javac命令,编译成字节码文件,即.class文件
运行:.class文件交给JVM运行
类加载过程即指JVM虚拟机把.class文件中类信息加载进内存,并运行解析生成class对象的过程,只加载一次
类加载器:类启动,扩展类,应用类,自定义
7. String类常用方法
equals() compareTo(unicode进行比较) indexOf() lastIndexOf()
valueOf() 其他类型转字符串
concat() 追加字符串 contains()
format() length() join() replace() spilt() replaceAll()
trim() subString() matches()
8. 读取文件用到的类
File InputStream OutputStream FileInputStream
FileOutputStream BufferedReader BufferedWriter
File常用方法:
exist() 文件路径是否存在
createFiles() delete() createDirectory()
copy() move() size() read() writ()
9. Java类加载顺序
父类静态字段,父类静态代码块,子类静态字段,子类静态代码块,父类成员变量(非静态字段),父类非静态代码块,父类构造器,子类非静态代码块,子类构造器
10. 两个对象的hashcode相同,equals不一定为true
11. final
修饰的类不能被继承,修饰的方法不能被重写,修饰的变量必须实例化,不能被修改
12. Servlet的生命周期:实例化,初始化init,接收,响应请求,销毁destroy
13. IO流
功能角度:输入流,输出流
类型角度:字节流,字符流
区别:字节流按照8位,以字节为单位输入输出数据
字符流按照16位,以字符位单位输入输出数据
14. java 集合容器
-----------------------------Collection
--------List----------------------------Queue---------------------------Set
Vector–ArrayList—LinkedList–PriorityQueue—HashSet—TreeSet
Stack ----------------------------------------------------------LinkHashSet
HashSet原理:底层实现是HashMap,值存放在hashMap的key上
15. 创建线程的方式
1,extends Thread
2,implements Runnable
3,通过Callable 和Future创建
16. 线程的五种状态
create,就绪,运行,阻塞,死亡
17. Java内存泄漏:让JVM误以为对象还在引用,无法回收了
可能导致内存泄漏的原因:
1,静态集合类HashMap,LinkedList……
长生命周期的对象持有短声明周期对象的引用,尽管短声明周期的对象已经不再引用,但长生命周期的对象因持有它的引用而不能被回收
2,各种连接不关闭,如数据库,IO
3,变量不合理的作用域,如定义的作用范围>使用范围,还没有==null
4,内部类持有外部类,如一个外部类的实例对象的方法返回一个内部类的实例对象,而内部类的实例对象被长期引用,但外部类的对象不用了,此时外部因持有内部类不能被回收
5,改变哈希值,一个对象存入HeshSet后,不能更新对象中参与计算哈希值的字段,否则检索不到,也无法单独删除
18. JVM内存结构
-----------------------------JVM运行时内存划分
堆区---------------------------虚拟机栈 ---------------------本地方法栈
方法区---------------------------------------程序计数器
19. 异常
1,finally{} :放资源回收类代码
特性:不管发生异常,发生何种异常,就算try-catch语句里有return ,finally{}从句都会被执行
除非有System.exit(0)语句,才不会被执行
2,Exception:无需try-catch包含,一旦出现,程序终止,如除0,空指针异常
3,异常处理的准则:
在finally{}从句中释放资源
尽量缩小try语句的范围
先用专业的异常类来处理,益于定位,再用Exception类来兜底
在catch语句中尽可能详细的输出异常信息
处理机制:极可能缩小影响范围,平行业务间用不同的try-catch包起来

各种比较

  1. ==与equals的比较
    == : 8种基本数据类型的比较用,比较值是否相等
    引用数据类型中==比较的是引用的对象是否相等,即是否引用了同一对象
    equals:介于两个对象间的比较,没用重写的情况下,equals与 == 在引用数据类型中比较的结果是一样的
    当重写equals方法时,例如String,Integer,判断的是所存的值是否相等,String中equals方法重写成比较两个对象中对象的内容是否相等,而一般对象的equals方法判断两个对象是否属于同一个对象,如是String缓冲池内不存在与其指定值相同的String对象,那么JVM为其创建新的对象,并存放在缓冲池内
    否则判断的是地址是否相等
    instanceof:参数是否为正确的类型

  2. String和string的区别
    String 是java中的一个类,而string是String类的一个对象

  3. sleep和wait的区别
    1,sleep是线程中的方法,wait是object的方法
    2,sleep不会释放锁,wait会,且会进入等待队列
    3,sleep不依赖于synchronized同步代码,wait需要
    4,sleep不需要被唤醒,wait需要

  4. JDK与JRE
    JDK:Java Development Kit java开发工具包
    为java提供了开发和运行环境

  5. String ,String Builder ,String Buffer的不同
    3个区别在于,String 声明的是不可变的对象,每次操作都会生成新的对象,而后两者可以在原有对象的基础上进行操作
    String Buffer是线程安全的,但String Builder性能高,所以在单线程的环境下推荐使用String Builder,多线程环境下推荐使用String Buffer,且使用String builder或String buffer的reverse()可使字符串反转

  6. String s = “i”;与String s = new String(“i”);一样吗?
    不一样,内存分配方式不一样,前者会分配到常量池中,后者会分配到堆内存中

  7. 普通类与抽象类的区别:
    1,前者不含有抽象方法,可直接进行实例化
    2,后者可含抽象方法,但不能被实例化,能够被继承,且必须继承抽象方法
    3,有抽象方法的必然是抽象类,反之不然

  8. 接口与抽象类的却别
    1,实现不同,implements,extends
    2,接口没有构造函数,抽象类有
    3,接口没有main(),抽象类里有并运行它
    4,实现数量不同,类可实现多个接口(接口是对功能的封装,一个类允许有多种功能),但只能继承一个抽象类(若多继承,会导致方法逻辑混论,因为不重写equals,和hashCode,会调用Object类里的方法,导致混论,代码不易维护)
    5,访问修饰符不同,接口中默认为public,抽象类中可以为任意

  9. Collection 与Collections
    Collection:集合接口,提供了对集合对象进行基本操作的通用接口方法
    Collections:集合类的工具类,帮助类,有一系列静态方法,如对元素的排序,搜索等

  10. HashMap与HashTable的区别:
    父类不同,但都实现了Map接口
    •HashTable是线程安全的
    •HashMap 中允许有一个key为null的键,HashTable key/value不允许null值。
    •是否提供contains方法,HashMap中去掉了
    •遍历用的Iterator内部实现不同
    •Hash值不同,hashTable直接使用hashCode,而hashMap重新计算hash值
    •初始化大小和扩容方式不同,table为11,map为16

  11. 何时用HashMap ,何时用TreeMap
    HashMap:在map中插入,删除,定位,链表散列,即数组和链表的结合体,jdk1.8后链表中超过8个后,链表会转换位红黑树来提高查询速度
    先数组,后链表存储,数组的下标是:重新计算的hashcode值
    TreeMap:有序key进行遍历

  12. 深拷贝和浅拷贝的区别:
    浅:只是复制了对象的引用地址,两个对象指向同一内存地址,一个变另一个也会变
    深:对象及其值复制过来,一个变另一个不会变

  13. SOA与微服务的区别
    SOA:大块业务逻辑,适用于任何类型的公司架构,着重中央管理,确保应用能交互操作
    微服务:单独任务或小块业务逻辑,松耦合,适用于小型,专注于功能交叉团队,着重分散管理,目的:执行新功能,快速扩展开发团队

  14. ArrayList 和LinkedList的区别
    都实现了List和Collection接口
    ArrayList底层实现是数组,实现了RandomAccess接口,随机查询速度快,增删元素慢
    LinkList底层实现是链表,没有实现RandomAccess接口,增删速度快,查询速度慢
    如一次性的通过add从集合尾部添加元素,后只读取一次,建议用ArrayList,如频繁添加,或在完成添加后频繁查找,建议用LinkedList
    两者都不安全,Vector是线程安全的
    ArrayList是扩容50%,Vector是100%,LinkedList是双向链表,无需扩容

  15. 重载,覆盖,重写
    覆盖:两方法同名同参,子类覆盖父类
    覆盖时两个准则:1,不能缩小父类的可见范围,违背面向对象里”父类定义通用的属性和方法原则“,2,不能抛出更宽泛的异常,因为父类定义的方法原型时外部类调用的规范,若修改,则会造成外部调用时无法处理抛出的异常
    重载:同名不同参,能分离业务和业务实现细节,针对同类业务,同名的方法定义和实现(不同参数表示实现细节不同),提升代码可读性,两者解耦合,也提高了代码的可扩展性,多态的思想
    重复:同名,同参,不同参数名(不同返回值)

  16. final,finally,finaliza
    final:可作用再类,方法,属性上,引用上:说明该引用不能再指向其他内存空间,但引用指向的值可以改变
    finally:从句里放资源回收的代码,如释放数据库的连接,释放IO对象,清空clear集合,设置大对象为null,以减少大对象上的强引用,从而提升大对象的回收空间
    finalize:相当于C++里的析构函数,对象回收前会被调用,建议不重写,采用Object里默认的,若重写,需要慎重,一旦没有写好,会导致对象无法被回收,从而导致内存泄漏
    技巧:1,在详细设计和代码review的过程中,会根据业务需求,在相关类和方法前加final,然后展开……
    2,项目注重异常处理流程,比如在finally从句中加入资源回收代码
    3,项目里,因无需在对象回收前定义相关资源释放动作,所以无需重写finalize方法,并确保所有类的该方法都不重写

  17. Arraylist和Array区别
    Array类型的变量在声明的同时必须进行实例化(至少得初始化数组的大小),而ArrayList可以只是先声明;
    Array始终是连续存放的;而ArrayList的存放不一定连续;
    Array对象的初始化必须指定大小,且创建后的数组大小是固定的;而ArrayList的大小可以动态指定,空间大小可以任意增加;
    Array不能随意添加、删除;而ArrayList可以在任意位置插入和删除

  18. List和Set的区别
    List和set都是继承collection接口,存放对象,不同的是list存放有序可以重复的元素,set存放的无序不可重复元素,list比较常用的实现类有ArrayList和LinkList,Set是HashSet和TreeSet。
    Set是通过HaseCode是否相等判断元素的重复

  19. HashSet和TreeSet的区别
    HashSet是通过哈希表实现的,存的是无序数据,TreeSet 是二差树实现的,TreeSet中的数据是自动排好序的,不允许放入null值。

  20. 线程中run和start的区别
    run()相当于线程的任务处理逻辑的入口方法,它由Java虚拟机在运行相应线程时直接调用,而不是由应用代码进行调用。
    而start()的作用是启动相应的线程。启动一个线程实际是请求Java虚拟机运行相应的线程,而这个线程何时能够运行是由线程调度器决定的。start()调用结束并不表示相应线程已经开始运行,这个线程可能稍后运行,也可能永远也不会运行。
    Start()是启动一个新的线程,然后新的线程会调用run()方法,但是start()方法不可以重复调用,若会出现异常Exception in Thread “main” java.lang.IllegalThreadStateException.而且启动线程,会出现异步的效果,即线程创建和启动是随机的
    run()方法类似一个一个普通方法,如果单独调用,仅仅会在当前线程启用,不会重新启动新的线程。启动线程是同步的。

设计模式

  1. 23种
  2. 创建模式:工厂,抽象工厂,创建者,原型,单例 5
  3. 结构模式:适配器,桥接,过滤器,组合,装饰器,门面,享元,代理 8
  4. 行为模式:解释器,模板方法,责任链,命令模式,迭代器,策略,参观者 7

面向对象

  1. 抽象:将一类对象的共同特种总结出来构造类的过程,数据(属性),和行为的抽象,不关心细节
  2. 继承:从已有类得到继承信息创建类的过程
  3. 封装:把数据和操作数据的方法绑定,通过接口进行访问,即隐藏一切可以隐藏的东西
  4. 多态:用同样的对象引用调用同样的方法但做了不同的事情
    编译时的多态性:前绑定,方法重写override
    运行时的多态性:后绑定,方法重载,overload,是面向对象的精髓
    实现多态的两件事:1,方法重写
    2,对象造型:用父类型引用引用了子类型对象,这样同类型引用调用同样的方法会根据子类对象的不同而表现出不同的行为
  5. 构造器不能被继承,所以不能被重写,但可以被重载

数据类型

  1. 8种基本数据类型:byte,short,int,char,lang,float,double,boolean
    剩下的为引用数据类型
  2. 数组是一种对象,有的是length属性,String 有的是length()方法,且String不能被继承,因为是final的,list是size()方法,
  3. +=这种赋值运算符隐含了强制类型转换
  4. 自动拆箱和装箱机制: int 的包装类:Integer,char的包装类:Character
  5. switch可支持的数据类型:java版本不同,java5以前byte ,short,int,char ,java5版本:引入枚举,java7版本:引入字符串,lang在目前的版本中都不行
  6. 如整型字面量的值在-128到127之间,不会new新的Integer对象,而是直接引用常量池种的Integer对象进行比较
  7. 操作字符串有哪些类:
    String,String Builder,Sting Buffer,详见比较不同
  8. Map中哪些线程是安全的?
    HashTable,因为get/put方法被synchronized修饰
    SynchronizedMap
    ConcurrentHashMap
  9. 有序的Map有哪些?
    LinkedHashMap (维护着一个Hash表和双向链表)和TreeMap

java8新特性

  1. 新增Lambda表达式,代码简洁
  2. Stream API的支持
  3. List接口新增排序sort支持,
  4. Optional类的使用,最大化的减少空指针
  5. 新增了线程安全的日期API
  6. 接口的默认方法和静态方法 default static

Java8代替匿名内部类:

Java8之前:
new thread(new Runnable(){
@Overide
Public void run(){sout(“”)}
}).start();
Java8方式:new thread(()->sout(“”)).start();
Runnable r = new Runnable(){
@Override
Public void run(){sout(“”)}
}
Java8方式: Runnable r = ()->sout(“”);
直接调用r.run();

数据库

  1. java.sql:提供java存取数据库能力的包

MySql

  1. 去重3种办法:distinct,group by,row_number() over()
  2. sql的优化:
    按照1,SQL语句及索引的优化
    2,表结构优化,如拆表
    3,系统配置优化
    4,硬件优化

    1,在表中建立索引
    2,select时使用具体字段代替*
    3,在建立索引的情况下,避免使用in ,not in ,可以用exist,not exit代替,union 替代or,对数据开头就进行模糊查询,或进行表达式操作,避免在索引列上进行计算,这些都会导致数据库引擎放弃索引进行全表扫描,或数据量大的时候,避免使用where 1=1,进行nul值判断,可使用默认值0进行判断
    4,设计表的时候尽量减少字段宽度,可用数字或简写等
    5,join代替子查询
  3. 函数:
    1,concat(st1,st2……)返回结果为连接参数产生的字符串,有一个为null,返回就为null,连接缺省为逗号
    concat(str1,seperator,str2,sep……)带有分隔符
    concat_ws(seprator,st1,st2……)一次性指定分割符,分隔符不能为nul,否则都为null
    group_concat():group by 产生的同一分组中的值连接起来,返回一个字符串
    group_concat([distinct] 要连接的字段[order by 排序字段 desc/asc][seperator ‘分割符’])
    2, ABS()绝对值
    MOD(N,M)余数
    ROUND()四舍五入取余
    Length()字符串长度
    locate(a,b)返回a在b中第一个出现的位置,没有为0
    instr(a,b)返回b在a中……
    subString(str,pos)从str的起始pos位置返回一个串
    replace(str,a,b)b替换a
    now() month(date)
    current_date() current_time()
    控制流程的函数:case when if
    系统信息的函数:version() user()
    connection_id() 返回服务器连接数
    database() 数据库名
    MD5()
    format(x,n):x 保留后n位,5进位
    大小写转换:upper(s) lower(s)
    ucase() lcase()
    去空格:trim() rand()
    ceil() >=最小 floor() <=最大
    truncate(x,y) 返回x,保留y位
    adddate(date,n)date后加上n天后
    subdate(date,n)date后减去n天
    addtime(time,n)time加上n秒后
    subtime(time,n)time减去n秒后
  4. 三范式:
    1,属性的原子性,不可拆分
    2,记录的唯一性,有唯一标识
    3,对字段冗余的约束,即字段不能由其他派生出来
  5. 存储过程语法
    delimiter
    create procedure……
    begin
    sql……
    end
    delimiter
  6. 乐观锁
    总是假设最好的情况,每次拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号机制和CAS算法实现,乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库提供的类似于write_condition机制,其实都是提供的乐观锁
    实现方式:
    版本号机制:一般是在数据表中加上一个version字段,表示数据被修改的次数,当数据被修改时,version+1。当线程A要更新数据值时,在读取数据的同时也会读取version值,在提交更新时,若刚才读取到的version值为当前数据库中的version值相等时才更新,否则重试更新操作,直到更新成功。
    CAS算法:compare and swap 一种有名的无锁算法
  7. 悲观锁
    总假设最坏的情况,每次取拿数据的时候都会上锁,这样别人想拿这个数据,就会阻塞它拿到
    共享资源每次只给一个线程使用,其他线程阻塞,用完之后再把资源转给其他线程。
    如关系型数据库的读锁,写锁,表锁等,java中synchronized和reentrantLock等独占锁是悲观锁的实现。
    两种锁的使用场景:乐观锁适用于多读场景,写比较少,省去锁的开销,加大了系统的吞吐量。
    多写的场景用悲观锁比较合适

Mongo

  1. 语句
    db.dropDatabase()
    show collections/tables
    db.name.drop/insert/save/remove/update/find/findOne({age:2})
    db.createUser
  2. 使用场景
    1,存应用日志,查询比文本灵活,导出也方便
    2,存监控数据
    3,存储地理位置
    4,作为缓存查询
    5,第三方信息的抓取
    不适用:复杂事务逻辑操作,统计汇总+复杂运算

Mongo和Mysql的区别

关系型数据库:由数据库+表+记录组成
Mong由数据库+集合+文档对象

  1. 前者非关系型,后者关系型
  2. 存储方式:前者以类json的文档格式存储 后者不同引擎由不同方式
  3. 数据处理方式不同,前者基于内存,高速读写,后者不同引擎不同
  4. mongo相对来说热度低
  5. 前者只支持单文档事务性,弱一致性,后者支持事务
  6. 前者占用空间大,不支持join

Linux

  1. 读取权限:r=4,写入权限:w=2,执行权限:x=1
  2. 775:代表拥有者,组用户,其他用户的权限
  3. 命令:
    tail -f auth-service.log 动态实时日志
    head -n 10 auth-service.lot 前10行日志
    cat filename 一次显示整个文件
    cat >filename:创建一个文件
    cat file1 file2 >file 几个文件合并位一个文件
    tac cat为正向显示,tac反向显示
    more 一页一页的显示
    编辑命令
    1,vi filename 打开或创建一个文件
    2,进入编辑模式:i或a
    3,esc退回vi的命令模式
    4,: 末行模式
    5,wq保存
    w:保存当前文件 x 保存并退出
    q 退出vi q!不保存并退出
    ifconfig :查看ip
    ctrl+c 强制退出
    pwd:显示当前目录
    cd:切换目录 cd ~John
    clear:清空
    service network restart:重启网卡
    service iptables status:查看防火墙状态
    chmod:改变文件权限设置

Docker

  1. Docker是一个容器化平台,将应用程序及其所有依赖项以容器的形式打包在一起,以确保应用程序在任何环境中无缝进行
  2. Docker镜像image:Docker容器的源代码,包含运行容器的所有数据Docker镜像用于创建容器,build命令用于创建镜像
  3. Docker容器container:包含应用程序及其所有依赖项,作为os的独立进程运行,相对于image是动态的
  4. 容器的状态:运行,暂停,重启,退出
  5. 命令:对镜像的操作:docker pull+serviceId,docker push +serviceId ,docker images(列出所有镜像),docker rmi+serviceId(移除镜像)
    对容器的操作:docker ps(列出所有容器),docker rm+serviceId
  6. Docker的整个生命周期:image+容器+仓库
    可以类比image是类,容器是对象,
    image是文件,container是进程,image是由一层一层的文件系统组成,是只读的静态文件,包含镜像的位置
  7. docker的默认仓库是dockerhub工共仓库
  8. 监控Docker容器的两种方式
    1,通过脚本与Linux中的crontab结合
    2,通过使用zabbix工具去监控docker使用率和cpu使用率,通过设置阀值去进行邮件报警

Jenkins

Java编写的开源持续集成工具,用于跟踪版本控制系统,并在发生更改时启动和监视构建系统

Git

git 分布式版本控制,SVN:集中式版本控制

  1. 命令
    git fetch:从远程获取最新版本到本地
    git merge:将内容合并到当前分支
    git pull = git fetch + git merge
    git tag:查看标签
    tag:一次commit的id,用来给开发分支做标记
    git stash:缓存当前修改内容
    git stash pop:恢复最新一次缓存
    git branch -r :查看远程分支
    git branch:查看分支情况
    git push origin master:将本地分支推到远程分支
    git commit 提交代码

框架

Spring

  1. spring的注入Bean的方式:从容器中取出bean,注入到另一个bean中
    a. 构造器注入
    b. setter注入
    c.基于注解注入
    @Autowire(按照类型) @Qualifier(按照名称)
    @Resource
  2. @注解:
    Component:定义bean
    Repository:对Dao实现类进行注解
    Service:对Service实现类进行注解
    Controller:对Controller实现类进行注解
  3. spring的理解:是一个轻量级的IOC和AOP容器框架
    核心是IOC容器:用于对对象的创建,维护,和销毁等生命周期的控制
    spring按照设计模式打造的,实现了工厂模式的工厂类,BeanFactory
  4. 三种配置:基于XML,注解,Java类
  5. AOP
    支持通用事务,如安全,事务,日志,权限等,是面向切面,将与业务无关的但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块,降低了模块间的耦合度。

AOP的步骤:
a. 引入AOP相关pom文件
b.编写切面类
c.在业务方法上配置切面,用@Aspect生命

AOP通知类型
a.前置通知:在某连接点之前执行的通知
b.后置通知:有异常,没有正常返回时无法执行通知
c.抛出异常后的通知:在方法抛出异常退出执行的通知
d.环绕通知:在目标方法执行之前和之后都可以执行的通知
特:需要返回返回值,否则只得到null
可以控制目标方法是否执行,是否有返回值,及改变返回值
可以接收ProceedingJoinPoint,其他只能接收JoinPoint
e.最终通知:在目标方法执行后执行的通知。与后置通知的区别是,即使出现异常也会执行

  1. IOC:控制反转,指创建对象的控制权移交给Spring容器,使用java的反射机制,根据配置文件在运行时动态的去创建对象即管理对象并调用对象的方法。

  2. Spring bean的生命周期:类似,大概,按照流程随便总结下
    笔试+面试题_第1张图片

  3. bean的作用域:
    默认Singleton:每个容器种一个实例
    request:每个网络请求一个实例,请求完成后bean会失效并被垃圾回收
    session:每个session一个实例
    prototype:每个bean请求一个实例
    global-session:全局作用域

  4. spring框架中用到的设计模式
    工厂模式:beanFactory ,用于创建实例
    单例模式:bean的产生
    代理模式:AOP
    模板模式:用来解决代码重复的问题,RestTemplate,JpaTemplate
    观察者模式:当一个对象的状态改变,所有依赖于它的对象都会得到通知,被制动更新

  5. Transactional:一般不指定的情况下出现rollback,运行时异常error的异常会出现回滚,出现Exception的异常不会回滚
    解决办法:可以在注解属性上配置rollbackFor = Exception.class
    或者手动触发回滚:TranscationStatus.setRollbackObly();
    事务可以加到抽象类或接口上,具体的业务方法上,继承抽象类或实现接口的子类也就继承了这个事务

Spring MVC

  1. 表现层框架,是面向web应用的,通过把model-view-controller进行过分离,是基于MVC架构的用来简化web应用程序开发的应用开发框架,是spring的一个模块

  2. 流程:
    a、用户发送请求至前端控制器DispatcherServlet。
    b、DispatcherServlet收到请求调用HandlerMapping处理器映射器。
    c、处理器映射器找到具体的处理器(可以根据xml配置、注解进行查找),生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。
    d、 DispatcherServlet调用HandlerAdapter处理器适配器。
    e、HandlerAdapter经过适配调用具体的处理器(Controller,也叫后端控制器)。
    f、Controller执行完成返回ModelAndView。
    g、HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet。
    h、DispatcherServlet将ModelAndView传给ViewReslover视图解析器。
    i、ViewReslover解析后返回具体View.
    j、DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)。
    k、DispatcherServlet响应用户。

  3. SpringMVC的控制器是单例模式,所以在多线程访问时有线程安全的问题,不要用同步,会影响性能,解决方案是在控制器里不能加字段

  4. 常用的注解
    @RestMapping:用于处理请求url映射的注解
    @RequestBody:接收http请求的json数据,将json转换为java对象
    @ResponseBody:将Controller方法返回的对象转化为json对象响应给用户
    控制器的注解:@RestController = Controller+ResponseBody

  5. SpringMVC中的函数返回值
    有多种类型,String ,ModelAndView(视图和数据合并在一起的)

  6. SpringMVC用什么对象从后台返回数据到前台:ModelMap,放在put里

  7. 怎样把ModelMap里面的数据放在session里
    在类上加@SessionAttributes注解,里面包含的字符串就是放在session里的key

  8. 拦截器如何实现
    1,实现HandlerInterceptor接口
    2,继承适配器类
    然后在配置文件中配置拦截器

  9. 注解原理
    本质上是继承了@Annotation的特殊接口,具体实现类是Java运行时生成的动态代理类,通过反射获取注解时,返回的是Java运行时生成的动态代理对象

  10. MVC模式:用于应用程序的分层开发
    Model:代表一个存取数据的对象
    View:模型包含数据的可视化
    Controller:作用于Model+View,控制数据流向模型对象,并再数据变化时更新视图,使Model和View分离开

Spring Boot

  1. 理解:是Spring的子项目,是Spring组件的一站式解决方案,简化了配置,提供了各种启动器,简化了Spring的应用开发,约定大于配置,简化了maven配置,just run就能创建一个产品级别的应用
  2. 核心注解:SpringBootApplication
    = SpringBootConfiguration(实现配置文件的功能)+EnableAutoConfiguration(实现自动配置的功能,也可关闭)+ComponentScan(组件扫描)
  3. 自动配置的核心
    EnableAutoConfiguration+Configuration+ConditionalOnClass
  4. 核心配置文件
    application(spring boot项目的 自动化配置文件)
    bootstrap
  5. 核心配置文件的格式
    properties
    yml:是一种可读的数据序列化语言,用于配置文件,与属性文件相比,yaml更加结构化,有分层配置数据,采用缩进的格式,不支持@PropertySource注解导入配置
  6. spring boot 内置了tomcat/jetty容器,不需要独立容器
  7. Sping Boot的监视器:actuator 帮助访问生产环境中正在运行的应用程序的当前状态
    该模块公开了一组可直接作为HTTP URL访问的REST断点来检查状态
  8. 运行Spring Boot方式
    1,打包命令式放到容器中运行
    2,Maven插件运行
    3,main()
  9. 读取配置文件内容的方式
    1,@Value(’${student.name}’)
    2,@ConfigurationProperties
    3,@PropertySource配合@Value
    4,2和3配合
    5,@Configuration配合@Value

Spring Cloud

  1. 理解:
    是一系列框架的有序集合,简化了分布式系统基础设施的开发,如服务注册与发现,消息总线,负载均衡,断路器,数据监控等,都可以做到一键启动和部署
  2. 断路器的作用:某个服务单元发生故障之后,通过断路器的故障监控,向调用方法返回一个故障响应,而不是长时间等待,即不因调用故障服务长时间占用而不释放,避免故障在分布式系统中蔓延
  3. 核心组件
    服务注册与发现Eureka
    Feign:基于动态代理机制,根据注解和选择的机器拼接请求url,发起请求
    Ribbon:负载均衡,从一个服务的多台机器中选择一台
    Hystrix:提供线程池,不同的服务走不同的线程池,实现不同服务调用的隔离,避免了服务雪崩的问题
    Zuul:网关管理,由Zuul网关转发请求给对应的服务
  4. Spring cloud中服务之间通过restful方式调用有两种:
    1,restTemplate+Riiboe
    2,Feign
  5. Ribbon
    服务间调用作负载,轮询请求实现均衡负载
    基于Http和Tcp客户端的负载均衡器,在客户端配置服务端列表
    Ribbon和Feign区别
    Feign本身包含Ribbon
    1,启动类注解不同:RibbonClient,EnableFeignClients
    2,服务的指定位置不同:Ribbon是在RibbonClient注解上声明
    Feign是在定义抽象方法的接口中使用@FeignClient声明
    3,调用方式不同:
    Ribbon需要自己构建http请求,模拟http请求使用RestTemplate发送给其他服务
    Feign则在Ribbon基础上做了改进,采用接口方式,将调用的其他服务的方法定义成抽象方法即可
    ribbon也是依赖于Eureka,获得各个服务地址
    Zuul:对外部请求做负载均衡
  6. 服务熔断3种状态
    关闭:默认,打开,半开
    关闭:请求次数异常超过设定比例,时,打开
    打开:打开时执行降级方法
    半开:定期尝试发起请求确认是否恢复,若yes,转为关闭或保持打开
  7. Zuul的作用
    类似于Nginx的网址重定向
    过滤请求
  8. Feign 调用服务默认时长是1s
  9. 服务熔断:当某个服务出现不可用或者响应超时时,为了防止整个系统出现雪崩,暂时停止对该服务的调用
  10. 服务降级:从整个系统的负荷情况触发,对某些负荷会比较高的情况,为了预防某些功能(业务场景)出现负荷过载或响应慢,在其内部暂时舍弃对一些非核心接口和数据的请求,而是直接返回一个提前准备好的的fallback错误处理信息,这样,虽然提供一个有损service,但却保证了整个系统的稳定和可用
    区别:从可用和可靠角度,都是为了防止系统崩溃
    不同,触发原因不同,前者是某个服务故障引起,后者是从整个系统的负荷考虑的
  11. 服务注册与发现
    注册:将服务信息注册到注册中心上
    发现:从注册中心上获取服务信息
    若没有Eureka,则需要把新的服务器地址配置到所有依赖于某模块的服务上,并相继重启他们
    目的:保证当某服务上下线发生变更时,服务的消费者和提供者能保证正常通信
    步骤
    1,启动注册中心
    2,启动会员服务
    3,当服务启动时,会把当前自己服务器的信息比如服务地址通讯地址(端口)等以别名的方式注册到注册中心
    4,另一方消费者以该别名的方式去注册中心上获取实际的通讯地址(获取rcp远程调用地址)
    5,如消费者获取实际RCP远程调用地址后,在本地使用httpClient技术实现调用
    Eureka每30s更新一次服务调用地址
  12. Spring Cloud支持三种注册中心:
    Eureka
    Zookeeper
    Consul go语言
    Dubbo支持两种:Redis和Zookeeper
  13. Eureka和Zookeeper的区别:
    Zookeeper保证了CP Eureka保证了AP,且能很好的应对因网络故障导致的部分节点失去联系的情况,而不会像Zookeeper使微服务瘫痪
    C:一致性 P:分区容错性 A:高可用性

MyBatis

由于面向接口编程的趋势,Mybatis实现了通过接口调用mapper配置文件中的sql语句

  1. Mapper接口没有实现的方法却可以使用?
    由于动态代理
  2. 工作流程:
    a. 读取配置文件,如数据库连接信息,mapper映射文件等
    b.创建SqlSessionFactory(程序级别),继而创建SqlSession(过程级别),目的是执行sql语句
    c.返回结果映射到java对象
  3. 接口绑定方式
    注解绑定,@Select,@Inset,@Update,Delete
    优点:适合简单,效率高
    缺点:sql有变化时需要重新编译
    xml里写sql绑定
  4. 缓存:是避免与数据库频繁交互
    1,一级缓存:同一个SqlSession中,Mybatis会把执行方法和参数通过算法生成缓存的键值,将键值及结果存放在一个Map中,如果后续的键值一样,则直接从map中获取数据
    2,不同的SqlSession之间的缓存时相互隔离的
    3,用一个SlSessIon,可通过配置使得再查询前清空缓存
    4,任何cud语句都会清空缓存
    5,二级缓存:在SqlSeesionFactory得生命周期中
    有全局和分开关
    全局的在mybatis-config.xml中,< setting>标签中,默认为true
    分开关在*Mapper.xml中,默认不开启
  5. 动态SQL:以便签的形式编写,根据不同的条件执行不行的sql语句 9种
    < if ,where,choose,when,otherwise,set,trim,foreach,bind>
    模糊查询也算一种’
  6. 参数占位符有两种:#{},${}
    ${} :字符串替换,有sql注入的危险
    #{}:通过?作为参数占位符,且预编译处理,可防止sql注入
  7. 模糊查询的3种方式
    1,拼接%
    2,concat()字符串拼接
    3,bind标签
    < bind name ="||" value="%"+number+"%"/>
    select * from orders where number like #{||}
  8. sql注入:将sql代码添加到输入参数种,传递到sql服务器解析并执行的一种攻击手法
    如何执行:数字注入,where id = -1 or 1=1
    字符串注入:如以sql中的#注释符来攻击,或— 注释符
    如何让防止:严格检查输入变量类型和格式
    如数字的强校验,字符串中的正则校验,如过滤和转义特殊字符,如单引,双引,反斜杠,null等
  9. 在mapper中如何传递多个参数:
    1,Dao层的函数,无注解形式,直接写#{0},#{1}
    2,@Param注解
    3,多个参数封装成Map
  10. **一对多(多对一)的几种方式:**多有两种,只不过是配置标签不同
    联合查询和嵌套查询
    联合:几个表联合查询,只查询一次,在resultMap里配置
    嵌套:先查询一个表,根据这个表结果的外键id,再去另外一个表查询数据,在xml中的sql语句中使用select属性,