这是从网上看到的一套java面试题, 答案只是一个大概, 另外题目质量参差不齐, 斟酌参考(JVM的部分暂时没有答案)
一、Java 基础
- JDK 和 JRE 有什么区别?
答: JDK(Java Development Kit)是java开发工具包, 是针对开发人员提供的一套开发环境, 其中包含了jre(程序运行环境,标准类库class文件)以及编译器javac等一系列工具. JRE(Java Runtime Environment)是Java运行时环境, 针对生产环境发布, 其中包含了JVM虚拟机以及标准类库的class文件.
另外, JVM(Java Virtual Machine)Java虚拟机, 将class字节码指令翻译为机器指令, 是跨平台的核心.
- == 和 equals 的区别是什么?
答: == 可以用于值类型和对象类型, 可用于判定两个值是否相等. 当用于引用类型时, 用来判定两个对象地址, 即是否指向相同的内存空间. equals()方法的Object的默认实现使用==比较, 也就是说默认就是比较对象地址是否相等, 但类库中的很多类, 包括包装类型, String等均重写了该方法. equals用来比较对象内容是否相等, 对于自定义类型, 可以重写此方法实现对象自定义的比较逻辑, 这个逻辑一般要求比较严谨复杂,所以同时也要重写hashCode()方法,毕竟如果能在实现中先使用hashCode比较可能会对性能有积极影响.
- 两个对象的 hashCode()相同,则 equals()也一定为 true,对吗?
答: 不对! hashCode()相同, 因为可能存在hash碰撞, 也可能导致不同的对象计算出相同的hash值. 另外, hashCode()和equals()方法都是Object对象的方法, 自定义类型可能会覆盖也可能不覆盖这两个方法, 所以说他们一定为true是不对的. 一般而言, 如果equals()方法返回true, 则hashCode()的值相同, 反之不一定成立.
- final 在 java 中有什么作用?
答: final 可以修饰域变量, 方法, 类. 修饰的域变量的值不能被重新赋值, 修饰的方法不能在子类中被重写, 修饰的类不能被继承. final是保证程序不被恶意篡改的手段.
final经常是static一起使用.
final的优点: 1.提示了性能, jvm和程序会缓存变量, 方法是静态绑定的; 2. 安全地在多线程环境下共享变量,没有多余开销;
final变量一般要大写, 局部变量需要声明时就赋值, 但域变量可以在构造函数, 初始化块儿内赋值.
- java 中的 Math.round(-1.5) 等于多少?
答: 1 就近舍入也叫银行家舍入. 方法是原值+0.5后下取整.
- String 属于基础的数据类型吗?
答: 不属于! 基本数据类型只有8种. boolean byte short int long float double char . String是引用类型, 并将其设置为final, String的具体字符串值在常量池中存在.
- java 中操作字符串都有哪些类?它们之间有什么区别?
答: 除了基本的String, 还有StringBuidler和StringBuffer. String做字符串操作时效率太低, 因为其不可变性, 可能会有大量对象创建而占用大量内存. 而后两者用来处理字符串对象, 不同之处在于StringBuffer时线程安全的, StringBuilder为了效率考虑线程不安全.
- String str="i"与 String str=new String(“i”)一样吗?
答: 不一样, 字符串存在常量池中, 如果常量池中不存在, 则创建; 如果使用new 创建, 则需要在堆上创建String对象, 并在常量池中创建字符串"i"(如果不存在的话).
- 如何将字符串反转?
答: 有若干方法, 最简单的是使用StringBuilder#reverse()方法.
另外还有,(1).二分递归;(2),charAt()拼接字符串;(3)数组反转拼接;(4)数组字符首尾替换;(5)使用Stack
- String 类的常用方法都有那些?
答:
charAt(); compareTo();compareToIgnoreCase();concat();contains();static copyValueOf();endsWith();equals();equalsIgnoreCase(); static format(); getBytes();getChars(); indexOf(); isEmpty(); lastIndexOf();length();matchs();replace();replaceAll();replaceFirst();spilit();startsWith();subString();toCharArray();toLowerCase();toUpperCase();trim();valueOf();
;
- 抽象类必须要有抽象方法吗?
答: 抽象类中的抽象方法不是必须的, 但抽象方法一定在抽象类中; 即使没有抽象方法的抽象类也不能被实例化.
- 普通类和抽象类有哪些区别?
答: (1).抽象类不能被实例化;(2).抽象方法用abstract修饰且没有实现;(3).有抽象方法的类必须声明为抽象类;(4).抽象类的子类如果不是抽象类就必须实现抽象方法;(5).抽象方法不能为static;(6).抽象类可以有构造函数;
- 抽象类能使用 final 修饰吗?
答: 不能! final的类不能被其他类继承, 而抽象类中的方法必须被子类实现, 本身有冲突. 如果在idea中声明, 编译器会报非法组合的修饰符.
- 接口和抽象类有什么区别?
答: (1).本质上抽象类是类Class,接口是完全不同的一种类型Interface;(2)接口不能有构造函数,抽象类可以有;(3).接口中的方法无修饰符(但其是public),接口中的方法可以被普通方法的修饰符修饰(抽象方法除外,不能是private,static);(4)接口可以被多继承, 抽象类只能单继承;(5)接口中的属性默认是static final的,抽象类可以是任意;(6)1.8之前接口方法必须被实现类实现, 抽象类的抽象方法必须被子类实现,1.8之后接口可以有默认实现了;(7)抽象类可以有main方法,并可以执行;
- java 中 IO 流分为几种?
答: 字节流和字符流; 输入流和输出流;缓冲流和非缓冲流;
- BIO、NIO、AIO 有什么区别?
答: BIO:阻塞IO; NIO: new io 又叫非阻塞IO, 多路复用器seletctor; AIO : 异步IO. 参看:以Java的视角来聊聊BIO、NIO与AIO的区别
BIO 是同步阻塞IO, 读写操作由单独的线程完成, 如果出现资源等待则线程被阻塞,操作系统级别来说会出现线程上下文切换, 导致性能开销, 所以BIO适合少量读写操作, 不适合大量并发操作如web环境.
NIO 是同步非阻塞IO, NIO基于事件驱动, 目的就是解决BIO的高并发问题. NIO采用多路复用机制, 当有流需要读写时才使用线程处理, 否则不做操作.NIO抽象出Channal和Buffer的概念, 以及Selector, 而不针对Stream直接操作, 而是使用Buffer和Channel, Buffer中使用DirectByteBuffer性能更快, 因为这个类不使用java堆,直接使用系统接口申请内存,减少了数据复制转移等操作的开销,但也容易导致OOM. Selector多路复用的基础类, 单线程处理多个Channel
AIO 是异步非阻塞IO, java7发布. 是在数据准备好之后通知线程处理的方式, 而不是NIO的轮训, AIO是真正的同步, 底层调用了系统级别的API实现.
- File的常用方法都有哪些?
答:
canExecute();canRead();canWrite();compareTo();createNewFile();createTempFile();delete();deleteOnExit();equals();getAbsoluteFile();getFreeSpace();getName();getParent();getParentFile();getPath();isAbsolute();isDirectory();isFile();isHidden();lastModified();length();list();listFiles(),listRoots();mkdir();mkdirs();renameTo();setExecutable();setLastModified();setReadable();setWritable();toURI();
二、容器
- java 容器都有哪些?
答:
Array,String
,java.util
包下面的Collection,List,ArrayList,LinkedList,Vector,Stack,Map,HashMap,WeakHashMap,LinkedHashMap,HashTable,TreeTable,Set,HashSet,TreeSet,LinkedSet,Queue,
对应的并发容器类,阻塞容器类.
- Collection 和 Collections 有什么区别?
答: Collection 是集合接口,提供了对集合对象最基本的通用接口方法. 定义了集合最大化统一操作方式. Collections 是一个工具类, 包含了各种集合操作的静态方法, 这个类不能实例化, 只是一个工具类, 类似Arrays.
- List、Set、Map 之间的区别是什么?
答: List是有序集合,元素可以重复, Set的元素不能重复,只允许一个null元素; List和Set都继承自Collection; Map是键值对,键可以作为索引来查找值, 可以有多个null值,但只有一个null键.
- HashMap 和 Hashtable 有什么区别?
答: 简单的说, HashTable 是线程安全的, HashMap 是线程不安全的, 也正因为此, HashMap的效率更高. 从内部实现看, HashMap和HashTable实现上几乎完全相同, 只不过HashTable是用synchronized的. HashTable不允许null做键, HashMap允许null做键, 但仅允许一个.ConcurrentHashMap是HashTable的替代, 比后者具有更好扩展性.
- 如何决定使用 HashMap 还是 TreeMap?
答: 最大的区别是 TreeMap 是有序的, HashMap 并不能保证元素的顺序. HashMap 继承了AbstractMap,TreeMap继承了SortedMap. HashMap适用于Map中插入,删除和定位. TreeMap适用于按自然顺序和自定义顺序遍历(key).
- 说一下 HashMap 的实现原理?
答: 基于1.8. HashMap的内部实现为一个数组, 每个元素称为桶bucket, 每个元素为Node, 包含key, value, Node类型的next, 还有个hash值. HashMap的初始容量是16, 默认填充因子是0.75, 当容量不够时其扩容按N*2扩容. 当桶中元素不大于8时数据结构是个列表, 当大于是转化为红黑树, 当红黑树元素小于6时退化为列表.
hash的方法和定位, 先对hash值计算, 方法是高16位与低16位异或运算, 然后用容量n-1与hash结果与运算, 算出下标.
resize 的处理, 因为容量都是2的N次幂, 所以调整size的时候可以原位不变, 在高位填充随机的0或1. 即移动一个2次幂的位置. resize可以均匀的把冲突的节点分布到新的桶中了.
- 说一下 HashSet 的实现原理?
答: HashSet 基于 HashMap实现. 但仅仅使用key来实现各种特性. 内部定义了一个假值用来操作.
- ArrayList 和 LinkedList 的区别是什么?
答: ArrayList随机访问比较高效, LinkedList更适合做增加删除修改操作. 分开来说, ArrayList是以数组的方式实现, 能通过索引快速定位. LinkedList是链表, 每个元素保存了前一节点和后一节点的引用.
- 如何实现数组和 List 之间的转换?
答: List#toArray()方法将List转为数组, new ArrayList(Arrays.asList()) 方法将数组转为List.
- ArrayList 和 Vector 的区别是什么?
答: 都基于数组实现, Vector出现较早,提供了线程安全性, ArrayList效率更高. Vector默认增长为原容量2倍, ArrayList默认增长为原容量1.5倍+1.
- Array 和 ArrayList 有何区别?
答: Array是数组, ArrayList是列表实现了List接口. ArrayList可以动态扩容, Array的容量是固定的.
- 在 Queue 中 poll()和 remove()有什么区别?
答: 当队列为空时poll()会返回null, remove()则抛出异常.
- 哪些集合类是线程安全的?
答: HashTable, Vector,Stack, concurrent包下面的集合类, ConcurrentHashMap,ConcurrentSkipListMap、ConcurrentSkipListSet、ConcurrentLinkedQueue、ConcurrentLinkedDeque等, CopyOnWriteArrayList, CopyOnWriteArraySet.
- 迭代器 Iterator 是什么?
答: Iterator是个接口, 实现了该接口的类一般是集合类, 能够遍历集合中的元素. 迭代器是一种设计模式. Java中的Iterator只能单向移动, 包含的方法next(),hasNext(),remove(). 迭代器取代了原来的Enumeration接口.
- Iterator 怎么使用?有什么特点?
答:
while(iterator.hasNext()){...}
Iterator的特点是更加安全, 因为它可以确保在遍历的集合元素被修改后抛出ConcurrentModiicationException
- Iterator 和 ListIterator 有什么区别?
答: ListIterator 扩展了 Iterator. 当然Iterator有的功能ListIterator就有. 但ListIterator新增了一些额外的功能, 比如添加,替换获取前面或后面元素的索引位置. 另外, ListIterator是双向的.
- 怎么确保一个集合不能被修改?
答: 可以使用Collections类的静态方法unmodifiableCollection()方法创建只读集合. 任何改变集合的操作都将抛出
java.lang.UnsupportedOperationException
三、多线程
- 并行和并发有什么区别?
答: 并行是针对多核CPU的, 指多个任务可以同时分别在各自CPU上运行; 并发是指多个线程争夺同一个CPU资源(时间片), 存在上下文切换. CPU一个时间点只能处理一个任务.
- 线程和进程的区别?
答: 线程是系统调度资源的最小单位, 进程是系统分配资源的最小单位, 一个程序包含至少一个进程, 一个进程包含至少一个线程.
- 守护线程是什么?
答: 守护线程是针对用户线程的, 用户线程是程序启动的线程, 守护线程一般是有JVM启动, 但也不一定. 对于任何线程, 均可以在启动前调用setDeamon(true)方法设置为守护线程.
- 创建线程有哪几种方式?
答: Thread, Runnable, Callable
- 说一下 runnable 和 callable 有什么区别?
答: callable有返回值, runnable没有.
- 线程有哪些状态?
答: NEW, RUNABLE, TERMINATED, BLOCKED, WAITING,TIMED_WAITING
- sleep() 和 wait() 有什么区别?
答: (1).sleep()是线程方法,wait()是Object的方法;(2)sleep()超时后会继续执行, wait()需要notify()或notifyAll()唤醒;(3)sleep()不放弃对象锁,wait()会释放对象锁.
- notify()和 notifyAll()有什么区别?
答: nodify()唤醒一个等待锁的线程, 由JVM决定是哪个, nodifyAll()会通知所有等待锁的线程, 这些线程会争夺对象锁, 抢到的持有锁并继续执行, 其他的继续等待通知.
- 线程的 run()和 start()有什么区别?
答: run()定义了线程执行的逻辑, start()方法用来启动线程.run()可以执行多次.
- 创建线程池有哪几种方式?
答:
java.util.concurrent.Executors
方法下的几个静态创建线程池的方法,newFixedThreadPool(), newWorkStealingPool(),newSingleThreadExecutor(),newCachedThreadPool(),newSingleThreadScheduledExecutor(),newScheduledThreadPool()
public ThreadPoolExecutor(int corePoolSize, //核心线程数
int maximumPoolSize, //最大线程数, 当核心线程达到最大, 且队列满之后新入队的元素将开启更多线程, 总数最大不超过这个值
long keepAliveTime, // 线程存活时间, 指核心线程外的线程
TimeUnit unit, //时间单位
BlockingQueue<Runnable> workQueue //线程队列, 只有通过execute()方法调用的才会进入
) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
newSingleThreadExecutor()
corePoolSize=1 , maximumPoolSize=1 , keepAliveTime=0 , workQueue使用LinkedBlockingQueue
newCachedThreadPool()
corePoolSize=0 , maximumPoolSize=Integer.MAX_VALUE , keepAliveTime=60 , TimeUnit=SECONDS , workQueue使用 SynchronousQueue ; 同步队列, 来即处理
newFixedThreadPool()
corePoolSize=maximumPoolSize=传入的参数 , keepAliveTime=0 , workQueue使用 LinkedBlockingQueue ;
newWorkStealingPool()
使用ForkJoinPool
实例, 并行队列,since 1.8
- 线程池都有哪些状态?
答: RUNNING(正在运行), STOP(不接受新任务, 不再处理队列中的任务, 中断正在执行的线程), SHUTDOWN(不接受新任务, 但继续队列中的任务),TIDYING(所有任务均销毁了, workcount=0, 线程池转为此状态时会启动钩子方法terminated),TERMINATED(teminated()执行结束)
RUNNING -> SHUTDOWN :
shutdown()
, 或者隐式在finalize()
(RUNNING or SHUTDOWN) -> STOP :shutdownNow()
SHUTDOWN -> TIDYING : 队列和池均为空
STOP -> TIDYING : 池为空
TIDYING -> TERMINATED :terminated()
执行完
- 线程池中 submit()和 execute()方法有什么区别?
答: execute()方法用来执行
Runnable
接口类型的任务, submit()可以接受Runnable
也可以接受Callable
.
- 在 java 程序中怎么保证多线程的运行安全?
答: 使用锁
Lock
, 以及线程安全的类java.util.concurrent
, 代码同步关键字synchronized
等
- 多线程锁synchronized的升级原理是什么?
答: synchronized是重量级锁, 如果资源被占用则当前线程进入阻塞队列, 清空缓存, 但很多时候刚刚挂起资源就释放了, 也就是说资源征用其实没有想象的那么频繁, 一般资源总会被同一个线程占用. 从1.6开始对其优化, 分为三种级别, 偏向锁,轻量级锁,重量级锁. 参考Java并发——Synchronized关键字和锁升级,详细分析偏向锁和轻量级锁的升级
偏向锁, 在对象头设置标识位和threadid, 偏向锁不会主动释放锁, 如果同一线程再次获取, 则比较threadid, 相同则无需cas加锁解锁, 如果不一致则查看对象头中保存的线程是否存活, 不存活则当前线程设置为偏向锁, 存活则表明有多于一个线程竞争锁, 此时锁可能升级为轻量级锁.
轻量级锁: 对于多个线程争用锁, 但线程持有锁时间不长的情景. 使用自旋一定次数来等待锁释放. 从而减少阻塞.
重量级锁: 对于自旋一定时间的线程, 超出限制后锁可能膨胀为重量级锁, 由操作系统调度管理. 重量级锁会阻塞线程, 防止CPU空转.
锁升级后不再降级, 但偏向锁可以被重置为无锁状态.
- 什么是死锁?
答: 多个线程间互相等待对方释放自己所需资源(锁)的情况, 循环等待导致线程阻塞.
- 怎么防止死锁?
答: (1). 顺序执行; (2).检测死锁; (3). 减小锁粒度; (4).设置锁超时时间
- ThreadLocal 是什么?有哪些使用场景?
答: 线程本地变量, 为每个线程提供独立的变量副本, 本线程修改的值不会影响到其他线程的同名变量.
如数据库链接, session
- 说一下 synchronized 底层实现原理?
答: synchronized可以修饰代码块, 方法, 静态方法. 这三种情况实现不同. 对于代码块, 在块的开始和结束的地方虚拟机分别会插入monitorenter和monitorexit指令, 必须成对出现, monitor是实现锁的机制, 一个线程持有monitor, 其他线程就被挂起了. 对于方法, 虚拟机会在方法表中为其设置access_flag状态.
- synchronized 和 volatile 的区别是什么?
答: synchronized用于方法和代码块, volatile用于变量. valatile解决了变量在多线程环境下的可见性.synchronized解决了对临界资源的访问控制. volatile并不能保证原子性,synchronized能保证原子性; volatile不阻塞线程,synchronized会导致线程阻塞.volatile会防止指令重排.volatile效率更高.
- synchronized 和 Lock 有什么区别?
答: synchronized是关键字, 封装了java对锁的实现. Lock是jdk提供的, 包含一系列预定义的类. synchronized内部实现加锁解锁, 异常时释放锁, Lock需要代码中调用相关方法, 不解锁就不会释放. Lock抽象可以让程序员对锁有更精细控制以及定制操作.
- synchronized 和 ReentrantLock 区别是什么?
答: 参看54, 另外,ReentrantLock可以设置超时, 可被中断.
- 说一下 atomic 的原理?
答: 其内部实现不是简单的使用synchronized,而是一个更为高效的方式CAS (compare and swap) + volatile和native方法,从而避免了synchronized的高开销,执行效率大为提升. 核心是
UnSafe
类, 直接通过操作系统API操作. atomic并非无阻塞, 而是阻塞不在程序,线程级别, 而是在底层上面.
四、反射
- 什么是反射?
答: 反射是在程序运行时能够动态获取和操作对象的能力.
- 什么是 java 序列化?什么情况下需要序列化?
答: 序列化就是将对象转化为能够进行网络传输或存储的数据格式. java的序列化需要对象实现serializable接口.
保存对象文件; 网络传输; 远程方法调用.
- 动态代理是什么?有哪些应用?
答: 动态代理是在运行时生成代理类. 比如原生的基于接口的代理或CGLib实现Spring 的AOP机制.
- 怎么实现动态代理?
答: 基于java原生的接口方式的代理; 使用CGLib库实现的基于类继承的代理.
五、对象拷贝
- 为什么要使用克隆?
答: 克隆是Java对原型模式的实现, 克隆省去了new的开销, 同时可以保存对象的状态.
- 如何实现对象克隆?
答: (1).实现Cloneable接口并重写clone()方法; (2).使用序列化反序列化实现克隆
- 深拷贝和浅拷贝区别是什么?
答: 这就得区分java对象在内存中的存储方式, 浅拷贝对于值类型,直接复制内容, 但对于引用类型, 只是复制了一份对真实对象的引用. 深拷贝就是需要将引用类型的变量内容也同时复制, 从而生成一个完全不同的对象.
六、Java Web
- jsp 和 servlet 有什么区别?
答: jsp:java server page. 与asp,asp.net等一样,是java生态的服务端动态页面技术. servlet : server端小程序, 重在控制.事实上jsp就是在servlet基础上实现的, 但更着重前端页面.
- jsp 有哪些内置对象?作用分别是什么?
答: request: 封装客户端请求, 可以接收参数; respose: 封装服务端响应; pageContext: 页面上下文; session: 会话信息; application: 应用级别的对象; out: 服务端输出流对象; config: 配置对象; page: JSP页面; exception: 封装页面抛出的异常.
- 说一下 jsp 的 4 种作用域?
答: (1).page : 本页面相关的对象; (2). session: 代表一次会话作用域内的; (3).application: 应用程序级别的, 作用域最广, 全局, (4).request : 一次请求内
- session 和 cookie 有什么区别?
答: session是服务端对象, cookie是存储在客户端浏览器特定目录的; session理论上没有容量限制,cookie不能太大, 也有个数限制; session更为安全, cookie有伪造的风险;session一般存储在内存, 或内存数据库中, 也可以存在关系数据库;
- 说一下 session 的工作原理?
答: 用户初次登陆网站后, 服务端会给客户端响应添加session id, 之后客户端每次请求均带有这个id, 服务端获取后通过此id找到对应的session对象.
- 如果客户端禁止 cookie 能实现 session 还能用吗?
答: 可以, session id 可能以请求参数或header之类的方式请求到客户端.
- spring mvc 和 struts 的区别是什么?
答: (不了解struts) 下面是搜索到的答案.
(1). 拦截级别, struts是类级别的拦截, spring是方法级别拦截;(2). 数据独立性: Spring mvc 方法之间基本独立, 独享request,response数据, 请求通过参数获取, 返回ModelMap,方法之间不共享变量;struts2方法之间也独立, 但所有action变量是共享的, 编码和阅读代码不友好.(3). 拦截机制, spring mvc 用的独立的aop方式;struts2有自己框架的拦截机制;(4)对ajax的支持: spring mvc 更方便, 使用@ResponseBody注解可实现; struts2需要插件或自定义;
- 如何避免 sql 注入?
答: PreparedStatement, 关键字或符号转义, 过滤特殊字符
- 什么是 XSS 攻击,如何避免?
答: XSS: 跨站脚本攻击, 是攻击者在web页面植入js代码, 等页面被浏览后代码执行从而达到攻击目的. 避免方法是对用户输入的内容进行编码, 过滤js等关键字.
- 什么是 CSRF 攻击,如何避免?
答: 跨站请求伪造, 是攻击者盗用别人身份并发送恶意请求进行欺骗的手段. 避免方法是: 验证请求来源, 只接受同源的请求; 添加验证码; 使用token验证.
七、异常
- throw 和 throws 的区别?
答: throw 是抛出异常的关键字, 后面是一个异常实例;throws是表示方法可能会抛出的异常, 后面是异常的类名, 用来方法前面上面.
- final、finally、finalize 有什么区别?
答: final修饰变量, 方法, 类. 被修饰的表明不能被修改,重写或继承;finally是异常处理块, 其中的代码必然执行;finalize是类的析构方法, 一般不需自己定义.
- try-catch-finally 中哪个部分可以省略?
答: try与其他二者之一必须成对存在.
- try-catch-finally 中,如果 catch 中 return 了,finally 还会执行吗?
答: 会执行, finally中的代码不管什么情况下, 必然会执行.
- 常见的异常类有哪些?
答: NullPointerException, ClassNotFoundException,IOExcption,IndexOutOfBoundsException,ClassCastException,NoSuchMethodException...
八、网络
- http 响应码 301 和 302 代表的是什么?有什么区别?
答: 3XX 是跳转响应码. 301时永久跳转, 对SEO友好, 302时临时跳转,可能会被拦截.
- forward 和 redirect 的区别?
答: forward会带有原页面请求, redirect相当于重新打开一个页面; redirect的地址栏会变, forward则不会; forward效率要高点.
- 简述 tcp 和 udp的区别?
答: 都是传输层协议.
tcp面向链接, udp非链接即可发送数据; tcp提供可靠的数据传输,udp无法保证; tcp面向字节流, udp面向报文; tcp传输效率低, udp传输快;
- tcp 为什么要三次握手,两次不行吗?为什么?
答: 两次握手的话, 服务端发出确认信号, 但客户端未必会响应, 而此时连接以及建立, 但客户端并不是真的需要服务端, 这就造成了资源浪费.
- 说一下 tcp 粘包是怎么产生的?
答: 发送端: 发送端需要等缓冲区满才发送, 造成粘包; 接受端: 接受端不及时接受缓冲区的包, 造成多个包接受.
- OSI 的七层模型都有哪些?
答: 物理层, 数据链路层,网络层, 传输层,会话层,表现层,应用层.
- get 和 post 请求有哪些区别?
答: post比get更安全; get有限制, post没限制; get地址栏显式, 可收藏;post不行.get 可缓存;
- 如何实现跨域?
答: 反向代理如njinx; 服务端设置CORS为*; 单个接口设置@CrossOrigin; 使用jsonp;
- 说一下 JSONP 实现原理?
答: Json with Padding, 利用scrpt的src可以访问不同源的特性, 加载远程返回的js函数来执行.
九、设计模式
- 说一下你熟悉的设计模式?
答: 单例模式: 类自己管理自身的实例化, 节省资源; 工厂模式, 观察者模式;代理模式, 模版方法, 策略模式, 生成器模式.
- 工厂方法和抽象工厂有什么区别?
答: 抽象工厂用来生成一系列产品族, 工厂方法指示生成一种产品, 他们都支持增加产品. 简单工厂更像是一种编程习惯, 用来管理产品的生成.
十、Spring/Spring MVC
- 为什么要使用 spring?
答: Spring流行,生态完善, 特别是spring boot, spring cload简化了开发和部署; spring 是各种框架的粘合剂; spring将设计尽量延迟, 开发者可以尽量晚地做决定, 比如修改配置即可替换某个类; Spring 容器更容易管理依赖; aop技术可以抽离切面; spring提供事务支持.
- 解释一下什么是 aop?
答: 面向切面编程, 通过java原生或cglib增强来实现. aop可以将一部分如事务, 日志, 异常等统一的功能抽离, 统一维护.
- 解释一下什么是 ioc?
答: ioc是控制反转, 是将原来类自身管理它依赖的方式替换为由容器统一管理, 这就将依赖做到很大程度解耦.
- spring 有哪些主要模块?
答: spring core 提供ioc; spring context ; dao, aop, web, mvc等.
- spring 常用的注入方式有哪些?
答: setter注入, 构造函数注入. 另外还有编程注入, 但这方式太原始, 并不只是spring的, 只能说它只是达成注入的一种方式.
- spring 中的 bean 是线程安全的吗?
答: 默认的bean是单例的, spring 并不能保证bean的线程安全.
spring的bean大多是无状态的, 所以大多情况下不存在线程不安全的问题. 但如果需要保持有状态的bean, 就必须使用其他作业域了. 如prototype.
- spring 支持几种 bean 的作用域?
答: 六种: singleton, prototype, session, request,application, websocket. 后面四种仅仅在web环境下.
- spring 自动装配 bean 有哪些方式?
答: 四种方式, no 不使用, 通过ref等方式来完成; bytype 通过类型, byname 通过名字, constructor: 类似bytype只是应用于构造函数参数. bytype和byname可以组装数组.
- spring 事务实现方式有哪些? (垃圾问题)
答: 使用@Transaction的声明式事务, 或编码实现.
- 说一下 spring 的事务隔离?
答: 事务隔离机制: 未提交读,提交读,可重复读, 序列化; spring的事务隔离与数据库相同, 但还有个默认方式, 即使用数据库使用的隔离级别.
- 说一下 spring mvc 运行流程?
答: 请求-> DispatcherServlet-> handlermaping-> handler(controller action)-> modelAndView-> viewResolver->view
- spring mvc 有哪些组件?
答: DispatcherServlet, HandlerMapping, ViewResolver, Controller, ModelAndView,LocaleResolver等.
- @RequestMapping 的作用是什么?
答: 请求映射, 就是将http请求的特定url到特定的handler上.
- @Autowired 的作用是什么?
答: 自动装配, 使用该注解的字段,方法,构造函数自动完成依赖注入. 减少了配置.
十一、Spring Boot/Spring Cloud
- 什么是 spring boot?
答: Spring boot 是为了简化Spring开发的, 简化了spring程序的初始搭建和开发部署, 提供了开箱即用的开发体验和一套非功能性组件, 可以做到几乎没有配置.
- 为什么要用 spring boot?
答: 配置简单; 独立运行,有内置的web容器; 自动配置,极少的xml配置文件; 快速搭建和部署;
- spring boot 核心配置文件是什么?
答: bootstrap.XX 和 application.XX
- spring boot 配置文件有哪几种类型?它们有什么区别?
答: properties 和 yml 两种格式的配置文件, yml语法更严格, 可以减少错误, 但缺乏自由度, yml配置看起来有层次, 两种方式各有优劣. yml不支持@PropertySource. 可以通过实现
PropertySourceFactory
接口来实现.
- spring boot 有哪些方式可以实现热部署?
答: 开发时使用devtools, 配置中添加spring.devtools.restart.enabled=true; idea中配置自动编译.
- jpa 和 hibernate 有什么区别?
答: jpa时规范, java persistence api. hibernate是框架, 基于jpa实现.
- 什么是 spring cloud?
答: 基于spring boot 实现的一系列框架的合集. 提供了分布式系统中非功能性的基础实现, 如配置注册中心, 路由, 熔断, 负载均衡, 监控等. 只需要极少配置即可使用.
- spring cloud 断路器的作用是什么?
答: 当分布式架构中的服务单元发生故障时或其他原因如流量过载等原因时, 断路器会根据设置的阀值判定正常服务或快速返回错误, 这样可以防止服务长时间的等待, 从而防止故障蔓延.
- spring cloud 的核心组件有哪些?
答: Eureka, Feign, Ribbon, Hystrix,Zuul.
十二、Hibernate
- 为什么要使用 hibernate?
答: hibernate是对jdbc的封装, 简化了访问数据库的重复代码; hibernate提供ORM实现, 简化了DAO层的编码; 具有数据库可移植性; 缓存提升了效率.
- 什么是 ORM 框架?
答: Object Relation Mapping, 是将关系数据库的表或视图等映射为程序中的对象. 这样可以简化开发.
- hibernate 中如何在控制台查看打印的 sql 语句?
答: hibernate.show_SQL=true
- hibernate 有几种查询方式?
答: 原生SQL, HQL, 条件查询Criteria.
- hibernate 实体类可以被定义为 final 吗?
答: 可以, 但final的不能被继承, 也就不能使用代理模式实现延迟关联来提升性能了.
- 在 hibernate 中使用 Integer 和 int 做映射有什么区别?
答: 对象和值, Integer可以为null.
- hibernate 是如何工作的?
答: 读取解析配置文件, 创建SessionFacotry, 打开Session, 创建事务, 操作, 提交事务, 关闭Session, 关闭SessionFactory.
- get()和 load()的区别?
答: load()支持延迟加载, get()不支持; 没有OID指定的对象, get()返回null, load返回代理对象.
- 说一下 hibernate 的缓存机制?
答: 分为一级缓存和二级缓存; 一级缓存是Session缓存, Session作用域有效. 二级缓存是application缓存, 全局有效, 并支持三方缓存.
- hibernate 对象有哪些状态?
答: 临时状态(不受Session管理), 持久化状态(持久化到数据库中的), 游离状态(Session关闭后的对象).
- 在 hibernate 中 getCurrentSession 和 openSession 的区别是什么?
答: getCurrentSession() 绑定当前线程, openSession()不会; getCurrentSession()受事务管理, openSession()需要手动管理事务.
- hibernate 实体类必须要有无参构造函数吗?为什么?
答: 是的, hibernate使用反射实例化实体. 没有无参构造会报异常.
十三、Mybatis
- mybatis 中 #{}和 ${}的区别是什么?
答: #{} 是预编译处理, ${}是字符替换. 使用#{}时, Mybatis会将其替换为?, 这样可以防止SQL注入, 保证程序安全.
- mybatis 有几种分页方式?
答: 物理分页和逻辑分页. RowBounds使用逻辑分页. 分页插件PageHelper或自定义分页使用物理分页.
- RowBounds 是一次性查询全部结果吗?为什么?
答: 也不是, jdbc有个Fetch Size的设置, 只有当需要更多数据时, 它才会从数据库查询更多数据.
- mybatis 逻辑分页和物理分页的区别是什么?
答: 逻辑分页时在内存中进行, 一次查询出很多数据, 在内存进行分页, 这种方式占用大量内存, 可能导致内存溢出; 物理分页是直接查询出所需数据, 在数据库分页, 这种分页按需返回数据, 但数据库压力可能较大.
- mybatis 是否支持延迟加载?延迟加载的原理是什么?
答: 支持. 可以设置lazyLoadingEnable=true启用.
延迟加载是在使用实例的时候, 比如调用对象a.getName(), 如果发现a为null, 则加载a并返回. 延迟加载就是在使用时才去触发查询的SQL.
- 说一下 mybatis 的一级缓存和二级缓存?
答: 一级缓存是基于PerpetualCache的HashMap本地缓存, 生命周期与SQLSession相同, 可能会出现脏数据, 在session关闭或清空后缓存失效. 默认开启. 二级缓存也是基于PerpetualCache的HashMap本地缓存, 不同的是作用域为Mapper级别, 可以在多个Session间共享, 可以自定义缓存如使用EhCache. 使用二级缓存需要类实现Serializable接口.
查询顺序: 二级缓存 --> 一级缓存 --> 数据库.
更新策略: 同一作用域下发生更新后, 默认该作用域下的select缓存均clear.
- mybatis 和 hibernate 的区别有哪些?
答: Mybatis更灵活, 可以自己写sql; 可移植性hibernate要好; 二级缓存hibernate可以自行更换;
- mybatis 有哪些执行器(Executor)?
答: 有三种基本执行器: (1). SimpleExecutor: 每执行一次update或select就开启一个statement对象, 用完立即关闭statement对象; (2). ReuseExecutor: 执行update,select, 以sql语句作为key查找statement对象, 存在则使用, 不存在则创建, 用完存在Map以备后面再用. (3). BatchExecutor: 执行update, 将多个sql添加到批处理中, 等待统一执行, 它缓存了多个statement对象, 等待统一处理.
- mybatis 分页插件的实现原理是什么?
答: 拦截器实现, 拦截sql, 然后重写为对应的分页sql.
- mybatis 如何编写一个自定义插件?
答: (1). 插件要实现interceptor接口
public interface Interceptor{
// 拦截的适合要执行的逻辑
Object intercept(Invocation invocation) throws Throwable;
// 用于封装目标对象, 该方法返回对象本身或其代理, 可决定是否要进行拦截进而决定要返回什么样的对象.
Object plugin(Object target);
// 在MyBatis进行配置插件的适合可以配置自定义相关属性, 接口实现对象的参数配置.
void setProperties(Properties properties);
}
(2). 插件应用的目标对象: Executor, StatementHandler,ParameterHandler, ResultSetHandler.
(3). 实现示例:
@Intercepts({
@Signature(type=Executor.class, method="query",args={
MappedStatement.class,Object.class,RowBounds.class, ResultHandler.class
})
})
public class TestInterceptor implements Interceptor{
public Object intercept(Invocation invocation) throws Throwable{
Object target = invocation.getTarget();// 被代理对象
Method method = invocation.getMethod(); // 代理方法
Object[] args = invocation.getArgs(); //方法参数
///....... 方法执行前的代码
Object result = invocation.proceed();
///........方法执行后的代码
return result;
}
public Object plugin(Object target){
return Plugin.wrap(target,this);
}
}
十四、RabbitMQ
- rabbitmq 的使用场景有哪些?
答: rabbitmq是目前比较流行的amqp消息队列, 适合使用的场景有: 1. 系统削峰填谷; 2. 延迟队列; 3. 系统解耦.
- rabbitmq 有哪些重要的角色?
答: 问题问的是构成rabbitmq的系统角色, rabbitmq是生产者/消费者模式的结构. 因此分为生产者: 消息的创建方, 负责发送消息到消息服务器; 消费者: 消息接收方, 用于处理数据; 中介代理: 即rabbitmq本身, 用来接受消息并按一定数据格式存储, 并为消费者提供消息.
- rabbitmq 有哪些重要的组件?
答:
- ConnectionFactory: 建议链接的工厂类
- Channal: 信道, 消息通道
- Exchange: 交换器, 用于接收分配消息
- Queue: 队列, 用来存储消息
- RoutingKey: 路由键, 用来把生产者数据分配到交换机
- BindingKey: 用来把交换机的消息绑定到队列
- rabbitmq 中 vhost 的作用是什么?
答: 类似数据库的实例, 每个vhost有自己的一套队列,交换机和绑定以及自己的权限机制.
- rabbitmq 的消息是怎么发送的?
答: 客户端通过tcp链接到RabbitMQ服务器, 一旦通过了认证, 客户端和服务器之间就创建了一条amqp信道, 信道是创建在真实tcp上的虚拟链接, amqp命令是通过信道发出去的, 每个信道都有一个唯一的id, 不论发布还是订阅均通过此信道完成.
- rabbitmq 怎么保证消息的稳定性?
答: 提供了事务支持; 可以将channel设置为confirm模式.
- rabbitmq 怎么避免消息丢失?
答: 把消息持久化到磁盘, 保证重启数据不丢失; 集群中至少有个物理磁盘, 保证消息落入磁盘.
- 要保证消息持久化成功的条件有哪些?
答: 队列queue必须设置持久化durable为true; 消息推送投递模式必须设置持久化, deliveryMode=2; 消息已经到达持久化交换机; 消息已经到达持久化队列.
- rabbitmq 持久化有什么缺点?
答: 持久化需要将数据写入磁盘, 跟其他磁盘io的系统一样,这样会降低服务器吞吐量, 降低性能.
- rabbitmq 有几种广播类型?
答: direct, 默认方式, 发送消息给订阅方, 对于多个订阅方采用轮询的方式进行; headers, 性能较差, 此类型几乎用不到; fanout, 分发模式, 分发给所有订阅者; topic: 匹配订阅, 可以使用正则匹配到消息队列, 能匹配到的都能接收到.
- rabbitmq 怎么实现延迟消息队列?
答: 有两种方式: 一是消息过期后进入死信交换机, 再由交换机转发到延迟消费队列, 实现延迟功能; 二是使用delayed-message-exchange插件实现延迟功能.
- rabbitmq 集群有什么用?
答: 高可用, 高容量
- rabbitmq 节点的类型有哪些?
答: 磁盘节点, 可持久化数据; 内存节点, 高效.
- rabbitmq 集群搭建需要注意哪些问题?
答: 各节点之间用"-link"连接; 各节点使用erlang coolie值必须相同, 相当于密钥, 用于认证; 整个集群中必须包含一个磁盘节点.
- rabbitmq 每个节点是其他节点的完整拷贝吗?为什么?
答: 不是, 原因有二: 存储空间和性能.
- rabbitmq 集群中唯一一个磁盘节点崩溃了会发生什么情况?
答: 集群可以保持运行, 只是不能修改任何东西了. (1)不能创建队列;(2)不能创建交换器;(3)不能创建绑定;(4)不能添加用户;(5)不能更改权限;(6)不能添加删除节点.
- rabbitmq 对集群节点停止顺序有要求吗?
答: 需要先关闭内存节点, 再关闭磁盘节点, 否则可能会导致数据丢失.
十五、Kafka
- kafka 可以脱离 zookeeper 单独使用吗?为什么?
答: 不可以, kafka使用zookeeper协调管理kafka的节点服务器.
- kafka 有几种数据保留的策略?
答: 两种: 按过期时间保留, 按存储消息大小保留.
- kafka 同时设置了 7 天和 10G 清除数据,到第五天的时候消息达到了 10G,这个时候 kafka 将如何处理?
答: 两个规则为或的关系, 只要一个满足要求即清除数据.
- 什么情况会导致 kafka 运行变慢?
答: 傻逼问题, 傻逼答案... cpu, io, 网络
- 使用 kafka 集群需要注意什么?
答: 集群节点最好不要超过7个, 节点越多消息复制需要的时间越长, 整个群组的吞吐量就越低. 集群数为2N+1个较好. 超过一半故障集群就不能用了, 单数容错更高一点.
十六、Zookeeper
- zookeeper 是什么?
答: zookeeper是分布式协调调度RPC框架, 它为分布式应用提供一致性服务, 包括配置注册中心,域名服务,分布式锁等.
数据采用树形方式, 可以支持临时和永久, 有序和无序两种方式的任意组合.
- zookeeper 都有哪些功能?
答: 事件监听, 文件存储; 适用的场景包括: 发布订阅,配置注册中心, 命名服务, leader选举, 负载均衡, 分布式队列, 分布式锁等.
- zookeeper 有几种部署模式?
答: 单实例部署, 集群部署.
- zookeeper 怎么保证主从节点的状态同步?
答: Zookeeper 的核心是原子广播,这个机制保证了各个Server之间的同步。实现这个机制的协议叫做Zab协议。Zab协议有两种模式,它们分别是恢复模式(选主)和广播模式(同步)。当服务启动或者在领导者崩溃后,Zab就进入了恢复模式,当领导者被选举出来,且大多数Server完成了和 leader的状态同步以后,恢复模式就结束了。状态同步保证了leader和Server具有相同的系统状态。
为了保证事务的顺序一致性,zookeeper采用了递增的事务id号(zxid)来标识事务。所有的提议(proposal)都在被提出的时候加上了zxid。实现中zxid是一个64位的数字,它高32位是epoch用来标识leader关系是否改变,每次一个leader被选出来,它都会有一个新的epoch,标识当前属于那个leader的统治时期。低32位用于递增计数。
- 集群中为什么要有主节点?
答: 分布式环境中, 有些业务逻辑只需要在集群中的某台服务器执行, 这样可以保证这些事务逻辑的原子性, 同时也保证只在一台服务器执行,这样可以提高性能, 减少重复计算.等执行完成后结果被其他的节点共享.
- 集群中有 3 台服务器,其中一个节点宕机,这个时候 zookeeper 还可以使用吗?
答: 可以使用, 一般集群的机器节点数为2N+1, 只要有大于一半的服务器在线即可正常提供服务, 三台宕机一台依然有两台可用.
- 说一下 zookeeper 的通知机制?
答: zookeeper采用注册/监听方式, 使用
Watcher
来实现对节点和路径事件的监控.
十七、MySql
- 数据库的三范式是什么?
答: 1. 原子性, 每个字段都只表示一个属性; 2. 每个字段均依赖于主键, 也就是一张表只表示一个对象; 3. 属性不能传递依赖,也就是有传递依赖的地方要拆分为不同表;
- 一张自增表里面总共有 7 条数据,删除了最后 2 条数据,重启 mysql 数据库,又插入了一条数据,此时 id 是几?
答: Innodb和myIASM两种执行引擎的方式不同, Innodb会在内存中存储表中的自增id, myiasm是在表中保留, 因此innodb重启后会丢失最大id, 而会从现有表中计算所以结果为6, myiasm为8.
- 如何获取当前数据库版本?
答: select version()
- 说一下 ACID 是什么?
答: ACID是数据库事务的特性, A是atomicity, 即原子性, 原子性保证一个事务以一个整体逻辑执行, 要么成功, 要么失败, 执行完成后不会发生中间状态; C是consistency, 即一致性, 即事务执行开始和结束后, 数据的完整性没有遭到破坏; I是Isolation, 隔离性, 多个并发的事务对数据来说不会因为交叉执行而导致数据不一致, 数据库存在四个隔离级别, 分别解决不同程度的数据隔离;D 是 durability, 是持久性, 事务执行后对数据修改时永久的, 数据保持完成.
- char 和 varchar 的区别是什么?
答: char是固定长度的, varchar是可变长度的,char可能会存在空间浪费的情况, varchar使用的空间是n+1, 其中有一个char单位用来保存长度, 性能方法, char的性能要高一点,
- float 和 double 的区别是什么?
答: 大小不同, float是4字节的, double是8字节的.
- mysql 的内连接、左连接、右连接有什么区别?
答: 内连接是两个表能匹配的数据, 左连接和右连接则分别以左表或右边为主, 展示出左表或右表的数据, 对于匹配不到的, 右表或左表展示为null.
- mysql 索引是怎么实现的?
答: mysql或其他数据库的索引大多采用B+树数据结构, B+树本身就是有序结构, 可以达到二分法的性能.
- 怎么验证 mysql 的索引是否满足需求?
答: explain 查询语句, 查看执行计划
- 说一下数据库的事务隔离?
答: MySql.ini配置文件有默认的事务隔离配置, transaction-isolution=REPEATABLE-READ.
事务有四个级别的隔离方式, 分别为:
- READ-UNCOMMITED: 未提交读, 隔离级别最低, 可能导致脏读,幻读,不可重复读
- READ-COMMITED: 提交读, 可能导致幻读, 不可重复读, 可以避免脏读. 事务提交后其他事务才可见
- REPEATABLE-READ: 可重复读, 会造成幻读
- SERIALIZABLE: 序列化, 完全保证了事务是隔离的,但性能最低.
脏读: 一个事务能读取另一个事务未提交的数据; 不可重复读: 一个事务内多次读取同一个数据;幻读: 同一事务多次读取的数据不一致.
- 说一下 mysql 常用的引擎?
答: InnoDB: 提供了对数据库的acid事务支持, 并提供行级锁和外键约束, 它设计的目标就是处理大数据容量的数据库系统. 它会在启动时建立缓冲池, 用来缓存数据和索引. 但不支持全文索引, 启动也比较慢. 不会保存行数, 但并发环境下的读取效率很高.
MyIASM: 默认引擎, 不提供事务支持, 不支持行锁和外键. 所以变更时会锁表,效率比较低. 但其保存了行数, 读多余写的操作可以使用此搜索引擎.
- 说一下 mysql 的行锁和表锁?
答: 行锁是在数据变更时仅会在当前行加锁, 其他行还是可以访问的, 锁的级别较小, 不容易发生阻塞.表锁是变更时会对整个表加锁, 性能低下, 不利于并发.
- 说一下乐观锁和悲观锁?
答: 乐观锁是默认没有别的进程修改数据, 仅在提交更新时判定数据版本号是否与修改前一致; 悲观锁是默认认为数据在同一时间可能被其他进程修改, 因此先锁定数据, 修改后释放锁.
- mysql 问题排查都有哪些手段?
答: show processlist, explain, 查看日志
- 如何做 mysql 的性能优化?
答: 索引, 合适的查询语句, 表分区, 正确的搜索引擎
十八、Redis
- redis 是什么?都有哪些使用场景?
答: redis是一种nosql数据库, 是用C实现的, 具有高性能, 单线程, 支持持久化, 支持集群的高可用内存数据库.
用来做数据库, 缓存, 消息中间件
- redis 有哪些功能?
答: 复制, 集群, 持久化, 事务, 分布式锁,LUA脚本, LRU
- redis 和 memecache 有什么区别?
答: (1) 持久化的支持, 数据可靠性: redis可以做持久化, memcache是内存无法持久化; (2) 底层实现方面: redis利用单线程, memcache多线程, 可以使用多核CPU; (3). 数据结构方面: redis支持较多的数据结构, memcache仅仅是k-v结构;(4). 数据大小, 小于100kredis比较快, 大于100k,memcache较快, 但redis支持最大512M的数据, memcache最大为1M; (5)应用场景: memcache适合读多写少, 或数据比较大的对象,redis适合读写都很多, 比较复杂的数据结构.
- redis 为什么是单线程的?
答: redis是基于内存操作的, CPU不会存在瓶颈, 既然CPU不是瓶颈, 单线程又很容易实现, 那么redis自然就选择用单线程了.
对于多个CPU的服务器, 可以开多个redis实例来提高服务器资源使用率. 注意: redis4.0 开始可能会有条件地在某些操作时使用多线程.
183.什么是缓存穿透?怎么解决?
- redis 支持的数据类型有哪些?
答: String, Set, ZSet, List, Hash
不常见的有: Bitmaps,Hyperloglogs 和地理空间(Geospatial)索引半径查询
- redis 支持的 java 客户端都有哪些?
答: Jedis, Redisson等, 官方推荐Redisson
- jedis 和 redisson 有哪些区别?
答: jedis是对原生redis的简单封装, redisson是官方推荐的客户端程序, 除了基本的命令, 还有更丰富的数据结构以及锁的实现.
- 怎么保证缓存和数据库数据的一致性?
答: 更新时先删除缓存, 设置数据过期时间, 异步更新数据.
- redis 持久化有几种方式?
答: aof, rdb
aof是写日志方式, 是按指定策略通过日志恢复数据的方式.
rdb是快照方式, 支持同步(save)和异步方式(bgsave)保存数据. save会阻塞服务, 直到保存完成. bgsave不阻塞, 但可能运行期间的数据会发生丢失.
- redis 怎么实现分布式锁?
答: setNX命令, 返回1表示成功, 0位失败.
- redis 分布式锁有什么缺陷?
答: 执行时间超过锁超时时间时会导致并发问题.
- redis 如何做内存优化?
答: 尽量使用Redis的散列表, 把相关信息放在散列表里面, 而不是各个字段单独存储, 这样可以有效减少内存.
- redis 淘汰策略有哪些?
答: 六种, 分别是volatile-ttl, 过期的数据集中清除超时的;volatile-lru,过期的里面清除不常用的数据;volatile-random,过期的数据里面随机清除; allkeys-random, allkeys-lru, 与上面一样, 只是范围为所有的数据集.no-enviction,禁止淘汰
- redis 常见的性能问题有哪些?该如何解决?
答: redis是在进行持久化的时候会导致性能问题. 写内存快照会阻塞主线程, 当快照较大时会较长时间的导致服务暂停, 所有主服务器最好不要写快照. 主从复制的性能问题, 复制的速度和稳定性. 主从最好在一个局域网内.
十九、JVM
- 说一下 jvm 的主要组成部分?及其作用?
答:
- 类加载器(Class Loader) 用来将类文件加载到内存.
- 执行引擎(Execution Engine) 用来解析指令, 提供给操作系统执行
- 本地方法接口(Native Interface) 一些Java无法完成的任务 (1)与环境外交互;(2)与操作系统交互;
- 运行时数据区(Runtime Data Area) 加载的程序和运行时的对象都存储在这里,主要分堆和栈, 这里(特别是堆)也是GC的主要区域.
总的来说就是类加载器会将java代码转化为字节码, 运行时区把字节码加载到内存, 为了调用操作系统功能, 执行引擎需要调用本地方法.
- 说一下 jvm 运行时数据区?
答:
程序计数器, 堆, 栈, 方法区
栈又分为虚拟机栈和本地方法栈.
方法区和堆是所有线程共享的, 其他为线程隔离.
程序计数器是记录程序执行顺序的一小块内存区域, 存储程序执行指令的行号信息, 存储虚拟机字节码指令地址, 本地方法时计数值为空, 内存模型中不会发生 OutOfMemoryException
.
虚拟机栈是方法执行时同时创建的, 用于支持方法执行和调用的数据结构,栈帧用于存储局部变量表、操作数栈、动态链接、方法返回地址和一些额外的附加信息.编译程序代码时,栈帧中需要多大的局部变量表、多深的操作数栈都已经完全确定了,并且写入了方法表的 Code 属性之中.
1. 局部变量表: 一组变量值存储空间,用于存放方法参数和方法内部定义的局部变量, 局部变量表的容量以变量槽(Slot)为最小单位. 局部变量表所需的内存空间在编译期间完成分配;
2. 操作数栈: 最大深度也是在编译的时候就确定了.32 位数据类型所占的栈容量为 1,64 位数据类型所占的栈容量为 2。当一个方法开始执行时,它的操作栈是空的,在方法的执行过程中,会有各种字节码指令(比如:加操作、赋值元算等)向操作栈中写入和提取内容,也就是入栈和出栈操作。
3. 动态连接: 每个栈帧都包含一个指向运行时常量池(在方法区中,后面介绍)中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的动态连接。Class 文件的常量池中存在有大量的符号引用,字节码中的方法调用指令就以常量池中指向方法的符号引用为参数。这些符号引用,一部分会在类加载阶段或第一次使用的时候转化为直接引用(如 final、static 域等),称为静态解析,另一部分将在每一次的运行期间转化为直接引用,这部分称为动态连接。
4. 方法返回地址: 方法返回时可能需要在栈帧中保存一些信息,用来帮助恢复它的上层方法的执行状态。一般来说,方法正常退出时,调用者的 PC 计数器的值就可以作为返回地址,栈帧中很可能保存了这个计数器值,而方法异常退出时,返回地址是要通过异常处理器来确定的,栈帧中一般不会保存这部分信息
本地方法栈为使用到的本地操作系统(Native)方法服务。
它用于存储已经被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。运行时常量池是方法区的一部分.
堆是存储对象信息的内存区域, 是 GC 主要工作区域, 虚拟机将堆又分为 Eden, Survive, Old, Perm 等区块, 一般新生代分配为 1/4 到 1/3, 默认 3/8. 新生代的 Eden ,Survive 默认比例为 8:1:1 .
直接内存:直接从操作系统中分配,因此不受 Java 堆大小的限制,但是会受到本机总内存的大小及处理器寻址空间的限制. NIO 机制可以直接从操作系统中分配直接内存,即在堆外分配内存.
- 说一下堆栈的区别?
答:
堆是JVM分配的一块共享内存区域, 占用了虚拟机内存的绝大多数空间, 堆存储的是对象的实例或数组, 这里也是GC的主要区域. 栈是各线程独立的内存区域, 在编译时就已经确定大小, 栈存储方法执行时的相关信息, 包含局部变量表,操作数栈, 动态连接, 方法返回等信息.
- 队列和栈是什么?有什么区别?
答:
Queue
和 Stack
都是数据结构, Queue
支持 FIFO, Stack
支持 FILO. Queue
是接口, 继承了 Collection
. Stack
则实现了 Vector
, 方法是线程安全的.
- 什么是双亲委派模型?
答:
Java 的类加载机制支持四种类加载器, customer -> app -> ext -> bootstrap bootstrap 是最顶级加载器, 用于加载 rt.jar , ext 次之, 加载javahome/lib下的其他类. 双亲委派模型就是在类加载时当前加载器首先委托给上层加载器进行加载, 上层加载器无法加载才是本加载器进行加载, 这样设计是基于安全性考虑, 这样可以避免本加载器加载覆盖上层加载器的一些类, 比如 String
基于此模式必然会优先通过 bootstrap 加载.
- 说一下类加载的执行过程?
答:
加载 -> 连接(验证 -> 准备 -> 解析) -> 初始化
- 加载: 将 .class 文件加载到内存;
连接: 分为三步
- 验证: 检查类的正确性;
- 准备: 静态变量分配内存, 初始化为默认值;
- 解析: 符号引用转化为直接引用.
初始化: 静态变量赋值, 静态代码块执行
- 怎么判断对象是否可以被回收?
答:
通过垃圾对象标记算法, 分为两种:
- 引用计数法: 每个对象保存一个其他对象对其的引用, 引用为 0 则表示对象可以被回收
- Root 可达性分析算法: 四种对象可以作为根对象(1)虚拟机栈中对对象的引用;(2)类中全局静态对象;(3)常量引用;(4)本地方法栈中引用的对象;
JVM会起一个线程从所有的GC Roots开始往下遍历,当遍历完之后如果发现有一些对象不可到达,那么就认为这些对象已经没有用了,需要被回收。四种引用类型: 强引用、软引用、弱引用和虚引用.
- java 中都有哪些引用类型?
答:
强引用、软引用、弱引用和虚引用. 引用强度依次降低. 强引用不会被GC, 软引用在内存不够的情况下会被回收; 弱引用只要开始GC, 就会被回收. 虚引用最弱, 必须和 ReferenceQueue 队列一起使用.
- 说一下 jvm 有哪些垃圾回收算法?
答:
复制算法: 新生代使用的算法, 分为Eden,和两个 Survive 区域, GC时将Survive 1的对象全部复制到2, 同时Eden中非垃圾对象也复制到2.
标记-整理: Old区域的回收, 首先标记出垃圾对象, 然后将对象整理存放在内存的一端, 这样内存不会有碎片
标记-清除: 跟标记-整理类似, 不过GC时只清除垃圾对象. 可能导致内存不连续.
分代收集: 根据对象生命周期, 上述算法混合使用
- 说一下 jvm 有哪些垃圾回收器?
答:
Serial, Serial Old, ParNew , Parallel Scavenge, Serial Old, Parallel Old, CMS, G1
具体参看面试题 关于垃圾收集的部分.
- 详细介绍一下 CMS 垃圾回收器?
答:
CMS 追求最低停顿时间, 使用标记-清除算法. 一般与parnew 等收集器协同工作. 分四步: 1.初始标记, 2.并发标记, 3.重复标记, 4.并发清除. 1,3会导致停顿.
- 新生代垃圾回收器和老生代垃圾回收器都有哪些?有什么区别?
答:
新生代: Serial, ParNew, parallow scavenge (追求可控的吞吐量)
老生代: CMS (追求最低停顿时间) , Serial Old , parallow Old
G1 为分代收集.
- 简述分代垃圾回收器是怎么工作的?
答:
新生代和老生代采用不同的收集算法, 新生代采用复制算法, 老生代采用标记-整理算法.
- 说一下 jvm 调优的工具?
答:
jps, jinfo, jstat, jstack, jmap,jconsole,jvisualvm
参看这篇文章
- 常用的 jvm 调优的参数都有哪些?
答:
参看面试题 线上应用的 JVM 参数有哪些.