🗒️Java学习(上)

Agoinnnnnn

JavaSE|2024-3-23|Last edited: 2024-5-26|
type
status
date
slug
summary
tags
category
icon
password

继承

成员变量的访问特点

就近原则:谁离我近,我用谁
//现在局部位置找,再去本类变量找,再从父类变量找,逐级往上
局部变量直接用变量名调用 //name this调用变量名可以表示子类的变量 //this.name super调用变量名可以表示父类的变量 //super.name

成员方法的访问特点

直接调用满足就近原则:谁离我近,我用谁
super调用,直接访问父类

方法的重写:

当父类的方法不能满足子类现在的需求时,需要进行方法重写(在继承体系中,子类出现了和父类中一样的方法声明)
@override重写注解:@override是放在重写后的方法上,校验子类重写时的语法是否正确。

构造方法的访问特点

父类中的构造方法不会被子类继承
子类中的所有构造方法默认先访问父类中的无参构造,再执行自己的
可以通过super()来进行调用,第一行会有一个默认的super()
 
this,super使用总结: —this:可以理解为一个变量,来表示当前方法调用者的地址值。 —super:代表父类存储空间

多态

同类型的对象,表现出的不同形态
Student s = new Student(); Person p = new Person();

多态调用成员的特点

变量调用:编译看左边,运行也看左边
方法调用:编译看左边,运行看右边
Animal a = new Dog();
编译看左边:javac编译代码的时候,会看左边的父类有没有这个变量或方法,如果有,编译成功,如果没有就会报错 运行也看左边:Java在运行代码的时候,实际获取的就是左边父类中成员变量的值 运行看右边:Java在运行代码的时候,实际获取的就是右边子类中的方法

多态的优势

1.在多态形式下,右边对象可以实现解耦合,便于拓展和维护
2.定义方法的时候,使用父类类型作为参数,可以接受所有子类对象,体现多态的拓展性和便利

多态的弊端

不能调用子类的特有方法
解决方案: 将变量变为子类类型即可 比如: Animal a = new Dog(); Dog d = (Dog)a;
判断一个变量是否是某种类型:
新特性:
先判断a是否为Dog类型,如果是,则强转为Dog类型,转换后名为d
如果不是,则不强转,返回false

final关键字

(最终的→不能被改变的)
修饰方法:表明该方法是最终方法,不能被改写 修饰类:表明该类是最终类,不能被继承 修饰变量:表明该变量是常量,只能被赋值一次 //final修饰基本数据类型:记录的值不能发生改变 //final修饰引用数据类型:记录的地址值不能发生改变,内部的属性值是可以发生改变的

权限修饰符

是用来控制一个成员能够被访问的范围的,可以修饰成员变量,方法,构造方法,内部类
有四种(private<空着不写(默认)<protected<public)

代码块

局部代码块

方法内的局部代码,该块中定义的变量,出了该块之后就会被消除,以节省空间(现在已经不常用了)。

构造代码块

1.含义:写在成员位置的代码块
2.作用:可以把多个构造方法中重复的代码抽取出来
3.执行时机:我们在创建苯类对象的时候会先执行构造代码块再去执行构造方法
//渐渐淘汰

静态代码块

格式:static{}
需要通过static关键字修饰,随着类的加载而加载并且自动触发,只执行一次。
使用场景:在类加载的时候,需要初始化变量的时候使用。

抽象类和抽象方法

抽象方法:将共性的方法抽取到父类后,由于每一个子类执行的内容是不一样的。所以,在父类中不能确认具体的方法体,那我们就将该方法定义为抽象方法。
抽象类:如果一个类中存在抽象方法,那么该类就必须定义为抽象类。抽象类不能创建对象
抽象方法的定义格式:
public abstract 返回值类型 方法名(参数);//注意是不写方法体的
抽象类的定义格式:
public abstract class 类名{}

抽象类和抽象方法的意义:

强制子类必须按照特定方式重写,使代码统一。

接口

一种规则

接口的定义和使用

接口用关键字interface来定义
public interface 接口名{}
接口不能实例化:不能创建对象
接口和类之间是实现关系,通过implements关键字表示
public class 类名 implements 接口名{}

接口中成员的特点

1.成员变量:
只能是常量,默认修饰符:public static final
2.构造方法:
没有
3.成员方法:
只能写抽象方法

接口和类之间的关系

1.类和类之间的关系:继承关系,只能单继承不能多继承,但是可以多层继承
2.类和接口之间的关系:实现关系,可以单实现,也可以多实现,还可以在继承一个类的时候同时实现多个接口(需要重写接口里面所有的抽象方法)
3.接口和接口的关系:继承关系,可以单继承也可多继承

接口的扩展内容

jdk8开始接口中新增的方法

jdk7以前只能定义抽象方法,在jdk8以后就可以在接口中定义有方法体的方法。(默认,静态),在jdk9之后就可以定义私有方法了
默认方法:允许在接口中定义默认方法,需要用关键字default修饰。
作用:解决接口升级的问题(需要多定义方法的时候称为接口升级) 格式:public default 返回值类型 方法名(参数列表){}
注意事项:
1.默认方法不是抽象方法,所以不强制重写,如果要重写,则需要将default关键字去掉 2.public可以省略,default不能省略 3.如果实现了多个接口,且多个接口中存在相同名字的默认方法,子类必须对其进行重写
静态方法:允许在接口中定义静态方法,需要用关键字static修饰。
格式:public static 返回值类型 方法名(参数列表){}

内部类

在一个类里面,再定义一个类
内部类可以直接访问外部类的成员,包括私有 外部了想要访问内部类的成员,必须创建对象

内部类的分类

1.成员内部类
成员内部类可以被一些修饰符给修饰
获取内部类对象的两种方式:
(1)外部类编写方法,对外提供内部类对象(当内部类私有化时)
(2)直接创建
2.静态内部类
成员内部类被static关键字修饰。只能访问外部类中的静态变量和方法,如果想访问非静态则需要创建对象。
创建内部类对象的格式:
外部类名.内部类名 对象名 = new 外部类名.内部类名();
调用静态方法的格式:
外部类名.内部类名.方法名();
3.局部内部类
(1)将内部类定义在方法里面就叫做局部内部类
(2)外界是无法直接使用局部内部类的,需要在方法内部创建对象使用
(3)该类可以直接访问外部类的成员,也可以访问方法内的局部变量
4.匿名内部类 //重点
隐藏了名字的内部类,可以写在成员位置,也可以写在局部位置
格式:
new 类/接口(){
重写方法;
}
细节:
包含了继承或者实现,方法重写,创建对象 整体就是一个类的子类对象或者接口的实现类对象
使用场景:
当方法的参数时接口或者类的时候,以接口为例,可以传递这个接口的实现类对象,如果实现类只需要用到一次,那我们可以写匿名内部类来简化代码

事件

看看这个文章吧,讲了基本的事件定义,和动作,鼠标,键盘监听。

API

Math

math类(都是static修饰的方法,可以直接用类名点上方法名调用)的一些常用方法:
 

System

 

Runtime

注意事项:
1.静态方法只能通过接口名调用,不能通过实现类名或者对象名调用 2.public可以省略,static不能省略

jdk9新增的方法

格式:
private 返回值类型 方法名(参数列表){} //给默认方法服务 private static返回值类型 方法名(参数列表){} //给静态方法服务

接口的应用

1.接口代表规则,是行为的抽象。想要让哪个类拥有一个行为,就让这个类实现对应的接口就可以了
2.当一个方法的参数是接口时,可以传递接口所以实现类的对象,这种方式称作接口多态

适配器设计模式

1.当一个接口中抽象方法过多,但我只要使用其中的一部分的时候,就可以使用适配器设计模式
2.书写步骤:
编写中间类XXXAdapter,实现对应的接口 对接口中的抽象方法进行空实现 让真正的实现类继承中间类,并重写需要用到的抽象方法 为了避免其他类创建适配器类中的对象,适配器类需要用abstract进行修饰
 

Object

clone:
将一个对象的内容全部克隆到另外一个对象中,需要进行重写,还需要实现Cloneable接口。该接口中不含有方法,则说明该接口是一个标记性接口。如果Cloneable一旦被实现了就说明当前类的对象可以被克隆;如果没有实现,那就说明当前类的对象不能被克隆
浅克隆:不管对象内部的属性是基本数据类型还是引用数据类型,都是完全拷贝过去 深克隆:基本数据类型是完全拷贝,字符串是复用串池中的,而引用数据类型会重新创建一个新的地址来存储需要拷贝的数据
 

Objects

对象工具类
 

BigInteger

获取对象:
对象一旦创建不能再去改变
常见成员方法:去看api帮助文档吧

BigDecimal

用来表示很大的小数,用于小数的精确运算
与BigInteger的构造方法相似
重要方法:可以取小数点后几位和进位模式

正则表达式

可以校验字符串是否满足一定的规则,并用来校验数据格式的合法性
字符类:
  1. [abc]:代表a或者b,或者c字符中的一个。
  1. [^abc]:代表除a,b,c以外的任何字符。
  1. [a-z]:代表a-z的所有小写字符中的一个。
  1. [A-Z]:代表A-Z的所有大写字符中的一个。
  1. [0-9]:代表0-9之间的某一个数字字符。
  1. [a-zA-Z0-9]:代表a-z或者A-Z或者0-9之间的任意一个字符。
  1. [a-dm-p]:a 到 d 或 m 到 p之间的任意一个字符。
8.[a-z&&[def]]a-z和def的交集
预定义字符:
\ 表示转义字符
\\ 前面的\是转义字符,将后面的\转义成一个普通的\
数量词:
本地数据爬虫:
 
网络数据爬虫
 
按需求爬取:
贪婪爬取:在爬取数据的时候尽可能的多获得数据(Java中默认的就是贪婪爬取)
ab+或者ab*就是贪婪爬取
非贪婪爬取:在爬取数据的时候尽可能少的获取数据
ab+?或者ab*?就是非贪婪爬取

正则表达式在字符串方法中的应用

分组

从第一个左括号开始是第一组,以此类推,凡是遇到左括号就加一
捕获分组:
\\组号:表示把第X组的内容再拿出来用一次(在正则内部)
在正则外部需要用$组号来使用
非捕获分组:
不占用组号
(?:正则) //获取所有 (?=正则) //获取前面部分 (?!正则) //获取不是指定内容的部分

常用API

JDK7以前时间相关类

Date时间类:是一个jdk写好的JavaBean类,用来描述时间,精确到毫秒,利用空参构造创建的对象,默认表示系统当前时间,利用有参构造创建的对象,表示指定的时间
 
SimpleDateFormat类:
1.格式化时间
2.解析:把字符串表示的时间变成date对象
格式化的时间形式的常用模式为: y→年 M→月 d→日 H→小时 m→分钟 s→秒 例如:2023年11月11日 13:27:05 yyyy年MM月dd日 HH:mm:ss
其余的在api帮助文档有
Calendar类:
表示当前系统时间的日历对象,可以单独修改获取时间中的年,月,日(抽象类,不能直接创建对象)
月份的范围是0~11,星期中1索引表示的是星期天
 

JKD8新增的时间相关类

类名 作用
ZoneId
时区
Instant
时间戳
ZoneDateTime
带时区的时间
DateTimeFormatter
用于时间的格式化和解析
LocalDate
年、月、日
LocalTime
时、分、秒
LocalDateTime
年、月、日、时、分、秒
Duration
时间间隔(秒,纳,秒)
Period
时间间隔(年,月,日)
ChronoUnit
时间间隔(所有单位)
ZoneId:
 
Instant:
 
ZoneDateTime:

2024.4.18

DateTimeFormatter:
 
LocalDate(包含的是年月日):
 
LocalTime(包含的是时分秒)代码与上面基本一致
 
LocalDateTime(包含的是年月日时分秒)代码与上述基本一致
 
Period 时间间隔(年,月,日):
 
Duration 时间间隔(秒,纳,秒):
 
ChronoUnit 时间间隔(所有单位)//重点:
 

包装类

基本数据类型对应的引用数据类型
Integer类:
 
装箱与拆箱:
基本类型与对应的包装类对象之间,来回转换的过程称为”装箱“与”拆箱“:
  • 装箱:从基本类型转换为对应的包装类对象。
  • 拆箱:从包装类对象转换为对应的基本类型。
用Integer与 int为例:(看懂代码即可)
基本数值---->包装对象
包装对象---->基本数值
 
自动装箱与自动拆箱:
由于我们经常要做基本类型与包装类之间的转换,从Java 5(JDK 1.5)开始,基本类型与包装类的装箱、拆箱动作可以自动完成。例如:
 
成员方法:
static string tobinarystring(int i)
得到二进制
static string tooctalstring(int i)
得到八进制
static string toHexstring(int i)
得到十六进制
static int parseInt(string s)
将字符串类型的整数转成int类型的整数
 

常见算法

查找算法

1.基本查找:
也叫做顺序查找
说明:顺序查找适合于存储结构为数组或者链表。
基本思想:顺序查找也称为线形查找,属于无序查找算法。从数据结构线的一端开始,顺序扫描,依次将遍历到的结点与要查找的值相比较,若相等则表示查找成功;若遍历结束仍没有找到相同的,表示查找失败
 
2. 二分查找
说明:元素必须是有序的,从小到大,或者从大到小都是可以的。
如果是无序的,也可以先进行排序。但是排序之后,会改变原有数据的顺序,查找出来元素位置跟原来的元素可能是不一样的,所以排序之后再查找只能判断当前数据是否在容器当中,返回的索引无实际的意义。
基本思想:也称为是折半查找,属于有序查找算法。用给定值先与中间结点比较。比较完之后有三种情况:
  • 相等
    • 说明找到了
  • 要查找的数据比中间节点小
    • 说明要查找的数字在中间节点左边
  • 要查找的数据比中间节点大
    • 说明要查找的数字在中间节点右边
 
3. 插值查找
二分查找中查找点计算如下:
mid=(low+high)/2, 即mid=low+1/2*(high-low);
我们可以将查找的点改进为如下:
mid=low+(key-a[low])/(a[high]-a[low])*(high-low),
这样,让mid值的变化更靠近关键字key,这样也就间接地减少了比较次数。
基本思想:基于二分查找算法,将查找点的选择改进为自适应选择,可以提高查找效率。当然,差值查找也属于有序查找。
  • *细节:**对于表长较大,而关键字分布又比较均匀的查找表来说,插值查找算法的平均性能比折半查找要好的多。反之,数组中如果分布非常不均匀,那么插值查找未必是很合适的选择。
代码跟二分查找类似,只要修改一下mid的计算方式即可。
 
4. 斐波那契查找
在数学中有一个非常有名的数学规律:斐波那契数列:1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89…….
(从第三个数开始,后边每一个数都是前两个数的和)。
然后我们会发现,随着斐波那契数列的递增,前后两个数的比值会越来越接近0.618,利用这个特性,我们就可以将黄金比例运用到查找技术中。
notion image
基本思想:也是二分查找的一种提升算法,通过运用黄金比例的概念在数列中选择查找点进行查找,提高查找效率。同样地,斐波那契查找也属于一种有序查找算法。
斐波那契查找也是在二分查找的基础上进行了优化,优化中间点mid的计算方式即可
代码示例:
 

分块查找

当数据表中的数据元素很多时,可以采用分块查找。
汲取了顺序查找和折半查找各自的优点,既有动态结构,又适于快速查找
分块查找适用于数据较多,但是数据不会发生变化的情况,如果需要一边添加一边查找,建议使用哈希查找
分块查找的过程:
  1. 需要把数据分成N(总数的开根号)多小块,块与块之间不能有数据重复的交集。
  1. 给每一块创建对象单独存储到数组当中
  1. 查找数据的时候,先在数组查,当前数据属于哪一块
  1. 再到这一块中顺序查找

2024.4.20

排序算法

 
冒泡排序
 
选择排序
 
插入排序
 
快速排序
 

Arrays

里面的方法查找api帮助文档即可
其中sort方法的底层原理
利用插入排序和二分查找来进行排序 直接默认把0索引当作是有序序列,1索引到最后当作是无序序列 遍历无序序列的每一个元素,假设遍历到的元素是a元素 把a往有序序列里面插入,再插入的时候是通过二分查找的方法来找到插入点 拿着a元素和插入点的元素进行比较,比较的规则就是compare的方法体: 如果方法的返回值是负数,就再和前面的数进行比较 如果返回的值是正数,那就和后面的值进行比较 如果返回的值是0,那也和后面的数进行比较 知道能确定a的最终位置即可
 

lambda表达式

简化匿名内部类的书写格式,但不是所有匿名内部类都可以使用lambda表达式,它只能简化函数式接口的匿名内部类的写法(函数式接口:有且仅有一个抽象方法的接口叫做函数式接口,接口上方可以加上@FunctionalInterface注解)
函数式编程(Functional programming)是一种思想特点 忽略面向对象的复杂语法,强调做什么,而不是谁去做 →lambda表达式
格式:
还可以更省的规则(核心:只要可以推导出来的东西都可以省略):
1.如果参数类型都一致,那么可以不写参数类型 2.如果只有一个参数,,参数类型可以省略,()也可以省略 3.如果lambda表达式的方法体只有一行,那么大括号,分号,return都可以省略不写,但是必须同时省略不写
 

集合进阶

集合体系结构

单列(一次只能添加一个元素)集合顶层接口Collection
list系列集合:添加的元素是有序(指的是存入和取出的顺序相同),可重复,有索引 set系列集合:添加的元素是无序(指的是存入和取出的顺序不同),不重复,无索引
notion image
Collection的常用方法(他的功能是所有单列集合都可以使用的共性方法):
  • contains方法底层是依靠equals方法来进行比较的,默认比较的是对象,如果想比较对应的属性值,则需要进行重写,idea自定义重写的方法就直接比较的就是属性值
 

迭代器遍历

 

增强for遍历

  • 增强for的底层就是迭代器,是为了简化迭代器的代码而书写的
  • 所有的单列集合和数组才能用增强for进行遍历
 

lambda表达式遍历

 

List集合的特有方法

 

list独有的遍历方式:

列表迭代器(在遍历的时候需要添加元素的时候使用)
 

ArrayList

ArrayList集合的底层原理:
  • 利用空参创建的集合,在底层创建一个默认长度为0的数组(叫elementData)
  • 添加第一个元素的时候,底层会创建一个新的长度为10的数组
  • 当存满10时,会扩容为1.5倍(创建一个新的数组)
  • 如果一次添加多个元素,1.5倍放不下,则新数组的长度以实际长度为准
notion image
 

LinkedList

底层数据结构是双向链表,查询慢,增删快。由于操作首尾速度比较快,所有多了很多操作首尾的特有api
LinkedList集合的底层原理:
notion image
 

迭代器的底层源码

notion image

2024.4.21

泛型深入

泛型:可以在编译阶段约束操作的数据类型,并进行检查
格式:<数据类型>
注意:泛型只能支持引用数据类型。如果不写泛型,默认泛型是object
泛型类
当一个类中,某个变量的数据类型不确定时,就可以定义带有泛型的类
泛型方法
方法中形参类型不确定时,可以使用类名后面定义的泛型,也可以在方法申明上定义自己的泛型。
泛型接口
两种使用方式:
1.实现类给出具体的类型
2.实现类延续泛型,创建实现类时再确定泛型
 
泛型的通配符
?也表示不确定的数据类型 但是它可以进行类型的限定 ? extends E 表示可以传递E和E所有的子类 ? super E 表示可以传递E和E所有的父类
 

数据结构

一端开口,一端闭口。后进先出,先进后出。
 

队列

两端开口的容器,一端叫做前端,一端叫做后端,是先进先出,后进后出不过是从后端进入,前端出去。数据从后端进入队列叫做入队列,从前端出队列叫做出队列。
 

数组(array)

查询快,增删慢
 

链表(linked)

查询慢,增删快
单向链表:链表中每一个结点(独立的对象)都存储的是本身的内容和下一个结点的地址值,如果没有下一个结点就为空。
双向链表:每一个结点储存的是上一个结点的地址值,本身的值和下一个结点的地址值
 

  • 度:每一个节点的子节点个数
  • 二叉树:任意节点的度≤2
  • 树高:树的总层数
  • 根节点:最顶层的节点
  • 左子节点:左下方的节点
  • 右子节点:右下方的节点
  • 节点(独立的对象)的内部结构:父节点的地址值,当前节点的值,左子节点的地址值,右子节点的地址值
二叉查找树:
1.每个节点最多两个子节点 2.任意节点左子节点上的值都小于等于该节点 3.任意节点右子节点上的值都大于等于该节点
二叉查找树添加节点的规则:小的存左边,大的存右边,一样的不存。
二叉查找树查找节点的规则:先于根节点比较如果小于根节点就往左去找,如果大于根节点就往右去找,以此类推。
二叉树遍历方式:
  • 前序遍历:从根节点开始,按照当前节点,左子节点,右子节点的顺序遍历(是先遍历玩左子树才遍历右子树,与层序遍历分清楚)
  • 中序遍历:从最左边的子节点开始,按照左子节点,当前节点,右子节点的顺序遍历
  • 后序遍历:从最左边的子节点开始,按照左子节点,右子节点,当前节点的顺序遍历
  • 层序遍历:从根节点一层一层遍历
平衡二叉树:
任意节点的左右子树的高度差不超过1
平衡二叉树的旋转机制(当添加一个节点后不平衡则触发):
先确定支点:从添加的节点开始,不断地往父节点找不平衡的节点
左旋: 1.以不平衡的点作为支点,把支点左旋降级,变成左子节点,把原来的右子节点晋升。 2.以不平衡的点作为支点,把根节点的右侧往左拉,原来的右子节点变成新的父节点,并把多余的左子节点让出来,给已经降级的根节点当右子节点 右旋: 1.以不平衡的点作为支点,把支点右旋降级,变成右子节点,把原来的左子节点晋升。 2.以不平衡的点作为支点,把根节点的左侧往右拉,原来的左子节点变成新的父节点,并把多余的右子节点让出来,给已经降级的根节点当左子节点
平衡二叉树需要旋转的四种情况:
  • 左左:根节点左子树的左子树有节点插入,导致二叉树不平衡(一次右旋搞定)
  • 左右:根节点左子树的右子树有节点插入,导致二叉树不平衡(先局部左旋,在整体右旋)
  • 右右:根节点右子树的右子树有节点插入,导致二叉树不平衡(一次左旋搞定)
  • 右左:根节点右子树的左子树有节点插入,导致二叉树不平衡(先局部右旋,在整体左旋)
红黑树:
自平衡的二叉查找树,红黑树中的节点相对于二叉树的节点所储存的值会多出一个颜色的数据
是一个二叉查找树 但不是高度平衡的 添加的节点符合红黑规则
红黑规则:
  • 每一个节点或是红色的,或者是黑色的
  • 根节点必须是黑色
  • 如果一个节点没有子节点或者父节点,则该节点相应的指针属性值为Nil,这些Nil视为叶节点,每个叶节点(Nil)是黑色的
  • 如果某一个节点是红色,那么它的子节点必须是黑色(不能出现两个红色节点相连 的情况)
  • 对每一个节点,从该节点到其所有后代叶节点的简单路径上,均包含相同数目的黑色节点
红黑树添加节点的规则:
  • 添加的节点默认是红色的,红色的效率高
notion image
 

set系列集合

实现类:
  • HashSet:无序,不重复,无索引
  • LinkedHashSet:有序,不重复,无索引
  • TreeSet:可排序,不重复,无索引
 
HashSet底层原理
  • 采取哈希表(是一种对于增删改查数据性能比较好的一种数据结构)来存储数据,储存的索引值=(length-1)/哈希值
哈希表组成:jdk8以前:数组+链表 —— jdk8以后:数组+链表+红黑树
哈希值:对象的整数表现形式
  • 根据hashCode方法算出来的int类型的整数
  • 该方法定义在object类中,所有对象都可以调用,默认使用对象的地址值进行计算
  • 一般会重写hashCode方法,利用对象内部的属性值来计算哈希值
对象的哈希值特点:
  • 如果没有重写hashCode方法,不同对象所计算出来的哈希值是不同的
  • 如果重写了hashCode方法,不同对象但内部的属性值是相同的则计算出来的哈希值就是相同的
  • 在小部分情况下,不同属性值或者不同的地址值计算出来的哈希值也有可以一样(哈希碰撞)
notion image
如果数组存入的数据个数大于16*0.75(默认加载因子)=12时,数组长度就会扩容到原来的两倍。
当链表的长度大于8而且数组长度大于等于64时,此时的链表就会变成红黑树从而提高查找效率。
如果hashSet存储的是自定义对象,那么一定要重写hashCode方法和equals方法
 
LinkedHashSet
方法与他的上头collection一致
LinkedHashSet底层原理
底层数据结构依然是哈希表,只是每个元素多了一个双链表的机制来记录存储的顺序(有序的机制)(记录该元素上一个添加的元素的地址值和下一个添加的元素的地址值)
 
TreeSet
TreeSet的两种比较方式(默认使用第一种方式,如果第一种方式满足不了再去使用第二种,同时存在的话是以方式二为准)
  1. 默认排序/自然排序:JavaBean类实现Comparable接口,重写抽线方法,指定比较规则
2.比较器排序:创建TreeSet对象的时候去传递比较器comparator(接口)指定规则