1、什么是 Class 文件? Class 文件主要的信息结构有哪些?
Class 文件是一组以 8 位字节为基础单位的二进制流。各个数据项严格按顺序排列。
Class 文件格式采用一种类似于 C 语言结构体的伪结构来存储数据。这样的伪结构仅仅有两种数据类型:无符号数和表。
无符号数:是基本数据类型。以 u1、u2、u4、u8 分别代表 1 个字节、2 个字节、4 个字节、8 个字节的无符号数,能够用来描写叙述数字、索引引用、数量值或者依照 UTF-8 编码构成的字符串值。
表:由多个无符号数或者其它表作为数据项构成的复合数据类型。全部表都习惯性地以 _info 结尾。
2、并发编程三个必要因素是什么?
原子性:
原子,即一个不可再被分割的颗粒。原子性指的是一个或多个操作要么全部执行成功要么全部执行失败。
可见性:
一个线程对共享变量的修改,另一个线程能够立刻看到。(synchronized,volatile)
有序性:
程序执行的顺序按照代码的先后顺序执行。(处理器可能会对指令进行重排序)
3、说下有哪些类加载器?
Bootstrap ClassLoader(启动类加载器) Extention ClassLoader(扩展类加载器) App ClassLoader(应用类加载器)
4、线程的调度策略
线程调度器选择优先级最高的线程运行,但是,如果发生以下情况,就会终止线程的运行:
1、 线程体中调用了yield方法让出了对cpu的占用权利
2、 线程体中调用了sleep方法使线程进入睡眠状态
3、 线程由于IO操作受到阻塞
4、 另外一个更高优先级线程出现
5)在支持时间片的系统中,该线程的时间片用完
5、用Java写一个冒泡排序。
冒泡排序几乎是个程序员都写得出来,但是面试的时候如何写一个逼格高的冒泡排序却不是每个人都能做到,下面提供一个参考代码:
import java.util.Comparator;
/
* 排序器接口(策略模式: 将算法封装到具有共同接口的独立的类中使得它们可以相互替换)
* @author骆昊
*
*/
public interface Sorter {
/
* 排序
* @param list 待排序的数组
*/
public <T extends Comparable<T>> void sort(T[] list);
/
* 排序
* @param list 待排序的数组
* @param comp 比较两个对象的比较器
*/
public <T> void sort(T[] list, Comparator<T> comp);
}
import java.util.Comparator;
/
* 冒泡排序
*
* @author骆昊
*
*/
public class BubbleSorter implements Sorter {
@Override
public <T extends Comparable<T>> void sort(T[] list) {
boolean swapped = true;
for (int i = 1, len = list.length; i < len && swapped; ++i) {
swapped = false;
for (int j = 0; j < len - i; ++j) {
if (list[j].compareTo(list[j + 1]) > 0) {
T temp = list[j];
list[j] = list[j + 1];
list[j + 1] = temp;
swapped = true;
}
}
}
}
@Override
public <T> void sort(T[] list, Comparator<T> comp) {
boolean swapped = true;
for (int i = 1, len = list.length; i < len && swapped; ++i) {
swapped = false;
for (int j = 0; j < len - i; ++j) {
if (comp.compare(list[j], list[j + 1]) > 0) {
T temp = list[j];
list[j] = list[j + 1];
list[j + 1] = temp;
swapped = true;
}
}
}
}
}
6、Java对象的布局了解过吗?
对象头区域此处存储的信息包括两部分:1、对象自身的运行时数据( MarkWord ),占8字节 存储 hashCode、GC 分代年龄、锁类型标记、偏向锁线程 ID 、 CAS 锁指向线程 LockRecord 的指针等, synconized 锁的机制与这个部分( markwork )密切相关,用 markword 中最低的三位代表锁的状态,其中一位是偏向锁位,另外两位是普通锁位。2、对象类型指针( Class Pointer ),占4字节 对象指向它的类元数据的指针、 JVM 就是通过它来确定是哪个 Class 的实例。
实例数据区域 此处存储的是对象真正有效的信息,比如对象中所有字段的内容
对齐填充区域 JVM 的实现 HostSpot 规定对象的起始地址必须是 8 字节的整数倍,换句话来说,现在 64 位的 OS 往外读取数据的时候一次性读取 64bit 整数倍的数据,也就是 8 个字节,所以 HotSpot 为了高效读取对象,就做了"对齐",如果一个对象实际占的内存大小不是 8byte 的整数倍时,就"补位"到 8byte 的整数倍。所以对齐填充区域的大小不是固定的。
7、解释什么是Tomcat Valve?
Tomcat Valve——Tomcat 4引入的新技术,它允许您将Java类的实例链接到一个特定的Catalina容器。
8、同步方法和同步块,哪个是更好的选择?
同步块是更好的选择,因为它不会锁住整个对象(当然你也可以让它锁住整个对象)。同步方法会锁住整个对象,哪怕这个类中有多个不相关联的同步块,这通常会导致他们停止执行并需要等待获得这个对象上的锁。
同步块更要符合开放调用的原则,只在需要锁住的代码块锁住相应的对象,这样从侧面来说也可以避免死锁。
9、运行时常量池的作用是什么?
运行时常量池是方法区的一部分,Class 文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池表,用于存放编译器生成的各种字面量与符号引用,这部分内容在类加载后存放到运行时常量池。一般除了保存 Class 文件中描述的符号引用外,还会把符号引用翻译的直接引用也存储在运行时常量池。
运行时常量池相对于 Class 文件常量池的一个重要特征是动态性,Java 不要求常量只有编译期才能产生,运行期间也可以将新的常量放入池中,这种特性利用较多的是 String 的 intern
方法。
运行时常量池是方法区的一部分,受到方法区内存的限制,当常量池无法再申请到内存时会抛出 OutOfMemoryError。
10、如果你提交任务时,线程池队列已满,这时会发生什么
有俩种可能:
1、 如果使用的是无界队列 LinkedBlockingQueue,也就是无界队列的话,没关系,继续添加任务到阻塞队列中等待执行,因为 LinkedBlockingQueue 可以近乎认为是一个无穷大的队列,可以无限存放任务
2、 如果使用的是有界队列比如 ArrayBlockingQueue,任务首先会被添加到ArrayBlockingQueue 中,ArrayBlockingQueue 满了,会根据maximumPoolSize 的值增加线程数量,如果增加了线程数量还是处理不过来,ArrayBlockingQueue 继续满,那么则会使用拒绝策略RejectedExecutionHandler 处理满了的任务,默认是 AbortPolicy