Java基础+集合(含答案)

一、Java基础系列面试题

JDK 和 JRE 有什么区别?

jre是java程序运行的环境,jdk是java开发所需要的环境,jdk包含jre

== 和 equals 的区别是什么

==和Object的equals都是比较的地址,但有一些类会重写equals方法,string中会比较两个字符串的值

两个对象的 hashCode() 相同,则 equals() 也一定为 true,对吗?

不对,hashcode和equals是两个方法,没有重写的equals比较的是对象的地址值,hashcode比较的是对象地址的哈希值。如果目标对象重写了equals保证相同内容的两个对象返回true,而这个对象有可能需要被放入到散列表中(hashmap等),那么需要重写hashcode方法保证两个对象返回的哈希值也是相同的,不然可能会出现一个map中出现两个key 相同的对象。

hashcode相等两个类一定相等吗?

  1. hashcode方法和equals方法没有重写时
    1. hashcode相等两个类不一定相等
    2. equals返回true的两个类一定相等(为同一个对象)
    3. 两个类相等hashcode不一定相等
    4. 两个类相等equals不一定返回true
  2. hashcode方法和equals方法均已按规范重写时
    1. hashcode相等两个类不一定相等(存在哈希冲突)
    2. equals返回true的两个类一定相等
    3. 两个类相等hashcode一定相等
    4. 两个类相等equals一定返回true

java对重写equals方法的要求

1. 对称性:如果x.equals(y)返回是"true",那么y.equals(x)也应该返回是"true"。
2. 反射性:x.equals(x)必须返回是"true"。
3. 类推性:如果x.equals(y)返回是"true",而且y.equals(z)返回是"true",那么z.equals(x)也应该返回是"true"。
4. 一致性:如果x.equals(y)返回是"true",只要x和y内容一直不变,不管你重复x.equals(y)多少次,返回都是"true"。
5. 非空性,x.equals(null),永远返回是"false";x.equals(和x不同类型的对象)永远返回是"false"。

final 在 Java 中有什么作用?

final修饰的类不能被继承,final修饰的方法不能被重写,final修饰的变量不能被修改

final修饰引用类型时,其引用的地址中存储的值是允许改变的。

Java 中的 Math. round(-1. 5) 等于多少?

等于1,四舍五入原理是+0.5,然后向下取整

String 属于基础的数据类型吗?

基础类型就八个,其他都是引用类型

Java 中操作字符串都有哪些类?它们之间有什么区别?

String,StringBuilder,StringBuffer

String声明的字符串是final类型的,也就是个常量,底层是final类型的char数组

StringBuilder和StringBuffer都是用来字符串拼接操作,value不是final类型的,builder是非线程安全的,buffer是线程安全的,因为用synchronized修饰了方法

在涉及较少的修改操作时,尽可能用string类不通过new关键字去创建字符串,这样不会在堆空间产生对象,而是在String的常量池中生成,可以复用,效率更高

String str="i"与 String str=new String(“i”)一样吗?

不一样,前者在常量池中生成,后者在堆空间生成;字符串常量在编译器就已经确定,所以String str = "a" + "b";也会放在常量池中

如何将字符串反转?

StringBuilder的reverse()方法:new StringBuffer(s).reverse().toString();源码大概就是从中间往两边循环,两两交换。
用一个char数组去逆序遍历,通过stringbuilder去拼接append;

递归二分法,终止条件是s,len == 0

public static String reverse1(String s) {

  int length = s.length();

  if(length <= 1){

    return s;

    }

  String left = s.substring(0, length / 2);

  String right = s.substring(length / 2, length);

  return reverse1(right) + reverse1(left);

}


 

String 类的常用方法都有那些?

(1).indexOf():返回指定字符的索引。

(2).charAt():返回指定索引处的字符。

(3).replace():字符串替换。

(4).trim():去除字符串两端空白。

(5).split():分割字符串,返回一个分割后的字符串数组。

(6).getBytes():返回字符串的 byte 类型数组。

(7).length():返回字符串长度。

(8).toLowerCase():将字符串转成小写字母。

(9).toUpperCase():将字符串转成大写字符。

(10).substring():截取字符串。

(11).equals():字符串比较。 

抽象类必须要有抽象方法吗?

不是必须的,但有抽象方法一定要声明类是个抽象类,且抽象类中可以有具体方法;接口现在也可以通过default关键字声明一个具体实现的方法。abstract和stataic不能同时修饰方法,因为abstract是抽象方法,需要被子类实现的,但static是静态方法,类独有的,不能被子类重写,所以冲突。

普通类和抽象类有哪些区别?

普通类中所有方法都是具体实现了的,抽象类可以有具体方法,也可以有抽象方法,抽象类和抽象方法都需要abstract关键字修饰。普通类可以创建对象,抽象类不能。

抽象方法需要暴露出来给子类重写,所以不能用static,private,final修饰

抽象类能使用 final 修饰吗?

不能

接口和抽象类有什么区别?

接口和抽象类分别用interface和abstract修饰,抽象类相关的参照11,12,13.

接口需要用interface替代class修饰,接口中的普通方法都是默认public和abstract修饰的(可以不写),同抽象类一样,接口中方法都需要暴露出去给实现类实现,所以也不能用static,private,final修饰。

实现类通过implement实现接口,实现方法具体实现。

接口中定义的变量都是public和static修饰的常量(可以不写修饰符),抽象类则和普通类一样。

关于接口中的default和static:

static和普通类中的static一样,都是静态方法,通过类名引用;

default方法默认public,可以被子类继承;如果子类同时实现两个接口中有同名default方法,则子类必须重写;如果子类继承的类和实现的接口也有同名方法,则优先继承实现类的方法

Java 中 IO 流分为几种?

BIO、NIO、AIO 有什么区别?

  1. 同步,就是我调用一个功能,该功能没有结束前,我死等结果。
  2. 异步,就是我调用一个功能,不需要知道该功能结果,该功能有结果后通知我(回调通知)。
  3. 阻塞,就是调用我(函数),我(函数)没有接收完数据或者没有得到结果之前,我不会返回。
  4. 非阻塞,就是调用我(函数),我(函数)立即返回,通过select通知调用者

BIO表示同步阻塞式IO,一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。

NIO表示同步非阻塞IO,一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。

AIO表示异步非阻塞IO,一个有效请求一个线程,客户端的I/O请求都是由操作系统先完成IO操作后再通知服务器应用来启动线程进行处理。

应用场景:

  • BIO适用于连接数目比较小且固定的架构,该方式对服务器资源要求比较高,JDK 1.4以前的唯一选择。
  • NIO适用于连接数目多且连接比较短(轻操作)的架构,如聊天服务器,编程复杂,JDK 1.4开始支持,如在Netty框架中使用。
  • AIO适用于连接数目多且连接比较长(重操作)的架构,如相册服务器,充分调用操作系统参与并发操作,编程复杂,JDK 1.7开始支持。

备注:在大多数场景下,不建议直接使用JDK的NIO类库(门槛很高),除非精通NIO编程或者有特殊的需求。在绝大多数的业务场景中,可以使用NIO框架Netty来进行NIO编程,其既可以作为客户端也可以作为服务端,且支持UDP和异步文件传输,功能非常强大。

Files的常用方法都有哪些?

Files. exists():检测文件路径是否存在。
Files. createFile():创建文件。
Files. createDirectory():创建文件夹。
Files. delete():删除一个文件或目录。
Files. copy():复制文件。
Files. move():移动文件。
Files. size():查看文件个数。
Files. read():读取文件。
Files. write():写入文件。

二、容器系列面试题

Java 容器都有哪些?

Collection 和 Collections 有什么区别?

Collection是接口,Collections是工具类。

Collection提供集合的统一规范,Collections提供了一些静态方法,提供排序,搜索等。

List、Set、Map 之间的区别是什么?

有序:一组元素添加进集合的顺序和在集合中的顺序是一样的。

无序:一组元素添加进集合后,会在集合中按照某种规则进行排序。

List和Set都实现了Collection接口,List提供有序的集合,Set提供无序的集合(LinkedHashSet按照插入排序,SortedSet可排序)。

List允许可重复数据,Set和Map不允许重复数据。

Map提供哈希表的顶层接口,key-value形式存储数据

HashMap 和 Hashtable 有什么区别?

Hashtable

  • 初始size为11,扩容:newsize = olesize*2+1
  • 方法用synchronized修饰,是线程安全
  • 不允许键或者值是null

HashMap

  • 初始size为16,扩容:newsize = oldsize*2,size一定为2的n次幂
  • 线程不安全
  • 允许键和值是null
  • 如果链表大小超过8,链表就会被改造为树形结构。

如何决定使用 HashMap 还是 TreeMap?

什么时候使用特定的map取决于他们各自的特点。TreeMap要求作为key的对象必须实现Comparable接口,重写compareTo方法,所以内部是按照key升序排列的,底层实现是红黑树;而HashMap存储数据是无序的,通过hash值和容器长度计算元素位置hash & (size - 1)。

所以需要排序的时候要用TreeMap,否则用HashMap。

说一下 HashMap 的实现原理?

首先是数据结构,HashMap用数组+链表/红黑树的方式进行存储数据。

当一个key-value进入map时,首先计算出key的hash值,然后通过hash值和容器长度-1进行与&运算,得到元素应该放的位置,如果对应的index位置上是空的,就直接放上去。如果已经有元素了,先看看key是不是相同,相同的话用新value替换old-value;如果key不相同往后追加链表节点,当链表节点超过8个,链表转换为红黑树。

HashMap中存储数据的主干是Entry数组,且长度默认是2的n次方,因为这样可以减少hash冲突(具体计算方式为hash & (size - 1))。扩容方法:先拿oldTable看下是否为空,为空说明map还没初始化,就初始化一个16大小的数组;否则扩容为原来2倍大小。具体看问题31.

说一下 HashSet 的实现原理?

HashSet用HashMap作为实际存储数据的容器,对于add进来的值作为key,然后用一个final修饰的Object对象存入HashMap中,set中几乎所有操作都是基于map的函数调用的。

说一下 ConcurrentHashMap 的实现原理?

首先底层实现:和HashMap一样,也是Node数组作为散列表,后面跟链表/红黑树。线程安全的原因是因为用CAS+synchronized关键字的方式处理。put的时候进行初始化,如果对应的散列表位置上个没有值就通过CAS方式放入,如果有就对该节点加synchronized锁,插入新节点;当长度为8时就变为红黑树,节点类型变为TreeBin。

参考关于Java中的ConcurrentHashMap的实现原理有大神可以详细介绍下吗? - 知乎

ArrayList 和 LinkedList 的区别是什么?

都是线程不安全的。ArrayList底层是动态数组,LinkedList是链表;因为数组支持通过下标直接访问数据,所以查询性能上ArrayList更佳;对于插入和删除操作,如果是对于尾部的插入,数组比链表更快,因为ArrayList不需要额外的移动元素,如果是对于随机数据的插入,因为链表的插入删除只需要改变指针指向,而数组需要重新移动数据的位置,所以LinkedList效率更高。

ArrayList 和 Vector 的区别是什么?

底层实现:都是数组。

线程安全:Vector 的方法都是同步的,线程安全,方法上都有synchronized;ArrayList 非线程安全,但性能比Vector好

扩容机制:默认初始化容量都是10。Vector 扩容默认是原来的2倍,可指定扩容的大小;ArrayList扩容是原来的1.5倍

Array 和 ArrayList 有何区别?

Array指的是数组,数组有的特点ArrayList都有。

Array可以包含基本类型和对象类型,ArrayList只能包含对象类型。

Array大小是固定的,ArrayList是动态扩容的(其实就是帮你new了一个新的复制老数据过去)

ArrayList提供了很多方法,也都是在数组的基础上进行的。

在 Queue 中 poll()和 remove()有什么区别?

Queue是一个FIFO的数据结构,在头删除,在尾插入。

也要看实现了Queue的子类是如何处理这些方法的,比如LinkedList由于是链表,所以不存在队列满的情况。

        add        增加一个元索                     如果队列已满,则抛出一个IIIegaISlabEepeplian异常
  remove   移除并返回队列头部的元素    如果队列为空,则抛出一个NoSuchElementException异常
  element  返回队列头部的元素             如果队列为空,则抛出一个NoSuchElementException异常
  offer       添加一个元素并返回true       如果队列已满,则返回false
  poll         移除并返问队列头部的元素    如果队列为空,则返回null
  peek       返回队列头部的元素             如果队列为空,则返回null

哪些集合类是线程安全的?

  • Vector
  • Stack
  • Hashtable
  • java.util.concurrent 包下所有的集合类 ArrayBlockingQueue、ConcurrentHashMap、ConcurrentLinkedQueue、ConcurrentLinkedDeque...

HashMap扩容如何实现的?

参考:Hashmap实现原理及扩容机制详解_lkforce的博客-CSDN博客_hashmap扩容

HashMap是如何进行扩容的? - 周文豪 - 博客园

大概就是扩容发生在两个时机:

一个是初始化的时候,如果没有指定容量,那么按照默认16的大小初始化,如果指定了,按照最近的2进制数向上转换。

另一个发生在put过程,如果添加完元素的大小超过了阈值,就进行resize()。

先判断旧数组的长度如果大于Integer.MAX_VALUE,就把阈值设置为Integer.MAX_VALUE,然后返回旧数组;如果旧数组长度扩大为原来2倍仍然小于Integer.MAX_VALUE且大于默认初识容量,则把阈值扩大为原来两倍。阈值设置完后,判断如果旧数组的阈值大于0,就把阈值赋值给新数组的容量;初始化的时候阈值为0,所以此处要进行初始化新数组的阈值(容量乘以负载因子)和容量。至此,新容量和新阈值确定完毕,下一步就是要移动数据。

如果旧数组不为空(就是说不是初始化 时候),从0遍历旧数组,取出来每一个位置上的node,判断如果不为空说明有数据。然后分为几种情况:第一种:node的next为空,就是说这个位置只有一个节点,没有链表,那就直接计算出该节点在新数组中的位置并放入。第二种:如果当前节点是红黑树,那就按照红黑树的方式插入(后续再说)。第三种:后面跟了链表就看图吧。这块需要结合HashMap线程不安全的地方一起看。

Iterator 怎么使用?有什么特点?

  • java.lang.Iterable 接口被 java.util.Collection 接口继承,java.util.Collection 接口的 iterator() 方法返回一个 Iterator 对象
  • next() 方法获得集合中的下一个元素
  • hasNext() 检查集合中是否还有元素
  • remove() 方法将迭代器新返回的元素删除
  • forEachRemaining(Consumer<? super E> action) 方法,遍历所有元素

说一下红黑树?

红黑树是一种自平衡的二叉查找树AVL,也就是说二叉查找树的特点它都有:左子树的节点值都小于当前节点值,右子树节点值都大于当前节点值,左右子树也都是二叉查找树。由于AVL在删除和插入时要进行复杂的平衡操作,所以红黑树应用更广泛。

红黑树特性:

1.每个节点非红即黑。

2.根节点总是黑色的。

3.如果节点是红色的,那么它的子节点必须是黑色的(反之不一定)。

4.每个叶子节点都是黑色的空节点(NIL节点)。

5.从根节点到叶节点或空节点的每条路径,必须包含相同数目的黑色节点(即相同的黑色高度)。

新增节点为N,父节点为P,叔父节点为U,祖父节点为G

怎么确保一个集合不能被修改?

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值