第01章_Java语言概述
1. Java知识脉络图
1.1 Java基础全程脉络图
1.2 本章专题与脉络
2. 抽丝剥茧话Java
2.1 当前大学生就业形势
麦可思研究院
发布了《2022年中国大学生就业报告》,针对2021届毕业生收入较高的本科专业排行榜:
麦可思研究院
发布过《2021年中国大学生就业报告》,应届本科毕业生就业数量较大的前十位行业类的就业质量:报告还对毕业三年后的2017届毕业生所在十大行业进行了统计:
- 从国家统计局发布的2021年全国平均工资来看,不管在城镇非私营单位还是私营单位,
IT业均为最高
。
2.2 IT互联网是否依旧靠谱
过去不能代表未来!互联网是否依旧靠谱?!
2014 年至 2018 年间,我国网民规模从 6.49 亿增长为 8.29 亿,增幅为
27.5%
。同一时间段,全国移动互联网接入的流量却从 20.6EB 增长到了 711.1EB,增幅达3352%
(获取和处理的信息量大幅增加)。随着 5G 技术进一步拓宽移动互联网的速度和容量,
产业互联网
将在消费型流量的基础上创造生产型流量,根据报告的预测,至 2024 年,全国移动互联网的接入流量将达到 127663.8EB,流量规模达到2018年的179.5倍
。当下,5G、物联网、人工智能、产业互联网都是国家政策大方向,需要大量
能与机器对话
的中高端人才。
2.3 IT行业岗位分析
软件开发,是进入互联网IT圈最好的选择之一!
- 起始薪资高
- 工作环境好
- 涨薪幅度高
- 行业更公平
2.4 软件开发之Java开发
移动应用领域(集成Android平台):Java在Android端是主要开发的语言,占有重要的地位。
企业级应用领域(JavaEE后台):用来开发企业级的应用程序,大型网站如淘宝、京东、12306,以及各大物流、银行、金融、社交、医疗、交通、各种OA系统等都是用JavaEE技术开发的。
大数据分析、人工智能领域:流行的大数据框架,如Hadoop、Flink都是用Java编写的。Spark使用Scala编写,但可以用Java开发应用。
Eversoft公司在提到2022年Java发展趋势时写道:
Java 是用于开发大数据项目的最主流的语言
。我们可以轻松地预测它也将在之后继续主导大数据
!游戏领域、桌面应用、嵌入式领域:很多大型游戏的后台、桌面应用等也是Java开发的。
2.5 到底多少人在用Java
2020年,根据 IDC 的报告“Java Turns 25”显示,超过
900 万
名开发人员(全球专职开发人员中的69%
)在使用 Java——比其他任何语言都多。该报告指出,大多数企业业务服务都依靠 Java 来实现。… Java 一直是开发人员中最流行的编程语言,被誉为“
宇宙第一语言
”。
我想告诉大家:
“市场的需求比较大,市场的供给比较大”
“如果你在Java领域里持续积累5-7年以上,那么你至少会成为这个行业的一个专家!”
2.6 八卦一下程序员
还可以是:
2.7 Java系列课程体系
- 见02_学习路线图之《Java中高级程序员全程学习路线图.xmind》
2.8 Java职业晋升路线图
薪资数据统计来源:拉勾网
3. 计算机的硬件与软件
3.1 计算机组成:硬件+软件
3.2 CPU、内存与硬盘
CPU(Central Processing Unit,中央处理器)
- 人靠大脑思考,电脑靠CPU来运算、控制。
硬盘(Hard Disk Drive)
- 计算机最主要的存储设备,容量大,断电数据不丢失。
- 正常分类:
机械硬盘(HDD)
、固态硬盘(SSD)
以及混合硬盘(SSHD)
- 固态硬盘在开机速度和程序加载速度远远高于机械硬盘,缺点就是贵,所有无法完全取代机械硬盘。
内存(Memory)
- 负责硬盘上的数据与CPU之间数据交换处理
- 具体的:保存从硬盘读取的数据,提供给CPU使用;保存CPU的一些临时执行结果,以便CPU下次使用或保存到硬盘。
- 断电后数据丢失。
3.3 输入设备:键盘输入
- 熟悉指法
不熟悉键盘的小伙伴,可以“金山打字通
”走起了。坚决杜绝二指禅
!!
4. 软件相关介绍
4.1 什么是软件
软件,即一系列按照特定顺序组织
的计算机数据
和指令
的集合。有系统软件和应用软件之分。
Pascal之父Nicklaus Wirth: “Programs = Data Structures + Algorithms”
系统软件:
应用软件:
4.2 人机交互方式
图形化界面(Graphical User Interface,GUI),这种方式简单直观,使用者易于接受,容易上手操作。
命令行方式(Command Line Interface,CLI),需要在控制台输入特定的
指令
,让计算机完成一些操作。需要记忆一些指令,较为麻烦。
4.3 常用的DOS命令
DOS(Disk Operating System,磁盘操作系统)是Microsoft公司在Windows之前推出的一个操作系统,是单用户、单任务(即只能执行一个任务)的操作系统。现在被Windows系统取代。
对于Java初学者,学习一些DOS命令,会非常有帮助。
进入DOS操作窗口:
按下Windows+R键盘,打开运行窗口,输入cmd回车,进入到DOS的操作窗口。
常用指令:
操作1:进入和回退
操作 | 说明 |
---|---|
盘符名称: | 盘符切换。E:回车,表示切换到E盘。 |
dir | 列出当前目录下的文件以及文件夹 |
cd 目录 | 进入指定单级目录。 |
cd 目录1\目录2\… | 进入指定多级目录。cd atguigu\JavaSE |
cd .. | 回退到上一级目录。 |
cd \ 或 cd / | 回退到盘符目录。 |
操作2:增、删
操作 | 说明 |
---|---|
md 文件目录名 | 创建指定的文件目录。 |
rd 文件目录名 | 删除指定的文件目录(如文件目录内有数据,删除失败) |
操作3:其它
操作 | 说明 |
---|---|
cls | 清屏。 |
exit | 退出命令提示符窗口。 |
← → | 移动光标 |
↑ ↓ | 调阅历史操作命令 |
Delete和Backspace | 删除字符 |
5. 计算机编程语言
5.1 计算机语言是什么
语言:是人与人之间用于沟通的一种方式。例如:中国人与中国人用普通话沟通。而中国人要和英国人交流,可以使用英语或普通话。
计算机编程语言,就是人与计算机交流的方式。人们可以使用
编程语言
对计算机下达命令
,让计算机完成人们需要的功能。计算机语言有很多种。如:C 、C++、Java、Go、JavaScript、Python,Scala等。
体会:语言 = 语法 + 逻辑
5.2 计算机语言简史
第一代:机器语言(相当于人类的石器时代)
1946年2月14日,世界上第一台计算机
ENAC
诞生,使用的是最原始的穿孔卡片
。这种卡片上使用的是用二进制代码
表示的语言,与人类语言差别极大,这种语言就称为机器语言
。比如一段典型的机器码:1
2
31. 0000,0000,000000010000 代表 LOAD A, 16
2. 0000,0001,000000000001 代表 LOAD B, 1
3. 0001,0001,000000010000 代表 STORE B, 16这种语言本质上是计算机能识别的唯一语言,人类很难理解。可以大胆想象”
此时的程序员99.9%都是异类!
“
第二代:汇编语言(相当于人类的青铜&铁器时代)
使用英文缩写的
助记符
来表示基本的操作,这些助记符构成了汇编语言的基础。比如:LOAD
、MOVE
等,使人更容易使用。因此,汇编语言也称为符号语言
。优点:能编写
高效率
的程序缺点:汇编语言是
面向机器的
,不同计算机机型特点不同,因此会有不同的汇编语言,彼此之间不能通用
。程序不易移植,较难调试。比起机器语言,汇编大大进步了,是机器语言向更高级的语言进化的
桥梁
。目前仍然应用于工业电子编程领域、软件的加密解密、计算机病毒分析等。
第三代:高级语言(相当于人类的信息时代)
高级语言发展于20世纪50年代中叶到70年代,是一种
接近于人们使用习惯
的程序设计语言。它允许程序员使用接近日常英语
的指令来编写程序,程序中的符号和算式也与日常用的数学式子
差不多,接近于自然语言和数学语言,容易为人们掌握。比如:高级语言
独立于机器
,有一定的通用性;计算机不能直接识别和执行用高级语言编写的程序,需要使用编译器或者解释器,转换为机器语言
才能被识别和执行。
此外,高级语言按照程序设计方法的不同,又分为:面向过程的语言
、面向对象的语言
。
- C、Pascal、Fortran面向过程的语言
- C++面向过程/面向对象
- Java跨平台的纯面向对象的语言
- C#、Python、JavaScript、Scala…
目前以及可预见的将来,计算机语言仍然处于“第三代高级语言”阶段。但是不管是什么语言,最后都要向机器语言靠近,因为CPU只认识0和1。
5.3 计算机语言排行榜
TIOBE (https://www.tiobe.com/tiobe-index/)是一个流行编程语言排行,每月更新。排名权重基于世界范围内 工程师数量,Google、Bing、Yahoo! 、Wikipedia、Amazon、Youtube和百度这些主流的搜索引擎,也将作为排名权重的参考指标。
计算机语言走势
5.4 编程语言,该学哪个?
网传的编程语言鄙视链:
- C语言:万物之源
诞生于
1972年
,由AT&T公司旗下贝尔实验室
创建完成,用于构建Unix操作系统。偏向计算机底层操作(操作系统、网络、硬件驱动等)。
优势:几乎所有现代编程语言都脱胎于C
劣势:相当陡的学习曲线;不支持面向对象编程
- C++语言:难学的语言
- 诞生于1983年,作为C语言的增强方案、
升级版本
。C++是C语言的超集,C语言的大部分知识也适用于C++。- 用途:windows 或 MacOS UI、office全家桶、主流的浏览器、Oracle、MySQL、著名的游戏引擎(如星际争霸、魔兽世界)等
- 优势:很多公司都用 C++ 开发核心架构,如 Google、腾讯、百度、阿里云等;面向对象性
- 劣势:体系极为庞大,这是世界目前来说
最复杂
也是最难
的编程语言。
- C#语言:背靠大树的语言
- 诞生于
2000年
,一款强大而灵活的编程语言。靠着微软
这棵大树,是多年来windows平台的一门主流编程语言。- 用途:windows桌面应用开发、Windows Azure、游戏开发
- PHP语言:最好的语言?
- 诞生于1994年,一款
服务器端脚本语言
。最初表示个人主页(Personal Home Page)- PHP语法和C类似,有很多的模板和框架,简单易懂,也许你可以在短短几天做出web app。它主要用于web端,快速建站网络开发
- 劣势:学习门槛太低;其代码运行速度低于大部分编程语言党争对手
- Python:易学的语言
- 诞生于1991年,一种面向对象的语言,虽然运行效率不高,但是
开发效率非常高
。- Python被称为
胶水语言
,哪里都可以用。
- JavaScript语言:前端为王
- 诞生于1995年,网景公司开发完成。
- JavaScript是JavaScript 是目前
所有主流浏览器
上唯一支持的脚本语言。在前端开发中,占有不可替代的地位。
- Java语言:需求旺盛
- 创建于1995年,Java是一种面向对象、基于类的编程语言。
- Java可能是目前
运用最广的项目语言
。代码稳定性超过 C 和 C++,生产力远超 C 和 C++。有 JVM 在,可以轻松地跨平台。- 具有强大的开源开发工具,大量的开源共享库。
- Java拥有世界上
数量最多
的程序员,最不缺人。
- Go语言:夹缝中生存
- Go 语言现在很受关注,它是取代 C 和 C++ 的另一门有潜力的语言。
- C 语言太原始了,C++ 太复杂了,Java 太高级了,所以 Go 语言就在这个夹缝中出现了。
- Go语言已成为
云计算领域
事实上的标准语言,尤其是在 Docker/Kubernetes 等项目中。- Go 语言语法特别简单,你有了 C 和 C++ 的基础,学习 Go 的学习成本基本为零。
- Go社区从Java社区移植了各种优秀的框架或库。
总结:
- 程序设计语言有很多种,每种语言都是为了实现某个特定的目的而发明的。
- 没有“最好”的语言,只有在特定场景下相对来说,最适合的语言而已。
- 如果你掌握了一种编程语言,也会更容易上手其它的编程语言。关键是学习如何使用
程序设计方法
来解决问题。这也是本套课程的主旨。 Talk is cheap,Show me the code
。
6. Java语言概述
6.1 Java概述
是
SUN
(Stanford University Network,斯坦福大学网络公司 )1995年
推出的一门高级编程语言。是一种面向Internet的编程语言。Java一开始富有吸引力是因为Java程序可以在
Web浏览器
中运行。这些Java程序被称为Java小程序
(applet),内嵌在HTML代码中。伴随着互联网的迅猛发展,以及Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。
6.2 Java语言简史
起步阶段:
1991年,Sun公司的工程师小组想要设计一种语言,应用在电视机
、电话
、闹钟
、烤面包机
等家用电器的控制和通信。由于这些设备的处理能力
和内存
都很有限,并且不同的厂商会选择不同的中央处理器(CPU),因此这种语言的关键是代码短小
、紧凑
且与平台无关
(即不能与任何特定的体系结构捆绑在一起)。
Gosling团队率先创造了这个语言,并命名为“Oak
“(起名的原因是因为他非常喜欢自己办公室外的橡树)。后因智能化家电的市场需求没有预期的高,Sun公司放弃了该项计划。
随着20世纪90年代互联网的发展,Sun公司发现该语言在互联网上应用的前景,于是改造了Oak,于1995年5月以Java的名称正式发布。(Java是印度尼西亚爪哇岛
的英文名称,因盛产咖啡而闻名。)
发展阶段:
发行版本 | 发行时间 | 备注 |
---|---|---|
Java 1.0 | 1996.01.23 | Sun公司发布了Java的第一个开发工具包 |
Java 1.1 | 1997.02.19 | JavaOne会议召开,创当时全球同类会议规模之最。 |
Java 1.2 | 1998.12.08 | Java拆分成:J2SE(标准版)、J2EE(企业版)、J2ME(小型版) |
Java 1.3 | 2000.05.08 | |
Java1.4 | 2004.02.06 | |
Java 5.0 | 2004.09.30 | ①版本号从1.4直接更新至5.0;②平台更名为JavaSE、JavaEE、JavaME |
Java 6.0 | 2006.12.11 | 2009.04.20 Oracle宣布收购SUN公司 |
2009.04.20 | Oracle公司收购SUN,交易价格74亿 美元。 |
|
Java 7.0 | 2011.07.02 | |
Java 8.0 | 2014.03.18 | 此版本是继Java 5.0以来变化最大的版本。是长期支持版本(LTS) |
Java 9.0 | 2017.09.22 | ①此版本开始,每半年更新一次;②Java 9.0开始不再支持windows 32位系统 |
Java 10.0 | 2018.03.21 | |
Java 11.0 | 2018.09.25 | JDK安装包取消独立JRE安装包,是长期支持版本(LTS) |
Java 12.0 | 2019.03.19 | |
… | … | |
Java17.0 | 2021.09 | 发布Java 17.0,版本号也称为21.9,是长期支持版本。 |
… | … | |
Java19.0 | 2022.09 | 发布Java19.0,版本号也称为22.9。 |
6.3 Java之父
詹姆斯·高斯林(James Gosling)
先生以“Java 技术之父”而闻名于世。他是Java 技术的创始人,他亲手设计了Java语言,并开发了Java编译器和Java虚拟机,使Java成为了世界上最流行的开发语言。- James Gosling于1984 年加入Sun公司,并一直服务于Sun公司,直至2010年前后,Sun被Oracle并购而加入Oracle,担任客户端软件集团的首席技术官; 2010年4月从Oracle离职。
6.4 公司八卦
- SUN 与 Oracle
SUN是一家
极具创新能力
的公司,2001年 “9.11”以前,SUN公司市值超过1000亿
美元。 但是没能利用Java构建一个强有力、可变现的生态系统,没打好Java这张牌。此后,互联网泡沫破裂,硬件需求大幅减少,它的市值在一个月之内跌幅超过90%。SUN公司的成长用了20年,而衰落只用了1年!
- Oracle 与 Google
Google和Oracle的侵权事件:
2010 年 8 月,Oracle 起诉 Google 的 Android 系统侵权,要求赔偿 26 亿美元。
- Oracle认为Google的代码中使用了Java的
37个API
,并且认为Google是故意为之,因为这样做的好处是可以让更多的Java程序员更容易接受Android的代码。- Oracle认为Android 中有
9行代码
直接抄袭了Java的实现。这9行牛气哄哄的代码都出自一人之手,他就是Java 大牛——Joshua Bloch
。2018 年 3 月,美国联邦巡回上诉法院裁决,谷歌侵犯了甲骨文的版权,支付高达
88亿美元
的赔偿金。2021 年 4 月,美国最高法院给出了最终裁决:谷歌胜诉,其代码属于“合理使用”的范畴。为期十多年的软件行业“第一版权案”落幕。
1 | //Code In OpenJDK / Android : |
6.5 Java技术体系平台
- Java SE(Java Standard Edition)标准版
- 支持面向
桌面级应用
(如Windows下的应用程序)的Java平台,即定位个人计算机的应用开发。 - 包括用户界面接口AWT及Swing,网络功能与国际化、图像处理能力以及输入输出支持等。
- 此版本以前称为J2SE
- 支持面向
- Java EE(Java Enterprise Edition)企业版
- 为开发企业环境下的应用程序提供的一套解决方案,即定位
在服务器端的Web应用开发
。 - JavaEE是JavaSE的扩展,增加了用于服务器开发的类库。如:Servlet能够延伸服务器的功能,通过请求-响应的模式来处理客户端的请求;JSP是一种可以将Java程序代码内嵌在网页内的技术。
- 版本以前称为J2EE
- 为开发企业环境下的应用程序提供的一套解决方案,即定位
- Java ME(Java Micro Edition)小型版
- 支持Java程序运行在
移动终端(手机、机顶盒)上的平台
,即定位在消费性电子产品的应用开发 - JavaME是JavaSE的内伸,精简了JavaSE 的核心类库,同时也提供自己的扩展类。增加了适合微小装置的类库:javax.microedition.io.*等。
- 此版本以前称为J2ME
- 支持Java程序运行在
注意:
Android开发不等同于Java ME的开发
7. Java开发环境搭建(掌握)
7.1 什么是JDK、JRE
- JDK (
J
avaD
evelopmentK
it):是Java程序开发工具包,包含JRE
和开发人员使用的工具。 - *JRE * (
J
avaR
untimeE
nvironment) :是Java程序的运行时环境,包含JVM
和运行时所需要的核心类库
。
如下是Java 8.0 Platform:
小结:
JDK = JRE + 开发工具集(例如Javac编译工具等)
JRE = JVM + Java SE标准类库
7.2 JDK版本选择
- 自Java 8版本发布以来,其后的每次更新,都会有小伙伴高呼:Java8 YYDS!
- 论坛的声音:“
你发任你发,我用Java 8!
”
数据说话1:
JRebel 于2022年4月前后发布了《2022 年Java生态系统状况报告》,报告中提到使用Java11 的占比最多,Java 8 紧随其后,如下图。而此前2020年的报告显示,Java8占比达到了84.48%
。
我的分析:
G1是最受欢迎的GC算法。Java 11及更高版本的G1收集器是默认的GC,而Java 8中并不是。出于对G1的喜爱,很多开发者才会选择抛弃Java 8。
数据说话2:
此外,某美国软件开发商在对近千名专业的Java开发者调研后,发布的《2022年Java开发者生产力报告》称:八年前发布的Java 8依然是Java中应用最广泛的版本,占比 37%
,其次是 Java 11,占比29%
。
高斯林说话:
Spring框架说话:
在Java 17正式发布之前,Java开发框架Spring率先在官博宣布,Spring Framework 6和Spring Boot 3计划在2022年第四季度实现总体可用性的高端基线:
- Java 17+(来自 Spring Framework 5.3.x 线中的 Java 8-17)
- Jakarta EE 9+(来自Spring框架5.3.x 线中的 Java EE 7-8)
Spring 官方说明:https://spring.io/blog/2022/01/20/spring-boot-3-0-0-m1-is-now-available
意味着:springboot3.0 是需要用java17和spring6.0为基础建设。如果从企业选型最新springboot3.0作为架构来说,它搭配jdk17肯定是标配了。
7.3 JDK的下载
- 下载网址(Oracle公司官网):
- 下载步骤:如图所示,在官网底部选择Developers开发者
(1)在Developers页面中间的技术分类部分,选择Java
,单击进入,如图所示:
(2)这里展示的是最新Java版本,例如Java17。单击Download Java
,然后选择具体的版本下载。
(3)选择Download Java按钮后
(4)如果想安装Java8 可以选择如下位置:
(5)选择Accept License Agreement,
(6)注册或登录后下载:
(7)如果需要安装其它版本,可以选择Java archive:
接着进行选择下载即可:
7.4 JDK的安装
- 安装说明
- 傻瓜式安装,下一步即可。
- 建议:安装路径不要有中文或者空格等特殊符号。
- 本套课程会同时安装JDK8 和 JDK17,并以JDK17为默认版本进行讲解。
- 安装步骤:
(1)双击jdk-17_windows-x64_bin.exe
文件,并单击下一步,如图所示:
(2)修改安装路径,单击更改,如图所示:
(3)将安装路径修改为D:\develop_tools\jdk\jdk-17.0.2\
,并单击下一步,如图所示:
(4)稍后几秒,安装完成,如图所示:
7.5 配置path环境变量
7.5.1 理解path环境变量
什么是path环境变量?
答:window操作系统执行命令时,所要搜寻的路径。
为什么配置path?
答:希望在命令行使用javac.exe等工具时,任意目录下都可以找到这个工具所在的目录。
以JDK为例演示
我们在C:\Users\songhk
目录下使用javac命令,结果如下:
我们在JDK的安装目录的bin目录下使用javac命令,结果如下:
我们不可能每次使用java.exe,javac.exe等工具的时候都进入到JDK的安装目录下,太麻烦了。这时就需要配置path环境变量。
7.5.2 JDK8配置方案1:只配置path
- 步骤:
(1)打开桌面上的计算机,进入后在左侧找到此电脑
,单击鼠标右键
,选择属性
,如图所示:
(2)选择高级系统设置
,如图所示:
(3)在高级
选项卡,单击环境变量
,如图所示:
(4)在系统变量
中,选中Path
环境变量,双击
或者点击编辑
,如图所示:
(5)点击新建
,填入D:\develop_tools\jdk\jdk1.8.0_271\bin
,并将此值上移
到变量列表的首位。如图所示:
- 编辑模式1:
- 编辑模式2:(注意,结尾需要有英文模式下的;)
(6)环境变量配置完成,重新开启DOS命令行,在任意目录下输入javac
或java
命令或java -version
,运行成功。
7.5.3 JDK8配置方案2:配置JAVA_HOME+path(推荐)
- 步骤:
(1)打开桌面上的计算机,进入后在左侧找到计算机
,单击鼠标右键
,选择属性
,如图所示:
(2)选择高级系统设置
,如图所示:
(3)在高级
选项卡,单击环境变量
,如图所示:
(4)在系统变量
中,单击新建
,创建新的环境变量,如图所示:
(5)变量名输入JAVA_HOME
,变量值输入 D:\develop_tools\jdk\jdk1.8.0_271
,单击确定
,如图所示:
(6)选中Path
环境变量,双击
或者点击编辑
,如图所示:
(7)在变量值的最前面,键入%JAVA_HOME%\bin
。如图所示:
注意:强烈建议将%JAVA_HOME%\bin声明在path环境变量中所有变量的最前面!
(8)环境变量配置完成,重启DOS命令行,在任意目录下输入javac
或java
命令或java -version
,运行成功。
我想说:
有的书籍、论坛、视频上还提到配置classpath,用于指名class文件识别的路径。其实是没必要的,反而建议大家如果配置了classpath环境变量,要删除。对于初学者,反而不友好。
小结如下:
7.5.4 JDK17配置方案:自动配置
不管大家有没有提前安装JDK8或其它版本JDK,在我们安装完JDK17之后,理应按JDK8的方式配置path环境变量。但是,我们发现在安装完JDK17以后,配置环境变量之前,执行CMD指令:
竟然成功了!而且是17.0.2版本。因为JDK17在安装之后,自动进行了环境变量的配置。如下:
这里建议,将JDK17安装的路径,设置为JAVA_HOME,并将%JAVA_HOME%\bin
上移到首位。
思考:如果你仍然希望在JDK8下开发Java程序?如何做呢?
8. 开发体验:HelloWorld(掌握)
JDK安装完毕,我们就可以开发第一个Java程序了,习惯性的称为:HelloWorld。
8.1 开发步骤
Java程序开发三步骤:编写、编译、运行。
- 将 Java 代码编写到扩展名为 .java 的源文件中
- 通过 javac.exe 命令对该 java 文件进行编译,生成一个或多个字节码文件
- 通过 java.exe 命令对生成的 class 文件进行运行
8.2 编写
(1)在D:\JavaSE\chapter01
目录下新建文本文件,完整的文件名修改为HelloWorld.java
,其中文件名为HelloWorld
,后缀名必须为.java
。
(2)用记事本或editplus等文本编辑器打开(虽然记事本也可以,但是没有关键字颜色标识,不利于初学者学习)
(3)在文件中输入如下代码,并且保存:
1 | class HelloChina { |
友情提示1:每个字母和符号必须与示例代码一模一样,包括大小写在内。
友情提示2:
或
第一个HelloWord
源程序就编写完成了,但是这个文件是程序员编写的,JVM是看不懂的,也就不能运行,因此我们必须将编写好的Java源文件
编译成JVM可以看懂的字节码文件
,也就是.class
文件。
8.3 编译
在DOS命令行中,进入D:\JavaSE\chapter01
目录,使用javac
命令进行编译。
使用文件资源管理器打开D:\JavaSE\chapter01
目录,然后在地址栏输入cmd。
命令:
1 | javac Java源文件名.后缀名java |
举例:
1 | javac HelloWorld.java |
编译成功后,命令行没有任何提示。打开D:\JavaSE\chapter01
目录,发现产生了一个新的文件 HelloChina.class
,该文件就是编译后的文件,是Java的可运行文件,称为字节码文件,有了字节码文件,就可以运行程序了。
8.4 运行
在DOS命令行中,在字节码文件目录下,使用java
命令进行运行。
命令:
1 | java 主类名字 |
主类是指包含main方法的类,main方法是Java程序的入口:
1 | public static void main(String[] args){ |
举例:
1 | java HelloChina |
错误演示:
java HelloChina.class
9. Java开发工具
9.1 都有哪些开发Java的工具
- 级别一:文本开发工具
- 级别二:集成开发环境(Integrated Development Environment,IDE)
把代码编写,编译,执行,调试等多种功能综合到一起的开发工具。
9.2 如何选择
前期我们先使用文本开发工具,培养代码感
,利于公司笔、面试。
后期我们使用IDE,提供更强大的功能支持
。
10. HelloWorld案例常见错误
10.1 拼写问题
- 单词拼写问题
- 正确:class 错误:Class
- 正确:String 错误:string
- 正确:System 错误:system
- 正确:main 错误:mian
- Java语言是一门严格区分大小写的语言
- 标点符号使用问题
- 不能用中文符号,英文半角的标点符号(正确)
- 括号问题,成对出现
10.2 编译、运行路径问题
举例1:
- 源文件名不存在或者写错
- 当前路径错误
- 后缀名隐藏问题
举例2:
- 类文件名写错,尤其文件名与类名不一致时,要小心
- 类文件不在当前路径下,或者不在classpath指定路径下
10.3 语法问题
举例1:
声明为public的类应与文件名一致,否知编译失败。
举例2:
编译失败,注意错误出现的行数,再到源代码中指定位置改错
10.4 字符编码问题
当cmd命令行窗口的字符编码与.java源文件的字符编码不一致,如何解决?
解决方案一:
- 在Notepad++等编辑器中,修改源文件的字符编码:
- 在EditPlus中可以将Java源文件另存为ANSI编码方式(中文操作系统下即为GBK字符集)
解决方案二:
在使用javac命令式,可以指定源文件的字符编码
1 | javac -encoding utf-8 Review01.java |
10.5 建议
注意缩进!
一定要有缩进。缩进就像人得体的衣着一样!
只要遇到{}就缩进,缩进的快捷键tab键。
必要的空格
- 变量类型、变量、赋值符号、变量值之间填充相应空格,更美观。比如: int num = 10;
11. HelloWorld小结
11.1 Java程序的结构与格式
结构:
1 | 类{ |
格式:
(1)每一级缩进一个Tab键
(2){}的左半部分在行尾,右半部分单独一行,与和它成对的”{“的行首对齐
11.2 Java程序的入口
Java程序的入口是main方法
1 | public static void main(String[] args){ |
11.3 两种常见的输出语句
换行输出语句:输出内容,完毕后进行换行,格式如下:
1
System.out.println(输出内容);
直接输出语句:输出内容,完毕后不做任何处理,格式如下
1
System.out.print(输出内容);
注意事项:
换行输出语句,括号内可以什么都不写,只做换行处理
直接输出语句,括号内什么都不写的话,编译报错
11.4 源文件名与类名
(1)源文件名是否必须与类名一致?public呢?
1 | 如果这个类不是public,那么源文件名可以和类名不一致。但是不便于代码维护。 |
(2)一个源文件中是否可以有多个类?public呢?
1 | 一个源文件中可以有多个类,编译后会生成多个.class字节码文件。 |
12. 注释(comment)
什么是注释?
- 源文件中用于解释、说明程序的文字就是注释。
注释是一个程序员必须要具有的良好编程习惯。实际开发中,程序员可以先将自己的
思想
通过注释整理出来,再用代码
去体现。程序员最讨厌两件事:
一件是自己写代码被要求加注释
另一件是接手别人代码,发现没有注释
不加注释的危害
Java中的注释类型:
单行注释
1
//注释文字
多行注释
1
2
3
4
5/*
注释文字1
注释文字2
注释文字3
*/文档注释 (Java特有)
1
2
3
4/**
@author 指定java程序的作者
@version 指定源文件的版本
*/
注释的作用
- 它提升了程序的可阅读性。(不加注释的危害性,见图。)
- 调试程序的重要方法。
具体使用1:单行注释、多行注释
对于单行和多行注释,被注释的文字,不会出现在字节码文件中,进而不会被JVM(java虚拟机)解释执行。
多行注释里面不允许有多行注释嵌套。
一个段子
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16A:嘿 //是什么意思啊?
B:嘿.
A:呃 我问你//是什么意思?
B:问吧.
A:我刚才不是问了么?
B:啊?
A:你再看看记录...
B:看完了.
A:......所以//是啥?
B:所以什么?
A:你存心耍我呢吧?
B:没有啊 你想问什么?
……
不断循环之后,A一气之下和B绝交,自己苦学程序。
N年之后,A终于修成正果,回想起B,又把聊天记录翻出来看,这时,他突然发现B没有耍他……
而他自己也不知道当年他问B的究竟是什么问题……
具体使用2:文档注释(Java特有)
文档注释内容可以被JDK提供的工具 javadoc 所解析,生成一套以网页文件形式体现的该程序的说明文档。
操作方式。比如:
1
javadoc -d mydoc -author -version HelloWorld.java
案例:
1 | //单行注释 |
13. Java API文档
- API (Application Programming Interface,应用程序编程接口)是 Java 提供的基本编程接口。
- Java语言提供了大量的基础类,因此 Oracle 也为这些基础类提供了相应的说明文档,用于告诉开发者如何使用这些类,以及这些类里包含的方法。大多数Java书籍中的类的介绍都要参照它来完成,它是编程者经常查阅的资料。
- Java API文档,即为JDK使用说明书、帮助文档。类似于:
- 下载API文档:
14. Java核心机制:JVM
14.1 Java语言的优缺点
Java确实是从C语言和C++语言继承了许多成份,甚至可以将Java看成是类C语言发展和衍生的产物。“青出于蓝,而胜于蓝”。
14.1.1 优点
- 跨平台性:这是Java的核心优势。Java在最初设计时就很注重移植和跨平台性。比如:Java的int永远都是32位。不像C++可能是16,32,可能是根据编译器厂商规定的变化。
- 通过Java语言编写的应用程序在不同的系统平台上都可以运行。“
Write once , Run Anywhere
”。 - 原理:只要在需要运行 java 应用程序的操作系统上,先安装一个Java虚拟机 (
J
VM ,JavaV
irtualM
achine) 即可。由JVM来负责Java程序在该系统中的运行。
面向对象性:
面向对象是一种程序设计技术,非常
适合大型软件的设计和开发
。面向对象编程支持封装、继承、多态等特性,让程序更好达到高内聚
,低耦合
的标准。健壮性:吸收了C/C++语言的优点,但去掉了其影响程序健壮性的部分(如指针、内存的申请与释放等),提供了一个相对安全的内存管理和访问机制。
安全性高:
Java适合于网络/分布式环境,需要提供一个安全机制以防恶意代码的攻击。如:
安全防范机制
(ClassLoader类加载器),可以分配不同的命名空间以防替代本地的同名类、字节代码检查。简单性:
Java就是C++语法的
简化版
,我们也可以将Java称之为“C++--
”。比如:头文件,指针运算,结构,联合,操作符重载,虚基类等。高性能:
Java最初发展阶段,总是被人诟病“
性能低
”;客观上,高级语言运行效率总是低于低级语言的,这个无法避免。Java语言本身发展中通过虚拟机的优化提升了几十倍运行效率
。比如,通过JIT(JUST IN TIME)即时编译技术提高运行效率。Java低性能的短腿,已经被完全解决了
。业界发展上,我们也看到很多C++应用转到Java开发,很多C++程序员转型为Java程序员。
14.1.2 缺点
语法过于复杂、严谨
,对程序员的约束比较多,与python、php等相比入门较难。但是一旦学会了,就业岗位需求量大,而且薪资待遇节节攀升
。- 一般适用于大型网站开发,
整个架构会比较重
,对于初创公司开发和维护人员的成本比较高(即薪资高),选择用Java语言开发网站或应用系统的需要一定的经济实力。 并非适用于所有领域
。比如,Objective C、Swift在iOS设备上就有着无可取代的地位。浏览器中的处理几乎完全由JavaScript掌控。Windows程序通常都用C++或C#编写。Java在服务器端编程和跨平台客户端应用领域则很有优势。
14.2 JVM功能说明
JVM(J
ava V
irtual M
achine ,Java虚拟机):是一个虚拟的计算机,是Java程序的运行环境。JVM具有指令集并使用不同的存储区域,负责执行指令,管理数据、内存、寄存器。
14.2.1 功能1:实现Java程序的跨平台性
我们编写的Java代码,都运行在JVM 之上。正是因为有了JVM,才使得Java程序具备了跨平台性。
使用JVM前后对比:
14.2.2 功能2:自动内存管理(内存分配、内存回收)
- Java程序在运行过程中,涉及到运算的
数据的分配
、存储
等都由JVM来完成 - Java消除了程序员回收无用内存空间的职责。提供了一种系统级线程跟踪存储空间的分配情况,在内存空间达到相应阈值时,检查并释放可被释放的存储器空间。
- GC的自动回收,提高了内存空间的利用效率,也提高了编程人员的效率,很大程度上
减少了
因为没有释放空间而导致的内存泄漏
。
面试题:
Java程序还会出现内存溢出和内存泄漏问题吗? Yes!
15. 章节案例
案例1:个人信息输出
1 | class Exercise1{ |
案例2:输出:心形
结合\n(换行),\t(制表符),空格等在控制台打印出如下图所示的效果。
方式一:
1 | //方式一: |
方式二:
1 | class Exercise3{ |
第02章_变量与运算符
本章专题与脉络
1. 关键字(keyword)
- 定义:被Java语言赋予了特殊含义,用做专门用途的字符串(或单词)
- HelloWorld案例中,出现的关键字有
class
、public
、static
、void
等,这些单词已经被Java定义好了。
- HelloWorld案例中,出现的关键字有
- 特点:全部关键字都是
小写字母
。 - 关键字比较多,不需要死记硬背,学到哪里记到哪里即可。
- 官方地址: https://docs.oracle.com/javase/tutorial/java/nutsandbolts/_keywords.html
说明:
- 关键字一共
50个
,其中const
和goto
是保留字
(reserved word)。true
,false
,null
不在其中,它们看起来像关键字,其实是字面量,表示特殊的布尔值和空值。
2. 标识符( identifier)
Java中变量、方法、类等要素命名时使用的字符序列,称为标识符。
技巧:凡是自己可以起名字的地方都叫标识符。(自己定义类名/自己定义变量名/自己定义方法名)
标识符的命名规则(必须遵守的硬性规定
):
1 | > 由26个英文字母大小写,0-9 ,_或 $ 组成 |
练习:miles、Test、a++、 –a、4#R、$4、 #44、apps、class、public、int、x、y、radius
标识符的命名规范(建议遵守的软性要求
,否则工作时容易被鄙视):
1 | > 包名:多单词组成时所有字母都小写:xxxyyyzzz。 |
注意:在起名字时,为了提高阅读性,要尽量有意义,“见名知意”。
更多细节详见《代码整洁之道_关于标识符.txt》《阿里巴巴Java开发手册-1.7.1-黄山版》
3. 变量
3.1 为什么需要变量
一花一世界,如果把一个程序看做一个世界或一个社会的话,那么变量就是程序世界的花花草草、万事万物。即,变量是程序中不可或缺的组成单位,最基本的存储单元。
3.2 初识变量
变量的概念:
内存中的一个存储区域,该区域的数据可以在同一类型范围内不断变化
变量的构成包含三个要素:
数据类型
、变量名
、存储的值
Java中变量声明的格式:
数据类型 变量名 = 变量值
变量的作用:用于在内存中保存数据。
使用变量注意:
- Java中每个变量必须先声明,后使用。 ①先声明后赋值 int age; age=10; ②声明同时赋值 int age=25;
- 使用变量名来访问这块区域的数据。
- 变量的作用域:其定义所在的一对{ }内。
- 变量只有在其
作用域
内才有效。出了作用域,变量不可以再被调用。 - 同一个作用域内,不能定义重名的变量。(不同域可以同名)
3.3 Java中变量的数据类型
Java中变量的数据类型分为两大类:
基本数据类型:包括
整数类型
、浮点数类型
、字符类型
、布尔类型
。引用数据类型:包括
数组
、类
、接口
、枚举
、注解
、记录
。
3.4 变量的使用
3.4.1 步骤1:变量的声明
1 | 格式:数据类型 变量名; |
1 | //例如: |
注意:变量的数据类型可以是基本数据类型,也可以是引用数据类型。
3.4.2 步骤2:变量的赋值
给变量赋值,就是把“值”存到该变量代表的内存空间中。同时,给变量赋的值类型必须与变量声明的类型一致或兼容。
变量赋值的语法格式:
1 | 变量名 = 值; |
举例1:可以使用合适类型的常量值
给已经声明的变量赋值
1 | age = 18; |
举例2:可以使用其他变量
或者表达式
给变量赋值
1 | int m = 1; |
3:变量可以反复赋值
1 | //先声明,后初始化 |
举例4:也可以将变量的声明和赋值一并执行
1 | boolean isBeauty = true; |
内存结构如图:
4. 基本数据类型介绍
4.1 整数类型:byte、short、int(默认)、long
- Java各整数类型有固定的表数范围和字段长度,不受具体操作系统的影响,以保证Java程序的可移植性。
- 定义long类型的变量,赋值时需要以”
l
“或”L
“作为后缀。 – 例如:long age=12l - Java程序中变量通常声明为int型,除非不足以表示较大的数,才使用long。
- Java的整型
常量默认为 int 型
。
4.1.1 补充:计算机存储单位
字节(Byte):是计算机用于
计量存储容量
的基本
单位,一个字节等于8 bit。位(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
4.2 浮点类型:float、double(默认)
- 与整数类型类似,Java 浮点类型也有固定的表数范围和字段长度,不受具体操作系统的影响。
- 浮点型常量有两种表示形式:
- 十进制数形式。如:5.12 512.0f .512 (必须有小数点)
- 科学计数法形式。如:5.12e2 512E2 100E-2
- float:
单精度
,尾数可以精确到7位有效数字。很多情况下,精度很难满足需求。 - double(通常采用此类型):
双精度
,精度是float的两倍。 - 定义float类型的变量,赋值时需要以”
f
“或”F
“作为后缀。 – 例如:float age=12f - Java 的浮点型
常量默认为double型
。
4.2.1 关于浮点型精度的说明
并不是所有的小数都能可以精确的用二进制浮点数表示。二进制浮点数不能精确的表示0.1、0.01、0.001这样10的负次幂。
浮点类型float、double的数据不适合在
不容许舍入误差
的金融计算领域。如果需要精确
数字计算或保留指定位数的精度,需要使用BigDecimal类
。(算法题经常出现)测试用例:
1 | //测试1:(解释见章末企业真题:为什么0.1 + 0.2不等于0.3) |
4.2.2 应用举例
案例1:定义圆周率并赋值为3.14,现有3个圆的半径分别为1.2、2.5、6,求它们的面积。
1 | /** |
案例2:小明要到美国旅游,可是那里的温度是以华氏度为单位记录的。
它需要一个程序将华氏温度(80度)转换为摄氏度,并以华氏度和摄氏度为单位分别显示该温度。
1 | ℃ = (℉ - 32) / 1.8 |
1 | /** |
4.3 字符类型:char
char 型数据用来表示通常意义上“
字符
”(占2字节)Java中的所有字符都使用Unicode编码,故一个字符可以存储一个字母,一个汉字,或其他书面语的一个字符。
字符型变量的三种表现形式:
形式1:使用单引号(‘ ‘)括起来的单个字符。
例如:char c1 = ‘a’; char c2 = ‘中’; char c3 = ‘9’;
形式2:直接使用
Unicode值
来表示字符型常量:‘\uXXXX
’。其中,XXXX代表一个十六进制整数。例如:\u0023 表示 ‘#’。
形式3:Java中还允许使用
转义字符‘\’
来将其后的字符转变为特殊字符型常量。例如:char c3 = ‘\n’; // ‘\n’表示换行符
转义字符 说明 Unicode表示方式 \n
换行符 \u000a \t
制表符 \u0009 \"
双引号 \u0022 \'
单引号 \u0027 \\
反斜线 \u005c \b
退格符 \u0008 \r
回车符 \u000d char类型是可以进行运算的。因为它都对应有Unicode码,可以看做是一个数值。
举例:
1 | public class IdentifierTest { |
4.4 布尔类型:boolean
boolean 类型用来判断逻辑条件,一般用于流程控制语句中:
- if条件控制语句;
- while循环控制语句;
- for循环控制语句;
- do-while循环控制语句;
boolean类型数据只有两个值:true、false,无其它。
- 不可以使用0或非 0 的整数替代false和true,这点和C语言不同。
- 拓展:Java虚拟机中没有任何供boolean值专用的字节码指令,Java语言表达所操作的boolean值,在编译之后都使用java虚拟机中的int数据类型来代替:true用1表示,false用0表示。——《java虚拟机规范 8版》
举例:
1 | boolean isFlag = true; |
经验之谈:
Less is More!建议不要这样写:if ( isFlag = = true ),只有新手才如此。关键也很容易写错成if(isFlag = true),这样就变成赋值isFlag为true而不是判断!
老鸟的写法
是if (isFlag)或者if ( !isFlag)。
5. 基本数据类型变量间运算规则
在Java程序中,不同的基本数据类型(只有7种,不包含boolean类型)变量的值经常需要进行相互转换。
转换的方式有两种:①自动类型提升
和②强制类型转换
。
5.1 自动类型提升
规则:将取值范围小(或容量小)的类型自动提升为取值范围大(或容量大)的类型 。
基本数据类型的转换规则如图所示:
(1)当把存储范围小的值(常量值、变量的值、表达式计算的结果值)赋值给了存储范围大的变量时
1 | int i = 'A';//char自动升级为int,其实就是把字符的编码值赋值给i变量了 |
(2)当存储范围小的数据类型与存储范围大的数据类型变量一起混合运算时,会按照其中最大的类型运算(自动转换为最大类型)。
1 | int i = 1; |
(3)当byte,short,char(三个在int之前的不分前后的类型)数据类型的变量进行算术运算时,按照int类型处理。
1 | byte b1 = 1; |
练习:
1 | 设 x 为float型变量,y为double型变量,a为int型变量,b为long型变量,c为char型变量,则表达式 |
5.2 强制类型转换
将3.14
赋值到int
类型变量会发生什么?产生编译失败,肯定无法赋值。
1 | int i = 3.14; // 编译报错 |
想要赋值成功,只有通过强制类型转换
,将double
类型强制转换成int
类型才能赋值。
规则:将取值范围大(或容量大)的类型强制转换成取值范围小(或容量小)的类型。
自动类型提升是Java自动执行的,而强制类型转换是自动类型提升的逆运算,需要我们自己手动执行。
转换格式:
1 | 数据类型1 变量名 = (数据类型1)被强转数据值; //()中的数据类型必须<=变量值的数据类型 |
(1)当把存储范围大的值(常量值、变量的值、表达式计算的结果值)强制转换为存储范围小的变量时,可能会损失精度
或溢出
。
1 | int i = (int)3.14;//损失精度 输出为3 截断了后面的0.14 |
(2)当某个值想要提升数据类型时(本身可以通过自动类型提升完成),也可以使用强制类型转换。这种情况的强制类型转换是没有风险
的,通常省略。
1 | int i = 1; |
(3)声明变量时,long(可以√省略),float(不可以×省略)。
1 | //记忆方法: 可以看作long定义的时候可能是int(整数型默认)自动类型转换为long 所以可以省略 |
练习:判断是否能通过编译
1 | 1)short s = 5; |
*问答:为什么标识符的声明规则里要求不能数字开头? * — 是本身的数值 / 他是一个变量所对应的值
1 | //如果允许数字开头,则如下的声明编译就可以通过: |
5.3 基本数据类型与String的运算
5.3.1 字符串类型:String
- String不是基本数据类型,属于引用数据类型 【所以不能够进行基本数据类型的自动类型转换和强制类型转换】
- 使用一对
""
来表示一个字符串,内部可以包含0-n个字符。 - 声明方式与基本数据类型类似。例如:String str = “尚硅谷”;
5.3.2 运算规则
1、任意八种基本数据类型的数据与String类型只能进行连接“+”运算,且结果一定也是String类型
1 | System.out.println(" " + 1 + 2); //空格空格空格12 |
2、String类型不能通过强制类型()转换,转为其他的类型
1 | String str = "123"; |
5.3.3 案例与练习
案例:公安局身份登记
要求填写自己的姓名、年龄、性别、体重、婚姻状况(已婚用true表示,单身用false表示)、联系方式等等。
1 | /** |
练习:
练习1:
1 | String str1 = 4; //判断对错: × 可以改为String str1=String.valueOf(4); |
练习2:
1 | System.out.println("* *"); //输出: * * |
6. 计算机底层如何存储数据
计算机世界中只有二进制,所以计算机中存储和运算的所有数据
都要转为二进制
。包括数字、字符、图片、声音、视频等。
世界上有10种人 ,认识和不认识二进制的。
6.1 进制的分类
十进制(decimal)
- 数字组成:0-9
- 进位规则:满十进一
二进制(binary)
- 数字组成:0-1
- 进位规则:满二进一,以
0b
或0B
开头
八进制(octal):很少使用
- 数字组成:0-7
- 进位规则:满八进一,以数字
0
开头表示
十六进制
- 数字组成:0-9,a-f
- 进位规则:满十六进一,以
0x
或0X
开头表示。此处的 a-f 不区分大小写
代码演示:
1 | class BinaryTest { |
6.2 进制的换算举例
十进制 | 二进制 | 八进制 | 十六进制 |
---|---|---|---|
0 | 0 | 0 | 0 |
1 | 1 | 1 | 1 |
2 | 10 | 2 | 2 |
3 | 11 | 3 | 3 |
4 | 100 | 4 | 4 |
5 | 101 | 5 | 5 |
6 | 110 | 6 | 6 |
7 | 111 | 7 | 7 |
8 | 1000 | 10 | 8 |
9 | 1001 | 11 | 9 |
10 | 1010 | 12 | a或A |
11 | 1011 | 13 | b或B |
12 | 1100 | 14 | c或C |
13 | 1101 | 15 | d或D |
14 | 1110 | 16 | e或E |
15 | 1111 | 17 | f或F |
16 | 10000 | 20 | 10 |
6.3 二进制的由来
二进制,是计算技术中广泛采用的一种数制,由德国数理哲学大师莱布尼茨
于1679年发明。
二进制数据是用0和1两个数码来表示的数。它的基数为2,进位规则是“逢二进一
”。
二进制广泛应用于我们生活的方方面面。比如,广泛使用的摩尔斯电码(Morse Code),它由两种基本信号组成:短促的点信号“·
”,读“滴
”;保持一定时间的长信号“—
”,读“嗒
”。然后,组成了26个字母,从而拼写出相应的单词。
记忆技巧:
我们偶尔会看到的:SOS,即为:
6.4 二进制转十进制
二进制如何表示整数?
- 计算机数据的存储使用二进制
补码
形式存储,并且最高位是符号位
。- 正数:
最高位是0
- 负数:
最高位是1
- 正数:
- 规 定
- 正数:原码=反码=补码
- 负数:
- 负数的
原码
:把十进制转为二进制,然后最高位设置为1 - 负数的
反码
:在原码的基础上,最高位不变,其余位取反(0变1,1变0) - 负数的
补码
:反码+1
- 负数的
二进制转十进制:权相加法
针对于byte数据举例来说:
例如:byte类型(1个字节,8位)
25 ==> 原码 0001 1001 ==> 反码 0001 1001 –>补码 0001 1001
-25 ==>原码 1001 1001 ==> 反码1110 0110 ==>补码 1110 0111
1 | 整数: |
一个字节可以存储的整数范围是多少?
1 | //1个字节:8位 |
6.5 十进制转二进制
十进制转二进制:除2取余的逆
6.6 二进制与八进制、十六进制间的转换
二进制转八进制(三合一)
二进制转十六进制(四合一)
八进制、十六进制转二进制(一拆多)
6.7 各进制间的转换
练习:
1 | 1.将以下十进制数转换为十六进制和二进制 |
7. 运算符(Operator)(掌握)
运算符是一种特殊的符号,用以表示数据的运算、赋值和比较等。
运算符的分类:
- 按照
功能
分为:算术运算符、赋值运算符、比较(或关系)运算符、逻辑运算符、位运算符、条件运算符、Lambda运算符
分类 | 运算符 |
---|---|
算术运算符(7个) | +、-、*、/、%、++、– |
赋值运算符(12个) | =、+=、-=、*=、/=、%=、>>=、<<=、>>>=、&=、|=、^=等 |
比较(或关系)运算符(6个) | >、>=、<、<=、==、!= |
逻辑运算符(6个) | &、|、^、!、&&、|| |
位运算符(7个) | &、|、^、~、<<、>>、>>> |
条件运算符(1个) | (条件表达式)?结果1:结果2 |
Lambda运算符(1个) | ->(第18章时讲解) |
- 按照
操作数个数
分为:一元运算符(单目运算符)、二元运算符(双目运算符)、三元运算符 (三目运算符)
分类 | 运算符 |
---|---|
一元运算符(单目运算符) | 正号(+)、负号(-)、++、–、!、~ |
二元运算符(双目运算符) | 除了一元和三元运算符剩下的都是二元运算符 |
三元运算符 (三目运算符) | (条件表达式)?结果1:结果2 |
7.1 算术运算符
7.1.1 基本语法
举例1:加减乘除模
1 | public class ArithmeticTest1 { |
举例2:“+”号的两种用法
- 第一种:对于
+
两边都是数值的话,+
就是加法的意思 —– 数值+数值=数值 - 第二种:对于
+
两边至少有一边是字符串的话,+
就是拼接的意思 —– 字符串+数值=字符串
1 | public class ArithmeticTest2 { |
举例3:自加自减运算
理解:++
运算,表示自增1
。同理,--
运算,表示自减1
,用法与++ 一致。
1、单独使用
- 变量在单独运算的时候,变量
前++
和变量后++
,是没有区别的。 - 变量
前++
:例如++a
。 - 变量
后++
:例如a++
。
1 | public class ArithmeticTest3 { |
2、复合使用
- 和
其他变量放在一起使用
或者和输出语句放在一起使用
,前++
和后++
就产生了不同。
- 变量
前++
:变量先自增1,然后再运算。 - 变量
后++
:变量先运算,然后再自增1。
1 | public class ArithmeticTest4 { |
7.1.2 案例与练习
案例1:
1 | 随意给出一个整数,打印显示它的个位数,十位数,百位数的值。 |
1 | /** |
拓展:获取一个四位数的个位,十位,百位,千位
1 | /** |
案例2:为抵抗洪水,战士连续作战89小时,编程计算共多少天零多少小时?
1 | /** |
练习1:算术运算符:自加、自减
1 | public class ArithmeticExer3{ |
练习2:
1 | System.out.println("5+5=" + 5 + 5); //打印结果是? 5+5=55 因为string+int+int => string+int => string |
练习3:
1 | byte bb1 = 127; |
练习4:
1 | int i = 1; |
练习5:(企业真题)写出下列程序的输出结果
1 | int i = 2; |
7.2 赋值运算符
7.2.1 基本语法
符号:=
- 当“=”两侧数据类型不一致时,可以使用自动类型转换或使用强制类型转换原则进行处理。
- 支持
连续赋值
(先定义好之后才能连续赋值)。
扩展赋值运算符: +=、 -=、*=、 /=、%=
赋值运算符 符号解释 +=
将符号 左边的值
和右边的值
进行相加
操作,最后将结果赋值给左边的变量
-=
将符号 左边的值
和右边的值
进行相减
操作,最后将结果赋值给左边的变量
*=
将符号 左边的值
和右边的值
进行相乘
操作,最后将结果赋值给左边的变量
/=
将符号 左边的值
和右边的值
进行相除
操作,最后将结果赋值给左边的变量
%=
将符号 左边的值
和右边的值
进行取余
操作,最后将结果赋值给左边的变量
1 | public class SetValueTest1 { |
7.2.2 练习
练习1:
1 | short s = 3; |
练习2:
1 | int i = 1; |
练习3:
1 | int m = 2; |
练习4:
1 | int n = 10; |
练习5:你有几种办法实现变量值减1?变量值减2呢?
1 | /** |
7.3 比较(关系)运算符
比较运算符的结果都是boolean型,结果只有true/false。
> < >= <= :只适用于基本数据类型(除boolean类型之外)
== != :适用于基本数据类型和引用数据类型
比较运算符“
==
”不能误写成“=
”
举例:
1 | class CompareTest { |
思考:
1 | boolean b1 = false; |
7.4 逻辑运算符
7.4.1 基本语法
逻辑运算符,boolean 逻辑运算符 boolean =boolean 。
运算符说明:
- & 和 &&:表示”且”关系,当符号左右两边布尔值都是true时,结果才能为true。否则,为false。
- | 和 || :表示”或”关系,当符号两边布尔值有一边为true时,结果为true。当两边都为false时,结果为false。
- ! :表示”非”关系,当变量布尔值为true时,结果为false。当变量布尔值为false时,结果为true。
- ^ :当符号左右两边布尔值不同时,结果为true。当两边布尔值相同时,结果为false。
- 理解:
异或,追求的是“异”!
- 理解:
逻辑运算符用于连接布尔型表达式,在Java中不可以写成 3 < x < 6,应该写成x > 3 & x < 6 。
区分“&”和“&&”:
相同点:如果符号左边是true,则二者都执行符号右边的操作
不同点:& : 如果符号左边是false,则继续执行符号右边的操作
&& :如果符号左边是false,则不再继续执行符号右边的操作(有一个不符合可以跳过)
- 建议:开发中,推荐使用 &&
区分“|”和“||”:
- 相同点:如果符号左边是false,则二者都执行符号右边的操作
- 不同点:| : 如果符号左边是true,则继续执行符号右边的操作
|| :如果符号左边是true,则不再继续执行符号右边的操作(有一个符合可以跳过)
- 建议:开发中,推荐使用 ||
代码举例:
1 | public class LoginTest { |
7.4.2 案例与练习
案例:
1 | 1. 定义类 CompareLogicExer |
1 | public class CompareLogicExer { |
练习1:区分 & 和 && (&&被推荐使用,因为有一个不符合就跳出判断了)
1 | int x = 1; |
1 | int x = 1,y = 1; |
练习2:区分 | 和 ||(||被推荐使用,因为有一个符合就跳出判断了)
1 | int x = 1,y = 1; |
1 | int x = 1,y = 1; |
练习3:程序输出
1 | class Test { |
1 | //结果为: |
7.5 位运算符(难点、非重点)
7.5.1 基本语法
- 位运算符的运算过程都是基于二进制的补码运算
(1)左移:<<
运算规则:在一定范围内,数据每向左移动n位,相当于原数据*2^n。(正数、负数都适用)
【注意】当左移的位数n超过该数据类型的总位数时,相当于左移(n-总位数)位
1 | 3<<4 类似于 3*2^4 => 3*16 => 48 |
1 | -3<<4 类似于 -3*2^4 => -3*16 => -48 |
(2)右移:>>
运算规则:在一定范围内,数据每向右移动n位,相当于原数据/(2^n)。(正数、负数都适用)
【注意】如果不能整除,向下取整
。
1 | 69>>4 类似于 69/(2^4) = 69/16 =4 |
1 | -69>>4 类似于 -69/(2^4) = -69/16 = -5 |
(3)无符号右移:>>>
运算规则:往右移动后,左边空出来的位直接补0。(正数、负数都适用)
1 | 69>>>4 类似于 69/2的4次 = 69/16 =4 |
1 | -69>>>4 结果:268435451 |
(4)按位与:&
运算规则:对应位都是1才为1,否则为0。
1 & 1 结果为1
1 & 0 结果为0
0 & 1 结果为0
- 0 & 0 结果为0
1 | 9 & 7 = 1 |
1 | -9 & 7 = 7 |
(5)按位或:|
运算规则:对应位只要有1即为1,否则为0。
1 | 1 结果为1
1 | 0 结果为1
0 | 1 结果为1
0 & 0 结果为0
1 | 9 | 7 //结果: 15 |
1 | -9 | 7 //结果: -9 |
(6)按位异或:^
运算规则:对应位一个为1一个为0,才为1,否则为0(找异!)。
1 ^ 1 结果为0
1 ^ 0 结果为1
0 ^ 1 结果为1
- 0 ^ 0 结果为0
1 | 9 ^ 7 //结果为14 |
1 | -9 ^ 7 //结果为-16 |
(7)按位取反:~
运算规则:对应位为1,则结果为0;对应位为0,则结果为1。
~0就是1
~1就是0
1 | ~9 //结果:-10 |
1 | ~-9 //结果:8 |
7.5.2 举例
举例1:
举例2:体会 m = k ^ n = (m ^ n) ^ n
7.5.3 案例
案例1:高效的方式计算2 * 8的值(经典面试题)
1 | 答案:2 << 3 、 8 << 1 |
案例2:如何交换两个int型变量的值?String呢?
1 | /** |
7.6 条件运算符
7.6.1 基本语法
- 条件运算符格式:
1 | (条件表达式)? 表达式1:表达式2 |
说明:条件表达式是boolean类型的结果,根据boolean的值选择表达式1或表达式2
如果运算后的结果赋给新的变量,要求表达式1和表达式2为同种或兼容的类型
1 | public static void main(String[] args) { |
7.6.2 案例
案例1:获取两个数中的较大值
1 | /** |
案例2:获取三个数中的最大值
1 | /** |
案例3:今天是周2,10天以后是周几?
要求:控制台输出”今天是周2,10天以后是周x”。
1 | /** |
7.6.3 与if-else的转换关系
- 条件运算符——->if-else结构。
- 开发中,如果既可以使用条件运算符(执行效率高,推荐!),又可以使用if-else。
1 | //if-else实现获取两个数的较大值 |
7.7 运算符优先级
运算符有不同的优先级,所谓优先级就是在表达式运算中的运算符顺序。
上一行中的运算符总是优先于下一行的。
优先级 | 运算符说明 | Java运算符 |
---|---|---|
1 | 括号 | () 、[] 、{} |
2 | 正负号 | + 、- |
3 | 单元运算符 | ++ 、-- 、~ 、! |
4 | 乘法、除法、求余 | * 、/ 、% |
5 | 加法、减法 | + 、- |
6 | 移位运算符 | << 、>> 、>>> |
7 | 关系运算符 | < 、<= 、>= 、> 、instanceof |
8 | 等价运算符 | == 、!= |
9 | 按位与 | & |
10 | 按位异或 | ^ |
11 | 按位或 | ` |
12 | 条件与 | && |
13 | 条件或 | ` |
14 | 三元运算符 | ? : |
15 | 赋值运算符 | = 、+= 、-= 、*= 、/= 、%= |
16 | 位赋值运算符 | &= 、` |
开发建议:
- 不要过多的依赖运算的优先级来控制表达式的执行顺序,这样可读性太差,尽量
使用()来控制
表达式的执行顺序。- 不要把一个表达式写得过于复杂,如果一个表达式过于复杂,则把它
分成几步
来完成。例如:
(num1 + num2) * 2 > num3 && num2 > num3 ? num3 : num1 + num2;
8. 【拓展】关于字符集
8.1 字符集
- 编码与解码
计算机中储存的信息都是用二进制数
表示的,而我们在屏幕上看到的数字、英文、标点符号、汉字等字符是二进制数转换之后的结果。按照某种规则,将字符存储到计算机中,称为编码
。反之,将存储在计算机中的二进制数按照某种规则解析显示出来,称为解码
。
字符编码(Character Encoding) : 就是一套自然语言的字符与二进制数之间的对应规则。
字符集:也叫编码表。是一个系统支持的所有字符的集合,包括各国家文字、标点符号、图形符号、数字等。
8.2 ASCII码
- ASCII码(American Standard Code for Information Interchange,美国信息交换标准代码):上个世纪60年代,美国制定了一套字符编码,对
英语字符
与二进制位之间的关系,做了统一规定。这被称为ASCII码。 - ASCII码用于显示现代英语,主要包括控制字符(回车键、退格、换行键等)和可显示字符(英文大小写字符、阿拉伯数字和西文符号)。
- 基本的ASCII字符集,使用7位(bits)表示一个字符(最前面的1位统一规定为0),共
128个
字符。比如:空格“SPACE”是32(二进制00100000),大写的字母A是65(二进制01000001)。 - 缺点:不能表示所有字符。
8.3 ISO-8859-1字符集
- 拉丁码表,别名Latin-1,用于显示欧洲使用的语言,包括荷兰语、德语、意大利语、葡萄牙语等
- ISO-8859-1使用单字节编码,兼容ASCII编码。
8.4 GBxxx字符集
- GB就是国标的意思,是为了
显示中文
而设计的一套字符集。 - GB2312:简体中文码表。一个小于127的字符的意义与原来相同,即向下兼容ASCII码。但两个大于127的字符连在一起时,就表示一个汉字,这样大约可以组合了包含
7000多个简体汉字
,此外数学符号、罗马希腊的字母、日文的假名们都编进去了,这就是常说的”全角”字符,而原来在127号以下的那些符号就叫”半角”字符了。 - GBK:最常用的中文码表。是在GB2312标准基础上的扩展规范,使用了
双字节
编码方案,共收录了21003个
汉字,完全兼容GB2312标准,同时支持繁体汉字
以及日韩汉字等。 - GB18030:最新的中文码表。收录汉字
70244个
,采用多字节
编码,每个字可以由1个、2个或4个字节组成。支持中国国内少数民族的文字,同时支持繁体汉字以及日韩汉字等。
8.5 Unicode码
Unicode编码为表达
任意语言的任意字符
而设计,也称为统一码、标准万国码。Unicode 将世界上所有的文字用2个字节
统一进行编码,为每个字符设定唯一的二进制编码,以满足跨语言、跨平台进行文本处理的要求。Unicode 的缺点:这里有三个问题:
- 第一,英文字母只用一个字节表示就够了,如果用更多的字节存储是
极大的浪费
。 - 第二,如何才能
区别Unicode和ASCII
?计算机怎么知道两个字节表示一个符号,而不是分别表示两个符号呢? - 第三,如果和GBK等双字节编码方式一样,用最高位是1或0表示两个字节和一个字节,就少了很多值无法用于表示字符,
不够表示所有字符
。
- 第一,英文字母只用一个字节表示就够了,如果用更多的字节存储是
Unicode在很长一段时间内无法推广,直到互联网的出现,为解决Unicode如何在网络上传输的问题,于是面向传输的众多 UTF(UCS Transfer Format)标准出现。具体来说,有三种编码方案,UTF-8、UTF-16和UTF-32。
8.6 UTF-8
- Unicode是字符集,UTF-8、UTF-16、UTF-32是三种
将数字转换到程序数据
的编码方案。顾名思义,UTF-8就是每次8个位传输数据,而UTF-16就是每次16个位。其中,UTF-8 是在互联网上使用最广
的一种 Unicode 的实现方式。 - 互联网工程工作小组(IETF)要求所有互联网协议都必须支持UTF-8编码。所以,我们开发Web应用,也要使用UTF-8编码。UTF-8 是一种
变长的编码方式
。它可以使用 1-4 个字节表示一个符号它使用一至四个字节为每个字符编码,编码规则:- 128个US-ASCII字符,只需一个字节编码。
- 拉丁文等字符,需要二个字节编码。
- 大部分常用字(含中文),使用三个字节编码。
- 其他极少使用的Unicode辅助字符,使用四字节编码。
- 举例
Unicode符号范围 | UTF-8编码方式
1 | (十六进制) | (二进制) |
8.7 小结
注意:在中文操作系统上,ANSI(美国国家标准学会、AMERICAN NATIONAL STANDARDS INSTITUTE: ANSI)编码即为GBK;在英文操作系统上,ANSI编码即为ISO-8859-1。
第03章_流程控制语句
本章专题与脉络
流程控制语句是用来控制程序中各
语句执行顺序
的语句,可以把语句组合成能完成一定功能
的小逻辑模块。程序设计中规定的
三种
流程结构,即:- 顺序结构
- 程序从上到下逐行地执行,中间没有任何判断和跳转。
- 分支结构
- 根据条件,选择性地执行某段代码。
- 有
if…else
和switch-case
两种分支语句。
- 循环结构
- 根据循环条件,重复性的执行某段代码。
- 有
for
、while
、do-while
三种循环语句。 - 补充:JDK5.0 提供了
foreach
循环,方便的遍历集合、数组元素。(第12章集合中讲解)
- 顺序结构
生活中、工业生产中流程控制举例
1. 顺序结构
顺序结构就是程序从上到下逐行
地执行。表达式语句都是顺序执行的。并且上一行对某个变量的修改对下一行会产生影响。
1 | public class StatementTest{ |
Java中定义变量时采用合法的前向引用
。如:
1 | public static void main(String[] args) { |
错误形式:
1 | public static void main(String[] args) { |
2. 分支语句
2.1 if-else条件判断结构
2.1.1 基本语法
结构1:单分支条件判断:if
格式:
1 | if(条件表达式){ |
说明:
条件表达式必须是布尔表达式(关系表达式或逻辑表达式)/ 布尔变量。
执行流程:
- 首先判断条件表达式是true/false
- 如果是true就执行语句块
- 如果是false就不执行语句块
结构2:双分支条件判断:if…else
格式:
1 | if(条件表达式) { |
执行流程:
- 首先判断条件表达式看其结果是true还是false
- 如果是true就执行语句块1
- 如果是false就执行语句块2
结构3:多分支条件判断:if…else if…else
格式:
1 | if (条件表达式1) { |
说明:
一旦条件表达式为true,则进入执行相应的语句块。执行完对应的语句块之后,就跳出当前结构。
执行流程:
- 首先判断关系表达式1看其结果是true还是false
- 如果是true就执行语句块1,然后结束当前多分支
- 如果是false就继续判断关系表达式2看其结果是true还是false
- 如果是true就执行语句块2,然后结束当前多分支
- 如果是false就继续判断关系表达式…看其结果是true还是false
…
n. 如果没有任何关系表达式为true,就执行语句块n+1,然后结束当前多分支。
2.1.2 应用举例
案例1:成年人心率的正常范围是每分钟60-100次。体检时,如果心率不在此范围内,则提示需要做进一步的检查。
1 | public class IfElseTest1 { |
案例2:定义一个整数,判定是偶数还是奇数
1 | public class IfElseTest2 { |
案例3:
1 | 岳小鹏参加Java考试,他和父亲岳不群达成承诺: |
1 | public class IfElseTest3 { |
当条件表达式之间是“
互斥
”关系时(即彼此没有交集),条件判断语句及执行语句间顺序无所谓。当条件表达式之间是“
包含
”关系时,“小上大下 / 子上父下
”,否则范围小的条件表达式将不可能被执行。
2.1.3 if…else嵌套
在 if 的语句块中,或者是在else语句块中,又包含了另外一个条件判断(可以是单分支、双分支、多分支),就构成了嵌套结构
。
执行的特点:
(1)如果是嵌套在if语句块中的,只有当外部的if条件满足,才会去判断内部的条件
(2)如果是嵌套在else语句块中的,只有当外部的if条件不满足,进入else后,才会去判断内部的条件
案例4:由键盘输入三个整数分别存入变量num1、num2、num3,对它们进行排序(使用 if-else if-else),并且从小到大输出。
1 | class IfElseTest4 { |
2.1.4 其它说明
- 语句块只有一条执行语句时,一对
{}可以省略
,但建议保留 - 当if-else结构是“多选一”时,最后的
else是可选的
,根据需要可以省略
2.1.5 练习
练习1:
1 | //1)对下列代码,若有输出,指出输出结果。 |
练习2:
1 | boolean b = true; |
练习3:
定义两个整数,分别为small 和 big,如果第一个整数small大于第二个整数big,就交换。输出显示small和big变量的值。
1 | public class IfElseExer3 { |
练习4:小明参加期末Java考试,通过考试成绩,判断其Java等级,成绩范围[0,100]
- 90-100 优秀
- 80-89 好
- 70-79 良
- 60-69 及格
- 60以下 不及格
1 | import java.util.Scanner; |
1 | import java.util.Scanner; |
练习5:
1 | 编写程序,声明2个int型变量并赋值。判断两数之和,如果大于等于50,打印“hello world!” |
1 | public class IfElseExer5 { |
练习6:
1 | 编写程序,声明2个double型变量并赋值。判断第一个数大于10.0,且第2个数小于20.0,打印两数之和。否则,打印两数的乘积。 |
1 | public class IfElseExer6 { |
练习7:判断水的温度
1 | 如果大于95℃,则打印“开水”; |
1 | public class IfElseExer7 { |
2.2 switch-case选择结构
2.2.1 基本语法
语法格式:
1 | switch(表达式){ |
执行流程图:
执行过程:
第1步:根据switch中表达式的值,依次匹配各个case。如果表达式的值等于某个case中的常量值,则执行对应case中的执行语句。
第2步:执行完此case的执行语句以后,
情况1:如果遇到break,则执行break并跳出当前的switch-case结构
情况2:如果没有遇到break,则会继续执行当前case之后的其它case中的执行语句。—>case穿透
…
直到遇到break关键字或执行完所有的case及default的执行语句,跳出当前的switch-case结构
使用注意点:
switch(表达式)中表达式的值必须是下述几种类型之一:byte,short,char,int,枚举 (jdk 5.0),String (jdk 7.0);
case子句中的值必须是常量,不能是变量名或不确定的表达式值或范围;
同一个switch语句,所有case子句中的常量值互不相同;
break语句用来在执行完一个case分支后使程序跳出switch语句块;
如果没有break,程序会顺序执行到switch结尾;
default子句是可选的。同时,位置也是灵活的。当没有匹配的case时,执行default语句。
2.2.2 应用举例
案例1:
1 | public class SwitchCaseTest1 { |
案例2:
1 | public class SwitchCaseTest2 { |
错误举例:
1 | int key = 10; |
案例3:使用switch-case实现:对学生成绩大于60分的,输出“合格”。低于60分的,输出“不合格”。
1 | class SwitchCaseTest3 { |
2.2.3 利用case的穿透性
在switch语句中,如果case的后面不写break,将出现穿透现象,也就是一旦匹配成功,不会在判断下一个case的值,直接向后运行,直到遇到break或者整个switch语句结束,执行终止。
案例4:编写程序:从键盘上输入2023年的“month”和“day”,要求通过程序输出输入的日期为2023年的第几天。
1 | import java.util.Scanner; |
拓展:
1 | 从键盘分别输入年、月、日,判断这一天是当年的第几天 |
1 | import java.util.Scanner; |
案例5:根据指定的月份输出对应季节
1 | import java.util.Scanner; |
常见错误实现:
1 | switch(month){ |
使用if-else实现:
1 | if ((month == 1) || (month == 2) || (month == 12)) { |
2.2.4 if-else语句与switch-case语句比较
结论:凡是使用switch-case的结构都可以转换为if-else结构。反之,不成立。
开发经验:如果既可以使用switch-case,又可以使用if-else,建议使用switch-case。因为效率稍高。
细节对比:
- if-else语句优势
- if语句的条件是一个布尔类型值,if条件表达式为true则进入分支,可以用于范围的判断,也可以用于等值的判断,
使用范围更广
。 - switch语句的条件是一个常量值(byte,short,int,char,枚举,String),只能判断某个变量或表达式的结果是否等于某个常量值,
使用场景较狭窄
。
- if语句的条件是一个布尔类型值,if条件表达式为true则进入分支,可以用于范围的判断,也可以用于等值的判断,
- switch语句优势
- 当条件是判断某个变量或表达式是否等于某个固定的常量值时,使用if和switch都可以,习惯上使用switch更多。因为
效率稍高
。当条件是区间范围的判断时,只能使用if语句。 - 使用switch可以利用
穿透性
,同时执行多个分支,而if…else没有穿透性。
- 当条件是判断某个变量或表达式是否等于某个固定的常量值时,使用if和switch都可以,习惯上使用switch更多。因为
- if-else语句优势
案例:只能使用 if-else
从键盘输入一个整数,判断是正数、负数、还是零。
1 | import java.util.Scanner; |
2.2.5 练习
练习1:从键盘输入星期的整数值,输出星期的英文单词
1 | import java.util.Scanner; |
练习2:
1 | 使用 switch 把小写类型的 char型转为大写。只转换 a, b, c, d, e. 其它的输出 “other”。 |
1 | public class SwitchCaseExer2 { |
练习3:
1 | 编写程序:从键盘上读入一个学生成绩,存放在变量score中,根据score的值输出其对应的成绩等级: |
1 | public class SwitchCaseExer3 { |
练习4:
1 | 编写一个程序,为一个给定的年份找出其对应的中国生肖。中国的生肖基于12年一个周期,每年用一个动物代表:rat、ox、tiger、rabbit、dragon、snake、horse、sheep、monkey、rooster、dog、pig。 |
1 | /** |
练习5:押宝游戏
1 | 随机产生3个1-6的整数,如果三个数相等,那么称为“豹子”,如果三个数之和大于9,称为“大”,如果三个数之和小于等于9,称为“小”,用户从键盘输入押的是“豹子”、“大”、“小”,并判断是否猜对了 |
1 | import java.util.Scanner; |
练习6:
1 | 使用switch语句改写下列if语句: |
1 | int a = 3; |
3. 循环语句
理解:循环语句具有在
某些条件
满足的情况下,反复执行
特定代码的功能。循环结构分类:
- for 循环
- while 循环
- do-while 循环
循环结构
四要素
:- 初始化部分
- 循环条件部分
- 循环体部分
- 迭代部分
3.1 for循环
3.1.1 基本语法
语法格式:
1 | for (①初始化部分; ②循环条件部分; ④迭代部分){ |
执行过程:①-[②-③-④]-[②-③-④]-[②-③-④]-…..-直到②不满足
图示:
说明:
- for(;;)中的两个;不能多也不能少
- ①初始化部分可以声明多个变量,但必须是同一个类型,用逗号分隔 –例如: int i=10,j=12,k=13;
- ②循环条件部分为boolean类型表达式,当值为false时,退出循环
- ④可以有多个变量更新,用逗号分隔 –例如: ;i++,j++,k++;
3.1.2 应用举例
案例1:使用for循环重复执行某些语句
题目:输出5行HelloWorld
1 | public class ForTest1 { |
案例2:格式的多样性
题目:写出输出的结果
1 | public class ForTest2 { |
案例3:累加的思想
题目:遍历1-100以内的偶数,并获取偶数的个数,获取所有的偶数的和
1 | public class ForTest3 { |
案例4:结合分支结构使用
题目:输出所有的水仙花数,所谓水仙花数是指一个3位数,其各个位上数字立方和等于其本身。例如: 153 = 1*1*1 + 3*3*3 + 5*5*5
1 | public class ForTest4 { |
拓展:
1 | 打印出四位数字中“个位+百位”等于“十位+千位”并且个位数为偶数,千位数为奇数的数字,并打印符合条件的数字的个数。 |
案例5:结合break的使用
说明:输入两个正整数m和n,求其最大公约数和最小公倍数。
比如:12和20的最大公约数是4,最小公倍数是60。
1 | public class ForTest5 { |
说明:
1、我们可以在循环中使用break。一旦执行break,就跳出当前循环结构。
2、小结:如何结束一个循环结构?
结束情况1:循环结构中的循环条件部分返回false
结束情况2:循环结构中执行了break。
3、如果一个循环结构不能结束,那就是一个死循环!我们开发中要避免出现死循环。
3.1.3 练习
练习1:打印1~100之间所有奇数的和
1 | public class ForExer1 { |
练习2:打印1~100之间所有是7的倍数的整数的个数及总和(体会设置计数器的思想)
1 | public class ForExer2 { |
练习3:
编写程序从1循环到150,并在每行打印一个值,另外在每个3的倍数行上打印出“foo”,在每个5的倍数行上打印“biz”,在每个7的倍数行上打印输出“baz”。
1 | public class ForExer3 { |
3.2 while循环
3.2.1 基本语法
语法格式:
1 | ①初始化部分 |
执行过程:①-(②-③-④)-(②-③-④)-(②-③-④)-…-直到②不符合结束
图示:
说明:
- while(循环条件)中循环条件必须是boolean类型。
- 注意不要忘记声明④迭代部分。否则,循环将不能结束,变成死循环。
- for循环和while循环可以相互转换。二者没有性能上的差别。实际开发中,根据具体结构的情况,选择哪个格式更合适、美观。
- for循环与while循环的区别:初始化条件部分的作用域不同。
3.2.2 应用举例
案例1:输出5行HelloWorld!
1 | class WhileTest1 { |
案例2:遍历1-100的偶数,并计算所有偶数的和、偶数的个数(累加的思想)
1 | class WhileTest2 { |
案例3:猜数字游戏
1 | 随机生成一个100以内的数,猜这个随机数是多少? |
1 | /** |
案例4:折纸珠穆朗玛峰
1 | 世界最高山峰是珠穆朗玛峰,它的高度是8848.86米,假如我有一张足够大的纸,它的厚度是0.1毫米。 |
1 | /** |
3.2.3 练习
练习:从键盘输入整数,输入0结束,统计输入的正数、负数的个数。
1 | import java.util.Scanner; |
3.3 do-while循环
3.3.1 基本语法
语法格式:
1 | ①初始化部分; |
执行过程:①-③-④-②-③-④-②-③-④-…-②
图示:
说明:
- 结尾while(循环条件)中循环条件必须是boolean类型
- do{}while();最后有一个分号
- do-while结构的循环体语句是至少会执行一次,这个和for和while是不一样的
- 循环的三个结构for、while、do-while三者是可以相互转换的。
3.3.2 应用举例
案例1:遍历1-100的偶数,并计算所有偶数的和、偶数的个数(累加的思想)
1 | class DoWhileTest1 { |
案例2:体会do-while至少会执行一次循环体
1 | class DoWhileTest2 { |
案例3:ATM取款
1 | 声明变量balance并初始化为0,用以表示银行账户的余额,下面通过ATM机程序实现存款,取款等功能。 |
1 | import java.util.Scanner; |
3.3.3 练习
练习1:随机生成一个100以内的数,猜这个随机数是多少?
从键盘输入数,如果大了提示,大了;如果小了,提示小了;如果对了,就不再猜了,并统计一共猜了多少次。
1 | import java.util.Scanner; |
3.4 对比三种循环结构
- 三种循环结构都具有四个要素:
- 循环变量的初始化条件
- 循环条件
- 循环体语句块
- 循环变量的修改的迭代表达式
- 从循环次数角度分析
- do-while循环至少执行一次循环体语句。
- for和while循环先判断循环条件语句是否成立,然后决定是否执行循环体。
- 如何选择
- 遍历有明显的循环次数(范围)的需求,选择for循环
- 遍历没有明显的循环次数(范围)的需求,选择while循环
- 如果循环体语句块至少执行一次,可以考虑使用do-while循环
- 本质上:三种循环之间完全可以互相转换,都能实现循环的功能
3.5 “无限”循环
3.5.1 基本语法
语法格式:
- 最简单”无限”循环格式:
while(true)
,for(;;)
适用场景:
- 开发中,有时并不确定需要循环多少次,需要根据循环体内部某些条件,来控制循环的结束(使用break)。
- 如果此循环结构不能终止,则构成了死循环!开发中要避免出现死循环。
3.5.2 应用举例
案例1:实现爱你到永远…
1 | public class EndlessFor1 { |
1 | public class EndlessFor2 { |
1 | public class EndlessFor3 { |
思考:如下代码执行效果
1 | public class EndlessFor4 { |
案例2:从键盘读入个数不确定的整数,并判断读入的正数和负数的个数,输入为0时结束程序。
1 | import java.util.Scanner; |
3.6 嵌套循环(或多重循环)
3.6.1 使用说明
- 所谓嵌套循环,是指一个循环结构A的循环体是另一个循环结构B。比如,for循环里面还有一个for循环,就是嵌套循环。其中,for ,while ,do-while均可以作为外层循环或内层循环。
- 外层循环:循环结构A
- 内层循环:循环结构B
- 实质上,
嵌套循环就是把内层循环当成外层循环的循环体
。只有当内层循环的循环条件为false时,才会完全跳出内层循环,才可结束外层的当次循环,开始下一次的外层循环。 - 设外层循环次数为
m
次,内层为n
次,则内层循环体实际上需要执行m*n
次。 - 技巧:从二维图形的角度看,外层循环控制
行数
,内层循环控制列数
。 - 开发经验:实际开发中,我们最多见到的嵌套循环是两层。一般不会出现超过三层的嵌套循环。如果将要出现,一定要停下来重新梳理业务逻辑,重新思考算法的实现,控制在三层以内。否则,可读性会很差。
例如:两个for嵌套循环格式
1 | for(初始化语句①; 循环条件语句②; 迭代语句⑦) { |
执行特点:外层循环执行一次,内层循环执行一轮。
3.6.2 应用举例
案例1:打印5行6个*
1 | class ForForTest1 { |
案例2:打印5行直角三角形
1 | * |
1 | public class ForForTest2 { |
案例3:打印5行倒直角三角形
1 | ***** |
1 | public class ForForTest3 { |
案例4:打印”菱形”形状的图案
1 | * |
1 | public class ForForTest4 { |
案例5:九九乘法表
1 | public class ForForTest5 { |
3.6.3 练习
练习1:将一天中的时间打印到控制台
1 | public class ForForDemo { |
4. 关键字break和continue的使用
4.1 break和continue的说明
1 | 适用范围 在循环结构中使用的作用 相同点 |
此外,很多语言都有goto语句,goto语句可以随意将控制转移到程序中的任意一条语句上,然后执行它,但使程序容易出错。Java中的break和continue是不同于goto的。
4.2 应用举例
1 | class BreakContinueTest1 { |
4.3 带标签的使用
1 | break语句用于终止某个语句块的执行 |
continue语句出现在多层嵌套的循环语句体中时,也可以通过标签指明要跳过的是哪一层循环。
标号语句必须紧接在循环的头部。标号语句不能用在非循环语句的前面。
举例:
1 | class BreakContinueTest2 { |
4.4 经典案例
题目:找出100以内所有的素数(质数)?100000以内的呢?
目的:不同的代码的实现方式,可以效率差别很大。
分析:素数(质数):只能被1和它本身整除的自然数。 —> 从2开始,到这个数-1为止,此范围内没有这个数的约数。则此数是一个质数。
比如:2、3、5、7、11、13、17、19、23、…
实现方式1:
1 | class PrimeNumberTest { |
实现方式2:针对实现方式1进行优化
1 | class PrimeNumberTest1 { |
实现方式3(选做):使用continue + 标签
1 | class PrimeNumberTest2 { |
4.5 练习
练习1:
1 | 生成 1-100 之间的随机数,直到生成了 97 这个数,看看一共用了几次? |
1 | public class NumberGuessTest { |
5. Scanner:键盘输入功能的实现
如何从键盘获取不同类型(基本数据类型、String类型)的变量:使用Scanner类。
键盘输入代码的四个步骤:
- 导包:
import java.util.Scanner;
- 创建Scanner类型的对象:
Scanner scan = new Scanner(System.in);
- 调用Scanner类的相关方法(
next() / nextXxx()
),来获取指定类型的变量 - 释放资源:
scan.close();
- 导包:
注意:需要根据相应的方法,来输入指定类型的值。如果输入的数据类型与要求的类型不匹配时,会报异常导致程序终止。
5.1 各种类型的数据输入
案例:小明注册某交友网站,要求录入个人相关信息。如下:
请输入你的网名、你的年龄、你的体重、你是否单身、你的性别等情况。
1 | //① 导包 |
5.2 练习
练习1:
1 | 大家都知道,男大当婚,女大当嫁。那么女方家长要嫁女儿,当然要提出一定的条件:高:180cm以上;富:财富1千万以上;帅:是。 |
1 | import java.util.Scanner; |
练习2:
1 | 我家的狗5岁了,5岁的狗相当于人类多大呢?其实,狗的前两年每一年相当于人类的10.5岁,之后每增加一年就增加四岁。那么5岁的狗相当于人类多少年龄呢?应该是:10.5 + 10.5 + 4 + 4 + 4 = 33岁。 |
1 | import java.util.Scanner; |
6. 如何获取一个随机数(Math.random())
如何产生一个指定范围的随机整数?
1、Math类的random()的调用,会返回一个[0,1)范围的一个double型值
2、Math.random() * 100 —> [0,100)
(int)(Math.random() * 100) —> [0,99]
(int)(Math.random() * 100) + 5 —-> [5,104]
3、如何获取[a,b]
范围内的随机整数呢?(int)(Math.random() * (b - a + 1)) + a
4、举例
1 | class MathRandomTest { |
第05章_数组
本章专题与脉络
1. 数组的概述
1.1 为什么需要数组
需求分析1:
需要统计某公司50个员工的工资情况,例如计算平均工资、找到最高工资等。用之前知识,首先需要声明50个变量
来分别记录每位员工的工资,这样会很麻烦。因此我们可以将所有的数据全部存储到一个容器中统一管理,并使用容器进行计算。
需求分析2:
容器的概念:
- 生活中的容器:水杯(装水等液体),衣柜(装衣服等物品),集装箱(装货物等)。
- 程序中的容器:将多个数据存储到一起,每个数据称为该容器的元素。
1.2 数组的概念
数组(Array),是多个相同类型数据按一定顺序排列的集合,并使用一个名字命名,并通过编号的方式对这些数据进行统一管理。
数组中的概念
- 数组名
- 下标(或索引)
- 元素
- 数组的长度
数组的特点:
- 数组本身是
引用数据类型
,而数组中的元素可以是任何数据类型
,包括基本数据类型和引用数据类型。 - 创建数组对象会在内存中开辟一整块
连续的空间
。占据的空间的大小,取决于数组的长度和数组中元素的类型。 - 数组中的元素在内存中是依次紧密排列的,有序的。
- 数组,一旦初始化完成,其长度就是确定的。数组的
长度一旦确定,就不能修改
。 - 我们可以直接通过下标(或索引)的方式调用指定位置的元素,速度很快。
- 数组名中引用的是这块连续空间的首地址。
1.3 数组的分类
1、按照元素类型分:
- 基本数据类型元素的数组:每个元素位置存储基本数据类型的值
- 引用数据类型元素的数组:每个元素位置存储对象(本质是存储对象的首地址)(在面向对象部分讲解)
2、按照维度分:
- 一维数组:存储一组数据
- 二维数组:存储多组数据,相当于二维表,一行代表一组数据,只是这里的二维表每一行长度不要求一样。
2. 一维数组的使用
2.1 一维数组的声明
格式:
1 | //推荐 |
举例:
1 | int[] arr; |
数组的声明,需要明确:
(1)数组的维度:在Java中数组的符号是[],[]表示一维,[][]表示二维。
(2)数组的元素类型:即创建的数组容器可以存储什么数据类型的数据。元素的类型可以是任意的Java的数据类型。例如:int、String、Student等。
(3)数组名:就是代表某个数组的标识符,数组名其实也是变量名,按照变量的命名规范来命名。数组名是个引用数据类型的变量,因为它代表一组数据。
举例:
1 | public class ArrayTest1 { |
注意:Java语言中声明数组时不能指定其长度(数组中元素的个数)。 例如: int a[5]; //非法
2.2 一维数组的初始化
2.2.1 静态初始化
如果数组变量的初始化和数组元素的赋值操作同时进行,那就称为静态初始化。
静态初始化,本质是用静态数据(编译时已知)为数组初始化。此时数组的长度=sum(静态数据)。
一维数组声明和静态初始化格式1:
1
2
3
4
5
6数据类型[] 数组名 = new 数据类型[]{元素1,元 素2,元素3,...};
或
数据类型[] 数组名;
数组名 = new 数据类型[]{元素1,元素2,元素3,...};- new:关键字,创建数组使用的关键字。因为数组本身是引用数据类型,所以要用new创建数组实体。
例如,定义存储1,2,3,4,5整数的数组容器。
1 | int[] arr = new int[]{1,2,3,4,5};//正确 |
- 一维数组声明和静态初始化格式2:
1 | 数据类型[] 数组名 = {元素1,元素2,元素3...};//必须在一个语句中完成,不能分成两个语句写 |
例如,定义存储1,2,3,4,5整数的数组容器
1 | int[] arr = {1,2,3,4,5};//正确 |
举例:
1 | public class ArrayTest2 { |
2.2.2 动态初始化
数组变量的初始化和数组元素的赋值操作分开进行,即为动态初始化。
动态初始化中,只确定了元素的个数(即数组的长度),而元素值此时只是默认值,还并未真正赋自己期望的值。真正期望的数据需要后续单独一个一个赋值。
格式:
1 | 数组存储的元素的数据类型[] 数组名字 = new 数组存储的元素的数据类型[长度]; |
[长度]:数组的长度,表示数组容器中可以最多存储多少个元素。
注意:数组有定长特性,长度一旦指定,不可更改。和水杯道理相同,买了一个2升的水杯,总容量就是2升是固定的。
举例1:正确写法
1 | int[] arr = new int[5]; |
举例2:错误写法
1 | int[] arr = new int[5]{1,2,3,4,5};//错误的,后面有{}指定元素列表,就不需要在[]中指定元素个数了。 |
2.3 一维数组的使用
2.3.1 数组的长度
- 数组的元素总个数,即数组的长度
- 每个数组都有一个属性length指明它的长度,例如:arr.length 指明数组arr的长度(即元素个数)
- 每个数组都具有长度,而且一旦初始化,其长度就是确定,且是不可变的。
2.3.2 数组元素的引用
如何表示数组中的一个元素?
每一个存储到数组的元素,都会自动的拥有一个编号,从0开始,这个自动编号称为数组索引(index)或下标
,可以通过数组的索引/下标访问到数组中的元素。
1 | 数组名[索引/下标] |
数组的下标范围?
Java中数组的下标从[0]开始,下标范围是[0, 数组的长度-1],即[0, 数组名.length-1]
数组元素下标可以是整型常量或整型表达式。如a[3] , b[i] , c[6*i];
举例
1 | public class ArrayTest3 { |
2.4 一维数组的遍历
将数组中的每个元素分别获取出来,就是遍历
。for循环与数组的遍历是绝配。
举例1
1 | public class ArrayTest4 { |
举例2
1 | public class ArrayTest5 { |
2.5 数组元素的默认值
数组是引用类型,当我们使用动态初始化方式创建数组时,元素值只是默认值。例如:
1 | public class ArrayTest6 { |
对于基本数据类型而言,默认初始化值各有不同。
对于引用数据类型而言,默认初始化值为null(注意与0不同!)
1 | public class ArrayTest7 { |
3. 一维数组内存分析
3.1 Java虚拟机的内存划分
为了提高运算效率,就对空间进行了不同区域的划分,因为每一片区域都有特定的处理数据方式和内存管理方式。
区域名称 | 作用 |
---|---|
虚拟机栈 |
用于存储正在执行的每个Java方法的局部变量表等。局部变量表存放了编译期可知长度 的各种基本数据类型、对象引用,方法执行完,自动释放。 |
堆内存 |
存储对象(包括数组对象),new来创建的,都存储在堆内存。 |
方法区 |
存储已被虚拟机加载的类信息、常量、(静态变量)、即时编译器编译后的代码等数据。 |
本地方法栈 | 当程序中调用了native的本地方法时,本地方法执行期间的内存区域 |
程序计数器 | 程序计数器是CPU中的寄存器,它包含每一个线程下一条要执行的指令的地址 |
3.2 一维数组在内存中的存储
1、一个一维数组内存图
1 | public static void main(String[] args) { |
2、数组下标为什么是0开始
因为第一个元素距离数组首地址间隔0个单元格。
3、两个一维数组内存图
两个数组独立
1 | public static void main(String[] args) { |
4、两个变量指向一个一维数组
两个数组变量本质上代表同一个数组。
1 | public static void main(String[] args) { |
4. 一维数组的应用
案例1:升景坊单间短期出租4个月,550元/月(水电煤公摊,网费35元/月),空调、卫生间、厨房齐全。屋内均是IT行业人士,喜欢安静。所以要求来租者最好是同行或者刚毕业的年轻人,爱干净、安静。
1 | public class ArrayTest { |
案例2:输出英文星期几
用一个数组,保存星期一到星期天的7个英语单词,从键盘输入1-7,显示对应的单词
{“Monday”,”Tuesday”,”Wednesday”,”Thursday”,”Friday”,”Saturday”,”Sunday”}
1 | import java.util.Scanner; |
案例3:从键盘读入学生成绩,找出最高分,并输出学生成绩等级。
成绩>=最高分-10 等级为’A’
成绩>=最高分-20 等级为’B’
成绩>=最高分-30 等级为’C’
其余 等级为’D’
提示:先读入学生人数,根据人数创建int数组,存放学生成绩。
1 | /** |
5. 多维数组的使用
5.1 概述
- Java 语言里提供了支持多维数组的语法。
如果说可以把一维数组当成几何中的
线性图形
,那么二维数组就相当于是一个表格
,像Excel中的表格、围棋棋盘一样。应用举例1:
某公司2022年全年各个月份的销售额进行登记。按月份存储,可以使用一维数组。如下:
1
int[] monthData = new int[]{23,43,22,34,55,65,44,67,45,78,67,66};
如果改写为按
季度
为单位存储怎么办呢?1
int[][] quarterData = new int[][]{{23,43,22},{34,55,65},{44,67,45},{78,67,66}};
- 应用举例2:
高一年级三个班级均由多个学生姓名构成一个个数组。如下:
1 | String[] class1 = new String[]{"段誉","令狐冲","任我行"}; |
那从整个年级看,我们可以声明一个二维数组。如下:
1 | String[][] grade = new String[][]{{"段誉","令狐冲","任我行"},{"张三丰","周芷若"},{"赵敏","张无忌","韦小宝","杨过"}}; |
应用举例3:
蓝框的几个元素,可以使用一维数组来存储。但现在发现每个元素下还有下拉框,其内部还有元素,那就需要使用二维数组来存储:
使用说明
- 对于二维数组的理解,可以看成是一维数组array1又作为另一个一维数组array2的元素而存在。
- 其实,从数组底层的运行机制来看,其实没有多维数组。
5.2 声明与初始化
5.2.1 声明
二维数组声明的语法格式:
1 | //推荐 |
例如:
1 | public class Test20TwoDimensionalArrayDefine { |
面试:
1 | int[] x, y[]; |
5.2.2 静态初始化
格式:
1 | int[][] arr = new int[][]{{3,8,2},{2,7},{9,0,1,6}}; |
定义一个名称为arr的二维数组,二维数组中有三个一维数组
- 每一个一维数组中具体元素也都已初始化
- 第一个一维数组 arr[0] = {3,8,2};
- 第二个一维数组 arr[1] = {2,7};
- 第三个一维数组 arr[2] = {9,0,1,6};
- 第三个一维数组的长度表示方式:arr[2].length;
- 注意特殊写法情况:int[] x,y[]; x是一维数组,y是二维数组。
- 举例1:
1 | int[][] arr = {{1,2,3},{4,5,6},{7,8,9,10}};//声明与初始化必须在一句完成 |
- 举例2:
1 | public class TwoDimensionalArrayInitialize { |
5.2.3 动态初始化
如果二维数组的每一个数据,甚至是每一行的列数,需要后期单独确定,那么就只能使用动态初始化方式了。动态初始化方式分为两种格式:
格式1:规则二维表:每一行的列数是相同的
1 | //(1)确定行数和列数 |
举例:
1 | int[][] arr = new int[3][2]; |
定义了名称为arr的二维数组
二维数组中有3个一维数组
每一个一维数组中有2个元素
一维数组的名称分别为arr[0], arr[1], arr[2]
给第一个一维数组1脚标位赋值为78写法是:
arr[0][1] = 78;
格式2:不规则:每一行的列数不一样
1 | //(1)先确定总行数 |
举例:
1 | int[][] arr = new int[3][]; |
- 二维数组中有3个一维数组。
- 每个一维数组都是默认初始化值null (注意:区别于格式1)
- 可以对这个三个一维数组分别进行初始化:arr[0] = new int[3]; arr[1] = new int[1]; arr[2] = new int[2];
- 注:
int[][]arr = new int[][3];
//非法
练习:
1 | /* |
5.3 数组的长度和角标
- 二维数组的长度/行数:二维数组名.length
- 二维数组的某一行:二维数组名[行下标],此时相当于获取其中一组数据。它本质上是一个一维数组。行下标的范围:[0, 二维数组名.length-1]。此时把二维数组看成一维数组的话,元素是行对象。
- 某一行的列数:二维数组名[行下标].length,因为二维数组的每一行是一个一维数组。
- 某一个元素:二维数组名[行下标][列下标],即先确定行/组,再确定列。
1 | public class Test22TwoDimensionalArrayUse { |
5.4 二维数组的遍历
- 格式:
1 | for(int i=0; i<二维数组名.length; i++){ //二维数组对象.length |
- 举例:
1 | public class Test23TwoDimensionalArrayIterate { |
5.5 内存解析
二维数组本质上是元素类型是一维数组的一维数组。
1 | int[][] arr = { |
1 | //1、声明二维数组,并确定行数和列数 |
1 | //1、声明一个二维数组,并且确定行数 |
5.6 应用举例
案例1:获取arr数组中所有元素的和。
提示:使用for的嵌套循环即可。
1 | public class Exercise3 { |
案例2:声明:int[] x,y[]; 在给x,y变量赋值以后,以下选项允许通过编译的是:
1 | 声明:int[] x,y[]; 在给x,y变量赋值以后,以下选项允许通过编译的是: |
案例3:使用二维数组打印一个 10 行杨辉三角。
提示:
第一行有 1 个元素, 第 n 行有 n 个元素
每一行的第一个元素和最后一个元素都是 1
从第三行开始, 对于非第一个元素和最后一个元素的元素。即:
1
yanghui[i][j] = yanghui[i-1][j-1] + yanghui[i-1][j];
1 | /** |
6. 数组的常见算法
6.1 数值型数组特征值统计
- 这里的特征值涉及到:平均值、最大值、最小值、总和等
举例1:数组统计:求总和、均值
1 | public class TestArrayElementSum { |
举例2:求数组元素的总乘积
1 | public class TestArrayElementMul { |
举例3:求数组元素中偶数的个数
1 | public class TestArrayElementEvenCount { |
举例4:求数组元素的最大值
1 | public class TestArrayMax { |
举例5:找最值及其第一次出现的下标
1 | public class TestMaxIndex { |
举例6:找最值及其所有最值的下标
1 | public class Test13AllMaxIndex { |
优化
1 | public class Test13AllMaxIndex2 { |
举例7(难):输入一个整形数组,数组里有正数也有负数。数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和。求所有子数组的和的最大值。要求时间复杂度为O(n)。
例如:输入的数组为1, -2, 3, -10, -4, 7, 2, -5,和最大的子数组为3, 10, -4, 7, 2,因此输出为该子数组的和18。
1 | public class Test5 { |
举例8:评委打分
分析以下需求,并用代码实现:
(1)在编程竞赛中,有10位评委为参赛的选手打分,分数分别为:5,4,6,8,9,0,1,2,7,3
(2)求选手的最后得分(去掉一个最高分和一个最低分后其余8位评委打分的平均值)
1 | /** |
6.2 数组元素的赋值与数组复制
举例1:杨辉三角(见二维数组课后案例)
举例2:使用简单数组
(1)创建一个名为ArrayTest的类,在main()方法中声明array1和array2两个变量,他们是int[]类型的数组。
(2)使用大括号{},把array1初始化为8个素数:2,3,5,7,11,13,17,19。
(3)显示array1的内容。
(4)赋值array2变量等于array1,修改array2中的偶索引元素,使其等于索引值(如array[0]=0,array[2]=2)。打印出array1。 array2 = array1;
思考:array1和array2是什么关系?
拓展:修改题目,实现array2对array1数组的复制
举例3:一个数组,让数组的每个元素去除第一个元素,得到的商作为被除数所在位置的新值。
1 | public class Test3 { |
举例4:创建一个长度为6的int型数组,要求数组元素的值都在1-30之间,且是随机赋值。同时,要求元素的值各不相同。
1 | public class Test4 { |
举例5:扑克牌
案例:遍历扑克牌
遍历扑克牌,效果如图所示:
提示:使用两个字符串数组,分别保存花色和点数,再用一个字符串数组保存最后的扑克牌。
String[] hua = {“黑桃”,”红桃”,”梅花”,”方片”};
String[] dian = {“A”,”2”,”3”,”4”,”5”,”6”,”7”,”8”,”9”,”10”,”J”,”Q”,”K”};
1 | package com.atguigu3.common_algorithm.exer5; |
拓展:在上述基础上,增加大王、小王。
举例6:回形数
从键盘输入一个整数(1~20) ,则以该数字为矩阵的大小,把1,2,3…n*n 的数字按照顺时针螺旋的形式填入其中。
例如: 输入数字2,则程序输出:
1 2
4 3
输入数字3,则程序输出:
1 2 3
8 9 4
7 6 5
输入数字4, 则程序输出:
1 2 3 4
12 13 14 5
11 16 15 6
10 9 8 7
1 | //方式1 |
1 | //方式2 |
6.3 数组元素的反转
实现思想:数组对称位置的元素互换。
1 | public class TestArrayReverse1 { |
或
1 | public class TestArrayReverse2 { |
6.4 数组的扩容与缩容
数组的扩容
题目:现有数组 int[] arr = new int[]{1,2,3,4,5}; ,现将数组长度扩容1倍,并将10,20,30三个数据添加到arr数组中,如何操作?
1 | public class ArrTest1 { |
数组的缩容
题目:现有数组 int[] arr={1,2,3,4,5,6,7}。现需删除数组中索引为4的元素。
1 | public class ArrTest2 { |
6.5 数组的元素查找
1、顺序查找
顺序查找:挨个查看
要求:对数组元素的顺序没要求
1 | public class TestArrayOrderSearch { |
2、二分查找
举例:
实现步骤:
1 | //二分法查找:要求此数组必须是有序的。 |
6.6 数组元素排序
6.6.1 算法概述
定义
- 排序:假设含有n个记录的序列为{R1,R2,…,Rn},其相应的关键字序列为{K1,K2,…,Kn}。将这些记录重新排序为{Ri1,Ri2,…,Rin},使得相应的关键字值满足条Ki1<=Ki2<=…<=Kin,这样的一种操作称为排序。
- 通常来说,排序的目的是快速查找。
衡量排序算法的优劣:
时间复杂度
:分析关键字的比较次数和记录的移动次数常见的算法时间复杂度由小到大依次为:Ο(1)<Ο(log2n)<Ο(n)<Ο(nlog2n)<Ο(n2)<Ο(n3)<…<Ο(2n)<Ο(n!)<O(nn)
空间复杂度
:分析排序算法中需要多少辅助内存1
一个算法的空间复杂度S(n)定义为该算法所耗费的存储空间,它也是问题规模n的函数。
稳定性
:若两个记录A和B的关键字值相等,但排序后A、B的先后次序保持不变,则称这种排序算法是稳定的。
6.6.2 排序算法概述
排序算法分类:内部排序和外部排序
内部排序
:整个排序过程不需要借助于外部存储器(如磁盘等),所有排序操作都在内存中完成。外部排序
:参与排序的数据非常多,数据量非常大,计算机无法把整个排序过程放在内存中完成,必须借助于外部存储器(如磁盘)。外部排序最常见的是多路归并排序。可以认为外部排序是由多次内部排序组成。
十大内部排序算法
数组的排序算法很多,实现方式各不相同,时间复杂度、空间复杂度、稳定性也各不相同:
常见时间复杂度所消耗的时间从小到大排序:
O(1) < O(logn) < O(n) < O(nlogn) < O(n^2) < O(n^3) < O(2^n) < O(n!) < O(n^n)
注意,经常将以2为底n的对数简写成logn。
6.6.3 冒泡排序(Bubble Sort)
排序思想:
比较相邻的元素。如果第一个比第二个大(升序),就交换他们两个。
对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。
针对所有的元素重复以上的步骤,除了最后一个。
持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较为止。
动态演示:https://visualgo.net/zh/sorting
1 | /* |
冒泡排序优化(选讲)
1 | /* |
6.6.4 快速排序
快速排序(Quick Sort)由图灵奖
获得者Tony Hoare
发明,被列为20世纪十大算法之一
,是迄今为止所有内排序算法中速度最快的一种,快速排序的时间复杂度为O(nlog(n))。
快速排序通常明显比同为O(nlogn)的其他算法更快,因此常被采用,而且快排采用了分治法的思想,所以在很多笔试面试中能经常看到快排的影子。
排序思想:
从数列中挑出一个元素,称为”基准”(pivot),
重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区结束之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。
递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。
递归的最底部情形,是数列的大小是零或一,也就是永远都已经被排序好了。虽然一直递归下去,但是这个算法总会结束,因为在每次的迭代(iteration)中,它至少会把一个元素摆到它最后的位置去。
动态演示:https://visualgo.net/zh/sorting
图示1:
图示2:
第一轮操作:
第二轮操作:
6.6.5 内部排序性能比较与选择
性能比较
- 从平均时间而言:快速排序最佳。但在最坏情况下时间性能不如堆排序和归并排序。
- 从算法简单性看:由于直接选择排序、直接插入排序和冒泡排序的算法比较简单,将其认为是简单算法。对于Shell排序、堆排序、快速排序和归并排序算法,其算法比较复杂,认为是复杂排序。
- 从稳定性看:直接插入排序、冒泡排序和归并排序时稳定的;而直接选择排序、快速排序、 Shell排序和堆排序是不稳定排序
- 从待排序的记录数n的大小看,n较小时,宜采用简单排序;而n较大时宜采用改进排序。
选择
- 若n较小(如n≤50),可采用直接插入或直接选择排序。
当记录规模较小时,直接插入排序较好;否则因为直接选择移动的记录数少于直接插入,应选直接选择排序为宜。 - 若文件初始状态基本有序(指正序),则应选用直接插入、冒泡或随机的快速排序为宜;
- 若n较大,则应采用时间复杂度为O(nlgn)的排序方法:快速排序、堆排序或归并排序。
- 若n较小(如n≤50),可采用直接插入或直接选择排序。
7. Arrays工具类的使用
java.util.Arrays类即为操作数组的工具类,包含了用来操作数组(比如排序和搜索)的各种方法。 比如:
数组元素拼接
- static String toString(int[] a) :字符串表示形式由数组的元素列表组成,括在方括号(”[]”)中。相邻元素用字符 “, “(逗号加空格)分隔。形式为:[元素1,元素2,元素3。。。]
- static String toString(Object[] a) :字符串表示形式由数组的元素列表组成,括在方括号(”[]”)中。相邻元素用字符 “, “(逗号加空格)分隔。元素将自动调用自己从Object继承的toString方法将对象转为字符串进行拼接,如果没有重写,则返回类型@hash值,如果重写则按重写返回的字符串进行拼接。
数组排序
sort- static void sort(int[] a) :将a数组按照从小到大进行排序
- static void sort(int[] a, int fromIndex, int toIndex) :将a数组的[fromIndex, toIndex)部分按照升序排列
- static void sort(Object[] a) :根据元素的自然顺序对指定对象数组按升序进行排序。
- static
void sort(T[] a, Comparator<? super T> c) :根据指定比较器产生的顺序对指定对象数组进行排序。
数组元素的二分查找
binarySearch- static int binarySearch(int[] a, int key) 、static int binarySearch(Object[] a, Object key) :要求数组有序,在数组中查找key是否存在,如果存在返回第一次找到的下标,不存在返回负数。
数组的复制
copyof- static int[] copyOf(int[] original, int newLength) :根据original原数组复制一个长度为newLength的新数组,并返回新数组
- static
T[] copyOf(T[] original,int newLength):根据original原数组复制一个长度为newLength的新数组,并返回新数组 - static int[] copyOfRange(int[] original, int from, int to) :复制original原数组的[from,to)构成新数组,并返回新数组
- static
T[] copyOfRange(T[] original,int from,int to):复制original原数组的[from,to)构成新数组,并返回新数组
比较两个数组是否相等
equals- static boolean equals(int[] a, int[] a2) :比较两个数组的长度、元素是否完全相同
- static boolean equals(Object[] a,Object[] a2):比较两个数组的长度、元素是否完全相同
填充数组
fill- static void fill(int[] a, int val) :用val值填充整个a数组
- static void fill(Object[] a,Object val):用val对象填充整个a数组
- static void fill(int[] a, int fromIndex, int toIndex, int val):将a数组[fromIndex,toIndex)部分填充为val值
- static void fill(Object[] a, int fromIndex, int toIndex, Object val) :将a数组[fromIndex,toIndex)部分填充为val对象
举例:java.util.Arrays类的sort()方法提供了数组元素排序功能:
1 | import java.util.Arrays; |
8. 数组中的常见异常
8.1 数组角标越界异常
当访问数组元素时,下标指定超出[0, 数组名.length-1]的范围时,就会报数组下标越界异常:ArrayIndexOutOfBoundsException。
1 | public class TestArrayIndexOutOfBoundsException { |
创建数组,赋值3个元素,数组的索引就是0,1,2,没有3索引,因此我们不能访问数组中不存在的索引,程序运行后,将会抛出 ArrayIndexOutOfBoundsException
数组越界异常。在开发中,数组的越界异常是不能出现的,一旦出现了,就必须要修改我们编写的代码。
8.2 空指针异常
观察一下代码,运行后会出现什么结果。
1 | public class TestNullPointerException { |
因为此时数组的每一行还未分配具体存储元素的空间,此时arr[0]是null,此时访问arr[0][0]会抛出NullPointerException
空指针异常。
空指针异常在内存图中的表现
小结:空指针异常情况
1 | //举例一: |