6. 流程控制语句
求职,任意,请联系我,能敲电脑就行。
内容导视:
- 输入与输出
- 分支控制语句
- 循环控制语句
- 转向控制语句
6.1 输入与输出
内容导视:
- 接收键盘输入
- 普通输出
- 格式化输出
- 输出不同颜色的文字
6.1.1 接收键盘输入
我们需要用到 java .util 包下的 Scanner 类,非 lang 包下的类需要导入,在首行加入 import 完整类名;
import java.util.Scanner;
public class Hello {
public static void main(String[] args) {
// 创建一个 扫描仪 实例,扫描指定输入流产生的值(System.in 流对应于键盘输入)
Scanner scanner = new Scanner(System.in);
System.out.print("等待你的输入,请按下任意键:");
// 等待输入 字符串 ,使用 String 类型的变量 name 接收
String name = scanner.next();
System.out.println("你输入的字符串为:" + name);
System.out.print("等待你输入整数:");
// 等待输入整数,使用 int 类型的变量 num 接收
int num = scanner.nextInt();
System.out.println("你输入的整数为:" + num);
/*
scanner.nextDouble() 是接收小数
scanner.next().charAt(0) 是接收字符
*/ }
}
不想输入,请按 Ctrl + C 结束程序。
注意,让你输入整数时,你却输入了其它类型的字符,会报 java.util.InputMismatch Exception 输入不匹配异常。
可以使用如下方法避免:
System.out.print("等待输入中:");
// 如果输入的是整数,此方法会返回 true,执行第一条分支
if (scanner.hasNextInt()) {
int num = scanner.nextInt();
System.out.println("你输入的是个整数:" + num);
} else {
System.out.println("你输入的不是整数");
}
可以自定义 Readable 的实现类,next 方法内部会调用 getCompleteTokenInBuffer 方法以空格为分隔点读取字符串缓冲区 cb 中的内容,返回的内容称为 token;
当 cb 中无内容或无空格作为分隔点,会调用 read 方法往字符串缓冲区 cb 填充内容,现在字符串缓冲区内容为: 随意内容 随意内容2 随意内容3 ;
再次调用 getCompleteTokenInBuffer 方法,返回 随意内容 ;
如果调用 read 方法时返回了 -1(规定读到末尾返回 -1),cb 不再填充内容,getCompleteTokenInBuffer 会以空格为分隔点读取 cb 中的内容并返回(如果没有空格,直接读取剩余的所有内容),直至无内容可以读取;
这时再调用 next 方法就会抛出 NoSuchElementException 没有更多的元素异常,可以使用 scanner.hasNext 方法判断是否还有内容可以读取;
如果 cb 一直在填充内容,直到 char 数组容量超出 1024 后,报 BufferOverflowException 缓冲区溢出 异常。
public class TestScanner {
public static void main(String[] args) {
Scanner scanner = new Scanner(new MyReadable());
System.out.println(scanner.next());// 随意内容
System.out.println(scanner.next());// 随意内容2
System.out.println(scanner.next());// 随意内容3随意内容
}
}
class MyReadable implements Readable {
@Override
public int read(CharBuffer cb) throws IOException {
StringBuilder builder = new StringBuilder ("随意内容");
builder.append(" ");
builder.append("随意内容2");
builder.append(" ");
builder.append("随意内容3");
cb.append(builder);
return builder.length();// 没有返回 -1,cb 无限填充内容
}
}
System.in 的实际类型为 java.io.Buffered InputStream ,可以使用 InputStreamReader 转换成字符流,将输入存入 char 数组。
InputStreamReader reader = new InputStreamReader(System.in);
char[] chars = new char[1024];
int length = reader.read(chars, 0, 1024);
System.out.println(new String(chars, 0, length));
没想到啊, handle 是随机生成的唯一句柄
/**
* 调用 read 方法等待键盘输入,将结果存入 bytes 数组中
*/ public static void main(String[] args) throws Exception {
FileDescriptor fd = new File Descriptor();
Field handle = FileDescriptor.class.getDeclaredField("handle");
handle.setAccessible(true);
handle.set(fd, getHandle());
FileInputStream inputStream = new FileInputStream(fd);
byte [] bytes = new byte[1024];
System.out.print("请输入:");
int length = inputStream.read(bytes);
String result = new String(bytes, 0, length);
System.out.println(result);
}
/*
* 获取 obj 的某个字段值,从 obj 的实际类型开始找,找不到就往上找
* 一直到 Object 还没有,抛出异常
*/ private static <T> Object getFieldValue(T obj, String fieldName) throws NoSuchFieldException, IllegalAccessException {
Class<?> currentClass = obj.getClass();
while (currentClass != null) {
try {
Field field = currentClass.getDeclaredField(fieldName);
field.setAccessible(true);
return field.get(obj);
} catch (NoSuchFieldException e) {
currentClass = current Class .getSuperclass();
}
}
throw new NoSuchFieldException(fieldName);
}
private static <T> Object getCurrentClassField(T obj, String fieldName) throws IllegalAccessException, NoSuchFieldException {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
return field.get(obj);
}
private static <T> T cast(Object o, Class<T> toClass) {
if (o == null) {
return null;
}
if (toClass.isInstance(o)) {
return toClass.cast(o);
}
throw new ClassCastException(o.getClass() + " cannot be cast to " + toClass);
}
private static long getHandle() {
try {
// 在 BufferedInputStream 的父类 FilterInputStream 中找到 in 字段
FileInputStream fileInputStream = (FileInputStream) getFieldValue(System.in, "in");
FileDescriptor fd = fileInputStream.getFD();
return (long) getFieldValue(fd, "handle");
} catch (IOException | NoSuchFieldException | IllegalAccessException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
6.1.2 普通输出
输出与打印是一个意思,就是使用标准输出流 System .out 在控制台上显示一些文本信息。
System.out.println(“向控制台输出一些内容”);
println 是 print line,即打印并换行:如果接着打印,内容会在下一行显示;如果想下次打印时,文本在同一行,去掉 ln。
6.1.3 格式化输出
就是把数据按指定格式输出,使用 printf 配合占位符 %,先把地方占着,再接收数据填充上去;
由于没有 ln,需要使用 转义字符 n 手动换行, %d 是给整数占位置, %s 是给字符串占位置。占位后,需要传入值,从左至右,顺序不可颠倒,数据类型必须要对应上。
下面是常用的 占位符 :
占位符 | 类型 |
%d | 十 进制 整数 |
%x | 十六进制整数 |
%o | 八进制整数 |
%f | 浮点数 |
%e | 以 科学计数法表示 的 浮点数 |
%s | 字符串 |
%c | 字符 |
%b | 布尔 类型 |
String ln = "n";
System.out.println("班级人员详情:");
String str = "序号:%d,姓名:%s" + ln;
System.out.printf(str, 101, "张三");
System.out.printf(str, 102, "猛男");
System.out.printf(str, 103, "武丑");
System.out.println();
double num = 3.14256;
double num2 = 100123450.0;
int num3 = 15;
System.out.println("数字测试:");
System.out.printf("这是小数:%f" + ln, num);
System.out.printf("%f 保留 2 位小数:%.2f" + ln, num, num);
System.out.printf("%f 的科学计数法显示:%e" + ln, num2, num2);
System.out.printf("%d 对应的十六进制:%x" + ln, num3, num3);
printf 方法底层调用 format 方法实现,所以使用 System.out.format()效果也一样。
6.1.4 输出不同颜色的文字
我一般使用两种颜色打印内容,已足够用了。
System.out.println("正常内容");// 白色
System.err.println("警告内容");// 红色
也许你不满足这两种颜色,可以输出以下字符串修改字体样式:
“33[?m”
问号取值如下:(多个 ? 使用 ; 分隔)
0:原本样式
1:粗体
3:斜体
4:下划线
7:背景颜色与字体颜色互换
9:中划线
21:粗下划线
51、52:方框
30 ~ 37:字体颜色分别为黑色、红色、绿色、黄色、蓝色、紫色、蓝绿色、灰色
40 ~ 47:背景颜色分别为黑色、红色、绿色、黄色、蓝色、紫色、蓝绿色、灰色
90 ~ 97:字体颜色,同 30 ~ 37,但更深
大致分为三类:
font Color:字体颜色,在 30 ~ 37 范围内,默认白色
background:背景颜色,在 40 ~ 47 范围内,默认黑色
fontStyle:字体样式:1、3、4、9
假如我现在想要输出带中划线的红色粗斜体,选用 9、31、1、3 这 4 种:(顺序不分先后)
System.out.print("33[9;31;1;3m");
System.out.println("指定样式的字体");
System.out.println("指定样式的字体2");
想要恢复为原本样式,输出如下字符串:
System.out.print("33[0m");
System.out.println("原本样式");
可以配合占位符使用:
// 格式化输出字符串
public static void printf(String str, int fontColor, int background, int fontStyle) {
// 前半部分使用占位符,指定输出字体的样式,后半部分 33[0m 还原样式,n 换行
System.out.printf("33[%d;%d;%dm%s33[0mn", fontColor, background, fontStyle, str);
}
// 返回格式化的字符串
public static String getColorString(String str, int fontColor, int background, int fontStyle) {
return String.format("33[%d;%d;%dm%s33[0m", fontColor, background, fontStyle, str);
}
// 输出的字符串为蓝色
public static void printlnBlueFont(String str) {
System.out.println(getColorString(str, 34, 2, 2));
}
// 输出的字符串为紫色
public static void printlnPurpleFont(String str) {
printf(str, 35, 2, 2);
}
public static void main(String[] args) {
printlnBlueFont("Hello World!");
printlnPurpleFont("Hello World!");
// 输出蓝色背景、带下划线的红色字体
printf("Hello World!", 31, 44, 4);
}
6.2 分支 控制语句
内容导视:
- if else
- switch case
分支控制语句也称选择语句。
程序一般自上而下,逐行执行,但是有时也需要特殊操作;比如根据条件选择性地执行某段代码、循环执行某段代码…
6.2.1 if else
if else 语句最多只会执行一条分支,类似走路遇见岔路,只能选一条通过。先从最简单的 if 讲起。
单条分支
语法:
if (布尔类型的值) {
java 语句...
}
当布尔类型的值为 true 时,才会执行 {} 中的 java 语句,如下:
int age1 = 11;
int age2 = 8;
if (age1 > age2) {
System.out.println("我比你大");// 此句话会输出
}
当 {} 中只有一条语句时,可以省略 {};但是最好不要那么做,以免别人误解。
if (age1 > age2) System.out.println("我比你大");
if else
语法:
if (布尔类型的值) {
分支一...
} else {
分支二...
}
布尔类型的值为 true,执行第一条分支;为 false,执行第二条分支。
char sex = '1';
if (sex == '0') {
System.out.println("输出女");
} else {
System.out.println("输出男");// 此句会输出
}
多条分支
语法:
if (值1) {
分支一...
} else if (值2) {
分支二...
} else if (值3) {
分支三...
} ...
从上至下,只执行第一个值为 true 的分支;else if 可以无限追加。
double scope = 100.0;
if (scope < 60) {
System.out.println("你的成绩不及格");
} else if (scope < 80) {
System.out.println("你的成绩一般");
} else if (scope < 90) {
System.out.println("你的成绩良好");
} else if (scope < 100) {
System.out.println("你的成绩优秀");
}
上面的分支都不会执行,因为从上至下,没有布尔表达式为 true 的。
int age = 20;
if (age < 18) {
System.out.println("未成年人");
} else if (age < 35) {
System.out.println("青年人"); // 此句将会输出
} else if (age < 50) {
System.out.println("中年人");
}
执行第二条分支。
if else if … else
语法:
if (值1) {
分支一...
} else if (值2) {
分支二...
} else if (值3) {
分支三...
} ...
} else {
最后的分支
}
当所有分支括号里的值都为 false 时,执行最后的分支。
int age = 888;
if (age < 18) {
System.out.println("未成年人");
} else if (age < 35) {
System.out.println("青年人"); // 此句将会输出
} else if (age < 50) {
System.out.println("中年人");
} else if (age < 200) {
System.out.println("老年人");
} else {
System.out.println("妖怪吧!");// 由于上面分支都不满足,执行此条分支
}
嵌套分支
if 语句中有 if 语句。
double scope = 120;
if (scope >= 0 && scope <= 100) {
// 语句块 1
if (scope < 60) {
System.out.println("你的成绩不及格");
} else if (scope < 80) {
System.out.println("你的成绩一般");
} else if (scope < 90) {
System.out.println("你的成绩良好");
} else {
System.out.println("你的成绩优秀");
}
} else {
// 语句块 2
System.out.println("这是人能考出的成绩?");
}
/*
只有分数在 [0 ~ 100] 之间才会执行语句块 1,否则执行语句块 2
*/
嵌套最好不要超过三层,否则人容易迷惑,可读性太差。(可以使用 if else)
if else 不是新的关键字,而是嵌套的简单写法,因为当 if 或 else 中只包含一条 java 语句时,大括号可以省略。(建议不要省略)
if (true)
System.out.println("好");
else
System.out.println("差");
例:
int scope = 50;
if (scope < 60) {
System.out.println("差");
} else if (scope < 80) {
System.out.println("良");
} else if (scope <= 100) {
System.out.println("优");
}
把被省略的大括号加上:
int scope = 50;
if (scope < 60) {
System.out.println("差");
} else {
if (scope < 80) {
System.out.println("良");
} else {
if (scope <= 100) {
System.out.println("优");
}
}
}
6.2.2 switch case
语法:
switch (值) {
case 字面量1:
语句块1;
break;
case 字面量2:
语句块2;
break;
case 字面量3:
语句块3;
break;
...
default:
语句块4;
}
从上至下,执行字面量等于 switch 括号中的值的 case 分支语句,遇见 break 跳转至 switch 主体的末尾,然后结束 switch 语句。
default 如同 else 一样,可以不写;当没有一个匹配上时,就执行此分支的语句。
char key = 's';
switch (key) {
case 'w' :
System.out.println("上");
break;
case 's' :
System.out.println("下");// 此条将会被输出
break;
case 'a' :
System.out.println("左");
break;
case 'd' :
System.out.println("右");
break;
default:
System.out.println("其它");
}
注意 :
- case 后的字面量对应的数据类型必须与 switch 括号中的值的类型一致,或者可以自动转成此类型。
- case 后的字面量值不能重复。
- switch 括号中的值的数据类型只能是 int、String、Enum。(能够自动转换为这三种类型的也算)
- case 后的值不能是变量,只能是字面量与常量。
JDK7 之前只能是 int。
case 穿透现象
当执行某条 case 分支的语句时,如果没有 break 语句结尾,直接顺序执行之下的所有 case、default 分支的语句;只有执行了 break 语句,才会退出 switch 语句。
看清楚了,我把每条分支的 break 都略去了。
String weather = "sunny";
switch (weather) {
case "cloudy" :
System.out.println("阴天");
case "sunny" :
System.out.println("晴天");
case "rain" :
System.out.println("雨天");
default:
System.out.println("其它天气");
}
当输出 “晴天” 时,由于没有遇到 break 语句,继续执行之下语句,输出 “雨天”、“其它天气”。
打个比方吧:
在一个神奇的小区,房子一排排却单向连通,一旦进入其中一家,便可以由内部通道去往下一家。有的住户觉得很不安全,偷偷做了防范措施。
一个小偷捡到了一把锁,尝试着用它开启一户户门,从巷头走到巷尾。诶,有一扇门开了,进去拿了东西,但是主人安了警报铃,小偷马上 GG 了。另一个小偷也来了,也打开扇门,恰好主人家忽视了防护,小偷偷完这家后,直接顺着通道前往下一家,居然都没防范措施,直接全部偷完。
case 合并
如果多个 case 分支执行的是相同的语句,可以将其合并:
char sex = '男';
switch (sex) {
case '0' : case '女' :
System.out.println("我有四个蛋");
break;
case '1' : case '男' :
System.out.println("咖喱?什么咖喱?");
System.out.println("快还给我!这是我的!");
break;
default:
System.out.println("只想守护你");
}
当 sex 等于 ‘0’ 或 ‘女’ 时,执行第一条分支;sex 等于 ‘1’ 或 ‘男’ 执行第二条分支;都不满足执行默认分支。
这不是新语法,只是 case ‘0’ 分支中没有语句,包括 break 语句;当 sex = ‘0’ 时,会出现 case 穿透现象,接着执行下个 case 分支中的语句,也就是 case ‘女’,直到遇见 break 退出 switch 语句。
switch (sex) {
case '0' :
case '女' :
System.out.println("我有四个蛋");
break;
case '1' :
case '男' :
...
}
default
同理,default 分支不一定非要在最后一条,也可以与第一条的 case 语句合并,都不匹配时,case 穿透,执行第一条分支。
如下例,都不满足,应执行 default 中的语句,由于 default 中没有 break 语句,接着执行 case 0 分支,输出 0 ,遇到 break,结束 switch 语句。
switch(3) {
default:
case 0:
System.out.println("0");
break;
case 1:
System.out.println("1");
break;
case 3:
System.out.println("2");
break;
}
6.3 循环控制语句
内容导视:
- for
- while
有些时候,我们需要重复执行某些事情…
6.3.1 for
语法:
for (初始化表达式; 布尔值(循环条件); 更新表达式) {
循环体中的语句
}
执行顺序:
先执行初始化表达式,如果循环条件为 true,则执行循环体中的语句;
执行完后,执行更新表达式,如果循环条件为 true,则执行循环体中的语句…
直到循环条件为 false,退出 for 语句。
示例:
for (int i = 0; i < 2; i++) {
System.out.println("两指夫人" + i);
}
分析:
i --> 0;
此时 i 小于 2,为 true,执行循环体中的语句,输出:两指夫人0
执行更新表达式 i++
i --> 1;
此时 i 小于 2,为 true,执行循环体中的语句,输出:两指夫人1
执行更新表达式 i++
i --> 2;
此时 i 小于 2 为 false,退出循环
小技巧:
1)由于定义在 for () 中的变量的作用域仅在 for 循环体内:
for (int i = 0; i < 2; i++) {}
System.out.println(i);
Hello.java:8: 错误: 找不到符号
System.out.println(i);
为了能够在其它地方能够访问到此变量,可以将其提取出来。
int i = 0;
for (; i < 2; i++) {}
System.out.println(i);
2)初始化语句与更新表达式可以有多个,使用 , 隔开,但是要求变量的类型相同。
for (int i = 1, j = 2; i < 4 && j < 6; i++, j += 2) {
System.out.println("i:" + i
+ ",j:" + j);
}
分析:
i --> 1,j --> 2
i < 4 && j < 6 为 true,执行循环体,输出:i:1,j:2
执行更新表达式:i++、j += 2
i --> 2,j --> 4
i < 4 && j < 6 为 true,执行循环体,输出:i:2,j:4
执行更新表达式:i++、j += 2
i --> 3,j --> 6
i < 4 && j < 6 为 false,结束 for 循环
6.3.2 while
语法:
while (布尔值(循环条件)) {
循环体中的语句
}
如果布尔值为 true,执行循环体;执行结束后再判断布尔值…直到布尔值为 false,退出循环。
示例:
int i = 0;
while (i < 3) {
System.out.println(i);
i++;
}
输出 0、1、2;因为当 i = 3 时,i < 3 为 false,退出循环。
do while
语法:
do {
循环体中的语句
} while (布尔值);
先执行循环体,执行结束后判断布尔值,如果为 true,执行循环体,再判断布尔值…直到布尔值为 false,退出循环。
与 while 不同的是,do while 是先执行再判断,所以一定会执行一次。
示例:
int i = 0;
do {
System.out.println(i);
i++;
} while (i < 3);
输出 0、1、2。
嵌套循环
以 for 循环为例。之前先讲过嵌套,你就理解为套娃就行了,看到阿衰他妈给阿衰做了一笼包子,结果大脸妹打开包子一看,包子里有多个小包子,继续打开,更小的包子…无穷尽也,很影响食欲。嵌套最好不要超过三层,第一怕人发昏,第二是效率低。
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
System.out.println(j);
}
}
分析:
当 i = 0 时,执行循环体输出 0、1、2,当 j = 3 时,不满足条件,结束内层 for 循环
当 i = 1 时,执行循环体输出 0、1、2...
当 i = 2 时,执行循环体输出 0、1、2...
当 i = 3 时,不满足循环条件,退出外层 for 循环
外层的 for 一共循环执行 3 次;每次外层循环时,内层的 for 会循环执行 3 次;也就是内层的循环体中的语句一共执行 3 * 3 = 9 次。
6.4 转向控制语句
内容导视:
- break
- continue
- return
终止或跳过循环…
6.4.1 break
break 代表终止语句块的执行(不再执行),一般用于 switch case、for、while 中。
举例:当 i = 2 时,终止循环。
int i = 0;
while (i < 10) {
if (i == 2) {
System.out.println("终止了循环");
break;
}
System.out.println(i);
i++;
}
输出 0、1;当 i 等于 2 时,执行 if 语句,输出 “终止了循环”,接着执行 break 终止 for 循环。
当 break 用在嵌套循环中,默认终止最近的循环体。
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 2; j++) {
if (j == 1) {
break;
}
System.out.println(j);
}
}
分析:
当 i = 0 时,输出 0
当 i = 1 时,输出 0
当 i = 2 时,输出 0
因为当 j = 1 时,满足了 if 条件,执行了 break,终止了里层的 for 循环。
(只是代表里层的循环执行结束,但外层循环还在继续执行)
当 i = 3 时,不满足条件,退出外层循环。
也可以自定义标签,终止标签对应的循环。
key1: for (int i = 0; i < 3; i++) {
key2: for (int j = 0; j < 2; j++) {
if (j == 1) {
break key1;
}
System.out.println(j);
}
}
输出 0;当 j = 1 时,执行了 break,终止 key1 对应的 for 语句。
6.4.2 continue
跳过本次循环。(提前结束本次循环,开始下一次循环)
int i = 0;
while (i < 6){
i++;
if (i == 3){
continue;
}
System.out.println(i);
}
当 i = 3 时,执行了 continue,跳过本次循环,没有执行剩下的代码,直接进入下一次循环。所以输出 1、2、4、5、6。
需要注意的是,此时我把 “i++” 放在了前面;因为 i++ 如果在 continue 后,当 i = 3 时,跳过本次循环,没有执行 i++,下次循环 i 还是 3,一直跳过,无限循环,永远执行不到 i++。
6.4.3 return
当方法没有返回值(void)时,使用 return; 代表结束当前方法的执行。(结束当前方法意味着其它语句不再执行;在 main 方法中使用代表结束程序)
public static void main(String[] args) {
// for
for (int i = 0; i < 6; i++) {
System.out.println(i);
if(i == 4) {
return;// 同一个域中,此语句必须放到结尾处,因为之后的语句执行不到,没有意义
}
}
// 其它语句
System.out.println("代码块");
}
输出 0、1、2、3、4 后,当 i = 4 时,执行 if 语句中的 return,结束 main 方法的执行。
如果把 return 换成 break,只结束了 for 循环,接着还会输出 “代码块”。
return 以后讲方法返回值时还会用到,现在只需了解它能够终止方法执行即可。
6.x 总结回顾
switch case 语句注意 break 不可省略,以免出现 case 穿透现象。
for 与 while 循环注意控制循环结束条件,防止循环条件一直为 true,避免无限循环。
6.y 课后习题
6.1 打印 [n,m] 之间的所有是 x 倍数的整数,统计个数及总和。
6.2 输出 1 + (n – 1)= n、2 + (n – 2)= n、…、50 + (n – 50)= n。
6.3 打印九九乘法口诀表。
6.4 打印一个 n 层的空心金字塔。例:当 n = 5 时,输出如下:
*
* *
* *
* *
*********
6.5 找出 3 位数的水仙花数,水仙花数是各位数的三次方之和等于此数,比如 153 = 13 + 53 + 33 = 153。
6.6 计算 1 – 1/2 + 1/3 – 1/4 + … – 1/100。
6.7 计算 1 + (1 + 2)+ (1 + 2 + 3)+ … + (1 + 2 + 3 + 4 + … + 100)。
6.z 习题答案
6.1 打印 [n,m] 之间的所有是 x 倍数的整数,统计个数及总和。
先考虑输出 [1,50] 之间所有是 5 的倍数:
// (1)输出 1 ~ 50
for (int i = 1; i <= 50; i++) {
System.out.println(i);
}
// (2)只有当 i 是 5 的倍数时,才输出
for (int i = 1; i <= 50; i++) {
if (i % 5 == 0) {
System.out.println(i);
}
}
// (3)定义变量保存统计个数、和
int count = 0;
int sum = 0;
for (int i = 1; i <= 50; i++) {
if (i % 5 == 0) {
count++;
sum += i;
}
}
System.out.println("总和:" + sum);
System.out.println("是 5 的倍数的数的个数:" + count);
// (4)扩展到一般情况
// 这里可以考虑接收键盘输入,得到整数值
int n = 1;
int m = 50;
int x = 5;
int count = 0;
int sum = 0;
for (int i = n; i <= m; i++) {
if (i % x == 0) {
count++;
sum += i;
}
}
System.out.println("总和:" + sum);
System.out.println("是 " + x + " 的倍数的数的个数:" + count);
6.2 输出 1 + (n – 1)= n、2 + (n – 2)= n、…、50 + (n – 50)= n。
观察变的部分,1 ~ 50,将其提取出来,定义为 i:
for (int i = 1; i <= 50; i++) {
System.out.println(i + " + (n - " + i + ") = n");
}
6.3 打印九九乘法口诀表。
效果如图:
由于我们是按行输出的,所以观察递增的部分:1 ~ 9;也就是乘号右边。
for (int i = 1; i <= 9; i++) {
System.out.println(i);
}
观察乘号左边与 i 的关系
当 i = 1 时,输出 1
i = 2 时,输出 1、2
i = 3 时,输出 1、2、3
...
i = 8 时,输出 1、2、3、4、5、6、8
i = 9 时,输出1、2、3、4、5、6、8、9
可以发现输出的开始都是 1,结束都是 i
定义一个变量 j,使用 for 循环输出这些值
for (int i = 1; i <= 9; i++) {
System.out.print(i + "t");
for (int j = 1; j <= i; j++) {
System.out.print(j + " ");
}
System.out.println();
}
开始组合 “j * i = 某值” 字符串:
for (int i = 1; i <= 9; i++) {
for (int j = 1; j <= i; j++) {
System.out.print(j + "x" + i
+ "=" + (i * j) + "t");
}
System.out.println();
}
注意同行显示的乘法口诀,别用 ln 换行了;应该输出完这行所有的乘法口诀后,再换行。
6.4 打印一个 n 层的空心金字塔。例:当 n = 5 时,输出如下:
*
* *
* *
* *
*********
简化 1:打印实心金字塔
*
***
*****
*******
*********
简化 2:将星号前面空格去掉
*
***
*****
*******
*********
第 2 层是 1+2 = 2*2 - 1
第 3 层是 1+2+2 = 2*3 - 1
第 4 层是 1+2+2+2 = 2*4 - 1
通过观察,发现每层都比上层多 2 个星号,通过归纳得出第 i 层为 2 * i – 1 个星号。
先打印 5 层。i 控制层数 1 ~ 5 层;j 控制每层的星号数,一共输出 2 * i – 1 个星号。
for (int i = 1; i <= 5; i++) {
for (int j = 1; j <= 2 * i - 1; j++) {
System.out.print("*");
}
System.out.println();
}
接下来观察简化 1:
第 1 行前面有 4 个空格
第 2 行前面有 3 个空格
第 3 行前面有 2 个空格
第 4 行前面有 1 个空格
第 5 行前面有 0 个空格
所以当第 i 行前面应该打印 5 - i 个空格后再打印 *
for (int i = 1; i <= 5; i++) {
for (int z = 1; z <= 5 - i; z++) {
System.out.print(" ");
}
for (int j = 1; j <= 2 * i - 1; j++) {
System.out.print("*");
}
System.out.println();
}
接下来观察最初的空心版本,除了最后一层,其它层除了开始的和结束的星号,都输出空格:
for (int i = 1; i <= 5; i++) {
for (int z = 1; z <= 5 - i; z++) {
System.out.print(" ");
}
for (int j = 1; j <= 2 * i - 1; j++) {
// 不是最后一层,除了开始与结束输出星号,其它都输出空格
if (i != 5) {
if (j == 1 || j == 2 * i - 1) {
System.out.print("*");
} else {
System.out.print(" ");
}
} else {
System.out.print("*");
}
}
System.out.println();
}
最后将 5 改为 n:
int n = 10;
for (int i = 1; i <= n; i++) {
for (int z = 1; z <= n - i; z++) {
System.out.print(" ");
}
for (int j = 1; j <= 2 * i - 1; j++) {
if (i != n) {
if (j == 1 || j == 2 * i - 1) {
System.out.print("*");
} else {
System.out.print(" ");
}
} else {
System.out.print("*");
}
}
System.out.println();
}
6.5 找出 3 位数的水仙花数,水仙花数是各位数的三次方之和等于此数,比如 153 = 13 + 53 + 33 = 153。
首先需要得到各位上的数:
设一个三位数的整数 i,把百位、十位、个位上的数得到
百位 = i / 100
十位 = i % 100 / 10
或
十位 = i / 10 % 10
个位 = i % 10
那么从 100 ~ 999 范围内查找,当满足水仙花就输出:
for (int i = 100; i < 1000; i++) {
int h = i / 100;
int t = i % 100 /10;
int u = i % 10;
// 可用 Math 的 pow 方法求幂
if (i == h * h * h + t * t * t + u * u * u) {
System.out.println(i);
}
}
扩充内容:
得到四位数上的各位数,以 1234 为例
千 1234 / 1000
百 1234 % 1000 / 100 或 1234 / 100 % 10
十 1234 % 100 / 10 或 1234 / 10 % 10
个 1234 % 10
6.6 计算 1 – 1/2 + 1/3 – 1/4 + … – 1/100。
观察规律:
分子都为 1,分母以 1 递增到 100;设为 i,当 i 为偶数时,符号为负。
double sum = 0;
for (int i = 1; i <= 100; i++) {
if (i % 2 == 0) {
sum += -1.0 / i;
} else {
sum += 1.0 / i;
}
/*
double num = i % 2 == 0 ? -1.0 / i : 1.0 / i;
sum += num;
*/}
System.out.println(sum);// 0.688172179310195
分子的 1 必须写成小数,否则除了第一项其它式子的结果都为 0;sum 必须为 double 类型,如果为 int 类型,+= 会被强转为整数,恰巧结果小于 1,统统转成 0。
6.7 计算 1 + (1 + 2)+ (1 + 2 + 3)+ … + (1 + 2 + 3 + 4 + … + 100)。
方法 1:
一共有 100 个式子相加
第 1 个式子是 1
第 2 个式子是 1+2
第 3 个式子是 1+2+3
得出规律,当 i > 1 时
第 i 个式子是从 1 加到 i,想办法把 1 到 i 这几个数弄到手,累加
定义 i 控制式子的个数,从 1 到 100
定义 j 控制每个式子,从 1 加到 i
int sum = 0;
for (int i = 1; i <= 100; i++) {
for (int j = 1; j <= i; j++) {
sum += j;
}
}
System.out.println(sum);// 171700
方法 2:
第 1 个式子是 1 = 1
第 2 个式子是 1+2 = 3
第 3 个式子是 1+2+3 = 6
得出规律,当 i > 1 时
第 i 个式子是第 i-1 个式子的结果 + i
i 从 1 到 100
考虑使用 formula 记录第 1 个式子的和,然后不停地累加 i。
第一次循环,formula = 1
第二次循环,formula + i = 1 + 2 = 3
第三次循环,formula + i = 3 + 3 = 6
...
每个式子的和得到了,再定义 sum 变量将这些式子的和累加,即 sum += formula
同时为了和谐,修正 formula = 0;第一次循环 formula + i = 0 + 1 = 1
int formula = 0;
int sum = 0;
for (int i = 1; i <= 100; i++) {
formula += i;
sum += formula;
}
System.out.println(sum);
方法 3:
观察式子
发现 1 出现了 100 次,2 出现了 98 次...100 出现了 1 次
所以总和为 1 * 100 + 2 * 99 + ... + 100 * 1
int sum = 0;
for (int i = 1; i <= 100; i++) {
sum += i * (101 - i);
}
System.out.println(sum);
扩展为一般情况:
int n = 100;
int sum = 0;
for (int i = 1; i <= n; i++) {
sum += i * (n + 1 - i);
}
System.out.println(sum);