金三银四马上要来了,整理了Java一些经典面试题,也给出了答案,希望对大家有帮助,有哪里你觉得不正确的话,欢迎指出,非常感谢。
思路:可以从它们的底层结构、是否允许存储null,是否线性安全等几个维度进行描述,最后可以向面试官描述一下HashMap的死循环问题,以及ConcurrentHashMap为啥放弃分段锁。
HashMap
有关于HashMap死循环,有兴趣可以看看这篇文章,写得很好: 老生常谈,HashMap的死循环
有关于HashMap这些常量设计目的,也可以看我这篇文章:
面试加分项-HashMap源码中这些常量的设计目的
HashTable
ConcurrentHashMap
思路:从它们的底层数据结构、效率、开销进行阐述
String:
StringBuffer:
StringBuilder:
基本类型 | 位数 | 字节 |
---|---|---|
int | 32 | 4 |
short | 16 | 2 |
long | 64 | 8 |
byte | 8 | 1 |
char | 16 | 2 |
float | 32 | 4 |
double | 64 | 8 |
boolean | 1 | 1/8 |
看例子:
public class Test {
public static void main(String[] args) {
System.out.println("Byte bit num: " + Byte.SIZE);
System.out.println("Short bit num : " + Short.SIZE);
System.out.println("Character bit num: " + Character.SIZE);
System.out.println("Integer bit num: " + Integer.SIZE);
System.out.println("Float bit num: " + Float.SIZE);
System.out.println("Long bit num: " + Long.SIZE);
System.out.println("Double bit num: " + Double.SIZE);
}
}
运行结果:
Byte bit num: 8
Short bit num : 16
Character bit num: 16
Integer bit num: 32
Float bit num: 32
Long bit num: 64
Double bit num: 64
String str ="whx";
String newStr =new String ("whx");
String str ="whx"
先在常量池中查找有没有"whx" 这个对象,如果有,就让str指向那个"whx".如果没有,在常量池中新建一个“whx”对象,并让str指向在常量池中新建的对象"whx"。
String newStr =new String (“whx”);
是在堆中建立的对象"whx" ,在栈中创建堆中"whx" 对象的内存地址。
如图所示:
这篇文章讲的挺好的:
String和New String()的区别
BIO
就是传统的 java.io 包,它是基于流模型实现的,交互的方式是同步、阻塞方式,也就是说在读入输入流或者输出流时,在读写动作完成之前,线程会一直阻塞在那里,它们之间的调用时可靠的线性顺序。它的有点就是代码比较简单、直观;缺点就是 IO 的效率和扩展性很低,容易成为应用性能瓶颈。
NIO
是 Java 1.4 引入的 java.nio 包,提供了 Channel、Selector、Buffer 等新的抽象,可以构建多路复用的、同步非阻塞 IO 程序,同时提供了更接近操作系统底层高性能的数据操作方式。
AIO
是 Java 1.7 之后引入的包,是 NIO 的升级版本,提供了异步非堵塞的 IO 操作方式,所以人们叫它 AIO(Asynchronous IO),异步 IO 是基于事件和回调机制实现的,也就是应用操作之后会直接返回,不会堵塞在那里,当后台处理完成,操作系统会通知相应的线程进行后续的操作。
以上内容来自这篇文章,大家可以看一下,写得比较详细
Java核心(五)深入理解BIO、NIO、AIO
Java反射机制:
Java 的反射机制是指在运行状态中,对于任意一个类都能够知道这个类所有的属性和方法; 并且对于任意一个对象,都能够调用它的任意一个方法;这种动态获取信息以及动态调用对象方法的功能成为Java语言的反射机制
获取 Class 类对象三种方式:
可以看一下我写的这篇文章:
谈谈Java反射:从入门到实践,再到原理
什么是幂等性?一次和多次请求某一个资源对于资源本身应该具有同样的结果。就是说,其任意多次执行对资源本身所产生的影响均与一次执行的影响相同。
实现幂等一般有以下几种方式:
可以看一下这篇文章,写得不错:
探讨一下实现幂等性的几种方式
直接看个例子吧:
public class Parent {
{
System.out.println("父类非静态代码块");
}
static {
System.out.println("父类静态块");
}
public Parent() {
System.out.println("父类构造器");
}
}
public class Son extends Parent {
public Son() {
System.out.println("子类构造器");
}
static {
System.out.println("子类静态代码块");
}
{
System.out.println("子类非静态代码块");
}
}
public class Test {
public static void main(String[] args) {
Son son = new Son();
}
}
运行结果:
父类静态块
子类静态代码块
父类非静态代码块
父类构造器
子类非静态代码块
子类构造器
所以,类实例化顺序为:
父类静态代码块/静态域->子类静态代码块/静态域 -> 父类非静态代码块 -> 父类构造器 -> 子类非静态代码块 -> 子类构造器
Class.forName和ClassLoader都可以对类进行加载。它们区别在哪里呢?
ClassLoader负责加载 Java 类的字节代码到 Java 虚拟机中。Class.forName其实是调用了ClassLoader,如下:
这里面,forName0的第二个参数为true,表示对加载的类进行初始化化。其实还可以调用Class> forName(String name, boolean initialize, ClassLoader loader)
方法实现一样的功能,它的源码如下:
所以,Class.forName和ClassLoader的区别,就是在类加载的时候,class.forName有参数控制是否对类进行初始化。
这篇文章写得不错,描述Java动态代理的几种实现方式,分别说出相应的优缺点
Error: 表示编译时或者系统错误,如虚拟机相关的错误,OutOfMemoryError等,error是无法处理的。
Exception: 代码异常,Java程序员关心的基类型通常是Exception。它能被程序本身可以处理,这也是它跟Error的区别。
它可以分为RuntimeException(运行时异常)和CheckedException(可检查的异常)。
常见的RuntimeException异常:
- NullPointerException 空指针异常
- ArithmeticException 出现异常的运算条件时,抛出此异常
- IndexOutOfBoundsException 数组索引越界异常
- ClassNotFoundException 找不到类异常
- IllegalArgumentException(非法参数异常)
常见的 Checked Exception 异常:
- IOException (操作输入流和输出流时可能出现的异常)
- ClassCastException(类型转换异常类)
有兴趣可以看我之前写得这篇文章:
Java程序员必备:异常的十个关键知识点
CAS涉及三个操作数
CAS思想:要进行更新时,认为位置V上的值还是跟A值相等,如果是是相等,就认为它没有被别的线程更改过,即可更新为B值。否则,认为它已经被别的线程修改过,不更新为B的值,返回当前位置V最新的值。
有兴趣的朋友可以看一下我这篇文章,一次CAS思想解决实际问题:
CAS乐观锁解决并发问题的一次实践
复制了对象的引用地址,两个对象指向同一个内存地址,所以修改其中任意的值,另一个值都会随之变化。
将对象及值复制过来,两个对象修改其中任意的值另一个值不会改变
这篇文章写得很好:
Java Serializable:明明就一个空的接口嘛
==
equals
AQS原理面试题的核心回答要点
可以看我这篇文章:AQS解析与实战
可以看一下我这篇文章:
Synchronized解析——如果你愿意一层一层剥开我的心
可以看我这篇文章:
面试必备:Java线程池解析
还有哪些经典Java面试题呢?你也可以告诉我,哈哈。