JavaSE基础总结
JavaSE – JavaSE简介&JDK安装与配置
一、JavaSE体系介绍
1.1 JavaSE知识图解
1.2 JavaSE知识模块介绍
- 第一部分:计算机编程语言核心结构:
数据类型
、运算符
、流程控制
、数组
、… - 第二部分:Java面向对象核心逻辑:
类和对象
、封装
、继承
、多态
、抽象
、接口
、… - 第三部分:JavaSE核心高级应用:
集合
、I/O
、多线程
、网络编程
、反射机制
、… - 第四部分:Java新特性:
Lambda表达式
、函数式编程
、新Date/Time API
、接口的默认、静态和私有方法
、… - 第五部分:MySQL/JDBC核心技术:
SQL语句
、数据库连接池
、DBUtils
、事务管理
、批处理
、…
二、Java语言介绍
2.1 Java语言的历史发展
Java诞生于SUN(Stanford University Network),09年SUN被Oracle(甲骨文)收购。
Java之父是詹姆斯.高斯林(James Gosling)。
1996年发布JDK1.0版。
2.2 Java发布版本日期对应表
版本 | 发布日期 | 最终免费公开更新时间[3][4] | 最后延伸支持日期 |
---|---|---|---|
JDK Beta | 1995 | ? | ? |
JDK 1.0 | 1996 年 1 月 | ? | ? |
JDK 1.1 | 1997 年 2 月 | ? | ? |
J2SE 1.2 | 1998 年 12 月 | ? | ? |
J2SE 1.3 | 2000 年 5 月 | ? | ? |
J2SE 1.4 | 2002 年 2 月 | 2008 年 10 月 | 2013 年 2 月 |
J2SE 5.0 | 2004 年 9 月 | 2009 年 11 月 | 2015 年 4 月 |
Java SE 6 | 2006 年 12 月 | 2013 年 4 月 | 2018 年 12 月 |
Java SE 7 | 2011 年 7 月 | 2015 年 4 月 | 2022 年 7 月 |
Java SE 8 (LTS) | 2014 年 3 月 | Oracle 于 2019 年 1 月停止更新(商用) Oracle 于 2020 年 12 月停止更新(非商用) AdoptOpenJDK 于 2026 年 5 月或之前停止更新 Amazon Corretto 于 2023 年 6 月或之前停止更新 | 2030 年 12 月 |
Java SE 9 | 2017 年 9 月 | OpenJDK 于 2018 年 3 月停止更新 | 不适用 |
Java SE 10 | 2018 年 3 月 | OpenJDK 于 2018 年 9 月停止更新 | 不适用 |
Java SE 11 (LTS) | 2018 年 9 月 | Amazon Corretto 于 2024 年 8 月或之前停止更新 AdoptOpenJDK 于 2022 年 9 月停止更新 | 2026 年 9 月 |
Java SE 12 | 2019 年 3 月 | OpenJDK 于 2019 年 9 月停止更新 | 不适用 |
Java SE 13 | 2019 年 9 月 | OpenJDK 于 2020 年 3 月停止更新 | 不适用 |
Java SE 14 | 2020 年 3 月 | OpenJDK 于 2020 年 9 月停止更新 | 不适用 |
2.3 计算机语言排行榜
2.4 Java技术体系平台
- JavaSE(Java Platform, Standard
Edition标准版):允许您在桌面和服务器上开发和部署Java应用程序。Java提供了丰富的用户界面、性能、多功能性、可移植性和当今应用程序所需的安全性。 - JavaEE(Java Platform, Enterprise Edition企业版):是为开发企业环境下的应用程序提供的一套解决方案,主要针对于Web应用程序开发。
- JavaME(Java Platform, Micro Edition 小型版):为互联网上的嵌入式和移动设备上运行的应用提供了一个健壮、灵活的环境:微控制器、传感器、网关、移动电话、个人数字助理(PDA)、电视机顶盒、打印机等等。JavaME包括灵活的用户界面、健壮的安全性、内置的网络协议,以及支持动态下载的网络和离线应用程序。基于JavaME的应用程序在许多设备上都是可移植的,但是利用了每个设备的本机功能。
- Java Embedded: 用于解锁物联网的智能设备的价值:
通过远程市场更新和刷新功能延长产品生命周期和价值;
利用Java的可伸缩性、健壮性、可移植性和全套功能,提高生产效率,降低成本,缩短上市时间;
在边缘启用快速数据功能; - Java Card:使安全元件(如智能卡和其他防篡改安全芯片)能够承载采用Java技术的应用程序。Java card提供了一个安全的、可互操作的执行平台,它可以在一个资源受限的设备上存储和更新多个应用程序,同时保持最高的认证级别和与标准的兼容性。
- Java TV:是一种基于JavaME的技术,它为开发在tv和机顶盒设备上运行的java应用程序提供了一个性能良好、安全且易于实现的解决方案。使用JavaTV运行时,开发人员可以轻松创建应用程序,例如电子节目指南(EPG)、视频点播(VOD)客户端、游戏和教育应用程序、用于访问Internet数据的应用程序(例如天气、新闻播报器、社交网络)以及大多数蓝光光盘标题上的用户界面和奖金内容。
- Java Embedded: 用于解锁物联网的智能设备的价值:
三、Java语言跨平台原理
3.1Java语言的特点
- 完全面向对象:Java语言支持封装、继承、多态,面向对象编程,让程序更好达到
高内聚
,低耦合
的标准。 - 支持分布式:Java语言支持Internet应用的开发,在基本的Java应用编程接口中有一个网络应用编程接口(java net),它提供了用于网络应用编程的类库,包括URL、URLConnection、Socket、ServerSocket等。Java的RMI(远程方法激活)机制也是开发分布式应用的重要手段。
- 健壮型:Java的强类型机制、异常处理、垃圾的自动收集等是Java程序健壮性的重要保证。对指针的丢弃是Java的明智选择。
-安全 Java通常被用在网络环境中,为此,Java提供了一个安全机制以防恶意代码的攻击。如:安全防范机制(类ClassLoader),如分配不同的名字空间以防替代本地的同名类、字节代码检查。 - 跨平台性Java程序(后缀为java的文件)在Java平台上被编译为体系结构中立的字节码格式(后缀为class的文件),然后可以在实现这个Java平台的任何系统中运行。
3.2 Java语言的跨平台原理
-
跨平台:任何软件的运行,都必须要运行在操作系统之上,而我们用Java编写的软件可以运行在任何的操作系统上,这个特性称为Java语言的跨平台特性。该特性是由JVM实现的,我们编写的程序运行在JVM上,而JVM运行在操作系统上。
-
JVM(Java Virtual Machine ):Java虚拟机,简称JVM,是运行所有Java程序的假想计算机,是Java程序的运行环境之一,也是Java 最具吸引力的特性之一。我们编写的Java代码,都运行在JVM 之上。
如图所示,Java的虚拟机本身是不具备跨平台功能的,每个操作系统下都有不同版本的虚拟机。- JRE (Java Runtime Environment) :是Java程序的运行时环境,包含
JVM
和运行时所需要的核心类库
。 - JDK (Java Development Kit):是Java程序开发工具包,包含
JRE
和开发人员使用的工具。
- JRE (Java Runtime Environment) :是Java程序的运行时环境,包含
开发一个全新的Java程序,那么必须安装JDK
,其内部包含JRE
四、 JDK安装&环境配置
4.1 JDK的安装
-
双击
jdk-8u202-windows-x64.exe
文件,并单击下一步
,如图所示:
-
取消独立JRE的安装,单击
公共JRE前的下拉列表
,选择此功能将不可用
如图所示:
-
修改安装路径,单击更改,如图所示:
-
将安装路径修改为
D:\develop\Java\jdk1.8.0_202\
,并单击确定,如图所示:
-
单击下一步,如图所示:
-
稍后几秒,安装完成,如图所示:
-
目录结构,如图所示:
4.2 配置环境变量(配置JAVA_HOME+path)
为什么配置path?
希望在命令行使用javac.exe等工具时,任意目录下都可以找到这个工具所在的目录。
步骤
-
打开桌面上的计算机,进入后在左侧找到
计算机
,单击鼠标右键
,选择属性
,如图所示:
-
选择
高级系统设置
,如图所示:
-
在
高级
选项卡,单击环境变量
,如图所示:
-
在
系统变量
中,单击新建
,创建新的环境变量,如图所示:
-
变量名输入
JAVA_HOME
,变量值输入D:\develop\Java\jdk1.8.0_202
,并单击确定
,如图所示:
-
选中
Path
环境变量,双击
或者点击编辑
,如图所示:
-
在变量值的最前面,键入
%JAVA_HOME%\bin;
分号必须要写,而且还要是英文符号。如图所示:
-
环境变量配置完成,重新开启DOS命令行,在任意目录下输入
javac
命令,运行成功。
JavaSE基础 – DOS命令
一、什么是DOS?
Java语言的初学者,学习一些DOS命令,会非常有帮助。DOS是一个早期的操作系统,现在已经被Windows系统取代,对于我们开发人员,目前需要在DOS中完成一些事情,因此就需要掌握一些必要的命令。。二、进入DOS操作窗口
-
按下
Windows+R
键,打开运行窗口,输入cmd
回车,进入到DOS的操作窗口。
-
打开DOS命令行后,看到一个路径
C:\Users\...
就表示我们现在操作的磁盘是C盘的Users的final目录。
三、常用命令
查看当前目录下有什么 输入命令:dir
进入目录命令:cd
(1)回到根目录
cd / 或 cd
(2)切换到上一级
cd ..
(3)当前盘的其他目录下
绝对路径:从根目录开始定位,例如:cd d:\test200\1 或者 cd d:/test200/1
相对路径:从当前目录开始定位,例如:…\…\…\test200\1 或者 …/…/…/test200/1
例如:现在在d:/test100/hello/a目录,要切换到d:/test200/1目录
切换盘符命令
(1)直接盘符:
例如:要切换到D盘,直接d:
(2)使用cd命令
例如:要切换到E盘,可以使用cd /D e:
使用 /D 开关,除了改变驱动器的当前目录之外,还可改变当前驱动器。
新建目录命令:md (make directory)
//在当前目录下创建hello文件夹
md hello
//在当前目录下创建a,b,c三个文件夹
md a b c
//在d盘test200下创建ok200文件夹
md d:\test200\ok200
新建空文件命令:type nul
type nul > 文件名.扩展名
追加内容到文件命令:echo
echo 内容 > 文件名.扩展名
*复制(copy)或移动(move)文件
copy 源文件 目标目录\新文件名.扩展名
move 源文件 目标目录
删除文件命令:del
//删除指定文件
del 文件名.扩展名
del 目标目录\文件名.扩展名
删除所有文件并询问
del .
删除所有文件不询问
del /Q .
删除目录命令:rd(remove directory)
//删除空目录
rd 空目录名
//删除目录以及下面的子目录和文件,带询问
rd /S 非空目录名
//删除目录以及下面的子目录和文件,不带询问
rd /S/Q 非空目录名
查看某个目录的下一级目录结构:tree
tree 目录
tree /F 目录
/F 显示每个文件夹中文件的名称。
清屏命令:cls
cls
退出命令:exit
exit
JavaSE --入门
一、 程序开发步骤说明
Java程序开发三步骤:编写、编译、运行。
二、编写Java源程序
2.1 编写 HelloWorld
- 在
D:\atguigu\javaee\JavaSE20190624\code\day01_code
目录下新建文本文件,完整的文件名修改为HelloWorld.java
,其中文件名为HelloWorld
,后缀名必须为.java
。 - 用记事本或notepad++等文本编辑器打开。
- 在文件中键入文本并保存,代码如下:
public class HelloWorld {
public static void main(String[] args) {
System.out.println("HelloWorld");
}
}
友情提示:
每个字母和符号必须与示例代码一模一样。(具体原因请往后看)
第一个HelloWord
源程序就编写完成了,但是这个文件是程序员编写的,JVM是看不懂的,也就不能运行,因此我们必须将编写好的Java源文件
编译成JVM可以看懂的字节码文件
,也就是.class
文件。
2.2 编译源文件
在DOS命令行中,进入D:\atguigu\javaee\JavaSE20190624\code\day01_code
目录,使用javac
命令进行编译。
命令
javac Java源文件名.后缀名
注意: 此处编译必须要加上文件的后缀名
举例:
javac HelloWorld.java
编译成功后,命令行没有任何提示。打开D:\atguigu\javaee\JavaSE20190624\code\day01_code
目录,发现产生了一个新的文件 HelloWorld.class
,该文件就是编译后的文件,是Java的可运行文件,称为字节码文件,有了字节码文件,就可以运行程序了。
Java源文件的编译工具`javac.exe
2.3 运行Java程序
在DOS命令行中,进入Java源文件的目录,使用java
命令进行运行。
命令:
java 类名字
举例:
java HelloWorld
注意事项:
java HelloWord 注意此处不要加文件的后缀名
Java字节码文件的运行工具:java.exe
三、小常识类
3.1 常见错误
- Java是一门严格区分大小写的语言
- 不能用中文符号,英文半角的标点符号(正确)
- 括号问题,成对出现
3.2 Java程序的格式
结构:
类{
方法{
语句;
}
}
格式:
(1)每一级缩进一个Tab键
(2){}的左半部分在行尾,右半部分单独一行,与和它成对的"{"的行首对齐
3.3 Java程序的入口
Java程序的入口是main方法
public static void main(String[] args){
}
3.4 编写Java程序时应该注意的问题
- 字符编码问题
当cmd命令行窗口的字符编码与.java源文件的字符编码不一致,如何解决?
解决方案一:
在Notepad++等编辑器中,修改源文件的字符编码
解决方案二:
在使用javac命令式,可以指定源文件的字符编码
javac -encoding utf-8 Review01.java
-
大小写问题
(1)源文件名:不区分大小写,我们建议大家还是区分
(2)字节码文件名与类名
区分大小写
(3)代码中:
区分大小写
3.4 标识符
1.什么是标识符
给类、变量、方法、包等命名的字符序列,称为标识符。
2.标识符的命名规则
a.Java的标识符只能使用26个英文字母大小写,0-9的数字,下划线_,美元符号$
b.不能使用Java的关键字(包含保留字)和特殊值
c.数字不能开头
d.不能包含空格
e.严格区分大小写
3. 标识符的命名规范
a.见名知意
b.类名、接口名等:每个单词的首字母都大写,形式:XxxYyyZzz,
c.变量、方法名等:从第二个单词开始首字母大写,其余字母小写,形式:xxxYyyZzz,
d.包名等:每一个单词都小写,单词之间使用点.分割,形式:xxx.yyy.zzz,
e.常量名等:每一个单词都大写,单词之间使用下划线_分割,形式:XXX_YYY_ZZZ
四 数据类型
1. 数据类型分类
基本数据类型:包括 整数
、浮点数
、字符
、布尔
。
** 引用数据类型**:包括 类
、数组
、接口
。
2.基本数据类型
2.1 四类八种基本数据类型:
Java中的默认类型:整数类型是int
、浮点类型是double
2.2 常量(constant)
常量:在程序执行的过程中,其值不可以发生改变的量
3.变量
3.1 变量的概念
变量:在程序执行的过程中,其值可以发生改变的量
变量的作用:用来存储数据,代表内存的一块存储区域,这块内存中的值是可以改变的。
3.2 变量的三要素
1、数据类型 2、变量名 3、值
3.3 使用注意事项
a. 先声明后使用(如果没有声明,会报“找不到符号”错误)
b. 在使用之前必须初始化(如果没有初始化,会报“未初始化”错误)
c.变量有作用域(如果超过作用域,也会报“找不到符号”错误)
d.在同一个作用域中不能重名
4. 输出语句
换行输出语句**:输出内容,完毕后进行换行,格式如下:
System.out.println(输出内容);
直接输出语句**:输出内容,完毕后不做任何处理,格式如下:
System.out.print(输出内容);
5. 进制
5.1 进制的分类
a.十进制: 数字组成:0-9
进位规则:逢十进一
b.二进制:数字组成:0-1
进位规则:逢二进一
c.八进制:数字组成:0-7
>进位规则:逢八进一
d.十六进制: 数字组成:0-9,a-f
与二进制换算规则:每四位二进制是一位十六进制值
5.2 进制的换算
5.3 在代码中如何表示四种进制的值
(1)十进制:正常表示
System.out.println(10);
(2)二进制:0b或0B开头
System.out.println(0B10);
(3)八进制:0开头
System.out.println(010);
(4)十六进制:0x或0X开头
System.out.println(0X10);
5. 计算机存储单位
6.1 计算机存储单位
- 字节(Byte):**是计算机信息技术用于计量存储容量的一种计量单位,一字节等于八位。
- (bit):**是数据存储的最小单位。也就是二进制。二进制数系统中,每个0或1就是一个位,叫做bit(比特),其中8 bit 就称为一个字节(Byte)。
- **转换关系:
- 8 bit = 1 Byte
- 1024 Byte = 1 KB
- 1024 KB = 1 MB
- 1024 MB = 1 GB
- 1024 GB = 1 TB
6.2 Java的基本数据类型的存储范围
1、整型系列
(1)byte:字节类型
- 占内存:1个字节
- 存储范围:-128~127
(2)short:短整型类型
- 占内存:2个字节
- 存储范围:-32768~32767
(3)int:整型
- 占内存:4个字节
- 存储范围:-2的31次方 ~ 2的31次方-1
(4)long:整型
-
占内存:8个字节
-
存储范围:-2的63次方 ~ 2的63次方-1
2、浮点型系列(小数)
(1)float:单精度浮点型 -
占内存:4个字节
-
精度:科学记数法的小数点后6~7位
注意:如果要表示某个常量小数是float类型,那么需要在数字后面加F或f,否则就是double类型
(2)double:双精度浮点型
- 占内存:8个字节
- 精度:科学记数法的小数点后15~16位
3、单字符类型:char
占内存:2个字节
4、布尔类型
boolean:只能存储true或false
6.基本数据类型转换
6.1 自动类型转换(隐式类型转换)
将取值范围小的类型
自动提升为取值范围大的类型
。
6.2 强制类型转换(显示类型转换)
将取值范围大的类型
强制转换成取值范围小的类型
。
注意:
自动转换是Java自动执行的,而强制转换需要我们自己手动执行。
6.3 特殊的数据类型转换
- 任意数据类型的数据与String类型进行“+”运算时,结果一定是String类型
- 但是String类型不能通过强制类型()转换,转为其他的类型
五.运算符
5.1 概念
- 运算符的分类:
按照功能分:算术运算符、赋值运算符、比较运算符、逻辑运算、条件运算符… - 按照操作数个数分:一元运算符(单目运算符)、二元运算符(双目运算符)、三元运算符 (三目运算符)
5.2 算术运算符
注意:
- ++在前,先自加,后使用;
- ++在后,先使用,后自加。
5.3 赋值运算符
所有的赋值运算符的=左边一定是一个变量
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210321161818755.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L20wXzUyNTQ1MTU5,size_16,color_FFFFFF,t_70)
5.4 关系运算符/比较运算符
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210321161848574.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L20wXzUyNTQ1MTU5,size_16,color_FFFFFF,t_70)
小结
-比较运算符,是两个数据之间进行比较的运算,运算结果一定是boolean值
true
或者false
-其中>,<,>=,<=不支持boolean,String类型,==和!=支持boolean和String
5.5 逻辑运算符
a. 逻辑运算符,是用来连接两个布尔类型结果的运算符(`!`除外),运算结果一定是boolean值`true`或者`false`
b.&&和&区别,||和|区别
- &&
和
&结果一样,
&&有短路效果,左边为false,右边不执行;
&`左边无论是什么,右边都会执行。 - ||
和
|结果一样,
||有短路效果,左边为true,右边不执行;
|`左边无论是什么,右边都会执行。
5.6 条件运算符
a. 条件运算符格式:
条件表达式?结果1:结果2
b.条件运算符计算方式:
条件判断的结果是true,条件运算符整体结果为结果1,赋值给变量。
判断条件的结果是false,条件运算符整体结果为结果2,赋值给变量。
5.7 位运算符
5.8 运算符优先级
算术->位–>比较–>逻辑–>三元–>赋值
六. 流程控制语句
6.1 概念
不论哪一种编程语言,都会提供两种基本的流程控制结构:分支结构和循环结构。其中分支结构用于实现根据条件来选择性地执行某段代码,循环结构则用于实现根据循环条件重复执行某段代码
6.2 顺序结构
顺序结构就是程序从上到下逐行地执行,中间没有任何判断和跳转
6.2.1 分支结构:if语句第一种格式
1.格式
- if语句第一种格式: if
if(条件表达式){
语句体;
}
2.执行流程
-
首先判断条件表达式看其结果是true还是false
-
如果是true就执行语句体
-
如果是false就不执行语句体
6.2.2 分支结构:if语句第一种格式
1.格式
if(关系表达式) {
语句体1;
}else {
语句体2;
}
2.执行流程
- 首先判断关系表达式看其结果是true还是false
- 如果是true就执行语句体1
- 如果是false就执行语句体2
6.2.3 分支结构:if语句第三种格式
1.格式
if (判断条件1) {
执行语句1;
} else if (判断条件2) {
执行语句2;
}
...
}else if (判断条件n) {
执行语句n;
} else {
执行语句n+1;
}
2.执行流程
- 首先判断关系表达式1看其结果是true还是false
- 如果是true就执行语句体1,然后结束当前多分支
- 如果是false就继续判断关系表达式2看其结果是true还是false
- 如果是true就执行语句体2,然后结束当前多分支
- 如果是false就继续判断关系表达式…看其结果是true还是false
- …
- 如果没有任何关系表达式为true,就执行语句体n+1,然后结束当前多分支。
6.2.4 分支结构:if…else嵌套
a.概念
在if的语句块中,或者是在else语句块中,
又包含了另外一个条件判断(可以是单分支、双分支、多分支)
b.执行的特点:
(1)如果是嵌套在if语句块中的
只有当外部的if条件满足,才会去判断内部的条件
(2)如果是嵌套在else语句块中的
只有当外部的if条件不满足,进入else后,才会去判断内部的条件
6.2.5 分支结构:switch选择结构
a.语法格式
switch(表达式){
case 常量值1:
语句块1;
【break;】
case 常量值2:
语句块2;
【break;】
。。。
【default:
语句块n+1;
【break;】
】
}
b.执行过程:
(1)入口
①当switch(表达式)的值与case后面的某个常量值匹配,就从这个case进入;
②当switch(表达式)的值与case后面的所有常量值都不匹配,寻找default分支进入;不管default在哪里
(2)一旦从“入口”进入switch,就会顺序往下执行,直到遇到“出口”,即可能发生贯穿
(3)出口
①自然出口:遇到了switch的结束}
②中断出口:遇到了break等
注意:
(1)switch(表达式)的值的类型,只能是:4种基本数据类型(byte,short,int,char),两种引用数据类型(JDK1.5之后枚举、JDK1.7之后String)
(2)case后面必须是常量值,而且不能重复
6.2.6 循环结构:while循环
- .while循环语句标准格式:
while (循环条件语句①) {
循环体语句②;
}
while(true){
循环体语句;//如果此时循环体中没有跳出循环的语句,也是死循环
}
流程
- 第一步:执行循环条件语句①,看循环条件语句的值是true,还是false;
- 如果是true,执行第二步;
- 如果是false,循环语句中止,循环不再执行。
- 第二步:执行循环体语句②;
- 第三步:循环体语句执行完后,重新从第一步开始再执行一遍
2.while循环语句扩展格式:
初始化语句①;
while (循环条件语句②) {
循环体语句③;
迭代语句④;
}
执行流程:
- 第一步:执行初始化语句①,完成循环变量的初始化;
- 第二步:执行循环条件语句②,看循环条件语句的值是true,还是false;
- 如果是true,执行第三步;
- 如果是false,循环语句中止,循环不再执行。
- 第三步:执行循环体语句③
- 第四步:执行迭代语句④,针对循环变量重新赋值
- 第五步:根据循环变量的新值,重新从第二步开始再执行一遍
6.2.7 循环结构:do…while循环
1、do…while循环语句标准格式:
do {
循环体语句①;
} while (循环条件语句②);
执行流程:
- 第一步:执行循环体语句①;
- 第二步:执行循环条件语句②,看循环条件语句的值是true,还是false;
- 如果是true,执行第三步;
- 如果是false,循环语句终止,循环不再执行。
- 第三步:循环条件语句执行完后,重新从第一步开始再执行一遍
2.do…while循环语句扩展格式:
初始化语句①
do {
循环体语句②;
迭代语句③;
} while (循环条件语句④);
流程:
- 第一步:执行初始化语句①,完成循环变量的初始化;
- 第二步:执行循环体语句②;
- 第三步:执行迭代语句③,针对循环变量重新赋值;
- 第四步:执行循环条件语句④,看循环条件语句的值是true,还是false;
- 如果是true,根据循环变量的新值,重新从第二步开始再执行一遍;
- 如果是false,循环语句中止,循环不再执行。
6.2.8 循环语句:for循环
1、for循环语句格式:
for(初始化语句①; 循环条件语句②; 迭代语句④){
循环体语句③
}
for(;;){
循环体语句块;//如果循环体中没有跳出循环体的语句,那么就是死循环
}
执行流程
- 第一步:执行初始化语句①,完成循环变量的初始化;
- 第二步:执行循环条件语句②,看循环条件语句的值是true,还是false;
- 如果是true,执行第三步;
- 如果是false,循环语句中止,循环不再执行。
- 第三步:执行循环体语句③
- 第四步:执行迭代语句④,针对循环变量重新赋值
- 第五步:根据循环变量的新值,重新从第二步开始再执行一遍
6.2.9 循环语句的区别
- 从循环次数角度分析
- do…while循环至少执行一次循环体语句
- for和while循环先循环条件语句是否成立,然后决定是否执行循环体,至少执行零次循环体语句
- 从循环变量的生命周期角度分析
- for循环的循环变量在for()中声明的,在循环语句结束后,不可以被访问;
- while和do…while循环的循环变量因为在外面声明的,所以while和do…while结束后可以被继续使用的;
- 如何选择
- 遍历有明显的循环次数(范围)的需求,选择for循环
- 遍历没有明显的循环次数(范围)的需求,循环while循环
- 如果循环体语句块至少执行一次,可以考虑使用do…while循环
- 本质上:三种循环之间是可以互相转换的,都能实现循环的功能
- 三种循环结构都具有四要素:
- (1)循环变量的初始化表达式
- (2)循环条件
- (3)循环变量的修改的迭代表达式
- (4)循环体语句块
6.3 控制语句
6.3.1 break
1.使用场景:终止switch或者当前循环
- 在选择结构switch语句中
- 在循环语句中
- 离开使用场景的存在是没有意义的
2.continue
使用场景:结束本次循环,继续下一次的循环
3. 嵌套循环
所谓嵌套循环**,是指一个循环的循环体是另一个循环。比如for循环里面还有一个for循环,就是嵌套循环。总共的循环次数=外循环次数*内循环次数。当然可以是三种循环任意互相嵌套。
for(初始化语句①; 循环条件语句②; 迭代语句⑦) {
for(初始化语句③; 循环条件语句④; 迭代语句⑥) {
循环体语句⑤;
}
}
七.数组
7.1 数组的概念
数组概念: 数组就是用于存储数据的长度固定的容器,保证多个数据的数据类型要一致。
数组的特点:
1、数组的长度一旦确定就不能修改
2、创建数组时会在内存中开辟一整块连续的空间。
3、存取元素的速度快,因为可以通过[下标],直接定位到任意一个元素。
7.2数组的声明与初始化
7.2.1 方式一:静态初始化
- 格式
数据类型[] 数组名 = new 数据类型[]{元素1,元素2,元素3...};
或
数据类型[] 数组名;
数组名 = new 数据类型[]{元素1,元素2,元素3...};
2.举例
int[] arr = new int[]{1,2,3,4,5};//正确
int[] arr;
arr = new int[]{1,2,3,4,5};//正确
7.2.2 方式三:动态初始化
1.格式:
数组存储的元素的数据类型[] 数组名字 = new 数组存储的元素的数据类型[长度];
或
数组存储的数据类型[] 数组名字;
数组名字 = new 数组存储的数据类型[长度];
- 数组定义格式详解:
- 数组存储的元素的数据类型: 创建的数组容器可以存储什么数据类型的数据。
- 元素的类型可以是任意的Java的数据类型。例如:int, String, Student等
- [] : 表示数组。
- 数组名字:为定义的数组起个变量名,满足标识符规范,可以使用名字操作数组。
- new:关键字,创建数组使用的关键字。因为数组本身是引用数据类型,所以要用new创建数组对象。
- [长度]:数组的长度,表示数组容器中可以存储多少个元素。
- 注意:数组有定长特性,长度一旦指定,不可更改。
- 和水杯道理相同,买了一个2升的水杯,总容量就是2升,不能多也不能少。
7.3 数组元素的访问
-
索引:** 每一个存储到数组的元素,都会自动的拥有一个编号,从0开始,这个自动编号称为数组索引(index),可以通过数组的索引访问到数组中的元素。
-
索引范围:[0, 数组的长度-1]
-
索引访问数组中的元素:**
-
数组名[索引]=数值,为数组中的元素赋值
-
变量=数组名[索引],获取出数组中的元素
7.4 数组的遍历
- 数组的长度属性: 每个数组都具有长度,而且是固定的,Java中赋予了数组的一个属性,可以获取到数组的长度,语句为:数组名.length
,属性length的执行结果是数组的长度,int类型结果。由次可以推断出,数组的最大索引值为数组名.length-1
。
- 数组遍历: 就是将数组中的每个元素分别获取出来,就是遍历。遍历也是数组操作中的基石。
public static void main(String[] args) {
int[] arr = new int[]{1,2,3,4,5};
//打印数组的属性,输出结果是5```
System.out.println("数组的长度:" + arr.length);
//遍历输出数组中的元素
System.out.println("数组的元素有:");
for(int i=0; i<arr.length; i++){
System.out.println(arr[i]);
}
}
7.5 数组元素的默认值
7.6 数组内存图
7.6.1 内存概述
内存是计算机中重要的部件之一,它是与CPU进行沟通的桥梁。其作用是用于暂时存放CPU中的运算数据,以及与硬盘等外部存储器交换的数据。
7.6.2 Java虚拟机的内存划分
八.面向对象基础
8.1 面向对象思想概述
1、概述
Java语言是一种面向对象的程序设计语言,而面向对象思想(OOP)是一种程序设计思想,我们在面向对象思想的指引下,使用Java语言去设计、开发计算机程序。
2、面向对象与面向过程的区别
面向过程:POP: Process-Oriented Programming
以函数(方法)为最小单位
数据独立于函数之外
以过程,步骤为主,考虑怎么做
面向对象:OOP: Object Oriented Programming
以类/对象为最小单位,类包括:数据+方法
以对象(谁)为主,考虑谁来做,谁能做
面向对象仍然包含面向过程,只不过关注点变了,关注谁来做
程序员的角色:
面向过程:程序员是具体执行者
面向对象:程序员是指挥者
面向对象思想是一种更符合我们思考习惯的思想,它可以将复杂的事情简单化,并将我们从执行者变成了指挥者。
3、面向对象的基本特征
面向对象的语言中,包含了三大基本特征,即封装、继承和多态。
8. 2 类和对象
8.2.1 什么是类
- 类:是一类具有相同特性的事物的抽象描述,是一组相关属性和行为的集合。可以看成是一类事物的模板,使用事物的属性特征和行为特征来描述该类事物。
-
- 属性**:就是该事物的状态信息。
- 行为:就是该事物能够做什么。
8.2.2 什么是对象
- 对象**:是一类事物的具体体现。对象是类的一个实例(对象并不是找个女朋友),必然具备该类事物的属性和行为。
8.2.3类与对象的关系
- 类是对一类事物的描述,是抽象的。
- 对象是一类事物的实例,是具体的。
- 类是对象的模板,对象是类的实体。
8.3 类的定义和对象的创建
8.3.1 类的定义格式
public class ClassName {
//成员变量
//成员方法
}
- 定义类**:就是定义类的成员,包括成员变量和成员方法。
- 成员变量:和以前定义变量几乎是一样的。只不过位置发生了改变。在类中,方法外。
- 成员方法:和以前写的main方法格式类似。只不过功能和形式更丰富了。在类中,方法外。
public class Person {
//成员变量
String name;//姓名
int age;//年龄
boolean isMarried;
public void walk(){
System.out.println("人走路...");
}
public String display(){
return "名字是:" + name + ",年龄是:" + age + ",Married:" + isMarried;
}
}
8.3.2 对象的创建
new 类名()//也称为匿名对象
//给创建的对象命名
//或者说,把创建的对象用一个引用数据类型的变量保存起来
类名 对象名 = new 类名();
示例
class Student{
}
public class TestStudent{
//Java程序的入口
public static void main(String[] args){
System.out.println(new Student());//Student@7852e922
Student stu = new Student();
System.out.println(stu);//Student@4e25154f
int[] arr = new int[5];
System.out.println(arr);//[I@70dea4e
}
}
8.4 成员变量
8.4.1 声明成员变量
【修饰符】 class 类名{
【修饰符】 数据类型 属性名; //属性有默认值
【修饰符】 数据类型 属性名 = 值; //属性有初始值
}
8.4.2 成员变量默认值
8.5 成员方法
8.5.1 方法的概念
方法也叫函数,是一个独立功能的定义,是一个类中最基本的功能单元。
把一个功能封装为方法的目的是,可以实现代码重用,从而简少代码量。
8.5.2 方法的原则
方法的使用原则:
(1)必须先声明后使用
类,变量,方法等都要先声明后使用
(2)不调用不执行,调用一次执行一次。
8.5.3 成员方法的分类
成员方法分为两类:
- 实例方法:没有static修饰的方法,必须通过实例对象来调用。
- 静态方法:有static修饰的方法,也叫类方法,可以由类名来调用。
8.5.4 声明方法
【修饰符】 返回值类型 方法名(【参数列表:参数类型1 参数名1,参数类型2 参数名, ...... 】){
方法体;
【return 返回值;】
}
- 返回值类型: 表示方法运行的结果的数据类型,方法执行后将结果返回到调用者
- 基本数据类型
- 引用数据类型
- 无返回值类型:void
- 方法名:给方法起一个名字,见名知意,能准确代表该方法功能的名字
- 参数列表:方法内部需要用到其他方法中的数据,需要通过参数传递的形式将数据传递过来,可以是基本数据类型、引用数据类型、也可以没有参数,什么都不写
- 方法体:特定功能代码
- return:结束方法,并将方法的结果返回去,
8.5.5 如何在其他类中调用方法
(1)实例方法
对象名.普通方法(【实参列表】) //必须通过对象来访问
(2)类方法
类名.类方法(【实参列表】) //推荐
8.5.6 形参和实参
- 形参:在定义方法时方法名后面括号中声明的变量称为形式参数(简称形参)即形参出现在方法定义时。
- 实参:调用方法时方法名后面括号中的使用的值/变量/表达式称为实际参数(简称实参)即实参出现在方法调用时。
8.5.7 方法的参数传递机制
方法的参数传递机制:实参给形参赋值
- 方法的形参是基本数据类型时,形参值的改变不会影响实参;
- 方法的形参是引用数据类型时,形参地址值的改变不会影响实参,但是形参地址值里面的数据的改变会影响实参,例如,修改数组元素的值,或修改对象的属性值。
8.5.8 成员变量与局部变量的区别
1、声明位置和方式
(1)静态变量:在类中方法外,并且有static修饰
(2)实例变量:在类中方法外,没有static修饰
(3)局部变量:在方法体{}中或方法的形参列表、代码块中
2、在内存中存储的位置不同
(1)静态变量:方法区
(2)实例变量:堆
(3)局部变量:栈
3、生命周期
(1)静态变量:和类的生命周期一样,因为它的值是该类所有对象共享的,早于对象的创建而存在。
(2)实例变量:和对象的生命周期一样,随着对象的创建而存在,随着对象被GC回收而消亡,
而且每一个对象的实例变量是独立的。
(3)局部变量:和方法调用的生命周期一样,每一次方法被调用而在存在,随着方法执行的结束而消亡,
而且每一次方法调用都是独立。
4、作用域
(1)静态变量和实例变量:不谈作用域
在本类中,唯一的限制,静态方法或静态代码块中不能使用非静态的,其他都可以直接使用。
在其他类中,能不能使用看修饰符(public,protected,private等)
(2)局部变量:有作用域
出了作用域就不能使用
5、修饰符(后面来讲)
(1)静态变量:很多
public,protected,private,final,volatile等,一定有的是static
(2)实例变量
public,protected,private,final,volatile,transient等
(3)局部变量
final
public,protected,private:权限修饰符
final:是否是常量,即值是否可以修改
volatile:和多线程有关
transient:是否序列化,和IO有关
6、默认值
(1)静态变量:有默认值
(2)实例变量:有默认值
(3)局部变量:没有,必须初始化
其中的形参比较特殊,靠实参给它初始化。
8.5.8 方法重载
- 方法重载**:指在同一个类中,允许存在一个以上的同名方法,只要它们的参数列表不同即可,与修饰符和返回值类型无关。
- 参数列表:数据类型个数不同,数据类型不同,数据类型顺序不同。
- 重载方法调用:JVM通过方法的参数列表,调用不同的方法。
public class Method_Demo6 {
public static void main(String[] args) {
//定义不同数据类型的变量
byte a = 10;
byte b = 20;
short c = 10;
short d = 20;
int e = 10;
int f = 10;
long g = 10;
long h = 20;
// 调用
System.out.println(compare(a, b));
System.out.println(compare(c, d));
System.out.println(compare(e, f));
System.out.println(compare(g, h));
}
// 两个byte类型的
public static boolean compare(byte a, byte b) {
System.out.println("byte");
return a == b;
}
// 两个short类型的
public static boolean compare(short a, short b) {
System.out.println("short");
return a == b;
}
// 两个int类型的
public static boolean compare(int a, int b) {
System.out.println("int");
return a == b;
}
// 两个long类型的
public static boolean compare(long a, long b) {
System.out.println("long");
return a == b;
}
}
8.6 封装
8.6.1 封装
1 .概念
隐藏对象内部的复杂性,只对外公开简单的接口。便于外界调用,从而提高系统的可扩展性、可维护性。通俗的讲,把该隐藏的隐藏起来,该暴露的暴露出来。这就是封装性的设计思想。
2.如何实现封装呢?
就是依赖访问控制修饰符,也称为权限修饰符来控制。
外部类:public和缺省
成员变量:public,protected,缺省,private
成员方法:public,protected,缺省,private
构造器:public,protected,缺省,private
8.6.2 成员变量/属性私有化问题
1、成员变量封装的目的
- 隐藏类的实现细节
- 让使用者只能通过事先预定的方法来访问数据,从而可以在该方法里面加入控制逻辑,限制对成员变量的不合理访问。还可以进行数据检查,从而有利于保证对象信息的完整性。
- 便于修改,提高代码的可维护性
2、实现步骤
public class Chinese {
private static String country;
private String name;
private int age;
private boolean marry;
public static void setCountry(String c){
country = c;
}
public static String getCountry(){
return country;
}
public void setName(String n) {
name = n;
}
public String getName() {
return name;
}
public void setAge(int a) {
age = a;
}
public int getAge() {
return age;
}
public void setMarry(boolean m){
marry = m;
}
public boolean isMarry(){
return marry;
}
}
3、如何解决局部变量与成员变量同名问题
当局部变量与类变量(静态成员变量)同名时,在类变量前面加“类名.";
当局部变量与实例变量(非静态成员变量)同名时,在实例变量前面加“this.”
8.6.3 包(Package)
1、包的作用
(1)可以避免类重名:有了包之后,类的全名称就变为:包.类名
(2)分类组织管理众多的类
2、声明包的语法格式
package 包名;
包的命名规范和习惯:
(1)所有单词都小写,每一个单词之间使用.分割
(2)习惯用公司的域名倒置
8.6.4 构造器(Constructor)
1、构造器的作用
在创建对象的时候为实例变量赋初始值。
注意:构造器只为实例变量初始化,不为静态类变量初始化
2、构造器的语法格式
构造器又称为构造方法,那是因为它长的很像方法。但是和方法还有有所区别的。
【修饰符】 构造器名(){
// 实例初始化代码
}
【修饰符】 构造器名(参数列表){
// 实例初始化代码
}
注意事项
- 构造器名必须与它所在的类名必须相同。
- 它没有返回值,所以不需要返回值类型,甚至不需要void
- 如果你不提供构造器,系统会给出无参数构造器,并且该构造器的修饰符默认与类的修饰符相同
- 如果你提供了构造器,系统将不再提供无参数构造器,除非你自己定义。
- 构造器是可以重载的,既可以定义参数,也可以不定义参数。
- 构造器的修饰符只能是权限修饰符,不能被其他任何修饰
8.6.5 标准JavaBean
JavaBean
是 Java语言编写类的一种标准规范。符合JavaBean
的类,要求:
(1)类必须是具体的和公共的,
(2)并且具有无参数的构造方法,
(3)成员变量私有化,并提供用来操作成员变量的set
和get
方法。
public class ClassName{
//成员变量
//构造方法
//无参构造方法【必须】
//有参构造方法【建议】
//getXxx()
//setXxx()
//其他成员方法
}
8.6 继承
8.6.1继承
1.概念
多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类中无需再定义这些属性和行为,只需要和抽取出来的类构成某种关系
其中,多个类可以称为子类,也叫派生类;多个类抽取出来的这个类称为父类、超类(superclass)或者基类。
继承的好处
- 提高代码的复用性。
- 提高代码的扩展性。
- 类与类之间产生了关系,是学习多态的前提。
2 继承的格式
1.格式
【修饰符】 class 父类 {
...
}
【修饰符】 class 子类 extends 父类 {
...
}
3 继承的特点一:成员变量
1、父类成员变量私有化(private)
- 父类中的成员,无论是公有(public)还是私有(private),均会被子类继承。
- 子类虽会继承父类私有(private)的成员,但子类不能对继承的私有成员直接进行访问,可通过继承的get/set方法进行访问
/*
* 定义动物类Animal,做为父类
*/
class Animal {
// 定义name属性
private String name;
// 定义age属性
public int age;
// 定义动物的吃东西方法
public void eat() {
System.out.println(age + "岁的" + name + "在吃东西");
}
}
/*
* 定义猫类Cat 继承 动物类Animal
*/
class Cat extends Animal {
// 定义一个猫抓老鼠的方法catchMouse
public void catchMouse() {
System.out.println("抓老鼠");
}
}
/*
* 定义测试类
*/
public class ExtendDemo01 {
public static void main(String[] args) {
// 创建一个猫类对象
Cat cat = new Cat();
// 为该猫类对象的name属性进行赋值
//cat.name = "Tom";// 编译报错
// 为该猫类对象的age属性进行赋值
cat.age = 2;
// 调用该猫的catchMouse()方法
cat.catchMouse();
// 调用该猫继承来的eat()方法
cat.eat();
}
}
2.父子类成员变量重名
(1)当父类的成员变量私有化时,在子类中是无法直接访问的,所以是否重名不影响,如果想要访问父类的私有成员变量,只能通过父类的get/set方法访问;
(2)当父类的成员变量非私有时,在子类中可以直接访问,所以如果有重名时,就需要加“super."进行区别。
4 继承的特点二:成员方法
1、方法重写
class Phone {
public void sendMessage(){
System.out.println("发短信");
}
public void call(){
System.out.println("打电话");
}
public void showNum(){
System.out.println("来电显示号码");
}
}
//智能手机类
class NewPhone extends Phone {
//重写父类的来电显示号码功能,并增加自己的显示姓名和图片功能
public void showNum(){
//调用父类已经存在的功能使用super
super.showNum();
//增加自己特有显示姓名和图片功能
System.out.println("显示来电姓名");
System.out.println("显示头像");
}
}
public class ExtendsDemo06 {
public static void main(String[] args) {
// 创建子类对象
NewPhone np = new NewPhone();
// 调用父类继承而来的方法
np.call();
// 调用子类重写的方法
np.showNum();
}
}
2、方法的重载
(1)同一个类中
class Test{
public int max(int a, int b){
return a > b ? a : b;
}
public double max(double a, double b){
return a > b ? a : b;
}
public int max(int a, int b,int c){
return max(max(a,b),c);
}
}
(2)父子类中
class Father{
public void print(int i){
System.out.println("i = " + i);
}
}
class Son extends Father{
public void print(int i,int j){
System.out.println("i = " + i ",j = " + j);
}
}
5 继承的特点三:构造方法
构造方法的定义格式和作用。
-
构造方法的名字是与类名一致的。
所以子类是无法继承父类构造方法的。
-
构造方法的作用是初始化实例变量的,而子类又会从父类继承所有成员变量
所以子类的初始化过程中,必须先执行父类的初始化动作。子类的构造方法中默认有一个
super()
,表示调用父类的实例初始化方法,父类成员变量初始化后,才可以给子类使用。
class Fu {
private int n;
Fu(){
System.out.println("Fu()");
}
}
class Zi extends Fu {
Zi(){
// super(),调用父类构造方法
super();
System.out.println("Zi()");
}
}
public class ExtendsDemo07{
public static void main (String args[]){
Zi zi = new Zi();
}
}
小结
- super():表示调用父类的无参实例初始化方法,要求父类必须有无参构造,而且可以省略不写;
- super(实参列表):表示调用父类的有参实例初始化方法,当父类没有无参构造时,子类的构造器首行必须写super(实参列表)来明确调用父类的哪个有参构造(其实是调用该构造器对应的实例初始方法)
- super()和super(实参列表)都只能出现在子类构造器的首行
6 继承的特点四:单继承限制
1.Java只支持单继承,不支持多继承。
//一个类只能有一个父类,不可以有多个父类。
class C extends A{} //ok
class C extends A,B... //error
2.Java支持多层继承(继承体系)。
class A{}
class B extends A{}
class C extends B{}
顶层父类是Object类。所有的类默认继承Object,作为父类。
- 子类和父类是一种相对的概念。
- 一个父类可以同时拥有多个子类
8.6.2final关键字
1、修饰类
表示这个类不能被继承,没有子类
2、修饰方法
表示这个方法不能被子类重写
3、声明常量
final修饰某个变量(成员变量或局部变量),表示它的值就不能被修改,即常量,常量名建议使用大写字母。
8.6.3 this和super关键字
1 this关键字
1、this的含义
this代表当前对象
2、this使用位置
- this在实例初始化相关的代码块和构造器中:表示正在创建的那个实例对象,即正在new谁,this就代表谁
- this在非静态实例方法中:表示调用该方法的对象,即谁在调用,this就代表谁。
- this不能出现在静态代码块和静态方法中
3、this使用格式
(1)this.成员变量名
- 当方法的局部变量与当前对象的成员变量重名时,就可以在成员变量前面加this.,如果没有重名问题,就可以省略this.
- this.成员变量会先从本类声明的成员变量列表中查找,如果未找到,会去从父类继承的在子类中仍然可见的成员变量列表中查找
(2)this.成员方法
- 调用当前对象的成员方法时,都可以加"this.",也可以省略,实际开发中都省略
- 当前对象的成员方法,先从本类声明的成员方法列表中查找,如果未找到,会去从父类继承的在子类中仍然可见的成员方法列表中查找
(3)this()或this(实参列表)
-
只能调用本类的其他构造器
-
必须在构造器的首行
-
如果一个类中声明了n个构造器,则最多有 n - 1个构造器中使用了"this(【实参列表】)",否则会发生递归调用死循环
2 super关键字
1、super的含义
super代表当前对象中从父类的引用的
2、super使用的前提
- 通过super引用父类的xx,都是在子类中仍然可见的
- 不能在静态代码块和静态方法中使用super
3、super的使用格式
(1)super.成员变量
在子类中访问父类的成员变量,特别是当子类的成员变量与父类的成员变量重名时。
public class Person {
private String name;
private int age;
//其他代码省略
}
public class Student extends Person{
private int score;
//其他成员方法省略
}
public class Test{
public static void main(String[] args){
Student stu = new Student();
}
}
(2)super.成员方法
在子类中调用父类的成员方法,特别是当子类重写了父类的成员方法时
public class Test{
public static void main(String[] args){
Son s = new Son();
s.test();
}
}
class Father{
public void method(){
System.out.println("aa");
}
}
class Son extends Father{
public void method(){
System.out.println("bb");
}
public void test(){
method();//bb
this.method();//bb
super.method();//aa
}
}
(3)super()或super(实参列表)
在子类的构造器首行,用于表示调用父类的哪个实例初始化方法
super() 和 this() 都必须是在构造方法的第一行,所以不能同时出现。
3 就近原则和追根溯源原则
1、找变量
- 没有super和this
- 在构造器、代码块、方法中如果出现使用某个变量,先查看是否是当前块声明的局部变量,
- 如果不是局部变量,先从当前执行代码的本类去找成员变量
- 如果从当前执行代码的本类中没有找到,会往上找父类的(非private,跨包还不能是缺省的)
- this :代表当前对象
- 通过this找成员变量时,先从当前执行代码的本类中找,没有的会往上找父类的(非private,跨包还不能是缺省的)。
- super :代表父类的
- 通过super找成员变量,直接从当前执行代码所在类的父类找
- super()或super(实参列表)只能从直接父类找
- 通过super只能访问父类在子类中可见的(非private,跨包还不能是缺省的)
注意:super和this都不能出现在静态方法和静态代码块中,因为super和this都是存在与对象中的
2、找方法
- 没有super和this
- 先从当前对象(调用方法的对象)的本类找,如果没有,再从直接父类找,再没有,继续往上追溯
- this
- 先从当前对象(调用方法的对象)的本类找,如果没有,再从父类继承的可见的方法列表中查找
- super
- 直接从当前对象(调用方法的对象)的父类继承的可见的方法列表中查找
3、找构造器
- this()或this(实参列表):只从本类中,不会再往上追溯
- super()或super(实参列表):只从直接父类找,不会再往上追溯
8.7 抽象类
1 由来
在父类中又无法给出具体的实现,而是应该交给子类各自具体实现。那么父类在声明这些方法时,就只有方法签名,没有方法体,我们把没有方法体的方法称为抽象方法。Java语法规定,包含抽象方法的类必须是抽象类。
2 语法格式
- 抽象方法 : 没有方法体的方法。
- 抽象类:被abstract所修饰的类。
抽象类的语法格式
【权限修饰符】 abstract class 类名{
}
【权限修饰符】 abstract class 类名 extends 父类{
}
抽象方法的语法格式
【其他修饰符】 abstract 返回值类型 方法名(【形参列表】);
3 注意事项
a.抽象类不能创建对象,如果创建,编译无法通过而报错。只能创建其非抽象子类的对象。
b.抽象类中,也有构造方法,是供子类创建对象时,初始化父类成员变量使用的。
c.抽象类中,不一定包含抽象方法,但是有抽象方法的类必定是抽象类。
d.抽象类的子类,必须重写抽象父类中所有的抽象方法,否则,编译无法通过而报错。除非该子类也是抽象类。
8.8 多态
多态是继封装、继承之后,面向对象的第三大特性。
8.8.1 定义
1、格式
父类类型 变量名 = 子类对象;
父类类型:指子类对象继承的父类类型,或者实现的父接口类型。
示例
class Person{
private String name;
private int age;
Person(String name, int age){
this.name = name;
this.age = age;
}
public void speak(){
System.out.println(name + "说:我今年" + age);
}
}
class Man extends Person{
Man(String name, int age){
super(name,age);
}
}
class Woman extends Person{
Woman(String name, int age){
super(name,age);
}
}
class Test{
public static void main(String[] args){
Person[] arr = new Person[2];
arr[0] = new Man("张三",23);
arr[1] = new Woman("aa",18);
for(int i=0; i<arr.length; i++){
arr[i].speak();
}
System.out.println("------------------------");
show(new Man("张三",23));
show(new Woman("aa",18));
}
public static void show(Person p){
p.speak();
}
}
2、编译时类型与运行时类型不一致问题
- 编译时,看“父类”,只能调用父类声明的方法,不能调用子类扩展的方法;
- 运行时,看“子类”,一定是执行子类重写的方法体;
8.8.2 父子类之间的类型转换
1、向上转型
- 当父类引用指向一个子类对象时,便是向上转型。这个过程是自动完成的
- 使用格式:
父类类型 变量名 = new 子类类型();
如:Animal a = new Cat();
2、向下转型
- 向下转型:父类类型向子类类型向下转换的过程,这个过程是强制的。
使用格式:
子类类型 变量名 = (子类类型) 父类变量名;
如:Cat c =(Cat) a;
8.8.3 instanceof运算符
为了避免ClassCastException的发生,Java提供了 instanceof
关键字,给引用变量做类型的校验,只要用instanceof判断返回true的,那么强转为该类型就一定是安全的,不会报ClassCastException异常。
格式如下:
变量名/对象 instanceof 数据类型
如果变量/对象属于该数据类型,返回true。
如果变量/对象不属于该数据类型,返回false。
代码实例如下
public class Test {
public static void main(String[] args) {
// 向上转型
Animal a = new Cat();
a.eat(); // 调用的是 Cat 的 eat
// 向下转型
if (a instanceof Cat){
Cat c = (Cat)a;
c.catchMouse(); // 调用的是 Cat 的 catchMouse
} else if (a instanceof Dog){
Dog d = (Dog)a;
d.watchHouse(); // 调用的是 Dog 的 watchHouse
}
}
}
8.9 枚举
1.语法格式:
【修饰符】 enum 枚举类名{
常量对象列表
}
【修饰符】 enum 枚举类名{
常量对象列表;
其他成员列表;
}
2.枚举类的要求和特点:
- 枚举类的常量对象列表必须在枚举类的首行,因为是常量,所以建议大写。
- 如果常量对象列表后面没有其他代码,那么“;”可以省略,否则不可以省略“;”。
- 编译器给枚举类默认提供的是private的无参构造,如果枚举类需要的是无参构造,就不需要声明,写常量对象列表时也不用加参数,
- 如果枚举类需要的是有参构造,需要手动定义private的有参构造,调用有参构造的方法就是在常量对象名后面加(实参列表)就可以。
- 枚举类默认继承的是java.lang.Enum类,因此不能再继承其他的类型。
- JDK1.5之后switch,提供支持枚举类型,case后面可以写枚举常量名。
- 枚举类型如有其它属性,建议(不是必须)这些属性也声明为final的,因为常量对象在逻辑意义上应该不可变。
3.枚举类型常用方法
1.toString(): 默认返回的是常量名(对象名),可以继续手动重写该方法!
2.name():返回的是常量名(对象名) 【很少使用】
3.ordinal():返回常量的次序号,默认从0开始
4.values():返回该枚举类的所有的常量对象,返回类型是当前枚举的数组类型,是一个静态方法
5.valueOf(String name):根据枚举常量对象名称获取枚举对象
public class TestEnum {
public static void main(String[] args) {
Season[] values = Season.values();
for (int i = 0; i < values.length; i++) {
switch(values[i]){
case SPRING:
System.out.println(values[i]+":春暖花开,万物复苏");
break;
case SUMMER:
System.out.println(values[i]+":百花争艳,郁郁葱葱");
break;
case AUTUMN:
System.out.println(values[i]+":菊桂飘香,百树凋零");
break;
case WINTER:
System.out.println(values[i]+":梅花独开,大地一色");
break;
}
}
}
}
enum Season{
SPRING,SUMMER,AUTUMN,WINTER
}
8.10 包装类
1.Java提供了两个类型系统,基本类型与引用类型,使用基本类型在于效率,然而当要使用只针对对象设计的API或新特性(例如泛型),那么基本数据类型的数据就需要用包装类来包装。
2 装箱与拆箱
装箱:把基本数据类型转为包装类对象。
拆箱:把包装类对象拆为基本数据类型。
注意:JDK1.5之后,可以自动装箱与拆箱,只能与自己对应的类型之间才能实现自动装箱与拆箱。
3.基本数据类型和字符串之间的转换
(1)把基本数据类型转为字符串
int a = 10;
//String str = a;//错误的
//方式一:
String str = a + "";
//方式二:
String str = String.valueOf(a);
(2)把字符串转为基本数据类型
String转换成对应的基本类型 ,除了Character类之外,其他所有包装类都具有parseXxx静态方法可以将字符串参数转换为对应的基本类型,例如:
public static int parseInt(String s)
:将字符串参数转换为对应的int基本类型。public static long parseLong(String s)
:将字符串参数转换为对应的long基本类型。public static double parseDouble(String s)
:将字符串参数转换为对应的double基本类型。
或把字符串转为包装类,然后可以自动拆箱为基本数据类型
public static Integer valueOf(String s)
:将字符串参数转换为对应的Integer包装类,然后可以自动拆箱为int基本类型public static Long valueOf(String s)
:将字符串参数转换为对应的Long包装类,然后可以自动拆箱为long基本类型public static Double valueOf(String s)
:将字符串参数转换为对应的Double包装类,然后可以自动拆箱为double基本类型
4.数据类型的最大最小值
Integer.MAX_VALUE和Integer.MIN_VALUE
Long.MAX_VALUE和Long.MIN_VALUE
Double.MAX_VALUE和Double.MIN_VALUE
5.字符转大小写
Character.toUpperCase('x');
Character.toLowerCase('X');
6.整数转进制
Integer.toBinaryString(int i)
Integer.toHexString(int i)
Integer.toOctalString(int i)
- 包装类对象的缓存问题
8.10 接口
8.10.1 定义格式
接口的定义,它与定义类方式相似,但是使用 interface
关键字。它也会被编译成.class文件,但一定要明确它并不是类,而是另外一种引用数据类型。
1、接口的声明格式
【修饰符】 interface 接口名{
//接口的成员列表:
// 静态常量
// 抽象方法
// 默认方法
// 静态方法
// 私有方法
}
示例代码:
interface Usb3{
//静态常量
long MAX_SPEED = 500*1024*1024;//500MB/s
//抽象方法
void read();
void write();
//默认方法
public default void start(){
System.out.println("开始");
}
public default void stop(){
System.out.println("结束");
}
//静态方法
public static void show(){
System.out.println("USB 3.0可以同步全速地进行读写操作");
}
}
2、接口的成员说明
接口定义的是多个类共同的公共行为规范,这些行为规范是与外部交流的通道,这就意味着接口里通常是定义一组公共方法。
在JDK8之前,接口中只允许出现:
(1)公共的静态的常量:其中public static final可以省略
(2)公共的抽象的方法:其中public abstract可以省略
理解:接口是从多个相似类中抽象出来的规范,不需要提供具体实现
在JDK1.8时,接口中允许声明默认方法和静态方法:
(3)公共的默认的方法:其中public 可以省略,建议保留,但是default不能省略
(4)公共的静态的方法:其中public 可以省略,建议保留,但是static不能省略
在JDK1.9时,接口又增加了:
(5)私有方法
除此之外,接口中不能有其他成员,没有构造器,没有初始化块,因为接口中没有成员变量需要初始化。
3.实现接口
接口的使用,它不能创建对象,但是可以被实现(implements
,类似于被继承)。
类与接口的关系为实现关系,即类实现接口,该类可以称为接口的实现类,也可以称为接口的子类。实现的动作类似继承,格式相仿,只是关键字不同,实现使用 implements
关键字。
4.实现接口语法格式
【修饰符】 class 实现类 implements 接口{
// 重写接口中抽象方法【必须】,当然如果实现类是抽象类,那么可以不重写
// 重写接口中默认方法【可选】
}
【修饰符】 class 实现类 extends 父类 implements 接口{
// 重写接口中抽象方法【必须】,当然如果实现类是抽象类,那么可以不重写
// 重写接口中默认方法【可选】
}
注意:
- 如果接口的实现类是非抽象类,那么必须重写接口中所有抽象方法。
- 默认方法可以选择保留,也可以重写。
5.如何调用对应的方法
- 对于接口的静态方法,直接使用“接口名.”进行调用即可
- 也只能使用“接口名."进行调用,不能通过实现类的对象进行调用
- 对于接口的抽象方法、默认方法,只能通过实现类对象才可以调用
- 接口不能直接创建对象,只能创建实现类的对象
public class TestInteface {
public static void main(String[] args) {
//创建实现类对象
MobileHDD b = new MobileHDD();
//通过实现类对象调用重写的抽象方法,以及接口的默认方法,如果实现类重写了就执行重写的默认方法,如果没有重写,就执行接口中的默认方法
b.start();
b.read();
b.stop();
//通过接口名调用接口的静态方法
MobileHDD.show();
}
}
6.接口的多实现
在继承体系中,一个类只能继承一个父类。而对于接口而言,一个类是可以实现多个接口的,这叫做接口的多实现。并且,一个类能继承一个父类,同时实现多个接口。
实现格式:
【修饰符】 class 实现类 implements 接口1,接口2,接口3。。。{
// 重写接口中所有抽象方法【必须】,当然如果实现类是抽象类,那么可以不重写
// 重写接口中默认方法【可选】
}
【修饰符】 class 实现类 extends 父类 implements 接口1,接口2,接口3。。。{
// 重写接口中所有抽象方法【必须】,当然如果实现类是抽象类,那么可以不重写
// 重写接口中默认方法【可选】
}
7 接口的多继承
一个接口能继承另一个或者多个接口,接口的继承也使用 extends
关键字,子接口继承父接口的方法。
小结:
子接口重写默认方法时,default关键字可以保留。
子类重写默认方法时,default关键字不可以保留。
九.Object根父类
9.1 如何理解根父类
类 java.lang.Object
是类层次结构的根类,即所有类的父类。每个类都使用 Object
作为超类。
- Object类型的变量与除Object以外的任意引用数据类型的对象都多态引用
- 所有对象(包括数组)都实现这个类的方法。
- 如果一个类没有特别指定父类,那么默认则继承自Object类。例如:
public class MyClass /*extends Object*/ {
// ...
}
9.2Object类的API
API(Application Programming Interface),应用程序编程接口。Java API是一本程序员的字典
,是JDK中提供给我们使用的类的说明文档。
1.toString()
public String toString()
①默认情况下,toString()返回的是“对象的运行时类型 @ 对象的hashCode值的十六进制形式"
②通常是建议重写,如果在eclipse中,可以用Alt +Shift + S–>Generate toString()
③如果我们直接System.out.println(对象),默认会自动调用这个对象的toString()
示例
public class Person {
private String name;
private int age;
@Override
public String toString() {
return "Person{" + "name='" + name + '\'' + ", age=" + age + '}';
}
// 省略构造器与Getter Setter
}
2.getClass()
public final Class<?> getClass():获取对象的运行时类型
public static void main(String[] args) {
Object obj = new String();
System.out.println(obj.getClass());//运行时类型
}
3.finalize()
protected void finalize():用于最终清理内存的方法
public class TestFinalize {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
MyData my = new MyData();
}
System.gc();//通知垃圾回收器来回收垃圾
try {
Thread.sleep(2000);//等待2秒再结束main,为了看效果
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class MyData{
@Override
protected void finalize() throws Throwable {
System.out.println("轻轻的我走了...");
}
}
4.hashCode()
public int hashCode():返回每个对象的hash值。
hashCode 的常规协定:
- ①如果两个对象的hash值是不同的,那么这两个对象一定不相等;
- ②如果两个对象的hash值是相同的,那么这两个对象不一定相等。
public static void main(String[] args) {
System.out.println("Aa".hashCode());//2112
System.out.println("BB".hashCode());//2112
}
5.equals()
public boolean equals(Object obj):用于判断当前对象this与指定对象obj是否“相等”
①默认情况下,equals方法的实现等价于与“==”,比较的是对象的地址值
②我们可以选择重写,重写有些要求:
A:如果重写equals,那么一定要一起重写hashCode()方法,因为规定:
a:如果两个对象调用equals返回true,那么要求这两个对象的hashCode值一定是相等的;
b:如果两个对象的hashCode值不同的,那么要求这个两个对象调用equals方法一定是false;
c:如果两个对象的hashCode值相同的,那么这个两个对象调用equals可能是true,也可能是false
B:如果重写equals,那么一定要遵循如下几个原则:
a:自反性:x.equals(x)返回true
b:传递性:x.equals(y)为true, y.equals(z)为true,然后x.equals(z)也应该为true
c:一致性:只要参与equals比较的属性值没有修改,那么无论何时调用结果应该一致
d:对称性:x.equals(y)与y.equals(x)结果应该一样
e:非空对象与null的equals一定是false
class User{
private String host;
private String username;
private String password;
public User(String host, String username, String password) {
super();
this.host = host;
this.username = username;
this.password = password;
}
public User() {
super();
}
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User [host=" + host + ", username=" + username + ", password=" + password + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((host == null) ? 0 : host.hashCode());
result = prime * result + ((password == null) ? 0 : password.hashCode());
result = prime * result + ((username == null) ? 0 : username.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
User other = (User) obj;
if (host == null) {
if (other.host != null)
return false;
} else if (!host.equals(other.host))
return false;
if (password == null) {
if (other.password != null)
return false;
} else if (!password.equals(other.password))
return false;
if (username == null) {
if (other.username != null)
return false;
} else if (!username.equals(other.username))
return false;
return true;
}
}