java面试题每日一练(3)

文章目录

  • 1、Java 静态变量和成员变量的区别?
  • 2、抽象类(abstract class)和接口(interface)有什么区别?
  • 3、Java 中的 final 关键字有哪些用法?
  • 4、try、catch、finally 考察,请指出下面程序的运行结果。
  • 5、wait() 和 sleep() 方法的区别
  • 6、线程的 sleep() 方法和 yield() 方法有什么区别?
  • 7、编写多线程程序有几种实现方式?
  • 8、synchronized 和 Lock 的区别
  • 9、BlockingQueue是什么?
  • 10、List、Set、Map三者的区别?
  • 11、介绍下 HashMap 的底层数据结构
  • 12、JAVA中HashMap和Hashtable区别
  • 13、垃圾收集有哪些算法,各自的特点?
  • 14、Java中的NIO,BIO,AIO分别是什么?
  • 15、Java 序列化与反序列化是什么?


1、Java 静态变量和成员变量的区别?

  • 成员变量存在于堆内存中。静态变量存在于方法区中。
  • 成员变量与对象共存亡,随着对象创建而存在,随着对象被回收而释放。静态变量与类共存亡,随着类的加载而存在,随着类的消失而消失。
  • 成员变量所属于对象,所以也称为实例变量。静态变量所属于类,所以也称为类变量。
  • 成员变量只能被对象所调用 。静态变量可以被对象调用,也可以被类名调用。

2、抽象类(abstract class)和接口(interface)有什么区别?

  • 抽象类只能单继承,接口可以多实现。
  • 抽象类可以有构造方法,接口中不能有构造方法。
  • 抽象类中可以有成员变量,接口中没有成员变量,只能有常量(默认就是 public static final)
  • 抽象类中可以包含非抽象的方法,在 Java 7 之前接口中的所有方法都是抽象的,在 Java 8 之后,接口支持非抽象方法:default方法、静态方法等。Java 9 支持私有方法、私有静态方法。
  • 抽象类中的抽象方法类型可以是任意修饰符,Java 8 之前接口中的方法只能是 public 类型,Java 9 支持 private类型。

设计思想的区别:
接口是自上而下的抽象过程,接口规范了某些行为,是对某一行为的抽象。我需要这个行为,我就去实现某个接口,但是具体这个行为怎么实现,完全由自己决定。

抽象类是自下而上的抽象过程,抽象类提供了通用实现,是对某一类事物的抽象。我们在写实现类的时候,发现某些实现类具有几乎相同的实现,因此我们将这些相同的实现抽取出来成为抽象类,然后如果有一些差异点,则可以提供抽象方法来支持自定义实现。

3、Java 中的 final 关键字有哪些用法?

修饰类:该类不能再派生出新的子类,不能作为父类被继承。因此,一个类不能同时被声明为abstract 和 final。

修饰方法:该方法不能被子类重写。

修饰变量:该变量必须在声明时给定初值,而在以后只能读取,不可修改。 如果变量是对象,则指的是引用不可修改,但是对象的属性还是可以修改的。

4、try、catch、finally 考察,请指出下面程序的运行结果。

实例代码1

public class TryDemo {
    public static void main(String[] args) {
        System.out.println(test());
    }
    public static int test() {
        try {
            return 1;
        } catch (Exception e) {
            return 2;
        } finally {
            System.out.print("3");
        }
    }
}

执行结果: 3 1
解释:try、catch。finally 的基础用法,在 return 前会先执行 finally 语句块,所以是先输出 finally 里的 3,再输出 return 的 1。

实例代码2

public class TryDemo {
    public static void main(String[] args) {
        System.out.println(test1());
    }
    public static int test1() {
        try {
            return 2;
        } finally {
            return 3;
        }
    }
}

执行结果:3

try 返回前先执行 finally,结果 finally 里不按套路出牌,直接 return 了,自然也就走不到 try 里面的 return 了。

实例代码3

public class TryDemo {
    public static void main(String[] args) {
        System.out.println(test1());
    }
    public static int test1() {
        int i = 0;
        try {
            i = 2;
            return i;
        } finally {
            i = 3;
        }
    }
}

执行结果:2。

原因是,在执行 finally 之前,JVM 会先将 i 的结果暂存起来,然后 finally 执行完毕后,会返回之前暂存的结果,而不是返回 i,所以即使这边 i 已经被修改为 3,最终返回的还是之前暂存起来的结果 2。

5、wait() 和 sleep() 方法的区别

  • 来源不同:sleep() 来自 Thread 类,wait() 来自 Object 类。
  • 对于同步锁的影响不同:sleep() 不会停止同步锁的行为,如果当前线程持有同步锁,那么 sleep是不会让线程释放同步锁的。wait() 会释放同步锁,让其他线程进入 synchronized 代码块执行。
  • 使用范围不同:sleep() 可以在任何地方使用。wait() 只能在同步控制方法或者同步控制块里面使用,否则会抛IllegalMonitorStateException。
  • 恢复方式不同:两者会暂停当前线程,但是在恢复上不太一样。sleep() 在时间到了之后会重新恢复;wait()则需要其他线程调用同一对象的notify()/nofityAll() 才能重新恢复。

6、线程的 sleep() 方法和 yield() 方法有什么区别?

线程执行 sleep() 方法后进入超时等待(TIMED_WAITING)状态,而执行 yield() 方法后进入就绪(READY)状态。

sleep() 方法给其他线程运行机会时不考虑线程的优先级,因此会给低优先级的线程运行的机会;yield() 方法只会给相同优先级或更高优先级的线程以运行的机会。

7、编写多线程程序有几种实现方式?

通常来说,可以认为有三种方式:1)继承 Thread 类;2)实现 Runnable 接口;3)实现 Callable 接口。
其中,Thread 其实也是实现了 Runable 接口。Runnable 和 Callable 的主要区别在于是否有返回值。

第一种继承Thread类
java面试题每日一练(3)_第1张图片
第二种实现Runnable接口
java面试题每日一练(3)_第2张图片
第三种,实现Callable 接口

8、synchronized 和 Lock 的区别

1)Lock 是一个接口;synchronized 是 Java 中的关键字,synchronized 是内置的语言实现;

2)Lock 在发生异常时,如果没有主动通过 unLock() 去释放锁,很可能会造成死锁现象,因此使用 Lock 时需要在 finally 块中释放锁;synchronized 不需要手动获取锁和释放锁,在发生异常时,会自动释放锁,因此不会导致死锁现象发生;

3)Lock 的使用更加灵活,可以有响应中断、有超时时间等;而 synchronized 却不行,使用 synchronized 时,等待的线程会一直等待下去,直到获取到锁;

4)在性能上,随着近些年 synchronized 的不断优化,Lock 和 synchronized 在性能上已经没有很明显的差距了,所以性能不应该成为我们选择两者的主要原因。官方推荐尽量使用 synchronized,除非 synchronized 无法满足需求时,则可以使用 Lock。

9、BlockingQueue是什么?

Java.util.concurrent.BlockingQueue是一个队列,在进行检索或移除一个元素的时候,它会等待队列变为非空;当在添加一个元素时,它会等待队列中的可用空间。BlockingQueue接口是Java集合框架的一部分,主要用于实现生产者-消费者模式。我们不需要担心等待生产者有可用的空间,或消费者有可用的对象,因为它都在BlockingQueue的实现类中被处理了。Java提供了集中BlockingQueue的实现,比如ArrayBlockingQueue、LinkedBlockingQueue、PriorityBlockingQueue,、SynchronousQueue等。

10、List、Set、Map三者的区别?

  • List(对付顺序的好帮手): List 接口存储一组不唯一(可以有多个元素引用相同的对象)、有序的对象。
  • Set(注重独一无二的性质):不允许重复的集合,不会有多个元素引用相同的对象。
  • Map(用Key来搜索的专业户): 使用键值对存储。Map 会维护与 Key 有关联的值。两个 Key可以引用相同的对象,但 Key不能重复,典型的 Key 是String类型,但也可以是任何对象。

11、介绍下 HashMap 的底层数据结构

我们现在用的都是 JDK 1.8,底层是由“数组+链表+红黑树”组成,如下图,而在 JDK 1.8 之前是由“数组+链表”组成。
java面试题每日一练(3)_第3张图片
JDK1.8中的涉及到的数据结构
1、位桶数组
在这里插入图片描述
2、数组元素Node实现了Entry接口


3、红黑树

12、JAVA中HashMap和Hashtable区别

Hashtable的方法是同步的,HashMap未经同步,所以在多线程场合要手动同步HashMap这个区别就像Vector和ArrayList一样。

查看Hashtable的源代码就可以发现,除构造函数外,Hashtable的所有 public 方法声明中都有 synchronized 关键字,而HashMap的源代码中则连 synchronized 的影子都没有,当然,注释除外。

HashMap 允许 key 和 value 为 null,Hashtable 不允许。
HashMap 的默认初始容量为 16,Hashtable 为 11。
HashMap 的扩容为原来的 2 倍,Hashtable 的扩容为原来的 2 倍加 1。
HashMap 是非线程安全的,Hashtable是线程安全的。
HashMap 的 hash 值重新计算过,Hashtable 直接使用 hashCode。
HashMap 去掉了 Hashtable 中的 contains 方法。
HashMap 继承自 AbstractMap 类,Hashtable 继承自 Dictionary 类。
Hashtable不允许 null 值(key 和 value 都不可以),HashMap允许 null 值(key和value都可以)。

13、垃圾收集有哪些算法,各自的特点?

标记 - 清除算法

首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象。它的主要不足有两个:一个是效率问题,标记和清除两个过程的效率都不高;另一个是空间问题,标记清除之后会产生大量不连续的内存碎片,空间碎片太多可能会导致以后在程序运行过程中需要分配较大对象时,无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作。

复制算法

为了解决效率问题,一种称为“复制”(Copying)的收集算法出现了,它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。这样使得每次都是对整个半区进行内存回收,内存分配时也就不用考虑内存碎片等复杂情况,只要移动堆顶指针,按顺序分配内存即可,实现简单,运行高效。只是这种算法的代价是将内存缩小为了原来的一半,未免太高了一点。

标记 - 整理算法

复制收集算法在对象存活率较高时就要进行较多的复制操作,效率将会变低。更关键的是,如果不想浪费50%的空间,就需要有额外的空间进行分配担保,以应对被使用的内存中所有对象都100%存活的极端情况,所以在老年代一般不能直接选用这种算法。

根据老年代的特点,有人提出了另外一种“标记-整理”(Mark-Compact)算法,标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。

分代收集算法

当前商业虚拟机的垃圾收集都采用“分代收集”(Generational Collection)算法,这种算法并没有什么新的思想,只是根据对象存活周期的不同将内存划分为几块。

一般是把Java堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法。

在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。

在老年代中因为对象存活率高、没有额外空间对它进行分配担保,就必须使用标记—清理或者标记—整理算法来进行回收。

14、Java中的NIO,BIO,AIO分别是什么?

BIO:同步并阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。BIO方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4以前的唯一选择,但程序直观简单易理解。

NIO:同步非阻塞,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。NIO方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程比较复杂,JDK1.4开始支持。

AIO:异步非阻塞,服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理.AIO方式使用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用OS参与并发操作,编程比较复杂,JDK7开始支持。

15、Java 序列化与反序列化是什么?

Java序列化就是指把Java对象转换为字节序列的过程
Java反序列化就是指把字节序列恢复为Java对象的过程。

对象
java面试题每日一练(3)_第4张图片
序列化
java面试题每日一练(3)_第5张图片
反序列化
java面试题每日一练(3)_第6张图片
运行结果
java面试题每日一练(3)_第7张图片

如有侵权,请联系删除~

你可能感兴趣的:(java面试题,java,开发语言,后端,面试,javase)