常见Java面试题解析(基础篇,附答案)

前言

金三银四立刻要来了,整理了Java一些经典面试题,也给出了答案,但愿对你们有帮助,有哪里你以为不正确的话,欢迎指出,很是感谢。html

HashMap,HashTable,ConcurrentHash的共同点和区别

思路:能够从它们的底层结构、是否容许存储null,是否线性安全等几个维度进行描述,最后能够向面试官描述一下HashMap的死循环问题,以及ConcurrentHashMap为啥放弃分段锁。java

HashMap程序员

  • 底层由链表+数组实现
  • 能够存储null键和null值
  • 线性不安全
  • 初始容量为16,扩容每次都是2的n次幂
  • 加载因子为0.75,当Map中元素总数超过Entry数组的0.75,触发扩容操做.
  • 并发状况下,HashMap进行put操做会引发死循环,致使CPU利用率接近100%

有关于HashMap死循环,有兴趣能够看看这篇文章,写得很好: 老生常谈,HashMap的死循环面试

有关于HashMap这些常量设计目的,也能够看我这篇文章: 面试加分项-HashMap源码中这些常量的设计目的redis

HashTable算法

  • HashTable的底层也是由链表+数组实现。
  • 不管key仍是value都不能为null
  • 它是线性安全的,使用了synchronized关键字。

ConcurrentHashMapspring

  • ConcurrentHashMap的底层是数组+链表/红黑树
  • 不能存储null键和值
  • ConcurrentHashMap是线程安全的
  • ConcurrentHashMap使用锁分段技术确保线性安全
  • JDK8为什么又放弃分段锁,是由于多个分段锁浪费内存空间,竞争同一个锁的几率很是小,分段锁反而会形成效率低。

ArrayList和LinkedList有什么区别。

思路:从它们的底层数据结构、效率、开销进行阐述数据库

  • ArrayList是数组的数据结构,LinkedList是链表的数据结构。
  • 随机访问的时候,ArrayList的效率比较高,由于LinkedList要移动指针,而ArrayList是基于索引(index)的数据结构,能够直接映射到。
  • 插入、删除数据时,LinkedList的效率比较高,由于ArrayList要移动数据。
  • LinkedList比ArrayList开销更大,由于LinkedList的节点除了存储数据,还须要存储引用。

String,Stringbuffer,StringBuilder的区别。

String:设计模式

  • String类是一个不可变的类,一旦建立就不能够修改。
  • String是final类,不能被继承
  • String实现了equals()方法和hashCode()方法

StringBuffer:数组

  • 继承自AbstractStringBuilder,是可变类。
  • StringBuffer是线程安全的
  • 能够经过append方法动态构造数据。

StringBuilder:

  • 继承自AbstractStringBuilder,是可变类。
  • StringBuilder是非线性安全的。
  • 执行效率比StringBuffer高。

JAVA中的几种基本数据类型是什么,各自占用多少字节。

基本类型 位数 字节
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 s 与new String的区别

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、Nio、Aio区别

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

谈谈spring的生命周期

  • 首先容器启动后,对bean进行初始化
  • 按照bean的定义,注入属性
  • 检测该对象是否实现了xxxAware接口,并将相关的xxxAware实例注入给bean,如BeanNameAware等
  • 以上步骤,bean对象已正确构造,经过实现BeanPostProcessor接口,能够再进行一些自定义方法处理。 如:postProcessBeforeInitialzation。
  • BeanPostProcessor的前置处理完成后,能够实现postConstruct,afterPropertiesSet,init-method等方法, 增长咱们自定义的逻辑,
  • 经过实现BeanPostProcessor接口,进行postProcessAfterInitialzation后置处理
  • 接着Bean准备好被使用啦。
  • 容器关闭后,若是Bean实现了DisposableBean接口,则会回调该接口的destroy()方法
  • 经过给destroy-method指定函数,就能够在bean销毁前执行指定的逻

反射的原理,反射建立类实例的三种方式是什么。

Java反射机制:

Java 的反射机制是指在运行状态中,对于任意一个类都可以知道这个类全部的属性和方法; 而且对于任意一个对象,都可以调用它的任意一个方法;这种动态获取信息以及动态调用对象方法的功能成为Java语言的反射机制

获取 Class 类对象三种方式:

  • 使用 Class.forName 静态方法
  • 使用类的.class 方法
  • 使用实例对象的 getClass() 方法

能够看一下我写的这篇文章: 谈谈Java反射:从入门到实践,再到原理

说几种实现幂等的方式

什么是幂等性?一次和屡次请求某一个资源对于资源自己应该具备一样的结果。就是说,其任意屡次执行对资源自己所产生的影响均与一次执行的影响相同。

实现幂等通常有如下几种方式:

  • 悲观锁方式(如数据库的悲观锁,select...for update)
  • 乐观锁方式 (如CAS算法)
  • 惟一性约束(如惟一索引)
  • 分布式锁 (redis分布式锁等)

能够看一下这篇文章,写得不错: 探讨一下实现幂等性的几种方式

讲讲类的实例化顺序,如父类静态数据,构造函数,字段,子类静态数据,构造函数,字段等。

直接看个例子吧:

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区别

Class.forName和ClassLoader均可以对类进行加载。它们区别在哪里呢? ClassLoader负责加载 Java 类的字节代码到 Java 虚拟机中。Class.forName实际上是调用了ClassLoader,以下:

这里面,forName0的第二个参数为true,表示对加载的类进行初始化化。其实还能够调用 Class<?> forName(String name, boolean initialize, ClassLoader loader)方法实现同样的功能,它的源码以下:

因此,Class.forName和ClassLoader的区别,就是在类加载的时候,class.forName有参数控制是否对类进行初始化。

JDK动态代理与cglib实现的区别

  • java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。
  • cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,经过修改其字节码生成子类来处理。
  • JDK动态代理只能对实现了接口的类生成代理,而不能针对类
  • cglib是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法。由于是继承,因此该类或方法最好不要声明成final

这篇文章写得不错,描述Java动态代理的几种实现方式,分别说出相应的优缺点

error和exception的区别,CheckedException,RuntimeException的区别。

Error: 表示编译时或者系统错误,如虚拟机相关的错误,OutOfMemoryError等,error是没法处理的。

Exception: 代码异常,Java程序员关心的基类型一般是Exception。它能被程序自己能够处理,这也是它跟Error的区别。

它能够分为RuntimeException(运行时异常)和CheckedException(可检查的异常)。 常见的RuntimeException异常:

- NullPointerException 空指针异常
- ArithmeticException 出现异常的运算条件时,抛出此异常
- IndexOutOfBoundsException 数组索引越界异常
- ClassNotFoundException 找不到类异常
- IllegalArgumentException(非法参数异常)
复制代码

常见的 Checked Exception 异常:

- IOException (操做输入流和输出流时可能出现的异常)
- ClassCastException(类型转换异常类)
复制代码

有兴趣能够看我以前写得这篇文章: Java程序员必备:异常的十个关键知识点

CAS机制是什么,如何解决ABA问题?

CAS涉及三个操做数

  • 1.须要读写的内存地址V
  • 2.进行比较的预期原值A
  • 3.拟写入的新值B 若是内存位置的值V与预期原A值相匹配,那么处理器会自动将该位置值更新为新值B。

CAS思想:要进行更新时,认为位置V上的值仍是跟A值相等,若是是是相等,就认为它没有被别的线程更改过,便可更新为B值。不然,认为它已经被别的线程修改过,不更新为B的值,返回当前位置V最新的值。

有兴趣的朋友能够看一下我这篇文章,一次CAS思想解决实际问题: CAS乐观锁解决并发问题的一次实践

深拷贝和浅拷贝区别

浅拷贝

复制了对象的引用地址,两个对象指向同一个内存地址,因此修改其中任意的值,另外一个值都会随之变化。

深拷贝

将对象及值复制过来,两个对象修改其中任意的值另外一个值不会改变

谈谈序列化与反序列化

  • 序列化是指将对象转换为字节序列的过程,而反序列化则是将字节序列转换为对象的过程。
  • Java对象序列化是将实现了Serializable接口的对象转换成一个字节序列,可以经过网络传输、文件存储等方式传输 ,传输过程当中却没必要担忧数据在不一样机器、不一样环境下发生改变,也没必要关心字节的顺序或其余任何细节,并可以在之后将这个字节序列彻底恢复为原来的对象。

这篇文章写得很好: Java Serializable:明明就一个空的接口嘛

==与equlas有什么区别?

==

  • 若是是基本类型,==表示判断它们值是否相等;
  • 若是是引用对象,==表示判断两个对象指向的内存地址是否相同。

equals

  • 若是是字符串,表示判断字符串内容是否相同;
  • 若是是object对象的方法,比较的也是引用的内存地址值;
  • 若是本身的类重写equals方法,能够自定义两个对象是否相等。

谈谈AQS 原理以及AQS同步组件

AQS原理面试题的核心回答要点

  • state 状态的维护。
  • CLH队列
  • ConditionObject通知
  • 模板方法设计模式
  • 独占与共享模式。
  • 自定义同步器。
  • AQS全家桶的一些延伸,如:ReentrantLock等。

能够看我这篇文章:AQS解析与实战

final、finalize()、finally的区别

  • final是关键字,用于修饰类、成员变量和成员方法。
  • Finalize是object类中的一个方法,子类能够重写finalize()方法实现对资源的回收。
  • finally通常跟try一块儿,出如今异常处理代码块中

synchronized 底层如何实现?

能够看一下我这篇文章: Synchronized解析——若是你愿意一层一层剥开个人心

Java线程池的原理?线程池有哪些?线程池工厂有哪些线程池类型,及其线程池参数是什么?

对于Java线程池,这个流程图比较重要:

能够看我这篇文章: 面试必备:Java线程池解析

待更新

还有哪些经典Java面试题呢?你也能够告诉我,哈哈。

参考与感谢

我的公众号

  • 若是你是个爱学习的好孩子,能够关注我公众号,一块儿学习讨论。
  • 若是你以为本文有哪些不正确的地方,能够评论,也能够关注我公众号,私聊我,你们一块儿学习进步哈。
相关文章
相关标签/搜索