Java从入门到精通
Java 语言基础
Java 语言概述
基础常识
软件:即一系列按照特定顺序组织的计算机数据和指令集合。分为:系统软件和应用软件
系统软件 :Windows Linux macOS Unix Android iOS……
应用软件:Word PowerPoint 画图板……
人机交互方式:图形化界面 vs 命令行方式
应用程序 = 算法 + 数据结构
常用 DOS 命令:
dir:列出点钱目录下的文件以及文件夹 md:创建目录 rd:删除目录 cd:进入指定目录 cd..:退回到上一级目录 cd/:退回到根目录 del:删除文件 exit:退出 DOS 命令行
计算机语言的发展迭代史
第一代:机器语言
第二代:汇编语言
第三代:高级语言
面向过程:C,Pascal, Fortran
面向对象:Java,JS,Python,Scala……
Java 语言版本迭代
1991年 Green 项目,开发语言最初命名为 Oak (橡树)
1994年,开发组意识到 Oak 非常适合于互联网
1996年,发布 JDK 1.0,约8.3万个网页应用Java技术来制作
1997年,发布 JDK 1.1,JavaOne 会议召开,创当时全球同类会议规模之最
1998年,发布 JDK 1.2,同年发布企业平台J2EE
1999年,Java 分成 J2SE、J2EE 和 J2ME,JSP/Servlet 技术诞生
2004年,发布里程碑式版本:JDK 1.5,为突出此版本的重要性,更名为 JDK 5.0
2005年,J2SE -> JavaSE,J2EE -> JavaEE,J2ME -> JavaME
2009年,Oracle 公司收购 SUN,交易价格74亿美元
2011年,发布 JDK 7.0
2014年,发布 JDK 8.0,是继 JDK 5.0 以来变化最大的版本
2017年,发布 JDK 9.0,最大限度实现模块化
2018年3月,发布 JDK 10.0,版本号也称为18.3
2018年9月,发布 JDK 11.0,版本号也称为18.9
Java语言的应用领域
Java Web 开发:后台开发
大数据开发
Android 应用程序开发:客户端开发
Java 语言的特点
面向对象
两个要素:类、对象
三个特征:封装、继承、多态
健壮性
去除了 C语言中的指针
自动的垃圾回收 --> 仍然会出现内存溢出、内存泄漏
跨平台性:write once, run anywhere:一次编译,到处运行
功劳归功于:JVM
JDK、JRE、JVM 的关系
JDK 的下载、安装和 path 环境变量的配置
为什么配置 path 环境变量?
path 环境变量:Windows 重装系统执行命令时所要搜寻的路径
为什么要配置 path:希望 Java 的开发工具在任何文件的文件路径下都可以执行成功
如何配置
https://blog.csdn.net/weixin_43344151/article/details/118917382
访问官网,下载对应的版本
https://www.oracle.com/java/technologies/javase/javase-jdk8-downloads.html
安装
注意问题:安装软件的路径中不能包含中文、空格
配置环境变量
此电脑 --> 右击“属性” --> 点击“高级系统设置” --> 点击“环境变量”
双击系统变量的PATH
依次点击确定
打开cmd窗口
运行java
,javac
,java -version
出现以下画面代表安装配置成功
开发体验 —— HelloWorld
编写
创建一个 Java 源文件:HelloWorld.java
public class HelloWorld{ public static void main(String[] args){ System.out.println("Hello, World!"); } }
编译
javac HelloWorld.java
运行
java HelloWorld
常见问题解决
总结第一个程序
Java程序编写、编译、运行的过程
编写:将编写的 Java代码保存在以 .java 结尾的源文件中
编译:使用 javac.exe 命令编译我们的 Java 源文件,格式:javac 源文件名.java
运行:使用 java.exe 命令解释运行我们的字节码文件,格式:java 类名
在一个 Java 源文件中可以声明多个 class,但是只能最多有一个类声明为 public 的,而且要求声明为 public 的类的类名必须与源文件名相同
程序的入口是 main() 方法,格式是固定的
输出语句:
System.out.println():先输出数据,然后换行
System.out.print():只输出数据
每一行执行语句都以 ; 结束
编译的过程:编译以后,会生成一个或多个字节码文件,字节码文件的文件名跟 Java 源文件中的类名相同
注释:Comment
分类:
单行注释://
单行注释:/* */
文档注释:/** */
作用:
对所写的程序进行解释说明,增强可读性,方便自己,方便别人
调试所写的代码
特点:
单行注释和多行注释,注释了的内容不参与编译,换句话说,编译以后生成的 .class 结尾的字节码文件不包含注释掉的内容
文档注释所注释的内容可以被 JDK 提供的工具 javadoc 所解析,生成一套以网页文件形式体现的该程序的说明文档
多行注释不可以嵌套使用
Java API 文档
API:application programming interface
习惯上:将语言提供的类库,都称为 API
API 文档:针对于提供的类库如何使用,给的一个说明书
良好的编程风格
正确的注释和注释风格
使用文档注释来注释整个类或整个方法。
如果注释方法中的某一个步骤,使用单行或多行注释。
正确的缩进和空白
使用一次tab操作,实现缩进
运算符两边习惯性各加一个空格。比如:2 + 4 * 5。
块的风格
- Java API 源代码选择了行尾风格
开发工具
文本编辑工具
记事本
UltraEdit
EditPlus
Notepad
TextPad
Java IDE
JBuilder
NetBeans
Eclipse
MyEclipse
IDEA
基本语法
关键字与标识符
关键字
定义:被 Java 语言赋予了特殊含义,用做专门用途的字符串(单词)
特点:关键字中所写字母都为小写
具体关键字:
保留字
定义:现有Java版本尚未使用,但以后版本可能会作为关键字使用。
具体哪些保留字:goto、const
注意:自己命名标识符时要避免使用这些保留字
标识符
定义:凡是自己可以起名字的地方都叫标识符
涉及到的结构:包名、类名、方法名、变量名、接口名、常量名
规则:(必须遵守,否则,编译不通过)
由26个英文字母大小写,0-9 ,_或$ 组成
数字不可以开头。
不可以使用关键字和保留字,但能包含关键字和保留字。
Java 中严格区分大小写,长度无限制。
标识符不能包含空格。
规范:(可以不遵守,不影响编译和运行,但是要求大家遵守
包名:多单词组成时所有字母都小写:xxxyyyzzz
类名、接口名:多单词组成时,所有单词的首字母大写:XxxYyyZzz
变量名、方法名:多单词组成时,第一个单词首字母小写,第二个单词开始每个 单词首字母大写:xxxYyyZzz
常量名:所有字母都大写。多单词时每个单词用下划线连接:XXX_YYY_ZZZ
注意点:
在起名字时,为了提高阅读性,要尽量有意义,“见名知意”。
代码整洁之道
介绍
软件中随处可见命名。我们给变量、函数、参数、类和包命名。我们给源代码及源代码所在目录命名。
这么多命名要做,不妨做好它。下文列出了取个好名字的几条简单规则。
名副其实,见名知意
变量名太随意,haha、list1、ok、theList 这些都没啥意义
避免误导
包含List、import、java等类名、关键字或特殊字;
字母o与数字0,字母l与数字1等
提防使用不同之处较小的名称。比如:XYZControllerForEfficientHandlingOfStrings与XYZControllerForEfficientStorageOfStrings
做有意义的区分
反面教材,变量名:a1、a2、a3
避免冗余,不要出现Variable、表字段中避免出现table、字符串避免出现nameString,直接name就行,知道是字符串类型
再比如:定义了两个类:Customer类和CustomerObject类,如何区分?
定义了三个方法:getActiveAccount()、getActiveAccounts()、getActiveAccountInfo(),如何区分?
使用读得出来的名称
不要使用自己拼凑出来的单词,比如:xsxm(学生姓名);genymdhms(生成日期,年、月、日、时、分、秒)
所谓的驼峰命名法,尽量使用完整的单词
使用可搜索的名称
一些常量,最好不直接使用数字,而指定一个变量名,这个变量名可以便于搜索到.
比如:找MAX_CLASSES_PER_STUDENT很容易,但想找数字7就麻烦了。
避免使用编码
匈牙利语标记法
即变量名表明该变量数据类型的小写字母开始。例如,szCmdLine的前缀sz表示“以零结束的字符串”。
成员前缀
避免使用前缀,但是Android中一个比较好的喜欢用m表示私有等,个人感觉比较好
接口和实现
作者不喜欢把接口使用I来开头,实现也希望只是在后面添加Impl
避免思维映射
比如传统上惯用单字母名称做循环计数器。所以就不要给一些非计数器的变量命名为:i、j、k等
类名
类名与对象名应该是名词与名词短语。如Customer、WikiPage、Account和AddressParser。避免使用Data或Info这样的类名。
不能使动词。比如:Manage、Process
方法名
方法名应当是动词或者动词短语。如postPayment、deletePage或save
别扮可爱
有的变量名叫haha、banana
别用eatMyShorts()表示abort()
每个概念对应一个词
项目中同时出现controllers与managers,为什么不统一使用其中一种?
对于那些会用到你代码的程序员,一以贯之的命名法简直就是天降福音。
别用双关语
有时可能使用add并不合适,比例insert、append。add表示完整的新添加的含义。
使用解决方案领域名称
看代码的都是程序员,所以尽量用那些计算机科学术语、算法名、模式名、数学术语,
依据问题所涉领域来命名不算是聪明的做法。
使用源自所涉问题领域的名称
如果不能用程序员熟悉的术语来给手头的工作命名,就采用从所涉问题领域而来的名称吧。
至少,负责维护代码的程序员就能去请教领域专家了。
添加有意义的语境
可以把相关的变量放到一个类中,使用这个类来表明语境。
不要添加没用的语境
名字中带有项目的缩写,这样完全没有必要。比如有一个名为“加油站豪华版”(Gas Station Deluxe)的项目,
在其中给每个类添加GSD前缀就不是什么好策略。
最后的话
取好名字最难的地方在于需要良好的描述技巧和共有文化背景
变量的使用
变量的分类
按数据类型分类
详细说明:
整型:byte(1字节) short(2字节) int(4字节) long(8字节)
byte 范围 -128~127
声明 long 型变量,必须以 “l” 或 ”L” 结尾
通常,定义整型变量时,使用 int 型
整型的常量默认类型是 int 型
浮点型:float (4字节) double(8字节)
浮点型表示带小数点的数值
float 表示数值的范围比 long 还大
定义 float 类型变量时,变量要以 “f” 或 “F” 结尾
通常,定义浮点型变量时,使用 double 型
浮点型的常量,默认类型为 double
字符型:char (2字节)
定义 char 型变量,通常使用一对 ‘’ ,内部只能写一个字符
表示方式
声明一个字符
转义字符
直接使用 Unicode 值来表示字符型常量
布尔型:boolean
只能取两个值之一:true false
通常在条件判断、循环结构中使用
按声明的位置分类
定义变量的格式
数据类型 变量名 = 变量值;
或者
数据类型 变量名;
变量名 = 变量值;
变量使用的注意点
变量必须先声明,后使用
变量都定义在其作用域内。在作用域内,它是有效的。换句话说,出了作用域就失效了
同一个作用域中不可以说明两个重名的变量
基本数据类型变量间运算规则
涉及到的基本数据类型
除了 boolean 类型之外的其它7种
自动类型转换(只涉及7种基本数据类型)
当容量小的数据类型的变量与容量大的数据类型的变量做运算时,结果 自动提升为容量大的数据类型
byte 、short 、char –> int –> long –> float –> double
特别地:当 byte 、short 、char 三种类型的变量做运算时,结果为 int 类型
说明:此时的容量大小指的是,表示数的范围的大小,比如,float 容量要大于 long 的容量
强制类型转换(只涉及7种基本数据类型)
自动类型提升运算的逆过程
需要使用强制符:()
注意点:强制类型转换,可能导致精度损失
String 与 8种基本数据类型间的运算
String 属于引用数据类型,翻译为:字符串
声明 String 类型变量时,使用一对 “”
String 可以和8种基本数据类型做运算,且运算只能是连接运算:+
运算的结果仍然是 String 类型
避免:
// 编译错误 String s = 123; // 编译错误 int i = (int)"123";
进制
编程中涉及的进制及表示方式
二进制(binary):0,1 ,满2进1.以0b或0B开头。
十进制(decimal):0-9 ,满10进1。
八进制(octal):0-7 ,满8进1. 以数字0开头表示。
**十六进制(hex):**0-9及A-F,满16进1. 以0x或0X开头表示。此处的A-F不区分大小写。 如:0x21AF +1= 0X21B0
二进制的使用说明
计算机底层存储方式:所有数字在计算机底层都以二进制形式存在
二进制数据的存储方式:所有的数值,不管正负,底层都以补码的形式存储
原码、反码、补码的说明
正数
三码合一
负数
原码:直接将一个数值换成二进制,最高位是符号位
反码:对原码按位取反,只是最高位(符号位)确定为1
补码:其反码加1
进制间的转换
运算符
算术运算符
代码
/* 运算符之一:算术运算符 + - + - * / % (前)++ ++(后) (前)-- --(后) + */ public class AriTest { public static void main(String[] args) { // 除号:/ int num1 = 12; int num2 = 5; int result1 = num1 / num2; // 2 System.out.println(result1); int result2 = num1 / num2 * num2; // 10 System.out.println(result1); double result3 = num1 / num2; // 2.0 System.out.println(result3); // 2.0 double result4 = num1 / num2 + 0.0; // 2.4 double result5 = num1 / (num2 + 0.0); // 2.4 double result6 = (double) num1 + num2; System.out.println(result5); System.out.println(result6); // 取余:% // 结果数的符号与被模数相同 // 开发中,经常使用 % 来判断能否被除尽的情况 int m1 = 12; int n1 = 5; System.out.println("m1 % n1 = " + m1 % n1); int m2 = -12; int n2 = 5; System.out.println("m1 % n1 = " + m2 % n2); int m3 = 12; int n3 = -5; System.out.println("m1 % n1 = " + m3 % n3); int m4 = -12; int n4 = -5; System.out.println("m1 % n1 = " + m4 % n4); // (前)++:先自增1,后运算 // ++(后):先运算,后自增1 int a1 = 10; int b1 = ++a1; int a2 = 10; int b2 = a2++; System.out.println("a1 = " + a1 + ", b1 = " + b1); System.out.println("a2 = " + a2 + ", b2 = " + b2); int a3 = 10; a3++; int b3 = a3; // 注意点:自增1不会改变本身变量的数据类型 short s1 = 10; s1++; System.out.println(s1); byte b = 127; b++; System.out.println("b = " + b); // (前)--:先自减1,后运算 // --(后):先运算,后自减1 int a4 = 10; int b4 = --a4; System.out.println("a4 = " + a4 + ", b4 = " + b4); int a5 = 10; int b5 = --a5; System.out.println("a5 = " + a5 + ", b5 = " + b5); } }
特别说明
(前)++:先自增,后运算
(后)++:先运算,后自增**
(前)–:先自减,后运算
(后)–:先运算,后自减
连接符:+ 只能使用在 String 与其它数据类型变量之间使用。
赋值运算符
代码
/* * 运算符之二:赋值运算符 * = += -= *= /= %= */ public class SetValueTest { public static void main(String[] args) { // =:赋值符号 int i1 = 10; int j1 = 10; int i2, j2; // 连续赋值 i2 = j2 = 10; int i3 = 10, j3 =10; int num1 = 10; num1 += 2; // num1 = num1 + 2; System.out.println(num1); int num2 = 10; num2 %= 5; // num2 = num2 % 5; System.out.println(num2); short s1 = 10; // 不会改变变量本身的数据类型 s1 += 2; System.out.println(s1); // 开发中,如果希望变量实现+2的操作,有几种方法?(前提:int num = 10) // 方式1:num = num + 2; // 方式2:num += 2; (推荐) // 开发中,如果希望变量实现+1的操作,有几种方法?(前提:int num = 10) // 方式1:num = num + 1; // 方式2:num += 1; // 方式3:num++; (推荐) // 练习1 int i = 1; i *= 0.1; // 0 System.out.println(i); i++; // 1 System.out.println(i); // 练习2 int m = 2; int n = 3; n *= m++; // 3 System.out.println("m = " + m); // 6 System.out.println("n = " + n); // 练习3 int n1 = 10; n1 += (n1++) + (++n1); // 32 System.out.println( n1); } }
特别说明
运算结果不会改变变量本身的数据类型
赋值运算符
代码
/* * 运算符之三:比较运算符 * == != > < >= <= instanceof * 结论: * 1.比较运算符的结果是 boolean 类型 * 2.区分 == 和 = */ public class CompareTest { public static void main(String[] args) { int i = 10; int j = 20; System.out.println(i == j); // false System.out.println(i = j); // 20 boolean b1 = true; boolean b2 = false; System.out.println(b2 == b1); // false System.out.println(b2 = b1); // true } }
特别说明
比较运算符的结果都是 boolean 类型
> < >= <= :只能使用在数值类型的数据之间
== != :不仅可以使用在数值类型数据之间,还可以使用在引用类型变量之间
Account acct1 = new Account(1000); Account acct2 = new Account(2000); boolean b1 = acct1 == acct2; // 比较两个 Account 是否是同一账户 boolean b2 = acct1 != acct2;
逻辑运算符
代码
/** * Filename : LogicTest.java * Author : keke * Creation time : 下午8:06:50 2021年10月30日 * Description : 运算符之四:逻辑运算符 * && & || | ! ^ * 说明: * 1.逻辑运算符操作的都是 boolean 类型的变量 */ public class LogicTest { public static void main(String[] args) { // 区分 & 与 && // 相同点: // 1.& 与 && 的运算结果相同 // 2.当符号左边为 true 时,二者都会执行符号右边的运算 // 不同点: // 当符号左边为 false 时,& 会执行符号右边的运算,&& 不会执行符号右边的运算 // 开发中,推荐使用 && boolean b1 = true; b1 = false; int num1 = 10; if (b1 & (num1++ > 0)) { System.out.println("我现在在北京"); }else { System.out.println("我现在在南京"); } System.out.println("num1 = " + num1); boolean b2 = true; b2 = false; int num2 = 10; if (b2 && (num2++ > 0)) { System.out.println("我现在在北京"); }else { System.out.println("我现在在南京"); } System.out.println("num2 = " + num2); // 区分:| 与 || // 相同点: // 1.| 与 || 的运算结果相同 // 2.当符号左边为 false 时,二者都会执行符号右边的运算 // 不同点: // 当符号左边为 true 时,| 会执行符号右边的运算,|| 不会执行符号右边的运算 // 开发中,推荐使用 || boolean b3 = false; b3 = true; int num3 = 10; if (b3 | (num3++ > 0)) { System.out.println("我现在在北京"); }else { System.out.println("我现在在南京"); } System.out.println("num3 = " + num3); boolean b4 = false; b4 = true; int num4 = 10; if (b4 || (num4++ > 0)) { System.out.println("我现在在北京"); }else { System.out.println("我现在在南京"); } System.out.println("num4 = " + num4); } }
特别说明
逻辑运算符的操作的都是 boolean 类型的变量,运算结果是 boolean 类型
位运算符
代码
/** * Filename : BitTest.java * Author : keke * Creation time : 下午8:42:10 2021年10月30日 * Description : 运算符之五:位运算符(了解)& | ~ ^ << >> >>> * 结论: * 1.位运算符操作的都是整型的数据 * 2.<<:在一定范围内,向左移1位,相当于乘以2 * >>:在一定范围内,右移1位,相当于除以2 * * 面试题:最高效的计算 2 * 8 ? 2 << 3 或 8 << 1 */ public class BitTest { public static void main(String[] args) { int i = 21; i = -21; System.out.println("i << 2 = " + (i << 2)); System.out.println("i << 3 = " + (i << 3)); System.out.println("i << 26 = " + (i << 26)); System.out.println("i << 27 = " + (i << 27)); System.out.println("i >> 2 = " + (i >> 2)); int m = 12; int n = 5; System.out.println("m & n = " + (m & n)); System.out.println("m | n = " + (m | n)); System.out.println("m ^ n = " + (m ^ n)); // 练习:交换两个变量的值 int num1 = 10; int num2 = 20; System.out.println("num1 = " + num1 + ", num2 =" + num2); // 方式一:定义临时变量 (推荐) int temp = num1; num1 = num2; num2 = temp; System.out.println("num1 = " + num1 + ", num2 =" + num2); // 方式二: // 好处:不用定义临时变量 // 弊端: // 1、相加操作可能超出存储范围 // 2.有局限性:只能适用于数值类型 num1 = num1 + num2; num2 = num1 - num2; num1 = num1 - num2; System.out.println("num1 = " + num1 + ", num2 =" + num2); // 方式三:使用位运算符 num1 = num1 ^ num2; num2 = num1 ^ num2; num1 = num1 ^ num2; System.out.println("num1 = " + num1 + ", num2 =" + num2); } }
面试题:最高效的计算 2 * 8 ?
答案: 2 << 3 或 8 << 1
特别说明
1.位运算符操作的都是整型的数据
2.<<:在一定范围内,每向左移1位,相当于乘以2
>>:在一定范围内,每向右移1位,相当于除以2
三元运算符
代码
/** * Filename : SanYuanTest.java * Author : keke * Creation time : 下午9:16:12 2021年10月30日 * Description : 运算符之六:三元运算符 * 1.结构:(条件表达式) ? 表达式1 : 表达式2 * 2.说明 * 1.条件表达式的结果为 boolean 类型 * 2.根据条件表达式真或假,决定执行表达式1,还是表达式2 * 如果表达式为 true,则执行表达式1 * 如果表达式为 false,则执行表达式2 * 3.表达式1和表达式2要求是一致的 * 4.三元运算符是可以嵌套的 * 3.凡是可以使用三元运算符的地方,都可以改写成 if-else * 4.如果程序既可以用三元运算符,也可以用 if-else,那么优先选择三元运算符 * 原因:简洁、执行效率高 */ public class SanYuanTest { public static void main(String[] args) { // 获取两个整数的较大值 int m = 12; int n = 5; int max = m > n ? m : n; System.out.println(max); double num = m > n ? 2 : 1.0; // m > n ? 2 : "n大"; // 编译错误 // 不建议 String maxStr = m > n ? "m 大" : m == n ? "m 和 n 相等" : "n 大"; System.out.println(maxStr); // 获取三个数的最大值 int n1 = 12; int n2 = 30; int n3 = -43; max = n1 > n2 ? n1 > n3 ? n1 : n3 : n2; System.out.println(max); if (m > n) { System.out.println(m); }else { System.out.println(n); } } }
特别说明
条件表达式的结果为 boolean 类型
根据条件表达式真或假,决定执行表达式1,还是表达式2
如果表达式为 true,则执行表达式1
如果表达式为 false,则执行表达式2
表达式1和表达式2要求是一致的
三元运算符是可以嵌套的
流程控制
分支结构
if-else
格式:
结构一
if(条件表达式){ 执行表达式 }
结构二
if(条件表达式){ 执行表达式1 } else{ 执行表达式2 }
结构三
if(条件表达式){ 执行表达式1 }else if(条件表达式){ 执行表达式2 } ... else{ 执行表达式n }
说明:
else 结构是可选的
针对于条件表达式:
如果多个条件表达式之间是“互斥”关系(或没有交集的关系),哪个判断和执行语句声明在上面函数下面,无所谓
如果多个条件表达式之间有交集的关系,需要根据实际情况,考虑清除应该将哪个结构声明在上面
如果多个条件表达式之间有包含的关系。通常情况下,需要将范围小的声明在范围大的上面,否则,范围小的没机会执行
if-else 结构是可以相互嵌套的
如果 if-else 结构中的执行语句只有一行时,那么这一对大括号可以省略,但是不建议省略
switch-case
格式:
switch(表达式){ case 常量1: 执行语句1; // break; case 常量2: 执行语句2; // break; ... default: 执行语句n; // break; }
说明:
根据 switch 表达式中的值,依次匹配各个 case 中的常量,一旦匹配成功,则进入相应 case 结构中,调用其执行语句。当调用完执行语句以后,则仍然继续向下执行其它 case 结构中的执行语句,直到遇到 break 关键字或此 switch-case结构末尾结束为止
break 关键字可以使用在 switch-case 结构中,表示一旦执行到此关键字,就跳出 switch-case 结构
switch结构中的表达式,只能是如下的6种数据类型之一:
byte、short、char、int、枚举类型(JDK5.0新增)、String(JDK7.0新增)
case 之后只能声明常量,不能声明范围
break 关键字是可选的
default 相当于 if-else 结构中的 else, default 结构是可选的,而且位置是灵活的
循环结构
循环结构的四要素
初始化条件
循环条件 --> 是 boolean 类型
循环体
迭代条件
说明:通常情况下,循环结束都是因为循环条件返回 false 了
for
结构:
for(1;2;4){ 3 }
执行过程:1 --> 2 --> 3 --> 4 --> 2 --> 3 --> 4 --> … --> 2
while
结构:
1 while(2){ 3; 4; }
执行过程:1 --> 2 --> 3 --> 4 --> 2 --> 3 --> 4 --> … --> 2
说明
写 while 循环千万小心不要丢了迭代条件,一旦丢了,就可能导致死循环
for 和 while 循环总结:
开发中,基本上都会从 for、while 中进行选择,实现循环结构
for 循环和 while 循环是可以相互装换的
区别:for 循环和 while 循环的初始化条件部分的作用范围不同
避免出现死循环
do-while
结构:
1 do{ 3 4; }while(2);
执行过程:1 -->3 --> 4 --> 2 --> 3 --> 4 --> 2 --> … --> 2
说明:
do-while 循环至少会执行一次循环体
开发中,使用 for 和 while 更多一些,较少使用 do-while
“无限循环”结构
for(;;){ } while(true){ }
总结:如何结束一个循环结构?
当循环条件是 false 时
在循环体中,执行 break
嵌套循环
嵌套循环:将一个循环结构 A 声明在另一个循环结构 B 的循环体中,就构成了嵌套循环
外层循环:循环结构 B
内层循环:循环结构 A
2.说明
内层循环结构遍历一遍,只相当于外层循环循环体执行了一次
假设外层循环需要执行 m 次,内层循环需要执行 n 次,此时内层循环的循环体一个执行了 m * n 次
外层循环控制行数,内层循环控制列数
典型练习
package day05; /** * Filename : ForForTest.java * Author : keke * Creation time : 下午1:47:24 2021年11月1日 * Description : * 嵌套循环的使用 * 1.嵌套循环:将一个循环结构 A 声明在另一个循环结构 B 的循环体中,就构成了嵌套循环 * 2.外层循环:循环结构 B * 内层循环:循环结构 A * 3.说明 * 1.内层循环结构遍历一遍,只相当于外层循环循环体执行了一次 * 2.假设外层循环需要执行 m 次,内层循环需要执行 n 次,此时内层循环的循环体一个执行了 m * n 次 * 3.外层循环控制行数,内层循环控制列数 */ public class ForForTest { public static void main(String[] args) { for(int i = 1; i <= 4; i++) { for(int j = 1; j <= 6; j++) { System.out.print('*'); } System.out.println(); } for(int i = 0; i < 5; i++) { for(int j = 0; j <= i; j++) { System.out.print('*'); } System.out.println(); } for(int i = 0; i < 5; i++) { for(int j = 1; j <= 5 - i; j++) { System.out.print('*'); } System.out.println(); } System.out.println("=============================================="); for(int i = 0; i < 5; i++) { for(int j = 0; j <= i; j++) { System.out.print('*'); } System.out.println(); } for(int i = 0; i < 5; i++) { for(int j = 2; j <= 5 - i; j++) { System.out.print('*'); } System.out.println(); } } }
package day05; /** * Filename : NineNineTable.java * Author : keke * Creation time : 下午2:31:29 2021年11月1日 * Description : * 九九乘法表 */ public class NineNineTable { public static void main(String[] args) { for (int i = 1; i < 10; i++) { for(int j = 1; j <= i; j++) { System.out.print(j + " * " + i + " = " + i * j + "\t"); } System.out.println(); } } }
package day05; /** * Filename : PrimeNumberTest2.java * Author : keke * Creation time : 下午2:41:08 2021年11月1日 * Description : * 100以内所有的质数 * 质数:只能被1和它本身整除的自然数,又叫素数 --> 从2开始,到这个数-1结束为止,都不能被这个数本身整除 * 最小的质数:2 */ public class PrimeNumberTest2 { public static void main(String[] args) { // 获取当前时间的毫秒数 long start = System.currentTimeMillis(); label: for(int i = 2; i <= 1000; i++) { for(int j = 2; j <= Math.sqrt(i); j++) { // i 被 j 除尽 if (i % j == 0) { continue label; } } // 能执行到此步骤的,都是质数 System.out.print(i + " "); } System.out.println(); long end = System.currentTimeMillis(); System.out.println(end - start); } }
补充:衡量一个功能代码的优劣:
正确性
可读性
健壮性
高效率与低存储:时间复杂度、空间复杂度(衡量算法的好坏)
break 和 continue 关键字的使用
关键字 | 使用范围 | 循环中使用的作用 | 相同点 |
break | switch-case | ||
循环结构 | 结束当前循环 | 关键字后面不能声明执行语句 | |
continue | 循环结构 | 结束当次循环 | 关键字后面不能声明执行语句 |
带标签的 break 和 continue 关键字的使用
Scanner 类的使用
package day04; import java.util.Scanner; /** * Filename : ScannerTest.java * Author : keke * Creation time : 下午3:23:13 2021年10月31日 * Description : * 如何从键盘获取不同类型的变量:需要使用 Scanner 类 * 具体实现步骤: * 1.导包:import java.util.Scanner; * 2.Scanner 实例化:Scanner scanner = new Scanner(System.in); * 3.调用 Scanner 类的相关方法(next() 和 nextXxx()),来获取指定类型的变量 * 注意: * 需要根据相应的方法,来输入指定类型的值,如果深入到数据类型与要求的类型不匹配,则报异常 InputMismatchException * 导致程序终止 */ public class ScannerTest { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); System.out.print("请输入你的姓名:"); String name = scanner.next(); System.out.println(name); System.out.print("请输入你的年龄:"); int age = scanner.nextInt(); System.out.println(age); System.out.print("请输入你的体重:"); double weight = scanner.nextDouble(); System.out.println(weight); System.out.print("是否单身:"); boolean isLove = scanner.nextBoolean(); System.out.println(isLove); // 对于 char 型的获取,Scanner 没有提供相关的方法。只能获取一个字符串 System.out.print("请输入你的性别:"); // 获取索引为0位置上的字符 char genderChar = scanner.next().charAt(0); System.out.println(genderChar); } }
数组
数组的概述
数组:数组(Array),是多个相同类型数据按一定顺序排列的集合,并使用一个名字命名,并通过编号的方式对这些数据进行统一管理。
数组相关概念
数组名
元素
角标、下标、索引
数组的长度:元素的个数
数组的特点:
数组是有序排列的
数组属于引用数据类型的变量。数组的元素,既可以是基本数据类型,也可以是引用数据类型
创建数组对象会在内存中开辟一整块连续的空间
数组的长度一旦确定,就不能修改
数组的分类
按照维数:一维数组、二维数组、…
按照数组元素的类型:基本数据类型元素的数组、引用数据类型元素的数组
数据结构:
数据与数据之间的逻辑关系:集合、一对一、一对多、多对多
数据的存储结构
线性表:顺序表(比如:数组)、链表、栈、队列
树形结构:二叉树
图形结构
算法:
排序算法
搜索算法
一维数组
一维数组的声明和初始化
// 1.一维数组的声明和初始化 int[] ids; // 1.1 静态初始化:数组的初始化和数组元素的赋值操作同时进行 ids = new int[]{1001, 1002, 1003, 1004}; int dids = {1001, 1002, 1003, 1004}; // 类型推断 // 1.2 动态初始化:数组的初始化和数组元素的赋值操作分开进行 String[] names = new String[5]; // 错误的写法 // int[] arr1 = new int[]; // int[5] arr1 = new int[]; // int[] arr1 = new int[3]{1, 2, 3};
一维数组的引用
通过角标的方式调用
// 数组的角标(或索引)从0开始,到数组的长度-1结束 names[0] = "王铭"; names[1] = "王赫"; names[2] = "张学良"; names[3] = "张居龙"; names[4] = "王宏志";
数组的属性
length
System.out.println(names.length); System.out.println(ids.length);
注意:
- 数组一旦初始化完成,其长度就确定了
- 数组长度一旦确定,就不可以修改
遍历一维数组
for(int i = 0; i < names.length; i++){ System.out.print(names[i] + " "); }
一维数组元素的默认初始化值
- 数组元素是整型:0
- 数组元素是浮点型:0.0
- 数组元素是字符型:0 ‘\u0000’
- 数组元素是布尔型:false
- 数组元素是引用类型: null
数组的内存解析
二维数组
如何理解二维数组
数组属于引用数据类型,数组的元素也可以是引用数据类型,一个一维数组的元素如果还是一个一维数组类型的,则此数组称为二维数组
二维数组的声明和初始化
// 静态初始化 int[][] arr1 = new int[][] {{1, 2, 3}, {4, 5}, {6, 7, 8}}; // 动态初始化 String[][] arr2 = new String[3][2]; String[][] arr3 = new String[3][]; // 错误的情况 // String[][] arr4 = new String[][3]; // 正确 int arr4[][] = new int[3][]; int[] arr5[] = new int[3][]; // 类型推断 int[] arr6[] = {{1, 2, 3}, {4, 5}, {6, 7, 8}};