Java中的=号
-
= 在数学中是等价的概念
-
= 在Java中是赋值的概念,讲右边的值赋给左边
浮点数
-
两个整数相除,结果只能是整形
-
当浮点数和整数放到一起运算时,Java会将整数转换成浮点数,然后进行浮点数的运算
-
浮点运算是有误差的
System.out.println(1.2-1.1); //0.09999999999999987
运算符 +、-
结合关系
-
一般自左向右
-
单目±和赋值=自右向左
result = a = b = 3 + c
强制类型转换
只是从那个变量计算出了一个新的类型的值,它并不改变那个变量,无论是值还是类型都不改变
关系运算符
-
==
and!=
and>
and>=
and<
and<=
-
优先级
-
判断是否相等的==和!=的优先级比其他的低,而连续的关系运算是从左到右进行的
-
7 >= 3 + 4
-
5 > 3 == 6 > 4
-
true 和 false 可以用 ==比较,但是不能用<>比较
-
-
浮点数是否相等
-
1 e − 6 1e-6 1e−6表示 1 ∗ 1 0 − 6 1 * 10^{-6} 1∗10−6
-
Math.abs(f1 - f2) < 0.000001
-
If语句
-
if (total > amount) total += amount + 10;
嵌套和级联的判断
-
当if的条件满足或者不满足的时候要执行的语句也可以是一条if或if-else语句,这就是嵌套的if语句
-
else总是和最近的if匹配
-
在if或else的后面总是使用{},即使只有一条语句的时候
-
分段函数
// f(x) = -1; x < 0 // = 0; x = 0 // = 2x; x > 0 if (x < 0) { f = -1; } else if (x == 0) { f = 0; } else { f = 2 * x; }
-
在多级If嵌套的时候__单一出口__是更好的代码风格
判断语句常见错误
- 忘记大括号 解决办法:永远在if和else后面加上大括号,即使当时后面只有一条语句
- if后面加上了分号
- if括号里面的判断使用
=
多路分支
多条if-else语句换成switch-case
switch (控制表达式)
{
case 常量:
语句
....
break;
case 常量:
....
break;
default:
语句
....
break;
}
- 表达式判断之后进入对应的case,执行语句。
- 如果有break,则该switch-case执行结束
- 如果没有break,则执行下一个case(包括default),直到执行到beak结束或整个switch-case执行结束
循环
- while
数数字
-
程序要读入一个正整数,然后输出这个整数的位数。例如:输入352,输出3
// 初始化 Scanner in = new Scanner(System.in); int number = in.nextInt(); int count = 0; while (number > 0) { number = number / 10; count = count + 1; } System.out.println(count);
While循环
看运行结果
-
人脑模拟计算机的运行,在纸上列出所有的变量,随着程序的进展不断重新计算变量的值。当程序运行结束时,留在表格最下面的就是程序的最终结果。变量表格
以上面的循环代码为例:
number count 352 0 35 1 3 2 0 3 -
调试:在程序适当的地方插入输出来显示变量的内容
-
debug,打断点,跟踪程序中变量的值
验证
- 测试程序常使用边界数据,如有效范围两端的数据,特殊的倍数等
do-while循环
- 在进入循环的时候不做检查,而是在执行完一轮循环体代码之后,再来检查循环的条件是否满足,如果满足则继续下一轮循环,不满足则结束循环
do
{
<循环语句>
} while (<循环条件>)
计数循环
int count = 100;
while (count >=0)
{
count = count - 1;
System.out.println(count);
}
System.out.println("发射!");
- 小套路:如果要模拟运行一个很大次数的循环,可以模拟较少的循环次数,然后做出推断
算平均数
- 变量 ->算法 -> 流程图 ->程序
- 开始写程序时:
- 输入输出数据
- 逻辑
猜数游戏
- 让计算机来想一个 [ 1 , 100 ] [1,100] [1,100]数,然后让用户来猜,用户每输入一个数,就告诉它是大了还是小了,直到用户猜中为止,最后还要告诉用户它猜了多少次
- 因为需要不断重复让用户猜,所以需要用到循环
- 在实际写出程序之前,我们可以先用文字描述程序的思路
- 核心重点是循环的条件
- 人们往往会考虑循环终止的条件
- 利用二分法一定可以在7次包括7次以内得出结果
整数分解
- 在写循环不能确定是while还是do-while,先写出循环体,最后再确认是while还是do-while
for循环
-
for循环像一个计算循环
for (初始化; 条件; 单步动作) { }
-
for中的每一个表达式都是可以省略的
for (;条件;) == while (条件)
-
n!=1*2*3*…*n
-
表达累积的结果应该初始化为1
-
int: 4字节 or 32bits
- 最大 2 31 − 1 2^{31}-1 231−1
- 最小 − 2 31 -2^{31} −231
Tips for loops
- 如果有固定次数,用for
- 如果必须执行一次,用do-while
- 其他情况用while
复合赋值
- 优先级和普通赋值符=一样,优先级低
- i++ & ++i ++i出现是因为历史原因,不建议使用
循环控制
-
素数:只能被1和自己整除的数,不包括1
- 2,3,5,7,9,11,13,17,19…
-
break:跳出循环
-
continue:跳出循环这一轮剩下的语句进入下一轮
多重循环
- 标号:多重循环使用
break 标号
orcontinue 标号
跳出标号标记的循环
逻辑类型
- boolean
- 优先级
- () 第一优先级
- 单目运算符
!+-++--
第二优先级 - && || 优先级较低
求和
-
$f(n)=1 + \frac{1}{2} + \frac{1}{3} + \cdots + \frac{1}{n} $
-
$f(n)=1 - \frac{1}{2} + \frac{1}{3} - \cdots + \frac{1}{n} $
-
for (int i = 1; i <= n; i++, sign = -sign)
for循环中单步动作可以执行多个操作,注意要用,
分隔
最大公约数
- 辗转相除法(欧几里得法)
- 如果b等于0,计算计算,a就是最大公约数
- 否则,计算a除以b的余数,让a等于b,而b等于那个余数;
- 回到第一步
初试数组
-
如何写出一个程序计算用户输入的数字的平均数,并输出所有大于平均数的数
- 如何记录很多数?
Scanner in = new Scanner(System.in); int x; // 初始化数组 int[] numbers = new int[100]; double sum = 0; int cnt = 0; x = in.nextInt(); while (x != -1) { numbers[cnt] = x; sum += x; cnt++; x = in.nextInt(); } if (cnt > 0) { double average = sum / cnt; for (int i = 0; i < cnt; i++) { if (numbers[i] > average) { System.out.println(numbers[i]); } } System.out.println(sum/cnt); }
创建数组
- 数组创建后,语法很严格,创建的是int类型的数组,不能放进double类型
<类型>[] <名字> = new <类型>[元素个数]
- int[] grades = new int[100];
- double[] averages = new double[20];
- 元素个数必须是整数
- 元素个数必须给出
- 元素个数可以是变量
数组中的元素
-
最小的下标是0,最大的下标是数组的个数-1
-
可是编译器不会检查你是否用了有效的下标
-
但是如果运行的时候出现了无效的下标,可能会导致程序终止
-
每个数组有一个内部成员length,会告诉你它的元素的数量
for (i = 0; i < 100; ++i) { sum += grade[i]; } 最好是: for (i = 0; i < grade.length; ++i) { sum += grade[i]; }
可扩展性代码
- new 数组后,Java会自动初试化数组,如果是一个int数组,那么数组中的每个元素都是0
数组变量
-
直接初始化数组
- new创建的数组会得到默认的0值
- int[] scores = {87, 98, 69, 54, 65, 76, 87, 99};
- 直接用大括号给出数组的所有元素的初始值
- 不需要给出数值的大小,编译器替你数数
-
数组变量赋值
int[] a = new int[10]; a[0] = 5; int[] b = a; b[0] = 16; System.out.println(a[0]); // a[0] = 16; b[0] = 16;
-
数组变量是数组的管理者而非数组本身
-
数组必须创建出来然后交给数组变量来管理
-
数组变量之间的赋值是管理权限的赋予
-
数组变量之间的比较是判断是否管理同一数组
int[] a = {1, 2, 3, 4, 5}; int[] b = {1, 2, 3, 4, 5}; System.out.println(a == b); // false
-
数组复制
int[] a = {1, 2, 3, 4, 5}; int[] b = new int[a.length]; for (int i = 0; i < a.length; i++) { b[i] = a[i]; } System.out.println(a == b); // false
遍历数组
for (int i = 0; i < data.length; i++)
{
if (x == data[i])
{
loc = i;
break;
}
}
-
通常都是使用for循环,让循环变量i从0到<数组的length,这样循环体内最大的i正好是数组最大的有效下标
-
常见错误是:
- 循环结束条件是<=数组长度
- 离开循环后,继续用i的值来做数组元素的小标
-
另一种遍历数组for-each
- for-each取到的是数组中元素的值,而不是地址中的值
loolean found = false; for (int k: data) { if (k == x) { found =true; break; } }
素数
-
构造前50个素数的表
int[] primes = new int[50]; primes[0] = 2; int cnt = 1; MAIN_LOOP: for (int x = 3; cnt < primes.length; x++) { for (int i = 0; i < cnt; i++) { if (x % primes[0] == 0) { continue MAIN_LOOP; } } primes[cnt++] = x; } for (int k: primes) { Syetem.out.print(k + " "); } System.out.println();
-
构造素数表
booean[] isPrime = new boolean[100]; for (int i = 2; i < isPrime.length; i++) { isPrime[i] = true; } for (int i = 2; i < isPrime.length; i++) { if (isPrime[i]) { for (int k = 2; i * k < isPrime.length; k++) { isPrime[i*k] = false; } } } for (int i = 0; i < isPrime.length; i++) { if (isPrime[i]) { System.out.print(i + " "); } } System.out.println();
- 算法不一定和人的思考方式相同
二维数组
-
int[][] a = new int[3][5];
-
通常理解a是一个3行5列的矩阵
-
二维数组的遍历
for (int i = 0; i < 3; i++) { for (int j = 0; j < 5; j++) { a[i][j] = i * j; } }
- a[i][j]是一个int
- 表示第i行第j列上的单元
- a[i,j]并不存在
-
二维数组的初始化
int[][] a = { {1, 2, 3, 4}, {1, 2, 3}, };
- 编译器来数数
- 每行一个{},逗号分隔
- 最后的逗号可以存在,有古老的传统
- 如果省略,表示补零
字符类型
-
单个的字符是一种特殊的类型:char
-
用单引号表示的字符字面量:‘a’,‘1’
-
字符之间可以使用加减法,表示对应的Unicode相加减
-
字符之间相加减,得到的是对应的Unicode码相加减得到的十进制数字
char c = 'A'; char d = 'D'; System.out.println(d - c); // 3 c++; System.out.println(c); // B
-
-
Java使用Unicode来表示字符,可以表达包括汉字在内的多种文字
-
Unicode使用16进制,
\u0041
换算成10进制是65,表示A
System.out.println("\u0041"); // A
-
-
大小写转换
-
字母和数字在Unicode表中是顺序排列的
- ‘0’,‘1’,‘2’…‘9’
- ‘a’…‘z’
- ‘A’…‘Z’
-
大写字母和小写字母是分开排列的,并不在一起
// 大写的D转为小写的d char f = (char)(d + 'a' - 'A'); System.out.println(f);
-
逃逸字符
-
用来表达无法打印出来的控制字符或特殊字符,它由一个反斜杠“\”开头,后面跟上另一个字符,这两个字符合起来,组成了一个字符
字符 意义 字符 意义 \b 回退一格 \" 双引号 \t 到下一个表格位 \’ 单引号 \n 换行 \\ 反斜杠本身 \r 回车
包裹类型
-
每种基础类型都有对应的包裹类型
基础类型 包裹类型 boolean Boolean char Character int Integer double Double
字符串变量
-
String是一个类,String的变量是对象管理者而不是所有者
- 数组也是这样
- 注意基础类型变量是对象的所有者
-
初始化字符串
- String s = new String(“hello”);
- String s = “hello”;
-
用加号(+)可以连接两个字符串
-
比较两个String
- == 是比较两个字符串的地址
- 字符串内容比较使用equals()
-
输入一个字符串
in.next()
读入一个单词,单词的标志是空格- 空格包括空格,tab,换行
in.nextLine()
读入一整行
字符串操作
-
字符串是对象,对它的操作都是通过
.
-
Strings大小的比较
- 两个字符串比较大小
s1.compareTo(s2)
- compareToIgnoreCase可以不区分大小写地来比较大写
- 两个字符串比较大小
-
获得String的长度
- 用length()函数
-
访问String里的字符
-
s.charAt(index)
- 返回在index上的单个字符
- index的范围是0到length()-1
- 第一个字符的index是0,和数组一样
-
但是不能用for-each循环来遍历字符串
-
用普通for循环来遍历字符串
for (int i = 0; i < s.length(); i++) { System.out.println(s.charAt(i)); }
-
-
得到子串
- s.substring(n)
- 得到从n号位置到末尾的全部内容
- s.substring(b,e)
- 得到从b号位置到e号位置之前的内容
- s.substring(n)
-
寻找字符
- s.indeOf()
-
其他String操作
- s.startsWith(t)
- s.endsWith(t)
- s.trim()
- s.replace(c1,c2)
- s.toLowerCase()
- s.toUpperCase()
-
不可变得String
- 所有的字符串都是不可变的,对他们的操作的结果都是制造新的字符串出来
-
在switch-case中使用字符串
Math类
- abs 算绝对值
- pow 算幂次
- random 生成 ( 0 , 1 ] (0,1] (0,1]的随机数
- round 取整,四舍五入
定义函数(方法)
- “代码复制”是程序不良的表现
- 什么是函数
- 函数是一块代码,接受零个或多个参数,做一件事情,并返回零个或一个值
- 可以先想像成数学中的函数 y = f ( x ) y=f(x) y=f(x)
调用函数
- 函数名(参数值)
- 函数返回值
- 有返回值
- 函数体中必须有return
- return停止函数的执行,并返回一个值
- 没有返回值
- void 函数名(参数表)
- 不能使用带值的return,可以使用return(也可以没有return)
- 有返回值
- 函数如果return一个int,可以把对这个函数的调用结果当成一个数来使用。就行在线性代数中,我们把行列式也当成一个数
参数传递
-
如果函数有参数,调用函数时必须传递给它数量,类型正确的值
-
类型不匹配
- 当函数期望的参数类型比调用函数时给的值的类型宽的时候,编译器能悄悄帮你把类型转换好
- char --> int --> double
- 当函数期望的参数类型比调用函数时给的值的类型窄的时候,需要你写强制类型转换
- (int)5.0
- 当函数期望的参数类型比调用函数时给的值的类型之间无法转换的时候 -->不行 !
- 当函数期望的参数类型比调用函数时给的值的类型宽的时候,编译器能悄悄帮你把类型转换好
-
传过去的是什么?
- Java语言在调用函数时,永远只能传值给函数
- 传值
- 每个函数有自己的变量空间,参数也位于这个独立的空间中,和其他函数没有关系
- 过去,对于函数参数表中的参数,叫做“形式参数”,调用函数时给的值,叫做实际参数
- 由于容易让初学者误会实际参数就是实际在函数中进行计算的参数,误会调用函数的时候把变量而不是值传进去,所以我们不建议继续用这种古老的方式来称呼它们
-
我们认为,它们是参数和值的关系
本地变量
- 函数的每次运行,就产生了一个独立的变量空间,在这个空间中的变量,是函数的这次运行所独有的,称作本地变量
- 定义在函数内部的变量就是本地变量
- 参数也是本地变量
- 变量的生存期和作用域
- 生存期:什么时候这个变量开始出现了,到什么时候它消亡了
- 作用域:在(代码的)什么范围内可以访问这个变量(这个变量可以起作用)
- 对于本地变量,这两个问题的答案是统一的:大括号内—块
- 本地变量的规则
- 本地变量是定义在块内的
- 它可以是定义在函数的块内
- 也可以定义在语句的块内
- 甚至可以随便拉一对大括号来定义变量
- 程序运行进入这个块之前,其中的变量不存在,离开这个块,其中的变量就消失了
- 块外面定义的变量在里面仍然有效
- 不能在一个块内定义同名的变量,也不能定义块外面定义过的变量
- 本地变量是定义在块内的
- 本地变量不会被默认初始化
- 参数在进入函数的时候被初始化了