Java基础面试题

70 阅读9分钟

==和equals区别

  1. ==对于基本类型来说用于比较数值是否相同,对于引用类型来说用于比较引用地址是否相同。
  2. 基本类型没有equasl方法,对于引用类型,如果没有重写equals方法,效果同==比较的是引用地址是否相同,如果重写了equals方法,如String类则比较的是内容是否相同。

重载和重写的区别

  1. 重载发生在一个类中,而重写发生在父子类中。
  2. 重载是编译时多态,重写是运行时多态。
  3. 重载要求方法名相同,参数列表不同,与方法的修饰符和返回类型无关;重写要求方法名相同,参数列表相同,方法修饰符的权限需要大于等于被重写方法修饰符权限,方法返回类型必须兼容被重写方法的方法返回值类型。

Java中静态代码块、实例代码块、静态方法和构造方法执行顺序

首先是静态代码块,其次实例代码块、最后构造方法juejin.cn/post/738164…

重写equals方法时,为什么还要重写hashcode方法

  1. 首先是,equals和hashcode方法相互配合用于比较两个对象是否相同
  2. 如果只重写了equals方法,会出现两个对象equals相同,而hashcode不同,会导致两个对象在hash表中的存储位置不同。

String、StringBuilder、StringBuffer区别

  • 线程安全上来说,String底层采用的是final修饰的char型数组,每次对字符串对象的操作都会生成一个新的字符串,所以多线程下是线程安全的,而StringBuilder和StringBuffer每次操作都是在原有字符串的基础上操作,但是StringBuffer每次操作对象都会加锁,因此线程安全,而StringBuilder没有加锁因此线程不安全
  • 性能上来说,由于StringBuilder没有加锁因此性能要强于StringBuffer.

String i = ""和String i = new String("")区别

-String i = ""这种形式首先会往字符量常量池中查找是否存在该字符串,如果有则直接返回常量池中的对象地址,如果没有则在常量池中创建一个,并返回地址。String i = new String("")这种形式会直接到堆内存中new一个对象并返回。

抽象类和接口的区别

  • 1.从概念上来讲,抽象类是对一类事物进行抽象,描述的是一类事物的属性和行为。而接口是对事物的共有行为进行抽象。
  • 2.类是单继承、接口可以多实现。
  • 3.抽象类和接口都不能直接实例化对象。
  • 4.抽象类中不仅可以包含抽象方法,还可以包含普通方法,且修饰符。接口中只能包含抽象方法和默认方法。

List和Set的区别

  • List特点是存储数据有序,且可以重复。
  • Set的特点是存储数据无序,且不可以重复。

ArrayList和LinkedList的区别

  • ArrayList底层是数组结构,数组结构的特点是在内存中是一块连续的内存空间,因此寻址比较容易继而查询数据很快。用无参构造方法创建的对象,默认初始化容量为0,初次添加元素的时候,会进行扩容,默认是10,后续添加元素是1.5倍扩容。同时从中插入元素,会进行数据的复制和移动,因此插入效率低。
  • LinkedList底层是链表结构,链表结构导致数据分散存储内存,因此查询数据效率低,但是插入元素效率高,因为只需要更改指针指向即可。

List如何一边遍历一边删除元素

使用迭代器iterator删除元素

public static void main(String[] args) {
    ArrayList<Integer> list = new ArrayList<>();
    list.add(1);
    list.add(2);
    list.add(3);
    // 使用迭代器删除所有元素
    Iterator<Integer> iterator = list.iterator();
    while (iterator.hasNext()) {
        iterator.next(); // 必须调用next()来移动到下一个元素,然后才能调用remove()
        iterator.remove(); // 调用remove()来删除当前元素
    }
    System.out.println(list.size()); // 应该输出0
}

如何实现List和数组之间的转换

List转数组利用toArray方法 数组转List利用Arrays.asList方法

HashMap的实现原理

  • hashMap操作元素通过Hash算法实现的,存储元素是数组+链表的结构,当添加元素时,会计算key的hash值,并通过某种算法进而计算出对应的数组下标,如果当前下标下存在元素,我们称之为hash碰撞或者hash冲突,然后通过equals方法比较内容是否相同,如果相同则替换为当前元素,如果不相同则直接挂在元素的下方形成一个链表。
  • hashMap的初始化容量为16,2倍扩容。

HashMap中的key存储为null,那么key的索引位置在哪

存储在索引下标为0的位置。 来源:www.jb51.net/program/339…

HashMap和HashTable的区别?

  • 1.线程安全上,HashTable是线程安全的,HashMap是线程不安全的,因为HashTable每次操作元素的方法都有synchronized同步锁。
  • 2.存储元素上,hashMap存储元素的key和value都可以为null,而HashTable存储元素的key和value都不能为null。
  • 3.性能上,由于HashTable存在锁的操作,因此性能上比HashMap差。

HashSet的实现原理

hashset的实现原理利用到了HashMap的key的存储原理,也是计算元素的hash值,如果hash值相同,即hash冲突,进而调用equals方法比较内容是否相同,如果相同,则替换元素,否则挂在元素下方。

------------------------------------------异常---------------------------------------------

Exception和Error的区别

  • 同:Exception和Error都是Throwable下的子类
  • 异:Exception是程序可以处理的异常,分为编译时异常(受检查时异常)和运行时异常(非受检查时异常)。而Error是程序无法处理的异常,如虚拟机内存溢出错误。

编译时异常和运行时异常的区别

  • 编译时异常即受检查时异常,即编译时必须要处理的异常,常见的编译时异常有IOException,ClassNotFoundException,FileNotFoundException等。
  • 运行时异常即非受检查时异常,即编译时不需要处理的异常。常见的运行时异常有NullPointException,IndexOutOfBoundsException.

throw和throws的区别

throw用于手动抛出一个异常,throws用于声明可能会抛出的异常。

------------------------------------------异常---------------------------------------------

------------------------------------------线程---------------------------------------------

线程的创建方式有哪些

  1. 继承Thread类重写run方法。
  2. 实现Runnable接口重写run方法。
  3. 实现Callable接口重写call方法,这个方法有返回值。通过FutureTask封装callable对象,通过FutureTask.get方法获取返回值。
  4. 通过线程池创建线程

线程有哪些状态

  • NEW:新建状态,线程被创建后还没被调用start方法前的状态。
  • RUNNABLE:运行状态,线程调用start方法后,并被jvm调用执行的状态。
  • BLOCK:阻塞状态,线程内如等待获取锁的状态。
  • TIMED_WAITING:定时等待状态,调用wait有参方法时所处的状态。
  • WAITING:无限等待撞他, 调用wait无参方法时所处的状态。
  • TERMINATED:终结状态,线程执行完毕所处的状态。

sleep方法和wait方法区别?

  • 位置上:sleep是Thread的方法,wait是Object的方法。
  • 锁的释放行:sleep方法不会释放锁,wait方法会释放锁。
  • 使用范围上:sleep方法可以使用在任何代码块,休眠一段时间,获取到cpu执行权后,会自动恢复执行,而wait方法一般用在同步方法或同步代码块中,必须通过notify或notifyall方法恢复执行。

如何控制线程的执行顺序

1.通过单线程的线程池。 2.通过Thread.join方法。 2.利用CountDownLatch。

多线程如何保证事务

多线程共用一事务,通过代码的方式,手动控制事务。

线程池的创建方式

  • 1.通过Executors工具类下的方法创建线程池
  • 2.通过ThreadPoolExecutor设置自定义参数创建线程池

ThreadPoolExecutor的核心参数有哪些

  • 核心线程数
  • 最大线程数
  • 线程存活时间
  • 线程存活时间对应的单位
  • 任务阻塞队列
  • 线程工厂
  • 拒绝策略

线程池的工作原理

  1. 当一个任务进入线程池时,首先判断当前线程是否超过核心线程,如果不超过则创建核心线程进行任务的执行。
  2. 如果超过核心线程数量,则判断工作队列是否已满,如果没有满,则放入队列中。
  3. 如果队列已满,且线程数不超过最大线程数,则创建最大线程进行任务的执行。
  4. 如果超过最大线程数,则执行拒绝策略。

拒绝策略有哪些

  • AbortPolicy:拒绝当前任务,并抛出异常。
  • DiscardPolicy:拒绝当前任务,不抛出异常。
  • CallRunsPolicy:使用当前调用线程的线程执行任务,如主线程执行当前任务。
  • DiscardOldestPolicy:抛弃队列中的最老的任务,并加入到队列。

如何设置合适的核心线程数和最大线程数

  • CPU密集型:一般核心线程数为cpu核心数,最大线程也是cpu核心数。
  • IO密集型:因为IO操作需要长时间操作磁盘,因此为了避免长期占用系统资源,可以设置为2倍核心数

线程池都有哪些状态

  • RUNNING运行状态,可以接收新任务,可以处理阻塞队列中的任务。
  • SHUTDOWN状态,不会接收新的任务,但会处理阻塞队列中的剩余任务。
  • STOP状态,会中断正在执行的任务,并会抛弃阻塞队列中的任务。
  • TIDYING状态,所有任务执行完毕,活动线程为0,即将终结的状态。
  • TERMINATED状态,终结状态。

线程池中的subimt和execute方法的区别

1.submit方法可以提交Runnable和Callable类型的任务,execute只能提交Runnable类型的任务。 2.submit提交的任务发生异常时会内部处理,execute提交的任务发生异常时会抛出异常。

-----------------------------------------线程----------------------------------------------