Java基础面试题与答案

x33g5p2x  于2021-11-21 转载在 Java  
字(5.8k)|赞(0)|评价(0)|浏览(273)

八种基本数据类型以及包装类

八种基本数据类型默认值?大小?范围区间?包装类的缓存区间?

序号类型名称默认值大小最小值最大值包装类缓冲区间
1booleanfalse1B0(false)1(true)Boolean
2byte(byte)01B-128127Byte-128 ~ 127
3char‘\u0000’2B‘\u0000’‘\uFFFF’Character(char)0 ~ (char)127
4short(short)02B-2^152^15 - 1Short-128 ~ 127
5int04B-2^312^31 - 1Integer-128 ~ 127
6long0L8B-2^632^63 - 1Long-128 ~ 127
7float0.0f4B1.4e-45 (1.4 * 10^-45)3.4e+38 (3.4 * 10^38)Float
8double0.0d8B4.9e-324(4.9 * 10^-324)1.798e+308(1.798 * 10^308)Double

转换关系是什么?

  • 从左到右自动转换,从右到左需要强制转换;

包装类有哪些共性?

  1. 具有基本数值作为参数的构造函数。例如:Integer i = new Integer(66);
  2. 具有字符串作为参数的构造函数。例如Integer i = new Integer("-12");如果字符串内容与当前包装类型不匹配,会抛出NumberFormatException异常,例如Integer i = new Integer("-12.5");
  3. 具有返回对象基本值的typeValue方法,例如int num = i.intValue();
  4. 具有将字符串转换为基本值的parseType方法,例如int num = Integer.parseInt(“56”);
  5. 所有包装类都覆写了hashCode与equals方法,以提供对Map的支持;
  6. equals方法用于比较同一类型两个对象的值是否相等,不要使用"==";
  7. 具有将值转成字符串的方法toString()方法;
  8. 都支持自动装箱与拆箱,大大方便了基本数据类型与它们包装类的使用。

核心类之Object

Object类有哪些核心方法?

  • getClass()、hashcode()、equals()、clone()、toString()、notify()、notifyAll()、wait()、finalize() 等。

equal() 与 "=="的区别?

  • equal被重写前无区别。

clone()如何使用?深clone与浅clone的区别?

  • 想要实现clone,需要实现Cloneable接口,并重写Object的clone()方法;

  • 浅clone:对基本数据类型进行值传递,对引用数据类型进行引用传递般的拷贝,此为浅拷贝;

  • 深clone:对基本数据类型进行值传递,对引用数据类型可以通过以下方式实现:

  • 如果所有对象都支持clone,可以在clone方法内再一次clone引用的对象,然后set进去;

  • 通过字节流反序列化实现;

  • 通过序列化工具实现,如apache.commons.lang3.SerializationUtils或Gson。

finalize()方法什么时候被调用?

  • 对象被GC回收前。

String

String的底层数据结构是什么?

  • 不可变的字符数组。

String定义为final的原因?

  • 作为与基本数据类型有着相同地位的数据结构,不可变性是定义常量的基础,因此JVM中才可以定义字符串常量池,以此来提升使用效率与安全性。

String为什么非常适合作为HashMap的key?

  • String重写了Object类的hashCode()与equals()方法,这两个方法都是根据字符串的内容去计算和比较,而非内存地址;
  • hash值在首次计算后会被缓存,性能良好,这也是String能广泛使用于Map的key的原因。

集合体系架构图

HashMap

底层数据结构是什么?

  • 数组 + 链表 + 红黑树

put一个元素的全过程?

  • 初始化 -> hashCode寻址 -> equals比较 -> 替换或追加 -> 扩容

get一个元素的全过程?

  • hashCode寻址 -> equals确认

初始容量是什么?什么时候转红黑树?什么时候扩容?如何扩容?

  • 初始容量16;
  • 链表长度 >= 8,且总容量 >= 64 转红黑树;
  • 扩容因子0.75;
  • 扩大2倍;

为什么每次扩容2倍而不是3倍?

  • 容量n为2的指数倍可以保证元素在数组中分布的更均匀:(n - 1)% hashcode 等价于二进制运算 1111 & 0101

可不可以不扩容,会有什么问题?

  • 不扩容会导致hash冲突越来越多,使用效率逐渐下降。

多线程put时会出现哪些问题?

  • 同一个槽下的元素可能被覆盖;
  • 扩容时,原put到旧table的元素会丢失;
  • jdk1.7在对链表追加元素时,使用头插法,扩容后,链表元素顺序会发生反转,多线程同时触发扩容会导致死链;
  • jdk1.8在扩容时,将链表的头插法改为尾插法,保证链表的顺序不会改变,避免了死链发生。

红黑树的特性要求是什么?

  • 每个节点要么是红色,要么是黑色,但根节点永远是黑色的;
  • 红色节点不能连续,也就是说,红色节点的子和父都不能是红色的;
  • 从任一节点到其每个叶子节点的路径都包含相同数量的黑色节点。

ConcurrentHashMap

底层数据结构是什么?

  • 数组 + 链表 + 红黑树

如果保证线程安全?

  • 在put和resize时加乐观锁cas。

高并发下扩容原理是什么?

  • 在put过程中,如果发现table处于迁移状态,当前线程会协助迁移,将老数据都迁移到新的table中后再执行put过程。

高并发下size值如何计算?

  • 并发量不大的情况下直接通过cas更新baseCount,并发量很大的情况下采用分段计算方式,把新增加的值落到counterCells[]数组来减少冲突,counterCells满了还会扩容,最后 size = baseCount + counterCells。

ArrayList

底层数据结构是什么?

  • 初始大小为10的数组。

如果在中间位置add或remove一个元素会发生什么?

  • 导致该位置后的元素都需要进行位移,从而导致内存拷贝。

什么时候扩容,扩容多少?

  • 数组满的时候,扩容1.5倍。

适合哪种场景使用?

  • 数组内存连续,查询效率较高O(1);
  • 而修改可能会导致元素位移,内存copy;
  • 因此适合查询多,修改少(特别是在中间位置修改)的场景。

LinkedList

底层数据结构是什么?

  • 双向链表。

如何add一个元素?

  • 先找到要add的位置,再将前后节点的引用指向该节点,同时更新该节点的前序后续节点的引用。

描述get一个元素的过程

  • 如果知道位置,则根据总量判断该节点距离头近还是尾近,然后从近的一端去遍历。如果不知道位置则直接去遍历。

适合哪种场景使用?

  • 修改只修改引用,不涉及内存copy,因此代价较低;
  • 但是查询只能从头或从尾去遍历O(n);
  • 适合写多读少的场景。

LinkedHashMap

实现LRU

  • LinkedHashMap extends HashMap 具有和HashMap一样快的查找速度;
  • 内部维护一个双向链表,用来维护插入顺序或者LRU顺序;
  • 内部属性accessOrder决定了顺序,默认为false,此时维护的是插入顺序 ;
  • 构造函数 LinkedHashMap(int initialCapacity,float loadFactor,boolean accessOrder);
  • when accessOrder = true;
  • get操作会将该节点移到链表尾部,保证链表尾部是最近访问的节点,链表head节点就是最久未使用的节点;
  • put操作会将该节点移到链表尾部,保证链表尾部是最近访问的节点,链表head节点就是最久未使用的节点;
  • put操作后,when removeEldestEntry()方法返回为true会移除最晚的节点,就是head节点;
  • removeEldestEntry()默认为false,如果为true,必须继承LinkedHashMap重写这个方法;
  • 实例:实现LRU缓存,通过移除最近最久未使用的节点。从而保证缓存空间足够。并且缓存的数据都是热点数据。

泛型

泛型的作用和原理是什么?

  • 泛型可以消除代码中的强制类型转换,同时获得一个附加的类型检查层,该检查层可以防止有人将错误类型的键或值保存在集合中。这就是泛型所做的工作。
  • Java中的泛型基本上都是在编译器这个层次来实现的。在生成的Java字节码中是不包含泛型中的类型信息的。使用泛型的时候加上的类型参数,会在编译器在编译的时候去掉。这个过程就称为类型擦除。

使用泛型有什么好处?

  • 泛型的好处是在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,提高代码的重用率。

  • 类型安全:提高编译器验证的效率;

  • 消除强制类型转换:提高代码可读性,简化程序员开发难度;

  • 提升性能:将强制类型转换、安全校验等操作在编译器完成,简化了程序执行期的工作。

<? extends T>与<? super T>的区别是什么?

  • <? extends T>可以赋值给任何T以及T的子类的集合,上界为T,取出的类型带有泛型限制,向上转型为T,除了null以外,任何元素不得添加进<? extends T>集合内。

  • <? super T>可以赋值给任何T以及T的父类集合,下界为T,取出的类型会泛型丢失,添加时只能添加T或T的子类。

stream

stream的底层实现是怎样的?

  • Stream 处理数据的过程可以类别成工厂的流水线。数据可以看做流水线上的原料,对数据的操作可以看做流水线上的工人对原料的操作;

  • Stream 是一个接口,最主要的实现是ReferencePipeline;

  • ReferencePipeline 包含了控制数据流入的 Head ,中间操作 StatelessOp、StatefulOp,终止操作 TerminalOp;

  • Head节点定义了集合流入流水线的规范与方式;

  • StatelessOp是无状态操作,每个数据的处理是独立的,不会影响或依赖之前的数据,如filter()、map、flatMap();

  • StatefulOp是有状态操作,处理时会记录状态,比如处理了几个。后面元素的处理会依赖前面记录的状态,或者拿到所有元素才能继续下去,如distinct()、sorted()、limit();

  • TerminalOp是终止操作,其中又分为短路操作和非短路操作;

  • 短路操作:拿到符合预期的结果就会停下来,不一定会处理完所有数据。如anyMatch()、allMatch()、findFirst()、findAny() 等;

  • 非短路操作:处理完所有数据才能得到结果。如collect()、count()、forEach()、max()、min()、reduce()、toArray()等。

fruitList.stream().filter(a -> a.getName().equals("香蕉")).collect(Collectors.toList());

并行流是什么?

  • parallelStream

lambda实现List分组、过滤、去重、排序、转Map

https://hujinyang.blog.csdn.net/article/details/96432029

stream().map与stream().flatMap的区别?

  • map处理完数据后返回的是一个与原结构一致的stream;
  • flatMap把流中的层级结构扁平化,就是将最底层元素抽出来放到一起。

反射

反射的原理是什么?

  • 反射通过解析字节码,根据动态信息获取类、方法、属性等相关信息的技术。

反射为什么性能差?

  • java反射之所以慢,根本原因是JIT即时编译器没法对反射相关的代码做优化;
  • JIT是指在程序运行期间把字节码文件编译成机器码的过程,由于反射涉及动态解析的类型,没有具体的字节码,所以无法被JIT优化。

IO流

常见的IO流有哪些?

https://hujinyang.blog.csdn.net/article/details/103774297

Java Socket

Socket连接过程是什么?

https://hujinyang.blog.csdn.net/article/details/103775022

BIO与NIO

如何理解同步、异步、阻塞、非阻塞?

  • 同步强调的是顺序,所谓同步调用,就是可以确定程序执行的顺序的调用。比如说执行一个调用,知道调用返回之前下一行代码不会执行。这种顺序是确定的情况,就是同步;
  • 而异步调用则恰恰相反,异步调用不明确执行顺序。比如说一个回调函数,不知道何时会回来;
  • 阻塞和非阻塞是一种读取或者写入操作函数的实现方式,阻塞方式下读取或者写入函数将一直等待,而非阻塞方式下,读取或者写入函数会立即返回一个状态值;
  • 同步/异步是宏观上(进程间通讯,通常表现为网络IO的处理上),阻塞/非阻塞是微观上(进程内数据传输,通常表现为对本地IO的处理上);阻塞和非阻塞是同步/异步的表现形式。
  • 由上描述基本可以总结一句简短的话:同步和异步是针对目的和顺序,阻塞和非阻塞是针对实现方式。

什么是同步非阻塞?

  • 同步主要是针对client端获取结果的过程而言。一个线程从某条通道发送请求读取数据,但是它仅能得到目前可用的数据,如果目前没有可用数据,就什么都不会获取,它不会保持线程阻塞,可以暂时做其它事情,但是仍然需要时不时的去询问,看数据有没有就绪(虽然不用阻塞,但仍然要定时询问,而不是等人家送上门)。
  • 非阻塞是针对server端的处理过程而言。一个线程请求写入一些数据到某条通道,但不需要等待它完全写入,这个线程同时可以去做别的事情(多路复用)。 线程通常将非阻塞 IO 的空闲时间用于在其它通道上执行 IO 操作,所以一个单独的线程现在可以管理多个输入和输出的通道(channel)。
  • Java NIO 是一种同步非阻塞模式 IO。

BIO与NIO的区别是什么?

https://hujinyang.blog.csdn.net/article/details/103796894

NIO的核心概念有哪些?

https://hujinyang.blog.csdn.net/article/details/103789632

相关文章

微信公众号

最新文章

更多

目录