这一课学的确实是非常的头疼了,各种int i、int j,给本不富裕的脑细胞增加了超级多的压力。。。
跟着敲代码都有敲不对的时候,这个确实有点丢人。上课的时候老师用的是EditPlus,不过因为感觉后面就要换成Eclipse了,所以也就懒得下载了,一直用笔记本敲(弱弱的说一句居然macOS怎么新建笔记本我都百度了一下。。。)。之前就听说想要学好代码,需要数学特别好,真是这样的话,那我这个高考数学87的战五渣真的能学明白这个东西么。。。
感觉现在特别像上学的时候,上课听啥都能听懂,课上的例题也都能做出来或者听明白跟上思路,但是一到写作业的时候就开始抓耳挠腮。自己做课后练习题的之前,都需要把之前做过的题翻出来重新看一遍,再尝试着敲,敲不对了就再看看然后接着尝试着敲,这一课学的真的是效率特别的低,视频断断续续的看,老师说一句,我就得敲一下感受感受,估计后面能够好不少,能够想明白i和j这两对兄弟到底应该有多长。。。
程序流程控制:流程控制语句是用来控制程序中各语句执行顺序的语句,可以把语句组合成能完成一定功能的小逻辑模块,其流程控制方式采用结构化程序设计中规定的三种基本结构
一、顺序结构:程序从上到下,顺序执行,中间没有任何判断和跳转
二、分支结构:
1、根据条件,选择性的执行某段代码
2、有if-else和switch-case两种分支语句
1、if-else结构:条件判断结构,是可以嵌套使用的,不过实际开发中嵌套三层以上的情况很少见,因为逻辑太复杂,会丢情况,针对这种情况一定有更简单的方法。
1、第一种结构
2、第二种结构
3、第三种结构
4、if-else使用说明
1、条件表达式必须是布尔表达式(关系表达式或逻辑表达式)、布尔变量
2、语句块只有一条执行语句时,一对{}可以省略,但建议保留
3、if-else语句结构,根据需要可以嵌套使用
4、当if-else结构是“多选一”时,如果不是一定要有一个结果,最后的else时可选的,根据需要可以省略
5、当多个条件是“互斥”关系时,条件判断语句及执行语句间顺序无所谓
6、当多个条件是“包含”关系时,“小上大下/子上父下”
/*
【一道练习题】
假设你想开发一个玩彩票的游戏,程序随机的产生一个两位数的彩票,提示用户输入一个两位数,然后按照下面的规则判定用户是否能赢
1、如果用户输入的数字匹配彩票的实际顺序,奖金10000美元
2、如果用户输入的所有数字匹配彩票的所有数字,但顺序不一致,奖金3000美元
3、如果用户输入的一个数字仅能满足顺序情况下匹配彩票的一个数字,奖金1000美元
4、如果用户输入的一个数字仅能满足非顺序情况下匹配彩票的一个数字,奖金500美元
5、如果用户输入的数字没有匹配任何一个数字,则彩票作废
提示:使用(int)(Math.random() * 90 / 10)产生随机数
Math.random():[0,1) * 90 ---> [0,90] / 10 ---> [10,99]
*/
import java.util.Scanner;
class Caipiao{
public static void main(String[] args){
//1、随机产生一个两位数
//System.out.println(Math.random());//产生[0,1)
int number = (int)(Math.random() * 90 + 10);//得到[10,99],即[10,100)
//System.out.println(number);
int numberShi = number / 10;
int numberGe = number % 10;
//2、用户输入一个两位数
Scanner input = new Scanner(System.in);
System.out.print("请输入一个两位数:");
int guess = input.nextInt();
int guessShi = guess / 10;
int guessGe = guess % 10;
if(number == guess){
System.out.println("奖金10000美元");
}else if(numberShi == guessGe && numberGe == guessShi){
System.out.println("奖金3000美元");
}else if(numberShi == guessShi || numberGe == guessGe){
System.out.println("奖金1000美元");
}else if(numberShi == guessGe || numberGe == guessShi){
System.out.println("奖金500美元");
}else{
System.out.println("没中奖");
}
System.out.println("中奖号码是:" + number);
}
//如何获取一个随机数:[10,99],推导过程如下:
//double value = Math.random();//[0.0,1.0),解释:math类中,仅有一个random方法,对应的是double型,且取值范围是大于等于0.0,且「小于1.0」
//double value = Math.random() * 100;//[0.0,100.0)
//double value = Math.random() * 90;//[0.0,90.0)
//double value = Math.random() * 90 + 10;//[10.0,100.0)
int value = (int)(Math.random() * 90 + 10)//[10,99],用(int)强制类型转换,从double型转换为int型
//公式:[a,b]:(int)(Math.random() * (b - a + 1) + a)
System.out.println(value);
}
2、switch-case结构:
1、结构:
2、说明:
1、根据switch表达式中的值,按照从上到下的顺序,依次匹配各个case中的常量,一旦匹配成功,则进入相应case结构中,调用其执行语句。当调用完执行语句后,则仍然继续向下执行其他case结构中的执行语句,直到遇到break关键字或此switch-case结构末尾为止结束。
2、Break可以只用在switch-case结构中,表示一旦执行到此关键字,就跳出switch-case结构。default后可以不加break
3、switch结构中的表达式,只能是如下的6种数据类型之一:byte、short、char、int、枚举类型(Java基础高级部分讲解,JDK5.0新增的)、String类型(JDK7.0新增的)
4、case之后,只能声明常量,不能声明一个范围
5、break关键字在switch-case结构中是可选的,如果不加也可以编译通过。不过绝大多数情况是需要写break的。
6、default:类似于if-else结构中的else。default是可选的,而且位置是灵活的。比如:default可以放在首位,但是如果default后面没写break的话,还会执行后续的case。但是平时写代码基本上都是写在后面,没人会写在前面
7、如果switch-case结构中的多个case的执行语句相同,则可以进行合并。
/*
练习1
使用switch把小写类型的char型转化为大写。只转化a、b、c、d、e,其他的输出other
提示:String word = scan.next();,char c = word.charAt(0);switch(c){}
练习2
对学生成绩大于60分的,输出“合格”,低于60分的,输出“不合格”
练习3
根据用于指定月份,打印该月份所属的季节
345春季,678夏季,91011秋季,1212冬季
练习4
编写程序:从键盘上输入2019年的“month”和“day”,要求通过程序输出输入的日期为2019年的第几天
*/
import java.util.Scanner;
class SwitchCaseTest2{
public static void main(String[] args){
/*练习1
Scanner scan = new Scanner(System.in);
System.out.println("请输入小写字母");
String word = scan.next();
char a = word.charAt(0);
switch(a){
case 'a':
System.out.println("A");
break;
case 'b':
System.out.println("B");
break;
case 'c':
System.out.println("C");
break;
case 'd':
System.out.println("D");
break;
case 'e':
System.out.println("E");
break;
default:
System.out.println("other");
}
*/
/*
//练习2
int score = 78;
switch(score / 60){//除10缩小比较范围,少写几个case
case 0://其实就相当于是没写break
System.out.println("不及格");
break;
case 1:
System.out.println("及格");
break;
}
*/
//练习3
/*
同上
*/
//练习4
Scanner scan = new Scanner(System.in);
System.out.println("请输入2019年的month:");
int month = scan.nextInt();
System.out.println("请输入2019年的day:");
int day = scan.nextInt();
int sumDays = 0;
switch(month){
case 12:
sumDays += 30;
case 11:
sumDays += 31;
case 10:
sumDays += 30;
case 9:
sumDays += 31;
case 8:
sumDays += 31;
case 7:
sumDays += 30;
case 6:
sumDays += 31;
case 5:
sumDays += 30;
case 4:
sumDays += 31;
case 3:
sumDays += 28;
case 2:
sumDays += 31;
case 1:
sumDays += day;
}
System.out.println("2019年" + month + "月" + day + "日是当年的第" + sumDays + "天");
}
}
3、需要使用分支结构时,怎么选择if-else结构还是switch-case结构
1、凡是可以使用switch-case的结构,都可以转换为if-else结构,反之不成立(因为switch-case的类型有限制,比如布尔型)
2、如果需要使用分支结构时,两种结构都可以使用,应当优先使用switch-case,不过也要看具体情况(结构冗余程度,即case情况不能太多)。原因:switch-case相较于if-else执行效率稍高
4、使用scanner从键盘获取不同类型的变量
1、导包;import java.util.Scanner
2、Scanner的实例化:Scanner scan = new Scanner(System.in)
3、调用Scanner类的相关方法,来获取指定类型的变量,String是直接next(),其他类型是nextXxx()
4、对于char型的获取,Scanner没有提供相关的方法,只能够获取一个字符串,如果非得要不带双引号的字符(char型),需要用到charAt
5、注意:需要根据相应的方法,来输入指定类型的值,如果输入的数据类型与要求的类型不匹配时,会报异常:InputMisMatchException,导致程序终止
import java.util.Scanner;
class ScannerTest{
public static void main(String[] args){
//Scanner scan = new Scanner(System.in);//System.in意思是从键盘输入
//int num = scan.nextInt();
//System.out.println(num);
Scanner scan = new Scanner(System.in);
System.out.println("请输入你的姓名:");
String name = scan.next();
System.out.println(name);
System.out.println("请输入你的年龄:");
int age = scan.nextInt();
System.out.println(age);
System.out.println("请输入你的体重:");
double weight = scan.nextDouble();
System.out.println(weight);
System.out.println("你是否同意与我约会?(true/false)");
boolean isLove = scan.nextBoolean();
System.out.println(isLove);
//对于char型的获取,Scanner没有提供相关的方法,只能够获取一个字符串
System.out.println("请输入你的性别:(男/女)");
String gender = scan.next();//“男”,“女”
char genderChar = gender.charAt(0);//获取索引为0位置上的字符,因为第一个字符是从0开始的
System.out.println(genderChar);
}
}
三、循环结构
1、根据循环条件,重复性的执行某段代码
2、有while、do-while、for三种循环语句,使用最多的是for循环,while循环。
1、循环结构
2、for循环:
1、初始化条件部分
2、循环条件部分:布尔类型的
3、循环体部分
4、迭代条件部分
class ForTest{
public static void main(String[] args){
for(int i = 1;i <= 5;i++){
System.out.println("Hello World!");
}
//练习
int num = 1;
for(System.out.print('a');num <= 3;System.out.print('c'),num++){//System.out.print('c')和num++中间的逗号意味着迭代条件是两个,这个时候原则上应该用分号隔开,但是用分号的话for()的内容就没办法区分了,所以就用逗号来区分两个迭代条件
System.out.print('b');
}
//输出结果为:abcbcbc
//例题:遍历(从头到尾输出一遍)100以内的偶数,输出所有偶数的和,输出偶数的个数
int sum = 0;//定义一个变量,记录所有偶数的和
int count = 0;//定义一个变量,记录所有偶数的个数
for(int i = 1;i <= 100;i++){
if(i % 2 == 0){
System.out.println(i);
sum += i;
count++;
}
}
System.out.println("和为:" + sum);
System.out.println("所有偶数的个数为:" + count);
}
}
3、while循环:
1、初始化条件部分
2、循环条件部分:布尔类型的
3、循环体部分
4、迭代条件部分:这部分和循环体部分在一起,如果没有迭代条件部分,就会进入死循环。
class WhileTest{
public static void main(String[] args){
//遍历100以内所有的偶数
int i = 1;
while(i <= 100){
if(i % 2 == 0){
System.out.println(i);
}
i++;//没有这个就会进入死循环!!
}
//i变量出了while循环之后仍然可以调用,因为i是在结构外进行定义的
System.out.println(i);
}
}
4、do-while循环:do-while循环至少会执行一次循环体
1、初始化条件部分
2、循环条件部分:布尔类型的
3、循环体部分
4、迭代条件部分
class DoWhileTest{
public static void main(String[] args){
//遍历1-100以内的偶数。并计算所有偶数的和,及偶数的个数
int a = 1;
int sum = 0;
int count = 0;
do{
if(a % 2 == 0){
System.out.println(a);
sum += a;
count++;
}
a++;
}while(a <= 100);
System.out.println("总和为:" + sum);
System.out.println("个数为:" + count);
int num1 = 10;
while(num1 > 10){
System.out.println("Hello: While!");
num1--;
}
int num2 = 10;
do{
System.out.println("Hello: Do-while!");
num2--;
}while(num2 > 10);
}
}
5、循环结构间的转换关系:
1、For循环和while循环是可以相互转换的。
1、不同点:初始化条件的作用域不同:
1、for循环的初始化条件在for后面的()里,比如定义了一个变量i,在for循环中,只在循环内部可以调用,出了循环结构后不可调用
2、但是while结构由于初始化条件定义在结构之前,也就是说是先定义变量然后进入循环结构,所以在循环结构外也可以调用该变量。
2、For循环和while循环如果循环条件不符合是一次都进不了循环结构,但是do-while结构至少会执行一次循环体
3、经验来讲,开发中实际使用for循环和while循环更多一些,较少使用do-while循环
1、如果需要遍历数组等简单循环,for循环会写起来舒服一点
2、但是如果初始化条件相对比较复杂,建议使用while循环,因为while循环的初始化条件是在循环结构最前面
6、不在循环条件部分限制次数的结构:for(;;)或while(true)
7、结束循环的两种方式:
1、方式一:循环条件部分返回false
2、方式二:在循环体中执行break
8、“无限循环”:while(true)、for(;;){}
9、如何结束循环:
1、当循环条件是false时,自动结束循环
2、当循环体中,执行break;
四、嵌套循环:不超过3个
1、定义:将一个循环结构A声明在另一个循环结构B的循环体中,构成了嵌套循环
1、外层循环:循环结构B
2、内层循环:循环结构A
3、内外层是一个“相对上”的概念,要注意区分
2、内层循环结构遍历一遍,只相当于外层循环循环体执行了一次
假设外层循环需要执行m次,内层循环需要执行n次,此时内层循环的循环体一共执行了m * n次
3、相当于:外层循环控制行数,内层循环控制列数
class Loop2{
public static void main(String[] args){
//输出6个*
//System.out.println("******");
//for(int a = 1;a <= 6;a++){
// System.out.print("*");
//}
for(int j = 1;j <= 4;j++){
for(int a = 1;a <= 6;a++){
System.out.print("*");
}
System.out.println();
}
System.out.println();
/* m n
* 1 1
** 2 2
*** 3 3
**** 4 4
***** 5 5
*/
for(int m = 1;m <= 5;m++){//控制行数
for(int n = 1;n <= m;n++){//控制列数
System.out.print("*");
}
System.out.println();
}
System.out.println();
/* m n 规律m + n = 5换句话说n = 5 - m
**** 1 4
*** 2 3
** 3 2
* 4 1
*/
for(int m = 1;m <= 5;m++){
for(int n = 1;n <= 5 - m;n++){
System.out.print("*");
}
System.out.println();
}
System.out.println();
/*
*
**
***
****
*****
****
***
**
*
*/
//上面两个结合到一起就行,这个没有放到一起的方法
/*
*
* *
* * *
* * * *
* * * * *
* * * *
* * *
* *
*
*/
//上半部分
for(int i = 0;i < 5;i++){
//输出" "
for(int j = 0;j < 4 - i;j++){
System.out.print(" ");
}
//输出"* "
for(int k = 0;k < i + 1;k++){
System.out.print("* ");
}
System.out.println();
}
//下半部分
for(int i = 0;i < 4;i++){
for(int j = 0;j < i + 1;j++){
System.out.print(" ");
}
for(int k = 0;k < 4 - i;k++){
System.out.print("* ");
}
System.out.println();
}
}
}
/*
嵌套循环应用:九九乘法表
*/
class Loop3{
public static void main(String[] args){
for(int i = 1;i <= 9;i++){
for(int j = 1;j <= i;j++){
System.out.print(i + " * " + j + " = " + (i * j) + " ");
}
System.out.println();
}
}
}
五、特殊关键字的使用
1、break
1、使用范围:
1、switch-case结构中
2、循环结构中
1、作用:结束当前循环
2、continue
1、使用范围:
1、循环结构中
1、作用:结束当次循环
3、二者相同点:
1、两个关键字后是不能够声明执行语句的,是不可能执行的,编译都无法通过
4、特殊说明:return,这个关键字不是专门用来结束循环的,而是用来结束方法的,之所以能够达成结束循环的结果,是因为整个方法被结束了,循环也就结束了
5、带标签的break和continue的使用:labelXX
class BreakContinueTest{
public static void main(String[] args){
for(int i = 1;i <= 10;i++){
if(i % 4 == 0){
//break;//123
continue;//123567910
}
System.out.print(i);
}
System.out.println("/n");
label:for(int i = 1;i <= 4;i++){//label是个标识
for(int j = 1;j <= 10;j++){
if(j % 4 == 0){
//break;//默认结束包裹此关键字最近的一层循环
//continue;//默认跳出包裹此关键字的最近一层循环的当次循环
//break label;//结束指定标识的一层循环结构
continue label;//结束指定标识的一层循环的当次循环
}
System.out.print(j);
}
System.out.println();
}
}
}
⚠️衡量一个代码功能的优劣
-
正确性
-
可读性
-
健壮性
-
高效率「时间复杂度」与低存储「空间复杂度」——衡量算法好坏的标准
⚠️生成随机数的方法:公式:[a,b]:(int)(Math.random() * (b - a + 1) + a)
/*
例如:生成一个[10,99]范围内的随机整数,方法如下:
*/
double value = Math.random();// [0.0,1.0),解释:math类中,仅有一个random方法,对应的是double型,且取值范围是大于等于0.0,且「小于1.0」
double value = Math.random() * 100;// [0.0,100.0)
double value = Math.random() * 90;// [0.0,90.0)
double value = Math.random() * 90 + 10;// [10.0,100.0)
int value = (int)(Math.random() * ((99 - 10 + 1) + 10))// [10,99],用(int)强制类型转换,从double型转换为int型,并且用int接收
int value = (int)(Math.random() * 90 + 10)
System.out.println(value);
⚠️100以内质数的输出的练习题:
/*
嵌套循环应用:100以内所有的质数的输出
质数:素数,只能被1和自己整除的自然数。==>从2开始,到n-1结束为止,都不能被这个数本身整除
最小的质数是2
*/
class PrimeNumberTest{
public static void main(String[] args){
for(int i = 2;i <= 100;i++){//遍历100以内的自然数
boolean isFlag = true;//作为标识,标识i是否被j除尽,一旦除尽修改其值
for(int j = 2;j < i;j++){//j:被i除,看是否能整除
if(i % j == 0){//i被j整除了
isFlag = false;
}
}
if(isFlag == true){
System.out.println(i);
}
}
}
}
⬇️质数输出优化方案1:在「重置标志」的步骤后添加一个break,即:只要出现一个能够整除i的j,就不继续往下计算了,比如100,除到2的时候发现这个数不是质数,后续的3、4、5等等就不继续计算了。这种方式只对非质数的自然数是有效的,比如97是质数,从1-96除完之后都没有整除,是不会触发break的
/*
嵌套循环应用:100000以内所有的质数的输出
优化方案⬇️
*/
class PrimeNumberTest2{
public static void main(String[] args){
boolean isFlag = true;//作为标识,标识i是否被j除尽,一旦除尽修改其值
//获取当前时间距离1970-1-1 00:00:00的毫秒数
long start = System.currentTimeMillis();
for(int i = 2;i <= 100000;i++){//遍历100000以内的自然数
for(int j = 2;j <= Math.sqrt(i);j++){//j:被i除,看是否能整除//优化方案三:把i开方,记得前面改成<=
if(i % j == 0){//i被j整除了
isFlag = false;
break;//优化方案二:只要出现一个能够整除i的j,就不继续往下计算了,比如100,除到2的时候发现这个数不是质数,后续的3、4、5等等就不继续计算了
}
}
if(isFlag == true){
System.out.println(i);
}
//重置isFlag
isFlag = true;
}
//获取当前时间距离1970-1-1 00:00:00的毫秒数
long end = System.currentTimeMillis();
System.out.println("所花费的时间为:" + (end - start));//优化前6107ms,方案二优化后889ms,方案三优化后为64ms
}
}
⬇️质数输出优化方案2:
将数字开方,原理是:a * b = c,则c / b = a且c / a = b,所以a和b间寻找到的极限值即为a = b,即为根号下c。这种方法对所有数字都有效,但是在优化方案2的基础上,非质数已经在方案2中被break了。开方方法如下⬇️:
Math.sqrt(i)
/*
嵌套循环应用:100000以内所有的质数的输出,实现方式二
优化方案⬇️
*/
class PrimeNumberTest3{
public static void main(String[] args){
//获取当前时间距离1970-1-1 00:00:00的毫秒数
long start = System.currentTimeMillis();
label:for(int i = 2;i <= 100000;i++){//遍历100000以内的自然数
for(int j = 2;j <= Math.sqrt(i);j++){//j:被i除,看是否能整除//优化方案三:把i开方,记得前面改成<=
if(i % j == 0){//i被j整除了
continue label;
}
}
System.out.println(i);
}
//获取当前时间距离1970-1-1 00:00:00的毫秒数
long end = System.currentTimeMillis();
System.out.println("所花费的时间为:" + (end - start));//优化前6107ms,方案二优化后889ms,方案三优化后为64ms
}
}
⚠️运算时间区别验证方法⬇️:
//获取当前时间距离1970-1-1 00:00:00起到现在为止的毫秒数
long start = System.currentTimeMillis();//运算开始前的时间长度
long end = System.cuttentTimeMillis();//运算结束后的时间长度
System.out.println(“所花费的时间为:” + (end - start));//计算两个时间的时间长度差