Java程序流程控制与数组
程序流程控制与数组
顺序结构测试
- 程序从上到下逐行执行,中间没有任何判断和跳转。
- 如果没有任何流程控制,程序总是从上到下执行每条语句。
分支结构
- 根据条件,选择性地执行某段代码。
- 有if-else和switch-case两种分支语句。
if-else结构
- else是可选的
- 针对多个条件表达式之间是“互斥”关系(或者没有交集的关系)。
- 如果多个条件表达式之间有交集关系,需要根据实际情况,考虑应该将哪个结构声明在上面。
- 如果多个条件表达式之间有包含关系,一定要先处理包含范围小的情况,否则范围小的没机会运行。
- if-else结构是可以嵌套的。
if语句三种格式
1 | if(条件表达式){ |
1 | if(条件表达式){ |
1 | if(条件表达式){ |
具体举例1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31class IfTest{
public static void main(String[] args){
//格式1
int hearBeats = 78;
if(hearBeats < 60 || hearBeats > 100){
System.out.println("需要进一步检查!");
}
System.out.println("检查结束");
//格式2
int age = 23;
if(age < 18){
System.out.println("你不能结婚");
}else{
System.out.println("你可以结婚");
}
//格式3
if(age < 0){
System.out.println("您输入数据非法!");
}else if(age < 18){
System.out.println("青年时期");
}else if(age < 35){
System.out.println("青壮年时期");
}else if(]age < 60){
System.out.println("中年时期");
}else{
System.out.println("老年时期");
}
}
}
- if、else-if、else后面花括号内的多行代码被称为代码块,一个代码块通常看作一个整体来执行(除非运行过程中遇到return、break、continue等关键字,或是遇到了异常。)
- else的含义是“否则”,else本身就是一个条件,这也是把if、else后面代码块统称为条件执行体的原因,else的隐含条件是对前面的条件取反。
switch-case结构
1 | switch(表达式){ |
- 根据switch表达式中的值,依次匹配各个case中的常量。一旦匹配成功,则进入相应case结构中,调用其执行语句。当调用完执行语句后,仍然继续向下执行其他case结构中的执行语句,直到遇到break关键字或程序终止为止。
- 在switch-case结构中,一旦执行break关键字,就跳出switch-case结构。
- switch结构表达式,只能是byte、short、char、int四种整数类型和枚举类型、String类型(Java7开始允许),6中数据类型之一,不能是boolean类型。
- case之后只能声明常量不能声明 范围。
- break关键字是可选的。
- default相当于if-else中的else。
例子
1 | String season = "summer"; |
关于break例题
1 | //从键盘输入2019年的“month”和“day”,要求通过程序输入的日期为2019年的第几天 |
说明break在switch-case中是可选的
- 凡是使用switch-case的结构,都可以转化为if-else。反之,不成立。
- 写分支结构时,当发现既可以使用switch-case,(同时,switch中表达式的取值情况不太多),又可以使用if-else时,我们优先选择使用switch-case。原因:switch-case执行效率稍高。
循环结构
- 在某些条件满足的情况下,反复执行特定代码的功能。
- 有for、while、do-while三种循环语句。
注:JDK1.5提供了for-each循环,方便地遍历集合、数组元素。
循环结构的4个要素
① 初始化条件
② 循环条件 (条件是boolean类型)
③ 循环体
④ 迭代条件
for循环结构的使用
for循环的结构
1 | for(①;②;④){ |
执行过程:① -> ② -> ③ -> ④ -> ② -> ③ -> ④ … -> ②
for循环圆括号内两个分号是必须的,初始化语句、循环条件、迭代语句都是可以省略的,如果省略了循环条件,则这个循环条件默认为true,将会产生一个死循环。
1 | public class Text09 { |
输出
123
123
…
使用for循环时,还可以把初始化条件定义在循环体外,把循环迭代语句放在循环体内,这种方式类似下面的while循环。
1 | public class Text10 { |
输出:
0
1
2
循环结束
for循环例题
1 | /* |
while循环
① 初始化条件
② 循环条件 (条件是boolean类型)
③ 循环体
④ 迭代条件
while循环结构
1 | ① |
执行过程:① -> ② -> ③ -> ④ -> ② -> ③ -> ④ … -> ②
说明
- 写while循环千万小心不要丢了迭代条件。一旦丢了,就可能导致死循环!
- 写程序时,要避免出现死循环,算法要具有有限性。
- for循环和while循环时可以相互转换的
区别:for循环和while循环的初始化条件部分的作用范围不同。
1 | class WhileTest{ |
do-while循环
① 初始化条件
② 循环条件 (条件是boolean类型)
③ 循环体
④ 迭代条件
do-while循环结构
1 | ① |
执行过程:① -> ③ -> ④ -> ② -> ③ -> ④ … -> ②
说明:
- do-while循环至少会执行一次循环体
- 开发中,使用for和while循环更多些,较少使用do-while
1 | class DoWhileTest{ |
while(true)结构的使用
题目:从键盘读入个数不确定的整数,并判断读入的正数和负数的个数,输入为0时结束程序
1 | import java.util.Scanner; |
说明:
- 不在循环条件部分限制次数的结构:for(;;)或while(true)
- 循环结束的方式:
1、循环条件部分返回false
2、在循环体中,执行break - 建议不要在循环体中改变循环变量的值,否则会增加出错的可能性。(需要修改的情况下,重新定义一个临时变量)
注意
for循环和while、do-while有区别:while、do-while的循环迭代语句紧跟着循环体,如果循环体不能完全执行(如用continue结束本次循环),循环迭代语句是不会被执行的。但for循环的迭代语句并没有与循环体放在一起,因此不管是否使用continue语句来结束本次循环,迭代语句一样会获得执行。
嵌套循环
嵌套循环的使用
- 将一个循环结构A声明在另一个循环结构B的循环体中,就构成了循环嵌套
- 外层循环就是循环结构B,内层循环就是循环结构A
- 内层循环结构遍历一遍,只相当于外层循环循环体执行了一次
- 外层循环执行m次,内层循环执行n次,此时内层循环的循环体一共执行了m*n次。
嵌套循环联系
九九乘法表
1 × 1 = 1
2 × 1 = 2 2 × 2 = 4
…
9 × 1 = 9 … 9 × 9 = 81
1 | class NineTable{ |
100以内所有质数
质数:素数,只能被1和他本身整除的自然数
1 | class PrimeNumberTest{ //质数 |
优化算法1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32public class PrimeNumberTest1 {
public static void main(String[] args) {
//获取当前时间距离1970-01-01 00:00:00的距离
long start = System.currentTimeMillis();
int count = 0;
for(int i = 2;i <= 10000;i++){ //遍历100以内的自然数
boolean isFlag = true;
for(int j = 2;j <= Math.sqrt(i);j++) { //和i做除法
if(i % j == 0) {
isFlag = false;
break;//优化1:只对本身非质数的自然数有效
}
}
if(isFlag == true) {
//System.out.println(i);
count++;
}
}
//获取当前时间距离1970-01-01 00:00:00的距离
long end = System.currentTimeMillis();
System.out.println(count);
System.out.println(end-start);
}
}
如何从键盘获取变量
具体实现步骤:
- 导包:import java.util.Scanner;
- Scanner的实例化:Scanner scan = new Scanner(System.in);
- 调用Scanner类的相关方法,来获取指定类型的变量。
注意
需要根据相应的方法,来输入指定类型的值。如果输入的数据类型与要求的类型不匹配时,会报异常,导致程序终止。
1 | import java.util.Scanner; |
- 对于char型的获取,Scanner没有提供相关方法,只能获取一个字符串。
如何获取一个随机数
公式:[a,b] (int)(Math.random() * (b - a + 1) + a);
1 | //获取10-90的随机数 |
获取字符串对应位置的字符
1 | String gender = scan.next();//输入字符串 |
数组类型
数组的概述
数组是多个相同类型数据按一定顺序排列的集合,并使用一个名字命名,通过编号的方式对这些数据进行统一管理。
数组是一种数据结构,用来储存多个数据,每个数组元素存放一个数据,通常可以通过数组元素的索引arr[i]
来访问数组元素。所有的数组元素需具有相同的数据类型,一个数组只能存储一种数据类型的数据。
数组的相关概念
- 数组名
- 元素
- 角标、下标、索引
- 数组的长度:元素的个数
数组的特点
- 数组是有序排列的
- 数组属于引用数据类型的变量。数组的元素既可以是基本数据类型,也可以是引用数据类型
- 创建数组对象会在内存中开辟一整块连续的空间
- 数组的长度一旦确定,就不能修改
数组的分类
- 按维数分:一维数组、二维数组…..
- 按数组元素的类型:基本数据类型元素的数组、引用数据类型的数组
一维数组的使用
一维数组的声明和初始化
数组的初始化
Java数组必须先初始化,然后才可以使用。初始化就是为数组的数组元素分配内存空间,并为每个数组元素分配初始值。
静态初始化
数组的初始化和数组元素的赋值同时进行,初始化时由程序员指定每个数组元素的初始值,由系统决定数组长度。
静态初始化的语法格式如下:
arrayName = new type[]{element1,element2,element3 ... }
其中type
就是数组元素的数据类型,此处的type
必须与定义数组变量时所使用的type
相同,也可以定义数组时所指定的type
的子类
动态初始化
数组的初始化和数组元素的赋值同时进行,初始化时程序员只指定数组长度,由系统为每个数组元素指定初始值。
动态初始化的语法格式如下:
arrayName = new type[length]
其中需要指定一个int的类型的length参数,这个参数指定了数组的长度,也就是可以容纳数组元素的个数。type
与静态初始化类似。
1 | public static void main(String[] args) { |
数组一旦初始化完成,数组的长度就确定了
1 | public static void main (String[] args){ |
如何调用数组指定位置的元素
通过角标(索引)的方式调用:arr[i]
,数组的角标(索引)从0开始,到数组的长度-1结束
1 | public static void main (String[] args){ |
如果访问数组元素时指定值小于0,或大于等于数组长度,运行时会出现异常报错: java.lang.ArrayIndexOutOfBoundsException:N(数组索引越界异常),其中N就是我们试图访问的数组索引。
如何获取数组的长度和遍历数组
1 | public static void main(String[] args) { |
foreach 循环(增强for循环)
使用foreach 循环(增强for循环)遍历数组和集合元素时,无需获得数组和集合的长度,无需根据索引来访问数组元素和集合元素,foreach 循环(增强for循环)自动遍历数组和集合的每个元素。
1 | for(type variableName : array | collection){//把冒号右边的数组遍历,遍历出来的元素放到定义的变量中 |
foreach 循环(增强for循环)和普通循环的不同是 ,它无须循环条件,无需迭代语句,这些由系统完成,当每个数组元素都被迭代一次后,foreach 循环(增强for循环)自动结束。
使用foreach 循环(增强for循环)迭代数组元素时,并不能改变数组元素的值,因此不需要对foreach 循环(增强for循环)的循环变量进行赋值。
数组元素的默认初始化值
为数组的数组元素分配内存空间时,一旦分配完空间后,每个内存空间里存储的内容就是该数组元素的值,即使内存空间内存储的内容是空,也是有默认的值(null)
数组的元素是基本数据类型:
- 数组类型是整型:0
- 数组类型是浮点型:0.0
- 数组类型是char型:0或’\u0000’,而非’0’
- 数组类型是boolean型:false
数组的元素是引用类型(String):null
注意:
不要同时使用静态初始化和动态初始化,也就是说,不要在进行数组初始化时,既指定数组长度也为每个数组元素分配初始值,错误代码如下
int[] arr = new int[5]{1,2,3,4,5}
1 | public static void main(String[] args) { |
输出:
0 0 0 0
0 0 0 0
0.0 0.0 0.0 0.0
false false false false
null null null null
一维数组的内存解析
内存简化结构:
一维数组的内存解析:
程序员进行程序开发时,不仅仅要停留在代码表面,而要深入底层的运行机制,才可以对程序的运行机制有更准确的把握。
看待一个数组时,要把数组看作两个部分:一部分是数组引用,也就是在代码中定义的数组变量;还有一部分是实际的数组对象,这部分是在堆内存里运行的,通常无法直接访问,只能通过数组引用变量来访问。