Java基础
语言特性
1.Java语言的优点?
① 平台无关性,“一次编写,到处运行”。
② 相对安全的内存管理和访问机制,避免大部分内存泄漏和指针越界。
③ 热点代码检测和运行时编译及优化,使程序运行时间增长获得更高性能。
④ 完善的应用程序接口,支持第三方类库
2.Java如何实现与平台无关?
JVM: 我们所写的Java程序是.java
为后缀的源文件,通过编译成为.class
为后缀的字节码文件,字节码文件不仅可以轻易的在任何机器上解释执行,还可以动态的转换成本地机器代码,转换是由 JVM 实现的。不同的操作系统都有与之对应的 JVM ,所以只需要写一个Java程序,就可以在多个不同的操作系统上执行。这样就实现了Java的跨平台。
语言规范: 基本数据类型大小有明确规定。Java中数值类型有固定字节数,二进制数据以固定格式存储和传输,字符串采用标准的Unicode格式存储
3.JDK 和 JRE 的区别?
JDK: Java Development Kit, 开发工具包,提供了编译运行Java程序的各种工具,包括编译器,JRE及常用类库,是Java核心
JRE: Java Runtime Environment, 运行时环境, 运行Java程序的必要环境,包括JVM、核心类库、核心配置工具。
4.“==” 和 equals 的区别是什么?
** “==” 解读**
基本类型:“ = = ” 是比较两个对象的值是否相同
引用类型:“ = = ” 是比较两个对象的地址值是否相同
equals 解读
equals 本质上就是 ==,只不过String 和 Integer 等重写了equals方法,把他变成了值比较。
5. Java按值调用还是引用调用?
按值调用 指方法接收调用者提供的值,按引用调用 指方法接受调用者提供的变量地址
Java总是按值调用。方法得到的是所有参数值的副本,传递对象时候实际上方法接受的是对象引用的副本。方法不能修改基本数据类型的参数,如果传递了一个int值,改变值不会影响实参,因为改变的是值的一个副本
6.浅拷贝和深拷贝的区别?
浅拷贝: 只复制当前对象的基本数据类型以及引用变量,没有复制引用变量指向的实际对象,修改克隆对象可影响原对象,不安全
深拷贝: 完全拷贝基本数据类型和引用数据类型,安全。
7.两个对象的hashCode()相同,那么equals()也一定为true吗?
不对,两个对象的hashcode相同,即两个对象的键值对的哈希值相等,然而哈希值相等,并不一定能得出键值对相等。
8. final在Java中有什么作用?
final作为Java中的关键字可以用于三个地方。用于修饰类、类属性、和类方法。
特征: 凡是引用final关键字的地方皆不可修改!
① 修饰类: 表示该类不能被继承;
② 修饰方法: 表示该方法不能被重写;
③ 修饰变量: 表示变量只能一次赋值之后不能被修改(常量)。
9. String 属于基础的数据类型吗?
String是final修饰的java类,java中的基本类型一共有8个,分别是
byte、 short、 int 、long、 double、 float 、char、 boolean
10. 什么是反射?
在运行状态中,对于任意一个类都能知道它的所有属性和方法,对于任意一个对象都能够调用它的任意方法和属性,这种动态获取信息及调用对象方法的功能称为反射,缺点是破坏了封装性以及泛型约束。
11.Class类的作用? 如何获取一个Class对象?
在程序运行期间,Java运行时系统为所有对象维护一个运行时类型标识,这个信息会跟踪每个对象所属的类,虚拟机利用运行时类型信息选择要执行的正确方法,保存这些信息的类就是Class,这是一个泛型类。
获取Class对象:
- ①
类名.class
- ②对象的getClass方法
- ③Class.forName(类的全限定名)
12.Java中操作字符串都有哪些类?他们之间有什么区别?
操作字符串的类有: String、 StringBuffer、 StringBuilder。
String 是一个封装char[] 数组的对象,字符串不可变(被final修饰,是常量),声明的对象是不可变的对象,每次操作都会生成新的String对象,然后将指针指向新的String对象。而StringBuffer、StringBuilder可以在原有对象的基础上进行操作,所以经常改变字符串内容的情况下最好不要使用String
StringBuffer 和 StringBuilder最大的区别在于:
StringBuffer是线程安全的,而StringBuilder是非线程安全的,但SringBuilder的性能却高于StringBuffer,所以单线程环境下推荐使用StringBuilder,多线程环境下推荐使用StringBuffer。
13. String str = “i” 与 String str = new String(“i”)一样吗?
不一样,因为分配内存的分配方式不一样。String str = "i"的方式,Java悉尼及会将其分配到常量池中;而String str = new String(“i”)则会被分到堆内存中。
14. String 类的常用方法都有哪些?
indexOf():返回指定字符的索引。
charAt():返回指定索引处的字符。
replease():字符串替换
trim():去除字符串两端空白。
split():分割字符串,返回一个分割后的字符串数组。
getBytes():返回字符串的byte类型数组。
length():返回字符串长度
toLowerCase():将字符串转换成小写字母
toUpperCase():将字符串转换成大写字符
substring():截取字符串。
equals():字符串比较
15.抽象类必需要有抽象方法?
不需要,抽象类不一定有抽象方法
16.抽象类能使用final修饰吗?
不能,定义抽象类就是让其它类继承的,如果定义为final该类就不能被继承,这样彼此就会产生矛盾,多以不能使用final修饰抽象类。
17.抽象类和普通类的区别?
普通类不能包含抽象方法,抽象类可以包含抽象方法。
抽象类不能直接实例化,普通类可以直接实例化。
18.抽象类和接口有什么区别?
实现:抽象类的子类使用extends来继承;接口必须使用implements来实现接口。
构造函数:抽象类可以有构造函数;接口不能有。
实现数量:类可以实现很多个接口;但是只能继承一个抽象类。
访问修饰符:接口中的方法默认使用public修饰;抽象类中的方法可以是任意访问修饰符。
19.Java 中 IO 流分为 几种?
按功能来分:输入流(input)、输出流(output)。
按类型来分:字节流和字符流。
字节流和字符流的区别是:
字节流按8位传输,以字节为单位输入输出数据。
字符流按16位传输,以字符为单位输入输出数据。
20.BIO、NIO、AIO
BIO:Block IO 同步阻塞式IO,就是我们平常使用的传统IO,它的特点是模式简单使用方便,并发处理能力低。
NIO:Non IO 同步非阻塞IO,是传统IO的升级,客户端和服务端通过Channel(通道)通讯,实现了多路复用。
AIO:Asynchronous IO 是NIO的升级,也叫NIO2, 实现了异步非阻塞IO,异步IO的操作基于事件和回调机制。
21.Files的常用方法都有哪些?
Files.exists():检查文件路径是否存在
Files.createFile():创建文件
Files.createDirectory():创建文件夹
Files.delete():删除一个文件或目录
Files.copy():复制文件
Files.move():移动文件
Files.size():查看文件个数
Files.read():读取文件
Files.write():写入文件
22.什么是注解?什么是元注解?
注解是一种标记,使类或接口附加额外信息,帮助编译器和JVM完成一些特定功能,例@Override
标识一个方法是重写方法。
元注解 是自定义注解的注解
23.什么是泛型,有什么作用?
泛型 本质是参数化类型,解决不确定对象具体类型的问题。泛型在定义处只具备执行Object方法的能力。
泛型的好处:
① 类型安全,放置什么就出来什么,不存在ClassCastException。
② 提升可读性,编码阶段就显式知道泛型集合,泛型方法等处理的对象类型。
③ 代码重用,合并了同类型的处理代码。
24.泛型擦除是什么?
泛型用于编译阶段,编译后的字节码文件不包含泛型类型信息,因为虚拟机没有泛型类型对象,所有对象都属于普通类。
例如:定义List<Object>
或List<String>
,在编译后都会变成List。
定义一个泛型类型,会自动提供一个对应原始类型,类型变量会被擦除。如果没有限定类型就会替换为Object,如果有限定类型就会替换成第一个限定类型。
例如:<T extends A & B>
会使用A类型替换T。
25.JDK8新特性有哪些?
lambda 表达式: 允许把函数作为参数传递到方法,简化匿名内部类代码
函数式接口: 使用 @Functionalnterface
标识,有且仅有一个抽象方法,可被隐式转换为lambda表达式。
接口: 接口可以定义default
修饰的默认方法,降低了接口升级的复杂性,还可以定义静态方法。
注解: 引用重复注解机制,相同注解在同地方可以声明多次。注解作用范围也进行了扩展,可作用于局部变量,泛型,方法异常等。
类型推测: 加强了类型推测机制,使代码更加简洁。
Optional类: 处理空指针异常,提高代码的可读性。
Stream类: 引入函数式编程风格,提供了很多功能,使代码更简洁。
日期: 增强了日期和时间API,新的java.time 包主要包含了处理日期、时间、日期/时间、时区、时刻和时钟等操作。
JavaScript: 提供了一个新的JavaScript引擎,允许在JVM上运行特定JavaScript应用。
26.异常有哪些分类?
异常的顶级父类Throwable,分为Error和Exception。
- Error: 是Java运行时系统的内部错误和资源耗尽错误,这种异常程序无法处理
- Exception: 分为受检异常和非受检异常,受检异常需要在代码中显式处理,否则会报错,非受检异常是运行时异常,继承自RuntimeException。
- 受检异常:
① 无能为力型,如字段超长导致的SQLException
② 力所能及型,UnAuthorizedException(未授权异常)、FileNotFoundException、ClassNotFoundException、IOException等。 - 非受检异常:
① 可预测异常,例如:IndexOutOfBoundException、NullPointerException等,这类异常应该提前处理。
② 需捕捉异常,例如进行RPC调用时的远程服务超时,这类异常客户端必须显式处理
③ 可透出异常,指框架或系统产生的且会自行处理异常。将异常自动映射到合适的状态码。
- 受检异常:
数据类型
1.Java有哪些基本数据类型?
数据类型 | 内存大小 | 默认值 | 取值范围 |
---|---|---|---|
byte | 1B | (byte)0 | -128 ~ 127 |
short | 2B | (short)0 | -215 ~ 215-1 |
int | 4B | 0 | -231 ~ 231-1 |
long | 8B | 0L | -263 ~ 263-1 |
float | 4B | 0.0F | -3.4E+38 ~ +3.4E+38 |
double | 8B | 0.0D | -1.7E+308 ~ +1.7E+308 |
char | 英文1B、中文UTF-8占3B、GBK占2B | ‘\u0000’ | ‘\u0000’ ~ ‘\uFFFF’ |
boolean | 单个变量4B/数组1B | falsh | true、false |
JVM没有boolean赋值的专用字节码指令,boolean f = false
就是使用 ICONST_0 即常数 0 赋值。
单个boolean变量用int代替,boolean 数组汇编码成byte数组。
2.自动装箱、自动拆箱是什么?
每个基本数据类型都对应一个包装类,除了int和char对应Integer和Charactor外,其余基本数据类型的包装类都是首字母大写即可。
自动装箱: 将基本数据类型包装为一个包装类对象。例如像一个泛型为Integer的集合添加int元素。
Integer a = 5;
a是引用类型,引用了包装对象的地址。
编译器会完成对象的自动装箱:Integer a = Integer.valueOf(5);
自动拆箱: 将包装类对象转换为一个基本数据类型。例如将一个包装类对象赋值给一个基本数据类型的变量。
int i = a;
a 现在是包装类型, 没法给变量赋值,需要把5取出来。
编译器会完成对象的自动拆箱:int i = a.intValue();
比较两个包装类数值要用equals
,而不能使用 ==
。
3.String 是不可变类,为什么可以修改?
String 类和其存储数据的成员变量 value 字节数组都是final修饰的。对一个String对象的任何修改实际上都是创建一个新的String对象,再引用该对象,只是修改String变量引用的对象,没有修改原String对象的内容。
4.字符串拼接的方式有哪些?
① 直接用+
,底层用StringBuilder实现。只适用于小数量,如果在循环中使用,相当于不断船舰新的StringBuilder对象再转换成String对象,效率极差。
② 使用String 的 concat方法,该方法中使用Arrays.copyOf
创建一个新的字符组buf 并将当前字符串 value 数组的值拷贝到 buf 中, buf 长度 = 的当前字符串长度 + 拼接字符串长度。之后调用getChars
方法使用System.arraycopy
将拼接字符串的值也拷贝到buf数组,最后用buf最为构造参数new 一个新的String 对象返回。效率稍高于直接使用+
③ 使用StringBuffer或者StringBuilder,两者的append
方法都继承自AbstractStringBuilder,该方法首先使用Arrays.copyOf
确定性的字符数组容量,再调用getChars
方法使用System.arraycopy
将新的值追加到数组中,StringBuilder是JDK5引入的,效率高但线程不安全,StringBuilder使用synchronized
保证线程安全。
5.String a = “a” + new String(“b”)创建了几个对象?
常量和常量拼接仍是常量,结果在常量池,只要有变量参与拼接结果就是变量,存在堆内存中。
使用字面量时之创建一个常量池中的常量,使用new时如果常量池中没有该值就会在常量池中新创建,再在堆内存中创建一个对象引用常量池中常量,因此String a = "a" + new String("b")
会创建4个
对象。
常量池中的a 和 b ,堆中的b 和堆中的 ab。
面向对象
1.谈一谈你对面向对象的理解
面向过程是一种思想,意思是我们要做任何事情都需要亲力亲为,强调的是过程。面向对象也是一种编程思想,相对于面向过程,我们可以由原来的执行者变为指挥者,进而把生活中很多复杂的问题变得简单化。
面向过程让计算机有步骤地顺序做一件事,是过程化思维,使用面向过程语言开发大型项目,软件复用和维护存在很大问题,模块之间耦合严重,面向对象相对面向过程更适合解决规模较大的问题,可以拆解问题复杂度,对现实事物进行抽象并映射为开发对象,更接近人的思维。
假如开门这个动作,面向过程是open(Door door),动宾结构,door动作为操作对象的参数传入方法,方法内定义开门的具体步骤。
面向对象的方式首先会定义一个类Door,抽象出门的属性(如尺寸,颜色)和行为(open和close),主谓结构。
面向过程代码松散,强调流程化解决问题。面向对象强调高内聚,低耦合,先抽象模型定义共性行为,在解决实际问题。
2.面向对象的三大特征?
封装:
封装 是隐藏对象的属性和实现细节,仅仅对外提供公共的访问方式,比如类和方法
好处:①提高安全性 ②提高重用性
封装是对象功能内聚的表现形式,在抽象基础上决定信息是否公开及公开等级,核心问题是以什么方式暴露那些信息。主要任务是对属性、数据、敏感行为实现隐藏,对属性的访问和修改必须通过公共接口实现。封装使对象关系变得简单,降低了代码耦合度,方便维护。
继承:
继承 是已有的类中派生出新的类,新类能吸收已有类的数据属性和行为,并扩展新的能力。Java继承是用已存在的类的定义作为基础建立新类的技术。新类的定义可以增加新的数据或者新的功能,也可以使用父类的功能,但不能选择性的继承父类(超类/基类)。这种继承是的服用以前的代码非常容易,能大大的缩短开发的周期,减低开发费用。
特点:① 使用extends关键字表示继承关系。②Java只支持单继承。③继承可以传递(爷/父/子)。④父类的私有成员也会被继承,但由于私有限制访问,所以子类不能使用父类的额私有资源。
继承用来扩展整个类,子类可继承父类的部分属性和行为使模块具有复用性。继承是“ is - a ” 的关系。可使用里氏替换原则判断是否满足 “ is - a ” 的关系,即任何父类出现的地方子类都可以出现,如果父类直接使用子类引用来代替且可以正确编译并执行,输出结果符合子类场景预期,那么说明两个类符合里氏替换原则。
多态:
多态是指同一个实体同时具有多种形式,即同一个对象,在不同时刻,代表的对象不一样,指的是对象不一样,指的是对象的多种形态。
特点:①多态的前提:继承、要有方法的重写。②父类引用指向子类对象。③,编译看左边,运行看右边。
多态以封装和继承为基础,根据运行时对象实际类型使同一行行为具有不同表现形式。多态指在编译层面无法确定最终调用的方法体,在运行期由JVM动态绑定,调用合适的重写方法。由于重载属于静态绑定,本质上重载结果是完全不同的方法,因此多态一般专指重写。
3.重写和重载的区别?
重载: 重载指方法名相同,但参数类型个数不同,是行为水平方向不同实现。对编译器来说,方法名称和参数列表组成了一个唯一键,称为方法签名,JVM通过方法签名决定调用哪些重载方法。不管继承关系如何复杂,重载在编译时可以根据规则知道哪种目标方法,因此属于静态绑定。
JVM 在重载方法中选择合适方法的顺序:①精确匹配。②基本数据类型自动转换成更大表示范围。③自动拆箱与装箱。④子类向上转型。⑤可变参数。
重写: 指子类实现接口或继承父类时,保持方法签名完全相同,实现不同方法体,是行为垂直方向不同实现。
元空间有一个方法保存方法信息,如果子类重写了父类的方法,则方法表中的方法引用会指向子类实现,父类引用执行子类方法时无法调用子类存在而父类不存在的方法。
重写方法访问权限不能变小,返回类型和抛出的异常类型不能变大,必须加 @Override
4.类之间有哪些关系?
类关系 | 描述 | 权力强侧 | 举例 |
---|---|---|---|
继承 | 父子类之间的关系:is-a | 父类 | 小狗继承于动物 |
实现 | 接口和实现类之间的关系:can-do | 接口 | 小狗实现了狗叫接口 |
组合 | 比聚合更强的关系:contains-a | 整体 | 头是身体的一部分 |
聚合 | 暂时组装的关系:has-a | 组装方 | 小狗和绳子是暂时的聚合关系 |
依赖 | 一个类用到另一个:depends-a | 被依赖方 | 人养小狗,小狗依赖于人 |
关联 | 平等的使用关系:links-a | 平等 | 人使用消费卡,卡可以提取人的信息 |
5.Object类有哪些方法?
equals: 检测对象是否相等,默认使用 ==
比较对象引用,可以重写equals方法自定义比较规则。
equals方法规范:自反性、对称性、传递性、一致性、对于任何非空引用x.equals(null)
返回false。
hashcode: 散列码是由对象导出的一个整型值,没有规律,每个对象都有默认散列码,值由对象存储地址得出。字符串散列码由内容导出,值可能相同。为了在集合中正确使用,一般需要同时重写equals和hashcode,要求 equals 相同hashcode必须相同,hashcode 相同 equals 未必相同, 因此hashcode 是对象相等的必要不充分条件。
toString: 打印对象时的默认方法,如果没有重写,打印的是表示对象值的一个字符串。
clone: clone方法声明为protected,类只能通过该方法克隆它自己的对象,如果希望其他类也能调用该方法必须定义该方法为public。如果一个对象的类没有实现Cloneable 接口,该对象调用clone 方法会抛出一个CloneNotSupport异常。默认的clone方法时浅拷贝,一般重写clone 方法需要实现Cloneable 接口并指定访问修饰符为public。
finalize: 确定一个对象死亡至少要经过两次标记,如果对象在可达性分析后发现没有与GCRoots连接的引用链会被第一次标记,随后进行一次筛选,条件是对象是否有必要执行,对象会被放置在F-Queue队列,由一条低调度优先级的Finalizer线程去执行。虚拟机会触发该方法但不保证会结束,这是为了防止某个对象的finalize方法执行缓慢或发生死循环。只要对象在finalize方法中重新与引用链上的对象建立关联就会在第二次标记时被移出回收集合。由于运行代价高昂且无法保证调用顺序,在JDK9被标记为过时方法,并不适合释放资源。
getClass: 返回包含对象信息的类对象。
wait / notify / notiflyAll: 阻塞或欢喜持有该对象锁的线程。
6.内部类的作用是什么,有哪些分类?
内部类可对同一个包中其他类隐藏,内部类方法可以访问定义这个内部类的作用域中的数据,包括private数据。
内部类是一个编译器现象,与虚拟机无关。编译器会把内部类转换成常规的类文件,用$ 分割外部类名与内部类名,其中匿名内部类使用数字编号,虚拟机会对此一无所知。
静态内部类: 属于外部类,只加载一次。作用域仅在包内,可通过外部类名.内部类名
直接访问,类内只能访问外部类所有静态属性和方法。HashMap的Node节点,ReentrantLock 中的Sync类,ArrayList的SubList 都是静态内部类。内部类还可以定义内部类,如ThreadLocal静态内部类ThreadLocalMap中定义了内部类Entry。
成员内部类: 属于外部类的每个对象,随对象一起加载,不可以定义静态成员和方法,可访问外部类的所有内容。
局部内部类: 定义在方法内,不能声明访问修饰符,只能定义实例成员变量和实例方法,作用范围仅在声明类的代码块中。
匿名内部类: 只用一次的没有名字的类,可以简化代码,创建的对象类型相当于new的类的子类类型。用于实现监听和其他回调。
7.访问权限控制符有哪些?
访问权限控制符 | 本类 | 包内 | 包外子类 | 任何地方 |
---|---|---|---|---|
public | √ | √ | √ | √ |
protected | √ | √ | √ | × |
无 | √ | √ | × | × |
private | √ | × | × | × |
8.接口和抽象类的异同?
接口和抽象类对实体类进行更高层次的抽象,仅定义公共行为和特征
语法维度 | 抽象类 | 接口 |
---|---|---|
成员变量 | 无特殊要求 | 默认 public static final 常量 |
构造方法 | 有构造方法,不能实例化 | 没有构造方法,不能实例化 |
方法 | 抽象类可以没有抽象方法,但有抽象方法一定是抽象类。 | 默认pubic abstract,JDK8 支持默认/静态方法, JDK9 支持私有方法。 |
继承 | 单继承 | 多继承 |
9.接口和抽象类应该怎么选择?
抽象类体现 is-a 关系,接口体现 can-do 关系。与接口相比,抽象类通常是对同类事物相对具体的抽象。
抽象类是模板式设计,包含一组具体特征,例如某汽车、底盘、控制电路等是抽象出来的共同特征,但内饰、显示屏、座椅材质、可以根据不同级别配置存在不同实现。
接口是契约式设计,是开放的,定义了方法名、参数、返回值、抛出的异常类型,谁都可以实现它,但必须遵守接口的约定。例如所有车辆都必须实现刹车这种强制规范。
接口是顶级类,抽象类在接口下面的第二层,对接口进行了组合,然后实现部分接口。当纠结定义接口和抽象类时,推荐定义为接口,遵循接口隔离原则,安维度划分成多个接口,再利用抽象类去实现这些,方便后续的扩展和重构。
例如Plane和Bird都有fly方法,应把fly定义为接口,而不是抽象类的抽象方法再继承,因为除了fly行为Plane和Bird见很难再找到其他共同特征。
10.子类初始化的顺序
①父类静态代码块和静态变量。
②子类静态代码块和静态变量。
③父类普通代码块和普通变量。
④父类构造方法。
⑤子类普通代码块个普通变量
⑥子类构造方法。
集合
1.说说ArrayList
Arraylist: 使容量可变的非线程安全列表,使用数组实现,集合扩容时候会创建更大的数组,把原有数组复制到新数组。支持对元素的快速随机访问,但插入与删除速度很慢。Arraylist实现了RandomAccess标记接口,如果一个类实现了该接口,那么表示使用索引遍历比迭代器更快。
elementDate 是ArrayList的数据域,被transient修饰,序列化时会调用wruteObject写入流,反序列化时候调用readObject重新赋值到新对象的elementData。原因是elementDate容量通常大于实际存储元素的数量,所以只需发送真正有实际值的数组元素。
size 是当前实际大小,elementData大小 大于等于size。
modCount 记录了ArrayList结构性变化的次数,继承自AbstractList。所有涉及结构变化的方法都会增加该值。expectedModCount是迭代器初始化时记录相等的modCount值,每次访问新元素时候都会检查modCount和expectedModCount是否相等,不相等就会抛出异常,这种机制叫做fail-fast,所有集合都有这种机制。
2.说说LinkedList
LinkedList 本质是双向链表,与Arraylist相比插入和删除速度更快,但随机访问元素很慢。除继承AbstractList外还实现了Deque接口,这个接口具有队列和栈的性质。成员变量被transient修饰,原理和ArrayList类似。
LinkedList包含三个重要的成员:size、first和last。size是双线链表中结点的个数,first和last分别指向守卫节点的引用。
LinkedList的优点在于可以将零散的内存单元通过附加引用的方式关联起来,形成按链路顺序查找的线性结构,内存利用率较高。
3.Set有什么特点,有哪些实现?
Set 不允许元素重复且无序,常用实现有hashSet、LinkedHashSet 和 TreeSet。
HashSet 通过hashMap实现,HashMap的Key即HashSet存储的元素,所有Key都是用相同的Value,一个名为 PRESENT 的 Object 类型常量。使用Key保证元素唯一性,但不保证有序性。由于HashSet是HashMap实现的,因此线程不安全。
HashSet判断元素是否相同时候,对于包装类型直接按值比较。对于引用类型先比较hashCode是否相同,不同则代表不是同一个对象,相同则继续比较equals,都相同才是一个对象。
LinkedHashSet 继承自 HashSet,通过LinkedHashMap实现,使用双线链表维护元素插入顺序。
TreeSet 通过TreeMap实现的,添加元素到集合时按照比较规则将其插入合适的位置,保证插入后的集合仍然有序。
4.TreeMap 有什么特点?
TreeMap 基于红黑树实现,增删改查的平均水平和最差时间复杂度均为O(logn),最大特点是Key有序.key必须实现Comparable接口或提供的Comparator比较器,所以Key不允许为null。
HashMap依靠hashCode
和equals
去重,而TreeMap依靠Comparable或Comparator。TreeMap排序时,如果比较器不为空就会优先使用比较器的compare
方法,否则使用Key实现的Comparable的compareTo
方法,两者都不满足会抛出异常。
TreeMap通过put
和deleteEntry
实现增加和删除树节点。插入新节点的规则有三个:①