JavaSE
一、Java基础语法
1、基本常识
JRE = JVM+SE标准类库 JDK=JRE+开发工具集
java关键字:
2、常量与变量
简单理解什么是变量:
java中变量的命名规则:
满足标识符规则,单个单词 小写,多个单词 首单词小写 后续单词开头大写,命名尽量简单具有语言含义,命名长度无限制。
标识符命名规则: 字母+数字+_+$,首字母不能是数字,不能是java的关键字和保留字,严格区分大小写
java中类的命名规则:
每个单词 的 首字母大写
整型的字面值可以是二进制、十进制、八进制和十六进制。
浮点型字面值默认情况下表示double类型,也可以在值后加d或D,表示float类型,则需要在字面值后加f或F。
基本数据类型变量按作用范围分为 类级、对象实例级、方法级、块级 Java中内存分为 栈、堆、常量池等 当前主方法中局部变量是存储在栈当中的。
字符型字面值用 单引号(英文) 内的 单个字符 表示 整形可以赋值给字符型,此时整形值视为ASCII ASCII主要用于显示英文和西欧字母,使用7位或8位二进制组合来表示128或256种可能的字符(标准7位二进制 后128位扩展ASCII表示特殊字符)
unicode编码的目标是支持世界上所有的字符集 unicode表示法,在值前加前缀\u 范围 \u0000到 \uFFFF
Java中Boolean值只能是 true 和 false,字符串字面值是双引号引起来的 0个 或 多个 字符,其中空格也占一个字符
转义字符:
java中的自动类型转换:
java中常量名一般使用大写(单词间以下划线分割)
3、运算符
复合赋值运算符:
当字符串与基本数据类型进行加法运算时,实际上是在底层进行 字符串拼接运算
关系运算符 ‘A’>'B’比较的是两个字符的ASCII值 浮点型与整数进行比较,只要值相等就返回true。
如何从键盘输入数据:
import java.util.Scanner;
Scanner s=new Scanner(System.in);
int n=s.nextInt();
&& 和 || 又叫短路运算符 第一个表达式的值能决定结果时,右边的表达式就不再运算了
& “与”运算符的结果:
运算符的优先级:
4、流程结构
switch 表达式的值的类型:
switch中,case和其后的常量之间必须有一个空格。
while循环中 1. n的值必须先进行初始化 2.循环变量n的值必须被改变
do{}while; 循环 1. 至少执行一次 2. 循环条件后的分号不能丢
多重循环中,break语句只向外跳一层
continue只能用在循环里,可以结束当前循环的执行,但是依然进行下一次循环的执行
debug调试: 1.设置断点 2.打开debug模式 3.单步调试 F6是单步调试的快捷键
5、数组
数组是 相同类型 的数据 按顺序 组成的一种 引用数据类型
数组的声明: 数据类型[] 数组名; 数据类型 数组名[];
语法格式一:先声明后创建 数据类型[] 数组名; 数组名=new 数据类型[数组长度]
语法格式二:声明的同时创建数组 数据类型[] 数组名=new 数据类型[数组长度]; 注意:数组长度必须指定!
局部变量没有默认值,数组有默认值。 声明的同时给数组赋值,叫数组的初始化,如int[] arr={1,2,3,4,5}; 数组元素的引用: 数组名[下标]; 下标从零开始
增强型for循环: 又叫foreach循环
int[] arr={1,2,3,4,5};
for(int n:arr) {
System.out.println(n);
}
for(int n=0;n<a.length;n++)与for(int n:a)功能相同
数组 array 的长度一旦定义就不能再改变了。
6、方法
1)方法重载
方法重载:方法名相同;参数列表不同(顺序,个数,类型);方法返回值、访问修饰符任意 ;与方法的参数名无关(仅仅参数名不一样会报错)
为什么要方法重载? 有时候一个方法名,要用到很多次,而且每次跟每次的参数都不一样,而且这个方法名,特别适合某个业务(比如登录),这个时候你变成其他的方法名,对大家来讲都很别扭,这时候就用到重载的概念了。
同类中方法使用:参数的传递问题: 主方法中调用方法必须创建对象,同类中其他方法调用无需创建对象,直接使用名字即可
数组作为方法参数传值问题: 基本数据类型传值不会影响主方法中的值,引用数据类型的传值会影响主方法中的值。对象作为方法参数也会对对象内容进行影响,对象用类来声明的,本身也是一个引用的数据类型。
可变参数:参数的数量不确定,可以随时变化 例:public void sum(int… n){} 参数列表中有两个或以上参数,可变参数一定是放在最后的。
可以将数组的值传递给 可变参数列表,但数组作为参数时,不能将多个值传递给数组
二、Java面向对象
7、面向对象
当你拿到一个问题时,你分析这个问题不再是第一步先做什么,第二步再做什么,这是面向过程的思维,**你应该分析这个问题里面有哪些类和对象,这是第一点,然后再分析这些类和对象应该具有哪些属性和方法。这是第二点。最后分析类和类之间具体有什么关系,这是第三点。**面向对象有一个非常重要的设计思维:合适的方法应该出现在合适的类里面。
单一职责原则:建议一个类有且只有一个引起功能变化的原因。
包的推荐命名规范: 小写字母+域名反写
1)栈、堆、常量池、方法区
1、堆空间用于存储使用new关键字所创建的对象和数组,由JVM的自动垃圾回收器来管理。被所有线程共享。此外,创建对象和数组时会向栈中添加对象的引用变量,取值等于数组或者对象在堆内存中的首地址;
2、栈空间用于存储程序运行时在方法中声明的所有的局部变量、对象的引用变量;当超过变量的作用域后,java会自动释放掉为该变量所分配的内存空间。存在栈中的数据同一个线程可以共享;
假设我们同时定义 int a = 3; int b = 3; 编译器先处理int a = 3;首先它会在栈中创建一个变量为a的引用,然后查找有没有字面值为3的地址,没找到,就开辟一个存放3这个字面值的地址,然后将a指向3的地址。接着处理int b = 3;在创建完b的引用变量后,由于在栈中已经有3这个字面值,便将b直接指向3的地址。这样,就出现了a与b同时均指向3的情况。特 别注意的是,这种字面值的引用与类对象的引用不同。假定两个类对象的引用同时指向一个对象,如果一个对象引用变量修改了这个对象的内部状态,那么另一个对象引用变量也即刻反映出这个变化。相反,通过字面值的引用来修改其值,不会导致另一个指向此字面值的引用的值也跟着改变的情况。如上例,我们定义完a与 b的值后,再令a=4;那么,b不会等于4,还是等于3。在编译器内部,遇到a=4;时,它就会重新搜索栈中是否有4的字面值,如果没有,重新开辟地址存放4的值;如果已经有了,则直接将a指向这个地址。因此a值的改变不会影响到b的值。
3、方法区是一块所有线程共享的内存区域,用于存放类的信息,比如类的字段、方法、常量池等。常量池存放基本类型的常量字面值、以“”定义的字符串等。方法区的大小决定系统可以保存多少个类。如果系统定义太多的类,导致方法区溢出。虚拟机同样会抛出内存溢出的错误。
2)构造方法
1、构造方法与类同名且没有返回值
2、只能在对象实例化的时候调用(new关键字的好搭档)
3、当没有指定构造方法时,系统会自动添加无参的构造方法;当有指定构造方法时,无论是有参、无参的构造方法,都不会自动添加无参的构造方法;一个类中可以有多个构造方法
3) THIS
简单来说,this在类中就是代表当前对象,谁调用谁就是当前对象。引用成员变量。如果在某个变量前面加上一个this关键字,其指的就是这个对象的成员变量或者方法,而不是指成员方法的形式参数或者局部变量。
8、封装
封装:将类的某些信息隐藏在类内部,不允许外部程序直接访问,只能通过该类提供的方法来实现对隐藏信息的操作和访问。隐藏对象的信息,留出访问的接口
1)static
1、定义静态变量。当在类中定义了一个静态变量时,不管使用这个类定义多少对象,这些对象中的static方法对应的变量始终指向了同一内存空间,对任意一个对象修改其中的内容,都会影响其他静态变量的值。
1 类对象共享 2 类加载时产生,生命周期长 3 既可以通过对象名访问,也可以通过类名访问
2、static修饰方法 称 静态方法,也叫类方法,推荐调用的方式为 类名.成员
3、static不能用来修饰 类,也不能修饰 成员方法内的局部变量
4、静态方法中不能直接访问同一个类中的非静态成员,只能直接调用静态成员。静态方法中也不能使用this, 只能通过对象实例化后,对象.成员/方法 的方式访问类中的非静态成员。
5、类方法中调用本类的类方法时可直接调用,也可以通过类名.方法名 直接调用其他类的类方法
2)代码块
1、普通代码块——如 public void getName() { }——顺序执行,先出现,先执行
2、直接使用 { } 定义一个构造代码块,它每次创建对象都会调用一次,优先于构造方法执行。
注:构造代码块不是优先于构造函数执行,而是依托于构造函数。也就是说,如果你不实例化对象,构造代码块是不会执行的。构造代码块每次实例化对象时都会执行一次。
3、static{} 静态代码块,类加载时调用,优先于构造代码块执行。 静态代码块无论实例化多少个对象,都只执行一次。
4、构造代码块中可以给普通成员赋值,静态代码块中只能直接访问类中静态变量,除非实例化对象通过对象访问。
9、继承
继承:体现一种类与类之间的关系 使用已存在的类的定义作为基础类建立新类 新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类。
java中的继承只能是单继承 子类可以访问父类非私有成员。
1)方法重写
方法重写:
1、有继承关系的子类中
2、方法名,参数列表(类型、顺序、个数)必须相同
3、与参数名无关
4、访问修饰符,访问范围需要大于等于父类方法的访问范围
5、子类的返回值类型必须相同,或者是,可以允许父类的子类作为返回值类型
6、子类抛出的异常类型要小于或者等于父类抛出的异常类型。
2)访问修饰符
1、private :只允许在本类中进行访问
2、default(不加修饰符):允许在当前类、同包子类/同包非子类 访问,无法在跨包子类/跨包非子类中调用
3、protected:允许在当前类、同包子类/同包非子类、跨包子类调用,但不能在跨包非子类中调用
4、public :访问权限最大,运行在任意位置访问
3)继承初始化
super :父类对象的引用。在继承关系中,父类的构造方法是不允许被继承的,也不允许被重写。
继承类在进行初始化时,会优先加载父类的静态成员,然后加载子类的静态成员,接下来构造时会逐层完成父类对象加载,即先去找父类,最终到object类(所有类的父类),接下来逐层回到父类加载成员属性,执行构造代码块,执行构造方法。最后完成子类对象构造。
当进行静态成员加载时,访问修饰符不影响成员加载顺序,与成员的书写顺序有关。
**子类构造过程中必须调用,而且默认调用的是父类的无参构造方法。**当父类中没有无参构造方法的时候,子类在继承时就会出错。可以通过super() 调用父类允许被访问的其它构造方法和属性,super( ) 必须放在子类构造方法的有效代码的第一行。 虽然父类的构造不允许被继承、不允许被重写,但是会影响子类对象的实例化。
构造方法调用时,super和this不能同时出现,因为它们会抢占第一行的位置
4)Object
Object类是所有类的父类。继承Object中的equals方法时,比较的是两个引用是否指向同一个对象,作用相等于 == 符号。 String类中重写的 equals方法,比较的是两个字符串里的内容。
toString() 方法: 输出对象名时默认调用,继承自Object中的 toString() 方法,输出 包.类名(类型信息) +@ +内存位置(地址信息)。String类型中重写了 toString() ,toString(对象名)会直接输出 对象值。
5)final
1、final class : 表示该类没有子类 public final class \ final public class 两种写法都正确
2、如果 final 添加在方法前,那么该方法不能被子类所重写。但是在子类当中仍然可以正常的被继承使用。注: final不能修饰构造方法。
3、final 可以用来修饰变量:
修饰方法内局部变量:只要在具体使用之前进行赋值即可,一旦被赋值就不允许被修改。
修饰类成员属性:修饰后可以对该属性赋值的地方:1、定义直接初始化 2、构造方法 3、构造代码块(也只能在这三个地方被赋值,其它地方不允许进行赋值操作,再次强调!)不初始化,编译前会直接报错
另外,final 修饰 引用数据类型时,实例化后不允许对引用地址重新修订,但对象里的属性值可以重新修改。 4、final static 表示全局不可更改,常用来修饰配置信息等
10、单例模式
场景:比如无论计算机连接多少打印机,但同时只有一台打印机在工作。
应用场景:如果希望在系统中某个类的对象只能存在一个,单例模式是最好的解决方案。
1.创建对象时占用资源过多,但同时有需要用到该类对象 2.对系统内资源要求统一读写,如读写配置信息 3.当多个实例存在可能引起程序逻辑错误,如号码生成器
目的:使得类的一个对象成为该系统中的唯一实例。
定义:一个类有且仅有一个实例,并且自行实例化向整个系统提供。
设计要点: 1、某个类只能有一个实例 2、必须自行创建实例 3、必须自行向整个系统提供这个实例
实现: 1、只提供 私有 的构造方法 2、必须在类中创建该类的 静态 私有对象 3、在类中提供一个 静态 的公有方法用于创建、获取静态私有对象
代码实现方案: 1、饿汉式:对象创建过程中实例化 2、懒汉式:静态公有方法中实例化
饿汉式
创建对象实例的时候直接初始化
1.创建私有构造 2.创建该类型的私有静态实例 3.创建公有静态方法返回静态实例对象
public class SingletonOne {
//1、创建类中私有构造
private SingletonOne() {
}
//2、创建该类型的私有实例
private static SingletonOne instance=new SingletonOne();
//3、创建公有静态方法
public static SingletonOne getInstance() {
return instance;
}
}
空间换时间。进行加载的时候,就已然完成了对实例化的操作,此时无论你是否使用,这个实例都将存在在内存中。饿汉式因为在类加载时已经完成了对象的实例化创建,所以是线程安全的。
懒汉式
1.创建私有构造方法; 2.创建静态的该类实例对象 3.创建开放的静态方法提供实例对象
时间换空间,类内实例对象创建时并不直接初始化,直到第一次调用get方法时,才完成初始化操作。这使得它存在线程风险。可以通过 1、同步锁 2、双重校验锁 3、静态内部类 4、枚举 解决
public class SingletonTwo {
//1、创建私有构造方法
private SingletonTwo() {
}
//2、创建静态的该实例对象
private static SingletonTwo instance=null;
//3、创建开放的静态方法提供实例对象
public static SingletonTwo getInstance() {
if(instance==null) //
instance=new SingletonTwo();
return instance;
}
}
优缺点
优点: 1.在内存中只有一个对象,节省内存空间 2.避免频繁的创建销毁对象,提高性能 3.避免对共享资源的多重占用。
缺点: 1.扩展比较困难 2.如果实例化后的对象长期不利用,系统将默认为垃圾进行回收,造成对象状态丢失。
11、多态
多态: 意味着允许不同类的对象对同一消息做出不同的响应。
分类:1)编译时多态(也叫设计时多态,举例如方法重载) 2)运行时多态(程序运行时决定调用哪个方法) java中的多态大多指运行时多态。多态的实现有两个必要条件: 1 、满足继承关系 2 、父类引用指向子类对象
1)向上转型
父类引用指向子类具体实例:向上转型(隐式转型、自动转型)
可以调用子类重写父类的方法以及父类派生的方法,无法调用子类特有的方法。
2)向下转型
向下转型(也称强制类型转换):子类引用指向父类对象。此处必须强制转换,可以调用子类特有的方法
instanceof :判断左边的对象是否满足右边类的实例特征,是 返回true,否则返回false。
总结:在父类当中使用static 关键字修饰的方法不能被子类所重写。加上@Override注解会报错。 父类引用指向子类实例,可以调用子类重写的方法以及父类派生的方法,无法调用子类独有的方法。 注意:父类中的静态方法无法被子类重写,所以向上转型之后,只能调用到父类原有的静态方法。
3)抽象类
java中使用抽象类限制实例化。
abstract 修饰类 (抽象类)不能直接实例化,但是可以通过让子类向上转型,父类引用指向子类实例间接实例化。可以达到在编译期间提示程序员不要实例化一些没有意义的类,真正干活的有意义的是子类的实例对象。
注: public 与 abstract 可以互换,但是却不能 与class关键字相互换位。
使用规则:
1、abstract 定义抽象类
2、抽象类不能直接被实例化,只能被继承,可以通过向上转型的方式完成实例化对象
3、abstract 在父类中定义抽象方法,不需要关心其具体实现,定义时语法上不允许其包含方法体,参数列表后要加分号。子类必须重写父类的抽象方法,否则编译时会报错,除非子类也是抽象类。
4、包含抽象方法的类一定是抽象类。
5、一个抽象方法必须定义在抽象类中,而抽象类可以没有抽象方法。static、final、private 不能与abstract 并存
4)接口
场景:如何解决一个类型中需要兼容多种类型特征的问题,以及多个不同类型具有相同特征的问题呢?
定义接口: public interface+接口名,成员方法不能有方法体 。
调用接口: 类名+implements+接口名,必须重写接口里的方法。
接口定义了某一批类所需要遵守的规范 接口不关心这些类的内部数据,也不关心这些类里的方法的实现细节,它只规定这些类里必须提供某些方法。
接口访问修饰符包括:public、默认 接口中方法的访问修饰符默认为public
接口当中抽象方法可以不写abstract关键字。
当一个类实现接口的时候,需要去实现接口中的所有抽象方法,否则需要将该类设置为抽象类。
接口可以实现继承,并且可以继承多个父接口。
接口中定义的都是常量,它们默认public static final,必须在初始化时赋值。使用接口名.常量去访问接口中的常量。注意:如果实现类中存在与接口中同名的常量,当用接口的引用指向实现类时,接口引用访问到的依然是接口中的常量。
如果接口A中有一个public访问权限的静态变量a。按照java的语义,我们可以不通过实现接口的对象来访问变量a,通过A.a = xxx;就可以改变接口中的变量a的值了。正如抽象类中是可以这样做的,那么实现接口A的所有对象也都会自动拥有这一改变后的a的值了,也就是说一个地方改变了a,所有这些对象中a的值也都跟着变了。这和抽象类有什么区别呢,怎么体现接口更高的抽象级别呢,怎么体现接口提供的统一的协议呢,那还要接口这种抽象来做什么呢?所以接口中不能出现变量,如果有变量,就和接口提供的统一的抽象这种思想是抵触的。所以接口中的属性必然是常量,只能读不能改,这样才能为实现接口的对象提供一个统一的属性。
通俗的讲,你认为是要变化的东西,就放在你自己的实现中,不能放在接口中去,接口只是对一类事物的属性和行为更高层次的抽象。对修改关闭,对扩展(不同的实现implements)开放,接口是对开闭原则的一种体现。
(原文:https://blog.csdn.net/happydecai/article/details/80193062)
JDK1.8为接口提供了: 使用default关键字定义的方法,可以在接口中带有方法体,在实现接口的类的方法中可以不重写该接口提供的方法(可选择性)如果想要在实现类中访问接口的默认成员,需要通过接口名.super.方法名访问。
JDK1.8为接口提供了:static静态方法,也可以在接口中带有方法体,通过 接口名.方法 调用,无法在子类中重写。
实现类 实现接口时重名处理
方法重名:
类实现的多个接口存在重名默认方法、子类实现的接口与父类存在重名方法的处理:
1、多接口中存在同名方法:在继承接口的实现类中会编译报错。解决方法为:在继承接口的实现类中,重写同名的默认方法。此时若用指向 实现类实例 的 接口引用调用该方法,则会调用类中重写的方法。
2、父类与接口中都存在重名的方法:指向子类的父类引用调用时,会默认指向父类中的重名方法,除非子类重写该重名方法。
常量重名:
一个类如果实现若干个接口,而这若干个接口之间有同名的常量的时候,在此类中直接调用同名常量时会报错,即使该类有继承关系的父类当中也含有该同名的变量,依然会报错。 解决此类办法的途径,一是通过接口名.变量的方式访问,二是在该子类中定义同名的属于子类自己的成员属性并初始化,以消除这种错误。
5)内部类
内部类:将一个类定义在另一个类的内部,我们就称这个类为内部类,而包含内部类的类称之为外部类。
好处:内部类隐藏在外部内的里面,更好的实现了信息的隐藏。
内部类的分类: 1 、成员内部类 2 、静态内部类 3 、方法内部类 4 、匿名内部类
1-成员内部类
1、内部类在外部使用时,无法进行直接实例化,需要借由外部类的信息才能完成实例化。
获取内部类的对象实例:
方式1:外部类.内部类 类标识名 = new 外部类().new 内部类()
方法2:外部类实例对象 . new 内部类()
方法3:外部类对象 . 内部类的get方法() 需要在外部类中定义内部类的get方法
2、**关于内部类的访问修饰符可以任意,但是访问内部类的范围会受到影响 **
3、内部类可以直接访问外部类的成员,包括成员属性和方法。内部类和外部类如果出现同名属性,内部类中调用时优先访问内部类中定义的。
4、内部类和外部类存在同名成员属性情况下,如果想要访问外部类成员属性,可以使用 外部类 . this . 成员的方式访问。
5、外部类访问内部类的信息,需要通过 内部类实例 . 内部类成员,否则无法直接访问。
6、内部类编译后 .class 文件名命名:外部类$内部类.class
2-静态内部类
1、静态内部类中,只能直接访问外部类的静态成员,如果需要调用非静态成员,只能通过外部类的对象实例
2、静态内部类对象实例时,可以不依赖于外部类对象(不需要先new一个外部类对象,直接用类)
3、可以通过 外部类 . 内部类 . 静态成员 访问内部类中的静态成员
4、当内部类属性于外部类属性同名的时候,默认直接调用内部类中的成员。
5、如果需要访问外部类当中的的静态属性,则可以通过 外部类 . 属性 的方式。
6、如果需要访问外部类当中的的非静态属性,则可以通过 new 外部类() . 属性 的方式访问。
3-方法内部类(局部内部类)
1、定义在方法内部,作用范围也在方法内
2、和方法内部成员使用规则一样,class前面不可以添加public、private、protected、static。作为方法里的一个成员,方法内部类的所有动作只能基于这个方法去做。一般使用方法内部类时很少在方法上直接返回类,而是返回调用方法内部的get方法。
3、方法内部类中不能包含静态成员
4、方法内部类中可以包含final、abstract(不推荐)修饰的成员
4-匿名内部类
隐藏名字。适用于:只用到类的一次实例,类在定义后马上用到,给类命名并不会导致代码更容易被理解 的场景。 类 . 方法 ( new 类() { 方法体 } ) 实例对象的同时完成对对象的编写,内存损耗较小。
1、匿名内部类没有类型名称、实例对象名称
2、编译后的文件命名:外部类$数字.class
3、无法使用private\public \protected\abstract\static修饰。
4、无法编写构造方法(因为没有类名),可以添加构造代码块
5、匿名内部类不能出现静态成员
6、匿名内部类可以实现接口也可以继承父类,但是不可兼得。
三、Java常用工具类
12、异常
1)常见异常
13、包装类
14、字符串
substring() 方法:返回字符串的子字符串。
public String substring(int beginIndex)
或
public String substring(int beginIndex, int endIndex)
beginIndex – 起始索引(包括), 索引从 0 开始。
endIndex – 结束索引(不包括)。
String的不可变性:String对象一旦被创建,则不能修改,是不可变的 所谓的修改其实是创建了新的对象,所指向的内存空间不变。
String s1=“a”; String s2=“a”; s2这个引用指向的是s1在常量池中的“a”。s2=“b”; s2并没有改变原来“a”的值,它指向了一个新的常量。原来的“a”任然占据内存空间,等待被垃圾回收。
15、集合与排序
集合应用场景:
集合框架的体系结构
1)List
List是元素有序并且可以重复的集合,称为序列。 List可以精确的控制每个元素的插入位置,或删除某个位置的元素。 List的两个主要实现类是ArrayList和LinkedList。
ArrayList
ArrayList底层是由数组实现的。 在列表尾部插入或删除数据非常有效,更适合查找和更新元素。ArrayList中的元素可以为null,允许重复值。
ArrayList可以动态增长。
HashSet
HashSet是Set的一个重要实现类,称为哈希集。HashSet中的元素无序并且不可以重复,HashSet中只允许一个null元素,具有良好的存取和查找性能。
Iterator
Set接口、HashSet类中都没有显示元素的get方法,想显示元素要靠 Iterator(迭代器):
Iterator接口可以以统一的方式对各种集合元素进行遍历。
.hasNext() 方法检测集合中是否还有下一个元素,它的返回值是boolean类型。
.next() 方法返回集合中的下一个元素,返回Object类型的对象。
向 HashSet() 中插入重复元素不会报错,因为系统并不会把重复元素插入到集合中。
解答:当调用add(Object)方法时候,首先会调用Object的hashCode方法判断hashCode是否已经存在,如不存在则直接插入元素;如果已存在则调用Object对象的equals()方法判断是否返回true,如果为true则说明元素已经存在,如为false则插入元素。HashSet是借助HashMap来实现的,利用HashMap中Key的唯一性,来保证HashSet中不出现重复值。
当Set集合要添加新的元素时,会先隐式调用这个元素的hashCode方法,就能定位到它应该放置的物理位置。如果这个位置上没有元素,它就可以直接存储在这个位置上,不用再进行任何比较了;如果这个位置上已经有元素了,就调用它的equals方法与新元素进行比较,相同的话就不存了,不相同就散列其它的地址。
2)泛型
在集合中引入泛型之后可以使用增强型for循环遍历集合中的每个元素. 集合为避免数据的不一致性,限制读取时不允许删除。 集合中多条删除,要把它们添加到一个集合当中:
Set<Cat> set1=new HashSet<Cat>();
for(Cat cat:set) {
if(cat.getMouth()<5){
set1.add(cat);
}
}
set.removeAll(set1);
3)Map
Map中的数据是以键值对(key-value)的形式存储的。key-value以Entry类型的对象实例存在。可以通过key值快速地查找value值。 一个映射不能包含重复的键。 每个键最多只能映射到一个值。
HashMap
基于哈希表的Map接口的实现。允许null值和null键。key值不允许重复。 HashMap中的Entry对象是无序排列的。
4)Comparator和Comparable
Comparator接口
强行对某个对象进行整体排序的比较函数 可以将Comparator传递给sort方法(如Collections.sort或Arrays.sort) int compare(T o1,T o2) 比较用来排序的两个参数。
Comparable接口
此接口强行实现它的每个类的对象进行整体排序 这种排序被称为类的自然排序,类的compareTo方法被称为它的自然比较方法。
对于集合,通过调用Collections.sort方法进行排序;对于数组,通过调用Arrays.sort方法进行排序
int comparaTo(T o) 方法。