Java技术知识点总结

Java总结

JAVA基础

1、JAVA中的几种基本数据类型是什么,各自占用多少字节。

Java技术知识点总结_第1张图片

2、String类能被继承吗,为什么。

String不能被继承,使用了final关键字定义String类。

3、String,Stringbuffer,StringBuilder的区别。

可变与不可变:
String类中使用字符数组保存字符串,如下就是,因为有“final”修饰符,所以可以知道string对象是不可变的。private final char value[]; StringBuilder与StringBuffer都继承自AbstractStringBuilder类,在AbstractStringBuilder中也是使用字符数组保存字符串,如下就是,可知这两种对象都是可变的。char[] value;

是否多线程安全:
String中对象是不可变的,可以理解为线程安全。
StringBuffer对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。
StringBuilder并没有对方法进行加同步锁,所以是非线程安全的。

StringBuilder与StringBuffer共同点:

StringBuilder与StringBuffer有公共父类AbstractStringBuilder(抽象类)。

抽象类与接口的其中一个区别是:抽象类中可以定义一些子类的公共方法,子类只需要增加新的功能,不需要重复写已经存在的方法;而接口中只是对方法的申明和常量的定义。

StringBuilder、StringBuffer的方法都会调用AbstractStringBuilder中的公共方法,如super.append(…)。只是StringBuffer会在方法上加synchronized关键字,进行同步。

最后,如果程序不是多线程的,那么使用StringBuilder效率高于StringBuffer。

4、ArrayList和LinkedList有什么区别。

(1)、ArrayList是实现了基于动态数据的数据结构,LinkedList是基于链表结构。

(2)、对于随机访问的get方法,ArrayList的效率要高于LinkedList,因为LinkedList需要移动指针。

(3)、对于新增和删除操作,LinkedList要优于ArrayList,因为ArrayList要移动数据。

具体说明:
ArrayList源码:
Java技术知识点总结_第2张图片
在源码中可以看到,ArrayList是实现了基于动态数据的数据结构,而数组是一段连续的内存空间,在内存中我们可以简单表示下图样式:
Java技术知识点总结_第3张图片
因为数组在存储数据时是按顺序存储的,存储数据的内存也是连续的,所以他的特点就是寻址读取数据比较容易,插入和删除比较困难。对于ArrayList,它在集合的末尾删除或添加元素所用的时间是一致的,但是在列表中间的部分添加或删除时所用时间就会大大增加。但是它在根据索引查找元素的时候速度很快。

LinkedList源码:Java技术知识点总结_第4张图片
在源码中可以看出,LinkedList的底层数据结构为双向链表的数据结构。
当需要在首位置插入元素时,first 引用指向需要插入到链表中的节点对象,新的节点对象的next引用指向原先的首节点对象;
所以,对于LinkedList,它在插入、删除集合中任何位置的元素所花费的时间都是一样的,但是它根据索引查询一个元素的时候却比较慢。

ArrayList和LinkedList的缺点如下:
1、对ArrayList和LinkedList而言,在列表末尾增加一个元素所花的开销都是固定的。对ArrayList而言,主要是在内部数组中增加一项,指向所添加的元素,偶尔可能会导致对数组重新进行分配;而对LinkedList而言,这个开销是 统一的,分配一个内部Entry对象。

2、在ArrayList集合中添加或者删除一个元素时,当前的列表移动元素后面所有的元素都会被移动。而LinkedList集合中添加或者删除一个元素的开销是固定的。

3、LinkedList集合不支持 高效的随机随机访问(RandomAccess),因为可能产生二次项的行为。

4、ArrayList的空间浪费主要体现在在list列表的结尾预留一定的容量空间,而LinkedList的空间花费则体现在它的每一个元素都需要消耗相当的空间。

5、讲讲类的实例化顺序,比如父类静态数据,构造函数,字段,子类静态数据,构造函数,字段,当new的时候,他们的执行顺序。

1、父类的静态变量
2、父类的static静态代码块
3、子类的静态变量
4、子类的static静态代码块
5、从父类的第一行开始执行,将父类的成员变量和代码块按顺序执行。
6、父类构造函数
7、从子类的第一行开始执行,将子类的成员变量和代码块按顺序执行。
8、子类构造函数

public class Parent {

    public static Test b1 = new Test("Parent 静态变量") ;

    public int a2 = 9;

    int a3 = getA3();

    {
        int a4 = 10;
        System.out.println("Parent构造函数前的普通方法块 a2= "+a2+" a3 = "+ a3+" a4 = "+ a4);
    }

    public Parent(){
        System.out.println("Parent构造函数");
    }

    {
        System.out.println("Parent构造函数后的普通方法块");
    }

    static {
        System.out.println("Parent静态代码块");
    }

    int getA3() {
        System.out.println("getA3..");
        return 7;
    }

    public void methodA() {
        System.out.println("methodA");
    }


}

public class Child  extends Parent{

    public static Test b1 = new Test("Chlid 静态变量") ;

    int b2 = getB2();
    {
        int b3 = 5;
        System.out.println("Child 构造函数前的代码块 b1=" + b1 + " b2=" + b2 + " b3=" + b3);

    }

    public Child() {
        this(33);
        // super(44);//添加super语句,会导致实例化时直接执行父类带参数的构造函数
        System.out.print("Child 构造函数\n");
    }

    public Child(int num) {
        // 添加super语句,会导致实例化时直接执行父类带参数的构造函数
        // 前提是带参数的构造函数B会被运行(new实例化或this)
        // super(77);

        System.out.print("Child 带参数构造函数:" + num + "\n");
    }

    {
        System.out.println("Child 构造函数后的代码块");
    }
    static {
        System.out.println("Child的静态代码块");
    }

    int getB2() {
        System.out.println("getChild..");
        return 33;

    }

    @Override
    public void methodA() {
        System.out.println("methoaA int class Child");
        super.methodA();

    }

}

public static void main(String[] args) {

        Child b = new Child();
        b.methodA();
    }
``
**最终得到结果如下:**

```java
Parent 静态变量
Parent静态代码块
Chlid 静态变量
Child的静态代码块
getA3..
Parent构造函数前的普通方法块 a2= 9 a3 = 7 a4 = 10
Parent构造函数后的普通方法块
Parent构造函数
getChild..
Child 构造函数前的代码块 b1=com.liyaomeng.javatest.Test@4554617c b2=33 b3=5
Child 构造函数后的代码块
Child 带参数构造函数:33
Child 构造函数
methoaA int class Child
methodA

6、用过哪些Map类,都有什么区别,HashMap是线程安全的吗,并发下使用的Map是什么,他们内部原理分别是什么,比如存储方式,hashcode,扩容,默认容量等。

用过的Map如下:
HashMap、HashTable、LinkedhashMap、TreeMap。

(1)、HashMap
Hashmap 是一个最常用的Map,它根据键的HashCode 值存储数据,根据键可以直接获取它的值,具有很快的访问速度,遍历时,取得数据的顺序是完全随机的。

HashMap最多只允许一条记录的键为Null;允 许多条记录的值为 Null;HashMap不支持线程的同步,即任一时刻可以有多个线程同时写HashMap;可能会导致数据的不一致。如果需要同步,可以用 Collections的synchronizedMap方法使HashMap具有同步的能力,或者使用ConcurrentHashMap。

底层原理可以参考HashMap底层原理

(2)、HashTable
Hashtable对外提供的public函数几乎都是同步的(synchronized关键字修饰),线程安全。

key和value都不能为null。

HashTable的数据结构和HashMap一样,采用Entry数组 + 链表的方法实现。

HashTabale初始的容量为11,负载因子为0.75,这点和HashMap不同,HashMap初始化时容量大小总是2的幂次方,即使给定一个不是2的幂次方容量的值,也会自动初始化为最接近其2的幂次方的容量。

哈希值计算方式是:int hash = key.hashCode()

数组索引计算方式是:int index = (hash & 0x7FFFFFFF) % tab.length; // 0x7FFFFFFF 即 Integer.MAX_VALUE(01111111 11111111 11111111 11111111)确保index为正数。

让HashMap同步:Map m =Collections.synchronizedMap(hashMao);

HashMap的迭代器(Iterator)是fail-fast迭代器,Hashtable的iterator遍历方式支持fast-fail,用Enumeration不支持fast-fail。

(3)、LinkedHashMap
LinkedHashMap保存了记录的插入顺序,在用Iterator遍历LinkedHashMap时,先得到的记录肯定是先插入的.也可以在构造时 用带参数,按照访问顺序排序。在遍历的时候会比HashMap慢,不过有种情况例外,当HashMap容量很大,实际数据较少时,遍历起来可能会比 LinkedHashMap慢,因为LinkedHashMap的遍历速度只和实际数据有关,和容量无关,而HashMap的遍历速度和他的容量有关。

底层原理可以参考LinkedHashMap底层原理

(4)、TreeMap
TreeMap实现SortMap接口,能够把它保存的记录根据键排序,默认是按键值的升序排序,也可以指定排序的比较器,当用Iterator 遍历TreeMap时,得到的记录是排过序的。底层数据结构是红黑树

并发情况下,使用的是ConcurrentHashMap,底层原理以及存储方式,hashcode,扩容,默认容量等可以参考ConcurrentHashMap底层原理

7、JAVA8的ConcurrentHashMap为什么放弃了分段锁,有什么问题吗,如果你来设计,你如何设计。

分段锁最大并发是16,16个桶,而1.8中,将数组的节点进行锁住,优化了锁粒度,以及增加了并发访问量。
细节可以参考ConcurrentHashMap底层原理

8、有没有有顺序的Map实现类,如果有,他们是怎么保证有序的。

Hashmap和Hashtable 都不是有序的。

TreeMap和LinkedHashmap都是有序的。(TreeMap默认是key升序,LinkedHashmap默认是数据插入顺序)

TreeMap是基于比较器Comparator来实现有序的。

LinkedHashmap是基于链表来实现数据插入有序的。

9、抽象类和接口的区别,类可以继承多个类么,接口可以继承多个接口么,类可以实现多个接口么。

含有abstract修饰符的class即为抽象类,abstract 类不能创建实例对象。含有abstract方法的类必须定义为abstract class,abstract class类中的方法不必是抽象的。abstract class类中定义抽象方法必须在具体(Concrete)子类中实现,所以,不能有抽象构造方法或抽象静态方法。如果子类没有实现抽象父类中的所有抽象方法,那么子类也必须定义为abstract类型。

接口(interface)可以说成是抽象类的一种特例,接口中的所有方法都必须是抽象的。接口中的方法定义默认为public abstract类型,接口中的成员变量类型默认为public static final。

下面比较一下两者的语法区别:
1.抽象类可以有构造方法,接口中不能有构造方法。

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

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

4.抽象类中的抽象方法的访问类型可以是public,protected和(默认类型,虽然eclipse下不报错,但应该也不行),但接口中的抽象方法只能是public类型的,并且默认即为public abstract类型。

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

6.抽象类和接口中都可以包含静态成员变量,抽象类中的静态成员变量的访问类型可以任意,但接口中定义的变量只能是public static final类型,并且默认即为public static final类型。

7.一个类可以实现多个接口,但只能继承一个抽象类。

10、继承和聚合的区别在哪。

继承: 指的是一个类继承另外的一个类的功能,并可以增加它自己的新功能的能力,继承是类与类或者接口与接口之间最常见的关系;在Java中此类关系通过关键字extends明确标识。
Java技术知识点总结_第5张图片

聚合: 聚合体现的是整体与部分、拥有的关系,此时整体与部分之间是可分离的,他们可以具有各自的生命周期;比如计算机与CPU、公司与员工的关系等
Java技术知识点总结_第6张图片

11、IO模型有哪些,讲讲你理解的nio ,他和bio,aio的区别是啥,谈谈reactor模型。

JAVA BIO:同步并阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程并处理,如果这个连接不做任何事情会造成不必要的开销,当然可以通过线程池机制改善。

JAVA NIO:同步非阻塞,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有IO请求时才启动一个线程进行处理。

JAVA AIO(NIO2):异步非阻塞,服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理。

BIO同步并阻塞
tomcat采用的传统的BIO(同步阻塞IO模型)+线程池模式,对于十万甚至百万连接的时候,传统BIO模型是无能为力的:
①线程的创建和销毁成本很高,在linux中,线程本质就是一个进程,创建销毁都是重量级的系统函数
②线程本身占用较大的内存,像java的线程栈一般至少分配512K-1M的空间,如果系统线程过高,内存占用是个问题
③线程的切换成本高,操作系统发生线程切换的时候,需要保留线程的上下文,然后执行系统调用,如果线程数过高可能执行线程切换的时间甚至大于线程执行的时间,这时候带来的表现是系统load偏高,CPUsy使用率很高
④容易造成锯齿状的系统负载。系统负载是用活动线程数或CPU核心数,一旦线程数量高但外部网络环境不是很稳定,就很容易造成大量请求的结果同时返回,激活大量阻塞线程从而使系统负载压力过大。

NIO同步非阻塞
NIO基于Reactor,当socket有流可读或可写入socket,操作系统会相应的通知引用程序进行处理,应用再将流读取到缓冲区或写入操作系统。也就是,不是一个链接就要对应一个处理线程,而是一个有效请求对应一个线程,当连接没有数据时,是没有工作线程来处理的。
Java技术知识点总结_第7张图片
nio只有acceptor的服务线程是堵塞进行的,其他读写线程是通过注册事件的方式,有读写事件激活时才调用线程资源区执行,不会一直堵塞等着读写操作,Reactor的瓶颈主要在于acceptor的执行,读写事件也是在这一块分发。

AIO异步非阻塞IO
Java技术知识点总结_第8张图片
AIO需要一个链接注册读写事件和回调方法,当进行读写操作时,只须直接调用API的read或write方法即可,这两种方法均为异步,对于读操作而言,当有流可读取时,操作系统会将可读的流传入read方法的缓冲区,并通知应用程序;对于写操作而言,当操作系统将write方法传递的流写入完毕时,操作系统主动通知应用程序。即,read/write方法都是异步的,完成后会主动调用回调函数。

Reactor三种线程模型
(1)、单线程模型
单个线程以非阻塞IO或事件IO处理所有IO事件,包括连接、读、写、异常、关闭等等。单线程Reactor模型基于同步事件分离器来分发事件,这个同步事件分离器,可以看做是一个单线程的while循环。下图描述了单线程模型的处理过程,看起来与网上大部分资料的图片不同,但本质是相同的。
Java技术知识点总结_第9张图片
注意上面的Selector之所以会有OP_ACEEPT事件,是因为在单线程模型中,Selector轮询的是监听套接字与已连接客户端套接字的所有IO事件。

单线程处理所有IO事件的弊端很明显。没能利用计算机CPU多核的特性,一个线程某个时刻只能处理单个IO事件,此时如果有其他描述符有IO事件就绪(如来了一个新的连接),这些IO事件将暂时得不到处理。

(2)、多线程模型
一个线程/进程接收连接、一组线程/进程处理IO读写事件。也就是将accept的线程与处理读、写等IO事件的线程分离并且使用m多个线程以非阻塞IO或者事件IO来处理n个套接字的IO事件,这里的n一般远大于m,线程数m一般取CPU逻辑核心数的1-3倍,而套接字数n则取决于请求数和进程可以打开的最大描述符个数。下图是多线程模型
Java技术知识点总结_第10张图片
可以看到,这里把客户端的已连接套接字,转交给某个IO线程之后,由此线程轮询处理其之后的所有IO事件,这实际参考了netty4的线程模型设计。实际reactor的多线程模型,并不需要将已连接套接字绑定在某个线程上,也可以统一放在连接池中,由多个IOWork线程从池中取连接进行轮询并处理,但这样会复杂很多,而且容易出问题,比如说不同线程从同一个channel收到了write事件,这就类似惊群问题了;并且多线程并发操作同一个channel,后续很可能需要你将IO事件进行同步,与其如此,不如直接将channel绑定到一个线程,让channel上触发与处理IO事件逻辑上同步。netty3中channel(已连接套接字)入站事件由固定线程处理,出站事件由触发的线程处理,netty4中修改了设计,将channel绑定到固定的eventloop(线程)。

(3)、主从多线程模型
一组线程/进程接收连接、一组线程/进程处理IO读写事件。它与多线程模型的主要区别在于其使用一组线程或进程在一个共享的监听套接字上accept连接。这么做的原因是为了应付单个线程/进程不足以快速处理内核中监听套接字的已连接套接字队列(并发量极大)的情况。如下:
Java技术知识点总结_第11张图片
主从多线程模型,有可能引起惊群效应。不过这个问题已经渐渐被规避,内核可以保证连接只被唯一一个accept调用所获取,其余对此连接的accept调用将失败。

12、反射的原理,反射创建类实例的三种方式是什么。

java反射机制: 在运行状态下,对任意一个类都能知道这个类的所有属性和方法;并且对于任意一个对象,都能调用它的任意一个方法和属性。这种动态获取信息以及动态调用对象方法的功能称为Java的反射机制。

获取Class对象
1、调用某个对象的getClass()方法

Person p=new Person();
Class clazz=p.getClass();

2、调用某个类的class属性来获取该类对应的Class对象

Class clazz = Person.class;

3、使用Class类中的forName()静态方法

Class clazz = Class.forName("类的全路径");

示例:

     Class clazz=Class.forName("reflection.Person");
    //获取Person类的所有方法信息
    Method[] method=clazz.getDeclaredMethods();
        for(Method m:method){
        System.out.println(m.toString());
    }
    //获取Person类的所有成员属性信息
    Field[] field=clazz.getDeclaredFields();
        for(Field f:field){
        System.out.println(f.toString());
    }
    //获取Person类的所有构造方法信息
    Constructor[] constructor=clazz.getDeclaredConstructors();
        for(Constructor c:constructor){
        System.out.println(c.toString());
    }

通过反射创建对象
1、Class对象的newInstance()
这种方法要求该Class对象对应的类有默认的空构造器。

2、Constructor对象的newInstance()
- 先使用Class对象获取指定的Constructor对象
- 再调用Constructor对象的newInstance()方法来创建 Class对象对应类的实例
- 通过这种方法可以选定构造方法创建实例。

13、反射中,Class.forName和ClassLoader区别 。

Java中Class.forName和classloader都可以用来对类进行加载。

Class.forName(“className”);
其实这种方法调用的是:Class.forName(className, true, ClassLoader.getCallerClassLoader())方法

  • 参数一:className,需要加载的类的名称。
  • 参数二:true,是否对class进行初始化(需要initialize)
  • 参数三:classLoader,对应的类加载器

ClassLoader.laodClass(“className”);
其实这种方法调运的是:ClassLoader.loadClass(name, false)方法

  • 参数一:name,需要加载的类的名称
  • 参数二:false,这个类加载以后是否需要去连接(不需要linking)

可见Class.forName 除了将类的.class文件加载到jvm中之外,还会对类进行解释,执行类中的static块

而classloader只干一件事情,就是将.class文件加载到jvm中不会执行static中的内容,只有在newInstance才会去执行static块

所以这就是为什么在jdbc进行数据库操作的时候要用Class.forName进行驱动的加载。

14、描述动态代理的几种实现方式,分别说出相应的优缺点。

(1)JDK动态代理实现
jdk动态代理是jdk原生就支持的一种代理方式,它的实现原理,就是通过让target类和代理类实现同一个接口,代理类持有target对象,来达到方法拦截的作用,这样通过接口的方式有两个弊端,一个是必须保证target类有接口,第二个是如果想要对target类的方法进行拦截,那么就要保证这些方法都要在接口中声明,实现上略微优点限制。

package com.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class MyInvocationHandler implements InvocationHandler {

    public Object target;

    MyInvocationHandler() {
        super();
    }

    MyInvocationHandler(Object target) {
        super();
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if ("getName".equals(method.getName())) {
            System.out.println("++++++before " + method.getName() + "++++++");
            Object result = method.invoke(target, args);
            System.out.println("++++++after " + method.getName() + "++++++");
            return result;
        } else {
            Object result = method.invoke(target, args);
            return result;
        }
    }
}

package com.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

public class Main1 {
    public static void main(String[] args) {
        UserService userService = new UserServiceImpl();
        InvocationHandler invocationHandler = new MyInvocationHandler(userService);
        UserService userServiceProxy = (UserService) Proxy.newProxyInstance(userService.getClass().getClassLoader(),
                userService.getClass().getInterfaces(),invocationHandler);
        System.out.println(userServiceProxy.getName(1));
        System.out.println(userServiceProxy.getAge(1));
    }
}

输出结果:

++++++before getName++++++
------getName------
++++++after getName++++++
riemann
------getAge------
26

实现InvocationHandler,利用Proxy代理类来newProxyInstance。我们调用方法实际上是调用代理类的方法,代理类则可以通过我们传入的InvocationHandler反射调用原本的方法来实现无侵入的修改原有方法逻辑.

(2)cglib动态代理实现
Cglib是一个优秀的动态代理框架,它的底层使用ASM在内存中动态的生成被代理类的子类,使用CGLIB即使代理类没有实现任何接口也可以实现动态代理功能。CGLIB具有简单易用,它的运行速度要远远快于JDK的Proxy动态代理:

cglib有两种可选方式,继承和引用。第一种是基于继承实现的动态代理,所以可以直接通过super调用target方法,但是这种方式在spring中是不支持的,因为这样的话,这个target对象就不能被spring所管理,所以cglib还是才用类似jdk的方式,通过持有target对象来达到拦截方法的效果。

CGLIB的核心类:
net.sf.cglib.proxy.Enhancer – 主要的增强类
net.sf.cglib.proxy.MethodInterceptor – 主要的方法拦截类,它是Callback接口的子接口,需要用户实现
net.sf.cglib.proxy.MethodProxy – JDK的java.lang.reflect.Method类的代理类,可以方便的实现对源对象方法的调用,如使用:
Object o = methodProxy.invokeSuper(proxy, args);//虽然第一个参数是被代理对象,也不会出现死循环的问题。

net.sf.cglib.proxy.MethodInterceptor接口是最通用的回调(callback)类型,它经常被基于代理的AOP用来实现拦截(intercept)方法的调用。这个接口只定义了一个方法
public Object intercept(Object object, java.lang.reflect.Method method,
Object[] args, MethodProxy proxy) throws Throwable;

第一个参数是代理对像,第二和第三个参数分别是拦截的方法和方法的参数。原来的方法可能通过使用java.lang.reflect.Method对象的一般反射调用,或者使用 net.sf.cglib.proxy.MethodProxy对象调用。net.sf.cglib.proxy.MethodProxy通常被首选使用,因为它更快。

package com.proxy.cglib;

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
 
public class CglibProxy implements MethodInterceptor {
    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println("++++++before " + methodProxy.getSuperName() + "++++++");
        System.out.println(method.getName());
        Object o1 = methodProxy.invokeSuper(o, args);
        System.out.println("++++++before " + methodProxy.getSuperName() + "++++++");
        return o1;
    }
}

package com.proxy.cglib;
 
import com.meituan.hyt.test3.service.UserService;
import com.meituan.hyt.test3.service.impl.UserServiceImpl;
import net.sf.cglib.proxy.Enhancer;
 
public class Main2 {
    public static void main(String[] args) {
        CglibProxy cglibProxy = new CglibProxy();
 
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(UserServiceImpl.class);
        enhancer.setCallback(cglibProxy);
 
        UserService o = (UserService)enhancer.create();
        o.getName(1);
        o.getAge(1);
    }
}

输出结果:

++++++before CGLIB$getName$0++++++
getName
------getName------
++++++before CGLIB$getName$0++++++
++++++before CGLIB$getAge$1++++++
getAge
------getAge------
++++++before CGLIB$getAge$1++++++

需要注意的问题:
需要注意的是,当一个方法没有被aop事务包裹,在该方法内部去调用另外一个有aop事务包裹的方法时,这个方法的aop事务不会生效。比如:

public void register() {
    aopRegister();
}
 
@Transactional
public void aopRegister() {
 
}

因为通过上面的分析我们知道,在spring中,无论通过jdk的形式还是cglib的形式,代理类对target对象的方法进行拦截,其实都是通过让代理类持有target对象的引用,当外部引用aop包围的方法时,调用的其实是代理类对应的方法,代理类持有target对象,便可以控制target方法执行时的全方位拦截。

而如果在target的内部方法register调用一个aop包围的target方法aopRegister,调用的其实就是target自身的方法,因为这时候的this指针是不可能指向代理类的。所以事务是不能生效的。

优缺点分析:
使用JDK动态代理,目标类必须实现的某个接口,如果某个类没有实现接口则不能生成代理对象。
Cglib原理是针对目标类生成一个子类,覆盖其中的所有方法,所以目标类和方法不能声明为final类型。

从执行效率上看,Cglib动态代理效率较高。

15、jdk动态代理与cglib实现的区别。

java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。

cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。

1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP
2、如果目标对象实现了接口,可以强制使用CGLIB实现AOP
3、如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换

如何强制使用CGLIB实现AOP?
(1)添加CGLIB库,SPRING_HOME/cglib/*.jar
(2)在spring配置文件中加入

JDK动态代理和CGLIB字节码生成的区别?
(1)JDK动态代理只能对实现了接口的类生成代理,而不能针对类
(2)CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法
因为是继承,所以该类或方法最好不要声明成final

16、为什么CGlib方式可以对接口实现代理。

JDK实现动态代理需要实现类通过接口定义业务方法,对于没有接口的类,如何实现动态代理呢,这就需要CGLib了。CGLib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。JDK动态代理与CGLib动态代理均是实现Spring AOP的基础。

对接口进行代理的cglib,最后生成的源码是实现了该接口和Factory接口
对实现类进行代理的cglib,最后生成的源码是继承了实现类并实现了Factory接口

17、final的用途。

1、被final修饰的类不可以被继承

2、被final修饰的方法不可以被重写

3、被final修饰的变量不可以被改变。(被final修饰不可变的是变量的引用,而不是引用指向的内容,引用指向的内容是可以改变的)

18、写出三种单例模式实现 。

1、懒汉,线程不安全

public class Singleton {
    private static Singleton instance;
    private Singleton (){}

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

这种写法能够在多线程中很好的工作,而且看起来它也具备很好的lazy loading,但是,遗憾的是,效率很低,99%情况下不需要同步。

2、饿汉式

public class Singleton {  
    private static Singleton instance = new Singleton();  
    private Singleton (){}  
    public static Singleton getInstance() {  
    return instance;  
    }  
}

这种方式基于classloder机制避免了多线程的同步问题,instance在类装载时就实例化。目前java单例是指一个虚拟机的范围,因为装载类的功能是虚拟机的,所以一个虚拟机在通过自己的ClassLoader装载饿汉式实现单例类的时候就会创建一个类的实例。这就意味着一个虚拟机里面有很多ClassLoader,而这些classloader都能装载某个类的话,就算这个类是单例,也能产生很多实例。当然如果一台机器上有很多虚拟机,那么每个虚拟机中都有至少一个这个类的实例的话,那这样 就更不会是单例了。(这里讨论的单例不适合集群!)

3、静态内部类

public class Singleton {  
    private static class SingletonHolder {  
    private static final Singleton INSTANCE = new Singleton();  
    }  
    private Singleton (){}  
    public static final Singleton getInstance() {  
    return SingletonHolder.INSTANCE;  
    }  
} 

这种方式同样利用了classloder的机制来保证初始化instance时只有一个线程,这种方式是Singleton类被装载了,instance不一定被初始化。因为SingletonHolder类没有被主动使用,只有显示通过调用getInstance方法时,才会显示装载SingletonHolder类,从而实例化instance。想象一下,如果实例化instance很消耗资源,我想让他延迟加载!这个时候,这种方式相比第2种方式就显得很合理。

4、双重校验锁

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;
    }
}

这样方式实现线程安全地创建实例,而又不会对性能造成太大影响。它只是第一次创建实例的时候同步,以后就不需要同步了。

由于volatile关键字屏蔽了虚拟机中一些必要的代码优化,所以运行效率并不是很高,因此建议没有特别的需要不要使用。双重检验锁方式的单例不建议大量使用,根据情况决定。

19、如何在父类中为子类自动完成所有的hashcode和equals实现?这么做有何优劣。

20、请结合OO设计理念,谈谈访问修饰符public、private、protected、default在应用设计中的作用。

public: Java语言中访问限制最宽的修饰符,一般称之为“公共的”。被其修饰的类、属性以及方法不仅可以跨类访问,而且允许跨包(package)访问。

private: Java语言中对访问权限限制的最窄的修饰符,一般称之为“私有的”。被其修饰的类、属性以及方法只能被该类的对象访问,其子类不能访问,更不能允许跨包访问。

protect: 介于public 和 private 之间的一种访问修饰符,一般称之为“保护形”。被其修饰的类、属性以及方法只能被类本身的方法及子类访问,即使子类在不同的包中也可以访问。

**default:**即不加任何访问修饰符,通常称为“默认访问模式“。该模式下,只允许在同一个包中进行访问。

21、深拷贝和浅拷贝区别。

什么是拷贝?
开始之前,我要先强调一下 Java 中的拷贝是什么。首先,让我们对引用拷贝和对象拷贝进行一下区分。 引用拷贝, 正如它的名称所表述的意思, 就是创建一个指向对象的引用变量的拷贝。如果我们有一个 Car 对象,而且让 myCar 变量指向这个变量,这时候当我们做引用拷贝,那么现在就会有两个 myCar 变量,但是对象仍然只存在一个。
Java技术知识点总结_第12张图片
对象拷贝会创建对象本身的一个副本。因此如果我们再一次服务我们 car 对象,就会创建这个对象本身的一个副本, 同时还会有第二个引用变量指向这个被复制出来的对象。
Java技术知识点总结_第13张图片
浅拷贝
首先让我们来说说浅拷贝。对象的浅拷贝会对“主”对象进行拷贝,但不会复制主对象里面的对象。"里面的对象“会在原来的对象和它的副本之间共享。例如,我们会为一个 Person对象创建第二个 Person 对象, 而两个 Person 会共享相同的 Name 和 Address 对象。

让我们来看看代码示例。在示例中,我们有一个类 Person,类里面包含了一个 Name 和 Address 对象。拷贝构造器会拿到 originalPerson 对象,然后对其应用变量进行复制。

public class Person {
    private Name name;
    private Address address;

    public Person(Person originalPerson) {
         this.name = originalPerson.name;
         this.address = originalPerson.address;
    }
[]
}

浅拷贝的问题就是两个对象并非独立的。如果你修改了其中一个 Person 对象的 Name 对象,那么这次修改也会影响奥另外一个 Person 对象。

让我们在示例中看看这个问题。假如说我们有一个 Person 对象,然后也会有一个引用变量 monther 来指向它;然后当我们对 mother 进行拷贝时,创建第二个 Person 对象 son。如果在此后的代码中, son 尝试用 moveOut() 来修改他的 Address 对象, 那么 mother 也会跟着他一起搬走!

Person mother = new Person(new Name(), new Address());
[]
Person son  = new Person(mother);
[]
son.moveOut(new Street(), new City());

这种现象之所以会发生,是因为 mother 和son 对象共享了相同的 Address 对象,如你在示例 7 中所看到的描述。当我们在一个对象中修改了 Address 对象,那么也就表示两个对象总的 Address 都被修改了。
Java技术知识点总结_第14张图片
深拷贝
不同于浅拷贝,深拷贝是一个整个独立的对象拷贝。如果我们对整个 Person对象进行深拷贝,我们会对整个对象的结构都进行拷贝。
Java技术知识点总结_第15张图片
对一个 Person 的Address对象进行了修改并不会对另外一个对象造成影响。当我们观察示例中的代码,会发现我们不单单对 Person 对象使用了拷贝构造器,同时也会对里面的对象使用拷贝构造器。

public class Person {
    private Name name;
    private Address address;

    public Person(Person otherPerson) {
         this.name    =  new Name(otherPerson.name);
         this.address =  new Address(otherPerson.address);
    }
[]
}

22、数组和链表数据结构描述,各自的时间复杂度。

数组 是将元素在内存中连续存放,由于每个元素占用内存相同,可以通过下标迅速访问数组中任何元素。但是如果要在数组中增加一个元素,需要移动大量元素,在内存中空出一个元素的空间,然后将要增加的元素放在其中。同样的道理,如果想删除一个元素,同样需要移动大量元素去填掉被移动的元素。如果应用需要快速访问数据,很少插入和删除元素,就应该用数组。

链表 中的元素在内存中不是顺序存储的,而是通过存在元素中的指针联系到一起,每个结点包括两个部分:一个是存储 数据元素 的 数据域,另一个是存储下一个结点地址的 指针。如果要访问链表中一个元素,需要从第一个元素开始,一直找到需要的元素位置。但是增加和删除一个元素对于链表数据结构就非常简单了,只要修改元素中的指针就可以了。如果应用需要经常插入和删除元素你就需要用链表。

内存存储区别
数组从栈中分配空间, 对于程序员方便快速,但自由度小。

链表从堆中分配空间, 自由度大但申请管理比较麻烦.

逻辑结构区别
数组必须事先定义固定的长度(元素个数),不能适应数据动态地增减的情况。当数据增加时,可能超出原先定义的元素个数;当数据减少时,造成内存浪费。

链表动态地进行存储分配,可以适应数据动态地增减的情况,且可以方便地插入、删除数据项。(数组中插入、删除数据项时,需要移动其它数据项)。

23、error和exception的区别,CheckedException,RuntimeException的区别。

Java技术知识点总结_第16张图片
java.lang.Throwable是所有异常的根

java.lang.Error是错误信息

java.lang.Exception是异常信息

Exception
一般分为Checked异常和Runtime异常,所有RuntimeException类及其子类的实例被称为Runtime异常,不属于该范畴的异常则被称为CheckedException。

Checked异常
只有java语言提供了Checked异常,Java认为Checked异常都是可以被处理的异常,所以Java程序必须显示处理Checked异常。如果程序没有处理Checked异常,该程序在编译时就会发生错误无法编译。这体现了Java的设计哲学:没有完善错误处理的代码根本没有机会被执行。对Checked异常处理方法有两种

1 当前方法知道如何处理该异常,则用try…catch块来处理该异常。
2 当前方法不知道如何处理,则在定义该方法是声明抛出该异常。

我们比较熟悉的Checked异常有

Java.lang.ClassNotFoundException
Java.lang.NoSuchMetodException
java.io.IOException

RuntimeException
Runtime如除数是0和数组下标越界等,其产生频繁,处理麻烦,若显示申明或者捕获将会对程序的可读性和运行效率影响很大。所以由系统自动检测并将它们交给缺省的异常处理程序。当然如果你有处理要求也可以显示捕获它们。

我们比较熟悉的RumtimeException子类:

Java.lang.ArithmeticException
Java.lang.ArrayStoreExcetpion
Java.lang.ClassCastException
Java.lang.IndexOutOfBoundsException
Java.lang.NullPointerException

Error
当程序发生不可控的错误时,通常做法是通知用户并中止程序的执行。与异常不同的是Error及其子类的对象不应被抛出。
Error是throwable的子类,代表编译时间和系统错误,用于指示合理的应用程序不应该试图捕获的严重问题。
Error由Java虚拟机生成并抛出,包括动态链接失败,虚拟机错误等。程序对其不做处理。

24、请列出5个运行时异常。

Java.lang.ArithmeticException
Java.lang.ArrayStoreExcetpion
Java.lang.ClassCastException
Java.lang.IndexOutOfBoundsException
Java.lang.NullPointerException

25、在自己的代码中,如果创建一个java.lang.String类,这个类是否可以被类加载器加载?为什么。

26、说一说你对java.lang.Object对象中hashCode和equals方法的理解。在什么场景下需要重新实现这两个方法。

27、在jdk1.5中,引入了泛型,泛型的存在是用来解决什么问题。

泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数,泛型的好处是在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,以提高代码的重用率

28、这样的a.hashcode() 有什么用,与a.equals(b)有什么关系。

29、有没有可能2个不相等的对象有相同的hashcode。

30、Java中的HashSet内部是如何工作的。

底层是基于hashmap实现的

31、什么是序列化,怎么序列化,为什么序列化,反序列化会遇到什么问题,如何解决。

序列化 :把对象转换为字节序列的过程称为对象的序列化。
反序列化 :把字节序列恢复为对象的过程称为对象的反序列化。

java中一般将需要序列化的对象实现serializable接口。如果你想某个字段不序列化,将其声明为transient.
序列化的原因是想将对象转换成流,方便存储和在网络上传输。

如何实现序列化
实现Serializable接口即可

32、java8的新特性。

JVM知识

1、什么情况下会发生栈内存溢出。

如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常。 如果虚拟机在动态扩展栈时无法申请到足够的内存空间,则抛出OutOfMemoryError异常。

2、JVM的内存结构,Eden和Survivor比例。

3、JVM内存为什么要分成新生代,老年代,持久代。新生代中为什么要分为Eden和Survivor。

4、JVM中一次完整的GC流程是怎样的,对象如何晋升到老年代,说说你知道的几种主要的JVM参数。

5、你知道哪几种垃圾收集器,各自的优缺点,重点讲下cms和G1,包括原理,流程,优缺点。

6、垃圾回收算法的实现原理。

7、当出现了内存溢出,你怎么排错。

8、JVM内存模型的相关知识了解多少,比如重排序,内存屏障,happen-before,主内存,工作内存等。

9、简单说说你了解的类加载器,可以打破双亲委派么,怎么打破。

10、讲讲JAVA的反射机制。

11、你们线上应用的JVM参数有哪些。

12、g1和cms区别,吞吐量优先和响应优先的垃圾收集器选择。

13、怎么打出线程栈信息。

14、请解释如下jvm参数的含义:

-server -Xms512m -Xmx512m -Xss1024K
-XX:PermSize=256m -XX:MaxPermSize=512m -
XX:MaxTenuringThreshold=20XX:CMSInitiatingOccupancyFraction=80 -
XX:+UseCMSInitiatingOccupancyOnly。

开源框架知识

1、简单讲讲tomcat结构,以及其类加载器流程,线程模型等。

2、tomcat如何调优,涉及哪些参数 。

3、讲讲Spring加载流程。

4、Spring AOP的实现原理。

5、讲讲Spring事务的传播属性。

6、Spring如何管理事务的。

7、Spring怎么配置事务(具体说出一些关键的xml 元素)。

8、说说你对Spring的理解,非单例注入的原理?它的生命周期?循环注入的原理,aop的实现原理,说说aop中的几个术语,它们是怎么相互工作的。

9、Springmvc 中DispatcherServlet初始化过程。

10、netty的线程模型,netty如何基于reactor模型上实现的。

11、为什么选择netty。

12、什么是TCP粘包,拆包。解决方式是什么。

13、netty的fashwheeltimer的用法,实现原理,是否出现过调用不够准时,怎么解决。

14、netty的心跳处理在弱网下怎么办。

15、netty的通讯协议是什么样的。

16、springmvc用到的注解,作用是什么,原理。

17、springboot启动机制。

操作系统

1、Linux系统下你关注过哪些内核参数,说说你知道的。

2、Linux下IO模型有几种,各自的含义是什么。

3、epoll和poll有什么区别。

4、平时用到哪些Linux命令。

5、用一行命令查看文件的最后五行。

6、用一行命令输出正在运行的java进程。

7、介绍下你理解的操作系统中线程切换过程。

8、进程和线程的区别。

9、top 命令之后有哪些内容,有什么作用。

10、线上CPU爆高,请问你如何找到问题所在。

多线程

1、多线程的几种实现方式,什么是线程安全。

2、volatile的原理,作用,能代替锁么。

3、画一个线程的生命周期状态图。

4、sleep和wait的区别。

5、sleep和sleep(0)的区别。

6、Lock与Synchronized的区别 。

7、synchronized的原理是什么,一般用在什么地方(比如加在静态方法和非静态方法的区别,静态方法和非静态方法同时执行的时候会有影响吗),解释以下名词:重排序,自旋锁,偏向锁,轻量级锁,可重入锁,公平锁,非公平锁,乐观锁,悲观锁。

8、用过哪些原子类,他们的原理是什么。

9、JUC下研究过哪些并发工具,讲讲原理。

10、用过线程池吗,如果用过,请说明原理,并说说newCache和newFixed有什么区别,构造函数的各个参数的含义是什么,比如coreSize,maxsize等。

11、线程池的关闭方式有几种,各自的区别是什么。

12、假如有一个第三方接口,有很多个线程去调用获取数据,现在规定每秒钟最多有10个线程同时调用它,如何做到。

13、spring的controller是单例还是多例,怎么保证并发的安全。

14、用三个线程按顺序循环打印abc三个字母,比如abcabcabc。

15、ThreadLocal用过么,用途是什么,原理是什么,用的时候要注意什么。

16、如果让你实现一个并发安全的链表,你会怎么做。

17、有哪些无锁数据结构,他们实现的原理是什么。

18、讲讲java同步机制的wait和notify。

19、CAS机制是什么,如何解决ABA问题。

20、多线程如果线程挂住了怎么办。

21、countdowlatch和cyclicbarrier的内部原理和用法,以及相互之间的差别(比如countdownlatch的await方法和是怎么实现的)。

22、对AbstractQueuedSynchronizer了解多少,讲讲加锁和解锁的流程,独占锁和公平所加锁有什么不同。

22、使用synchronized修饰静态方法和非静态方法有什么区别。

23、简述ConcurrentLinkedQueue和LinkedBlockingQueue的用处和不同之处。

24、导致线程死锁的原因?怎么解除线程死锁。

25、非常多个线程(可能是不同机器),相互之间需要等待协调,才能完成某种工作,问怎么设计这种协调方案。

26、用过读写锁吗,原理是什么,一般在什么场景下用。

27、开启多个线程,如果保证顺序执行,有哪几种实现方式,或者如何保证多个线程都执行完再拿到结果。

28、延迟队列的实现方式,delayQueue和时间轮算法的异同。

点击这里有一套答案版的多线程试题。

TCP与HTTP

1、http1.0和http1.1有什么区别。

2、TCP三次握手和四次挥手的流程,为什么断开连接要4次,如果握手只有两次,会出现什么。

3、TIME_WAIT和CLOSE_WAIT的区别。

4、说说你知道的几种HTTP响应码,比如200, 302, 404。

5、当你用浏览器打开一个链接(如:http://www.javastack.cn)的时候,计算机做了哪些工作步骤。

6、TCP/IP如何保证可靠性,说说TCP头的结构。

7、如何避免浏览器缓存。

8、如何理解HTTP协议的无状态性。

9、简述Http请求get和post的区别以及数据包格式。

10、HTTP有哪些method

11、简述HTTP请求的报文格式。

12、HTTP的长连接是什么意思。

13、HTTPS的加密方式是什么,讲讲整个加密解密流程。

14、Http和https的三次握手有什么区别。

15、什么是分块传送。

16、Session和cookie的区别。

点击这里有一套答案版的试题。

架构设计与分布式

1、用java自己实现一个LRU。

2、分布式集群下如何做到唯一序列号。

3、设计一个秒杀系统,30分钟没付款就自动关闭交易。

4、如何使用redis和zookeeper实现分布式锁?有什么区别优缺点,会有什么问题,分别适用什么场景。(延伸:如果知道redlock,讲讲他的算法实现,争议在哪里)

5、如果有人恶意创建非法连接,怎么解决。

6、分布式事务的原理,优缺点,如何使用分布式事务,2pc 3pc 的区别,解决了哪些问题,还有哪些问题没解决,如何解决,你自己项目里涉及到分布式事务是怎么处理的。

7、什么是一致性hash。

8、什么是restful,讲讲你理解的restful。

9、如何设计一个良好的API。

10、如何设计建立和保持100w的长连接。

11、解释什么是MESI协议(缓存一致性)。

12、说说你知道的几种HASH算法,简单的也可以。

13、什么是paxos算法, 什么是zab协议。

14、一个在线文档系统,文档可以被编辑,如何防止多人同时对同一份文档进行编辑更新。

15、线上系统突然变得异常缓慢,你如何查找问题。

16、说说你平时用到的设计模式。

17、Dubbo的原理,有看过源码么,数据怎么流转的,怎么实现集群,负载均衡,服务注册和发现,重试转发,快速失败的策略是怎样的 。

18、一次RPC请求的流程是什么。

19、自己实现过rpc么,原理可以简单讲讲。Rpc要解决什么问题。

20、异步模式的用途和意义。

21、编程中自己都怎么考虑一些设计原则的,比如开闭原则,以及在工作中的应用。

22、设计一个社交网站中的“私信”功能,要求高并发、可扩展等等。 画一下架构图。

23、MVC模式,即常见的MVC框架。

24、聊下曾经参与设计的服务器架构并画图,谈谈遇到的问题,怎么解决的。

25、应用服务器怎么监控性能,各种方式的区别。

26、如何设计一套高并发支付方案,架构如何设计。

27、如何实现负载均衡,有哪些算法可以实现。

28、Zookeeper的用途,选举的原理是什么。

29、Zookeeper watch机制原理。

30、Mybatis的底层实现原理。

31、请思考一个方案,实现分布式环境下的countDownLatch。

32、后台系统怎么防止请求重复提交。

33、描述一个服务从发布到被消费的详细过程。

34、讲讲你理解的服务治理。

35、如何做到接口的幂等性。

36、如何做限流策略,令牌桶和漏斗算法的使用场景。

37、什么叫数据一致性,你怎么理解数据一致性。

38、分布式服务调用方,不依赖服务提供方的话,怎么处理服务方挂掉后,大量无效资源请求的浪费,如果只是服务提供方吞吐不高的时候该怎么做,如果服务挂了,那么一会重启,该怎么做到最小的资源浪费,流量半开的实现机制是什么。

39、dubbo的泛化调用怎么实现的,如果是你,你会怎么做。

40、远程调用会有超时现象,如果做到优雅的控制,JDK自带的超时机制有哪些,怎么实现的。

算法

1、10亿个数字里里面找最小的10个。

2、有1亿个数字,其中有2个是重复的,快速找到它,时间和空间要最优。

3、2亿个随机生成的无序整数,找出中间大小的值。

4、给一个不知道长度的(可能很大)输入字符串,设计一种方案,将重复的字符排重。

5、遍历二叉树。

6、有3n+1个数字,其中3n个中是重复的,只有1个是不重复的,怎么找出来。

7、写一个字符串(如:www.javastack.cn)反转函数。

8、常用的排序算法,快排,归并、冒泡。 快排的最优时间复杂度,最差复杂度。冒泡排序的优化方案。

9、二分查找的时间复杂度,优势。

10、一个已经构建好的TreeSet,怎么完成倒排序。

11、什么是B+树,B-树,列出实际的使用场景。

12、一个单向链表,删除倒数第N个数据。

13、200个有序的数组,每个数组里面100个元素,找出top20的元素。

14、单向链表,查找中间的那个元素。

数据库知识

1、数据库隔离级别有哪些,各自的含义是什么,MYSQL默认的隔离级别是是什么。

2、什么是幻读。

3、MYSQL有哪些存储引擎,各自优缺点。

4、高并发下,如何做到安全的修改同一行数据。

5、乐观锁和悲观锁是什么,INNODB的标准行级锁有哪2种,解释其含义。

6、SQL优化的一般步骤是什么,怎么看执行计划,如何理解其中各个字段的含义。

7、数据库会死锁吗,举一个死锁的例子,mysql怎么解决死锁。

8、MYsql的索引原理,索引的类型有哪些,如何创建合理的索引,索引如何优化。

9、聚集索引和非聚集索引的区别。

10、select for update 是什么含义,会锁表还是锁行或是其他。

11、为什么要用Btree实现,它是怎么分裂的,什么时候分裂,为什么是平衡的。

12、数据库的ACID是什么。

13、某个表有近千万数据,CRUD比较慢,如何优化。

14、Mysql怎么优化table scan的。

15、如何写sql能够有效的使用到复合索引。

16、mysql中in 和exists 区别。

17、数据库自增主键可能的问题。

18、MVCC的含义,如何实现的。

19、你做过的项目里遇到分库分表了吗,怎么做的,有用到中间件么,比如sharding jdbc等,他们的原理知道么。

20、MYSQL的主从延迟怎么解决。

消息队列

1、消息队列的使用场景。

2、消息的重发,补充策略。

3、如何保证消息的有序性。

4、用过哪些MQ,和其他mq比较有什么优缺点,MQ的连接是线程安全的吗,你们公司的MQ服务架构怎样的。

5、MQ系统的数据如何保证不丢失。

6、rabbitmq如何实现集群高可用。

7、kafka吞吐量高的原因。

8、kafka 和其他消息队列的区别,kafka 主从同步怎么实现。

9、利用mq怎么实现最终一致性。

10、使用kafka有没有遇到什么问题,怎么解决的。

11、MQ有可能发生重复消费,如何避免,如何做到幂等。

12、MQ的消息延迟了怎么处理,消息可以设置过期时间么,过期了你们一般怎么处理。

缓存

1、常见的缓存策略有哪些,如何做到缓存(比如redis)与DB里的数据一致性,你们项目中用到了什么缓存系统,如何设计的。

2、如何防止缓存击穿和雪崩。

3、缓存数据过期后的更新如何设计。

4、redis的list结构相关的操作。

5、Redis的数据结构都有哪些。

6、Redis的使用要注意什么,讲讲持久化方式,内存设置,集群的应用和优劣势,淘汰策略等。

7、redis2和redis3的区别,redis3内部通讯机制。

8、当前redis集群有哪些玩法,各自优缺点,场景。

9、Memcache的原理,哪些数据适合放在缓存中。

10、redis和memcached 的内存管理的区别。

11、Redis的并发竞争问题如何解决,了解Redis事务的CAS操作吗。

12、Redis的选举算法和流程是怎样的。

13、redis的持久化的机制,aof和rdb的区别。

14、redis的集群怎么同步的数据的。

15、知道哪些redis的优化操作。

16、Reids的主从复制机制原理。

17、Redis的线程模型是什么。

18、请思考一个方案,设计一个可以控制缓存总体大小的自动适应的本地缓存。

19、如何看待缓存的使用(本地缓存,集中式缓存),简述本地缓存和集中式缓存和优缺点。本地缓存在并发使用时的注意事项。

搜索

1、elasticsearch了解多少,说说你们公司es的集群架构,索引数据大小,分片有多少,以及一些调优手段 。elasticsearch的倒排索引是什么。

2、elasticsearch 索引数据多了怎么办,如何调优,部署。

3、elasticsearch是如何实现master选举的。

4、详细描述一下Elasticsearch索引文档的过程。

5、详细描述一下Elasticsearch搜索的过程。

6、Elasticsearch在部署时,对Linux的设置有哪些优化方法?

7、lucence内部结构是什么。

8、详细描述一下Elasticsearch文档写入的过程。

你可能感兴趣的:(面试)