——- android培训、java培训、期待与您交流! ———-
一、程序流程控制
程序流程控制(又称语言)有:判断结构、选择结构、循环结构
1、判断结构——if
if(条件表达式){执行语句}
if(条件表达式){执行语句} else{执行语句}
if(条件表达式){执行语句} else if(条件表达式){执行语句}......else{执行语句}
注:a).如果if语句中只有一条语句,那么可以不写大括号。
b).如果有多条语句且if语句没写大括号,if就只能控制离它最近的单条语句。
c).三元运算符就是if else语句的简写格式。当if else运算后,有一个具体的结果时,可以简化写成三元运算符。
d).如果if( 表达式 )后面加上分号,表示无论表达式为true或者false,都不执行任何语句。
示例:判断结构的使用
class IfElseDemo { public static void main(String[] args) { if(false); { System.out.println("Hello World!"); } } }
运行结果为:
注:由于if( false );语句后面加了分号,因此不执行任何操作。{ System.out.println("Hello World"); }为局部代码块。
局部代码块:局部代码块可以定义局部变量的生命周期。class IfElseDemo { public static void main(String[] args) { { int m=5; System.out.println(m); } System.out.println("Hello World!"+m); } }
运行结果为:
注:变量m是局部变量,生命周期仅在局部代码块括号中,当代码执行到局部代码块右括号外,变量m便消失了。局部代码块外的代码当然也就访问不到变量m了。2、选择结构——switch
格式:
switch(表达式)
{
case 取值1:
执行语句;
break;
case 取值2:
执行语句;
break;
.................
default:
执行语句;
break;
}
注:a).表达式的值只有4种:byte,short,int,char
b). 结束switch有2种情况:1)遇到break;2)switch结尾
c).case与default没有顺序。先执行第一个case,没有匹配的case执行default。
d).如果匹配的case或者default没有对应的break,那么程序会继续向下执行,运行可以执行的语句,直到遇到break或者switch结尾结束。
e).进入switch语句后,执行顺序是先执行case,然后从上到下,最后再执行default。即使default放在case上面,执行顺序也不变。
f).建议:当值少时使用switch;当值多时和有double时,可用if
示例:选择结构的使用
1)部分case和default中没有break语句的情况:
class SwitchDemo { public static void main(String[] args) { int m=3; switch(m) { case 1: System.out.println("1"); break; case 2: System.out.println("2"); case 3: System.out.println("3"); case 4: System.out.println("4"); default: System.out.println("default"); break; } } }
运行结果为:
2)多个case同一种处理方式的情况:
class SwitchDemo { public static void main(String[] args) { int m=2; switch(m) { case 3: case 4: case 5: System.out.println("春"); break; case 6: case 7: case 8: System.out.println("夏"); break; case 9: case 10: case 11: System.out.println("秋"); break; case 12: case 1: case 2: System.out.println("冬"); break; default: System.out.println("default"); break; } } }
运行结果为:
注:if和switch语句的应用:
if:1.对具体的值进行判断。
2.对区间判断。
3.对运算结果是boolean类型的表达式进行判断。switch:1.对具体的值进行判断。
2. 值的个数通常是固定的。
建议:对于几个固定的值判断,使用switch语句;因为switch语句会将具体的答案都加载进内存,效率相对高。3、循环结构——while,do.....while,for
1) while(条件表达式)
{
执行语句;
}
2) do
{
执行语句;
}while(条件表达式);
注:while:先判断条件,只有条件满足后才执行循环体。注意不要写成while(x<5);因为后面的分号就是循环体,代表不执行任何语句,这个循环就成了一个死循环。
do....while:先执行循环体,再判断条件,条件满足,再执行循环体;也就是说,do....while无论条件是否满足,循环体至少要执行一次。
3) for(初始化表达式;循环条件表达式;循环后的操作表达式)
{
执行语句;
}
注:1).for循环的初始化表达式、循环后的操作表达式可以是多个表达式,通过逗号分隔。
如:for(int a = 1,b =2; a < 2 & b < 3; a++,b++){}
2).变量有自己的作用域,对于for来讲,如果将用于控制循环的增量定义在for语句中,那么该变量只能在for语句中有效,for语句执行完毕后,该变量在内存中将被释放,当循环体结束后,想变量不被释放,可用while。
3).for和while可以进行互换,如果需定义循环增量,用for更为适合。
4).当要对语句执行很多次时,可用循环结构。
class ForDemo { public static void main(String[] args) { int x=1; for(System.out.println("A");x<3;System.out.println("C")) { System.out.print("D"); x++; } } }
注:最简单无限循环格式:while(true){}, for(;;){},对于for来讲,不写条件表达式默认就是ture。无限循环存在的原因是并不知道循环多少次,而是根据某些条件,来控制循环。
什么时候使用循环结构?
当要对某些语句执行很多次时,就使用循环结构。
循环注意:一定要明确哪些语句需要参与循环,哪些不需要。
示例:
1)1~10之间的所有数的和。
/* 需求:1~10之间的所有数的和。 思路:用到了累加的思想。通过sum变量记录住每次变化的结果, 通过循环的形式,进行累加的动作。 */ class ForDemo { public static void main(String[] args) { int sum=0; for(int i=1;i<=10;i++) { sum+=i; } System.out.println(sum); } }
2)1~100之间7的倍数。
/* 需求:1~100之间7的倍数。 思路:用到了计数器的思想。通过一个变量记录住数据的状态变化, 通过循环的形式完成操作。 */ class ForDemo { public static void main(String[] args) { int num=0; for(int i=1;i<=100;i++) { if(i%7==0) num++; } System.out.println(num); } }
3)语句的嵌套——循环嵌套
/*循环嵌套*/ class ForDemo { public static void main(String[] args) { for(int i=1;i<=3;i++) { for(int j=1;j<=10;j++) { System.out.print("*"); } System.out.println(); } } }
运行结果为:
结论:对于打印长方形,外循环控制行数,内循环控制的是每一行的列数,也就是一行中元素的个数。
4)语句的嵌套——九九乘法表
class ForDemo { public static void main(String[] args) { for(int i=1;i<=9;i++) { for(int j=1;j<=i;j++) { System.out.print(j+"*"+i+"="+i*j+"\t"); } System.out.println(); } } }
运行结果为:
注:1、代码中的"\t"是一个转义字符,也就是制表符。还有其他的一些转义字符:\n:回车,\b:退格,\r:回车符。
2、windows系统中回车符其实是由两个转义字符组成的:\r\n,linux中回车符是\n。
二、其他流程控制语句
1、break(跳出):应用范围:选择结构,循环结构。
2、continue(继续):只能应用于循环结构,特点:结束本次循环继续下次循环。
注:1).break和continue语句都有作用范围。
2).break和continue单独存在时,如果在此之后还有任何语句都执行不到。
示例:
1)break的使用:
class ForDemo { public static void main(String[] args) { w:for(int i=1;i<=10;i++) { q:for(int j=1;j<=10;j++) { System.out.print(i+":::"+j); break q; } System.out.println(); } } }
注:其中的w和q都是标号。标号:只能用于循环结构,用于给循环体标注名字,标号的出现,可以让这两个语句作用于指定的范围。运行结果是:
2)continue的使用:
class ForDemo { public static void main(String[] args) { w:for(int i=1;i<=10;i++) { q:for(int j=1;j<=10;j++) { System.out.print(i+":::"+j+" "); continue w; } System.out.println(); } } }
运行结果为:
三、函数
定义:也成方法,就是定义在类中具有特定功能的一段独立小程序。
格式:修饰符 返回值类型 函数名(参数类型 形式参数1,参数类型 形式参数2,..........参数类型 形式参数n)
{
执行语句;
return 返回值;
}
其中:
返回值类型:函数运行后的结果的数据类型。
参数类型:是形式参数的数据类型。
形式参数:是一个变量,用于存储调用函数时传递给函数的实际参数。
实际参数:传递给形式参数的具体数值。
return:用于结束函数。
返回值:该值会返回给调用者。
特点:
1).定义函数可以将功能代码进行封装,便于对该功能进行复用。
2).函数的出现提高了代码的复用性。
3).函数只有被调用才会被执行。
4).对于函数没有具体返回值的情况,返回值类型用关键字void表示,那么该函数中的return语句如果在最后一行可以省略不写。
注:a).函数中只能调用函数,不可以在函数内部定义函数。
b).定义函数时,函数的结果应该返回给调用者,交由调用者处理。
定义一个函数的思想?
1.既然函数是一个独立的功能,那么先明确该功能的结果是什么?——即明确函数返回值类型
2.明确在定义该功能的过程中是否需要未知的内容参与运算?——即明确函数列表(参数类型和个数)
示例:定义一个打印九九乘法表功能的函数
class ForDemo { public static void main(String[] args) { print99();//调用函数 } public static void print99()//定义函数 { for(int i=1;i<=9;i++) { for(int j=1;j<=i;j++) { System.out.print(j+"*"+i+"="+i*j+"\t"); } System.out.println(); } } }
运行结果为:
函数的特性——重载(overLoad)
定义:在同一个类中,允许存在一个以上的同名函数,只要它们的参数个数或者参数类型不同即可。
特点:重载与返回值类型无关,只看参数列表。
好处:方便了阅读,优化了程序设计。
示例:public static int add(int a,int b){return a+b;}//定义返回两个整数的和的函数
public static int add(int a,int b,int c){return a+b+c;}//定义返回三个整数和的函数
public static String add(String a,String b)//定义返回两个连接字符串的函数什么时候定义重载?
当定义的功能相同,但参数运算的未知数内容不同,那么,这时就定义一个函数名称以表示该功能,方便阅读,而通过参数列表的不同来区分多个同名函数。
四、数组
定义:同一种类型数据的集合,相当于一个容器,可以自动给数组中的元素从0开始编号。
一维数组的格式:
格式一:元素类型[ ] 数组名 = new 元素类型[ 元素个数或数组长度 ] ; //定义一个容器,但是没有明确容器中的具体数据
如:int [ ] arr=new int[3]; 或者写成: int arr[ ] =new int [3];
格式二:元素类型[ ] 数组名 = new 元素类型[ ]{ 元素,元素,........元素} ; //定义一个容器,存储已知的数值
如:int [ ] arr =new int[ ]{3,4,5};
格式三:元素类型[ ] 数组名 ={ 元素,元素,........元素} ; //静态初始化格式
如:int [ ] arr={3,4,5};
其中:new是用来在对内存中产生一个容器实体。
在内存中的分配如下:
注:int [ ] arr=new int[3];arr[0]=5;这两句话在内存中都做了什么事呢?
1).先将arr变量加载到栈内存中
2).new int[3]:在堆内存中开辟空间,分配内存地址,假设内存地址值为0x2233,实体中的变量int类型数组默认值为0,arr[0]=5;将5赋值给数组中的0角标位
3).将内存地址值传给arr变量,arr变量通过内存地址值指向数组
4).当arr=null时就不指向堆,就和数组没关系了,堆内存中的数组实体就变成了垃圾,会在不确定的时间内被垃圾回收器回收
内存结构:Java程序 运行时,需在内存中分配空间,处理方式不同。
内存结构有:栈内存,堆内存,方法区,本地方法区,寄存器
其中:栈内存:用于存储局部变量,当数据使用完,所占空间会自动释放
堆内存:1.数组和对象,通过new建立的实例都存放在堆内存中
2.每个实体都有内存地址值
3.实体中的变量都有默认的初始化值;例如:int型数组默认为0,double型数组默认为0.0,float型数组默认为0.0f,boolean型默认为false
4.实体不再被使用,会在不确定的时间内被垃圾回收器回收
在运行时易出现的异常:
1.如在运行时出现ArrayIndexOutOfBoundsException:3错误时,意为:在操作数组时访问到了数组中不存在的角标。
2.如在运行时出现NullPointerException错误时,意为:空指针异常,当引用没有任何指向值为null的情况,该引用还在使用操作实体。
二维数组:数组的数组
格式一:int [][] arr=new int[3][4]; //表示定义了名称为arr的二维数组。其中,有三个一维数组,每个一维数组中有4个元素。一维数组的名称分别为:arr[0],arr[1],arr[3]。给第一个一维数组1脚标位赋值为23写法是:arr[0][1] = 23。
格式二:int [][] arr=new int[3][]; //注:此种格式中每个一维数组都是默认初始化值null
格式三:int [][] arr={{1,2,3,4},{4,5,6},{7,8,9,3}}; //每一个一维数组中具体元素都初始化了。
在内存中的分配如下:
![]()
注:int[ ][ ] arr=new int[2][3];这句话在内存中都做了什么事呢?
1).先将arr变量加载到栈内存中
2).new int[2][3]:在堆内存中开辟空间,分配内存地址,假设内存地址值为0x0079指向数组,数组中又各自包含了内存地址值指向了数组,即数组的数组
3).将内存地址值传给arr变量,arr变量通过内存地址值指向数组
4).当arr=null时就不指向堆,就和数组没关系了,堆内存中的数组实体就变成了垃圾,会在不确定的时间内被垃圾回收器回收
数组的常见操作:
对数组操作最基本的动作就是存和取。 核心思想:就是对角标的操作。
操作1:获取元素中的最大/少值
/** * 需求:第1题: 定义一个二维int数组,编写代码获取最小元素。 * * 思路:1.定义变量记录住每次比较后较大的值。 * 2.对数组中的元素进行遍历取出,和变量中记录的元素进行比较。 * 3.遍历结果,该变量记录就是最大值。 */ public class Test1 { public static void main(String[] args) { //定义一个二维数组 int [][] arr=new int [][]{{1,2,5},{1,2,4},{2,5,6},{2,-2,2}}; //调用getMinValue方法,获取数组中的最小值 int minValue=getMinValue(arr); System.out.println("这个二维数组中的最小值是:"+minValue); } /** * 获取二维数组中的最小值 * @param arr 接收一个二维整数类型的数组 * @return 返回二维int数组中的最小元素 */ public static int getMinValue(int[][] arr) { //定义变量,用来记录元素中最小值 int minValue=arr[0][0]; //遍历二维数组,获取其中最小值 for(int x=0;x<arr.length;x++){ for(int y=0;y<arr[x].length;y++){ //将minValue的值与元素进行比较,获取最小值,并赋给minValue if(minValue>arr[x][y]) minValue=arr[x][y]; } }//返回所得的最小值 return minValue; } }
运行结果为:
操作2:选择排序
思路:先拿0角标位和所有角标位的元素进行比较,然后再拿1角标位和其他角标位数尽心比较(除0角标位),然后再拿2角标位和其他角标位数尽心比较(除0,1角标位)..........以此类推。
注:选择排序的最值出现在头角标位置。
代码显示:
class Test2 { public static void main(String[] args) { int [] arr=new int []{3,1,4,6,2}; for(int i=0;i<arr.length-1;i++)//arr.length-1:最后一位不用再比 { for(int j=i+1;j<arr.length;j++)//int j=i+1;相同的角标位不用比 { if(arr[i]>arr[j]) { int temp=arr[i]; arr[i]=arr[j]; arr[j]=temp; } } } for(int i=0;i<arr.length;i++) { System.out.print(arr[i]+" "); } } }
显示结果为:
操作3:冒泡排序
思路:相邻的两个元素进行比较,如果符合条件就换位置。
注:冒泡排序的第一圈最值出现在最后位
代码显示:
class Test2 { public static void main(String[] args) { int [] arr=new int []{3,1,4,6,2}; for(int i=0;i<arr.length-1;i++) { for(int j=0;j<arr.length-i-1;j++)//arr.length-i-1中的-i:让每一次比较的元素减少,-1:避免角标越界 { if(arr[j]>arr[j+1]) { int temp=arr[j]; arr[j]=arr[j+1]; arr[j+1]=temp; } } } for(int i=0;i<arr.length;i++) { System.out.print(arr[i]+" "); } } }
显示结果为:
注:Java中已经定义好了排序方法,可直接拿来使用,如Arrays.sort(arr);最快的排序方法是希尔排序。
操作4:折半查找(又称二分查找):前提条件:必须保证查找前数组是有序的。
思路:1.设计三个变量记录角标:min,max,mid。min为0角标位,max为数组最大角标位,mid为(min+max)/2。
2.将已知元素与中间值位元素进行比较,如果相同则返回角标位,程序结束。
3.如果比中间值元素大,说明待查找的元素的位置可能在mid与max角标之间,设置max = mid - 1,mid = (max + min)/2,重复第1、2步的操作。
4.如果比中间值元素小,说明待查找的元素的位置可能在mid与min角标之间,设置min = mid +1,mid = (max + min)/2,重复第1、2步的操作。
5.如果数组中不存在待查找的元素,那么按照如上流程,最终min角标值会大于max角标值,此时返回-1。
代码显示:
class Test4 { public static void main(String[] args) { int [] arr={1,3,4,8,10,15,21}; System.out.println("index="+halfSearch(arr ,15)); System.out.println("index="+halfSearch_1(arr ,15)); } //方法一: public static int halfSearch(int [] arr ,int key) { int min=0,max=arr.length-1,mid=(min+max)/2; while(arr[mid]!=key) { if(key>arr[mid]) min=mid+1; else if(key<arr[mid]) max=mid-1; mid=(min+max)/2; if(min>=max) return -1; } return mid; } //方法二: public static int halfSearch_1(int [] arr ,int key) { int min=0,max=arr.length-1,mid; while(min<=max) { mid=(min+max)>>1;//相当于mid=(min+max)/2; if(key>arr[mid]) min=mid+1; else if(key<arr[mid]) max=mid-1; else return mid; } return -1; } }
运行结果为:
注:在实际开发中,折半查找不需要我们自己去写,JDK中已经提供了相关的API供调用。如:Arrays.binarySearch(arr,15);其显示结果为负数。
其原因是:
操作5:进制转换
十进制——>二进制
思路:二进制就是十进制的余数倒序。
代码显示:
class Test5 { public static void main(String[] args) { StringBuilder sb=new StringBuilder();//定义一个容器 int num=6; while(num>0) { sb.append(num%2);//将余数存进容器 num=num/2; } System.out.println(sb.reverse());//倒序输出 } }
运行结果为:
十进制——>二进制、八进制、十六进制
思路:以十进制转成十六进制为例:就是将十进制数与15相与,其值为十六进制的最低位;然后将十进制右移4位,再与15相与,得到十六进制的倒数第二位.......直到相与的结果是0为止。进而可以推理得到,十进制转换为二进制和八进制的规律与转换成十六进制很相似,只是偏移量和相与的数字不同而已。
十进制转换成二进制的偏移量为1,相与数字为1。
十进制转换成八进制的偏移量为3,相与数字为7。
示例:十进制转换成十六进制
运行结果为:public class ArrayDemo { public static void main(String[] args) { int num=60; toChange(num); } public static void toChange(int num){ StringBuilder sb=new StringBuilder(); while(num!=0){ int temp=num&15; if(temp>9) sb.append((char)(temp-10+'A')); else sb.append(temp); num=num>>>4; } System.out.println(sb.reverse()); } }
使用查表法将十进制——>二进制、八进制、十六进制:
运行结果为:public class ArrayDemo { public static void main(String[] args) { int num=60; toBin(num); toOctal(num); toHex(num); } //十进制——>二进制 public static void toBin(int num){ toChange(num,1,1); } //十进制——>八进制 public static void toOctal(int num){ toChange(num,7,3); } //十进制——>十六进制 public static void toHex(int num){ toChange(num,15,4); } //使用查表法进行进制转换 public static void toChange(int num,int base,int offset){ StringBuilder sb=new StringBuilder(); char[] arr={'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; while(num!=0){ int temp=num&base; sb.append(arr[temp]); num=num>>>offset; } System.out.println(sb.reverse()); } }
注: 1.如果数据出现了对应关系,而对应关系的一方是有序的数字编号,并作为角标使用,这时候可以使用数组。
2.将这些数据存储到数组中,根据运算的结果作为角标直接去查数组中对应的元素即可,这种方式称为查表法。