JavaSE重要概念与常见陷阱梳理

1 Java简介

1.1 Java体系分类

1.1.1 JavaSEjava平台标准版

1.1.2 JavaEEjava平台企业版

1.1.3 JavaMEjava平台微型版

1.2 Java主要特性

1.2.1 Java语言是面向对象的

1.2.2 Java语言是分布式的

1.2.3 Java语言是健壮的

1.2.4 Java语言是安全的

1.2.5 Java语言是跨平台的

1.2.6 Java语言是半编译半解释的

1.3 JavaWindows下环境配置

1.4 测试JDK是否安装成功

2 Java变量与运算符

2.1 变量

2.2 常量

2.3 标识符

2.4 标识符的命名规则

2.5 变量和常量可以使用的数据类型

2.5.1 整数类型

2.5.2 浮点数类型

2.5.3 字符类型

2.5.4 布尔类型

2.5.5 各种变量类型的默认值

2.6 变量类型的自动转换与强制转换

2.6.1 自动转换(隐式转换)

2.6.2 强制转换(显式转换)

2.7 运算符

2.7.1 赋值运算符

2.7.2 算术运算符

2.7.3 关系运算符

2.7.4 逻辑运算符

2.7.5 三元运算符

2.7.6 运算符优先级

2.7.7 ==equals的区别

3 数组

3.1 一维数组声明与初始化的三种形式

3.2 二维数组声明与初始化的三种形式

3.3 锯齿型二维数组声明与初始化三种形式

4 面向对象编程

4.1 理解对象

4.2 理解类

4.3 理解面向对象编程思想的三个层面

4.3.1 面向对象分析(OOAObject Oriented Analysis

4.3.2 面向对象设计(OODObject Oriented Design

4.3.3 面向对象编程实现(OOPObject Oriented Programming

4.4 对象创建过程

4.5 理解类的构造器

4.6 包、packageimport

4.6.1 包的作用

4.6.2 Package

4.6.3 import

4.7 理解封装

4.8 与封装有关的访问权限修饰符

4.9 理解局部变量与成员变量

4.10 Java方法参数传值陷阱

4.11 理解静态static

4.11.1 静态属性

4.11.2 静态方法

4.11.3 静态块

4.12 垃圾回收机制

4.12.1 垃圾回收概述

4.12.2 对象被当作垃圾回收的条件

5 面向对象高级特性

5.1 面向对象编程三大特性

5.1.1 继承(inheritance

5.1.2 封装(encapsulation

5.1.3 多态(polymorphism

5.2 Superthis关键字

5.2.1 This

5.2.2 Super

5.3 抽象类与接口

5.3.1 抽象类

5.3.2 接口

5.3.3 抽象类与接口的区别

5.4 方法重写(覆盖)与重载的区别

5.4.1 重写(覆盖)

5.4.2 重载

5.4.3 关于构造器重载

5.5 多态

5.6 Final关键字

5.7 类型转换

5.7.1 向上转型

5.7.2 向下转型

5.7.3 Instanceof

5.8 子类对父类属性的继承与隐藏

6 异常处理

6.1 异常继承关系

6.2 异常处理机制

7 工具类

7.1 Object

7.2 hashCode()equals()方法重写

7.3 StringStringBufferStringBuilder的区别

7.4 字符串比较

7.4.1 String比较

7.4.2 StringBufferStringBuilder

7.5 包装类、基本数值类型和字符串之间转换关系

7.5.1 基本数值类型转换为包装类

7.5.2 包装类转换为基本数值类型

7.5.3 字符串转换为包装类

7.5.4 包装类转换为字符串

7.5.5 字符串转换为基本数值类型

7.6 包装类与方法重载

7.7 Long型、Float型和Double型包装类对象声明陷阱

7.8 包装类相等比较陷阱

7.8.1 ==比较

7.8.2 equals比较

7.9 日期类

8 集合

8.1 集合概述

8.2 集合与数组区别

8.3 各类集合的特点

8.3.1 Collection

8.3.2 Map

8.4 集合中元素排序

 

 

Java简介

1.1 Java体系分类

1.1.1 JavaSE,java平台标准版

是为开发普通桌面和商务应用程序提供的解决方案。该技术体系是其他两者的基础,可以完成一些桌面应用程序的开发。比如Oracle的安装界面。

1.1.2 JavaEE,java平台企业版

是为开发企业环境下的应用程序提供的一套解决方案。该技术体系中包含的技术如 Servlet JSP等,主要针对于Web应用程序开发

1.1.3 JavaME,java平台微型版

是为开发电子消费产品和嵌入式设备提供的解决方案。该技术体系主要应用于小型电子消费类产品,如手机、机顶盒、智能电视中的应用程序等。

 

1.2 Java主要特性

以下这些特性在Java没有学完情况下,理解起来恐怕有一定难度,可以先记忆这几个标题,然后在后续学习过程中再去逐步理解。

1.2.1 Java语言是面向对象的:

 Java语言提供类、接口和继承等原语,为了简单起见,只支持类之间的单继承,但支持接口之间的多继承,并支持类与接口之间的实现机制(关键字为implements),Java语言全面支持动态绑定。总之,Java语言是一个纯的面向对象程序设计语言。

1.2.2 Java语言是分布式的:

Java语言支持Internet应用的开发,在基本的Java应用编程接口中有一个网络应用编程接口(java net),它提供了用于网络应用编程的类库,包括URL、URLConnection、Socket、ServerSocket等。

1.2.3 Java语言是健壮的:

Java的强类型机制、异常处理、垃圾的自动收集等是Java程序健壮性的重要保证,Java的安全检查机制使得Java更具健壮性。

1.2.4  Java语言是安全的:

 Java通常被用在网络环境中,为此,Java提供了一个安全机制以防恶意代码的攻击。除了Java语言具有的许多安全特性以外,Java对通过网络下载的类具有一个安全防范机制(类ClassLoader),如分配不同的名字空间以防替代本地的同名类、字节代码检查,并提供安全管理机制(类SecurityManager)让Java应用设置安全哨兵。

1.2.5  Java语言是跨平台的(或称之为体系中立):

 Java程序(后缀为java的文件)在Java平台上被编译为体系结构中立的字节码格式(后缀为class的文件),然后可以在实现这个Java平台的任何系统中运行。这种途径适合于异构的网络环境和软件的分发。

1.2.6 Java语言是半编译半解释的:

如前所述,Java程序在Java平台上被编译为字节码格式,然后可以在实现这个Java平台的任何系统中运行。在运行时,Java平台中的Java解释器对这些字节码进行解释执行,执行过程中需要的类在联接阶段被载入到运行环境中。

1.3 Java在Windows下环境配置

假定JDK安装在C:\Program Files (x86)\Java\目录下

在"系统变量"中设置3项属性,JAVA_HOME,PATH,CLASSPATH(大小写无所谓),若已存在则点击"编辑",不存在则点击"新建"。

变量设置参数如下:

变量名:JAVA_HOME

变量值:C:\Program Files (x86)\Java\jdk1.8.0_91// 要根据自己的实际路径配置

变量名:CLASSPATH

变量值:.;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar; //记得前面有个"."

变量名:Path

变量值:%JAVA_HOME%\bin;

1.4 测试JDK是否安装成功

1、"开始"->"运行",键入"cmd";

2、键入命令:javajavac 两个命令,出现相关信息,则说明环境变量配置成功;

Java变量与运算符

2.1 变量

变量是指在程序的运行过程中随时可以发生变化的量,变量本质上是计算机根据程序运行需要对内存的分配方式。

2.2 常量

常量指程序里持续不变的值,在整个程序运行过程中它是不可改变的

2.3 标识符

Java中的包、类、方法、参数和变量的名称总称为标识符

2.4 标识符的命名规则

必须以字母、下划线(_)或美元符号($)开头;

余下的字符可以是下划线、美元符号或任何的字母或数字,长度不限;

标识符中不能有空格;

不能使用Java中的关键字或者保留字做为标识符;

Java是大小写敏感的,这就意味着标识符Hello与hello是不同的。

2.5 变量和常量可以使用的数据类型

 

2.5.1 整数类型

 

出错提示:

Java语言的整数类型默认为int型,声明long型后面需加上l或者L,否则字面值一旦超出int范围就会出错,由于小写字母l和数字1容易混淆,因此建议使用大写L表示。

多个Short或byte类型变量做数学运算后的结果系统默认为int型,因此当把运算结果赋值给另外一个short或byte型变量时需要使用强制转换,否则会有编译错误!

short s1=34;

short s2=100;

short s3=(short) (s1+s2);

byte b1=10;

byte b2=55;

byte b3=(byte) (b1+b2);

2.5.2 浮点数类型

 

Java浮点型常量默认为double型,如要声明一个常量为float型,则需在数字后面加f或F,如:

double d = 12345.6;

float f = 12.3f;

注意:如果float型常量后面没有加f或F,会报错,如:

float f = 12.3; X

如果声明float型变量时,赋值号右侧是整数型数值,则可以不加f,注意以下三句代码中1和1.0的区别:

float f1=1;//OK,1为int类型,int类型数值范围小于float,int可以自动转换为float

float f2=1.0;//编译错误,浮点数默认为double,double数值范围大于float

float f3=1.0f;//ok

2.5.3 字符类型

字符类型包含:char

Java字符类型有固定的长度为:16位(2字节),是一个16位无符号整数。

最小值:0, 最大值:65535 = 2^16-1

'0'与'\u0000'不是同一个字符

字符通常为:英文字母、中文字、其他国家文字、数字、转义序列、特殊字符等。

Java中的字符也可用十六进制编码形式表示,注意以下两种形式差别,例如:

int b=0x4e2d;//十六进制整数形式,打印时需要强制转换为字符

System.out.println((char)b);

char c='\u4e2d';//按Unicode字符集编码形式表示字符,可以直接打印

System.out.println(c);

2.5.4 布尔类型

boolean类型适于逻辑运算,一般用于表示真或假

boolean 数据类型只有两个值:true 和 false,Java中真或假不能转换为int型的1或0

boolean类型没有给出具体的占用字节数

Java语言表达式所操作的boolean值,在编译之后都使用Java虚拟机中的int数据类型来代替,而boolean数组将会被编码成Java虚拟机的byte数组,每个元素boolean元素占8位。

2.5.5 各种变量类型的默认值

整数型

0

浮点型

0.0

字符型

空白字符(不是空格),对应整数为0

布尔型

FALSE

引用型

null

2.6 变量类型的自动转换与强制转换

2.6.1 自动转换(隐式转换)

2.6.1.1 条件

两种类型彼此兼容;目标类型的取值范围要大于源类型

2.6.1.2 规则

boolean类型不可以转换为其他的数据类型

byte、short、char之间不会相互转换,他们三者在计算时首先会转换为int类型

整型、字符型、浮点型的数据在混合运算中相互转换,转换时遵守以下原则:

 

2.6.2 强制转换(显式转换)

2.6.2.1 条件

当两种类型彼此不兼容,或目标类型取值范围小于源类型。在强制类型转换过程中,源类型的值可能大于目标类型,因此可能造成精度降低或溢出,使用时需注意。

2.6.2.2 规则

变量 = (目标类型) 值

byte  a;

int b=10;

a = (byte) b;

2.7 运算符

2.7.1 赋值运算符

 

2.7.2 算术运算符

 

2.7.2.1 i=i++陷阱

i=i++运算过程说明:假定i=2,i=i++先把i目前数值2保存到一个临时变量里,后然i自增到3,接下来在把存在临时变量的数值2赋给i,最终i的结果是2。

2.7.2.2 /陷阱

/是计算商数值,相应的%求的是余数。

整数/整数的商数为整数,如有浮点数参与运算,则商为浮点数。

2.7.3 关系运算符

 

2.7.4 逻辑运算符

 

2.7.5 三元运算符

表达式1?表达式2:表达式3;

所谓三元运算符,是对三个表达式进行集中比较,表达式1的结果为true时,就为第二个表达式,如果为false时,就为第三个表达式。

//4 < 3表达式的结果为true和false的其中一个。

boolean n = (4 < 3) ? true : false;

//打印并显示结果为false

System.out.println(n);

2.7.6 运算符优先级

 

 

2.7.7 ==与equals的区别

== 的作用:

可以比较基本类型:比较的是值是否相同

也可以比较引用类型:比较的是地址值是否相同

比较之前需要保证左右两边操作数类型相同,或者彼此能相互自动转换,否则比较时会报错!

equals 的作用:

仅能比较引用类型:默认情况下,比较的是地址值。

可以根据情况自己重写该方法,重写后可以比较对象的属性值是否相同。

数组

3.1 一维数组声明与初始化的三种形式

形式1

int [] x=new int [3];

x[0]=1;

x[1]=2;

x[2]=3;

形式2

int [] x={1,2,3};

形式3:

int [] x=new int[]{1,2,3}

3.2 二维数组声明与初始化的三种形式

形式1

Int [][] a=new int[3][4];

a[0][0]=0;

…..

a[2][3]=12;

形式2

Int [ ][ ] a= new int [][]{ {1,2,3,4}, {5,6,7,8}, {9,10,11,12} } ;

形式3

Int [ ][ ] a= { {1,2,3,4}, {5,6,7,8}, {9,10,11,12} } ;

3.3 锯齿型二维数组声明与初始化三种形式

形式1

Int [][] a=new int[3][];

a[0][0]=0;

…..

a[2][3]=12;

形式2

Int [ ][ ] a= new int [][]{ {1,2,3,4}, {5,6,7}, {9,10} } ;

形式3

Int [ ][ ] a= { {1,2,3,4}, {5,6,7}, {9,10} } ;

错误形式

int [][] a=new int[][4];

面向对象编程

4.1 理解对象

对象是系统中用来描述客观事物的一个实体,也是构成世界的一个独立单位;对象具有自己的静态结构(属性)和动态行为(方法);每个对象有自己的唯一标识。

4.2 理解类

类是对具有相同属性和行为的同类型对象的抽象;类是创建对象的模板,对象是类的具体实例;类是抽象的,对象是具体的,根据一个类可以产生多个对象。

4.3 理解面向对象编程思想的三个层面

4.3.1 面向对象分析(OOA,Object Oriented Analysis)

通常是把业务系统分解成各个对象,给对象赋予相应的属性和行为,通过多个对象的相互协调来解决问题。

4.3.2 面向对象设计(OOD,Object Oriented Design)

根据面向对象分析的结果设定所需的类、类的操作以及类之间关联的过程

4.3.3 面向对象编程实现(OOP,Object Oriented Programming)

根据面向对象设计的结果,对类及其属性、方法进行编码实现

综上所述,面向对象编程思想实际上就是一种运用对象、类、继承、封装、聚合、关联、消息、多态性等概念来构造系统的软件开发方法。

4.4 对象创建过程

语法形式:类名 对象名 = new 类名();

创建一个对象时,同时操作了栈内存和堆内存,在栈内保存对象的首地址,即引用;在堆内存中保存了对象的属性;对对象的所有操作只能通过引用完成,一旦引用出栈释放没有任何引用指向该对象,对象就变成垃圾失效。

New关键字后边的“ 类名()”是类的构造器,通过构造器完成对象创建

4.5 理解类的构造器

所有的对象都是通过构造器来创建的,构造器名必须与类相同的名称;构造器不含返回值类型,也没有void;不能在构造器中用return语句返回一个值;构造器在声明对象时用new关键字来调用。

构造器除了可以产生对象外可以用来给对象属性赋值。

在继承条件下,子类构造器运行时先调用父类无参数构造器,如果父类未显示声明无参构造器,只有有参数构造器,则必须先调用父类有参数构造器;调用父类构造器的目的是给继承过来的父类属性赋值。

如使用构造器给对象属性赋值,在对象属性个数过多情况下,建议使用属性设置器(set方法)替代有参数构造器。

带参数构造器可以用来给对象属性赋值,但不能二次调用来修改对象属性值,如二次调用相当于创建了新对象,修改属性建议使用属性设置器(set方法)。

4.6 包、package与import

4.6.1 包的作用

包作用和文件夹类似,起到文件组织作用,防止类、接口等可能出现命名冲突,并配合访问修饰符进行访问控制,提供类、接口等搜索和定位。

在java中位于包中的类,在文件系统中的存放位置,必须有与包名层次相对应的目录结构。

4.6.2 Package

每个源文件只能声明一个包

把类放到包里的语法为:

package   包名 ;

package语句作为java源文件的第一条语句

位于同一包中的类可以直接访问并使用,不同包里的类相互访问需要使用import

4.6.3 import

使用import关键字引入其它包中的类

必须在package声明后面,类声明前面

import语句可以出现多次

import可以使用星号通配符,代表可以访问某一包里所有的类,例如import java.sql.*;但按照编码规范,不建议这样使用。

Java默认自动导入的包是java.lang

4.7 理解封装

隐藏对象的属性和方法的实现细节,只公开对外接口。封装类的属性和方法时会使用private、default、protected、public等访问权限修饰符

4.8 与封装有关的访问权限修饰符

private(私有):私有的,不对外公开,类的private成员只能被该类的成员访问。private权限最小,限制类外访问,一般把属性设为private,让其他类不能直接访问属性,达到保护的目的。

default(默认):不使用权限修饰符时(即default)的成员在类内以及在同一个包中的其他类可以访问

public(公有):公有的,最高的访问级别,public成员可以在所有其他类中访问

protected(受保护):同一个包内,各类可以彼此访问该修饰符修饰的属性和方法,但不在同一个包情况下,除子类外其他类都不能彼此访问相应的属性和方法,子类在和父类不同包情况下,虽不能直接访问对应属性和方法,但可以继承这些属性和方法。

 

4.9 理解局部变量与成员变量

在方法内部、代码块内部声明的变量称为局部变量,局部变量位于内存栈区,随方法或代码块运行而建立,随方法和代码块运行结束而从栈区消失。当局部变量和类的属性同名时,局部变量会自动隐藏类的属性,此时若访问类的属性需要使用this关键字加以区分。方法以外的代码无法访问方法内部的局部变量。

上边提到的代码块可以是if…else…语句块、循环语句块、swith语句块、静态块、实例块、try…catch…finally…语句块等,在这些语句块里声明的变量同样是局部变量。

相对局部变量而言,类的属性被称为成员变量(因为类的属性和方法都被称为类的成员),也有一些资料模仿C语言的叫法称属性为全局变量,建议使用成员变量或属性的叫法更准确。

4.10 Java方法参数传值陷阱

public class Cal {

private int number;

public int getNumber(){

return number;

}

public void changeA(int number){

//注意方法参数number这里虽然和Cal的属性number同名

//但仅仅是一个局部变量,以下代码不改变属性本身而是局部变量的自身变化

number=number+10;

}

public void changeB(int number){

//注意方法参数number这里仍然和Cal的属性number同名,还是局部变量

//但以下代码中赋值号左侧使用了this关键字,this后边跟的是Cal的number属性,赋值号右侧是传入的方法参数number

//这句代码确实改变了对象的属性值

this.number=number+10;

}

public void changeC(Cal c){

//方法参数c代表一个Cal对象的内存地址副本

//以下代码会改变该地址上的对象属性值

c.number=1000;

}

 

public Cal(int number) {

 

this.number = number;

}

public static void main(String[] args) {

Cal c1=new Cal(10);

System.out.println(c1.getNumber());//打印结果为10

c1.changeA(c1.getNumber());//把c1的属性number的副本值传递进去

System.out.println(c1.getNumber());//打印结果仍然为10

c1.changeB(c1.getNumber());//把c1的属性number的副本值传递进去

System.out.println(c1.getNumber());//打印结果为20,改变了属性值

Cal c2=new Cal(20);

c1.changeC(c2);//把c2内存地址的副本值传递进去

System.out.println(c2.getNumber());//打印结果为1000,改变了属性值

 

 

}

 

}

4.11 理解静态static

4.11.1 静态属性

所有对象共享,称为静态变量或类变量,一个对象修改了静态属性数值,其他对象共享修改后的结果

例如在单例模式中声明一个类的该类型静态属性,确保内存中只有一个对象。

需要注意static不修饰方法内部的局部变量

4.11.2 静态方法

不需声明对象可以用类名直接访问的方法,称为静态方法或类方法,常见于工具类中的方法、工厂模式中创造对象的create方法

静态方法只能访问静态属性和静态方法,不能访问实例属性和实例方法

但实例方法可以访问静态属性和静态方法。

静态方法内部可以声明以局部变量形式存在的对象实例,例如我们在主方法里声明的各种类的对象。

4.11.3 静态块

类中由static关键字修饰的,不包含在任何方法体中的代码块,称为静态代码块,例如数据库工具类中加载JDBC的数据库驱动程序的静态代码块。

4.12 垃圾回收机制

4.12.1 垃圾回收概述

当没有对象引用指向原先分配给某个对象的内存时,该内存便成为垃圾。JVM的一个系统级线程会自动释放该内存块。

垃圾回收机制作用于堆内存,与栈内存无关。

垃圾回收机制是JVM内部运行的一个优先级比较低的后台线程,自动进行垃圾回收。它是保证程序健壮的主要手段,不用程序员参与,避免了由于程序员忘记回收内存而引起的内存泄漏,同时也避免了回收内存带来的代码繁琐。

4.12.2 对象被当作垃圾回收的条件

1、对象的引用被赋值为null:

Person p = new Person( );   

p = null;

2、使用的匿名对象: new Person( ).sayHello( );

3、超出生命周期的,如:

      for( int i = 0; i< 100; i++){

Person p = new Person( );

  }

这里,创建了100个对象,循环赋值给变量p,每结束一次循环,变量p就超出生命周期,对象变为垃圾。

面向对象高级特性

5.1 面向对象编程三大特性

5.1.1 继承(inheritance):

也称泛化,继承性是子类自动共享父类属性和方法的机制,在定义和实现一个类的时候,可以在一个已经存在的类的基础之上来进行,把这个已经存在的类所定义的属性和方法作为自己的属性和方法,并加入自己若干新的属性和方法。

继承简化了人们对事物的认识和描述,有益于软件复用,是OO技术提高软件开发效率的重要原因之一,是类之间的一种关系,一般类与特殊类之间的关系。

Java属于单继承,一个子类只能有一个父类,但可以通过实现多个接口来间接弥补单继承的不足。

5.1.2 封装(encapsulation):

所谓封装是把对象的属性和行为结合在一个独立的系统单位内部,尽可能隐蔽对象的内部细节,只向外部提供接口。

封装的重要意义:使对象能够集中而完整地描述并对应一个具体事物,体现了事物的相对独立性,使对象外部不能随意存取对象的内部数据。

5.1.3 多态(polymorphism):

常指在一般类中定义的属性或方法被特殊类继承之后,可以具有不同的数据类型或表现出不同的行为,对于子类,可用不同的方法替代实现父类的服务的方法。

5.2 Super和this关键字

5.2.1 This

当声明带参数的构造器或属性设置器时,用来给属性赋值的传入形式参数名和属性同名时,可以在属性名前加this.以和形式参数区分,this代表程序运行时的本类对象。

类内部存在多个重载构造器情况下,用一个构造器调用本类其他构造器时,需要使用this关键字,并且此时this应位于构造器代码中的第一行,详细参见后边“5.4.3构造器重载”内容。

5.2.2 Super

程序运行时指向父类对象。

通过关键字super我们可以指定子类在构造时调用父类的哪个构造器,达到先实例化父类然后实例化子类的目的,利用实例化父类的过程可以将子类继承自父类的私有属性赋值。

子类的构造器默认的调用父类无参构造器,即子类构造器中没有用super指明调用父类哪个构造器的话,实际上编译器会自动的在子类构造器第一行加入代码super( );如果父类未显示声明无参构造器,只有有参数构造器,则必须先调用父类有参数构造器;

5.3 抽象类与接口

5.3.1 抽象类

抽象类是包含抽象方法的类,产生于程序设计阶段,包括了所有属于同类型的子类的共同方法和属性,由于子类之间在共同行为上可能会有不同表现形式,通常会把这种行为设计为抽象方法,由各个子类根据具体应用场景进行方法重写,同时子类可以在继承抽象类基础上增加全新的属性和方法以扩展功能。

抽象类有构造器,但不能直接生成对象,用于子类继承时调用。

5.3.2 接口

接口中是一套方法规范,是不同的类所具有的共同方法的抽象。设计类的方法时,原本需要硬编码的方法体可以通过传入同一接口的不同实现类的对象,实现方法功能实现的多样化和未来功能升级,以及开发团队分工。

一个接口可以继承另一个接口,两个接口中的全部抽象方法都交由实现类来重写。

抽象类可以“实现”多个接口(并非真的实现这些方法),相当于在抽象类中增加了多个抽象方法,最后交给子类来全部重写。

5.3.3 抽象类与接口的区别

5.3.3.1 语法层面:

抽象类本质上是类,有构造器、属性和方法,抽象类的方法既有抽象方法,也可以有具体实现的方法,属于同一类型的类才能继承同一个抽象类,而无关的类不可能有同一个抽象父类。

接口本质上不是类,没有构造器和属性,只有抽象方法和常量(JDK8.0以后接口的抽象方法可以有默认实现),无关的类可以实现同一个接口。

一个类只能继承一个抽象类,但可以实现多个接口。

5.3.3.2 设计层面:

抽象类作为很多子类的父类,它是一种模板式设计,如需要所有子类公共内容改动,则只需要抽象类中内容,不需要重新对所有子类进行改动。而接口是一种行为规范,它是一种辐射式设计,如果接口进行了变更,则所有实现这个接口的类都必须进行相应的改动。

5.4 方法重写(覆盖)与重载的区别

5.4.1 重写(覆盖)

重写(覆盖)是子类对父类的允许访问的方法的实现过程进行重新编写!返回值和形参都不能改变;子类方法的访问权限必须大于或等于父类方法的访问权限。例如:如果父类的一个方法被声明为public,那么在子类中重写该方法就不能声明为protected。

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

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

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

如果一个方法不能被继承,那么该方法不能被重写。

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

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

重写的方法能够抛出任何非强制异常,无论被重写的方法是否抛出异常。但是,重写的方法不能抛出新的强制性异常,或者比被重写方法声明的更广泛的强制性异常,反之则可以。

如果不能继承一个方法,则不能重写这个方法。

在重写方法体中,可以通过super关键字调用重写前的父类方法体。

5.4.2 重载

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

被重载的方法可以改变访问修饰符;

被重载的方法可以声明新的或更广的检查异常;

子类继承父类方法后,针对父类方法可以同时进行重载和重写

5.4.3 关于构造器重载

构造器可以重载,但不能重写!

只能在本类的构造方法中,调用其它重载的构造方法;在一个构造器中调用另外一个本类另外一个构造器时使用this关键字,并且this只能在构造方法的第一行,例如:

public class Employee{

  int age;

  public Employee(){

     this(20);

  }

  public Employee(int age,String name){

  }

  public Employee(int age){

    this.age = age;

  }

}

5.5 多态

多态是不同的对象对同一行为作出的不同响应。

多态存在的条件:要有继承父类,或实现接口;要有方法重写;需要声明父类变量指向子类对象(即向上转型)。

5.6 Final关键字

Final修饰的类不可继承,如工具类中的String、Math等。

Final修饰的方法不可重写;

Final修饰的变量均为常量,数值一旦确定不可修改。

5.7 类型转换

5.7.1 向上转型

子类转换为父类,自动转换;前提是具有继承或实现关系;向上转换损失了子类新扩展的属性和方法,仅可以使用从父类中继承的属性和方法。

Animal c = new Cat();

5.7.2 向下转型

父类对象显示转换为子类,属于强制转换。曾经向上转换过的对象,才能再向下转换。对象不允许不经过上溯造型而直接下溯造型。

如下写法是会出现运行时错误:

Person p = new Person();

Student s = (Student)p;

但以下转换可以成功

Person p=new Student();

Student s = (Student)p;

5.7.3 Instanceof

通过instanceof来判断该经过上溯转型后是哪一个子类的,以确保向下转型成功。

语法:对象名 instanceof 类名

语法:对象名 instanceof 接口名

5.8 子类对父类属性的继承与隐藏

子类会继承父类的全部属性,但子类内部一旦声明了与父类同名的属性,则子类属性会自动隐藏继承过来的同名属性。如果希望访问父类属性,则需要在子类方法内部利用super关键字访问父类属性。

异常处理

6.1 异常继承关系

Object 类的直接子类Throwable描述了所有被虚拟机抛出的非正常状况。一般情况下很少用Throwable,而是使用它的两个子类Error、Exception。

Error类特指应用程序在运行期间发生的严重错误。如:虚拟机内存用尽、堆栈溢出等等。一般情况下这种错误都是灾难性的,所以没有必要使用异常处理机制处理Error。

Exception类有几十个子类,描述了不同类型的异常,其中:

以RuntimeException为代表的一些类,称为非检查性异常(unchecked Exception),若系统运行时可能产生该类异常,则不必在程序中声明对该类异常的处理,就可以编译执行。

以IOException为代表的一些类为检查性异常(checked Exception),若系统运行时可能产生该类异常,则必须写出相应的处理代码,否则无法通过编译。

6.2 异常处理机制

使用try….catch……finally语句进行处理

catch不能独立于try存在。

在try/catch后面添加finally块并非强制性要求的。

try代码后不能既没catch块也没finally块。

try, catch, finally块之间不能添加任何代码。

Try后边可以有多个catch语句,但是catch语句中的Exception类型应按先子异常类型,再父类异常类型的顺序排列。

对应finally代码中的语句,即使try代码块和catch代码块中使用了return语句退出当前方法或般若break跳出某个循环,相关的finally代码块都有执行。

当try或catch代码块中执行了System.exit(0)时,finally代码块中的内容不被执行

工具类

7.1 Object类

Object类是所有类的超类,唯一一个没有父类的类。

一个类可以不是Object类的直接子类,但一定是Object类的子类,Java中的每一个类都是从Object扩展来的。

在Object类中定义的方法,在所有类中都可以使用,有以下三个方法常用:

public boolean equals(Object obj)

比较两个对象引用的值是否相等(比较哈希地址)

public int hashCode()

返回十进制整数,唯一标识一个对象,十六进制哈西地址的十进制形式

public String toString()

返回  类名@hashcode

7.2 hashCode()与equals()方法重写

对象放入Set或Map集合(以对象作为Map的键)之前,对象所属类必须重写hashCode()和equals()方法,否则无法在集中中正确查找到相关对象。

按照哈希契约,如果两个对象相等的话,它们的hash code必须相等(相等对象应在内存上有相同地址);但如果两个对象的hash code相等的话,这两个对象不一定相等(分配内存地址时可能有哈希冲突即将两个不同对象分配到相同地址上,不同哈希算法都在尽量减少这种冲突)。

默认情况下,hashCode()表示的是内存地址,如不重写hashCode(),Map中作为键的对象和作为查询条件的对象即便在意义上相同(比如两个表示人的对象都有相同身份证属性),但内存地址不同,所以会出现在Map中找不到对应键(对象)情况。重写hashCode()后按相应属性生成新哈希码则可以规避这个问题。

重写hashCode()后还应重写equals方法,因为按照哈希契约比较对象时是先比较hashcode(),然后再比较equals方法,默认情况下equals方法也是比较对象内存地址,故也需要按属性比较原则重写。

7.3 String、StringBuffer、StringBuilder的区别

String属于final类,代表一组不可改变的Unicode字符序列,对它的任何修改实际上又产生一个新的字符串,String类对象的内容一旦被初始化就不能再改变。

StringBuffer和StringBuilder代表一组可改变的Unicode字符序列。

StringBuilder是线程不安全的,StringBuffer是线程安全的,StringBuilder在并发操作上效率高于StringBuffer。

7.4 字符串比较

7.4.1 String比较

==比较的是字符串内存地址是否相同

String s1=”abc”;

String s2=new String(“abc”);

String s3=”abc”;

S1==s3成立,两者指向常量池中相同地址;s2==s1、s2==s3均为false,因为指向内存地址不同

按equalsIgnorecase方法,三者比较结果均相同,该方法比较的各个字符是否相同

按equals方法比较也相同,该方法继承自Object,被String类重写,也是比较各个字符是否相同,但不忽略大小写。

如果希望按照字典顺序比较字符串,则应使用str1.compareTo(str2),如果返回大于0的数,说明str1排在str2后边,如果返回小于0的数值,说明str1排在str2前边。

7.4.2 StringBuffer和StringBuilder

==比较的是内存地址

Equals比较的也是内存地址,因为未重写equals方法

需要用toString()方法转换为String后再调用String的equalsIgnorecase()方法或equals()方法

7.5 包装类、基本数值类型和字符串之间转换关系

7.5.1 基本数值类型转换为包装类

方法1:装箱

方法2:通过包装类构造器

方法3:通过包装类的静态valueOf方法

7.5.2 包装类转换为基本数值类型

方法1:拆箱

方法2:通过包装类实例方法xxxValue

7.5.3  字符串转换为包装类

方法1:通过包装类构造器

方法2:通过包装类的静态valueOf方法

7.5.4 包装类转换为字符串

利用包装类的toString()方法

7.5.5 字符串转换为基本数值类型

利用包装类的两个静态方法parseXXX()、valueOf()之一

7.6 包装类与方法重载

假定声明变量int x=10

以下有四个重载方法:

Void test(int x){

System.out.println(“int”);

}

Void test(long x){

System.out.println(“long”);

}

Void test(Integer x){

System.out.println(“Integer”);

}

Void test(Long x){

System.out.println(“Long”);

}

当调用test(10)时,会优先选择第一个方法,即打印出int,因为系统会选择参数类型最相符的那个方法,第一个恰好输入参数为int

当第一个方法不存在情况下,系统会对传入的x进行加宽处理,即选择第二个方法,打印出long。

如果第二个方法也不存在,则系统会对传入的x进行装箱处理,即选择第三个方法,打印出Integer.

如果第三个方法也不存在,则调用test(10)时会出现编译错误,因为系统传递变量x时,参数最相符的第一个方法如果不存在,则只能在参数加宽和参数装箱之间二选择一,如果这两种方法也不存在,则不允许进行先加宽再装箱的操作,所以第四个方法不会被选择。

7.7 Long型、Float型和Double型包装类对象声明陷阱

以装箱方式声明Long型和Float型包装类对象时,务必在赋值号右侧的数值后边分别加L和f,否则会报出编译错误。原因分析:

声明Long型对象时,赋值号右侧的字面值默认为int,int型的基本数值不能通过先加宽再装箱的方式转化为Long型,故需要在数值结尾加L表示这是long型,然后系统才能自动装箱。

声明Float型对象时,赋值号右侧的字面值默认为int或double,int型的基本数值不能通过先加宽再装箱的方式转化为Double型;而double型的字面值装箱后对应的包装类是Double不是Float,因此赋值号右侧的字面值末尾必须有f,表示这是一个float型基本数值,系统方能将其自动装箱为Float。

声明Double型包装类,如果赋值号右侧的字面值为int型,会报出编译错误,需要加小数点和0方可通过,因为int型的基本数值不能通过先加宽再装箱的方式转化为Double型,

Long l1=130;//错误,不能将int型字面值先加宽再装箱

Long l2=130L;//正确

long l3=130;//正确,注意本行代码不是声明包装类对象而是基本数值类型,进行了加宽处理

long l4=10000000000;//错误,字面值超出了int范围,注意本行代码不是声明包装类对象而是基本数值类型

long l5=10000000000L;//正确,处理为长整型,注意本行代码不是声明包装类对象而是基本数值类型

Float f6=1;//错误,不能将int型字面值先加宽再装箱

Float f7=1f;//正确

float f8=1;//正确,注意本行代码不是声明包装类对象而是基本数值类型,进行了加宽处理

float f9=1.0;//错误,注意本行代码不是声明包装类对象而是基本数值类型

float f10=1.0f;//正确,注意本行代码不是声明包装类对象而是基本数值类型

7.8 包装类相等比较陷阱

7.8.1 按==比较

==本质上比较的是内存地址,但需要注意Byte、Short、Integer、Long内置了字节型缓存数组(范围是-128-127),Character内置了0-127的字节型缓存数组,所以当使用装箱方式声明以上包装类对象时,只要数值不超过上述数组范围,所有声明的对象都会指向数组中的元素,这样同一类型的对象的内存地址相同,==比较结果为true。

如果数值超过了数组范围,则比较结果为false,因为包装类会在内存上重新开辟内存空间保存这些数值。

如果不使用装箱方式声明包装类对象,则相当于不使用内置缓存数组元素而是在内存上重新开辟空间,则比较对象是内存地址不同,结果为false。

Integer i5=125;

Integer i6=125;//使用了Integer内置的缓存字节型数组,i5和i6均指向数组中同一个元素,内存地址相同

Integer i7=new Integer(125);//未使用Integer内置的缓存字节型数组,在内存堆区上新建了对象

System.out.println("i5==i6"+(i5==i6));//由于使用了内置缓存字节型数组中的元素,故内存地址相同,结果为true

System.out.println("i5==i7"+(i5==i7));//i7未使用内置缓存数组,内存地址不同,结果为false

Integer i8=129;//未使用Integer内置的缓存字节型数组,在内存堆区上新建了对象

Integer i9=129;//未使用Integer内置的缓存字节型数组,在内存堆区上新建了对象

System.out.println("i8==i9"+(i8==i9));//i8和i9未使用内置缓存数组,内存地址不同,结果为false

7.8.2 按equals比较

包装类已经重写了equals方法,所以该方法实际上是先比较两个包装类对象类型是否相同,如果相同再比较包装类的内置属性值是否相同,如果都相同则返回true,否则返回false。

Integer i5=125;

Integer i6=125;

Integer i7=new Integer(125);

System.out.println(i5.equals(i6));//结果为true,两个对象类型相同,所包含的属性值也相同

System.out.println(i5.equals(i7));//结果为true,两个对象类型相同,所包含的属性值也相同

Short s7=129;

Short s8=129;

int y=129;

System.out.println("s7.equals(s8):"+s7.equals(s8));//结果为true,对象类型相同,属性值相同

System.out.println("s7.equals(y):"+s7.equals(y));//结果为false,两个对象类型不同,y装箱后为Integer,而s7为Short

7.9 日期类Calendar

7.9.1 Set方法

set(int year, int month, int date, int hourOfDay, int minute, int second)

给Calendar对象赋予年月日时分秒属性

Calendar类的set方法设置月份时,月份0-11代表1-12月

7.9.2 add(int field,int amount)方法

按照指定日历字段增加时间,其中field可以是Calendar.Year(年)、Calendar.Month(月)等各种字段,amount是相应的数值。

7.9.3 getTime()方法

利用该方法可以得到与当前日期相对应的Date对象

7.10 SimpleDateFormat

7.10.1 Format(Date d)方法

利用SimpleDateFormat类的Format(Date d)可以将日期按照指定样式格式化输出日期字符串。

7.10.2 的格式化参数:

执行格式化方法format前,需要在SimpleDateFormat的构造器中输入以下格式化参数。

 

 

7.11 把字符串解析成日期

利用DateFormat类的parse方法可以把字符串解析成日期

DateFormat df=DateFormat.getDateInstance();

Date d=df.parse(“1981-1-1”);

7.12 Math类

7.12.1  Floor(double m)方法

该方法返回小于m的最大整数值,例如floor(3.14)=3;

7.12.2  Ceil(double m)方法

该方法返回大于m最小整数值,例如ceil(3.14)=4

7.12.3  round(double m)方法

该方法等价于floor(m+0.5),无论负数还是正数,均按此处理。

例如round(4.84)等价于floor(4.84+0.5)=floor(5.34)=5,round(-3.7)=floor(-3.2)=-4,

Round(-3.5)=floor(-3.0)=-3;

7.12.4  random()方法

该方法返回值范围大于等于0,但小于1,如果用该方法产生0-n的随机整数,则需要在random()*(n+1)基础上整体强制转换,即(int)(random*(n+1)),如果乘法算式没有括号,则按从左向右计算原则,结果永远为0。

集合

8.1 集合概述

Java中集合类是用来存放对象的

集合相当于一个容器,里面包容着一组对象,因此集合被称为容器类

其中的每个对象作为集合的一个元素出现

Java API提供的集合类位于java.util包内

8.2 集合与数组区别

数组也是容器,它是定长的,访问较快,但是数组不会自动扩充

数组可以包含基本数据类型或引用类型的对象,而集合中只能包含引用类型的对象

8.3 各类集合的特点

8.3.1 Collection

8.3.1.1 List

List集合关注的是索引,List集合中的元素是有存放顺序的,元素可以重复。

ArrayList与LinkedList是List的两种具体实现,比较如下:

存储结构

ArrayList是线性顺序存储,内部结构是数组,当数组长度不够用的时候就会重新开辟一个新的数组,然后将原来的数据拷贝到新的数组内;

LinkedList对象间彼此串连起来的一个双向链表,和数据结构中链表的增删改查完全相同,而且插入是有序的;

操作性能

ArrayList适合随机查询的场合;

LinkedList元素的插入和删除操作性高;

从功能上,LinkedList要多一些。

8.3.1.2 Set

Set集合关注唯一性,它的值不允许重复,例如学生的学号,学号是不允许重复的。

另外Set中元素是无序的,是指元素加入集合中的顺序和集合内存储的顺序不同;

HashSet是Set的一种典型实现,HashSet的底层实际上是一个HashMap实例;

如果Set中存储的是用户自行定义的类的对象,则该类需要重写hashCode()和equals()方法。

遍历Set需要通过迭代器Iterator接口来完成。

8.3.2 Map

Map集合关注的是唯一的标识符(key),它将唯一的键映射到某个元素,例如学生的学号与姓名的映射,每个学号对应一个学生姓名,学号是不能重复的,但是学生的姓名可能重复。

Map中的key是唯一的,如果用put方法增加同键不同值的键值对,后者对应的value会覆盖前者的value。

如果作为键的是用户自行定义的类的对象,则该类需要重写hashCode()和equals()方法,否则无法从业务逻辑上实现键值查找。

HashMap是Map接口的一种典型实现,HashMap底层是数组和链表的混合体,数组中的每个元素都是一个链表,在插入元素的时候,首先根据key值来计算hash值h,然后计算h%1000得到一个数字,然后把该对象插入到对应的数组元素内:如:h%1000得到10,那就将对象插入到list[10]这个链表内。插入的内容是一个Node类的对象,该类使用了一对泛型元素来充当键值对。

8.4 关于HashMap的容量、元素数量、加载因子、临界值和自动扩容问题

容量:capacity,代表HashMap中可以容纳的最大键值对数量,默认为16。

元素数量:size(),代表HashMap中实际存储的键值对数量。

临界值:threhold,代表HashMap中可以实际容纳键值对数量的警戒值,一旦超过该警戒值,HashMap会自动扩容为原来两倍。

加载因子:loadfactor,临界值除以容量得到的小数,即为加载因子

自动扩容:当HashMap的键值对数量超过警戒值时,会实现自动扩容,为原来容量的2倍。

8.5 关于迭代器Iterator和ListIterator

遍历Set时可以借助迭代器完成功能。

Iterator只能单向迭代,可以使用于各种List和Set集合,另外还有一个删除集合元素的功能remove,此方法运行前需要先执行next方法

ListIterator可以双向迭代,仅适用于List类型集合,除了像Interator那样可以删除元素外,还可以执行添加元素的功能。

8.6 遍历HashMap的两种方式

第一种方式,利用HashMap的entrySet()方法得到当前集合中所有的键值对的集合Set<Entry<K,V>> se,然后再利用迭代器顺次读取各个键值对,然后利用Entry<K,V>的getKey和getValue方法读取每组键值对中相应的键和值。

第二种方式,利用HashMap的keySet()方法获取所有键的Set集合,然后利用迭代器遍历Set集合,取出所有键后再利用get方法得到对应的值。

以上两种方式可以参见课上实例代码。

 

8.7 集合中元素排序

对于List集合,所有元素排序可以使用Collections类的静态方法sort()方法进行排序,该方法是用前提是List中类型需要实现Comparable接口,Java官方定义的String、Integer等包装类都已经实现该接口,不需要程序员做额外工作,直接向Collections.sort()方法传入相应List即可。

如果List中的类是自行定义的类,希望按该类的某一个属性进行排序,如程序员自行定义的User类按年龄属性排序,则该类需要实现Comparable接口。

TreeMap和TreeSet是红黑树的具体实现,如果TreeMap的键或TreeSet存储的对象是Java官方定义的类型,如包装类、字符串等,这些官方类型已经实现了Comparable接口,所以TreeMap的键或TreeSet存储的对象可以按照自然顺序从小到大排序。

但是如果存储的是自行定义的类型的对象,则该类需要实现Comparable接口。

posted @ 2023-01-12 15:20  java小铭  阅读(114)  评论(0)    收藏  举报