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 为每个原始类型提供了包装类型:
原始类型 : boolean char byte short int long float double 包装类型: Boolean
Character Byte Short Integer Long Float , Double

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

相等

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

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

2023最新面试题-Java-4_第1张图片

 

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); // false

Integer a2 = 127;
Integer b2 = 127;
 System.out.println(a2 == b2); // true
}

集合容器概述

5. 集合和数组的区别

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

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

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

Map 接口和 Collection 接口是所有集合框架的父接口:
1. Collection 接口的子接口包括: Set 接口和 List 接口
2. Map 接口的实现类主要有: HashMap TreeMap Hashtable ConcurrentHashMap 以及
Properties
3. Set 接口的实现类主要有: HashSet TreeSet LinkedHashSet
4. List 接口的实现类主要有: ArrayList LinkedList Stack 以及 Vector

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

2023最新面试题-Java-4_第2张图片

 2023最新面试题-Java-4_第3张图片

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

 Collection集合主要有ListSet两大接口

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

 

Map 是一个键值对集合,存储键、值和之间的映射。 Key 无序,唯一; value 不 要求有序,允许重复。 Map没有继承于 Collection 接口,从 Map 集合中检索元 素时,只要给出键对象,就会返回对应的值对 象。
Map 的常用实现类: HashMap TreeMap HashTable LinkedHashMap 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 list = new ArrayList<>();
        list.add("x");
        Collection clist = Collections. unmodifiableCollection(list);
        // 运行时此行报错
        clist.add("y");
        System.out.println(list. size());

2023最新面试题-Java-4_第4张图片

 11 迭代器 Iterator 是什么?

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

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

Iterator 使用代码如下:

 List list = new ArrayList
2 Iterator 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 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. 迭代器遍历, Iterator Iterator 是面向对象的一个设计模式,目的是屏 蔽不同数据集合的特点,
统一遍历集合的接口。 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 list = new ArrayList();
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 的值存放于 HashMap key 上, HashMap value 统一为 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 queue = new LinkedList();
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. 当我们往 Hashmap put 元素时,利用 key hashCode 重新 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 锁进
行保护,相对于 HashTable synchronized 锁的粒度更精细了一些,并发性能更好,而 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 的锁。
2023最新面试题-Java-4_第5张图片

 

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 倍。
2023最新面试题-Java-4_第6张图片

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

 

你可能感兴趣的:(2023最新面试题整理,java)