目录
5. ArrayList和Vector的区别 (带一下CopyOnWriteArrayList)
6. Array怎么转换为 ArrayList, ArrayList怎么转换为Array
8. HashMap和Hashtable的区别 (带一下ConcurrentHashMap)
10. HashMap的底层原理 (数据结构+put()流程+resize()扩容策略)
12. 什么是哈希碰撞/哈希冲突, 怎么解决哈希冲突, HashMap采用的是什么策略
13. HashMap为什么不直接使用key的hashCode()函数返回的哈希码作为落槽时的索引号, HashMap是怎么解决的呢
15. 为什么重写了equals()方法, 必须也要重写hashcode()方法
4、Exception和RuntimeException的区别
10、try-catch-finally语法中那一部分可以省略
12、try-catch-finally 中,如果 catch 中 return 了,finally 还会执行吗
三次握手刚刚好完成信息对等,再增加握手次数将没有意义
(包括图片,视频,音频)
1. Collection和Map是官方提供的集合容器的两大体系的顶层接口
2. Collection代表单元素集合体系
3. Map代表kv键值对集合体系
4. Collection体系继承了Iterable迭代器接口, 所有的子类都提供了迭代器的实现, Map体系没有
1. List, Set, Queue都是Collection体系下的子接口, 分别代表三个体系
2. List体系的特点是有序, 不唯一
3. Set体系的特点是无序, 唯一
4. Queue体系的特点是先入先出
1. 队列是一种FIFO (First In First Out) 先入先出的结构
2. 栈是一种FILO (First In Last Out) 先入后出的结构
Java集合体系中的LinkedList类可以实现队列和栈结构
在链表头部插入尾部取出或者尾部插入头部取出就是队列 (插入和取出在不同方向上进行)
在链表头部插入头部取出或者尾部插入尾部取出就是栈 (插入和取出在相同方向上进行)
1.Array是数组ArrayList是类
2.Array是定长的(需要手动扩容),ArrayList长度可变(使用过程中自动扩容)
3.ArrayList的底层是Array
1.底层数据结构实现︰ArrayList底层数据结构是动态数组,而 LinkedList的底层数据结构是双向链表
2.随机访问(即读)效率∶ArrayList比LinkedList在随机访问的时候效率要高,因为ArrayList底层是数组,可以通过索引号快速访问,LinkedList是通过二分查找法遍历链表节点进行查 找的
3.增加和删除效率∶在非首尾的增加和删除操作,LinkedList 要比 ArrayList 效率要高,因为ArrayList增删操作需要大量的前移或后移,这个过程中涉及到大量的赋值操作比较耗时间,LinkedList只需要修改节点对象的左右指针即可。
4.内存空间占用:LinkedList 比 ArrayList更占内存,因为 LinkedList的节点除了存储数据,还存储了两个引用,一个指向前一个元素,一个指向后一个元素。
5.综合来说,在需要频繁读取集合中的元素时,更推荐使用ArrayList,而在插入和删除操作较多时,更推荐使用 LinkedList。
1. 官方提供的数组工具类Arrays中提供了一个静态方法asList()可以把数组转换为List, 参数是数组, 返回值是List
2. ArrayList类中提供了toArray()成员方法, 可以把ArrayList转换为Array后进行返回
ConcurrentHashMap的key和value都不支持null
HashMap在JDK1.8之前是数组+链表, JDK1.8之后是数组+链表/红黑树
1. 根据key的hashCode计算出数组index
2. 落槽时
1. 如果数组中节点为null, 创建新的节点对象, 把k,v存储在节点对象中, 把节点对象存储在数组中
2. 如果数组的节点不为null, 判断节点的key与插入元素的key是否相等
1. 相等, 直接用新的k,v覆盖原节点中的k,v
2. 不相等, 判断此时节点是否为红黑树
1. 是红黑树, 创建红黑树节点对象存储k,v, 插入到红黑树中
2. 不是红黑树, 创建链表节点对象存储k,v, 插入到链表中, 判断链表长度是否大于阈值8
1. 大于阈值8, 链表转换为红黑树
3. 判断++size 是否大于阈值, 是就扩容
HashMap默认初始容量是16
resize()方法是在size大于阈值时或者初始化时,就调用resize方法进行扩容
每次扩容的时候始终是原数组长度的2倍, 即长度永远是2的n次方
扩容后节点对象的位置要么在原位置,要么偏移到两倍的位置
为了能让 HashMap 存取高效,尽量较少碰撞,也就是要尽量把数据分配均匀,每个链表/红黑树长度大致相同。这个实现就是把数据存到哪个链表/红黑树中的算法。
如果有两个不同字符串通过同样的哈希算法计算出来的哈希码是一样的,则称他们发生了哈希碰撞,哈希冲突,解决方法:
1. 开放地址法
2. 拉链法(链地址法)HashMap默认使用的就是这种
3. 再哈希法(选记)
4. 建立公共溢出区(选记)
这题也可以这样问”HashMap的底层是如何计算key落槽时的索引的”
HashMap的底层采用了key的hashcode()来计算数组的索引index
如果数组[index]为null说明key不存在, 直接落槽插入
如果数组[index]不为null说明该位置有key存在, 但不能一定说明已存在的key与要插入的key重复, 因为可能会发生哈希碰撞, 此时应该进一步用equals方法比较已存在的key与要插入的key是否相等。如果相等就说明一定是重复的, 应该覆盖, 如果不相等, 说明发生了哈希碰撞, 那么应该插入在链表中
重写equals方法的目的是为了不去比较两个对象的内存地址, 改为比较对象的内容, 如果一个类重写了equals, 没有重写hashcode, 就可能出现两个地址不同的对象equals比较相等, 但是hashcode比较不相等, 这样会违法HashMap的唯一性, 因此, 重写了equals方法必须也要重写hashcode方法, 且必须满足两个对象equals相等, hashcode也必须相等。
1. 异常是被调用方向调用方传递错误信息的一种途径, 当被调用、方的方法在执行过程中出现了问题, 可以通过抛出异常对象, 让调用方捕捉从而实现错误信息的传递。
2. 使用异常机制可以保证程序代码更加优雅,并提高程序健壮性。
3. 被调用方抛出的异常对象能清晰的向调用方反应what, where, why这3个问题:
异常类型反应了“什么”被抛出
异常堆栈跟踪反应了“在哪”抛出
异常信息反应了“为什么”会抛出
-=
PS: 这题的问法也可能是 "受检异常和不受检异常的区别是什么"
答: 受检异常指的是在编译时接受编译器的检查, 如果没有处理异常则不能通过编译, Exception类及其子类都属于受检异常, RuntimeException类除外
受检: 接受编译器的检查
Error错误(不受检) | Exception 编译时异常 (受检) | RuntimeException运行时异常 (不受检) |
内存溢出 OutOfMemoryError | 数据库异常 SQLException | 空指针异常 NullPointerException |
栈溢出 StackOverflowError | 类找不到异常 ClassNotFoundException | 类型转换异常 ClassCastException |
| IO流异常 IOException | 算术异常 ArithmeticException |
| 文件找不到异常 FileNotFoundException | 索引越界异常 IndexOutOfBoundsException |
|
| 输入不匹配异常 InputMismatchException |
|
| 数字转换异常 NumberFormatException |
空指针异常:NullpointerException 简称NPE
当指针是一个空指针,没有指定地址,所以在堆区中找不到空间地址,就会发生NPE
outofmemory
内存溢出异常
1)分配的少了:比如虚拟机本身可使用的内存(一般通过启动时的VM参数指定)太少。
2)应用用的太多,并且用完没释放,浪费了。此时就会造成内存泄露或者内存溢出。
(1)throws在签名处,后面跟的是异常类型
(2)throw在方法体内,作用是抛出,返回异常对象
reture的作用:方法返回,并且可以根据返回值类型返回相应的变量
throw的作用:也是方法返回,但是只能返回异常对象,也就是说throw后面跟的对象必须是throwable的子类创建的
try不可以省略
catch可以省略
finally可以省略
catch和finally不可以同时省略
try-catch: try中发生的异常被catch捕捉, 程序可以继续往后运行
try-finally: try中发生的异常没有被程序捕捉, 程序会立即退出, 但退出之前会执行finally
try-catch-finally: try中发生异常, 异常代码后面的代码不会执行, 直接进入catch, try中没有发生异常, 不会进入catch, 无论是否发生异常, finally都会执行
在try-catch-finally语法中, 无论执行try还是catch, 最终都会执行finally, 所有我们一般把在try中打开的资源放在finally中进行释放
finally会在return之前执行, 但是finally中无法改变返回值的结果
可以理解为执行return的时候, 先确定下了返回值, 但方法还没有出栈, 然后执行finally, 如果finally中改变了返回的变量的值, 也不会对最终返回值产生影响。
(1)、面向过程:面向过程编程思想不考虑封装、继承、多态这些事情,直接定义数据为静态变量,用静态函数去操作数据,基于过程编程。面向过程编程,代码不具有可复用性和可扩展性
(2)、面向对象:面向对象编程思想需要先抽象出实体的结构,并用类进行封装,用成员变量表达实体的属性,用成员方法封装堆实体属性的操作。提供构造方法构造对象,基于对象编程。面向对象编程,代码具有可复用性和可扩展性
1、封装
2、继承
3、多态
(1)、用成员变量来描述对象的属性,并用private进行私有化封装,不对外暴露对象的属性,防止外部对属性进行误操作
(2)、用成员方法来封装对属性的操作,并暴露给外部调用,典型的成员方法就是setter和getter
一个是提供给外部进行属性设置,一个是提供外部读取属性的值
1、继承是java中类与类之间的一种联系
2、继承的关键字是extends
3、发生继承的类称为子类,被继承的类叫做父类
4、Java不支持多继承,只能支持只继承,但能支持多级继承,一个类只能继承一个父 类,但是一个父类可以有多个子类
5、如果一个类没有显示的继承父类,则隐式继承Object类
6、子类可以继承父类的非私有(非private修饰)成员
7、父类的构造方法不能被子类继承,但可以被super()调用
8、父类的静态成员与继承无关
9、如果父类中有抽象方法,子类必须重写,除非子类也是抽象类,让子类的子类去重写(实现)
先有继承而后有多态,多态的反义词叫做单态
1、指针的多态(数组的多态,参数的多态,返回值的多态归根都是指针的多态)
(指针是引用类型的变量,指针可以是成员变量,静态变量,局部变量/参数/返回值)
如果指针是具体的子类类型,则指针是单态指针,只能指向具体的子类对象
如果指针是抽象的父类类型,则指针是多态指针,可以指向任意的子类对象
2、方法的多态(1、基于重载实现。2、基于重写
实现)
重载是编译时多态的体现
重写是运行时多态的体现
重载和重写都是方法多态的体现
重载(overload)是编译时多态的体现
1、重载发生在同一类中,java允许同一个类中多个方法同名存在,但是满足重载的要求
2、方法名同名,但是方法的参数列表不同(可以是参数个数不同,或者是参数类型不同)
3、静态方法、构造方法、成员方法都可以重载
4、不是面向对象的特征
5、与访问修饰符,返回值类型,抛出异常类型无关,只跟参数列表有关
重写(override)是运行时多态的体现
1、重写发生在子类和父类之间,子类重写父类的方法,其目的是为了:当使用 父类型指针调用子类方法的时候,可以无需做向下转型
2、子类方法与父类方法同名,
3、访问修饰符要大于等于父类方法
例如:不能子类是public,父类是private
4、参数的个数必须与父类保持一致,参数类型可以小于等于父类的参数类型
5、返回值类型可以小于等于父类方法的返回值
6、抛出的异常类型小于等于父类方法的异常类型
7、是面向对象的特征
1、抽象类和抽象方法都需要使用abstract关键字修饰
2、抽象方法必须出现在抽象类中
3、抽象类中可以有抽象方法。也可以没有
1、普通类可以实例化,也可以被继承
2、抽象类不可以实例化(实例化就是new 构造方法()创建对象),只能被继承
1、将子类对象/指针赋给父类指针的语法称为“向上转型”,隐式的
2、继承是向上转型的前提,向上转型的目的是为了实现多态的指针
3、向上指针的副作用:指针无法访问下级对象中的成员(除非发生重写)
1、将父类型指针赋给其子类类型的指针称为向下转型,需要显示(强制)的向下转换,通常伴随着instanceof做类型判断,否则可能会出现ClassCastException(类型转换异常)
2、指针向下转型为子类类型,就可以访问子类中特有的成员了(非重写的成员方法)
修饰符 |
| 当前类 | 同包 | 子类 | 其他包 |
private | 私有的 | 可见 | 不可见 | 不可见 | 不可见 |
(dafault)不写 | 同包可见 | 可见 | 可见 | 不可见 | 不可见 |
protected | 受保护的 | 可见 | 可见 | 可见 | 不可见 |
public | 公有的 | 可见 | 可见 | 可见 | 可见 |
private的特点是:只有本类可见
(default)的特点:只有同包可见
protected的特点:子类可见
public的特点:任何地方都可见
接口也是一种源代码文件
定义类的关键字是class
定义接口的关键字是interface
接口的出现是弥补了java也能够实现多继承,一个类只能继承一个父类,但能继承多个接口
子类继承父类,关键字是extends
子类实现接口,关键字是implements
相同点:
(1)都可以作为指针类型,实现多态指针
(2)都不可以实例化
不同点:
(1)抽象类用abstract class定义,接口用interface定义
(2)抽象类用extends继承,接口用implements实现
(3)类只能单继承,接口可以多继承(实现)
(4)虽然两者都不可以实例化,但抽象类可以有构造方法,接口不可以有构造 方法
(5)抽象类可以有抽象方法,但也可以有具体方法,接口只有抽象方法,而且接 口中所有的方法默认"public abstract"修饰的
(6)抽象类可以有成员变量,接口不能有成员变量,只能有静态常量
破坏任意一个条件即可
CPU执行时间片:时间非常短 (1ms)
阻塞的情况分两种: