> 文章列表 > 2023最新面试题-Java-4

2023最新面试题-Java-4

2023最新面试题-Java-4

Date相关

1.  java8的Date相关API:

常用 api

1、 获取当前日期

LocalDate.now()

2、创建日期

LocalDate date = LocalDate.of(2020, 9, 21)

3、获取年份

date.getYear()//通过 TemporalField 接口的实现枚举类 ChronoField.YEAR 获取年份
date.get(ChronoField.YEAR)

4、获取月份

date.getMonth().getValue()//通过 TemporalField 接口的实现枚举类 ChronoField.MONTH_OF_YEAR 获取月份
date.get(ChronoField.MONTH_OF_YEAR)

5、获取日

date.getDayOfMonth()//通过 TemporalField 接口的实现枚举类 ChronoField.DAY_OF_MONTH 获取日
date.get(ChronoField.DAY_OF_MONTH)

6、获取周几

date.getDayOfWeek()

7、获取当前月多少天

date.lengthOfMonth()

8、获取当前年是否为闰年

date.isLeapYear()

9、当前时间

LocalTime nowTime = LocalTime.now()

10、创建时间

LocalTime.of(23, 59, 59)

11、获取时

nowTime.getHour()

12、获取分

nowTime.getMinute()

13、获取秒

nowTime.getSecond()

14、获取毫秒

nowTime.getLong(ChronoField.MILLI_OF_SECOND)

15、获取纳秒

nowTime.getNano()

16、创建日期时间对象

LocalDateTime.of(2020, 9, 21, 1, 2, 3);
LocalDateTime.of(date, nowTime);

17、获取当前日期时间对象

LocalDateTime.now()

18、通过 LocalDate 创建日期时间对象

date.atTime(1, 2, 3)

19、通过 LocalTime 创建日期时间对象

nowTime.atDate(date)

20、通过 LocalDateTime 获取 LocalDate 对象

LocalDateTime.now().toLocalDate()

21、通过 LocalDateTime 获取 LocalTime 对象

LocalDateTime.now().toLocalTime()

22、解析日期字符串

LocalDate.parse("2020-09-21")

23、解析时间字符串

LocalTime.parse("01:02:03")

24、解析日期时间字符串

LocalDateTime.parse("2020-09-21T01:02:03", DateTimeFormatter.ISO_LOCAL_DATE_TIME)

25、方便时间建模、机器处理的时间处理类 Instant,起始时间 1970-01-01 00:00:00

//起始时间 + 3 秒
Instant.ofEpochSecond(3)
//起始时间 + 3 秒 + 100 万纳秒
Instant.ofEpochSecond(3, 1_000_000_000)
//起始时间 + 3 秒 - 100 万纳秒
Instant.ofEpochSecond(3, -1_000_000_000))
//距离 1970-01-01 00:00:00 毫秒数
Instant.now().toEpochMilli()

26、Duration:LocalTime、LocalDateTime、Intant 的时间差处理

Duration.between(LocalTime.parse("01:02:03"), LocalTime.parse("02:03:04"))
Duration.between(LocalDateTime.parse("2020-09-21T01:02:03"), LocalDateTime.parse("2020-09-22T02:03:04"))
Duration.between(Instant.ofEpochMilli(1600623455080L), Instant.now())

27、日期时间,前、后、相等比较

//2020-09-21 在 2020-09-18 前?
LocalDate.parse("2020-09-21").isBefore(LocalDate.parse("2020-09-18"))
//01:02:03 在 02:03:04 后?
LocalTime.parse("01:02:03").isAfter(LocalTime.parse("02:03:04"))

28、修改日期、时间对象,返回副本

//修改日期返回副本
LocalDate.now().withYear(2019).withMonth(9).withDayOfMonth(9)
LocalDate date4Cal = LocalDate.now();
//加一周
date4Cal.plusWeeks(1)
//减两个月
date4Cal.minusMonths(2)
//减三年
date4Cal.minusYears(3)

29、格式化

//格式化当前日期
LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE)
//指定格式,格式化当前日期
LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"))
指定格式,格式化当前日期时间
//格式化当前日期时间
LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd  HH:mm:ss"))

30、解析

//日期解析
LocalDate.parse("2020-09-20")
//指定格式,日期解析
LocalDate.parse("2020/09/20", DateTimeFormatter.ofPattern("yyyy/MM/dd"))
//指定格式,日期时间解析
LocalDateTime.parse("2020/09/20 01:01:03", DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss"))

31、时区

//上海时区
ZoneId shanghaiZone = ZoneId.of("Asia/Shanghai");
//设置日期为上海时区
LocalDate.now().atStartOfDay(shanghaiZone)
//设置日期时间为上海时区
LocalDateTime.now().atZone(shanghaiZone)
//设置 Instant 为上海时区
Instant.now().atZone(shanghaiZone)

32、子午线时间差

//时间差减 1 小时
ZoneOffset offset = ZoneOffset.of("-01:00");
//设置时间差
OffsetDateTime.of(LocalDateTime.now(), offset)

 包装类相关

 2. 自动装箱与拆箱

  • 装箱:将基本类型用它们对应的引用类型包装起来;
  • 拆箱:将包装类型转换为基本数据类型;

3. int Integer 有什么区别

 Java 是一个近乎纯洁的面向对象编程语言,但是为了编程的方便还是引入了基本数据类型,但是为了能 够将这些基本数据类型当成对象操作,Java 为每一个基本数据类型都引入了对应的包装类型(wrapper class),int 的包装类就是 Integer,从 Java 5 开始引入了自动装箱/拆箱机制,使得二者可以相互转 换。

Java 为每个原始类型提供了包装类型:
原始类型: booleancharbyteshortintlongfloatdouble 包装类型:Boolean
CharacterByteShortIntegerLongFloat, Double

4. Integer a= 127 Integer b = 127相等吗

相等

对于对象引用类型:==比较的是对象的内存地址。
对于基本数据类型:==比较的是值。如果整型字面量的值在-128127之间,那么自动装箱时不会new 新的Integer 对象,而是直接引用常量池中的Integer对象,超过范围 a1==b1的结果是false

又维护一个缓存,共用一个对象 

 

public static void main(String[] args) {
Integer a = new Integer(3);
Integer b = 3; // 将3自动装箱成Integer类型
int c = 3;
System.out.println(a == b); // false 两个引用没有引用同一对象
System.out.println(a == c); // true a自动拆箱成int类型再和c比较
System.out.println(b == c); // true
Integer a1 = 128;
Integer b1 = 128;
System.out.println(a1 == b1); // falseInteger a2 = 127;
Integer b2 = 127;System.out.println(a2 == b2); // true
}

集合容器概述

5. 集合和数组的区别

  • 数组是固定长度的;集合可变长度的。
  • 数组可以存储基本数据类型,也可以存储引用数据类型;集合只能存 储引用数据类型。
  • 数组存储的元素必须是同一个数据类型;集合存储的对象可以是不同 数据类型。

 数据结构:就是容器中存储数据的方式。

6. 常用的集合类有哪些?

Map接口和Collection接口是所有集合框架的父接口:
1. Collection接口的子接口包括:Set接口和List接口
2. Map接口的实现类主要有:HashMapTreeMapHashtable ConcurrentHashMap以及
Properties
3. Set接口的实现类主要有:HashSetTreeSetLinkedHashSet
4. List接口的实现类主要有:ArrayListLinkedListStack以及Vector

7. ListSetMap三者的区别?ListSetMap 是否继 承自 Collection 接口?ListMapSet 三个接口存取 元素时,各有什么 特点?

 

Java 容器分为 Collection Map 两大类,Collection集合的子接口有Set ListQueue三种子接口。
我们比较常用的是SetListMap接口不是 collection的子接口。

 Collection集合主要有ListSet两大接口

  • List:一个有序(元素存入集合的顺序和取出的顺序一致)容器,元素可以重 复,可以插入多个 null元素,元素都有索引。常用的实现类有 ArrayListLinkedList Vector
  • Set:一个无序(存入和取出顺序有可能不一致)容器,不可以存储重复元素, 只允许存入一个 null元素,必须保证元素唯一性。Set 接口常用实现类是 HashSet LinkedHashSet 以及 TreeSet

 

Map是一个键值对集合,存储键、值和之间的映射。 Key无序,唯一;value 不 要求有序,允许重复。 Map没有继承于Collection接口,从Map集合中检索元 素时,只要给出键对象,就会返回对应的值对 象。
Map 的常用实现类:HashMapTreeMapHashTableLinkedHashMap ConcurrentHashMap

8. 集合框架底层数据结构

Collection

List
  • Arraylist Object数组
  • Vector Object数组
  • LinkedList: 双向循环链表

Set

  • HashSet(无序,唯一):基于 HashMap 实现的,底层采用 HashMap 来保存元素
  • LinkedHashSet LinkedHashSet 继承与 HashSet,并且其内部是通过 LinkedHashMap 来实现 的。有点类似于我们之前说的LinkedHashMap 其内部是基 于 Hashmap 实现一样,不过还是有一 点点区别的。
  • TreeSet(有序,唯一): 红黑树(自平衡的排序二叉树。)
  • HashMap JDK1.8之前HashMap由数组+链表组成的,数组是HashMap的主 体,链表则是主要 为了解决哈希冲突而存在的(拉链法解决冲突).JDK1.8以后 在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为8)时,将链表转 化为红黑树, 以减少搜索时间
  • LinkedHashMapLinkedHashMap 继承自 HashMap,所以它的底层仍然是 基于拉链式散列结 构即由数组和链表或红黑树组成。另外,LinkedHashMap 在上面 结构的基础上,增加了一条双向 链表,使得上面的结构可以保持键值对的插入顺序。 同时通过对链表进行相应的操作,实现了访问 顺序相关逻辑。
  • HashTable: 数组+链表组成的,数组是 HashMap 的主体,链表则是主要为 了解决哈希冲突而存 在的
  • TreeMap: 红黑树(自平衡的排序二叉树)

9.  Java集合的快速失败机制 “fail-fast”

 是java集合的一种错误检测机制,当多个线程对集合进行结构上的改变的操作 时,有可能会产生 fail-fast 机制。 

例如:假设存在两个线程(线程1、线程2),线程1通过Iterator遍历集合A中 的元素,在某个时候线 程2修改了集合A的结构(是结构上面的修改,而不是简 单的修改集合元素的内容),那么这个时候程序 就会抛出ConcurrentModificationException 异常,从而产生fail-fast机制。

 

原因:迭代器在遍历时直接访问集合中的内容,并且在遍历过程中使用一个 modCount 变量。集合在 被遍历期间如果内容发生变化,就会改变modCount 的值。每当迭代器使用hashNext()/next()遍历下一 个元素之前,都会检测 modCount变量是否为expectedmodCount值,是的话就返回遍历;否则抛出 异常,终止遍历。

解决办法:

1. 在遍历过程中,所有涉及到改变modCount值得地方全部加上 synchronized
2. 使用CopyOnWriteArrayList来替换ArrayList.

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

可以使用 Collections. unmodifiableCollection(Collection c) 方法来创建一个 只读集合,这样改变集合 的任何操作都会抛出 Java. lang. UnsupportedOperationException 异常。 示例代码如下:

 

        List<String> list = new ArrayList<>();list.add("x");Collection<String> clist = Collections. unmodifiableCollection(list);// 运行时此行报错clist.add("y");System.out.println(list. size());

 11 迭代器 Iterator 是什么?

Iterator 接口提供遍历任何 Collection 的接口。我们可以从一个 Collection 中使用迭代器方法来获取迭 代器实例。迭代器取代了 Java 集合框架中的 Enumeration,迭代器允许调用者在迭代过程中移除元 素。

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

Iterator 使用代码如下:

 List<String> list = new ArrayList
2 Iterator<String> it = list. iterator
3 while(it. hasNext()){
4 String obj = it. next();
5 System. out. println(obj);
6 }
Iterator 的特点是只能单向遍历,但是更加安全,因为它可以确保,在当前遍历
的集合元素被更改的时候,就会抛出 ConcurrentModificationException 异常。

13. 如何边遍历边移除 Collection 中的元素?

边遍历边修改 Collection 的唯一正确方式是使用 Iterator.remove() 方法,如下:
1 Iterator<Integer> it = list.iterator();
2 while(it.hasNext()){
3 *// do something*
4 it.remove();5 }
一种 常见的错误代码如下:
1 for(Integer i : list){
2 list.remove(i)
3 }

14. 遍历方式有以下几种:

1. for 循环遍历,基于计数器。在集合外部维护一个计数器,然后依次读 取每一个位置的元素,当读
取到后一个元素后停止。
2. 迭代器遍历,IteratorIterator 是面向对象的一个设计模式,目的是屏 蔽不同数据集合的特点,
统一遍历集合的接口。Java Collections 中支 持了 Iterator 模式。
3. foreach 循环遍历。foreach 内部也是采用了 Iterator 的方式实现,使 用时不需要显式声明
Iterator 或计数器。优点是代码简洁,不易出错;缺 点是只能做简单的遍历,不能在遍历过程中操
作数据集合,例如删除、替 换
4. java8的Stream
最佳实践:Java Collections 框架中提供了一个 RandomAccess 接口,用来标 记 List 实现是否支持 Random Access。
  • 如果一个数据集合实现了该接口,就意味着它支持 Random Access,按位置读 取元素的平均时间 复杂度为 O(1),如ArrayList
  • 如果没有实现该接口,表示不支持 Random Access,如LinkedList。 推荐的做法就是,支持 Random Access 的列表可用 for 循环遍历,否则建议 用 Iterator foreach 遍历。

 

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

数组转 List:使用 Arrays. asList(array) 进行转换。
List 转数组:使用 List 自带的 toArray() 方法。
代码示例:
1 // list to array
2 List<String> list = new ArrayList<String>();
3 list.add("123");
4 list.add("456");
5 list.toArray();
6
7 // array to list
8 String[] array = new String[]{"123","456"};
9 Arrays.asList(array);

16. ArrayList LinkedList 的区别是什么?

  • 数据结构实现:ArrayList 是动态数组的数据结构实现,而 LinkedList 是双向链表的数据结构实 现。
  • 随机访问效率:ArrayList LinkedList 在随机访问的时候效率要高,因为 LinkedList 是线性的数 据存储方式,所以需要移动指针从前往后依次查找。
  • 增加和删除效率:在非首尾的增加和删除操作,LinkedList 要比 ArrayList 效率要高,因为 ArrayList 增删操作要影响数组内的其他数据的下标。
  • 内存空间占用:LinkedList ArrayList 更占内存,因为 LinkedList 的节点除了存储数据,还存储 了两个引用,一个指向前一个元素,一个指向后一个元素。
  • 线程安全:ArrayList LinkedList 都是不同步的,也就是不保证线程安全;
综合来说,在需要频繁读取集合中的元素时,更推荐使用 ArrayList,而在插入和删除操作较多时,更推 荐使用 LinkedList
补充:数据结构基础之双向链表
双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前 驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点。

17. ArrayList Vector 的区别是什么?

这两个类都实现了 List 接口(List 接口继承了 Collection 接口),他们都是有序集合
线程安全:Vector 使用了 Synchronized 来实现线程同步,是线程安全的,而ArrayList 是非线程安全的。
1. 性能:ArrayList 在性能方面要优于 Vector
2. 扩容:ArrayList Vector 都会根据实际的需要动态的调整容量,只不过在
Vector 扩容每次会增加 1 倍,而 ArrayList 只会增加 50%
3. 线程安全:
Vector类的所有方法都是同步的。可以由两个线程安全地访问一个Vector对 象、但是一个线程访问Vector的话代码要在同步操作上耗费大量的时间。 Arraylist不是同步的,所以在不需要保证线程安全时时建议使用Arraylist

18. 说一下 HashSet 的实现原理?

HashSet 是基于 HashMap 实现的,HashSet的值存放于HashMapkey上,HashMapvalue统一为 PRESENT,因此 HashSet 的实现比较简单,相关 HashSet 的操作,基本上都是直接调用底层 HashMap 的相关方法来完成,HashSet 不允许重复的值。 (HashMap的key不允许重复)

19. Queue 了解吗?

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

1. ArrayDeque, (数组双端队列)
2. PriorityQueue, (优先级队列)
3. ConcurrentLinkedQueue, (基于链表的并发队列)
4. DelayQueue, (延期阻塞队列)(阻塞队列实现了BlockingQueue接口)
5. ArrayBlockingQueue, (基于数组的并发阻塞队列)
6. LinkedBlockingQueue, (基于链表的FIFO阻塞队列)
7. LinkedBlockingDeque, (基于链表的FIFO双端阻塞队列)
8. PriorityBlockingQueue, (带优先级的无界阻塞队列)
9. SynchronousQueue (并发同步阻塞队列)

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

  • 相同点:都是返回第一个元素,并在队列中删除返回的对象。
  • 不同点:如果没有元素 poll()会返回 null,而 remove()会直接抛出 NoSuchElementException 常。

1 Queue<String> queue = new LinkedList<String>();
2 queue. offer("string"); // add
3 System. out. println(queue. poll());
4 System. out. println(queue. remove());
5 System. out. println(queue. size());

Map接口

 21、说一下 HashMap 的实现原理?

HashMap的数据结构: 在Java编程语言中, 基本的结构就是两种,一个是数组,另外一个是模拟指针 (引用),所有的数据结构都可以用这两个基本结构来构造的,HashMap也不例外。HashMap实际上 是一个“链表散列的数据结构,即数组和链表的结合体。
HashMap 基于 Hash 算法实现的
1. 当我们往Hashmapput元素时,利用keyhashCode重新hash计算出当前对象的元素在数组中
的下标
2. 存储时,如果出现hash值相同的key,此时有两种情况。
(1)如果key相同,则覆盖原始值;
(2)如果key不同(出现冲突),则将当前的key-value 放入链表中
3. 获取时,直接找到hash值对应的下标,在进一步判断key是否相同,从而找到对应值。
4. 理解了以上过程就不难明白HashMap是如何解决hash冲突的问题,核心就是使用了数组的存储方 式,然后将冲突的key的对象放入链表中,一旦发现冲突就在链表中做进一步的对比。
需要注意Jdk 1.8中对HashMap的实现做了优化,当链表中的节点数据超过八个
之后,该链表会转为红黑树来提高查询效率,从原来的O(n)O(logn)。

22. HashMap 的长度为什么是2的幂次方

为了能让 HashMap 存取高效,尽量较少碰撞。

23. HashMap ConcurrentHashMap 的区别

1. ConcurrentHashMap对整个桶数组进行了分割分段(Segment),然后在每一个分段上都用lock锁进
行保护,相对于HashTablesynchronized 锁的粒度更精细了一些,并发性能更好,而HashMap
没有锁机制,不是线程安全的。(JDK1.8之后ConcurrentHashMap启了一种全新的方式实现,利用
CAS算法。)
2. HashMap的键值对允许有null,但是ConCurrentHashMap都不允许。
1.7 (默认分配16 个Segment,比Hashtable效率提高16 倍。)
1.8 摒弃了Segment的概念,而是直接用Node 数组+链表+红黑树的数据结构来实现,并发控制使用synchronized CAS 来操作。

24. ConcurrentHashMap 底层具体实现知道吗?实现原理是什么?

JDK1.7
首先将数据分为一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据 时,其他段的数据也能被其他线程访问。
JDK1.7中,ConcurrentHashMap采用Segment + HashEntry的方式进行实现,结构如下:
一个 ConcurrentHashMap 里包含一个 Segment 数组。Segment 的结构和 HashMap类似,是一种数 组和链表结构,一个 Segment 包含一个 HashEntry 数组,每个 HashEntry 是一个链表结构的元素,每 个 Segment 守护着一个 HashEntry数组里的元素,当对 HashEntry 数组的数据进行修改时,必须首先 获得对应的 Segment的锁。

 

1. 该类包含两个静态内部类 HashE(img)ntry Segment ;前者用来封装映射表的键值对,后者用来
充当锁的角色;
2. Segment 是一种可重入的锁 ReentrantLock,每个 Segment 守护一个HashEntry 数组里得元素,
当对 HashEntry 数组的数据进行修改时,必须首先获得对应的 Segment 锁。

 JDK1.8

数据结构:数组+链表+红黑树  线程安全:synchronized+CAS

JDK1.8中,放弃了Segment臃肿的设计,取而代之的是采用Node + CAS + Synchronized来保证并发 安全进行实现,synchronized只锁定当前链表或红黑二叉树的首节点,这样只要hash不冲突,就不会产 生并发,效率又提升N 倍。

参考这篇文章看源码:Java8 ConcurrentHashMap实现原理_小波同学的技术博客_51CTO博客