⭐ 循环结构
循环结构分两大类,一类是当型,一类是直到型。
🐟 当型:
当布尔表达式条件为 true 时,反复执行某语句,当布尔表达式的值为 false 时才停止循环,比如:while 与 for 循环。
🐟 直到型:
先执行某语句, 再判断布尔表达式,如果为 true,再执行某语句,如此反复,直到布尔表达式条件为 false 时才停止循环,比如 do-while 循环。
while 循环
语法结构:
while(布尔表达式){
循环体;
}
🐟 在循环刚开始时,会计算一次“布尔表达式”的值,若条件为真,执行循环体。而对于后来每一次额外的循环,都会在开始前重新计算一次。
🐟 语句中应有使循环趋向于结束的语句,否则会出现无限循环–––"死"循环。
【eg】while 循环结构:求 1 到 100 之间的累加和
public class Test {
public static void main(String[ ] args) {
int i = 0;
int sum = 0;
// 1+2+3+…+100=?
while (i <= 100) {
sum += i;//相当于 sum = sum+i;
i++;
}
System.out.println("Sum= " + sum);
}
}
执行结果如图所示:
do-while 循环
语法结构:
do{
循环体
}while(布尔表达式);
do-while 循环结构会先执行循环体,然后再判断布尔表达式的值,若条件为真,执行循环体,当条件为假时结束循环。do-while 循环的循环体至少执行一次。
【eg】do-while 循环结构:求 1-100 之间的累加和
public class Test {
public static void main(String[ ] args) {
int i = 0;
int sum = 0;
do {
sum += i; // sum = sum + i
i++;
} while (i <= 100); //此处的;不能省略
System.out.println("Sum= " + sum);
}
}
执行结果如下所示:
【eg】while 与 do-while 的区别
public class Test {
public static void main(String[ ] args) {
//while 循环:先判断再执行
int a = 0;
while (a < 0) {
System.out.println(a);
a++;
}
System.out.println("-----");
//do-while 循环:先执行再判断
a = 0;
do {
System.out.println(a);
a++;
} while (a < 0);
}
}
// 运行结构,可以看出 do-while 总是保证循环体至少会被执行一次!
循环结构(for)
语法结构:
for (初始表达式; 布尔表达式; 迭代因子) {
循环体;
}
🐟 初始化部分设置:循环变量的初值
🐟 条件判断部分为:布尔表达式
🐟 迭代因子:控制循环变量的增减
for 循环在执行条件判定后,先执行的循环体部分,再执行步进。
【eg】for 循环
public class Test {
public static void main(String args[ ]) {
int sum = 0;
//1.求 1-100 之间的累加和
for (int i = 0; i <= 100; i++) {
sum += i;
}
System.out.println("Sum= " + sum);
//2.循环输出 9-1 之间的数
for(int i=9;i>0;i--){
System.out.print(i+"、");
}
System.out.println();
//3.输出 90-1 之间能被 3 整除的数
for(int i=90;i>0;i-=3){
System.out.print(i+"、");
}
System.out.println();
}
}
执行结果如下所示:
【eg】逗号运算符
public class Test {
public static void main(String[ ] args) {
for(int i = 1, j = i + 10; i < 5; i++, j = i * 2) {
System.out.println("i= " + i + " j= " + j);
}
}
}
执行结果如下所示:
【eg】无限循环
public class Test {
public static void main(String[ ] args) {
for ( ; ; ) { // 无限循环: 相当于 while(true)
System.out.println("北京");
}
}
}
编译器将 while(true)与 for(;;)看作同一回事,都指的是无限循环。
【eg】初始化变量的作用域
public class Test {
public static void main(String[] args) {
for(int i = 1; i < 10; i++) {
System.out.println(i+" ");
}
System.out.println(i);//编译错误,无法访问在 for 循环中定义的变量 i
}
}
⭐ 嵌套循环
循环语句内部,再写一个或多个循环,称为嵌套循环。一般工作中多见的就是两层。
【eg】嵌套循环
public class Test {
public static void main(String args[ ]) {
for (int i=1; i <=5; i++) {
for(int j=1; j<=5; j++){
System.out.print(i+" ");
}
System.out.println();
}
}
}
执行结果如下图所示:
【eg】使用嵌套循环实现九九乘法表
public class Test {
public static void main(String args[ ]) {
for (int i = 1; i < 10; i++) { // i 是一个乘数
for (int j = 1; j <= i; j++) { // j 是另一个乘数
System.out.print(j + "*" + i + "=" + (i * j < 10 ? (" " + i * j) : i * j) + " ");
}
System.out.println();
}
}
}
执行结果如下图所示:
⭐ break 语句和 continue 语句
🐟 break 用于强行退出整个循环
🐟 continue 用于结束本次循环,继续下一次
【eg】break 语句
//产生 100 以内的随机数,直到随机数为 88 时终止循环
public class Test {
public static void main(String[ ] args) {
int total = 0;//定义计数器
System.out.println("Begin");
while (true) {
total++;//每循环一次计数器加 1
int i = (int) Math.round(100 * Math.random());
//当 i 等于 88 时,退出循环
if (i == 88) {
break;
}
}
//输出循环的次数
System.out.println("Game over, used " + total + " times.");
}
}
执行结果如下所示:
continue 语句用在循环语句体中,用于终止某次循环过程,即跳过循环体中尚未执行的语句,接着进行下一次是否执行循环的判定。
【eg】continue 语句
//把 100~150 之间不能被 3 整除的数输出,并且每行输出 5 个
public class Test {
public static void main(String[ ] args) {
int count = 0;//定义计数器
for (int i = 100; i < 150; i++) {
//如果是 3 的倍数,则跳过本次循环,继续进行下一次循环
if (i % 3 == 0){
continue;
}
//否则(不是 3 的倍数),输出该数
System.out.print(i + "、");
count++;//每输出一个数,计数器加 1
//根据计数器判断每行是否已经输出了 5 个数
if (count % 5 == 0) {
System.out.println();
}
}
}
}
执行结果如下所示:
带标签的 continue 语句
🐟 “标签”是指后面跟一个冒号的标识符,例如:“label:”。对 Java 来说唯一用到标签的地方是在循环语句之前。
🐟 “goto 有害”论中,最有问题的就是标签,而非 goto,随着标签在一个程序里数量的增多,产生错误的机会也越来越多。但 Java 标签不会造成这方面的问题,因为它们的活动场所已被限死,不可通过特别的方式到处传递程序的控制权。
【eg】带标签的 continue
//控制嵌套循环跳转(打印 101-150 之间所有的质数)
public class Test {
public static void main(String args[ ]) {
outer: for (int i = 101; i < 150; i++) {
for (int j = 2; j < i / 2; j++) {
if (i % j == 0){
continue outer; //符合某条件,跳到外部循环继续
}
}
System.out.print(i + " ");
}
}
}
执行结果如下所示:
⭐ 方法
语句块
🐟 语句块(也叫复合语句)。语句块中定义的变量只能用于自己,外部不能使用。
🐟 语句块可以使用外部的变量,而外部不能使用语句块的变量;
【eg】语句块
public class Test {
public static void main(String[ ] args) {
int n;
int a;
{
int k;
int n; //编译错误:不能重复定义变量 n
} //变量 k 的作用域到此为止
}
}
方法
⭐ 方法(method):一段用来完成特定功能的代码片段,类似于其它语言的函数(function)。
⭐ 方法用于定义该类或该类的实例的行为特征和功能实现。
⭐ 面向过程中,函数是最基本单位,整个程序由一个个函数调用组成。
⭐ 面向对象中,整个程序的基本单位是类,方法是从属于类和对象的。
方法声明格式:
[修饰符 1 修饰符 2 …] 返回值类型 方法名(形式参数列表){
Java 语句;… … …
}
方法的调用方式:
方法的详细说明
⭐ 形式参数:在方法声明时用于接收外界传入的数据。(方法定义时)
⭐ 实参:调用方法时实际传给方法的数据。 (方法调用时)
⭐ 返回值:执行完毕后,返还给调用它的环境的数据。
⭐ 返回值类型:事先约定的返回值的数据类型,如无返回值,则为 void。
【eg】方法的声明及调用
public class Test {
/** main 方法:程序的入口 */
public static void main(String[ ] args) {
int num1 = 10;
int num2 = 20;
//调用求和的方法:将 num1 与 num2 的值传给 add 方法中的 n1 与 n2
// 求完和后将结果返回,用 sum 接收结果
int sum = add(num1, num2);
System.out.println("sum = " + sum);//输出:sum = 30
//调用打印的方法:该方法没有返回值
print();
}
/** 求和的方法 */
public static int add(int n1, int n2) {
int sum = n1 + n2;
return sum;//使用 return 返回计算的结果
}
/** 打印的方法 */
public static void print() {
System.out.println("北京...");
}
}
注意
🐟 实参必须和形参列表匹配。
🐟 return:终止方法运行,并返回的数据。
🐟 Java 中传递参数,遵循值传递的原则(传递的都是数据的副本):
🐟 基本类型传递的是该数据值的 copy 值。
🐟 引用类型传递的是该对象引用的 copy 值,但指向的是同一个对象。
⭐ 方法的重载(overload)
重载:一个类中可以定义多个名称相同,但形式参数列表不同的方法。
构成方法重载的条件:
🐟 形参列表不同的含义:形参类型、形参个数、形参顺序不同
🐟 只有返回值不同不构成方法的重载
如:int a(String str){}与 void a(String str){}不构成方法重载
🐟 只有形参的名称不同,不构成方法的重载
如:int a(String str){}与 int a(String s){}不构成方法重载
【eg】方法重载
public class Test {
public static void main(String[ ] args) {
System.out.println(add(3, 5));// 8
System.out.println(add(3, 5, 10));// 18
System.out.println(add(3.0, 5));// 8.0
System.out.println(add(3, 5.0));// 8.0
// 我们已经见过的方法的重载
System.out.println();// 0 个参数
System.out.println(1);// 参数是 1 个 int
System.out.println(3.0);// 参数是 1 个 double
}
/** 求和的方法 */
public static int add(int n1, int n2) {
int sum = n1 + n2;
return sum;
}
// 方法名相同,参数个数不同,构成重载
public static int add(int n1, int n2, int n3) {
int sum = n1 + n2 + n3;
return sum;
}
// 方法名相同,参数类型不同,构成重载
public static double add(double n1, int n2) {
double sum = n1 + n2;
return sum;
}
// 方法名相同,参数顺序不同,构成重载
public static double add(int n1, double n2) {
double sum = n1 + n2;
return sum;
}
//编译错误:只有返回值不同,不构成方法的重载
public static double add(int n1, int n2) {
double sum = n1 + n2;
return sum;
}
//编译错误:只有参数名称不同,不构成方法的重载
public static int add(int n2, int n1) {
double sum = n1 + n2;
return sum;
}
}
⭐ 递归结构
🐟 递归是一种常见的算法思路,在很多算法中都会用到。比如:深度优先搜索(DFS:Depth First Search)等。
🐟 递归的基本思想就是“自己调用自己”。
递归结构包括两个部分:
🐟 定义递归头。 解决:什么时候不调用自身方法。如果没有头,将陷入死循环,也就是递归的结束条件。
🐟 递归体。 解决:什么时候需要调用自身方法。
【eg】使用递归求 n!
public class Test {
public static void main(String[ ] args) {
long d1 = System.currentTimeMillis();
factorial(10);
long d2 = System.currentTimeMillis();
System.out.printf("递归费时:"+(d2-d1)); //耗时:32ms
}
/** 求阶乘的方法*/
static long factorial(int n){
if(n==1){//递归头
return 1;
}else{//递归体
return n*factorial(n-1);//n! = n * (n-1)!
}
}
}
执行结果如下所示:
执行结果如下所示:
递归的缺陷
❗算法简单是递归的优点之一。但是递归调用会占用大量的系统堆栈,内存耗用多,在递归调用层次多时速度要比循环慢的多,所以在使用递归时要慎重。
比如上面的递归耗时 558ms(看电脑配置)。但是用普通循环的话快得多,如下所示。
【eg】使用循环求 n!
public class Test {
public static void main(String[ ] args) {
long d3 = System.currentTimeMillis();
int a = 10;
int result = 1;
while (a > 1) {
result *= a * (a - 1);
a -= 2;
}
long d4 = System.currentTimeMillis();
System.out.println(result);
System.out.printf("普通循环费时:"+(d4 - d3));
}
}
执行结果如下所示: