java 基础
1.Arrays.sort 实现原理和 Collection 实现原理
Collection.sort()底层会调用 Arrays.sort(),Arrays.sort()底层实现是 TimeSort,TimeSort 的算法就是先找到已经排好序数据的子序列, 然后对剩余部分数据进行排序, 然后在合并起来.
2.foreach 和 while 的区别(编译之后)
while 会读一行输入, 把它存入某个变量并执行循环体, 然后再找其他行的输入, 适用于不确定循环次数的情况; foreach 是增强 for 循环, 它是逐条读取, 在循环开始前会将所有输入全部读入, 适用于数组, 集合等确定长度的情况; 当输入内容非常大的时候 foreach 会非常占内存.
3. 线程池的种类, 区别和使用场景
a.newCachedThreadPool 创建一个可缓存线程池, 如果线程池长度超过处理需要, 可灵活回收空闲线程, 若无可回收, 则新建线程.
b.newFixedThreadPool 创建一个定长线程池, 可控制线程的最大并发数, 超出的线程会进入阻塞队列中等待.
c.newScheduledTreadPool 创建一个定长线程池, 支持定时或者周期性任务执行.
d.newSingleThreadPool 创建一个单线程化的线程池, 它只会用唯一的线程来执行任务, 保证所有任务按照指定顺序执行.
4. 线程的调度过程
a. 当线程池小于 corePoolSize 时, 新提交的任务会创建一个新的线程执行, 技术此线程池中有空闲线程;
b. 当线程池达到 corePoolSize 时, 新提交的任务将被放入 workQueue, 等待线程池中任务调度执行;
c. 当 workQueue 已满, 并且 maximunPoolSize> corePoolSize 时, 新提交的任务会创建新的线程执行;
d. 当提交的任务数超过 maximunPoolSize 时, 新提交的任务将被拒绝;
e. 当线程池中超过 corePoolSize 线程, 空闲时间达到 keepAliveTime 时, 关闭空闲线程;
f. 当线程池中设置 allowCoreThreadTimeOut=true 时, 线程池中 corePoolSize 线程超过 keepAliveTime 时也将被关闭;
5. 动态代理的几种方式
jdk 动态代理和 cglib 动态代理. jdk 动态代理是有 java 内部的反射机制实现的, 前提是代理类和目标类必须实现统一的接口; cglib 动态代理是借助 asm 来实现的.
6.HashMap 的并发问题
HashMap 是线程不安全的, 当 size 超过一定大小需要扩容时, 会重新计算 hashcode 值, 在多线程的情况下可能会产生死循环. 可以使用 HashTable 和 ConcurrentHashMap.
7.HashTable 和 ConcurrentHashMap 区别
当 HashTable 增加到一定数量时性能会急剧降低, 因为迭代时会锁很长时间, 而 ConcurrentHashMap 使用了分割, 无论数量多大, 都只需要锁 map 的某一个部分.
8. 反射的原理, 反射创建类实例的三种方式是什么
java 反射机制实在运行当中, 对任意一个类来说, 能够知道它的所有属性和方法, 都能调用它的任意一个属性和方法.
三种方式: getClass()方式, 调用类的静态属性 class,Class.forName().
9.hashmap 的理解
hashmap 是链表 + 数组的存储结构, 外层是一个链表, 而每个链表中又是一个数组, 存储时先通过 key 获取 hashcode 并计算出 value 存储的链表位置, 如果不存在 hash 碰撞则 value 存在该链表中数组的第一个位置, 如果发生 hash 碰撞则存在该链表的数组的最后一个位置.
10.arraylist 和 linkedlist 区别及实现原理
arraylist 是基于动态数组的数据结构, 而 linkedlist 是基于链表的数据结构; 对于查询来说 arraylist 优于 linkedlist, 而对于删除和新增则 linkedlist 更好些.
11. 反射中, Class.forName 和 ClassLoader 区别
java 类加载的过程包括: 加载 ->验证 ->准备 ->解析 ->初始化 ->使用 ->卸载, 而初始化就是激活 java 类中静态变量初始化代码和静态代码块, 并初始化程序设置的变量值.
Class.forName 会执行类的初始化, 而 ClassLoader 不会执行类的初始化.
JVM 相关
1.jvm 的理解:
java 虚拟机内存分 5 个部分, 程序计数器, java 虚拟机栈, 本地方法栈, 堆, 方法区.
程序计数器记录当前代码执行位置, 进行代码流程控制, 多线程时记录当前线程执行的位置, 从而记录线程返回时上一次执行到哪了, 程序计数器是一个很小的内存空间, 每个线程都有一个程序计数器, 随着线程的启动而创建线程的结束而消亡, 不会出现 OutOfMemoryError.
java 虚拟机栈, java 虚拟机会为每一个即将运行的方法分配一个叫 "栈帧" 的区域, 记录该方法执行过程中所需要记录的信息(局部变量表, 操作数栈, 动态链接, 方法出口等信息), 方法运行时需要创建的局部变量被存放在局部变量表中, 当方法执行完后这个这个方法所对应的栈帧将会出栈, 释放空间, java 虚拟机栈也是随着线程的创建而创建, 线程的结束而释放, 会出现 2 中异常: StackOverFlowError 和 OutOfMemoryError
本地方法栈和 java 虚拟机栈类似, 只不过是用来运行本地方法的内存模型.
堆是用来存放对象的内存空降, 几乎所有的对象都存放在堆中, java 中堆时内存共享的随着虚拟机的启动而创建, 堆是垃圾回收的主要场所, 又可以分为新生代和老年代, 堆的大小是可以扩展的, 但也会抛出 OutOfMemoryError.
方法区是堆的一个逻辑部分, 存放着已经被虚拟机加载的类的信息, 常量, 静态变量, 编辑后的代码等, 方法区是内存共享的, 整个虚拟机只有一个方法区, 可以看做是堆的老年代, 这里回收效率比较低, 主要回收目标是对常量池的回收和类型的卸载.
2.java 类创建的过程
当虚拟机遇到一个 new 指令时, 检查常量池中是否有该对象所属类的符号引用, 如果没有则抛出 ClassNotFoundExecption 异常, 如果有, 则检查这个符号所代表的类是否已经被 jvm 加载, 如果没有被加载, 则找到该类的 class 文件并加载进方法区, 如果已经加载, 则根据方法区信息为该对象在堆中分配一块内存空间指给新的对象.
3. 如何判断哪些对象需要回收
引用计数法: 每个对象都有一个计数器, 当这个对象被引用时计数器加 1, 不被引用时减 1, 当这个计数器为 0 时则认为这个对象时无效对象.
可达性分析法: 所有和 gc roots(java 虚拟机栈所引用的对象, 方法区中静态属性和常量应用的对象, 本地方法栈所引用的对象)有直接或间接关系的对象认为是有效对象, 和 gc roots 没有关系的对象认为是无效对象.
4.java 类实例创建的过程
先父类后子类; 父类静态 - 子类静态 - 父类初始化块 - 父类构造方法 - 子类初始化块 - 子类构造方法.
5.synchronized 与 lock 的区别
synchronized 在同步块执行完成或者发生异常时释放锁, lock 必须在 finally 中释放锁, 不然容易死锁; synchronized 假如线程 A 获得锁, 线程 B 线程等待, 如果线程 A 阻塞, 则线程 B 一直等待 lock 有多种获取锁的方法, B 可以不用等待尝试去获取锁; synchronized 锁是非公平的, lock 可以公平也可以非公平; synchronized 适用于少量的代码同步, lock 适用于大量的代码同步.
spring
1.Spring AOP 与 IoC 的实现原理
IoC 是 spring 最核心的内容, 控制反转, 也叫依赖注入, 让一个对象的创建不需要 new 就能产生, 实际上是通过反射机制, 在对象调用的时候动态的创建和调用某个对象和方法; 注入的方法有 set 注入, 构造器注入和基于注解的注入.
aop 也是 spring 的一大特色, aop 通过动态代理实现了切面编程, 可以用于对某个类的监督和管理, 从而达到了一个模块扩充的功能.
spring 的的目的就是让对象与对象之间的关系变成 xml 配置, 从而实现了对象之间的解耦, spring 就是一个容器, 只有在容器中的对象才能提供这些功能和服务.
2.BeanFactory 和 FactoryBean 的区别
BeanFactory 是 IoC 的核心接口, 用来创建和管理 bean, 它为其他 IoC 容器提供了最基本的规范, FactoryBean 是 IoC 容器中为 bean 的实现提供了更加灵活的方式, FactoryBean 在 IoC 容器中是给 bean 加了一个工厂模式和装饰模式.
3. 为什么 CGlib 方式可以对接口的实现进行代理
cglib 采用了非常底层的字节码技术, 通过字节码为一个类创建子类, 并在子类中采用方法拦截的方式拦截所有父类的调用顺势植入横切逻辑, 所以传入接口就能实现.
分布式相关
1.Dubbo 的底层实现原理和机制
client 调用一个远程接口, 生成一个唯一 id, 打包调用信息 (接口, 方法, 参数), 和结果返回对象 callback, 将其封装成一个 object, 向专门的调用信息 concurrentHashMap 中 put(id,object), 然后将 id 和 object 封装成一个 conRequest, 使用 IOSession.write(conRequest) 异步发送请求, 然后当前线程在试图使用 callback 的 get()方法获取返回值, 在 get 方法内部使用 synchronized 获取 callback 锁, 如果获取不到结果则调用 callback 的 wait()方法, 释放 callback 锁并使线程处于等待状态, 服务端处理后将结果返回给客户端, 客户端的 socket 专门监听消息的线程接收到消息, 分析结果取到 id, 从 concurrentHashMap 中获取 object 中的 callback, 将结果设置到 callback 中, 监听线程在用 synchronized 获取 callback 锁, 在 notifyAll()唤醒当前处于等待的线程.
2. 分布式系统怎么做服务治理
服务降级, 服务流控, 服务动态扩展, 超时控制, 优先级调度, 负载均衡策略调整, 分组调整, 等.
3. 对分布式事务的理解
消息事物 + 最终一致性
tcc 编程模式
4. 如何实现负载均衡, 有哪些算法可以实现
轮询法, 随机法, 源地址 Hash 法, 加权轮询法, 加权随机法, 最小链接数法
Redis & 缓存相关
1.Redis 的并发竞争问题如何解决
使用 Redis 自带的 incr 命令
使用独占锁方式
程序加锁
利用 Redis 的 setnx 实现内置的锁
2.Redis 持久化的几种方式, 优缺点是什么, 怎么实现的
RDB 存入缓存快照, AOF 存入 Redis 执行命令
3.Redis 的缓存失效策略
Redis 在 get 时判断是否过期, 如果过期则删除, 同事 Redis 会定时删除过期的 key.
4. 缓存穿透的解决办法
即使查询出来的值为 null 也存入一个默认的值, 可以加上一个很短的过期时间
给缓存的 key 上加一个规则, 如果不合法的 key, 则直接过滤掉
采用布隆过滤器
四, 总结
在此我向大家推荐一个 java 互联网架构学习交流群. 交流学习 Q 群号: 815220368 加群获得以上学习视频, 群里面还会分享一些资深架构师录制的视频录像: 有 Spring,MyBatis,Netty 源码分析, 高并发, 高性能, 分布式, 微服务架构的原理, JVM 性能优化, 分布式架构等这些成为架构师必备的知识体系. 还能领取免费的学习资源, 目前受益良多
来源: http://www.jianshu.com/p/a870aea13c08