一前言
今天依旧更新有关JAVA基础的知识,唉。自从更新JAVA之后浏览量什么的都下降了,可能是大家也不喜欢这么枯燥的基础学习吧,但是基础还是很重要的,明天和后天可能会停更,因为我要回家了。
二主要内容
if条件判断(和c基本一致)
在Java程序中,如果要根据条件来决定是否执行某一段代码,就需要if语句。
if语句的基本语法是:
if (条件) { // 条件满足时执行 }根据if的计算结果(true还是false),JVM决定是否执行if语句块(即花括号{}包含的所有语句)。
让我们来看一个例子:
// 条件判断 public class Main { public static void main(String[] args) { int n = 70; if (n >= 60) { System.out.println("及格了"); } System.out.println("END"); } }当条件n >= 60计算结果为true时,if语句块被执行,将打印"及格了",否则,if语句块将被跳过。修改n的值可以看到执行效果。
注意到if语句包含的块可以包含多条语句:
// 条件判断 public class Main { public static void main(String[] args) { int n = 70; if (n >= 60) { System.out.println("及格了"); System.out.println("恭喜你"); } System.out.println("END"); } }当if语句块只有一行语句时,可以省略花括号{}:
// 条件判断 public class Main { public static void main(String[] args) { int n = 70; if (n >= 60) System.out.println("及格了"); System.out.println("END"); } }但是,省略花括号并不总是一个好主意。假设某个时候,突然想给if语句块增加一条语句时:
// 条件判断 public class Main { public static void main(String[] args) { int n = 50; if (n >= 60) System.out.println("及格了"); System.out.println("恭喜你"); // 注意这条语句不是if语句块的一部分 System.out.println("END"); } }由于使用缩进格式,很容易把两行语句都看成if语句的执行块,但实际上只有第一行语句是if的执行块。在使用git这些版本控制系统自动合并时更容易出问题,所以不推荐忽略花括号的写法。(这个就是书写规范的问题了,这个地方也很容易出题,因为不加括号if只能控制它下面的一条语句,第二条就不行了,大家注意)
else
if语句还可以编写一个else { ... },当条件判断为false时,将执行else的语句块:
// 条件判断 public class Main { public static void main(String[] args) { int n = 70; if (n >= 60) { System.out.println("及格了"); } else { System.out.println("挂科了"); } System.out.println("END"); } }修改上述代码n的值,观察if条件为true或false时,程序执行的语句块。
注意,else不是必须的。
还可以用多个if ... else if ...串联。例如:
// 条件判断 public class Main { public static void main(String[] args) { int n = 70; if (n >= 90) { System.out.println("优秀"); } else if (n >= 60) { System.out.println("及格了"); } else { System.out.println("挂科了"); } System.out.println("END"); } }串联的效果其实相当于:
if (n >= 90) { // n >= 90为true: System.out.println("优秀"); } else { // n >= 90为false: if (n >= 60) { // n >= 60为true: System.out.println("及格了"); } else { // n >= 60为false: System.out.println("挂科了"); } }在串联使多个if时,要特别注意判断顺序。观察下面的代码:
// 条件判断 public class Main { public static void main(String[] args) { int n = 100; if (n >= 60) { System.out.println("及格了"); } else if (n >= 90) { System.out.println("优秀"); } else { System.out.println("挂科了"); } } }执行发现,n = 100时,满足条件n >= 90,但输出的不是"优秀",而是"及格了",原因是if语句从上到下执行时,先判断n >= 60成功后,后续else不再执行,因此,if (n >= 90)没有机会执行了。
正确的方式是按照判断范围从大到小依次判断:
// 从大到小依次判断: if (n >= 90) { // ... } else if (n >= 60) { // ... } else { // ... }或者改写成从小到大依次判断:
// 从小到大依次判断: if (n < 60) { // ... } else if (n < 90) { // ... } else { // ... }使用if时,还要特别注意边界条件。例如:
// 条件判断 public class Main { public static void main(String[] args) { int n = 90; if (n > 90) { System.out.println("优秀"); } else if (n >= 60) { System.out.println("及格了"); } else { System.out.println("挂科了"); } } }假设我们期望90分或更高为“优秀”,上述代码输出的却是“及格”,原因是>和>=效果是不同的。
前面讲过了浮点数在计算机中常常无法精确表示,并且计算可能出现误差,因此,判断浮点数相等用==判断不靠谱:
// 条件判断 public class Main { public static void main(String[] args) { double x = 1 - 9.0 / 10; if (x == 0.1) { System.out.println("x is 0.1"); } else { System.out.println("x is NOT 0.1"); } } }正确的方法是利用差值小于某个临界值来判断:
// 条件判断 public class Main { public static void main(String[] args) { double x = 1 - 9.0 / 10; if (Math.abs(x - 0.1) < 0.00001) { System.out.println("x is 0.1"); } else { System.out.println("x is NOT 0.1"); } } }判断引用类型相等(JAVA 特有)
在Java中,判断值类型的变量是否相等,可以使用==运算符。但是,判断引用类型的变量是否相等,==表示“引用是否相等”,或者说,是否指向同一个对象。例如,下面的两个String类型,它们的内容是相同的,但是,分别指向不同的对象,用==判断,结果为false:
// 条件判断 public class Main { public static void main(String[] args) { String s1 = "hello"; String s2 = "HELLO".toLowerCase(); System.out.println(s1); System.out.println(s2); if (s1 == s2) { System.out.println("s1 == s2"); } else { System.out.println("s1 != s2"); } } }要判断引用类型的变量内容是否相等,必须使用equals()方法:(有点类似于c中的strcmp)
// 条件判断 public class Main { public static void main(String[] args) { String s1 = "hello"; String s2 = "HELLO".toLowerCase();(这里的toLowerCase是一个方法,变小写=tolower System.out.println(s1); System.out.println(s2); if (s1.equals(s2)) { System.out.println("s1 equals s2"); } else { System.out.println("s1 not equals s2"); } } }注意:执行语句s1.equals(s2)时,如果变量s1为null,会报NullPointerException:
// 条件判断 public class Main { public static void main(String[] args) { String s1 = null; if (s1.equals("hello")) { System.out.println("hello"); } } }要避免NullPointerException错误,可以利用短路运算符&&:
// 条件判断 public class Main { public static void main(String[] args) { String s1 = null; if (s1 != null && s1.equals("hello")) { System.out.println("hello"); } } }还可以把一定不是null的对象"hello"放到前面:例如:if ("hello".equals(s)) { ... }。
switch多重选择
基本功能一致,我这里只讲不一样的
同时注意,上述“翻译”只有在switch语句中对每个case正确编写了break语句才能对应得上。
使用switch时,注意case语句并没有花括号{},而且,case语句具有“穿透性”,漏写break将导致意想不到的结果:(这个也是一个重要的考点,没加break会一直执行的,不管变量是否匹配case)
// switch public class Main { public static void main(String[] args) { int option = 2; switch (option) { case 1: System.out.println("Selected 1"); case 2: System.out.println("Selected 2"); case 3: System.out.println("Selected 3"); default: System.out.println("Selected other"); } } }当option = 2时,将依次输出"Selected 2"、"Selected 3"、"Selected other",原因是从匹配到case 2开始,后续语句将全部执行,直到遇到break语句。因此,任何时候都不要忘记写break。
switch语句还可以匹配字符串。字符串匹配时,是比较“内容相等”。例如:
// switch public class Main { public static void main(String[] args) { String fruit = "apple"; switch (fruit) { case "apple": System.out.println("Selected apple"); break; case "pear": System.out.println("Selected pear"); break; case "mango": System.out.println("Selected mango"); break; default: System.out.println("No fruit selected"); break; } } }switch语句还可以使用枚举类型,枚举类型我们在后面讲解。
switch表达式
使用switch时,如果遗漏了break,就会造成严重的逻辑错误,而且不易在源代码中发现错误。从Java 12开始,switch语句升级为更简洁的表达式语法,使用类似模式匹配(Pattern Matching)的方法,保证只有一种路径会被执行,并且不需要break语句:(这个就很方便了)
// switch public class Main { public static void main(String[] args) { String fruit = "apple"; switch (fruit) { case "apple" -> System.out.println("Selected apple"); case "pear" -> System.out.println("Selected pear"); case "mango" -> { System.out.println("Selected mango"); System.out.println("Good choice!"); } default -> System.out.println("No fruit selected"); } } }注意新语法使用->,如果有多条语句,需要用{}括起来。不要写break语句,因为新语法只会执行匹配的语句,没有穿透效应。
很多时候,我们还可能用switch语句给某个变量赋值。例如:
int opt; switch (fruit) { case "apple": opt = 1; break; case "pear": case "mango": opt = 2; break; default: opt = 0; break; }使用新的switch语法,不但不需要break,还可以直接返回值。把上面的代码改写如下:
// switch public class Main { public static void main(String[] args) { String fruit = "apple"; int opt = switch (fruit) { case "apple" -> 1; case "pear", "mango" -> 2; default -> 0; }; // 注意赋值语句要以;结束 System.out.println("opt = " + opt); } }这样可以获得更简洁的代码。
yield
大多数时候,在switch表达式内部,我们会返回简单的值。
但是,如果需要复杂的语句,我们也可以写很多语句,放到{...}里,然后,用yield返回一个值作为switch语句的返回值:
// yield public class Main { public static void main(String[] args) { String fruit = "orange"; int opt = switch (fruit) { case "apple" -> 1; case "pear", "mango" -> 2; default -> { int code = fruit.hashCode(); yield code; // switch语句返回值 } }; System.out.println("opt = " + opt); } }while和do-while循环(和c语言一致不用单独在讲)
ava提供的while条件循环。它的基本用法是:
while (条件表达式) { 循环语句 } // 继续执行后续代码while循环在每次循环开始前,首先判断条件是否成立。如果计算结果为true,就把循环体内的语句执行一遍,如果计算结果为false,那就直接跳到while循环的末尾,继续往下执行。
for循环
基本形式与c语言一致,不过多赘述
for each循环(有点像替代指针的功能)
for循环经常用来遍历数组,因为通过计数器可以根据索引来访问数组的每个元素:
int[] ns = { 1, 4, 9, 16, 25 }; for (int i=0; i<ns.length; i++) { System.out.println(ns[i]); }但是,很多时候,我们实际上真正想要访问的是数组每个元素的值。Java还提供了另一种for each循环,它可以更简单地遍历数组:
// for each public class Main { public static void main(String[] args) { int[] ns = { 1, 4, 9, 16, 25 }; for (int n : ns) { System.out.println(n); } } }和for循环相比,for each循环的变量n不再是计数器,而是直接对应到数组的每个元素。for each循环的写法也更简洁。但是,for each循环无法指定遍历顺序,也无法获取数组的索引。
除了数组外,for each循环能够遍历所有“可迭代”的数据类型,包括后面会介绍的List、Map等。
break和continue(和c语言基本一致,但是还是想讲一下)
break
在循环过程中,可以使用break语句跳出当前循环。我们来看一个例子:
// break public class Main { public static void main(String[] args) { int sum = 0; for (int i=1; ; i++) { sum = sum + i; if (i == 100) { break; } } System.out.println(sum); } }使用for循环计算从1到100时,我们并没有在for()中设置循环退出的检测条件。但是,在循环内部,我们用if判断,如果i==100,就通过break退出循环。
因此,break语句通常都是配合if语句使用。要特别注意,break语句总是跳出自己所在的那一层循环。例如:
// break public class Main { public static void main(String[] args) { for (int i=1; i<=10; i++) { System.out.println("i = " + i); for (int j=1; j<=10; j++) { System.out.println("j = " + j); if (j >= i) { break; } } // break跳到这里 System.out.println("breaked"); } } }上面的代码是两个for循环嵌套。因为break语句位于内层的for循环,因此,它会跳出内层for循环,但不会跳出外层for循环。
continue
break会跳出当前循环,也就是整个循环都不会执行了。而continue则是提前结束本次循环,直接继续执行下次循环。我们看一个例子:
// continue public class Main { public static void main(String[] args) { int sum = 0; for (int i=1; i<=10; i++) { System.out.println("begin i = " + i); if (i % 2 == 0) { continue; // continue语句会结束本次循环 } sum = sum + i; System.out.println("end i = " + i); } System.out.println(sum); // 25 } }注意观察continue语句的效果。当i为奇数时,完整地执行了整个循环,因此,会打印begin i=1和end i=1。在i为偶数时,continue语句会提前结束本次循环,因此,会打印begin i=2但不会打印end i=2。
在多层嵌套的循环中,continue语句同样是结束本次自己所在的循环。
小结
break语句可以跳出当前循环;
break语句通常配合if,在满足条件时提前结束整个循环;
break语句总是跳出最近的一层循环;
continue语句可以提前结束本次循环;
continue语句通常配合if,在满足条件时提前结束本次循环。
三最后一语
今天就先讲这么多,昨天应该是讲太多了,提前祝大家周末快乐
世界会变,
但是我始终如一,
我带着悲哀的自负想道。
博尔林斯/《阿菜夫》
感谢观看,共勉!!