递归 | break | continue | 自增自减运算符 | 我的博客 | |
表达式 | 移位运算符 | 赋值运算符 | 字符串连接运算符 | v512工作室 | |
循环语句 | 数值类型转换 | 程序运行流程 | Java整数二进制表示 | 中科院新科海学校 |
运算符(教程中00:06:20之前介绍的是各个符号的意义)
短路运算符:使用段落逻辑运算符&&和||可实现“短路”的“与”和“或”运算的功能
当根据第一个操作数的值已经能够确定整个表达式的结果时,将跳过对第二个操作数值的评估,直接得出最终结果
运算符举例:i=j=k=5+3; //等价于i=(j=(k=5+3));最后结果是三者值均为8
public static void ma(){ B=2; } System.out.println(ma()); //非法。因为println()括号里不允许使用void类型
boolean b=false; //如果条件是b=true,执行结果则是ok //如果条件是b==true,执行结果就是error
if(b=true) System.out.println("ok"); //注意:初始条件中只有一个等号
else System.out.println("error");
短路或举例:if(n<0||n>31){System.out.println("非法赋值");}
就是说一旦满足n<0,那么就不再计算它还是否满足n>31,而是直接执行语句块中的代码
短路与举例:if((m!=null)&&(m.day>0)&&(m.day<31)){System.out.println("日期合法");}
就是说如果第一个表达式的值为false,那么它会导致整个表达式的值为false
如果第一个表达式不成立。那么程序是不会计算第二个表达式和第三个表达式的,最后整个表达式结果为false
如果第一个表达式是true。那么程序会接着进行第二个表达式的运算,如果第二个表达式为false,道理一样,最后结果还是false
如果前两个表达式均true。那么如果第三个表达式为false,整个结果为false。但如果三个表达式均为true,整个结果才为true
而且在这个例子当中,第一个和第二个表达式的位置是不能换的。因为只有先判断m引用不为空之后,才可以根据m引用调用它的day属性
但如果硬性的把前两个表达式调换位置的话,程序运行时,一旦m值是null的情况下,它会抛出空指针(NullPointerException)
由于m是null,说明m没有指向任何一个对象,那么m就不能调用类的方法或属性,所有它会抛出一个违例,空指针的违例
通常来说,我们在实际编程中使用一个对象的时候,都会先判断一下这个对象,看它的引用是不是为null,就是看它的值是不是为null
只有在它的值不等于null的时候,再去调用它的方法或属性,所以我们在编程当中经常会用到m!=null
并且在这个例子中,它要放在第一位,而且要使用短路与。因为它只有在不等于null的时候,才可以做后面的运算
如果我们把短路与改成逻辑与,程序运行时,如果m值为null,这行也会抛出空指针的违例
因为在计算m!=null时得到false结果后,程序还是会计算后两个表达式。同样道理,这时m为null,还是不能调用day,所以抛出空指针
条件运算符:<表达式1>?<表达式2>:<表达式3>
其中<表达式1>必须为boolean类型,系统将首先计算<表达式1>的值,当其值为true时
则将<表达式2>的值作为整个表达式的最终结果,否则将<表达式3>的值作为整个表达式的最终结果
Java整数二进制表示
概述:Java语言二进制数采用补码形式表示,数正负区分标志:符号位
转换:正数进制转换:十进制→→二进制:除2取余,除尽为止
二进制→→十进制:累加求和
负数进制转换:十进制→→二进制:取绝对值,转换为二进制,逐位取反,加1
二进制→→十进制:减1,逐位取反,转换为十进制正数,乘负1
移位运算符
概述:用于对整型数据的二进制位进行移位处理
左移:a<<b将二进制形式的a逐位左移b位,最低位(右侧)空出的b位补0,原来最左侧的b位被丢弃
带符号右移:a>>b将二进制形式的a逐位右移b位,最高位(左侧)空出的b位补原来的符号位
无符号右移:a>>>b将二进制形式的a逐位右移b位,最高位空出的b位补0
性质:适用数据类型:byte、short、char、int、long。对低于int型的操作数将先自动转换为int型,再进行移位
对于int型整数移位a>>b,系统先将b对32取模,得到的结果才是真正移位的位数
对于long型整数移位a>>b,则是先将移位位数b对64取模,得到的结果才是真正移位的位数
说明:一个负数经无符号右移操作后,就变成了正数。一个整数,无论怎么移,它都始终为正数
移位主要应用在一些图形图像的底层处理上,还有一些科学运算上。在开发当中,应用的比较少
赋值运算符
概述:将等号右侧表达式的计算结果赋给等号左侧的变量。赋值操作时遵循值传递原则
等号左侧必须是一个已经声明过的变量,而不允许是常量或复合表达式
例如:int i=5,j=6; //合法
j=i+10; //合法
10=i+j; //非法
i+j=10; //非法
说明:赋值运算符左右两侧的数据类型应保持一致,可以自动进行类型转换的情况除外
扩展:将赋值运算符和其它运算符结合起来可以组成扩展赋值运算符,以实现简化的运算标记效果
自增和自减运算符
概述:++和--也称增量或减量运算符,用于对单个变量进行增1或减1操作
使用:可以单独使用,也可用于复合表达式中。运算符可以位于变量之前,也可用于变量之后
位于变量前面时,先进行变量的增量或减量运算,然后再取用变量的值计算整个表达式的结果
位于变量后面时,先取用变量的值计算整个表达式的结果,然后才进行增量或减量运算
举例:m++ //等价于m=m+1或++m
n=++m; //等价于m=m+1, n=m;
n=m++; //等价于n=m; m=m+1;
n=4*m--; //等价于n=4*m; m=m-1;
字符串连接运算符
概述:+除用于算术加法运算外,还可用于对字符串进行连接操作
+运算符两侧的操作数中只要有一个是字符串(String)类型,系统会自动将另一个操作数转换为字符串然后再进行连接
例子:int i=300+5; String s="hello,"+"world!";//最后s的值为hello,world!
int i=300+5; String s="hello,"+ i +"号";//最后s的值为hello,305号
表达式
概述:表达式是符合一定语法规则的运算符和操作数的序列。如:a 5.0+a (a-b)*c-4等均为表达式
类型和值:对表达式中操作数进行运算得到的结果称为表达式的值
表达式的值的数据类型即为表达式的类型
运算顺序:首先应按照运算符的优先级从高到低的顺序进行
优先级相同的运算符按照事先约定的结合方向进行
举例:String s1=3+5+"hello"; //输出8hello
String s2="hello"+3+5; //输出hello35
数值类型转换
概述:表达式运算中会出现将数值从一种类型转换为另外一种类型的情况
自动类型转换:int n=35 long a=n float f=n double d='c' double e=3.14f
强制类型转换:int i=(int)3.14 byte b=(byte)i
总结:如果把小的数据类型赋值给大的数据类型时,会自动转换
反之,会造成数值的丢失精度,所以才需要强制类型转换
程序运行流程
概述:按照运行流程可分为三种基本结构:顺序性结构、分支结构和循环结构
顺序性结构:按照语句出现的顺序依次执行的程序结构
分支结构:也称选择性结构。有条件地执行或跳过特定的语句或语句块,实现有选择的流程控制
循环结构:在一定的条件下重复执行特定代码
if-else:用于实现分支结构,其中的else子句不是必须的,if语句又可细分为三种形式(这里不再赘述)
如果if后面只需要执行一条语句,则不需要添加花括号。如果需要执行多条语句,则需要用花括号把这些语句括起来
switch:switch(<表达式>){ //用于实现简单的多路分支结构
case <常量1>:[<语句1>]
case <常量2>:[<语句2>]
......
case <常量n>:[<语句n>]
[default:<语句n+1>] }
相关规则:表达式expr的返回值必须是下述几种类型之一:int、byte、char、short、枚举类型和封装类型
case子句中的值constant必须是常量且所有case子句中的值应是不同的
default子句是任选的
break语句用来在执行完一个case分支后使程序跳出switch语句块
代码举例:switch(a){
case 1:day="星期一";
case 2:day="星期二";
case 5:day="星期五";break;
case 6:day="星期六";break;
代码说明:这时,如果a=1,最后day值为星期五。如果a=5,最后day值为星期五。如果a=6,最后day值即为星期六
因为在没有break时,由于a=1,所以case1执行了。接着程序并没有发现break,那么程序会继续执行下去
直到程序执行到day=星期五时才发现break,程序终止。所以呢,无论a=1或2或5,最后day的值都是星期五
循环语句
功能:在循环条件满足的情况下,反复执行特定代码
组成:初始化部分(init_statement)、循环条件部分(test_exp)、循环体部分(body_statement)、迭代部分(alter_statement)
分类:for循环、while循环、do-while循环、for-each循环
补充:在DOS终端窗口中结束死循环:Ctrl+C即可
for语法:for(<初始化表达式>;<循环条件表达式>;<迭代表达式>)
<循环体语句或语句块>
for举例:for(int i=1;i<=100;i++){ result+=i; }
while语法:while(<循环条件表达式>)
<循环体语句或语句块>
while举例:while(i<=100){ result+=i; i++; }
do-while语法:do <循环体语句或语句块>
while(<循环条件表达式>)
do-while举例:do { result+=i; i++; } while(i<=100)
for-each语法:for(<迭代变量声明>:<数组或集合>)
<循环体语句或语句块>
for-each功能:逐个处理(遍历)数组和集合中的所有元素
for-each举例:int[] a={3,5,78,12}; for(int k:a){ System.out.println(k); }
这里是迭代输出数组a中的每个元素,即把a中的每个元素临时赋给k,然后输出k值
循环用法总结:对于同一问题,for、while、do-while三种循环可相互替代
for功能强于while、do-while。但若不是明显地给出循环变量初始值(或修改条件),则应用while或do-while以增强程序的结构化和可读性
除for-each以外,其它的循环结构要防止出现无限循环(死循环)
循环过程中,也可以提前跳出本次喜欢或终止整个喜欢,这需要用到下述的特殊流程控制语句continue和break
break语句
作用:终止所在的switch语句或循环语句的运行
语法:break; //终止其所在的单层(内层)循环的运行
break <标签>; //在多层嵌套循环中,终止标签标记的外层循环的运行
举例:outer:for(int j=0; j<4; j++){
for(int i=0;i<10;i++){if(i==2) break outer; System.out.println("i="+i);}
System.out.println("j="+j);}System.out.println("Game Over");
说明:开始时j=0,满足j<4条件,于是执行内部for循环。当i=2时,结束标签outer循环。然后转而执行Game Over
continue语句
作用:用于结束所在的循环语句的本次运行,即跳过其后的循环体语句,开始下一次循环
语法:continue; //跳过其所在单层(内层)循环的本次运行
continue <标签>; //在嵌套循环中,跳过标签标记的外层循环的本次运行
举例:取100到200之间的所有素数。除了1和它本身外,不能被其它数所整除的数称为素数
public class TestContinueOuter{public static void main(String args[]){int n = 0;
outer:for(int i=101; i<200; i+=2){
for(int j=2; j<i; j++){if(i%j==0) continue outer; }
System.out.print(i+"\t");
n++; if(n<6) continue; //输出六个数据后换行,以达到美观的效果
System.out.println(); n = 0; } } }
说明:外层循环:所有偶数都不是素数,因为它们可以被2整除。所以i值从101开始,并且循环单位是2,就是每次的累加值都是2
内层循环:除了1和它本身外,它不能被其他数所整除的数是素数。所以j值从2开始,并且小于i
如果i%j==0,就是说i不是素数,那么就跳出外层的本次循环,接着就不再执行底下的输出语句,转而执行下一次的外层循环
如果通过内层的循环判断出i是素数(即i%j==0不成立),则输出i值和空出一个Tab格(\t)的效果
因为我们原本是想输出6个数之后换行,所以这里使用的是print。即不换行,而是输出一个素数并空一格
由于每行要输出6个素数,那么在每次输出完一个素数后则对n实行累加操作
如果n<6,那么它底下的println()和n=0就不再执行,而是开始继续执行外层的下一次循环
当n=6的时,print(i+"\t")已经在第一行中输出6个素数了。那么按照程序,n<6就不再成立,也就是不再执行这个continue了
然后执行println(),效果是切换到下一行开始。同时令n=0,即重新统计该行中所输出的素数的个数
补充:判断它是不是素数,实际上是让它去除从2开始一直除到小于它本身的那个整数为止。实际上没有这个必要
直接从2除到小于等于它的平方值的整数就可以,即本例内层循环的条件表达式可以写成j<=(int)Math.sqrt(i)
Math.sqrt()返回的是一个double,我们把它强制转换成int
sqrt()方法是Math类当中的静态方法,所以可以通过类的名字调用这个方法
然后重新编译,运行,可以看到输出效果跟刚才的是一样的。但是改过的程序在执行上就更有效率了
递归
递归:解决逻辑或数学问题的一种方法。递归是现在开发当中经常要用到的一种算法或者说方法
它通常会把一个大规模复杂问题层层转化为一个或多个性质相同,但是规模却缩小的问题来求解
通过对规模缩小的问题的求解,最后得出来大规模或者复杂问题的最后的结果
思想:依此类推。递归方法解决问题的基本思想是“依此类推”
要素:递推公式和递归终止条件。利用递推公式就可以编写递归的方法,利用终止条件就可以设定结束递归调用的条件
原理:先求得范围或规模缩小的同种性质问题的结果,然后再利用上一步已得到的结果经过简单操作处理求得问题的最后解答
重复前述的步骤,当相同性质的问题被简化到足够简单时,将可直接获得问题的答案,而不会无限重复下去
问题:求给定整数的阶乘,如何实现
方案:采用循环结构:public int f(int n){int result=1;for(int i=1;i<=n;i++){result=result*i; }return result; }
采用递归结构:public int f(int n){if(n==1){ return 1; }else{int k=f(n-1); return n*k; } }
说明:采用递归的方式,就是定义一个方法,然后在这个方法内部又调用这个方法,这就是递归
这样一来,如果没有结束条件的话,程序就会无穷无尽的调用下去,相当于陷入一种死循环
所以采用递归方式的话,要加上一个能结束递归的执行的条件
这里的if(n==1)就是结束该递归的条件。这里的方法叫做f(),在这个方法里又调用了自身的方法f()了
比如我们使用递归方式求6!。调用这个f(),传的n是6。先判断得出6不等于1的结果,所以不会执行return语句
接着执行int k=f(n-1)。传的n是6,这时调用之后就是f(5),并赋给k。返回的是n*f(5),即6*f(5)
于是,我们求6!,就相当于求6*5!。那么求5!,就相当于5*4!,依此递推。实际上这里利用的是递推公式n!=n*(n-1)!
然后当n=2时,k=f(1)。而f(1)返回的是1,所以当n=2时,k=1,返回2*f(1),即返回2*1!,即返回2*1
所以6!=6*5*4*3*2*1