1 流程控制
1.1 分支结构
1.1.1 if-else
- 语法格式:
if(boolean判断){
执行语句l;
}else if(boolean判断){
执行语句2;
}else if(boolean判断){
执行语句3;
}else{
执行语句4;
}
-
执行顺序
由if进入,逐个boolean进行判断,如果遇到false就向下执行,直到遇到true为止(排他性:一事物不容许其他事物与自己在同一范围内共存的性质,这个变量只对应一个条件)。所以在设置分支条件时,避免出现条件设置过于宽泛使得需要验证的变量有多个条件符合的现象。 -
其他需要注意的:
- 当只有if时,如果if所辖括号下面的语句全是else所辖的语句,else{}可以省略;当存在if-else if时必须有else。但是有没有else只是在字面上有不同,分支是存在的。
- 当if-else里面只有一个语句的时候,“{}”可以不写。
- 学会利用if-else的排他特性简化代码(如下面的代码),后面的条件里不能再写前面条件不成立的等价判断(如x>30,后面出现x>40,小于30的值也一定小于40),以减少冗余:
if(month <= 0){
...
}else if(month <= 3){
...
}else if(month <= 6){//month >= 4 && month <= 6
...
}else if(month <= 9){
...
}else if(month <= 12){
...
}else{//month > 12
...
}
- 当if里面有return语句的时候,else单词可以被省略。
- 如果遇到类似注释中这样只返回true/false的方法,if后面的boolean可以直接返回。
/*
if(x % 2 == 0){
return true;
}else{
return false;
}
*/
return x % 2 == 0;
- 永远不要拿着一个boolean类型的变量和true做连等比较,比完之后最终的结果都和这个变量的值一模一样。反之如果与false连等比较,结果相反。
boolean isOk;
//if(isOk == true)
//如果isOk = true -> (true == true) -> true
//如果isOk = false -> (false == true) -> false
//不如直接 ↓↓↓↓↓↓
if(isOk){
// 如果isOk为true
}else{
...
}
=======================================================
//if(isOk == false)
//如果isOk = true -> (true == false) -> false
//如果isOk = false -> (false == false) -> true
//不如直接 ↓↓↓↓↓↓
if(!isOk){
// 如果isOk为false
}else{
...
}
1.1.2 switch-case
- 语法格式:
switch(参数){
case XXX : 执行语句;[break;]
case YYY : 执行语句;[break;]
case ZZZ : 执行语句;[break;[
default : 执行语句;
}
-
执行顺序:将参数与每个case进行比对,如果符合条件且语句没有return/break/continue这样的终止语句,将向下执行每一个case语句(不管是否符合),如果一直没有终止语句,将一直执行到default。 所以如果只需要在一个case执行完后出现结果,就一定要加相应的终止语句。
-
终止语句不一定是break,如果是一个有返回值的方法,终止语句可能是return,且此时若有default,其中也要出现return,如果没有default,switch大括号外面需要有一个return;如果是一个循环,终止语句也可能是continue。
-
如果利用得好,上面这个也可以简化代码,提高效率。break共享代码,将执行相同操作的代码放在一起进行共享:
//定义一个方法 显示季节
//1-3:春 4-6:夏 7-9:秋 10-12:冬
public static void showSeason(int month){
switch(month){
case 1 :
case 2 :
case 3 :
System.out.println("春");
break;
case 4 :
case 5 :
case 6 :
System.out.println("夏");
break;
case 7 :
case 8 :
case 9 :
System.out.println("秋");
break;
case 10 :
case 11 :
case 12 :
System.out.println("冬");
break;
default :
System.out.println("月份错误");
}
}
- 面试题:★
switch-case的参数可以传哪些数据类型?(重点答版本)
版本 | 数据类型 |
---|---|
jdk1.0及以上 | char、byte、short、int |
jdk5.0及以上 | enum(枚举) |
jdk7.0及以上 | String(字符串) |
1.2 循环结构
循环体中定义的变量为局部变量,其他流程控制语句中定义的变量也是,结构体完毕后即消亡。
1.2.1 for
如果要执行的循环与次数相关,使用for循环。
- 语法格式:
for(1;2;3){
4;
}
- 初始化循环变量
- 循环执行的条件(存放boolean类型的数据,可以使用"&&"、"||"连接判断)
- 循环之后的变化
- 循环执行的代码
执行顺序:1(243243243…243)2,1、2、3为空表示死循环。
- 例:
- 打印所有小写字母
for(char x = 'a';x <= 'z';x++){
System.out.println(x);
}
for(char x = 97;x <= 122;x++){
System.out.println(x);
}
for(int x = 97;x <= 122;x++){
System.out.println((char)x);
}
- 找出100以内8的倍数(效率开发)
for(int x = 1;x <= 100;x++){
if(x % 8 == 0){
System.out.println(x);
}
}
for(int x = 8; x <= 100; x+=8){//尽量减少循环次数,提高效率
i++;
}
- for的括号后面不能加“;”,否则会导致空循环。
for(int x = 1;x <= 5;x++);{
System.out.println("test");
}
↓↓↓↓↓↓↓↓相当于
for(int x = 1;x <= 5;x++);
{
System.out.println("test");
}
- 当在for/while等条件中使用"&&"、"||"时,需要注意要判断清楚是否需要循环的所有元素都满足这两个条件。
//"x&7==0"并不对所有要循环值为true,应该放在for内层的if中
for(int x = 2; x <= 100 && ((x & 7) == 0); x++){
System.out.println(x);
}
1.2.2 for-each
用于数组、集合中。JDK5.0以上才可以使用。
- 语法格式:
for(1 : 2){
3;
}
- 与数组/集合中的元素数据类型相同的变量,用于取出元素
- 需遍历的数组/集合
- 循环执行的代码
1.2.3 while(当型循环)/ do-while(直到型循环)
如果要执行的条件相关,使用while/do while循环,do-while一般较少使用。
- 语法格式:
//while循环
1;
while(2){
3;
4;
}
//do while循环
1;
do{
4;
3;
}while(2);
执行顺序:
-
while:1(234234234234…)2
-
do while:1(432432432432…)2
- 初始化循环变量(一定要在循环体外先定义好,不能在括号里定义,变量在循环结束后消亡)
- 循环执行的条件(空表示死循环,存放boolean类型的数据,可以使用"&&"、"||"连接判断)
- 循环之后的变化
- 循环执行的代码(3、4没有顺序)
-
例:
- 同for的例题1
//a-z
char y = 'a';
while(y <= 'z'){
System.out.println(y);
y++;
}
-
while和do while之间的区别?
- while先判断,符合条件再执行
- do while先执行,再判断,能够保证程序至少执行一次。
1.2.4 循环嵌套、控制和标签
1.2.4.1 嵌套
一个循环定义在另一个循环里面。
- 语法格式:
// 1 2 3
for(int x = 1;x <= 10;x++){
//4
// a b c
for(int y = 1;y <= 10;y++){
//d
}
}
执行顺序:124a(bdcbdcbdc…b)324a(bdcbdc…b)324a(bdcbdc…b)32
- 例:
/**打印出
******
******
******
*/
for(int x = 1;x <= 3;x++){//x:行数
for(int y = 1;y <= 6;y++){//y:列数
System.out.print("*");
}//打印一行
System.out.println();//换行
}
/**打印
***** *****
***** *****
***** *****
第1行: 2个空 5个* 1个空 5个*
第2行: 1个空 5个* 3个空 5个*
第3行: 0个空 5个* 5个空 5个*
...
第x行: 3-x空 5个* 2*x-1个空 5个*
*/
for(int x = 1;x <= 3;x++){//行数,内层嵌套按数列看包含哪几部分打印,按行打印
for(int y = 1;y <= 3-x;y++){
System.out.print(" ");//第一部分 打印(3-x)个空格
}
//for(int y = 1;y <= 5;y++){
// System.out.print("*");
//}
System.out.print("*****");//第二部分,已知每行五个*,固定打印,减少不必要循环提高效率
for(int y= 1;y <= 2*x-1;y++){
System.out.print(" ");//第三部分 再打印(2*x-1)个空格
}
//for(int y = 1;y <= 5;y++){
// System.out.print("*");
//}
System.out.print("*****");//第四部分,已知每行五个*,固定打印,减少不必要循环提高效率
System.out.println();//换行
}
1.2.4.2 控制
-
continue:表示跳过本次循环,开始下一次,跳到所在循环的第三部分。
-
break:表示跳出所在的循环,跳到所在循环的结束部分。
-
例:
for(int x = 1;x <= 6;x++){
if(x == 4){
continue;//表示跳过本次循环,开始下一次,跳到所在循环的第三部分
}
System.out.println(x);//-->12356
}
for(int x = 1;x <= 6;x++){
if(x == 4){
break;//跳出所在的循环 跳到所在循环的结束部分
}
System.out.println(x);//-->123
}
注意,continue在dowhile中跳出后,需要判断的条件语句在最末尾,不要忽略。
/**打印
*******
******
*******
******
*******
continue break
*/
for(int x = 1;x <= 5;x++){//行
for(int y = 1;y <= 7;y++){//列
if(x % 2 == 0 && y == 7){//每到二的倍数行,第七个*总是不打印,跳过本次循环,跳到y++开始下一次
continue;
}
System.out.print("*");
}
System.out.println();
}
1.2.4.3 标签
当我们代码写在内层循环想直接操作外层循环时,需要给外层循环贴标签(“标签名:”),然后在内层循环里面“continue/break + 标签名”。
- 例:
//找出100以内所有质数(找有没有除了1和本身之外的约数)
//标准
a:for(int x = 2; x <= 100; x++){//被除数
for(int y = 2; y < x; y++){//除数
if(x % y == 0){
//break a 完全停止a所标识的循环
//continue a 停止a所标识的循环并开始新的循环
//如果这里使用break,只是停止本层循环,本层循环括号下面(如这里的System.out)的语句还是会执行
continue a;
}
}
System.out.println(x);
}
//精准,效率高↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
System.out.println(2);//2时最小的质数,直接打印减少循环一次
a:for(int x = 3; x <= 100; x+=2){//被除数,质数除了2之外的偶数都不是质数
for(int y = 3; y <= Math.sqrt(x); y+=2){//除数,例:81只需要从3找到9便可以找完所有的约数,且只需要找奇数,因为奇数除以偶数都无法除尽
if(x % y == 0){
continue a;
}
}
System.out.println(x);
}
2 数组
用来存放一组类型相同,存储空间连续的数据的容器。不管是局部还是成员变量,数组创建时即赋给初值。
- 对于数组的一些操作:
//如何创建一个数组对象:
int[] data = new int[4]; //5表示底层开辟空间大小
int[] data = new int[]{12,34,56,78};
int[] data = {12,34,56,78};
//输出这个数组的值(得到数组中某个元素)
System.out.println(data[0]);
//输出数组长度
System.out.println(data.length);
//如何遍历数组对象,如果需要在循环时取出指定下标的元素,必须使用for循环
//for + 数组对象下标
for(int x = 0;x < data.length;x++){
//x -> 下标
System.out.println(data[x]);
}
//foreach,使用对应的数据类型变量取出元素
for(int x : data){
//x -> 元素
System.out.println(x);
}
- 数组的内存存储
作为引用数据类型的其中一种,先理解数组存储有助于理解引用数据类型的存储。Java中的数组是静态的,即当数组被初始化之后,该数组所占的内存空间、数组长度都是不可变的。数组必须经过初始化才可使用。所谓初始化,即创建实际的数组对象,也就是在内存中为数组对象分配内存空间,并为每个数组元素指定初始值。(当使用引用数据类型的数组时,创建一个引用类型的数组,里面每个元素都是一个对象,传进来的对象元素必须先声明引用,否则是null。)
//1 仅声明数组的引用,但没有分配内存空间,动态初始化
int[] data;
//1 2 声明数组的同时,根据指定长度分配内存,但此时数组中没有值,动态初始化
int[] data = new int[4];
//1 2 3 在面的基础上声明数组并分配内存,同时将其初始化,静态初始化
int[] data = new int[]{12, 34, 56, 78};
//同上
int[] data = {12, 34, 56, 78};
//4
int[] data2 = {12, 34};
上面的代码实现的内存如图:
int[] data2 = data;
//输出此时data2的引用地址
System.out.println(data2);//-->[I@71b456f
data2[4] = 20;
System.out.println(data[4]);//-->20
此时的内存如下图,原来的{12,34}数组会因为不存在指向此对象的引用而被GC线程垃圾回收。
- 数组的复制
//1.错误的复制方式,只是内存里面的数组对象多了一个名字而已。
//将data数组里面的元素赋值到另一个数组里面
int[] data = new int[]{45,67,82,19};
int[] temp = data;//复制失败
data[0] = 55;
System.out.println(temp[0]);//-->55
//原因参考前面的内存机制,复制完之后两个数组不具有独立性
//2.笨重的复制方式,需要逐个手动复制。
int[] data = new int[]{12, 34, 56, 78};
int[] temp = new int[4];
temp[0] = data[0];
temp[1] = data[1];
temp[2] = data[2];
temp[3] = data[3];
data[0] = 55;
System.out.println(temp[0]);//12
//3. 调用"克隆"方法,方法过于死板,不适于正常的应用开发。
int[] data = new int[]{12,34,56,78};
int[] temp = data.clone();
data[0] = 55;
System.out.println(temp[0]);
- System.arraycopy(1,2,3,4,5);
* 参数意义:
1 要复制的原数组对象
2 原数组的起始下标位置
3 要复制到的目标数组
4 目标数组的起始下标位置
5 原数组对象需被复制的步长
* System.arraycopy()的1、3参数可以是同一个数组对象,从而实现的是一个数组的前后移动。
int[] data = {12, 34, 56, 78};
System.arraycopy(data, 0, data, 2, 2);
for(int i = 0;i < data.length; i++){
System.out.println(data[i]);//原数组变为12,34,12,34
}
-
数组的排序
- 手动排序(冒泡排序)★
如对下面的数组进行从小到大的排序
int[] data = {45,22,73,16,50};
举第一轮遍历的例子
45 22 73 16 50
[0] [1] [2] [3] [4]
[0] [1] 前大后小 需要交换
22 45 73 16 50
[0] [1] [2] [3] [4]
[1] [2] 前小后大 不需要交换
22 45 73 16 50
[0] [1] [2] [3] [4]
[2] [3] 前大后小 需要交换
22 45 16 73 50
[0] [1] [2] [3] [4]
[3] [4] 前大后小 需要交换
总结为: [y]与[y + 1]相比较
第一轮遍历结束,最大的数字已经到达最右边,需要再进行三轮遍历完成从小到大的排序,得
22 45 16 50 73
[0] [1] [2] [3] [4]
总结得
//data为需要排序的数组
for(int x = 0;x < data.length -1;x++){//需要从前到后搜(data.length -1)次
//更加提高效率的方法:在内层循环中将循环条件改为(data.length - 1 - x),因为每次循环后最值总在最右端
//y = 0 1 2 3
//y + 1 = 1 2 3 4
for(int y = 0;y < data.length - 1;y++){//每次需要每两个进行(data.length - 1)次比较
//前元素的下标:y 前元素:data[y]
//后元素的下标:y+1 后元素:data[y + 1]
if(data[y] > data[y + 1]){//从小到大,前元素比后面大就执行替换
data[y] = data[y] ^ data[y + 1];
data[y + 1] = data[y] ^ data[y + 1];
data[y] = data[y] ^ data[y + 1];
}
}
}
- 自动排序
调用方式:Arrays.sort(数组对象); 并导入"java.util.*"。但这个排序方式有局限性,只能从小到大进行排序。
3 应用
int i = 1,j = 10;
do{
if(i > j){
break;
}
j--;
}while(++i < 5);
System.out.println("i=" + i + "and j" + j);--->>输出什么内容
//分析:
int i = 1,j = 10;
do{
//L1 i = 1, j = 10
//L2 i = 2, j = 9
...
//L4 i = 4, j = 7
if(i > j){
break;
}
//如果是第一次循环到这里打印j--,得数时10
j--;//取值 运算
//L1 i = 2
//...
//L4 i = 5 跳出
}while(++i < 5);
System.out.println("i=" + i + "and j" + j); //i = 5, j = 6
boolean flag;
int i = 0;
do{
flag = false;
System.out.println(i++);//--->>输出什么内容
flag = i < 10;
continue;
}while(flag? true : false);
//分析:
boolean flag;
int i = 0;
do{
flag = false;
//L1 0
//L2 1
//...
//L10 9
System.out.println(i++); //--->输出0123456789
flag = i < 10;//-->等价于flag = (i < 10);
//L1 i = 1, flag = true
//L2 i = 2, flag = true
//...
//L10 i = 10, flag = false
continue;
}while(flag? true : false); //--->简化为flag
//若是flag? false : true 简化为!flag
for(int i = 0;i < 3;i++){
switch(i){
case0:break;
case1:System.out.println("one");
case2:System.out.println("two");
case3:System.out.println("three");
}
}
System.out.println("done");
//分析
for(int i = 0;i < 3;i++){
switch(i){
//L0 break;
//L1 ---> one two three
//L2 ---> two three
case0:break;
case1:System.out.println("one");
case2:System.out.println("two");
case3:System.out.println("three");
}
}
System.out.println("done"); //---> one two three two three done
- 2008年大陆送给台湾一对熊猫,叫团团圆圆。
当团团圆圆来到台湾的第一年时,休养生息;
第二年时,继续休养生息;
第三年时,生产出来一对小熊猫。
大熊猫只要开始生产,以后的每一年都会生产出来一对熊猫。
小熊猫是第一年休养生息,第二年休养生息,第三年开始产仔。
问第50年一共出现了多少对熊猫?
- 分析:第一年:1对,第二年:1对,第三年:2对,第四年:3对,第五年第三年的熊猫开始生产:5对,第六年第四年的熊猫开始生产:8对…得规律(n-1)+ n = n + 1。
// 将50年设置为一个数组
long[] panda = new long[50];//1 1 2 3 0 0 0 0
panda[0] = 1;
panda[1] = 1;
for(int x = 2;x < panda.length;x++){
//x -> 下标
/*分析:
panda[2] = 2 = panda[0] + panda[1]
panda[3] = 3 = panda[1] + panda[2]
panda[4] = 5 = panda[2] + panda[3]
panda[5] = 8 = panda[3] + panda[4]
.....
panda[18] = XXX = panda[16] + panda[17]
panda[x] = XXX = panda[x - 2] + panda[x - 1]
*/
panda[x] = panda[x - 2] + panda[x - 1];
}
System.out.println(panda[49]);
- 拉灯问题:
有100个房间:每个房间有1盏灯,这些灯都是灭的;另有100个人:每一个人来到每个房间前,如果房间编号能够整除这个人的编号(如1号来到1号房间前),则拉一下灯,每个人都要走一遍这100个房间。
问:当100个人都拉完灯之后 哪些灯是亮的?
//100个房间抽象为数组,默认值为false假定为灭的
boolean[] lightState = new boolean[100];
for(int i = 1; i <= 100; i++){//人
//内层循环的效率更高,题目的意思是房间号j整除人编号i,所以可以直接从人的这个编号开始找它的倍数,且加的是人编号本身,直接找到这个倍数将其对应的数组取反。
for(int j = i; j <= 100; j += i){//房间编号
lightState[j-1] = !lightState[j-1];
}
}
for(int i = 0; i < lightState.length ; i++){
if(lightState[i]) System.out.println("第" + (i+1) + "个房间亮");
}
System.out.println(lightState);
- 数组赋值
//利用数组复制实现,将数组{45,72,18,99,66,82}里面的18删除
//方法1
int[] data = {45,72,18,99,66,82};
//System.arraycopy(老数组,下标,新数组,下标,长度);
int[] temp = new int[5];//-->temp:0 0 0 0 0
System.arraycopy(data,0,temp,0,2);//-->temp:45 72 0 0 0
System.arraycopy(data,3,temp,2,3);//-->temp:45 72 99 66 82
for(int x : temp){
System.out.println(x);
}
//方法2
//45,72,18,99,66,82
System.arraycopy(data,3,data,2,3); //-->data:45 72 99 66 82 82
int[] temp = new int[5];//-->temp:0 0 0 0 0
System.arraycopy(data,0,temp,0,5);//-->temp:45 72 99 66 82
for(int x : temp){
System.out.println(x);
}
- 将“455,666,900,321,728”的每个元素的个位数、十位数、百位数相加后,按和的大小,从小到大排列。
int[] data = {455,666,900,321,728};
for(int i = 0; i < data.length - 1; i++){
for(int j = 0; j < data.length - 1 - i; j++){
if((getTotal(data[j])) > (getTotal(data[j + 1]))){
data[j] = data[j] ^ data[j + 1];
data[j + 1] = data[j] ^ data[j + 1];
data[j] = data[j] ^ data[j + 1];
}
}
}
for(int k : data){
System.out.println(k);
}
}
int getTotal(int x){
//get个位数
int single = x % 10;
//get十位数
int ten = x / 10 % 10;
//get百位数
int hundred = x / 100;
return single + ten + hundred;
}
- 将一组数“60, 115, 65, 50, 150”小于70的元素先乘以2后,按改变后的数组的大小将原数组按照从小到大的顺序排序。
int[] data = {60, 115, 65, 50, 150};
for(int i = 0; i < data.length - 1; i++){
for(int j = 0; j < data.length - 1 - i; j++){
if((data[j] < 70 ? data[j] * 2 : data[j]) > (data[j + 1] < 70 ? data[j + 1] * 2 : data[j + 1])){
data[j] = data[j] ^ data[j + 1];
data[j + 1] = data[j] ^ data[j + 1];
data[j] = data[j] ^ data[j + 1];
}
}
}
for(int k : data){
System.out.println(k);
}
- 将“C,a,b,D,B,c,d,A”按照“A,a,B,b,C,c,D,d”的顺序输出
//A a B b C c D d
//65 97 66 98 67 99 68 100
// 65.5 66.5 67.5 68.5
//规律:要将小写与大写混合,先将小写(或大写)的ascii码转换为两个大写(小写)字母中间的某个值,不一定是0.5,可以是0.1-0.9任何小数。
//ascii > 90时 ascii - 31.5
//ascii < 90时 ascii
char[] data = {'C','a','b','D','B','c','d','A'};
for(int i = 0; i < data.length - 1; i++){
for(int j = 0; j < data.length - 1 - i; j++){
double a = (double) scoreArray[j];
double b = (double) scoreArray[j + 1];
if((a > 90 ? a - 31.5 : a) > (b > 90 ? b - 31.5 : b)){
data[j] = (char)(data[j] ^ data[j + 1]);
data[j + 1] = (char)(data[j] ^ data[j + 1]);
data[j] = (char)(data[j] ^ data[j + 1]);
}
}
}
for(char k : data){
System.out.println(k);
}