Java基础复习

Java语言拥有跨平台、分布式、多线程、健壮性等主要特点,是一种面向对象型的编程语言。
 
 
面对过程让计算机可以有步骤地做一件事情,是一种过程化的叙事思维。但是在大型软件开发过程中,发现用面向过程语言开发、软件维护、软件复用存在着巨大困难,模块至今互相耦合,流程互相穿插,往往牵一发而动全身。
面向对象提出一种计算机世界里解决复杂软件工程的方法论,拆解问题复杂度,从人类思维角度提出解决问题的步骤和方案。
  面向对象的四大特性:抽象、封装、继承、多态
    抽象:找到属性和行为的共性
    封装:是一种对象内聚的表现形式,使模块之间耦合度变低,更具有维护性。
    继承:使子类能够继承父类,获得父类的部分属性和行为,是模块更有复用性
    多态:使模块在复用性基础上更加有扩展性
 
   
抽象类和接口的区别
 
语法维度 抽象类 接口
方法实现 可以有 不能有,JDK8之后允许有default实现
方法访问控制符 无限制 public abstract
属性访问控制符 无限制 public static final
静态方法和静态代码块 可以有 不能有
本类型之间拓展 单继承 多继承
继承和实现体现的关系 is-a can-do
 
 
基本类型
基本类型是指不可再分的原子数据类型,内存中直接存储此类型的值。
Java语言提供了八种基本类型。六种数字类型(4种整型,2种浮点型),一种字符类型,还有一种布尔型
  byte:
       -128(-2^7)—— 127(2^7-1),默认值0,8位,占用1个字节
  short:
       -32768(-2^15)—— 32767(2^15 - 1),默认值0,16位,占用2个字节
  int:
       -2,147,483,648(-2^31)—— 2,147,483,647(2^31 - 1),默认值0,32位,占用4个字节
  long:
       -2^63—— 2^63 -1,默认值0L。64位,占用8个字节
  float:
       单精度,默认值0.0f,32位,占用4个字节
  double:
       双精度,默认值0.0d,64位,占用8个字节
  char:
       16位,占用2字节,最小值 \u0000即0,最大值 \uffff即65535
  boolean:
       true/false
 
 
包装类型
  上面的8种基本类型都有相应的包装类,因为Java的设计理念是一切皆是对象,在很多情况下,需要以对象的形式操作,比如hashCode()获取哈希值,或者getClass()获取类等。
  包装类的存在解决了基本数据类型无法做到的事情:泛型类型参数、序列化、类型转换、高频区间数据缓存。
  高频区间数据缓存:
    Integer会缓存-128~127之间内的值,对于Integer var=?在-128~127之间的赋值,在自动装箱时,Integer对象由IntegerCache.cache产生,会复用已有对象,这个区间内的Integer值可以直接使用==进行判断,但是这个区间之外的值都会在堆上产生,并不会复用已有对象。因此,推荐所有包装类对象之间的比较,全部使用equals()方法
    
 
     基本数据类型及对应的包装类型:
类型名称 默认值 大小 最小值 最大值 包装类 缓存区间
boolean false 1B 0(false) 1(true) Boolean
byte (byte)0 1B -128 127 Byte -128~127
char '\u0000' 2B '\u0000' '\uFFFF' Character (char)0~(char)127
short (short)0 2B -2^15 2^15-1(32767) Short -128~127
int 0 4B -2^31 2^31-1 Integer -128~127
long 0L 8B -2^63 2^63-1 Long -128~127
float 0.0f 4B 1.4e-45 3.4e+38 Float
double 0.0d 8B 4.9e-324 1.798e+308 Double
 
 
自动拆装箱
  把基本数据类型转换成包装类型的过程就是打包装,称之为装箱
  把包装类型转换为基本数据类型的过程就是拆包装,称之为拆箱
 
JDK1.5开始提供了自动装箱与自动拆箱功能。
  自动装箱:就是将基本数据类型自动转换成对应的包装类型
  自动拆箱:就是将包装类型自动转换成对应的基本数据类型
 
  int的自动装箱是通过Integer.valueOf()方法来实现的,Integer的自动拆箱是通过Integer.intValue()来实现的。
 
  什么场景下会自动拆装箱:
  1)将基本数据类型放入集合类,会自动装箱
  2)包装类型和基本类型的大小比较:包装类与基本数据类型进行比较运算,是先将包装类进行拆箱成基本数据类型,然后进行比较的
  3)包装类型的运算:两个包装类型之间的运算,会被自动拆箱成基本类型进行
  4)函数参数与返回值
 
 
使用包装类型还是基本类型?
  1)所有的POJO类属性必须使用包装数据类型
  2)RPC方法的返回值和参数必须使用包装数据类型
  3)所有的局部变量推荐使用基本数据类型
  
 
引用类型
    对象、数组都是引用类型
    所有引用类型的默认值都是null
    
Java常量
  常量是在作用域内保持不变的值,使用final修饰常量。
  根据作用域区分,分为全局常量(公开静态常量,public static final)、类内常量(私有静态常量,private static final)、局部常量。
    局部常量又分为 :方法常量(方法体或代码块中定义的常量)、参数常量(final 修饰的形参,表示此参数值不能修改)
   命名形式上,全局常量和类内常量采用单词全部大写、单词之间加下划线的形式。局部常量采用小驼峰形式即可。
 
 
     byte、int、long、和short都可以用十进制、16进制以及8进制的方式来表示。当使用字面量的时候,前缀 0 表示 8 进制,而前缀 0x 代表 16 进制

  Java语言支持一些特殊的转义字符序列:

符号字符含义
\n 换行 (0x0a)
\r 回车 (0x0d)
\f 换页符(0x0c)
\b 退格 (0x08)
\0 空字符 (0x0)
\s 空格 (0x20)
\t 制表符
\" 双引号
\' 单引号
\\ 反斜杠
\ddd 八进制字符 (ddd)
\uxxxx 16进制Unicode字符 (xxxx)
    
自动类型转换
  整型、实型(常量)、字符型数据可以混合运算。运算中,不同类型的数据先转化为同一类型,然后进行运算

  转换从低级到高级:

  低  ------------------------------------>  

  byte,short,char—> int —> long—> float —> double

  数据类型转换必须满足如下规则:

    1. 不能对boolean类型进行类型转换。

    2. 不能把对象类型转换成不相关类的对象。

    3. 在把容量大的类型转换为容量小的类型时必须使用强制类型转换。

    4. 转换过程中可能导致溢出或损失精度,例如:byte 类型是 8 位,最大值为127,所以当 int 强制转换为 byte 类型时,值 128 时候就会导致溢出

    5. 浮点数到整数的转换是通过舍弃小数得到,而不是四舍五入

 

访问控制修饰符

  • public : 对所有类可见。使用对象:类、接口、属性、方法

  • protected : 对同一包内的类和所有子类可见。使用对象:属性、方法。 注意:不能修饰类(外部类)

  • : 在同一包内可见,不使用任何修饰符。使用对象:类、接口、属性、方法。
  • private : 在同一类内可见。使用对象:属性、方法。 注意:不能修饰类(外部类)

 

  访问权限控制及可见范围:

访问权限控制符 任何地方 包外子类 包内 类内
public OK OK OK OK
protected NO OK OK OK
NO NO OK OK
private NO NO NO OK

 

  由此可见,不同的访问权限控制符对应的可见范围不同。在定义类时,要慎重思考该方法、属性、内部类的访问权限,提倡严控访问范围。过于宽泛的访问范围不利于模块键的解耦及未来的代码维护。

  因此,在定义类时,推荐访问权限控制级别从严处理,规范如下:

    1)如果不允许外部直接通过new创建对象,构造方法必须是private

    2)工具类不允许有public或default构造方法

    3)类中非static成员变量并且与子类共享,必须是protected

    4)类中非static成员变量并且仅在本类内部使用,必须是private

    5)类中static成员变量如果仅在本类内使用,必须是private

    6)若是static成员变量,必须考虑是否为final

    7)类成员方法只供类内部调用,必须是private

    8)类成员方法只对继承类公开,那么限制为protected

 

访问控制和继承
  请注意以下方法继承的规则:

    父类中声明为 public 的方法在子类中也必须为 public。

    父类中声明为 protected 的方法在子类中要么声明为 protected,要么声明为 public,不能声明为 private。

    父类中声明为 private 的方法,不能够被继承

 

非访问修饰符

  static 修饰符,用来修饰方法(静态方法/类方法)和变量(静态变量/类变量)。

  final 修饰符,用来修饰类、方法和变量,final 修饰的类不能够被继承,修饰的方法可被子类继承,但是不能被子类重写,修饰的变量为常量,是不可修改的。

    final 修饰符通常和 static 修饰符一起使用来创建类常量

  abstract 修饰符,用来创建抽象类和抽象方法。

    一个类不能同时被 abstract 和 final 修饰。如果一个类包含抽象方法,那么该类一定要声明为抽象类,否则将出现编译错误

  synchronized 修饰符:synchronized 关键字声明的方法同一时间只能被一个线程访问

  volatile 修饰符:修饰的成员变量在每次被线程访问时,都强制从共享内存中重新读取该成员变量的值

  transient 修饰符:序列化的对象包含被 transient 修饰的实例变量时,java 虚拟机(JVM)跳过该特定的变量

 

Java运算符

 位运算符

  Java定义了位运算符,应用于整数类型(int),长整型(long),短整型(short),字符型(char),和字节型(byte)等类型。

  位运算符作用在所有的位上,并且按位运算

操作符描述例子
如果相对应位都是1,则结果为1,否则为0 (A&B),得到12,即0000 1100
| 如果相对应位都是 0,则结果为 0,否则为 1 (A | B)得到61,即 0011 1101
^ 如果相对应位值相同,则结果为0,否则为1 (A ^ B)得到49,即 0011 0001
按位取反运算符翻转操作数的每一位,即0变成1,1变成0。 (〜A)得到-61,即1100 0011
<<  按位左移运算符。左操作数按位左移右操作数指定的位数。 A << 2得到240,即 1111 0000
>>  按位右移运算符。左操作数按位右移右操作数指定的位数。 A >> 2得到15即 1111
>>>  按位右移补零操作符。左操作数的值按右操作数指定的位数右移,移动得到的空位以零填充。 A>>>2得到15即0000 1111

 

 

  赋值运算符:

    下面是Java语言支持的赋值运算符:优先级由高到低

操作符描述例子
= 简单的赋值运算符,将右操作数的值赋给左侧操作数 C = A + B将把A + B得到的值赋给C
+ = 加和赋值操作符,它把左操作数和右操作数相加赋值给左操作数 C + = A等价于C = C + A
- = 减和赋值操作符,它把左操作数和右操作数相减赋值给左操作数 C - = A等价于C = C - A
* = 乘和赋值操作符,它把左操作数和右操作数相乘赋值给左操作数 C * = A等价于C = C * A
/ = 除和赋值操作符,它把左操作数和右操作数相除赋值给左操作数 C / = A,C 与 A 同类型时等价于 C = C / A
(%)= 取模和赋值操作符,它把左操作数和右操作数取模后赋值给左操作数 C%= A等价于C = C%A
<< = 左移位赋值运算符 C << = 2等价于C = C << 2
>> = 右移位赋值运算符 C >> = 2等价于C = C >> 2
&= 按位与赋值运算符 C&= 2等价于C = C&2
^ = 按位异或赋值操作符 C ^ = 2等价于C = C ^ 2
| = 按位或赋值操作符 C | = 2等价于C = C | 2

 

包装类:

所有的包装类(Integer、Long、Byte、Double、Float、Short)都是抽象类 java.lang.Number 的子类。

包装类基本数据类型
Boolean boolean
Byte byte
Short short
Integer int
Long long
Character char
Float float
Double double

 

正则表达式:

  正则表达式定义了字符串的模式。

  正则表达式可以用来搜索、编辑或处理文本。

  正则表达式并不仅限于某一种语言,但是在每种语言中有细微的差别。

java.util.regex 包主要包括以下三个类:

  • Pattern 类:

    pattern 对象是一个正则表达式的编译表示。Pattern 类没有公共构造方法。要创建一个 Pattern 对象,你必须首先调用其公共静态编译方法,它返回一个 Pattern 对象。该方法接受一个正则表达式作为它的第一个参数。

  • Matcher 类:

    Matcher 对象是对输入字符串进行解释和匹配操作的引擎。与Pattern 类一样,Matcher 也没有公共构造方法。你需要调用 Pattern 对象的 matcher 方法来获得一个 Matcher 对象。

  • PatternSyntaxException:

    PatternSyntaxException 是一个非强制异常类,它表示一个正则表达式模式中的语法错误

 

正则表达式语法

在其他语言中,\\ 表示:我想要在正则表达式中插入一个普通的(字面上的)反斜杠,请不要给它任何特殊的意义。

在 Java 中,\\ 表示:我要插入一个正则表达式的反斜线,所以其后的字符具有特殊的意义。

所以,在其他的语言中(如Perl),一个反斜杠 \ 就足以具有转义的作用,而在 Java 中正则表达式中则需要有两个反斜杠才能被解析为其他语言中的转义作用。也可以简单的理解在 Java 的正则表达式中,两个 \\ 代表其他语言中的一个 \,这也就是为什么表示一位数字的正则表达式是 \\d,而表示一个普通的反斜杠是 \\\\。

字符

说明

\

将下一字符标记为特殊字符、文本、反向引用或八进制转义符。例如,"n"匹配字符"n"。"\n"匹配换行符。序列"\\\\"匹配"\\","\\("匹配"("。

^

匹配输入字符串开始的位置。如果设置了 RegExp 对象的 Multiline 属性,^ 还会与"\n"或"\r"之后的位置匹配。

$

匹配输入字符串结尾的位置。如果设置了 RegExp 对象的 Multiline 属性,$ 还会与"\n"或"\r"之前的位置匹配。

*

零次或多次匹配前面的字符或子表达式。例如,zo* 匹配"z"和"zoo"。* 等效于 {0,}。

+

一次或多次匹配前面的字符或子表达式。例如,"zo+"与"zo"和"zoo"匹配,但与"z"不匹配。+ 等效于 {1,}。

?

零次或一次匹配前面的字符或子表达式。例如,"do(es)?"匹配"do"或"does"中的"do"。? 等效于 {0,1}。

{n}

是非负整数。正好匹配 n 次。例如,"o{2}"与"Bob"中的"o"不匹配,但与"food"中的两个"o"匹配。

{n,}

是非负整数。至少匹配 次。例如,"o{2,}"不匹配"Bob"中的"o",而匹配"foooood"中的所有 o。"o{1,}"等效于"o+"。"o{0,}"等效于"o*"。

{n,m}

m 和 n 是非负整数,其中 n <= m。匹配至少 n 次,至多 m 次。例如,"o{1,3}"匹配"fooooood"中的头三个 o。'o{0,1}' 等效于 'o?'。注意:您不能将空格插入逗号和数字之间。

?

当此字符紧随任何其他限定符(*、+、?、{n}、{n,}、{n,m})之后时,匹配模式是"非贪心的"。"非贪心的"模式匹配搜索到的、尽可能短的字符串,而默认的"贪心的"模式匹配搜索到的、尽可能长的字符串。例如,在字符串"oooo"中,"o+?"只匹配单个"o",而"o+"匹配所有"o"。

.

匹配除"\r\n"之外的任何单个字符。若要匹配包括"\r\n"在内的任意字符,请使用诸如"[\s\S]"之类的模式。

(pattern)

匹配 pattern 并捕获该匹配的子表达式。可以使用 $0…$9 属性从结果"匹配"集合中检索捕获的匹配。若要匹配括号字符 ( ),请使用"\("或者"\)"。

(?:pattern)

匹配 pattern 但不捕获该匹配的子表达式,即它是一个非捕获匹配,不存储供以后使用的匹配。这对于用"or"字符 (|) 组合模式部件的情况很有用。例如,'industr(?:y|ies) 是比 'industry|industries' 更经济的表达式。

(?=pattern)

执行正向预测先行搜索的子表达式,该表达式匹配处于匹配 pattern 的字符串的起始点的字符串。它是一个非捕获匹配,即不能捕获供以后使用的匹配。例如,'Windows (?=95|98|NT|2000)' 匹配"Windows 2000"中的"Windows",但不匹配"Windows 3.1"中的"Windows"。预测先行不占用字符,即发生匹配后,下一匹配的搜索紧随上一匹配之后,而不是在组成预测先行的字符后。

(?!pattern)

执行反向预测先行搜索的子表达式,该表达式匹配不处于匹配 pattern 的字符串的起始点的搜索字符串。它是一个非捕获匹配,即不能捕获供以后使用的匹配。例如,'Windows (?!95|98|NT|2000)' 匹配"Windows 3.1"中的 "Windows",但不匹配"Windows 2000"中的"Windows"。预测先行不占用字符,即发生匹配后,下一匹配的搜索紧随上一匹配之后,而不是在组成预测先行的字符后。

x|y

匹配 x 或 y。例如,'z|food' 匹配"z"或"food"。'(z|f)ood' 匹配"zood"或"food"。

[xyz]

字符集。匹配包含的任一字符。例如,"[abc]"匹配"plain"中的"a"。

[^xyz]

反向字符集。匹配未包含的任何字符。例如,"[^abc]"匹配"plain"中"p","l","i","n"。

[a-z]

字符范围。匹配指定范围内的任何字符。例如,"[a-z]"匹配"a"到"z"范围内的任何小写字母。

[^a-z]

反向范围字符。匹配不在指定的范围内的任何字符。例如,"[^a-z]"匹配任何不在"a"到"z"范围内的任何字符。

\b

匹配一个字边界,即字与空格间的位置。例如,"er\b"匹配"never"中的"er",但不匹配"verb"中的"er"。

\B

非字边界匹配。"er\B"匹配"verb"中的"er",但不匹配"never"中的"er"。

\cx

匹配 x 指示的控制字符。例如,\cM 匹配 Control-M 或回车符。x 的值必须在 A-Z 或 a-z 之间。如果不是这样,则假定 c 就是"c"字符本身。

\d

数字字符匹配。等效于 [0-9]。

\D

非数字字符匹配。等效于 [^0-9]。

\f

换页符匹配。等效于 \x0c 和 \cL。

\n

换行符匹配。等效于 \x0a 和 \cJ。

\r

匹配一个回车符。等效于 \x0d 和 \cM。

\s

匹配任何空白字符,包括空格、制表符、换页符等。与 [ \f\n\r\t\v] 等效。

\S

匹配任何非空白字符。与 [^ \f\n\r\t\v] 等效。

\t

制表符匹配。与 \x09 和 \cI 等效。

\v

垂直制表符匹配。与 \x0b 和 \cK 等效。

\w

匹配任何字类字符,包括下划线。与"[A-Za-z0-9_]"等效。

\W

与任何非单词字符匹配。与"[^A-Za-z0-9_]"等效。

\xn

匹配 n,此处的 n 是一个十六进制转义码。十六进制转义码必须正好是两位数长。例如,"\x41"匹配"A"。"\x041"与"\x04"&"1"等效。允许在正则表达式中使用 ASCII 代码。

\num

匹配 num,此处的 num 是一个正整数。到捕获匹配的反向引用。例如,"(.)\1"匹配两个连续的相同字符。

\n

标识一个八进制转义码或反向引用。如果 \n 前面至少有 n 个捕获子表达式,那么 n 是反向引用。否则,如果 n 是八进制数 (0-7),那么 n 是八进制转义码。

\nm

标识一个八进制转义码或反向引用。如果 \nm 前面至少有 nm 个捕获子表达式,那么 nm 是反向引用。如果 \nm 前面至少有 n 个捕获,则 n 是反向引用,后面跟有字符 m。如果两种前面的情况都不存在,则 \nm 匹配八进制值 nm,其中 和 m 是八进制数字 (0-7)。

\nml

当 n 是八进制数 (0-3),m 和 l 是八进制数 (0-7) 时,匹配八进制转义码 nml

\un

匹配 n,其中 n 是以四位十六进制数表示的 Unicode 字符。例如,\u00A9 匹配版权符号 (©)。

根据 Java Language Specification 的要求,Java 源代码的字符串中的反斜线被解释为 Unicode 转义或其他字符转义。因此必须在字符串字面值中使用两个反斜线,表示正则表达式受到保护,不被 Java 字节码编译器解释。例如,当解释为正则表达式时,字符串字面值 "\b" 与单个退格字符匹配,而 "\\b" 与单词边界匹配。字符串字面值 "\(hello\)" 是非法的,将导致编译时错误;要与字符串 (hello) 匹配,必须使用字符串字面值 "\\(hello\\)"。

 

可变参数:

  JDK 1.5 开始,Java支持传递同类型的可变参数给一个方法。

  方法的可变参数的声明如下所示:typeName... parameterName

  在方法声明中,在指定参数类型后加一个省略号(...) 。

  注意:一个方法中只能指定一个可变参数,它必须是方法的最后一个参数。任何普通的参数必须在它之前声明。

 

finalize():

  它在对象被垃圾收集器回收之前调用当垃圾回收确定对象不再有引用时,由垃圾回收器在对象上调用。子类重写finalize方法以释放系统资源或执行其他清理

 

异常处理:

java.lang.Throwable  顶层父类

  |– Error错误:JVM内部的严重问题,如OOM,程序员无法在代码中无法处理。

  |–Exception异常:普通的问题。通过合理的处理,程序还可以回到正常执行流程。要求程序员要进行处理。

    |–RuntimeException:未检查异常(unchecked exception)。  这类异常是程序员的逻辑问题,由于程序员的疏忽导致的错误(如数组越界,空指针等)。

      Java编译器不进行强制要求处理。 也就是说,这类异常在程序中,可以进行处理,也可以不处理。 

    |–非RuntimeException:已检查异常(checked exception)、编译时异常。这类异常是由一些外部的偶然因素所引起的。Java编译器强制要求处理。也就是说,

      程序必须进行对这类异常进行处理,throw,throws或者try catch

 

 

继承:

  继承的特性:

    1)子类拥有父类非 private 的属性、方法

    2)子类是不继承父类的构造器(构造方法或者构造函数)的,它只是调用(隐式或显式)。

    3)Java继承是单继承,不能同时继承多个类,但是支持多重继承

    4)提高了类之间的耦合性

  super关键字:我们可以通过super关键字来实现对父类成员的访问,用来引用当前对象的父类

  this关键字:指向自己的引用

 

重写和重载:

  重写 Override:子类对父类的允许访问的方法的实现过程进行重新编写,返回值和形参都不能改变。

    重写方法不能抛出新的检查异常或者比被重写方法申明更加宽泛的异常

   

  方法的重写规则

    一大两小两同

    一大:子类的方法访问权限控制符只能相同或变大

    两小:抛出异常和返回类型只能变小,能够转型为父类对象。子类的方法返回值、抛出异常必须与父类的返回类型相同或是其子类

    两同:方法名和参数必须完全相同
 

   另外还有以下规则:

    父类的成员方法只能被它的子类重写。

    声明为 final 的方法不能被重写。

    声明为 static 的方法不能被重写,但是能够被再次声明。

    子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为 private 和 final 的方法。

    子类和父类不在同一个包中,那么子类只能够重写父类的声明为 public 和 protected 的非 final 方法。

    构造方法不能被重写。

    如果不能继承一个类,则不能重写该类的方法

 

  重载(overloading) :是在一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同。

    每个重载的方法(或者构造函数)都必须有一个独一无二的参数类型列表。

  在编译期眼中,方法名称+参数类型+参数个数,组成一个唯一键,称为方法签名,JVM通过这个唯一键决定调用哪种重载的方法。所以不能有两个方法签名相同,而返回类型不同。

  最常用的地方就是构造器的重载。

  重载规则:

    被重载的方法必须改变参数列表(参数个数或类型不一样);
    被重载的方法可以改变返回类型;
    被重载的方法可以改变访问修饰符;
    被重载的方法可以声明新的或更广的检查异常;
    方法能够在同一个类中或者在一个子类中被重载。
    无法以返回值类型作为重载函数的区分标准

 

区别点重载方法重写方法
参数列表 必须修改 一定不能修改
返回类型 可以修改 一定不能修改
异常 可以修改 可以减少或删除,一定不能抛出新的或者更广的异常
访问 可以修改 一定不能做更严格的限制(可以降低限制)

 

多态:

  多态是同一个行为具有多个不同表现形式或形态的能力

  多态就是同一个接口,使用不同的实例而执行不同操作

  多态指在编译层面无法确定最终调用的方法体,以重写为基础来实现面向对象特性,在运行期由JVM进行动态绑定,调用合适和的重写方法体来执行。

 

hashCode 和 equals

  hashCode和equals用来标识对象,两个方法协同工作可用来判断两个对象是否相等。

  Object.hashCode()的实现是默认为每一个对象生成不同的int数值,它本身是native方法,一般与对象内存地址相关。

  as we all konw,根据生成的哈希将数据离散开来,可以使存取元素更快。对象通过调用Object.hashCode() 生成哈希值;由于不可避免地会存在哈希值冲突的情况,因此当hashCode相同时,还需要再调用equals进行一次值的比较;

  但是,若hashCode不同,将直接判定Object不同,跳过equals,这加快了处理效率。Object类定义中对hashCode和equals要求如下:

    1)如果两个对象的equals的结果是相等的,则两个对象的hashCode的返回结果也必须是相同的

    2)任何时候重写equals,都必须重写hashCode

  在Map和Set类集合中,用到这两个方法时,首先判断hashCode的值,如果hash相等,则再判断equals的结果。

  如果自定义对象作为Map的键,那么必须重写hashCode和equals。此外,因为Set存储的是不重复的对象,依据hashCode和equals进行判断,所以Set存储的自定义对象也必须重写这两个方法。 

 

 

END.

posted @ 2021-03-21 22:33  杨岂  阅读(39)  评论(0编辑  收藏  举报