Java面试题

java面试题

Java基础

java虚拟机

Java虚拟机是一个可以执行Java字节码的虚拟机进程。Java源文件被编译成能被Java虚拟机执行的字节码文件。 Java被设计成允许应用程序可以运行在任意的平台,而不需要程序员为每一个平台单独重写或者是重新编译。

JVM

什么是类的加载

将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对象,用来封装类在方法区内的数据结构。
类的加载的最终产品是位于堆区中的Class对象,Class对象封装了类在方法区内的数据结构,并且向Java程序员提供了访问方法区内的数据结构的接口。
类加载器:
在这里插入图片描述

类的生命周期

类的生命周期:
包括这几个部分,加载、连接、初始化、使用和卸载,其中前三部是类的加载的过程,如下图;
在这里插入图片描述

加载查找并加载类的二进制数据,在Java堆中也创建一个java.lang.Class类的对象
连接连接又包含三块内容:验证、准备、初始化。 1)验证,文件格式、元数据、字节码、符号引用验证; 2)准备,为类的静态变量分配内存,并将其初始化为默认值; 3)解析,把类中的符号引用转换为直接引用
初始化为类的静态变量赋予正确的初始值
使用new出对象程序中使用
卸载执行垃圾回收

简述java类加载机制?

虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验,解析和初始化,最终形成可以被虚拟机直接使用的java类型。

什么是栈

栈: 是一种数据项按序排列的数据结构,只能在一端(称为栈顶(top))对数据项进行插入和删除。
分为:压栈(入栈) 和 弹栈(出栈)
遵循原则:后进先出(Last-In/First-Out)
在这里插入图片描述

多态

多态是继封装、继承之后,面向对象的第三大特性
1. 多态:
多态是同一个行为具有多个不同表现形式或形态的能力。如Student类继承了Person类,一个Student的对象便既是Student,具有Student的行为方式,又是Person,具有Person共同的行为方式。
表现形式:多态体现为父类引用变量可以指向子类对象
1.1 理解:
多态是同一个行为具有多个不同表现形式或形态的能力。
多态就是同一个接口,使用不同的实例而执行不同操作。
2. 多态中成员的特点
多态成员变量:编译运行看左边
Fu f=new Zi();
System.out.println(f.num);//f是Fu中的值,只能取到父中的值
2.1 多态成员方法:编译看左边,运行看右边
Fu f1=new Zi();
System.out.println(f1.show());//f1的门面类型是Fu,但实际类型是Zi,所以调用的是重写后的方法。
3. instanceof关键字
作用:用来判断某个对象是否属于某种数据类型。
注意: 返回类型为布尔类型
例如:

Fu f1=new Zi();
        Fu f2=new Son();
        if(f1 instanceof Zi){
            System.out.println("f1是Zi的类型");
        }
        else{
            System.out.println("f1是Son的类型");
        }

4. 多态的转型
多态的转型分为向上转型和向下转型两种
4.1 向上转型:
多态本身就是向上转型过的过程
使用格式:父类类型 变量名=new 子类类型();
适用场景:当不需要面对子类类型时,通过提高扩展性,或者使用父类的功能就能完成相应的操作。

4.2 向下转型:
一个已经向上转型的子类对象可以使用强制类型转换的格式,将父类引用类型转为子类引用各类型
使用格式:子类类型 变量名=(子类类型) 父类类型的变量;

public class demo04 {
    public static void main(String[] args) {
        People p=new Stu();
        p.eat();
        //调用特有的方法
        Stu s=(Stu)p;
        s.study();
        //((Stu) p).study();
    }
}
class People{
    public void eat(){
        System.out.println("吃饭");
    }
}
class Stu extends People{
    @Override
    public void eat(){
        System.out.println("吃水煮肉片");
    }
    public void study(){
        System.out.println("好好学习");
    }
}
class Teachers extends People{
    @Override
    public void eat(){
        System.out.println("吃樱桃");
    }
    public void teach(){
        System.out.println("认真授课");
    }
}

构造方法

作用:
1. 构造方法作用就是对类进行初始化。
2. 为对象成员变量赋初始值

public class Test_Constructor1 {

 private String name;
 private String gender;
 private long ID;
 private long birthday;

 Test_Constructor1(String name, String gender, long ID, long birthday) {
  this.name = name;
  this.gender = gender;
  this.ID = ID;
  this.birthday = birthday;
 }
 public static void main(String[] args) {
  Test_Constructor1 s = new Test_Constructor1("Lily", "女", 100001, 200000226);
  System.out.println("姓名:" + s.name +  " 性别:" + s.gender + " 学号:" + s.ID + " 生日:" + s.birthday);
 }
}

接口 、抽象类

抽象:抽象是从众多的事物中抽取出共同的、本质性的特征。
区别一
抽象类中可以存在非抽象的方法。
接口中的方法被默认的变成抽象方法,只要是定义了接口,接口中的方法 就全部变成了抽象类即使你不写 abstract 它也是抽象的方法
区别二
实现抽象类的方法时, 如果方法是抽象的,子类必须重写抽象的方法. 如果方法不是抽象的, 子类可以选择继承
实现了接口 就必须实现接口中的所有方法, 因为接口中的方法默认的全部都是抽象的方法
--------------所以这里可以说, 接口是抽象类的一种, 也可以说接口是抽象类的延伸
区别三
抽象类可以有私有的成员变量和成员方法
接口中的方法全都被默认的修饰为: public abstract 类型的方法
区别四
一个类只能继承一个抽象类
一个类可以实现多个接口 ,接口可以实现多继承
举例:interface display extends aa ,bb,cc …等等 然后让类去实现 display的接口 就可以实现 display aa bb cc接口
区别五
抽象类中的非抽象的方法可以选择继承
接口中的方法必须全部被重写 ,并且全部是公有的public 方法.

String

1. 对于基本数据类型来说,比较的是值;对于引用类型,比较的是地址。

public class Main {
 public static void main(String[] args) {
//== 测试
		String a="aaa";
		String b="aaa";
		System.out.println(a==b);  //ture
		String c=new String("aaa");
		String d=new String("aaa");
		String f=new String("aaa");
		System.out.println(c==d);   //false
		c=f;
		d=f;
		System.out.println(c==d);//true
		}
}	
第一次比较结果为true,是因为变量a和变量b都是基本数据类型,比较的是值。
第二次比较为false,是因为这次变量c,d为引用数据类型,比较的是地址。
第三次比较又变成了true,是因为c,d的地址指向一样,都为f的地址。	

2. equals方法是基类Object中的方法,因此对于所有的继承于Object的类都会有该方法。
equals方法不能作用于基本数据类型的变量。

String不是基本数据类型,是一个类,与Integer,Double等类一致,属于引用数据类型。

注意: 如果没有对equals方法进行重写,则比较的是引用类型的变量所指向的对象的地址;
诸如String、Date等类对equals方法进行了重写的话,比较的是所指向的对象的内容。
所以 如果比较内容是否相等,需要重写equals方法

3. 为什么要重写hashcode和equals方法?

1. 重写hashcode是为了保证相同的对象会有相同的hashcode;
2. 重写equals是为了保证在发生冲突的情况下取得到Entry对象(也可以理解是key或是元素);

java 中操作字符串都有哪些类?它们之间有什么区别?

String、StringBuffer、StringBuilder

1 String : final修饰,String类的方法都是返回new String。即对String对象的任何改变都不影响到原对象,对字符串的修改操作都会生成新的对象。

2 StringBuffer : 对字符串的操作的方法都加了synchronized,保证线程安全。

3 StringBuilder : 不保证线程安全,在方法体内需要进行字符串的修改操作,可以new StringBuilder对象,调用StringBuilder对象的append、replace、delete等方法修改字符串。

数组

Java中最基本的数据结构,提供动态创建和访问java数组的方法,根据定义的Array类型,其中的元素与类型必须相同。

集合(容器)

List、ArrayList、LinkedList 的区别

1 List: 是一个接口,它继承与Collection接口,代表有序的队列。

2 ArrayList: 动态数组, 采用的也是线性连续空间来存放元素,当线性连续空间不足以存放元素时,又重新申请一片更大的空间(大约是原空间的2倍),将原有的内容移过去。

**缺点:**当向集合插入不同类型的数据后(ArrayList将数据当作object存储),在进行数据处理时容易出现类型不匹配的错误,使用时需要进行类型转换处理,存在装箱与拆箱操作,造成性能大量损耗的现象。

3 LinkedList: 基于链表的数据结构,添加,删除操作速度更快,因为当元素被添加到集合任意位置的时候,不需要像数组那样重新计算大小或者是更新索引。

4 LinkedList比ArrayList更占内存,因为LinkedList为每一个节点存储了两个引用,一个指向前一个元素,一个指向下一个元素。

HashMap、Hashtable、LinkedHashMap、TreeMap、CuncurrentHashMap

1 HashMap: 根据键的hashCode值存储数据,根据键可以直接获取它的值,最多只允许一条记录的键为null,不支持线程的同步。
如果:
需要同步,可以用Collections.synchronizedMap(HashMap map)方法使HashMap具有同步的能力。

2 Hashtable 与HashMap类似,支持线程的同步。
即:任一时刻只有一个线程能写Hashtable。

3 LinkedHashMap 保存了记录的插入顺序,在用Iteraor遍历LinkedHashMap时,先得到的记录肯定是先插入的。在遍历的时候会比HashMap慢。有HashMap的全部特性。

4 TreeMap 保存的记录根据键排序,默认是按升序排序,也可以指定排序的比较器。当用Iteraor遍历TreeMap时,得到的记录是排过序的。

5 CuncurrentHashMap 由Segment数组结构和HashEntry数组结构组成。

Segment是一种可重入锁ReentrantLock,在ConcurrentHashMap里扮演锁的角色,HashEntry则用于存储键值对数据。

一个ConcurrentHashMap里包含一个Segment数组,Segment的结构和HashMap类似,是一种数组和链表结构, 一个Segment里包含一个HashEntry数组,每个HashEntry是一个链表结构的元素, 每个Segment守护者一个HashEntry数组里的元素,当对HashEntry数组的数据进行修改时,必须首先获得它对应的Segment锁。

队列

常用的阻塞队列有哪些?

1 ArrayBlockingQueue:

queue

线程

创建线程的三种方式

1. 继承Thread类创建线程类

定义Thread类的子类,并重写该类的run方法,该run方法的方法体就代表了线程要完成的任务。因此把run()方法称为执行体。
创建Thread子类的实例,即创建了线程对象。
调用线程对象的start()方法来启动该线程。

2. 通过Runnable接口创建线程类

定义runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。
创建 Runnable实现类的实例,并依此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。
调用线程对象的start()方法来启动该线程。

3. 通过Callable和Future创建线程

创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,并且有返回值。
创建Callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值。

使用FutureTask对象作为Thread对象的target创建并启动新线程。
调用FutureTask对象的get()方法来获得子线程执行结束后的返回值。

sleep() 和 wait() 有什么区别?

sleep():

方法是线程类(Thread)的静态方法,让调用线程进入睡眠状态,让出执行机会给其他线程,等到休眠时间结束后,线程进入就绪状态和其他线程一起竞争cpu的执行时间。因为sleep() 是static静态的方法,他不能改变对象的机锁,当一个synchronized块中调用了sleep() 方法,线程虽然进入休眠,但是对象的机锁没有被释放,其他线程依然无法访问这个对象。

wait():

wait()是Object类的方法,当一个线程执行到wait方法时,它就进入到一个和该对象相关的等待池,同时释放对象的机锁,使得其他线程能够访问,可以通过notify,notifyAll方法来唤醒等待的线程。

守护线程是什么?

notify()和 notifyAll()有什么区别?

如果线程调用了对象的 wait()方法,那么线程便会处于该对象的等待池中,等待池中的线程不会去竞争该对象的锁。

当有线程调用了对象的 notifyAll()方法(唤醒所有 wait 线程)或 notify()方法(只随机唤醒一个 wait 线程),被唤醒的的线程便会进入该对象的锁池中,锁池中的线程会去竞争该对象锁。也就是说,调用了notify后只要一个线程会由等待池进入锁池,而notifyAll会将该对象等待池内的所有线程移动到锁池中,等待锁竞争。

优先级高的线程竞争到对象锁的概率大,假若某线程没有竞争到该对象锁,它还会留在锁池中,唯有线程再次调用 wait()方法,它才会重新回到等待池中。而竞争到对象锁的线程则继续往下执行,直到执行完了 synchronized 代码块,它会释放掉该对象锁,这时锁池中的线程会继续竞争该对象锁。

线程的 run()和 start()有什么区别?

每个线程都是通过某个特定Thread对象所对应的方法run()来完成其操作的,方法run()称为线程体。通过调用Thread类的start()方法来启动一个线程。

start()方法来启动一个线程,真正实现了多线程运行。这时无需等待run方法体代码执行完毕,可以直接继续执行下面的代码; 这时此线程是处于就绪状态, 并没有运行。 然后通过此Thread类调用方法run()来完成其运行状态, 这里方法run()称为线程体,它包含了要执行的这个线程的内容, Run方法运行结束, 此线程终止。然后CPU再调度其它线程。

run()方法是在本线程里的,只是线程里的一个函数,而不是多线程的。 如果直接调用run(),其实就相当于是调用了一个普通函数而已,直接待用run()方法必须等待run()方法执行完毕才能执行下面的代码,所以执行路径还是只有一条,根本就没有线程的特征,所以在多线程执行时要使用start()方法而不是run()方法。

创建线/线程池程池有哪几种方式?

1. newFixedThreadPool(int nThreads)

创建一个固定长度的线程池,每当提交一个任务就创建一个线程,直到达到线程池的最大数量,这时线程规模将不再变化,当线程发生未预期的错误而结束时,线程池会补充一个新的线程。

2. newCachedThreadPool()

创建一个可缓存的线程池,如果线程池的规模超过了处理需求,将自动回收空闲线程,而当需求增加时,则可以自动添加新线程,线程池的规模不存在任何限制。

3. newSingleThreadExecutor()

这是一个单线程的Executor,它创建单个工作线程来执行任务,如果这个线程异常结束,会创建一个新的来替代它;它的特点是能确保依照任务在队列中的顺序来串行执行。

4. newScheduledThreadPool(int corePoolSize)

创建了一个固定长度的线程池,而且以延迟或定时的方式来执行任务,类似于Timer。

synchronized 、 volatile、Lock 和 ReentrantLock 的区别是什么?.

synchronized 同步锁
防止被多个线程同时执行,保证了在同一时刻,只能有一个线程执行同步代码块,所以执行同步代码块的时候相当于是单线程操作。
线程的可见性、原子性、有序性(线程之间的执行顺序)它都能保证了。

volatile java虚拟机提供的最轻量级的同步机制。
被volatile修饰的变量能够保证每个线程能够获取该变量的最新值,从而避免出现数据脏读的现象。

Lock 底层是CAS乐观锁,依赖AbstractQueuedSynchronizer(AQS)类,把所有的请求线程构成一个CLH队列(FIFO的双向双端队列),而对该队列的操作均通过Lock-Free(CAS)操作。
ReenTrantLock 可重入锁 , 是Lock接口的实现类

反射

什么是反射

反射机制:是在运行时,
1. 对于任意一个类,都能够知道这个类的所有属性和方法;
2. 对于任意个对象,都能够调用它的任意一个方法。
在java中,只要给定类的名字,就可以通过反射机制来获得类的所有信息。

Java反射机制的作用

  1. 在运行时判断任意一个对象所属的类
  2. 在运行时构造任意一个类的对象
  3. 在运行时判断任意一个类所具有的成员变量和方法
  4. 在运行时调用任意一个对象的方法

Java反射API有几类?

答:反射 API 用来生成 JVM 中的类、接口或则对象的信息。

1 Class 类: 反射的核心类,可以获取类的属性,方法等信息。
2 Field 类: Java.lang.reflec 包中的类,表示类的成员变量,可以用来获取和设置类之中的属性值。
3 Method 类: Java.lang.reflec 包中的类,表示类的方法,它可以用来获取类中的方法信息或者执行方法。
4 Constructor 类: Java.lang.reflec 包中的类,表示类的构造方法。

IO

InputStream类: 是字节输入流的抽象类,是所有字节输入流的父类,InputStream类具有层次结构如下图所示;
在这里插入图片描述
Reader类: 是字符输入流的抽象类,所有字符输入流的实现都是它的子类
在这里插入图片描述
输出流OutputStream类: 是字节输入流的抽象类,此抽象类表示输出字节流的所有类的超类。
在这里插入图片描述
Writer类: 是字符输出流的抽象类,所有字符输出类的实现都是它的子类。

在这里插入图片描述
File类: 是IO包中唯一代表磁盘文件本身的对象。通过File来创建,删除,重命名文件。File类对象的主要作用就是用来获取文本本身的一些信息。如文本的所在的目录,文件的长度,读写权限等等。

Http

简述下TCP三次握手的过程,并解释采用3次握手建立连接的原因?

在这里插入图片描述

  1. 客户端发送建立连接请求,携带信息syn = 1,seq =X --第一次握手

  2. 服务端在收到建立连接的请求之后,服务回复确认建立连接,携带信息:syn = 1,ack = X+1, seq =Y --第二次握手(服务端准备好了)

  3. 客户端在收到服务端的确认信息后,向服务端回复确认连接建立通知,携带信息:ack = Y+1, seq=X+1(客户端也准备 好了)

这样建立连接的原因:

具体例子:“已失效的连接请求报文段”的产生在这样一种情况下:client发出的第一个连接请求报文段并没有丢失,而是在某个网络结点长时间的滞留了,以致延误到连接释放以后的某个时间才到达server。本来这是一个早已失效的报文段。但server收到此失效的连接请求报文段后,就误认为是client再次发出的一个新的连接请求。于是就向client发出确认报文段,同意建立连接。假设不采用“三次握手”,那么只要server发出确认,新的连接就建立了。由于现在client并没有发出建立连接的请求,因此不会理睬server的确认,也不会向server发送数据。但server却以为新的运输连接已经建立,并一直等待client发来数据。这样,server的很多资源就白白浪费掉了。采用“三次握手”的办法可以防止上述现象发生。例如刚才那种情况,client不会向server的确认发出确认。server由于收不到确认,就知道client并没有要求建立连接。”

TCP连接断开的过程?为什么要进行四次挥手?

在这里插入图片描述

  1. 客户端发送个FIN报文,请求关闭连接

  2. 服务端回复:收到关闭连接请求

  3. 服务发送FIN报文,告知客户端服务端将关闭连接

  4. 客服端回复,收到你的通知。

原因:连接建立后是双向的,必须双方同意才能断开。

Java 调度器 、定时器

java实现任务调度,主要分为几个步骤:
1.创建一个服务
2.创建一个任务类,将服务作为一个任务去完成(实现job接口)
3.创建一个任务类实体
4.创建一个触发器,指定触发规则
5.创建一个调度,绑定任务和触发器
参考 :Java调度

Timer定时器的四种使用方法

import java.time.LocalDateTime;
import java.util.Calendar;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
 
public class TimeTest {
    public static void main(String[] args) {
        timer1();
        //timer2();
        //timer3();
        //timer4();
    }
 
    // 第一种方法:设定指定任务task在指定时间time执行 schedule(TimerTask task, Date time)
    public static void timer1() {
        Timer timer = new Timer();
        Calendar calendar = Calendar.getInstance();
        calendar.set(2020, 11, 05, 16, 05, 20);
        timer.schedule(new TimerTask() {
            public void run() {
                System.out.println("-------定时任务执行,时间:--------" + LocalDateTime.now());
            }
        }, calendar.getTime());
        calendar.set(2020, 11, 05, 16, 05, 30);
        timer.schedule(new TimerTask() {
            public void run() {
                System.out.println("-------定时任务执行,时间:--------" + LocalDateTime.now());
            }
        }, calendar.getTime());
        calendar.set(2020, 11, 05, 16, 05, 40);
        timer.schedule(new TimerTask() {
            public void run() {
                System.out.println("-------定时任务执行,时间:--------" + LocalDateTime.now());
            }
        }, calendar.getTime());
    }
 
    // 第二种方法:设定指定任务task在指定延迟delay后进行固定延迟peroid的执行
    // schedule(TimerTask task, long delay, long period)
    public static void timer2() {
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            public void run() {
                System.out.println("-------设定要指定任务--------");
            }
        }, 1000, 5000);
    }
 
    // 第三种方法:设定指定任务task在指定延迟delay后进行固定频率peroid的执行。
    // scheduleAtFixedRate(TimerTask task, long delay, long period)
    public static void timer3() {
        Timer timer = new Timer();
        timer.scheduleAtFixedRate(new TimerTask() {
            public void run() {
                System.out.println("-------设定要指定任务--------");
            }
        }, 1000, 2000);
    }
 
    // 第四种方法:安排指定的任务task在指定的时间firstTime开始进行重复的固定速率period执行.
    // Timer.scheduleAtFixedRate(TimerTask task,Date firstTime,long period)
    public static void timer4() {
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.HOUR_OF_DAY, 12); // 控制时
        calendar.set(Calendar.MINUTE, 0);    // 控制分
        calendar.set(Calendar.SECOND, 0);    // 控制秒
 
        Date time = calendar.getTime();     // 得出执行任务的时间,此处为今天的12:00:00
 
        Timer timer = new Timer();
        timer.scheduleAtFixedRate(new TimerTask() {
            public void run() {
                System.out.println("-------设定要指定任务--------");
            }
        }, time, 1000 * 60 * 60 * 24);// 这里设定将延时每天固定执行
    }
}

java设计模式

常用对10种设计模式

详细参考:java设计模式

动态代理是什么?有哪些应用?动态代理的两种实现方式

不改变源码的基础上,对已有方法增强,
1.JDK动态代理: 要求被代理类至少实现一个接口
2.CGLib动态代理: 基于子类的动态代理(被代理的类不能是最终类(Final修饰的))

容器

Servlet

什么是Servlet?

Servlet是用来处理客户端请求并产生动态网页内容的Java类。Servlet主要是用来处理或者是存储HTML表单提交的数据,产生动态内容,在无状态的HTTP协议下管理状态信息

转发与重定向

forward: 是服务器请求资源,服务器直接访问目标地址的URL,把那个URL的响应内容读取过来,然后把这些内容再发给浏览器,其实客户端浏览器只发了一次请求,所以它的地址栏中还是原来的地址,session,request参数都可以获取。

redirect: 就是服务端根据逻辑,发送一个状态码,告诉浏览器重新去请求那个地址,相当于客户端浏览器发送了两次请求。

什么情况下调用doGet()和doPost()?

JSP页面中的form标签里的method属性为get时调用doGet(),为post时调用doPost();超链接跳转页面时调用doGet()

servlet的生命周期

web容器加载servlet,
生命周期开始。通过调用servlet的init()方法进行servlet的初始化。
通过调用service()方法实现,
根据请求的不同调用不同的do***()方法。
结束服务,web容器调用servlet的destroy()方法。

jsp 和 servlet 有什么区别?

JSP是Servlet技术的扩展,本质上是Servlet的简易方式,更强调应用的外表表达。JSP编译后是"类servlet"。Servlet和JSP最主要的不同点在于,Servlet的应用逻辑是在Java文件中,并且完全从表示层中的HTML里分离开来。而JSP的情况是Java和HTML可以组合成一个扩展名为.jsp的文件。JSP侧重于视图,Servlet主要用于控制逻辑。
详细参考:Jsp与Servlet

doGet 和 doPost 有什么区别?

doGet: 处理get请求
通过get方式提交的数据: 有大小的限制,通常在1024字节左右。
通过get传递数据: 将传递的数据按照”key,value”的方式跟在URL的后面来达到传送的目的的
post方式: 没有数据大小的限制
post传递数据: 是通过http请求的附件进行的,在URL中并没有明文显示

Spring

配置方式

基于XML的配置
基于注解的配置
基于Java的配置

spring事务管理

原子性(Atomicity): 事务是最小的执行单位,不允许分割。事务的原子性确保动作要么都成功,要么都失败。

一致性(Consistency): 执行事务前后,数据保持一致,多个事务对同一个数据读取的结果是相同的;

隔离性(Isolation): 并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的

持久性(Durability): 一个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有任何影响。

Spring管理事务有几种方式?

实现方式:
有两种方式:

1、 编程式事务,在代码中硬编码。(不推荐使用)
2、 声明式事务,在配置文件中配置(推荐使用)

声明式事务又分为两种:
a、 基于XML的声明式事务
b、 基于注解的声明式事务

Spring事务隔离级别:

READ-UNCOMMITTED(读取未提交): 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。
READ-COMMITTED(读取已提交): 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生。
REPEATABLE-READ(可重复读): 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
SERIALIZABLE(可串行化): 最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。

幻读: 同样的事务操作过程中,不同时间段多次(不同事务)读取同一数据,读取到的内容不一致(一般是行数变多或变少)。
脏读: 一个事务读取到另外一个未提及事务的内容,即为脏读。
不可重复读: 同一事务中,多次读取内容不一致(一般行数不变,而内容变了)。

spring有哪些模块组成

Spring Core: 核心类库,提供IOC服务;
Spring Context: 提供框架式的Bean访问方式,以及企业级功能(JNDI、定时任务等);
Spring AOP: AOP服务;
Spring DAO: 对JDBC的抽象,简化了数据访问异常的处理;
Spring ORM: 对现有的ORM框架的支持;
Spring Web: 提供了基本的面向Web的综合特性,例如多方文件上传;
Spring MVC: 提供面向Web应用的Model-View-Controller实现。

IOC 与 DI

IOC:控制反转: 把创建对象的控制权利由代码转移到spring的配置文件中。
DI:依赖注入: 在程序运行期间,由外部容器动态地将依赖对象注入到组件中。
注入的方式:
构造注入
Set注入
接口注入

AOP

AOP: 不修改源代码的前提下,对原有方法进行增强逻辑处理。
实现方式: 动态代理(JDK动态代理、CGLib 动态代理)
应用场景:
1. Dao层处 处理DB 查询的重复代码片段,提高代码复用性等等
2. 处理给系统某个操作 进行日志操作 如增删改查等
3. 单点登录时,在登录前,进行权限认证、授权等操作等等
通过工厂模式 实现解耦.

数据库

常见攻击方式

一般说来,在Web安全领域,常见的攻击方式大概有以下几种:
1、SQL注入攻击
2、跨站脚本攻击 - XSS
3、跨站伪造请求攻击 - CSRF
4、文件上传漏洞攻击
5、分布式拒绝服务攻击 - DDOS

如何避免SQl注入

1. 代码层防止sql注入攻击的最佳方案就是sql预编译

public List<Course> orderList(String studentId){
    String sql = "select id,course_id,student_id,status from course where student_id = ?";
    return jdbcTemplate.query(sql,new Object[]{studentId},new BeanPropertyRowMapper(Course.class));
}

2 对特殊的字符进行转义或字符串过滤: 数字型注入可以通过检查数据类型防止,但是字符型不可以,那么怎么办呢,最好的办法就是对特殊的字符进行转义了。
比如在MySQL中我们可以对" ’ "进行转义,这样就防止了一些恶意攻击者来闭合语句。当然我们也可以通过一些安全函数来转义特殊字符。如addslashes()等,但是这些函数并非一劳永逸,攻击者还可以通过一些特殊的方式绕过。

public static boolean sql_inj(String str){
	String inj_str = "'|and|exec|insert|select|delete|update| 
	count|*|%|chr|mid|master|truncate|char|declare|;|or|-|+|,"; 
	String inj_stra[] = split(inj_str,"|"); 
	for (int i=0 ; i &lt; inj_stra.length ; i++ ){ 
		if (str.indexOf(inj_stra[i])&gt;=0){ 
			return true;
		}
	}
	return false; 
}

3 使用正则表达式过滤传入的参数:
要引入的包:

import java.util.regex.*;

正则表达式:

private String CHECKSQL =^(.+)\\sand\\s(.+)|(.+)\\sor(.+)\\s$”;

判断是否匹配:

Pattern.matches(CHECKSQL,targerStr);

MySQL

索引有哪几种类型?

主键索引: 数据列不允许重复,不允许为NULL,一个表只能有一个主键。

唯一索引: 数据列不允许重复,允许为NULL值,一个表允许多个列创建唯一索引。

可以通过 ALTER TABLE table_name ADD UNIQUE (column);创建唯一索引

可以通过 ALTER TABLE table_name ADD UNIQUE (column1,column2); 创建唯一组合索引

普通索引: 基本的索引类型,没有唯一性的限制,允许为NULL值。

可以通过ALTER TABLE table_name ADD INDEX index_name (column);创建普通索引

可以通过ALTER TABLE table_name ADD INDEX index_name(column1, column2, column3);创建组合索引

创建索引的原则

索引虽好,但也不是无限制的使用,最好符合一下几个 原则:

1) 最左前缀匹配原则,组合索引非常重要的原则,mysql会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配,比如a = 1 and b = 2 and c > 3 and d = 4 如果建立(a,b,c,d)顺序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引则都可以用到,a,b,d的顺序可以任意调整。
2)较频繁作为查询条件的字段才去创建索引
3)更新频繁字段不适合创建索引
4)若是不能有效区分数据的列不适合做索引列(如性别,男女未知,最多也就三种,区分度实在太低)
5)尽量的扩展索引,不要新建索引。比如表中已经有a的索引,现在要加(a,b)的索引,那么只需要修改原来的索引即可。
6)定义有外键的数据列一定要建立索引。
7)对于那些查询中很少涉及的列,重复值比较多的列不要建立索引。
8)对于定义为text、image和bit的数据类型的列不要建立索引。

MySQL中都有哪些触发器?

在MySQL数据库中有如下六种触发器:

Before Insert
After Insert
Before Update
After Update
Before Delete
After Delete

大表数据查询,怎么优化

1 优化shema、sql语句+索引;
2 第二加缓存,memcached, redis;
3 主从复制,读写分离;
4 优化长难的查询语句 :将一个大的查询分为多个小的相同的查询
5 分解关联查询: 让缓存的效率更高。
6 较少冗余记录的查询
7 使用缓存 将查询结果添加到Redis等缓存中
8 优化关联查询:
确定ON或者USING子句中是否有索引。
确保GROUP BY和ORDER BY只有一个表中的列,这样MySQL才有可能使用索引。
9 优化子查询: 用关联查询替代,或者可以使用索引来优化,是最有效的优化方法
10 优化LIMIT分页:
. LIMIT偏移量大的时候,查询效率较低
可以记录上次查询的最大ID,下次查询时直接根据该ID来查询

数据库结构优化

一个好的数据库设计方案对于数据库的性能往往会起到事半功倍的效果。
需要考虑数据冗余查询更新的速度字段的数据类型是否合理等多方面的内容。
将字段很多的表分解成多个表
对于字段较多的表,如果有些字段的使用频率很低,可以将这些字段分离出来形成新表。
因为当一个表的数据量很大时,会由于使用频率低的字段的存在而变慢。
增加中间表
对于需要经常联合查询的表,可以建立中间表以提高查询效率。
通过建立中间表,将需要通过联合查询的数据插入到中间表中,然后将原来的联合查询改为对中间表的查询。
增加冗余字段
设计数据表时应尽量遵循范式理论的规约,尽可能的减少冗余字段,让数据库设计看起来精致、优雅。但是,合理的加入冗余字段可以提高查询速度。

存储过程

格式

CREATE OR REPLACE PROCEDURE 存储过程名字
(
    参数1 IN NUMBER,
    参数2 IN NUMBER
) IS
变量1 INTEGER :=0;
变量2 DATE;
BEGIN
END 存储过程名字

Oracle

数据库的三大范式是什么?

**1)第一范式:**原子件,要求每一列的值不能再拆分了
2)第二范式: 一张表只描述一个实体(若列中有冗余数据,则不满足)
3)第三范式: 所有列与主键直接相关

Oracle 分区

Oracle表空间

Oracle的表空间属于Oracle中的存储结构,是一种用于存储数据库对象(如:数据文件)的逻辑空间,是Oracle中信息存储的最大逻辑单元,其下还包含有段、区、数据块等逻辑数据类型。表空间是在数据库中开辟的一个空间,用于存放数据库的对象,一个数据库可以由多个表空间组成。可以通过表空间来实现对Oracle的调优。(Oracle数据库独特的高级应用)

表空间的分类

永久表空间: 存储数据库中需要永久化存储的对象,比如二维表、视图、存储过程、索引。
临时表空间: 存储数据库的中间执行过程,如:保存order by数据库排序,分组时产生的临时数据。操作完成后存储的内容会被自动释放。临时表空间是通用的,所的用户都使用TEMP作为临时表空间。一般只有temp一个临时表空间,如果还需要别的临时表空间时,可以自己创建。
UNDO表空间: 保存数据修改前的副本。存储事务所修改的旧址,即被修改之前的数据。当我们对一张表中的数据进行修改的同时会对修改之前的信息进行保存,为了对数据执行回滚、恢复、撤销的操作。

临时表空间

临时数据产生后Oracle数据库会先将这些存放到内存的PGA)内的sor_area排序区(SORT_AREA_SIZE参数)的地方,专门用来存放这些因为排序操作而产生的临时数据。但是这个分区的容量是有限的。当这个分区的大小不足以容纳排序后所产生的记录时,数据库系统就会将临时数据存放到临时表空间中和重做日志与归档的关系类似

临时表空间作用

用来做查询和存放一些缓冲区数据,或 在数据库进行排序运算、管理索引、访问视图等操作时提供临时的运算空间,当运算完成之后系统会自动清理。

PL/SQL编程

参考 : PL/SQL编程

持久化工具

Mybatis

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

KaTeX parse error: Expected 'EOF', got '#' at position 10: {}是字符串替换,#̲{}是预处理; Mybatis…{}时,就是把${}直接替换成变量的值。而Mybatis在处理#{}时,会对sql语句进行预处理,将sql中的#{}替换为?号,调用PreparedStatement的set方法来赋值;

Mybatis是否支持延迟加载?如果支持,它的实现原理是什么?

Mybatis仅支持association关联对象和collection关联集合对象的延迟加载,association指的就是一对一,collection指的就是一对多查询。在Mybatis配置文件中,可以配置是否启用延迟加载lazyLoadingEnabled=true|false。

延迟加载的基本原理是:
使用CGLIB创建目标对象的代理对象,当调用目标方法时,进入拦截器方法,比如调用a.getB().getName(),拦截器invoke()方法发现a.getB()是null值,那么就会单独发送事先保存好的查询关联B对象的sql,把B查询上来,然后调用a.setB(b),于是a的对象b属性就有值了,接着完成a.getB().getName()方法的调用。

Mybatis是如何将sql执行结果封装为目标对象并返回的?都有哪些映射形式?

第一种: 是使用标签,逐一定义数据库列名和对象属性名之间的映射关系。
第二种: 是使用sql列的别名功能,将列的别名书写为对象属性名。
有了列名与属性名的映射关系后,Mybatis通过反射创建对象,同时使用反射给对象的属性逐一赋值并返回,那些找不到映射关系的属性,是无法完成赋值的。

当实体类中的属性名和表中的字段名不一样 ,怎么办 ?

第1种: 通过在查询的sql语句中定义字段名的别名,让字段名的别名和实体类的属性名一致。

<select id=”selectorder” parametertype=int” resultetype=”me.gacl.domain.order”>
       select order_id id, order_no orderno ,order_price price form orders where order_id=#{id};
    </select>

第2种: 通过来映射字段名和实体类属性名的一一对应的关系。

<select id="getOrder" parameterType="int" resultMap="orderresultmap">
        select * from orders where order_id=#{id}
    </select>
 
   <resultMap type=”me.gacl.domain.order” id=”orderresultmap”>
        <!–用id属性来映射主键字段–>
        <id property=”id” column=”order_id”>
 
        <!–用result属性来映射非主键字段,property为实体类属性名,column为数据表中的属性–>
        <result property = “orderno” column =”order_no”/>
        <result property=”price” column=”order_price” />
    </reslutMap>

在mapper中如何传递多个参数?

1)第一种:
//DAO层的函数
Public UserselectUser(String name,String area);  
//对应的xml,#{0}代表接收的是dao层中的第一个参数,#{1}代表dao层中第二参数,更多参数一致往后加即可。
<select id="selectUser"resultMap="BaseResultMap">  
    select *  fromuser_user_t   whereuser_name = #{0} anduser_area=#{1}  
</select>2)第二种: 使用 @param 注解:
public interface usermapper {
   user selectuser(@param(“username”) string username,@param(“hashedpassword”) string hashedpassword);
}
然后,就可以在xml像下面这样使用(推荐封装为一个map,作为单个参数传递给mapper):
<select id=”selectuser” resulttype=”user”>
         select id, username, hashedpassword
         from some_table
         where username = #{username}
         and hashedpassword = #{hashedpassword}
</select>3)第三种:多个参数封装成map
try{
//映射文件的命名空间.SQL片段的ID,就可以调用对应的映射文件中的SQL
//由于我们的参数超过了两个,而方法中只有一个Object参数收集,因此我们使用Map集合来装载我们的参数
Map<String, Object> map = new HashMap();
     map.put("start", start);
     map.put("end", end);
     return sqlSession.selectList("StudentID.pagination", map);
 }catch(Exception e){
     e.printStackTrace();
     sqlSession.rollback();
    throw e; }
finally{
 MybatisUtil.closeSqlSession();
 }

什么是MyBatis的接口绑定?有哪些实现方式?

接口绑定: 就是在MyBatis中任意定义接口,然后把接口里面的方法和SQL语句绑定, 我们直接调用接口方法就可以,这样比起原来了SqlSession提供的方法我们可以有更加灵活的选择和设置。
接口绑定有两种实现方式:
一种是通过注解绑定: 就是在接口的方法上面加上 @Select、@Update等注解,里面包含Sql语句来绑定;
一种就是通过xml里面写SQL来绑定: 在这种情况下,要指定xml映射文件里面的namespace必须为接口的全路径名。当Sql语句比较简单时候,用注解绑定, 当SQL语句比较复杂时候,用xml绑定,一般用xml绑定的比较多。

mybatis 有哪些执行器(Executor)?它们之间的区别是什么?

1、SimpleExecutor: 每执行一次update或select,就开启一个Statement对象,用完立刻关闭Statement对象。
2、ReuseExecutor: 执行update或select,以sql作为key查找Statement对象,存在就使用,不存在就创建,用完后,不关闭Statement对象,而是放置于Map内,供下一次使用。简言之,就是重复使用Statement对象。
3、BatchExecutor: 执行update(没有select,JDBC批处理不支持select),将所有sql都添加到批处理中(addBatch()),等待统一执行(executeBatch()),它缓存了多个Statement对象,每个Statement对象都是addBatch()完毕后,等待逐一执行executeBatch()批处理。与JDBC批处理相同。
作用范围: Executor的这些特点,都严格限制在SqlSession生命周期范围内。

Mybatis 批处理执行器

使用Mybatis批量操作,需要在全局配置文件或获取sqlSession时设置执行类型为BATCH

--- 全局配置文件中配置

<configuration>
    <!--  ...  -->
    <settings>
        <setting name="defaultExecutorType" value="BATCH"/>
    </settings>
    <!-- ... -->
</configuration>

----获取sqlSession时开启批处理执行

InputStream resourceAsStream2 = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory build2 = new SqlSessionFactoryBuilder().build(resourceAsStream2);
SqlSession openSession2 = build2.openSession(ExecutorType.BATCH);

---- 如果与spring框架整合后,那么批处理sqlSession可以配置在spring的bean容器中,使用时获取该sqlSession来进行dao操作。

<!--配置MybatisSqlSessionFactoryBean-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource"/>
    <!--配置mybatis核心配置文件-->
    <property name="configLocation" value="classpath:mybatis-config.xml"/>
    <!--配置mybatis的Mapper映射文件-->
    <property name="mapperLocations" value="classpath:com/test/mybatis/mapper/*.xml"/>
</bean>

<!--  在spring的bean容器中通过sqlSessionFactory配置批处理sqlSession  -->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
    <constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"/>
    <constructor-arg name="executorType" value="BATCH"/>
</bean>
---- 因为批处理sqlSession已经在spring容器中,所以在 需要批处理的service处直接将批处理sqlSession依赖注入进去,然后通过sqlSession的getMapper方法获取映射接口进行操作即可。
//...
/**
 * 将批处理sqlSession通过依赖注入加入到当前Service中进行批处理dao操作。
 */
@Autowired
private SqlSession sqlSession;
@Override
//...

@Override
public void insertBatch(Users user) {
    UsersMapper usersMapper = sqlSession.getMapper(UsersMapper.class);
    //批量插入操作
    usersMapper.insertBatch(user);
}

分布式/微服务

什么是微服务?

微服务架构是一种架构模式或者说是一种架构风格,它提倡将单一应用程序划分成一组小的服务,每个服务运行在其独立的自己的进程中,服务之间互相协调、互相配合,为用户提供最终价值。 服务之间采用轻量级的通信机制互相沟通(通常是基于HTTP的RESTful API)。
每个服务都围绕着具体业务进行构建,并且能够被独立地部署到生产环境、类生产环境等

微服务之间是如何通讯的?

远程过程调用(Remote Procedure Invocation)
直接通过远程过程调用来访问别的service。
示例:REST、gRPC、Apache、Thrift
消息
使用异步消息来做服务间通信。服务间通过消息管道来交换消息,从而通信。
示例:Apache Kafka、RabbitMQ
优点:
把客户端和服务端解耦,更松耦合 提高可用性,因为消息中间件缓存了消息,直到消费者可以消费
支持很多通信机制比如通知、请求/异步响应、发布/订阅、发布/异步响应

什么是服务治理

服务治理是主要针对分布式服务框架,微服务,处理服务调用之间的关系,服务发布和发现(谁是提供者,谁是消费者,要注册到哪里),出了故障谁调用谁,服务的参数都有哪些约束(尤其是dubbo.xml配置),如何保证服务的质量?如何服务降级和熔断?怎么让服务受到监控,提高机器的利用率?
在这里插入图片描述

服务熔断

熔断机制是应对雪崩效应的一种微服务链路保护机制。

服务降级

当扇出链路的某个微服务不可用或者响应时间太长时,会进行服务的降级,进而熔断该节点微服务的调用,快速返回"错误"的响应信息。当检测到该节点微服务调用响应正常后恢复调用链路。在SpringCloud框架里熔断机制通过Hystrix实现。Hystrix会监控微服务间调用的状况,当失败的调用到一定阈值,缺省是5秒内20次调用失败就会启动熔断机制。熔断机制的注解是@HystrixCommand。
参考:服务降级

你所知道的微服务技术栈有哪些?

服务开发
Springboot、Spring、SpringMVC
服务配置与管理
Netflix公司的Archaius、阿里的Diamond等
服务注册与发现
Eureka、Consul、Zookeeper等
服务调用
Rest、RPC、gRPC
服务熔断器
Hystrix、Envoy等
负载均衡
Ribbon、Nginx等
服务接口调用(客户端调用服务的简化工具)
Feign等
消息队列
Kafka、RabbitMQ、ActiveMQ等
服务配置中心管理
SpringCloudConfig、Chef等
服务路由(API网关)
Zuul等
服务监控
Zabbix、Nagios、Metrics、Spectator等
全链路追踪
Zipkin,Brave、Dapper等
服务部署
Docker、OpenStack、Kubernetes等
数据流操作开发包
SpringCloud Stream(封装与Redis,Rabbit、Kafka等发送接收消息)
事件消息总线
Spring Cloud Bus

Dubbo

Dubbo中zookeeper做注册中心,如果注册中心集群都挂掉,发布者和订阅者之间还能通信么?

可以通信的,启动dubbo时,消费者会从zk拉取注册的生产者的地址接口等数据,缓存在本地。每次调用时,按照本地存储的地址进行调用;
注册中心对等集群,任意一台宕机后,将会切换到另一台;注册中心全部宕机后,服务的提供者和消费者仍能通过本地缓存通讯。服务提供者无状态,任一台 宕机后,不影响使用;服务提供者全部宕机,服务消费者会无法使用,并无限次重连等待服务者恢复;

dubbo服务负载均衡策略?

l Random LoadBalance
随机,按权重设置随机概率。在一个截面上碰撞的概率高,但调用量越大分布越均匀,而且按概率使用权重后也比较均匀,有利于动态调整提供者权重。(权重可以在dubbo管控台配置)
l RoundRobin LoadBalance
轮循,按公约后的权重设置轮循比率。存在慢的提供者累积请求问题,比如:第二台机器很慢,但没挂,当请求调到第二台时就卡在那,久而久之,所有请求都卡在调到第二台上。
l LeastActive LoadBalance
最少活跃调用数,相同活跃数的随机,活跃数指调用前后计数差。使慢的提供者收到更少请求,因为越慢的提供者的调用前后计数差会越大。
l ConsistentHash LoadBalance
一致性Hash,相同参数的请求总是发到同一提供者。当某一台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起剧烈变动。缺省只对第一个参数Hash,如果要修改,请配置

Dubbo在安全机制方面是如何解决的

Dubbo通过Token令牌防止用户绕过注册中心直连,然后在注册中心上管理授权。Dubbo还提供服务黑白名单,来控制服务所允许的调用方。

dubbo 通信协议有哪些?

RMI协议
RMI协议采用JDK标准的java.rmi.*实现,采用阻塞式短连接和JDK标准序列化方式,Java标准的远程调用协议。
Hessian协议
Hessian协议用于集成Hessian的服务,Hessian底层采用Http通讯,采用Servlet暴露服务,Dubbo缺省内嵌Jetty作为服务器实现,基于Hessian的远程调用协议。
http协议
采用Spring的HttpInvoker实现,基于http表单的远程调用协议。
Webservice协议
基于CXF的frontend-simple和transports-http实现,基于WebService的远程调用协议。
Thrif协议
Thrift是Facebook捐给Apache的一个RPC框架,当前 dubbo 支持的 thrift 协议是对 thrift 原生协议的扩展,在原生协议的基础上添加了一些额外的头信息,比如service name,magic number等

Dubbo 和 Spring Cloud 有什么区别?

1 服务调用方式:: dubbo是RPC springcloud Rest Api
2 注册中心:,dubbo 是zookeeper springcloud是eureka,也可以是zookeeper
3 服务网关:,dubbo本身没有实现,只能通过其他第三方技术整合,springcloud有Zuul路由网关,作为路由服务器,进行消费者的请求分发,springcloud支持断路器,与git完美集成配置文件支持版本控制,事物总线实现配置文件的更新与服务自动装配等等一系列的微服务架构要素。

SpringBoot

SpringBoot常用注解

@ConfigurationProperties: 读取application属性配置文件中的属性
@Component 表明当需要创建类时
@Configuration: 相当于原来的声明了多个bean的xml配置文件,而且被@Configuration也相当于一个组件.
@SpringBootConfiguration: 申明让spring boot自动给程序进行必要的配置
@EnableAutoConfiguration 自动配置类
@ComponentScan 注解包扫描
等等

SpringBoot 四大核心组件

Spring Boot Starter 启动组件

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
</dependency>

Spring Boot Autoconfigure 自动配置组件

spring-boot-autoconfigure

Spring Boot CLI
Spring Boot CLI是一个命令行使用Spring Boot的客户端工具;主要功能如下:
1 运行groovy脚本 => 官网2.1
2 打包groovy文件到jar => 官网2.3
3 初始化Spring Boot项目 => 官网2.4
4 其他
Spring Boot actuator
actuator是Spring Boot的监控插件,本身提供了很多接口可以获取当前项目的各项运行状态指标。

springboot核心配置文件是什么?

application.properties配置文件
application.yml配置文件

spring boot 有哪些方式可以实现热部署?

1、模板热部署
2、使用调试模式Debug实现热部署
3、spring-boot-devtools
4、Spring Loaded
5、JRebel

springCloud

Spring Cloud是一系列框架的有序集合。它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、智能路由、消息总线、负载均衡、断路器、数据监控等,都可以用Spring Boot的开发风格做到一键启动和部署。

请谈谈对SpringBoot 和SpringCloud的理解

1 SpringBoot专注于快速方便的开发单个个体微服务。
2 SpringCloud 是关注全局的微服务协调整理治理框架,它将SpringBoot开发的一个个单体微服务整合并管理起来,
为各个微服务之间提供,配置管理、服务发现、断路器、路由、微代理、事件总线、全局锁、决策竞选、分布式会话等等集成服务
3 SpringBoot 可以离开SpringCloud独立使用开发项目,但是SpringCloud离不开SpringBoot,属于依赖的关系.
4 SpringBoot 专注于快速、方便的开发单个微服务个体,SpringCloud关注全局的服务治理框架。
5 Spring Boot 可以离开Spring Cloud独立使用开发项目,但是Spring Cloud离不开Spring Boot,属于依赖的关系

什么是SpringCloud Config分布式配置中心

SpringCloud Config为微服务架构中的微服务提供集中化的外部配置支持,配置服务器为各个不同微服务应用的所有环境提供了一个中心化的外部配置。

spring cloud 断路器的作用是什么?

为了防止在分布式系统中出现这种瀑布似的连锁反应导致的灾难.
断路器有完全打开状态: 一段时间内 达到一定的次数无法调用 并且多次监测没有恢复的迹象 断路器完全打开 那么下次请求就不会请求到该服务
半开: 短时间内 有恢复迹象 断路器会将部分请求发给该服务,正常调用时 断路器关闭
关闭: 当服务一直处于正常状态 能正常调用

spring cloud 的核心组件有哪些?

服务发现—Netflix EureKa
客服端负载均衡—Netflix Ribbon
断路器—Netflix Hystrix
服务网关—Netflix Zuul
分布式配置—Spring Cloud Config

zookeeper

zookeeper是什么?

ZooKeeper 是一个开源的分布式协调服务,由雅虎创建,是 Google Chubby 的开源实现。分布式应用程序可以基于 ZooKeeper 实现诸如数据发布/订阅、负载均衡、命名服务、分布式协调/通知、集群管理、Master 选举、配置维护,名字服务、分布式同步、分布式锁和分布式队列等功能。

zookeeper有哪些应用场景?

分布式协调
这个其实是 zookeeper 很经典的一个用法,简单来说,就好比,你 A 系统发送个请求到 mq,然后 B 系统消息消费之后处理了。那 A 系统如何知道 B 系统的处理结果?用 zookeeper 就可以实现分布式系统之间的协调工作。
分布式锁
对某一个数据连续发出两个修改操作,两台机器同时收到了请求,但是只能一台机器先执行完另外一个机器再执行。那么此时就可以使用 zookeeper 分布式锁,一个机器接收到了请求之后先获取 zookeeper 上的一把分布式锁,就是可以去创建一个 znode,接着执行操作;然后另外一个机器也尝试去创建那个 znode,结果发现自己创建不了,因为被别人创建了,那只能等着,等第一个机器执行完了自己再执行。
元数据/配置信息管理
zookeeper 可以用作很多系统的配置信息的管理,比如 kafka、storm 等等很多分布式系统都会选用 zookeeper 来做一些元数据、配置信息的管理
HA高可用性
这个应该是很常见的,比如 hadoop、hdfs、yarn 等很多大数据系统,都选择基于 zookeeper 来开发 HA 高可用机制,就是一个重要进程一般会做主备两个,主进程挂了立马通过 zookeeper 感知到切换到备用进程。

说一下 zookeeper 的通知机制?

客户端注册监听它关心的目录节点,当目录节点发生变化(数据改变、被删除、子目录节点增加删除)时,zookeeper会通知客户端。

集群中有 3 台服务器,其中一个节点宕机,这个时候 zookeeper 还可以使用吗?

可以继续使用,单数服务器只要没超过一半的服务器宕机就可以继续使用。

Redis

redis持久化方式有哪些?

RDB: 在指定时间间隔内,将内存中的数据作为一个快照文件(snapshot)写入到磁盘,读取的时候也是直接读取snapshot文件到内存中
AOF : 以日志形式记录每个写操作,启动时通过日志恢复操作,可读的日志文件,通过aof恢复更加稳健,可以处理失误

Redis拷贝

Fork: 会产生一个跟主进程一样的子进程,出于效率考虑,主进程和子进程会公用一段物理内存,当发生改变的时候,才会把主进程“”写时复制”一份给子进程

什么是缓存穿透?怎么解决?

是指在高并发场景下缓存中(包括本地缓存和Redis缓存)的某一个Key被高并发的访问没有命中,此时回去数据库中访问数据,导致数据库并发的执行大量查询操作,对DB造成巨大的压力。
解决方法:
1:对缓存失效的Key加分布式锁,当一个Key在本地缓存以及Redis缓存中未查询到数据,此时对Key加分布式锁访问db,如果取到数据就反写到缓存中,避免大量请求进入DB;如果取不到数据则缓存一个空对象,这样可以保证db不会被大量请求直接挂掉,从而引起缓存颠簸,更甚者缓存雪崩效应。
2:在本地缓存一个set集合,存储对应数据为空的key的集合,在请求前做拦截,此种方式牵涉到数据变更还要校验set集合的问题,一般适用于数据更新比较少的场景。
3:使用布隆过滤器

怎么保证缓存和数据库数据的一致性?

1 更新的时候,先更新数据库,然后再删除缓存。
2 读的时候,先读缓存;如果没有的话,就读数据库,同时将数据放入缓存,并返回响应。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值