目录
- ArrayList和LinkedList的区别
- String、StringBuffer、StringBuilder的区别
- java中的四种引用类型
- Java创建对象的几种方式
- try catch finally和return相关的问题
- OOM有哪些情况,SOF有哪些情况
- IO与NIO的区别
- 获取一个类的class对象有哪几种方式?
- 什么是fail-fast机制?
- HashMap的长度为什么要是2的n次方(保证取余运算能转化成位运算)
- 红黑树有哪些特征
- Object类中的常用方法(重要)
ArrayList和LinkedList的区别
1. ArrayList
1.数据结构
ArrayList是基于动态数组的,实现了动态扩容
2. 相关操作(查询、添加和删除)
ArrayList查询快,添加和删除慢
3. 源码
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
}
- 实现了RandomAccess接口,支持快速随机访问
- 实现了Cloneable接口,能被克隆
2. LinkedList
1. 数据结构
LinkedList是基于链表的(准确来说是双向链表),可以模拟链式队列,链式堆栈等数据结构
2. 相关操作
LinkedList查询慢,添加和删除快
3. 源码
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{
}
String、StringBuffer、StringBuilder的区别
1. String
String类中使用字符数组保存字符串,final修饰的,不可变的,每次赋值都会产生一个新的对象
private final char value[];
2. StringBuffer(线程安全)
StringBuilder是可变的,但append方法中加了synchronized同步锁,所以是线程安全的
public final class StringBuffer
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
{
@Override
public synchronized StringBuffer append(String str) {
toStringCache = null;
super.append(str);
return this;
}
}
3. StringBuilder(线程不安全)
StringBuilder是可变的,但append方法中没有加同步锁,所以是非线程安全的
public final class StringBuilder
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
{
@Override
public StringBuilder append(String str) {
super.append(str);
return this;
}
}
java中的四种引用类型
1. 强引用
内存不足会报OOM异常
2. 软引用
3. 弱引用
4. 虚引用
例如:可用来跟踪对象被垃圾回收器回收的活动,当一个虚引用关联的对象被垃圾收集器回收之前会收到一条系统通知
Java创建对象的几种方式
- 使用new创建对象
- 使用反射的机制创建对象
- 使用构造器创建:Constructor类的newInstance方法
- 采用clone创建
- 使用反序列化创建:Object的readObject方法
try catch finally和return相关的问题
- finally里面的方法时一定会被执行的
- 如果try里有return,执行顺序:先执行try的代码(如果return后面跟的是一个操作,也会先执行),然后再去执行finally方法(如果finally里有return,直接返回),finally执行完之后,再执行try的return操作
- catch里面的如果又return,和第2点流程一样
OOM有哪些情况,SOF有哪些情况
1. OOM(Out Of Memory)
1. 原因
内存溢出、内存泄漏
2. 常见场景
- 内存空间不足,申请不到足够的内存空间
- 垃圾收集器回收垃圾效率太低,也会OOM
- 运行期常量池具备动态扩展性,当intern方法用太多的时候,也会OOM
- 类创建太多的时候,编译期导致方法区内存溢出
- ThreadLocal内存泄漏
2. SOF(StackOverFlowError)
1. 常见场景
- 递归调用
IO与NIO的区别
1. IO(Input OutPut)
1. 面向流
面向流意味着每次从流中读一个或多个字节,直至读取所有字节,它们没有被缓存在任何地方。此外,它不能前后移动流中的数据。如果需要前后移动从流中读取的数据,需要先将它缓存到一个缓冲区。
2. 阻塞的
这意味着,当一个线程调用**read() 或 write()**时,该线程被阻塞,直到有一些数据被读取,或数据完全写入。该线程在此期间不能再干任何事情了。
1. NIO (New Input OutPut)
1. 面向缓冲区
Java NIO的缓冲导向方法略有不同。数据读取到一个它稍后处理的缓冲区,需要时可在缓冲区中前后移动。这就增加了处理过程中的灵活性。但是,还需要检查是否该缓冲区中包含所有您需要处理的数据。而且,需确保当更多的数据读入缓冲区时,不要覆盖缓冲区里尚未处理的数据。
2. 非阻塞的(通道和缓冲区实现)
Java NIO的非阻塞模式,使一个线程从某通道发送请求读取数据,但是它仅能得到目前可用的数据,如果目前没有数据可用时,就什么都不会获取,而不是保持线程阻塞,所以直至数据变的可以读取之前,该线程可以继续做其他的事情。 非阻塞写也是如此。一个线程请求写入一些数据到某通道,但不需要等待它完全写入,这个线程同时可以去做别的事情。 线程通常将非阻塞IO的空闲时间用于在其它通道上执行IO操作,所以一个单独的线程现在可以管理多个输入和输出通道(channel)。
1. 题外话:什么是通道Channel?
表示IO源与目标打开的连接。但是Channel不能直接访问数据,需要和缓冲区buffer进行交互
3. 选择器(Selectors)
Java NIO的选择器允许一个单独的线程来监视多个输入通道(卧槽?这不就是redis中的多路I/O复用模型,但是有点不太一样,redis监控的对象是IO,这里监控的对象是通道。。),你可以注册多个通道使用一个选择器,然后使用一个单独的线程来“选择”通道:这些通道里已经有可以处理的输入,或者选择已准备写入的通道。这种选择机制,使得一个单独的线程很容易来管理多个通道。
4. 为什么NIO会这么快?(基于通道、缓存区、堆外内存)
JDK1.4 中新加入的 NIO(New Input/Output) 类,引入了一种基于通道(Channel)与缓存区(Buffer)的 I/O 方式,它可以直接使用 Native 函数库直接分配堆外内存,然后通过一个存储在 Java 堆中的 DirectByteBuffer(翻译:直接字节缓冲区??) 对象作为这块内存的引用进行操作。这样就能在一些场景中显著提高性能,因为避免了在 Java 堆和 Native 堆之间来回复制数据。
获取一个类的class对象有哪几种方式?
- 类名.class:相对简单,但是需要明确具体类的静态成员(啊?为什么是静态成员呢,这点体现在哪里,原因是:首先你的类都创建好了,那说明类加载好了,类加载器的初始化阶段,编译器会去初始化这个类信息,包括了静态成员)
- 类的对象名.getClass():本质是用的Object的getClass方法,使用该方法2个条件,必须明确具体的类,且要创建对象
- Class.forName(“类的全限定名”) :该方式只需类的全类名即可,更为方便,扩展性更强。
什么是fail-fast机制?
1. 什么是fail-fast机制(ConcurrentModificationException什么时候会抛出)
集合的迭代器在调用next()、remove()方法时都会调用checkForComodification()方法,该方法主要就是检测modCount == expectedModCount ? 若不等则抛出ConcurrentModificationException 异常,从而产生fail-fast机制。modCount是在每次改变集合数量时会改变的值。
2. 什么时候会触发fail-fast机制
- 当遍历集合的同时修改集合
- 多个线程同时对集合进行结构做出改变的操作(未加锁)
1. 题外话:List如何在遍历时删除元素?
- 使用Iterator方法中的remove操作
其实在阿里巴巴Java开发手册中原话:不要在 foreach 循环里进行元素的 remove/add 操作。remove 元素请使用 Iterator方式,如果并发操作,需要对 Iterator 对象加锁。
public interface Iterator<E> {
default void remove() {
throw new UnsupportedOperationException("remove");
}
}
- 倒序遍历
list的size改变并没有对遍历造成影响,且元素的前移也不会对倒序遍历有影响
HashMap的长度为什么要是2的n次方(保证取余运算能转化成位运算)
HashMap为了存取高效,要尽量较少碰撞,就是要尽量把数据分配均匀,每个链表长度大致相同,这个实现就在把数据存到哪个链表中的算法;
这个算法实际就是取模,hash%length,计算机中直接求余效率不如位移运算,源码中做了优化hash&(length-1),
只要满足length是2的n次方,这个等式就成立:hash%length == hash&(length-1)
1. 题外话:hash是如何减少碰撞的?
为什么这样能均匀分布减少碰撞呢?2的n次方实际就是1后面n个0,2的n次方-1 实际就是n个1(要这个!);
例如长度为9时候,3&(9-1)=0 2&(9-1)=0,都在0上,碰撞了;
例如长度为8时候,3&(8-1)=3 2&(8-1)=2,不同位置上,不碰撞;
2. 题外话:什么时候会发生hash碰撞?
接着上个题外话,当提供的数字比hash的大小还要大(因为大的位和这家伙的位&运算一下,就是为0了,相当于取余不是本身了((如5/3 余2,2不是本身)),真的取余了),是不是就有可能存在hash碰撞了呀?
红黑树有哪些特征
- 根节点都是黑色的
- 父子节点不可能同时为红色
- 叶子节点是黑色的空节点(null),不存数据
- 从任意节点到其所有叶子节点的路径都包含相同的黑色节点(用变色或旋转(左旋或右旋)的调整方式实现)
Object类中的常用方法(重要)
1. 多线程相关的方法
- notify():唤醒在此对象监视器上等待的单个线程
- notifyAll():唤醒在此对象监视器上等待的所有线程
- wait(long timeout):使当前线程等待,当其他线程调用此对象的notify()方法或notifyAll()方法,或者超过指定的时间后结束等待
- wait(long timeout, int nanos):和wait(long timeout),不过多了纳秒的时间入参
- wait():当前线程等待
2. 其他方法
- registerNatives():私有方法,应该是注册native的
- getClass():返回此Object的运行Class类
- hashCode():用于获取对象的哈希值
- equals(Object obj):用于确认两个对象是否相同
- clone():创建并返回此对象的一个副本
- toString():返回该对象的字符串表示
- finalize():当改对象确定无引用时,由对象的垃圾回收器调用此方法,把对象的内存回收掉