八种基本数据类型以及包装类
八种基本数据类型默认值?大小?范围区间?包装类的缓存区间?
序号 | 类型名称 | 默认值 | 大小 | 最小值 | 最大值 | 包装类 | 缓冲区间 |
---|---|---|---|---|---|---|---|
1 | boolean | false | 1B | 0(false) | 1(true) | Boolean | 无 |
2 | byte | (byte)0 | 1B | -128 | 127 | Byte | -128 ~ 127 |
3 | char | ‘\u0000’ | 2B | ‘\u0000’ | ‘\uFFFF’ | Character | (char)0 ~ (char)127 |
4 | short | (short)0 | 2B | -2^15 | 2^15 - 1 | Short | -128 ~ 127 |
5 | int | 0 | 4B | -2^31 | 2^31 - 1 | Integer | -128 ~ 127 |
6 | long | 0L | 8B | -2^63 | 2^63 - 1 | Long | -128 ~ 127 |
7 | float | 0.0f | 4B | 1.4e-45 (1.4 * 10^-45) | 3.4e+38 (3.4 * 10^38) | Float | 无 |
8 | double | 0.0d | 8B | 4.9e-324(4.9 * 10^-324) | 1.798e+308(1.798 * 10^308) | Double | 无 |
转换关系是什么?
- 从左到右自动转换,从右到左需要强制转换;
包装类有哪些共性?
- 具有基本数值作为参数的构造函数。例如:Integer i = new Integer(66);
- 具有字符串作为参数的构造函数。例如Integer i = new Integer("-12");如果字符串内容与当前包装类型不匹配,会抛出NumberFormatException异常,例如Integer i = new Integer("-12.5");
- 具有返回对象基本值的typeValue方法,例如int num = i.intValue();
- 具有将字符串转换为基本值的parseType方法,例如int num = Integer.parseInt(“56”);
- 所有包装类都覆写了hashCode与equals方法,以提供对Map的支持;
- equals方法用于比较同一类型两个对象的值是否相等,不要使用"==";
- 具有将值转成字符串的方法toString()方法;
- 都支持自动装箱与拆箱,大大方便了基本数据类型与它们包装类的使用。
核心类之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