美文网首页
java面试八股文

java面试八股文

作者: 麻烦大麻要坚强 | 来源:发表于2022-06-27 11:21 被阅读0次

    本文转载自 https://blog.csdn.net/weixin_45927841/article/details/123217462

    一、java
    (1)集合
    1.list:LinkedList、ArrayList和Vector
    LinkedList 接口实现类, 链表, 插入删除, 没有同步, 线程不安全
    ArrayList 接口实现类, 数组, 随机访问, 没有同步, 线程不安全
    Vector 接口实现类 数组, 同步, 线程安全
    2.set:HashSet和TreeSet
    HashSet 使用哈希表存储元素,元素可以是null
    LinkedHashSet 链表维护元素的插入次序
    TreeSet 底层实现为红黑树,元素排好序,元素不可以是null
    3.map:HashMap、TreeMap和HashTable
    线程安全
    HshaMap线程不安全
    TreeMap线程不安全
    HashTable线程安全
    空值
    HashMap一个null key,多个null value
    TreeMap不能null key,多个null value
    HashTable都不能有null
    继承和接口
    HashMap继承AbstractMap,实现接口Map
    TreeMap继承AbstractMap,实现接口NavigableMap(SortMap的一种)
    HashTable继承Dictionary,实现接口Map
    顺序
    HashMap中key是无序的
    TreeMap是有序的
    HashTable是无序的
    构造函数
    HashMap有调优初始容量和负载因子
    TreeMap没有
    HashTable有
    数据结构
    HashMap是链表+数组+红黑树
    TreeMap是红黑树
    HashTable是链表+数组
    4.list、set和map的区别
    list:元素按进入先后有序保存,可重复
    set:不可重复,并做内部排序
    map:代表具有映射关系的集合,其所有的key是一个Set集合,即key无序且不能重复。
    5.HashMap扩容机制
    数组的初始容量为16,而容量是以2的次方扩充的,一是为了提高性能使用足够大的数组,二是为了能使用位运算代替取模预算(据说提升了5~8倍)。
    数组是否需要扩充是通过负载因子判断的,如果当前元素个数为数组容量的0.75时,就会扩充数组。这个0.75就是默认的负载因子,可由构造器传入。我们也可以设置大于1的负载因子,这样数组就不会扩充,牺牲性能,节省内存。
    为了解决碰撞,数组中的元素是单向链表类型。当链表长度到达一个阈值时(7或8),会将链表转换成红黑树提高性能。而当链表长度缩小到另一个阈值时(6),又会将红黑树转换回单向链表提高性能。
    对于第三点补充说明,检查链表长度转换成红黑树之前,还会先检测当前数组数组是否到达一个阈值(64),如果没有到达这个容量,会放弃转换,先去扩充数组。所以上面也说了链表长度的阈值是7或8,因为会有一次放弃转换的操作。
    6.HashMap中的循环链表是如何产生的
    由于多线程,存在两个线程同时对链表进行扩容的情况,执行transfer函数(链表数据转移)会导致链表数据倒置,当两个线程同时此操作,就导致链表死循环
    7.B树和B+树的区别
    B树是二叉排序树进化而来;B+树是分块查找进化而来
    B+树叶节点包含所有数据,非叶节点仅起到索引作用;B树终端节点及以上都包含数据且不重复(叶节点只是一个概念,并不存在)
    B+树叶节点包含了全部关键字
    B+树支持顺序查找和多路查找,B树只支持多路查找

    1. HashMap为什么用红黑树而不是AVL树或者B+树
      AVL树更加严格平衡,因此可以提供更快的査找效果。因此,对于查找密集型任务使用AVL树没毛病。 但是对于插入密集型任务,红黑树要好一些。
      B/B+树的节点可以存储多个数据,当数据量不够多时,数据都会”挤在“一个节点中,查询效率会退化为链表。
      9.CopyOnWriteArrayList的原理
      线程并发访问进行读操作时,没有加锁限制
      写操作时,先将容器复制一份,再在新的副本上执行写操作,此时写操作是上锁的。结束之后再将原容器的引用指向新容器。注意,在上锁执行写操作的过程中,如果有需要读操作,会作用在原容器上。因此上锁的写操作不会影响到并发访问的读操作。
      10.BlockingQueue中有哪些方法
      共四组增删API

    抛异常:如果操作无法立即执行,则抛一个异常;
    特定值:如果操作无法立即执行,则返回一个特定的值(一般是 true / false)。
    阻塞:如果操作无法立即执行,则该方法调用将会发生阻塞,直到能够执行;
    超时:如果操作无法立即执行,则该方法调用将会发生阻塞,直到能够执行。但等待时间不会超过给定值,并返回一个特定值以告知该操作是否成功(典型的是true / false)。
    (4)多线程
    1.Java中线程安全的基本数据结构
    string
    HashTable
    ConcurrentHashMap
    CopyOnWriteArrayList
    CopyOnWriteArraySet
    Vector
    stringBuffer
    2.创建线程有哪几种方式
    继承Thread类
    实现Runnable接口
    实现Callable接口
    3.线程的生命周期
    线程的状态有五种:新建(new)、就绪(start())、运行(分配到cpu)、阻塞和死亡
    CPU在多条线程之间切换,于是线程状态也会多次在运行、就绪之间切换。
    出现阻塞的情况
    线程调用sleep()方法主动放弃所占用的处理器资源。
    线程调用了一个阻塞式IO方法,在该方法返回之前,该线程被阻塞。
    线程试图获得一个同步监视器,但该同步监视器正被其他线程所持有。
    线程在等待某个通知(notify)
    程序调用了线程的suspend()方法将该线程挂起。但这个方法容易导致死锁,所以应该尽量避免使用该方法。
    解除阻塞重新进入就绪状态的情况
    调用sleep()方法的线程经过了指定时间
    线程调用的阻塞式IO方法已经返回。
    线程成功地获得了试图取得的同步监视器。
    线程正在等待某个通知时,其他线程发出了一个通知。
    处于挂起状态的线程被调用了resume()恢复方法。
    出现死亡的情况
    run()或call()方法执行完成,线程正常结束
    线程抛出一个未捕获的Exception或Error。
    直接调用该线程的stop()方法来结束该线程,该方法容易导致死锁,通常不推荐使用
    4.如何实现线程同步
    同步方法(synchronized)
    同步代码块
    ReentrantLock
    volatile
    5.Java多线程之间的通信方式
    wait()、notify()、notifyAll()。采用采用synchronized来保证线程安全
    await()、signal()、signalAll()。采用lock保证线程安全
    BlockingQueue。当生产者线程试图向BlockingQueue中放入元素时,如果该队列已满,则该线程被阻塞;当消费者线程试图从BlockingQueue中取出元素时,如果该队列已空,则该线程被阻塞。
    6.sleep()和wait()的区别
    sleep()是Thread类中的静态方法,而wait()是Object类中的成员方法;
    sleep()可以在任何地方使用,而wait()只能在同步方法或同步代码块中使用
    sleep()不会释放锁,而wait()会释放锁,并需要通过notify()/notifyAll()重新获取锁。
    7.synchronized与Lock的区别
    synchronized是Java关键字,在JVM层面实现加锁和解锁;Lock是一个接口,在代码层面实现加锁和解锁
    synchronized可以用在代码块上、方法上;Lock只能写在代码里。
    synchronized在代码执行完或出现异常时自动释放锁;Lock不会自动释放锁,需要在finally中显示释放锁。
    synchronized会导致线程拿不到锁一直等待;Lock可以设置获取锁失败的超时时间。
    synchronized无法得知是否获取锁成功;Lock则可以通过tryLock得知加锁是否成功。
    synchronized锁可重入、不可中断、非公平;Lock锁可重入、可中断、可公平/不公平,并可以细分读写锁以提高效率
    8.乐观锁和悲观锁的区别
    9.公平锁与非公平锁
    非公平锁: 当线程争夺锁的过程中,会先进行一次CAS尝试获取锁,若失败,则进入acquire(1)函数,进行一次tryAcquire再次尝试获取锁,若再次失败,那么就通过addWaiter将当前线程封装成node结点加入到Sync队列,这时候该线程只能乖乖等前面的线程执行完再轮到自己了
    公平锁: 当线程在获取锁的时候,会先判断Sync队列中是否有在等待获取资源的线程。若没有,则尝试获取锁,若有,那么就那么就通过addWaiter将当前线程封装成node结点加入到Sync队列中
    10.volatile
    保证可见性,不保证原子性
    禁止指令重排
    (3)其他
    1.面向对象三大特性
    封装:(将数据和代码捆绑在一起,防止外界干扰)把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏
    继承:(让一个类型的对象拥有另一个类型的对象的属性的方法)可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展
    多态:(就是一个事物拥有不同形式的能力)父类引用指向子类对象,从而具备多种形态
    2.Object类的常用方法
    equals()
    hashCode()
    toString()
    getClass()
    wait()
    notify()
    notifyall()
    clone()
    finalize()
    3.string、stringBuffer和stringBuilder
    string不可变、线程安全
    stringBuffer可变,效率低,线程安全
    stringBuilder可变、效率高,线程不安全
    4.抽象类与接口的区别
    抽象类属于继承,只能继承一个;接口可以实现多个
    接口里只能包含抽象方法、静态方法、默认方法和私有方法,不能为普通方法提供方法实现;抽象类则完全可以包含普通方法。
    接口里只能定义静态常量,不能定义普通成员变量;抽象类里则既可以定义普通成员变量,也可以定义静态常量。
    接口里不包含构造器;抽象类里可以包含构造器,抽象类里的构造器并不是用于创建对象,而是让其子类调用这些构造器来完成属于抽象类的初始化操作。
    5.java的基本数据类型
    byte:1字节(8位),数据范围是 -2^7 ~ 2^7-1。
    short:2字节(16位),数据范围是 -2^15 ~ 2^15-1。
    int:4字节(32位),数据范围是 -2^31 ~ 2^31-1。
    long:8字节(64位),数据范围是 -2^63 ~ 2^63-1。
    float:4字节(32位),数据范围大约是 -3.410^38 ~ 3.410^38。
    double:8字节(64位),数据范围大约是 -1.810^308 ~ 1.810^308。
    char:2字节(16位),数据范围是 \u0000 ~ \uffff。
    boolean:Java规范没有明确的规定,不同的JVM有不同的实现机制。
    6.java代码块执行顺序
    父类静态代码块
    子类静态代码块
    父类构造代码块
    父类构造方法
    子类构造代码块
    子类构造方法
    普通代码块
    7.static关键字
    修饰成员变量:该静态变量在内存中只有一个副本。只要静态变量所在的类被加载,这个静态变量就会被分配空间
    修饰成员方法:调用该方法只需类名.方法名;静态方法不依赖于任何对象就可以进行访问,因此对于静态方法来说,是没有this的。在静态方法中不能访问类的非静态成员变量和非静态成员方法,因为非静态成员方法/变量都必须依赖具体的对象才能够被调用。
    修饰代码块:在类初次被加载的时候,会按照static块的顺序来依次执行每个static块,并且只会执行一次。
    修饰内部类:静态内部类不能直接访问外部类的非静态成员,但,可以通过new 外部类().成员的方式访问;
    8.覆盖(重写)和重载的区别
    重写一般是子类重写父类方法(一对一),垂直关系;重载一般是一个类中多个方法重载(多个之间),水平关系
    重写方法之间参数相同;重载方法之间参数不同
    重写不可以修改返回值类型;重载可以修改返回值类型
    9.java四个访问修饰符
    private:本类中
    default:本包中
    protected:不同包的子类
    public:所有
    10.全局变量和局部变量的区别
    成员变量:

    成员变量是在类的范围里定义的变量;

    成员变量有默认初始值;

    未被static修饰的成员变量也叫实例变量,它存储于对象所在的堆内存中,生命周期与对象相同;

    被static修饰的成员变量也叫类变量,它存储于方法区中,生命周期与当前类相同。

    局部变量:

    局部变量是在方法里定义的变量;

    局部变量没有默认初始值;

    局部变量存储于栈内存中,作用的范围结束,变量空间会自动的释放。

    11.hashCode()和equals()的关系
    hashCode求的是对象的散列码(一般是对象的储存地址),equals是根据地址比较对象是否相同
    如果两个对象相等,则它们必须有相同的哈希码
    如果两个对象有相同的哈希码,则它们未必相等
    12.为什么要重写hashCode()和equals()
    Object类提供的equals()方法默认是用==来进行比较的,也就是说只有两个对象是同一个对象时,才能返回相等的结果。而实际的业务中,我们通常的需求是,若两个不同的对象它们的内容是相同的,就认为它们相等。
    13.反射
    动态获取类的信息,以及动态调用对象的方法的功能。可以理解为动态看透类的能力。
    14.cookie和session的区别
    存储位置不同:cookie存放于客户端;session存放于服务端。
    隐私策略不同:cookie对客户端是可见的,别有用心的人可以分析存放在本地的cookie并进行cookie欺骗,所以它是不安全的;session存储在服务器上,对客户端是透明的,不存在敏感信息泄露的风险。
    生命周期不同:设置cookie的属性,达到cookie长期有效的效果;session只需关闭窗口该session就会失效,因此session不能长期有效。
    15.get和post请求的区别
    url可见性:get,参数url可见;post,url参数不可见
    数据传输上:get,通过拼接url进行传递参数;post,通过body体传输参数
    缓存性:get请求是可以缓存的;post请求不可以缓存
    后退页面的反应:get请求页面后退时,不产生影响;post请求页面后退时,会重新提交请求
    安全性:这个也是最不好分析的,原则上post肯定要比get安全,毕竟传输参数时url不可见。
    get一般传输数据大小不超过2k-4k;post请求传输数据的大小根据php.ini 配置文件设定,也可以无限大。
    16.前后端数据交互
    form表单

    HttpServletRequest/HttpServletResponse

    @RequestParam

    数据在url后 path?id=1

    @PathVariable
    数据在url后 path/{id}

    @RequestBody
    以json数据为例,首先有一个类

    然后前端传过来数据

    后端接收

    • ModelAndView(只向前端传输数据)

    • 配置视图解析器

    • 创建ModelAndView对象,添加返回的数据和地址

    • model

    jquery实现的ajax
    前端

    eg:
    数据传输载体类

    前端部分

    后台部分

    17.IO分类
    按流方向分:输入流,输出流
    按数据单位分:字节流,字符流
    按功能分:节点流,处理流
    18.处理哈希冲突的方法
    开放定址法(再散列法)
    线性探测再散列
    二次探测再散列
    伪随机探测再散列
    再哈希法
    拉链法
    二、JVM
    1.JVM包含哪几部分
    类加载器
    运行时数据区(堆、栈、方法区、本地方法栈和程序计数器)
    方法区:静态变量、常量、类信息和常量池
    程序计数器:每个线程都有一个程序计数器,就像一个指针,指向方法去中的方法字节码(比如每次读下一条指令的时候给它+1)
    本地方法栈:等级native方法,在执行引擎的时候加载本地库
    栈:生命周期和线程同步;不存在垃圾回收问题;存放八大基本类型、对象引用变量名、实例的方法
    堆:类的实例、常量
    新生区:伊甸园区、幸存0区、幸存1区
    养老区
    永久区
    执行引擎
    本地库接口
    2.双亲委派机制
    类加载器收到类加载的请求
    将这个请求委托给父类加载器去完成,一直向上委托,直到启动类加载器
    启动类加载器能执行就结束,否则抛出异常,依次向下通知子类进行加载
    优点:
    从最内层开始加载,外层恶意同名类得不到加载从而无法使用
    严格通过包来区分了访问域,外层恶意的类通过内置代码也无法访问到内层类
    3.创建对象内存分析
    (案例取自遇见狂神说的java课程视频)

    4.JAVA对象实例化过程
    类的加载初始化
    加载:载入class对象,不一定是从class文件获取,可以是jar包,或者动态生成的class
    连接
    验证:校验class字节流是否符合当前jvm规范
    准备:为 类变量 分配内存并设置变量的初始值( 默认值 )。如果是final修饰的对象则是赋值声明值
    解析:将常量池的符号引用替换为直接引用
    初始化
    使用
    卸载
    对象的初始化

    5.什么时候进行GC
    引用计数算法
    可达性分析算法
    6.三种基本的GC算法
    标记-清除算法
    内存中的对象构成一棵树,当有效的内存被耗尽的时候,程序就会停止,做两件事,第一:标记,标记从树根可达的对象,第二:清除不可达的对象。标记清除的时候程序会停止运行
    缺点:递归效率低性能低;释放空间不连续容易导致内存碎片;会停止整个程序运行;
    复制算法
    把内存分成两块区域:空闲区域和活动区域,第一还是标记,标记之后把可达的对象复制到空闲区,将空闲区变成活动区,同时把以前活动区对象清除,变成空闲区。
    缺点:速度快但耗费空间
    标记-整理算法
    在标记清除算法之后不是直接清理可回收对象,而是将存活对象都向一端移动,然后清理掉端边界以外的内存。
    内存效率:复制算法>标记清除算法>标记压缩算法(时间复杂度)
    内存整齐度:复制算法=标记压缩算法>标记清除算法
    内存利用率:复制算法<标记清除算法=标记压缩算法
    三、mysql
    1.数据库三大范式
    第一范式:强调的是列的原子性,即列不能够再分成其他几列。
    第二范式:在第一范式基础上,必须有一个主键其他字段必须完全依赖于主键,而不能只依赖于主键的一部分。
    第三范式:在前两个范式基础上,非主键列必须直接依赖于主键,不能存在传递依赖。
    2.防止sql注入
    代码层防止sql注入攻击的最佳方案就是sql预编译(preparestatement类)
    规定数据长度,能在一定程度上防止sql注入
    严格限制数据库权限,能最大程度减少sql注入的危害
    3.索引
    索引是帮助MySQL高效获取数据的数据结构,通俗来讲索引就好比书本的目录,加快数据库的查询速度。
    分类
    主键索引:加速查询 + 列值唯一(不可以有null)+ 表中只有一个
    唯一索引:加速查询 + 列值唯一(可以有null)
    普通索引:仅加速查询
    组合索引:多列值组成一个索引,专门用于组合搜索,其效率大于索引合并
    全文索引:对文本的内容进行分词,进行搜索
    4.ACID
    原子性:要么都发生,要么都不发生。
    一致性:事务前后数据的完整性必须保持一致。
    隔离性:一个事务不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。
    持久性:一个事务一旦被提交,数据不可再恢复
    5.事务并发产生的三种问题
    脏读:一个事务读取到了另外一个事务没有提交的数据
    幻读:同一事务中,用同样的操作读取两次,得到的记录数不相同(数据条数)
    不可重复读:在同一事务中,两次读取同一数据,得到内容不同(数据内容)
    6.mysql的事务隔离级别
    读未提交 Read uncommitted:一个事务还没有提交时,它做的变更就能被别的事务看到。
    读提交 Read committed:一个事物提交之后,它做的变更才会被其他事务看到。
    可重复读 Repeatable read:一个事物执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的。未提交变更对其他事务也是不可见的。
    串行化 serializable:对于同一行记录,写会加“写锁”,读会加“读锁”,当出现锁冲突时,后访问的事务需要等前一个事务执行完成,才能继续执行。

    7.数据库锁
    按粒度分:
    行锁
    页锁
    表锁
    全局锁
    按锁的级别
    共享(读)锁(S)
    排他(写)锁(X)
    意向共享锁
    意向排他锁
    8.Mysql事务的分类
    扁平式事务
    带有保存点的扁平式事务
    链事务
    嵌套事务
    分布式事务
    9.联表查询

    四、计算机网络
    1.OSI七层模型
    (物)物理层:为数据端设备提供原始比特流的传输的通路
    (联)数据链路层:在通信的实体间建立数据链路连接
    (网)网络层:为数据在节点之间传输创建逻辑链路,并分组转发数据(IP、IPX)
    (淑)传输层:提供应用进程之间的逻辑通信(TCP、UDP)
    (惠)会话层:建立端连接并提供访问验证和会话管理
    (试)表示层:提供数据格式转换服务
    (用)应用层:访问网络服务的接口(DNS、HTTP、FTP)

    2.三次握手
    A将标志位SYN置为1,随机产生一个值seq=x,并将该数据包发送给B,A进入SYN_SENT状态,等待B确认。
    B收到数据包后由标志位SYN=1知道A请求建立连接,B将标志位SYN和ACK都置为1,ack=x+1,随机产生一个值seq=y,并将该数据包发送给A以确认连接请求,B进入SYN_RCVD状态。
    A收到确认后,检查ack是否为x+1,ACK是否为1,如果正确则将标志位ACK置为1,ack=y+1,并将该数据包发送给B,B检查ack是否为y+1,ACK是否为1,如果正确则连接建立成功,A和B进入ESTABLISHED状态,完成三次握手

    3.四次挥手
    第一次挥手:A发送一个FIN,用来关闭A到B的数据传送,A进入FIN_WAIT_1状态。
    第二次挥手:B收到FIN后,发送一个ACK给A,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号),B进入CLOSE_WAIT状态。此时TCP链接处于半关闭状态,即客户端已经没有要发送的数据了,但服务端若发送数据,则客户端仍要接收
    第三次挥手:B发送一个FIN,用来关闭B到A的数据传送,B进入LAST_ACK状态。
    第四次挥手:A收到B后,A进入TIME_WAIT状态,接着发送一个ACK给B,确认序号为收到序号+1,B进入CLOSED状态,完成四次挥手。

    五、java框架
    (1)springboot
    1.springboot优于spring的地方
    内嵌 Tomcat , Jetty Undertow 而且不需要部署他们
    提供的“starters” poms来简化 Maven 配置
    起步依赖,创建项目时候就可以勾选所需的依赖
    全部采用注解方式,没有繁琐的xml配置
    (2)spring
    1.IOC
    控制反转,是一种设计思想。将原本在程序中手动创建对象的控制权,交由Spring框架的容器来管理,并且需要一种描述让容器知道要创建的对象间的关系。即IOC容器来管理对象及其依赖关系。
    2.依赖注入的实现方法
    依赖注入(DI)是实现IOC的一种方法

    构造器注入
    set方法注入
    其他方式(接口注入、注解注入)
    3.AOP
    面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。基于动态代理,oop(面向对象编程),反射
    4.AOP的术语
    切面:切面泛指交叉业务逻辑。比如声明式事物处理,日志处理就可以理解为切面
    通知:就是你想要的功能,即切面中的方法
    连接点:可以使用通知的地方。如每个方法的前后
    切入点:切面实际切入的地方
    目标:要被通知的对象
    5.AOP的实现方式
    使用原生的Spring API接口(实现前置通知、后置通知、环绕通知等接口)
    自定义类
    注解实现(@Aspect)
    6.IOC容器
    BeanFactory:提供了最简单的容器的功能,只提供了实例化对象和拿对象的功能;
    ApplacationContext:继承BeanFactory接口,它是Spring的一各更高级的容器,提供了更多的有用的功能
    国际化
    访问资源,如URL和文件
    事件传递:通过实现ApplicationContextAware接口
    7.创建ApplacationContext的方法
    FileSystemXmlApplicationContext:从文件系统或者url指定的xml配置文件创建,参数为配置文件名或文件名数组
    ClassPathXmlApplicationContext:从classpath的xml配置文件创建,可以从jar包中读取配置文件
    WebApplicationContextUtils:从web应用的根目录读取配置文件,需要先在web.xml中配置,可以配置监听器或者servlet来实现
    8.bean的作用域
    singleton:在Spring容器中仅存在一个实例,即Bean以单例的形式存在。
    prototype:为每一个bean请求提供一个实例。
    request :每次HTTP请求都会创建一个新的Bean。
    session:同一个HTTP Session共享一个Bean,不同的HTTP Session使用不同的Bean。
    globalSession:同一个全局的Session共享一个Bean,一般用于Portlet环境。
    9.bean的生命周期
    实例化(通过构造方法或者工厂方法)
    属性赋值 (依赖注入)
    初始化
    销毁
    10.bean的自动装配
    @Autowried:通过byType方式实现,并且要求这个对象存在
    @Resouce:默认通过byName方式实现,如果name找不到再通过byType实现
    (3)SpringMVC
    1.SpringMVC概念
    MVC是一种设计模式,在这种模式下软件被分为三层,即Model(模型)、View(视图)、Controller(控制器)。
    2.Spring MVC的执行流程
    参考博客

    用户发送请求到前端控制器DispatcherServlet

    DispatcherServlet收到请求调用处理映射器HandlerMapping

    处理映射器根据请求url找到具体的处理器,生成处理器执行链HandlerExecutionChain(包含处理器对象和处理器拦截器)返回给DispatcherServlet

    DispatcherServlet根据处理器Handler获取对应的适配器

    HandlerAdapter调用处理器Handler

    Handler(Controller)执行完成后返回ModelAndView

    HandlerAdapter返回ModelAndView

    DispatcherServlet统一将返回的ModelAndView派送到ViewResolve(视图解析器)解析

    视图解析器解析之后返回View

    对View进行渲染

    响应用户

    3.拦截器和过滤器的区别
    实现原理不一样:拦截器实现原理是反射机制,过滤器是函数回调
    使用范围不一样:过滤器是servelet中定义的,依赖于tomcat等容器,只能在web程序中使用;拦截器是spring的一个组件,可以单独使用,还可用在web程序跟其他程序
    六、Redis
    1.持久化RDB和AOF的区别
    RDB持久化是指在指定的时间间隔内将内存中的数据集快照写入磁盘,实际操作过程是fork一个子进程,先将数据集写入临时文件,写入成功后,再替换之前的文件,用二进制压缩存储。
    优点:
    性能最大化。对于Redis的服务进程而言,在开始持久化时,它唯一需要做的只是fork出子进程,之后再由子进程完成这些持久化的工作,这样就可以极大的避免服务进程执行IO操作了。
    相比于AOF机制,如果数据集很大,RDB的启动效率会更高
    对于灾难恢复而言,RDB是非常不错的选择。因为我们可以非常轻松的将一个单独的文件压缩后再转移到其它存储介质上
    缺点:
    系统一旦在定时持久化之前出现宕机现象,此前没有来得及写入磁盘的数据都将丢失
    AOF持久化以日志的形式记录服务器所处理的每一个写、删除操作,查询操作不会记录,以文本的方式记录,可以打开文件看到详细的操作记录。
    优点:
    可以带来更高的数据安全性,即数据持久性。Redis中提供了3中同步策略,即每秒同步、每修改同步和不同步。
    即使出现宕机现象,也不会破坏日志文件中已经存在的内容。如果我们本次操作只写入了一半数据就出现了系统崩溃问题,在Redis下一次启动之前,通过redis-check-aof工具来解决数据一致性的问题。
    缺点:
    对于相同数量的数据集而言,AOF文件通常要大于RDB文件。RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快
    根据同步策略的不同,AOF在运行效率上往往会慢于RDB。
    2.redis的数据类型
    五大数据类型:String、List、Set、Hash、Zset(增加分数字段用来排序)
    三种特殊数据类型:Geospatial(地理位置)、Hyperloglog(基数统计,应用于网站访问量)、BitMaps(位图,应用于签到、状态统计)
    3.redis高可用
    哨兵和集群模式
    4.缓存穿透、缓存击穿、缓存雪崩
    缓存穿透:客户端查询根本不存在的数据,使得请求直达存储层,导致其负载过大,甚至宕机。出现这种情况的原因,可能是业务层误将缓存和库中的数据删除了,也可能是有人恶意攻击,专门访问库中不存在的数据。
    解决方案:缓存空对象;布隆过滤器
    缓存击穿:一份热点数据,它的访问量非常大。在其缓存失效的瞬间,大量请求直达存储层,导致服务崩溃。
    解决方案:热点数据永不过期;加互斥锁
    缓存雪崩
    解决方案:设置过期时间时,附加一个随机数,避免数据同时过期;
    七、Linux常用命令
    1.目录切换
    cd /:切换到根目录
    cd …/:切换到上级目录
    cd ~:切换到home目录
    2.目录查看
    ls:查看当前目录下的所有目录和文件
    ls -a:查看当前目录下的所有目录和文件(包含隐藏文件)
    ls -l:列表查看当前目录下的所有目录和文件(列表查看,显示更多信息)
    3.目录操作
    创建
    mkdir 目录名
    删除
    rm -f: 强行删除,不询问
    rm -r 目录名:递归删除目录
    修改
    mv 当前目录 新目录
    查找
    find 目录 参数 文件名称
    示例:find /usr/tem -name ‘a*’ 查找/usr/tmp目录下的所有以a开头的目录或文件
    4.文件操作
    创建
    touch 文件名
    删除
    rm -rf
    修改
    vi或者vim,分为三种模式
    命令行模式
    编辑模式
    底行模式
    查看
    cat:看最后一屏
    more:百分比显示
    less:翻页显示
    tail:指定行数或者动态查看
    5.权限操作
    chmod (u g o a) (+ - =) (r w x) (文件名)

    u:文件的拥有者
    g:与拥有者在同一组
    o:其他用户
    a:上面三者皆是
    +:增加权限
    -:撤销权限
    =:设定权限
    r:读权限
    w:写权限
    x:可执行(没有即代表不可cd进入这个目录)
    6.打包和压缩
    打包文件:tar -cvf 打包文件.tar 被打包的文件/路径…
    解包文件:tar -xvf 打包文件.tar
    参数说明
    c:生成档案文件,创建打包文件
    x:解开档案文件
    v:显示过程
    f:指定档案文件名称
    压缩文件:tar -zcvf 打包文件.tar.gz 被压缩的文件/路径…
    解压文件:tar -zxvf 打包文件.tar.gz
    7.查找指令
    grep:字符串匹配(从一个文件的内容中匹配一个字符串)
    样例:ps -ef | grep sshd -c 查找指定进程ssh个数
    -c:只输出匹配行的计数。
    -i:不区分大小写。
    -h:查询多文件时不显示文件名。
    -l:查询多文件时只输出包含匹配字符的文件名。
    -n:显示匹配行及 行号。
    -s:不显示不存在或无匹配文本的错误信息。
    -v:显示不包含匹配文本的所有行。
    find:从指定的目录下查找文件或目录。
    用法:find 路径 参数 文件名
    样例:find /home/ygt -name test.txt查找自己账户下文件名为test.txt的文件
    以文件名查找:find 目录 -name “文件名或者目录名”
    以文件类型查找:find 目录 -type d(目录型文件)/f(普通文件)
    以文件大小查找:find 目录 -size +nk(超过nk)/-n(小于nk)
    按分钟查找:find 目录 -amin/-bmin/-mmin +n(n分钟之外)/-n(n分钟之内)
    按天查找:find 目录 -atime/-ctime/-mtime +n(n天之外)/-n(n天之内)
    locate:更快速的定位文件。(默认每天自动更新一次,若要查询最近更改的文件得先updatedb 来更新检索数据库)
    样例:locate /etc/sh 搜索etc目录下所有以sh开头的文件
    locate pwd 查找和pwd相关的所有文件
    8.用户切换
    su
    sudo(为所有想使用root权限的普通用户设计的)
    9.查看当前目录
    pwd
    10.查看进程
    ps -ef
    11.结束进程
    kill

    相关文章

      网友评论

          本文标题:java面试八股文

          本文链接:https://www.haomeiwen.com/subject/hwbnvrtx.html