美文网首页
高级java工程师面经总结(更新中)

高级java工程师面经总结(更新中)

作者: _Rondo | 来源:发表于2019-11-21 21:36 被阅读0次

前言:最近在面高级,总结一些需要知道的知识,一边在面一边在写,欢迎补充

一、java 基础

1.基本数据类型
数据类型 字节长度 类型
byte 1 整型
shot 2 整型
int 4 整型
long 8 整型
float 4 浮点型
double 8 浮点型
boolean - 布尔型
char 2 字符型

String 是java中特殊的类,用来接收字符串,String的常量池和Integer的IntegerCache异曲同工,关于String 有兴趣的朋友可以看看这篇https://www.cnblogs.com/zhangyinhua/p/7689974.html

        String a = "a";
        String b = new String("a");
        String c = "a";
        //入常量池
        String d = new String("a").intern();
        System.out.println(a == b);//false
        System.out.println(a == c);//true
        System.out.println(a == d);//true

        Integer integer = 17;
        Integer integer1 = 17;
        System.out.println(integer == integer1);//true
        Integer integer2 = 128;
        Integer integer3 = 128;
        System.out.println(integer2 == integer3);//false
2.特性

①基本特性
封装:属性、方法的封装
多态:就是一个类中同名的方法不同参数和返回值
继承:选择题比较多,主要涉及父类中的方法重写,和父类静态属性和方法不能重写

②高级特性
反射:运行状态中获取类的名和方法,对象也是;可以延伸出静态、动态代理的比较,也会有手写动态代理的简答题,关于反射和代理看下这两篇
https://www.cnblogs.com/yueshutong/p/9495001.html
https://www.cnblogs.com/maohuidong/p/7992894.html

//静态代理 示例,可以参考下SpringContextHolder
public interface Job {
    void testJob();
}
public class JobImpl implements Job{
    @Override
    public void testJob() {
        System.out.println("静态job");
    }
}
public class Proxy implements Job{

    private Job job;

    @Override
    public void testJob() {
        init();
        job.testJob();
    }

    private void init(){
        if(job == null){
            job = new JobImpl();
        }
    }

    public static void main(String[] args) {
        new Proxy().testJob();
    }
}


//动态代理示例
public interface Job {
    void doSomething();
}
public class JobImpl implements Job{

    @Override
    public void doSomething() {
        System.out.println("动态job");
    }
}
public class MyProxy implements InvocationHandler {

    private Job job;

    public MyProxy(Job j){
        this.job = j;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object object = method.invoke(job,args);
        return object;
    }

    public Object getProxyInstance(){
        return Proxy.newProxyInstance(job.getClass().getClassLoader(),job.getClass().getInterfaces(),this);
    }
}
public class Test {
    public static void main(String[] args) {
        Job job = new JobImpl();
        MyProxy proxy = new MyProxy(job);
        Job job1 = (Job) proxy.getProxyInstance();
        job1.doSomething();
    }
}

泛型:只会作用于编译时期,指定数据类型,可能会问下?通配符
类型转换:回考察一些自动转换、强制转换的计算和编译问题
序列化:可能以前做xml解析的时候还能被问到,现在都是json了没怎么被问
注解:类、方法、变量、参数和包等都可以被标注,获取到的时候配合拦截
器做逻辑,和python、js的装饰器不太一样

//注解示例
@Target(value = {ElementType.FIELD,ElementType.METHOD})
@Retention(value = RetentionPolicy.RUNTIME)
public @interface Hello {
    String value();
}

@Hello("hello")
public static void main(String[] args) throws NoSuchMethodException {
    Class cls = Test.class;
    Method method = cls.getMethod("main", String[].class);
    Hello hello = method.getAnnotation(Hello.class);    
}
3.集合(可问点很多)
关系图

看下这篇https://www.cnblogs.com/jing99/p/7057245.html

①Collection型,存储单个key

  • List接口 特点存储有序
  • 实现类ArrayList和LinkedList经常拿来比,前者是数组查询快,后者是链表存取快
  • Vector 和ArrayList很像,方法里全是synchronized,线程安全但是同步性差
  • Set 存储无序不可重复,问的少

②map型,以key-value形式进行存储;

  • Map :
    接口,同步性最高
  • HashMap :
    key,value 可空,key不可重复,相同的key后者会把前者覆盖掉;
    用链地址法(数组+链表)解决hash冲突,线程不安全;
    默认容量16,启动因子0.75;
    jdk1.8后增加了红黑树底层实现,可以用 Collections的synchronizedMap方法使HashMap具有线程安全的能力(没用过)
  • HashTable :
    key,value都不可空,有大量synchronized,线程安全同步性差
4.并发编程

①java内存模型

  • JMM:
    主内存中存储着共享变量,然后各自的本地内存中存储着共享变量的副本,并发编程就是解决线程间通信,个线程间私有内存和主内存中共享数据的读写问题
  • volatile 和 synchronized:
    volatile本质是在告诉jvm当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读取; synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住。
    volatile仅能使用在变量级别;synchronized则可以使用在变量、方法、和类级别的
    volatile仅能实现变量的修改可见性,不能保证原子性;而synchronized则可以保证变量的修改可见性和原子性
    看下这篇https://www.cnblogs.com/kaleidoscope/p/9506018.html
  • Lock 和 synchronized:
    1)Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的语言实现;
    2)synchronized在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而Lock在发生异常时,如果没有主动通过unLock()去释放锁,则很可能造成死锁现象,因此使用Lock时需要在finally块中释放锁;
    3)Lock可以让等待锁的线程响应中断,而synchronized却不行,使用synchronized时,等待的线程会一直等待下去,不能够响应中断;
    4)通过Lock可以知道有没有成功获取锁,而synchronized却无法办到。
    5)Lock可以提高多个线程进行读操作的效率。
    看下这篇https://blog.csdn.net/qq_38200548/article/details/82943222
//synchronized 
public class TestSync {
    public static void main(String[] args) {
        final TestSync test = new TestSync();
        // 线程1
        new Thread(() -> {
                test.get(Thread.currentThread());
        }).start();
        // 线程2
        new Thread(() -> {
                test.get(Thread.currentThread());
        }).start();
    }

    public synchronized void get(Thread thread) {
        long start = System.currentTimeMillis();
        int i = 0;
        while(System.currentTimeMillis() - start <= 1) {
            i++;
            if(i % 4 == 0) {
                System.out.println(thread.getName() + "正在进行写操作");
            } else {
                System.out.println(thread.getName() + "正在进行读操作");
            }
        }
        System.out.println(thread.getName() + "读写操作完毕");
    }
}

//Lock
public class TestLock {

    private ReentrantReadWriteLock rwl  = new ReentrantReadWriteLock();

    public static void main(String[] args) {

        final TestLock lock = new TestLock();

        new Thread(() -> {
            lock.get(Thread.currentThread());
            lock.write(Thread.currentThread());
        }).start();

        new Thread(() -> {
            lock.get(Thread.currentThread());
            lock.write(Thread.currentThread());
        }).start();
    }

    /**
     * 读操作,用读锁锁定
     * @param thread
     */
    public void get(Thread thread) {
        boolean bool = rwl.readLock().tryLock();
        if(bool){
            try {
                long start = System.currentTimeMillis();
                while(System.currentTimeMillis() - start <= 1) {
                    System.out.println(thread.getName() + "正在进行读操作");
                }
                System.out.println(thread.getName() + "读操作完毕");
            } catch (Exception e) {

            } finally {
                rwl.readLock().unlock();
            }
        }

    }
    /**
     * 写操作,用写锁来锁定
     * @param thread
     */
    public void write(Thread thread) {
        boolean bool = rwl.writeLock().tryLock();
        if(bool){
            try {
                long start = System.currentTimeMillis();
                while(System.currentTimeMillis() - start <= 1) {
                    System.out.println(thread.getName() + "正在进行写操作");
                }
            } catch (Exception e) {

            } finally {
                rwl.writeLock().unlock();
            }
        }
    }
}
  • final 在内存模型中被做了增强,使得被final修饰的参数被多线程引用时cpu不会进行重排序
public class FinalExample {
    int i;                            //普通变量
    final int j ;                      //final变量
    static FinalExample obj;

    public FinalExample(int j) {
        this.j = j;
    }

    public void FinalExample () {     //构造函数
        i = 1;                             //写final域
    }

    public static void writer(int j,Thread thread) {    //写线程A执行
        obj = new FinalExample (j);
        System.out.println(thread.getName()+"  写入: "+j);
    }

    public static void reader(Thread thread) {       //读线程B执行
        FinalExample object = obj;       //读对象引用
        int a = object.i;                //读普通域
        int b = object.j;                //读final域
        System.out.println(thread.getName()+"  读出 :"+b);
    }
}

public class Test {
    public static void main(String[] args) {
        int num = 2;

        new Thread(() -> {
            FinalExample.writer(num, Thread.currentThread());
        }).start();

        new Thread(() -> {
            FinalExample.reader(Thread.currentThread());
        }).start();

    }
}

③线程
看看这篇https://www.cnblogs.com/wxd0108/p/5479442.html

  • 线程的状态:
    NEW:线程刚被创建
    RUNNABLE:线程处于可执行状态
    BLOCKED、WAITING:线程被阻塞,具体区别后面说
    TERMINATED:线程执行结束,被终止
  • 如何创建线程:
    自定义 Runnable 实现
    继承 Thread 类
  • 常用方法:
    sleep 阻塞当前线程指定时长
    start 真正的多线程启动,有时会问run和start的区别
    interrupt 终端当前线程
    join 阻塞主线程直到调用线程完成后

④生产者-消费者的三种实现方式,看下这篇https://blog.csdn.net/a491857321/article/details/79262996

⑤线程池:

  • 降低资源消耗:通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
  • 提高响应速度:当任务到达时,任务可以不需要等到线程创建就能立即执行。
  • 提高线程的可管理性:线程是稀缺资源,如果无限制地创建,不仅会消耗系统资源,还会降低系统的稳定性。使用线程池可以进行统一分配、调优和监控。
//简单线程池示例
public interface ThreadPool <Job extends Runnable>{

    //执行一个Job,job需要实现Runnable
    void execute(Job job);

    //关闭线程池
    void shutdown();

    //增加工作者线程
    void addWorkers(int nums);

    //减少工作者线程
    void removeWorkers(int nums);

    //得到正在工作的线程数
    int getJobSize();
}

public class DefaultThreadPool <Job extends Runnable> implements ThreadPool<Job> {

    //最大工作者线程数
    private static final int MAX_WORKERS_NUMS = 10;
    //默认工作者线程数
    private static final int DEFAULT_WORKERS_NUMS = 5;
    //最小工作者线程数
    private static final int MIN_WORKERS_NUMS = 1;
    //工作列表
    private final LinkedList<Job> jobs = new LinkedList<Job>();
    //工作者线程
    private final List<Worker> workers = Collections.synchronizedList(new ArrayList<>());

    private int workerNums = DEFAULT_WORKERS_NUMS;

    private AtomicLong threadNum = new AtomicLong();

    public DefaultThreadPool(int workerNums) {
        int workNums = workerNums > MAX_WORKERS_NUMS ? MAX_WORKERS_NUMS :
                workerNums < MIN_WORKERS_NUMS ? MIN_WORKERS_NUMS : workerNums;
        initializaWorkers(workNums);
    }


    public DefaultThreadPool() {
        initializaWorkers(DEFAULT_WORKERS_NUMS);
    }

    private void initializaWorkers(int workerNums) {
        for(int i = 0; i < workerNums; i++) {
            Worker worker = new Worker();
            Thread thread = new Thread(worker, "WorkNumble" + threadNum.incrementAndGet());
            thread.start();
        }
    }

    @Override
    public void execute(Job job) {
        if(job == null) {
            synchronized (jobs) {
                jobs.addLast(job);
                jobs.notify();
            }
        }
    }

    @Override
    public void shutdown() {
        for(Worker worker : workers) {
            worker.showdown();
        }
    }

    @Override
    public void addWorkers(int nums) {
        synchronized (jobs) {
            if(nums + this.workerNums > MAX_WORKERS_NUMS)
                nums = MAX_WORKERS_NUMS - nums;
            initializaWorkers(nums);
            this.workerNums += nums;
        }
    }

    @Override
    public void removeWorkers(int nums) {
        synchronized (jobs) {
            if(nums >= this.workerNums) {

            }
            int count = 0;
            while(count < nums) {
                Worker worker = workers.get(count);
                if(workers.remove(worker)) {
                    worker.showdown();
                    count++;
                }
            }
            this.workerNums -= count;
        }
    }

    @Override
    public int getJobSize() {
        return jobs.size();
    }

    class Worker implements Runnable{

        volatile private boolean running = true;

        @Override
        public void run() {
            while(running) {
                Job job = null;
                synchronized (jobs) {
                    while(jobs.isEmpty()) {
                        try {
                            jobs.wait();
                        }catch (InterruptedException e) {
                            Thread.currentThread().interrupt();
                            return ;
                        }
                    }
                    job = jobs.removeFirst();
                }
                if(job != null) {
                    try {
                        job.run();
                    }catch (Exception e) {

                    }
                }
            }
        }

        public void showdown() {
            running  = false;
        }

    }
}

⑥连接池:可能回会一下等待超时模型

//连接池简单示例
public class ConnectionPool {

    private LinkedList<Connection> pool = new LinkedList<Connection>();

    public ConnectionPool(int initialSize) {
        if (initialSize > 0) {
            for (int i = 0; i < initialSize; i++) {
                pool.addLast(ConnectionDriver.createConnection());
            }
        }
    }

    public void releaseConnection(Connection connection) {
        if (connection != null) {
            synchronized (pool) {
                // 连接释放后需要进行通知,这样其他消费者能够感知到连接池中已经归还了一个连接
                pool.addLast(connection);
                pool.notifyAll();
            }
        }
    }// 在mills内无法获取到连接,将会返回null

    public Connection fetchConnection(long mills) throws InterruptedException {
        synchronized (pool) {
            // 完全超时
            if (mills <= 0) {
                while (pool.isEmpty()) {
                    pool.wait();
                }
                return pool.removeFirst();
            } else {
                long future = System.currentTimeMillis() + mills;
                long remaining = mills;
                while (pool.isEmpty() && remaining > 0) {
                    pool.wait(remaining);
                    remaining = future - System.currentTimeMillis();
                }
                Connection result = null;
                if (!pool.isEmpty()) {
                    result = pool.removeFirst();
                }
                return result;
            }
        }
    }
}
public class ConnectionDriver {
    static class ConnectionHandler implements InvocationHandler {
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            if (method.getName().equals("commit")) {
                TimeUnit.MILLISECONDS.sleep(100);
            }
            return null;
        }
    }// 创建一个Connection的代理,在commit时休眠100毫秒

    public static final Connection createConnection() {
        return (Connection) Proxy.newProxyInstance(ConnectionDriver.class.getClassLoader(),
                new Class<?>[] { Connection.class }, new ConnectionHandler());
    }
}
public class ConnectionPoolTest {
    static ConnectionPool pool = new ConnectionPool(10);
    // 保证所有ConnectionRunner能够同时开始
    static CountDownLatch start = new CountDownLatch(1);
    // main线程将会等待所有ConnectionRunner结束后才能继续执行
    static CountDownLatch end;

    public static void main(String[] args) throws Exception {
        // 线程数量,可以修改线程数量进行观察
        int threadCount = 10;
        end = new CountDownLatch(threadCount);
        int count = 20;
        AtomicInteger got = new AtomicInteger();
        AtomicInteger notGot = new AtomicInteger();
        for (int i = 0; i < threadCount; i++) {
            Thread thread = new Thread(new ConnetionRunner(count, got, notGot), "ConnectionRunnerThread");
            thread.start();
        }
        start.countDown();
        end.await();
        System.out.println("total invoke: " + (threadCount * count));
        System.out.println("got connection: " + got);
        System.out.println("not got connection " + notGot);
    }

    static class ConnetionRunner implements Runnable {
        int count;
        AtomicInteger got;
        AtomicInteger notGot;

        public ConnetionRunner(int count, AtomicInteger got, AtomicInteger notGot) {
            this.count = count;
            this.got = got;
            this.notGot = notGot;
        }

        public void run() {
            try {
                start.await();
            } catch (Exception ex) {
            }
            while (count > 0) {
                try {
                    // 从线程池中获取连接,如果1000ms内无法获取到,将会返回null
                    // 分别统计连接获取的数量got和未获取到的数量notGot
                    Connection connection = pool.fetchConnection(1000);
                    if (connection != null) {
                        try {
                            connection.createStatement();
                            connection.commit();
                        } finally {
                            pool.releaseConnection(connection);
                            got.incrementAndGet();
                        }
                    } else {
                        notGot.incrementAndGet();
                    }
                } catch (Exception ex) {
                } finally {
                    count--;
                }
            }
            end.countDown();
        }
    }
}

⑦Fork/Join:将一个线程拆分到几个几个线程中并行计算结果

//斐波那契 forkjoin
public class RecursiveTaskDemo {

    private static class Fibonacci extends RecursiveTask<Integer> {

        final int n;

        public Fibonacci(int n) {
            this.n = n;
        }

        @Override
        protected Integer compute() {
            if (n <= 1) {
                return n;
            }else {
                Fibonacci f1 = new Fibonacci(n - 1);
                f1.fork();
                Fibonacci f2 = new Fibonacci(n - 1);
                return f2.compute() + f1.join();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException, ExecutionException {
        ForkJoinPool pool = new ForkJoinPool();
        Future<Integer> future = pool.submit(new Fibonacci(10));
        System.out.println(future.get());
        pool.shutdown();
    }
}

⑧并发容器和Executor(书没看完,暂时不写)

5.IO

①IO
io操作数据流和数组,两者可以相互转换,字节流继承inputstream,outputstream;字符流继承Writer,Reader,目前还没被问到,下面是关系图


image.png

②NIO 是一种同步非阻塞的I/O模型,也是I/O多路复用的基础,包括buffer、channel、selector,写过通讯其实很好理解。

③AIO 异步非阻塞的I/O模型(没用过暂时不写)

6.JVM

①JVM组成

  • 程序计数器:当前线程所执行的字节码的行号指示器,用于记录正在执行的虚拟机字节指令地址;
  • JVM栈:++存放编译期间可知的8种基本数据类型,及对象引用和指令地址++、方法出口等;
  • 本地方法栈:本地方法栈和虚拟机栈类似,只不过本地方法栈为Native方法服务;
  • 堆:堆被所有线程共享区域,java内存最大的一块,在虚拟机启动时创建,唯一目的存放对象实例。
    Java堆是垃圾收集器管理的主要区域,有时候也被称为“GC堆”。因为现在收集器基本都采用分代收集算法,所有Java堆还可以细分为:新生代和老年代,再细致一点有Eden空间、From Survivor空间、To Survivor空间等,默认情况下各自占比 8:1:1。
  • 方法区: 各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据,习惯是也叫它永久代(permanment generation)

②四种引用

  • 强引用(StrongReference):
    A a = new A()的形式就是强引用,对象是强引用就不会被回收;
  • 软引用(SoftReference):
    如果一个对象只具有软引用,则内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存;
  • 弱引用(WeakReference):
    在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。
  • 虚引用(PhantomReference):
    如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。
    看下这篇https://blog.csdn.net/sadeer126/article/details/89188572

③GC(还没看明白待完善)
④优化(没做过暂时不写)

二、Spring

1.spring bean

就是先编写一个XML文件,然后使用Spring框架了来读取这个配置文件就可以使用这个bean,有时候会问springbean 和 java bean 区别

2.AOP/IOC
  • 面向切面编程在spring中主要表现为两个方面 :面向切面编程提供声明式事务管理 和 用户自定义的切面
    依赖注入。
  • 由 IOC 容器动态的将某个对象所需要的外部资源(包括对象、资源、常量数据)注入到组件( spring 中的组件如:controller, service..等)之中。
3.spring 事务
  • 事务的传播行为:
    ① PROPAGATION_REQUIRED 支持当前事务,如果不存在,就创建一个
    ② PROPAGATION_SUPPORTS 支持当前事务,如果不存在,就不使用事务
    ③ PROPAGATION_MANDATORY 支持当前事务,如果不存在,就抛出异常
    ④ PROPAGATION_REQUIRES_NEW 如果有事务存在,挂起当前事务,创建一个新的事务
    ⑤ PROPAGATION_NOT_SUPPORTED 以非事务方式运行,如果有事务存在,挂起当前事务
    ⑥ PROPAGATION_NEVER 以非事务方式运行,如果有事务存在,抛出异常
    ⑦ PROPAGATION_NESTED 如果当前事务存在,则嵌套事务执行
  • spring隔离级别
    ①ISOLATION_DEFAULT:使用数据库默认的隔离级别
    ②ISOLATION_READ_UNCOMMITTED:最低的隔离级别,允许读取已改变而没有提交的数据,可能会导致脏读、幻读或不可重复读
    ③ISOLATION_READ_COMMITTED:允许读取事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生
    ④ISOLATION_REPEATABLE_READ:对同一字段的多次读取结果都是一致的,除非数据事务本身改变,可以阻止脏读和不可重复读,但幻读仍有可能发生
    ⑤ISOLATION_SERIALIZABLE:最高的隔离级别,完全服从ACID的隔离级别,确保不发生脏读、不可重复读以及幻读,也是最慢的事务隔离级别,因为它通常是通过完全锁定事务相关的数据库表来实现的
4.springmvc 执行流程:

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

5.spring 中的设计模式

1.简单工厂又叫做静态工厂方法(StaticFactory Method)
2.工厂方法(Factory Method)
3.单例模式(Singleton)
4.适配器(Adapter)
5.包装器(Decorator)
6.代理(Proxy)
7.观察者(Observer)
8.策略(Strategy)
9.模板方法(Template Method)

三、计算机组成原理/网络

1.OSI七层协议模型、TCP/IP四层模型和五层协议体系结构之间的关系

看下这篇https://www.cnblogs.com/wxd0108/p/7597216.html

image.png
2.TCP/IP和UDP
  • TCP面向连接;UDP是无连接的,即发送数据之前不需要建立连接
  • TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付
    Tcp通过校验和,重传控制,序号标识,滑动窗口、确认应答实现可靠传输。如丢包时的重发控制,还可以对次序乱掉的分包进行顺序控制。
  • UDP具有较好的实时性,工作效率比TCP高,适用于对高速传输和实时性有较高的通信或广播通信。
  • 每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信
  • TCP对系统资源要求较多,UDP对系统资源要求较少。
    https://blog.csdn.net/zhang6223284/article/details/81414149
3.HTTP

超文本传输协议

三、DB/SQL

1.关系型DB(mysql)

①索引
②优化

2.NoSQL

①redis
②memcache
③mongodb

四、服务器/容器

1.Linux
2.Docker(入门级别,欢迎指导)

五、分布式/MQ

1.分布式理论-CAP/BASE
2.分布式锁原理及实现方式
3.分布式事务
4.JMS/MQTT

六、数据结构/算法

1.数组/链表/队列
2.树
3.堆栈
3.算法

①选择/冒泡排序
②递归
③快速排序

七、Git

八、WEB前端

1.DOM
2.JavaScript

九、安卓

1.四大组件

十、大数据

1.hadoop、pig
2.hbase

十一、值得看的题目

1.sql
2.算法

十二、没想到吧还有发散思维题

相关文章

网友评论

      本文标题:高级java工程师面经总结(更新中)

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