一、Java基本语法面试题

1. 是否可以从一个static方法内部发出对非static方法的调用

不可以。因为非static方法是要与对象关联在一起的,必须创建一个对象后,才可以在该对象上进行方法调用,而static方法调用时不需要创建对象,可以直接调用。也就是说,当一个static方法被调用时,可能还没有创建任何实例对象,如果从一个static方法中发出对非static方法的调用,那个非static方法是关联到哪个对象上的呢?这个逻辑无法成立。

2. Integer与int的区别

int是java提供的8种原始数据类型之一,Integer是java为int提供的封装类。int的默认值为0,而Integer的默认值为null,即Integer可以区分出未赋值和值为0的区别,int则无法表达出未赋值的情况。

在JSP开发中,Integer的默认为null,所以用el表达式在文本框中显示时,值为空白字符串,而int默认的默认值为0,所以用el表达式在文本框中显示时,结果为0,所以,int不适合作为web层的表单数据的类型。

在Hibernate中,如果将OID定义为Integer类型,那么Hibernate就可以根据其值是否为null而判断一个对象是否是临时的,如果将OID定义为了int类型,还需要在hbm映射文件中设置其unsaved-value属性为0。

3. 抽象类和接口有什么区别

  1. 抽象类可以有构造方法,接口中不能有构造方法。
  2. 抽象类中可以有普通成员变量,接口中没有普通成员变量
  3. 抽象类中可以包含非抽象的普通方法,接口中的所有方法必须都是抽象的,不能有非抽象的普通方法。
  4. 抽象类中的抽象方法的访问类型可以是public,protected和(默认类型,虽然eclipse下不报错,但应该也不行),但接口中的抽象方法只能是public类型的,并且默认即为public abstract类型。
  5. 抽象类中可以包含静态方法,接口中不能包含静态方法
  6. 抽象类和接口中都可以包含静态成员变量,抽象类中的静态成员变量的访问类型可以任意,但接口中定义的变量只能是publicstatic final类型,并且默认即为publicstatic final类型。
  7. 一个类可以实现多个接口,但只能继承一个抽象类。

4. String、StringBuffer与StringBuilder的区别

  1. String 类型和 StringBuffer 类型的主要性能区别其实在于 String 是不可变的对象
  2. StringBuffer和StringBuilder底层是 char[]数组实现的
  3. StringBuffer是线程安全的,而StringBuilder是线程不安全的

5. final,finally,finalize的区别

  1. final 用于声明属性,方法和类,分别表示属性不可变,方法不可覆盖,类不可继承。内部类要访问局部变量,局部变量必须定义成final类型。
  2. finally是异常处理语句结构的一部分,表示总是执行。
  3. finalize是Object类的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法,可以覆盖此方法提供垃圾收集时的其他资源回收,例如关闭文件等。但是JVM不保证此方法总被调用.

6. error和exception有什么区别

  • Error(错误)表示系统级的错误和程序不必处理的异常,是java运行环境中的内部错误或者硬件问题。比如:内存资源不足等。对于这种错误,程序基本无能为力,除了退出运行外别无选择,它是由Java虚拟机抛出的。
  • Exception(违例)表示需要捕捉或者需要程序进行处理的异常,它处理的是因为程序设计的瑕疵而引起的问题或者在外的输入等引起的一般性问题,是程序必须处理的。
  • Exception又分为运行时异常,受检查异常。
    • 运行时异常,表示无法让程序恢复的异常,导致的原因通常是因为执行了错误的操作,建议终止程序,因此,编译器不检查这些异常。
    • 受检查异常,是表示程序可以处理的异常,也即表示程序可以修复(由程序自己接受异常并且做出处理), 所以称之为受检查异常。

7. Java中,throw和throws有什么区别

  • throw代表的是动作,throws是状态
  • throw用在方法中,而throws用在方法声明中
  • throw只能抛出一种异常,而throws可以抛出多个

8. &和&&的区别?

  • &运算符有两种用法:(1)按位与;(2)逻辑与。
  • &&运算符是短路与运算。 >如果&&左边的表达式的值是false,右边的表达式会被直接短路掉,不会进行运算。

9. switch能否用String做参数?

在 Java 7 之前, switch 只能支持byte,short,char,int 或者其对应的封装类,以及 Enum 类型。在JAVA 7中,String 支持被加上了。

10. finalize方法什么时候被调用?

在释放对象占用的内存之前,且对象的finalize()还没被垃圾收集器调用过,则垃圾收集器会调用对象的finalize()方法。

11. 用最有效率的方法计算2乘以8?

2 << 3(左移3位相当于乘以2的3次方,右移3位相当于除以2的3次方)。

二、面向对象面试题

1. 面向对象的特征有哪些方面

  • 抽象:抽象是将一类对象的共同特征总结出来构造类的过程,包括数据抽象和行为抽象两方面。抽象只关注对象有哪些属性和行为,并不关注这些行为的细节是什么。
  • 继承:继承是从已有类得到继承信息创建新类的过程。提供继承信息的类被称为父类(超类、基类);得到继承信息的类被称为子类(派生类)。继承让变化中的软件系统有了一定的延续性,同时继承也是封装程序中可变因素的重要手段。
  • 封装:通常认为封装是把数据和操作数据的方法绑定起来,对数据的访问只能通过已定义的接口。面向对象的本质就是将现实世界描绘成一系列完全自治、封闭的对象。我们在类中编写的方法就是对实现细节的一种封装;我们编写一个类就是对数据和数据操作的封装。可以说,封装就是隐藏一切可隐藏的东西,只向外界提供最简单的编程接口(可以想想普通洗衣机和全自动洗衣机的差别,明显全自动洗衣机封装更好因此操作起来更简单;我们现在使用的智能手机也是封装得足够好的,因为几个按键就搞定了所有的事情)。
  • 多态性:多态性是指允许不同子类型的对象对同一消息作出不同的响应。简单的说就是用同样的对象引用调用同样的方法但是做了不同的事情。多态性分为编译时的多态性和运行时的多态性。如果将对象的方法视为对象向外界提供的服务,那么运行时的多态性可以解释为:当A系统访问B系统提供的服务时,B系统有多种提供服务的方式,但一切对A系统来说都是透明的。方法重载(overload)实现的是编译时的多态性(也称为前绑定),而方法重写(override)实现的是运行时的多态性(也称为后绑定)。运行时的多态是面向对象最精髓的东西,要实现多态需要做两件事:1). 方法重写(子类继承父类并重写父类中已有的或抽象的方法);2). 对象造型(用父类型引用引用子类型对象,这样同样的引用调用同样的方法就会根据子类对象的不同而表现出不同的行为)。

2. 如何实现对象克隆?

有两种方式:

  • 实现Cloneable接口并重写Object类中的clone()方法;
  • 实现Serializable接口,通过对象的序列化和反序列化实现克隆,可以实现真正的深度克隆。

注意:基于序列化和反序列化实现的克隆不仅仅是深度克隆,更重要的是通过泛型限定,可以检查出要克隆的对象是否支持序列化,这项检查是编译器完成的,不是在运行时抛出异常,这种是方案明显优于使用Object类的clone方法克隆对象。让问题在编译的时候暴露出来总是优于把问题留到运行时。

3. 讲讲类的实例化顺序,比如父类静态数据,父类构造函数,父类字段,子类静态数据,子类构造函数,子类字段,当new的时候,他们的执行顺序。

类的实例化顺序:先静态再父子

父类静态数据->子类静态数据->父类字段->子类字段->父类构造函数->子类构造函数

三、集合面试题

1. Collection和Collections的区别?

  • Collection是一个接口,它是Set、List等容器的父接口;
  • Collections是个一个工具类,提供了一系列的静态方法来辅助容器操作,这些方法包括对容器的搜索、排序、线程安全化等等。

2. ArrayList和LinkedList有什么区别

区别

  • ArrayList是实现了基于动态数组的数据结构,LinkedList是基于链表结构。
  • 对于随机访问的get和set方法,ArrayList要优于LinkedList,因为LinkedList要移动指针。
  • 对于新增和删除操作add和remove,LinkedList比较占优势,因为ArrayList要移动数据。

性能差异

  • 对ArrayList和LinkedList而言,在列表末尾增加一个元素所花的开销都是固定的。对ArrayList而言,主要是在内部数组中增加一项,指向所添加的元素,偶尔可能会导致对数组重新进行分配;而对LinkedList而言,这个开销是统一的,分配一个内部Entry对象。
  • 在ArrayList集合中添加或者删除一个元素时,当前的列表所所有的元素都会被移动。而LinkedList集合中添加或者删除一个元素的开销是固定的。
  • LinkedList集合不支持 高效的随机随机访问(RandomAccess),因为可能产生二次项的行为。
  • ArrayList的空间浪费主要体现在在list列表的结尾预留一定的容量空间,而LinkedList的空间花费则体现在它的每一个元素都需要消耗相当的空间

3. HashMap,怎么扩容,怎么处理数据冲突?

HashMap底层数据结构是基于数组+链表的(1.8之后还引入了红黑树),Node数组的初始化长度为16,负载因子为0.75,HashMap所能容纳的最大数据量的Node(键值对)个数=Node数组初始化长度负载因子,当HashMap所能容纳的最大数据量的Node个数大于Node数组初始化长度负载因子的时候,会调用resize()方法进行扩容,扩容后的HashMap容量是之前容量的两倍。扩容的方法是创建一个新的数组替换原来的小数组,并把数据放入新数组。

HashMap通过对key的hash值进行高位运算和取模来定位键值对的存储位置,有时两个key会定位到相同的位置,表示发生了Hash碰撞。比较两个key是否相等,如果相等直接覆盖原来key的值。

四、多线程面试题

1. volatile关键字是否能保证线程安全?

否,volatile变量自身具有下列特性:

  • 可见性:对一个volatile变量的读,总是能看到(任意线程)对这个volatile变量最后的写入。
  • 禁止进行指令重排序
  • 不保证原子性:对任意单个volatile变量的读/写具有原子性,但类似于volatile++这种符合操作不具有原子性。 多线程共享变量的读写是通过主内存进行复制到线程本地内存中和把本地内存中值写入主内存中来实现的。

当写一个volatile变量时,JMM(Java内存模型)会把该线程对应的本地内存中的共享变量值刷新到主内存。

当读一个volatile变量时,JMM会把该线程对应的本地内存置为无效。线程接下来将从住内存中读取共享变量。

2. Java中sleep和wait的区别

  • 来自不同的类 : sleep来自Thread类,是Thread类的静态方法。而wait来自于object类。sleep是Thread的静态方法,那个线程调用的了sleep,那个线程就会休眠.
  • 持有锁:这一点很重要,sleep是没有释放锁的,使得该线程仍然占用这系统资源,wait方法则会释放锁,使得其他线程可以使用同步控制块或者方法。sleep 不释放系统资源并且有时间限制,需要调用sleep(milliseconds)来指定休眠时间 wait会释放资源,并且进入线程等待池等待,这个时候其他线程可以占用CPU,直到其他线程调用notify/notifyAll唤醒等待池中的线程,然后进入就绪队列等待系统分配资源
  • 使用范围: wait,notify,notifyAll只能在synchronized块中或者synchronied控制的方法中调用。而sleep可以在任何地方调用

3. synchronized和lock的区别?

  • 主要相同点:Lock能完成synchronized所实现的所有功能。
  • 主要不同点:Lock有比synchronized更精确的线程予以和更好的性能。
  1. synchronized会自动释放锁,但是Lock一定要求程序员手工释放,并且必须在finally从句中释放。
  2. synchronized修饰方法时,表示同一个对象在不同的线程中,表现为同步队列,如果实例化不同的对象,那么synchronized就不会出现同步效果了。

4. 创建线程有几种不同的方式?你喜欢哪一种?为什么?

四种方式可以用来创建线程:

  • 通过继承Thread类,优点:可以直接调用start方法启动。缺点:继承一个类后,不能再继承别的类。需要重写run方法。无返回值。
  • 实现Runnable接口,优点:可以实现多个接口或继承一个类;缺点:不能直接启动,要通过构造一个Thread把自己传进去。需要重写run方法,无返回值。
  • 实现Callable接口,优点:可以抛出异常,有返回值;缺点:只有jkd1.5以后才支持。需要重写call方法。结合FutureTask和Thread类一起使用,最后调用start启动。
  • 应用程序可以使用Executor框架来创建线程池,优点:非常高效的,很容易实现和使用。

5. 什么是线程?

线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。程序员可以通过它进行多处理器编程,你可以使用多线程对运算密集型任务提速。

6. 线程和进程有什么区别?

线程是进程的子集,一个进程可以有很多线程,每条线程并行执行不同的任务。不同的进程使用不同的内存空间,而所有的线程共享一片相同的内存空间。别把它和栈内存搞混,每个线程都拥有单独的栈内存用来存储本地数据。

7. Thread类中的start()和run()方法有什么区别?

start()方法被用来启动新创建的线程,而且start()内部调用了run()方法,这和直接调用run()方法的效果不一样。当你调用run()方法的时候,只会是在原来的线程中调用,没有新的线程启动,start()方法才会启动新线程。

8. Java中Runnable和Callable有什么不同?

Runnable和Callable都代表那些要在不同的线程中执行的任务。Runnable从JDK1.0开始就有了,Callable是在JDK1.5增加的。它们的主要区别是Callable的 call() 方法可以返回值和抛出异常,而Runnable的run()方法没有这些功能。Callable可以返回装载有计算结果的Future对象.

9. Java中CyclicBarrier和CountDownLatch有什么不同?

CyclicBarrier 和 CountDownLatch 都可以用来让一组线程等待其它线程。与 CyclicBarrier 不同的是,CountdownLatch 不能重新使用。 CountDownLatch :允许一个或多个线程等待其他线程完成操作。 CyclicBarrier :同步屏障,它可以让一组线程到达一个屏障(也可以叫做同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续运行。

10. Java中interrupted和isInterrupted方法的区别?

interrupted() 和 isInterrupted()的主要区别是前者会将中断状态清除而后者不会。Java多线程的中断机制是用内部标识来实现的,调用Thread.interrupt()来中断一个线程就会设置中断标识为true。当中断线程调用静态方法Thread.interrupted()来检查中断状态时,中断状态会被清零。而非静态方法isInterrupted()用来查询其它线程的中断状态且不会改变中断状态标识。简单的说就是任何抛出InterruptedException异常的方法都会将中断状态清零。无论如何,一个线程的中断状态有有可能被其它线程调用中断来改变。

11. 为什么wait和notify方法要在同步块中调用?

主要是因为Java API强制要求这样做,如果你不这么做,你的代码会抛出IllegalMonitorStateException异常。还有一个原因是为了避免wait和notify之间产生竞态条件。

写在最后

本次面试题分享到此为止,限于篇幅,没法在这里给大家分享更多的面试题; 之前很多粉丝一直私信我让我整理一些面试题,近些天终于整理好了,笔者整理了一份包含Kafka、Mysql、Tomcat、Docker、Spring、MyBatis、Nginx、Netty、Dubbo、Redis、Netty、Spring cloud、分布式、高并发、性能调优、微服务等架构技术的面试题和部分视频学习资料;

 

备战金九银十,200京东Java岗最新面试题:语法+面向对象+集合+多线程_程序员