💻 CSC1003 LEC1~3 函数、数据类型与计算
All credits go to Prof. WenYe Li, who gave this lecture to year 24 Freshmen.
一切归功于李文烨教授,他是24年这门科目的主讲人。
JAVA 第一个程序
public class Hello {
public static void main(String[] args) {
System.out.println("Hello, world!");
}
}
println() 表示打印后自动换行, print() 则不换行。
一个Java源码(一个程序)只能定义一个public类型的class,并且class名称和文件名要完全一致,某个类定义的public static void main(String[] args)是Java程序的固定入口方法,因此,Java程序总是从main方法开始执行。void 是方法类型,表示“无返回值”;static 是变量类型,表示“静态变量”。
使用javac可以将.java源码编译成.class字节码;使用java可以运行一个已编译的Java程序,参数是类名。
args 不是保留字,你叫它 lgu 也无所谓,但是调用时要保持一致:lgu[0], lgu[1]…… [0] 代表从命令行输入的第一个变量。以如下代码为例,如果运行时在终端输入java Hello lgu xys,那么 Java 就会输出Hello! xys。
public class Hello {
public static void main(String[] lgu) {
System.out.println("Hello! " + lgu[1]);
}
}
全过程:
1、定义 public 类,名称为 Name,保存为 Name.java
2、javac Name.java,编译并生成 Name.class 文件
3、java Name,运行这个类
数据类型
Java定义了以下几种基本数据类型:
- 整数类型:byte,short,int,long
- 浮点数类型:float,double
- 字符类型:char,string(引用类 borrowed)
- 布尔类型:boolean
计算机内存的最小存储单元是字节(byte),一个字节就是一个8位二进制数,即8个bit。它的二进制表示范围从00000000~11111111,换算成十进制是0~255,换算成十六进制是00~ff。
在集中数据类型中,byte 占一字节(apparently),short 占2字节,int 占4字节,long 占8字节,float 占4字节,double 占8字节,char 占2字节。
对于布尔值,情况比较复杂:
在Java虚拟机规范中,布尔值在编译之后通常使用Java虚拟机中的int数据类型来代替,而int数据类型占用4个字节。因此,在这种情况下,布尔值被视为占用4个字节。内存对齐:JVM通常会将变量对齐到自然边界(如4字节),这在某些硬件架构上可以提高访问速度和效率。因此,即使从存储效率的角度看,把boolean用1位来表示似乎更合理,但考虑到性能和实现的简洁性,JVM使用了4字节来表示boolean。
在数组中,每个布尔元素使用1位,即1bit来表示。这是因为Java虚拟机使用1表示真(true),0表示假(false),来对布尔值进行编码。因此,当布尔值作为数组元素时,它们各自占用1bit。
综上所述,虽然无论在什么地方存储的布尔值都是0和1表示的,有效使用空间只有1 bit,但实际占用字节随情况改变。
-
IEEE 754 标准表示 float类型数据 (4byte,32bits) • 先将浮点数表示为:(-1)^a x b x 2^c 的形式 • 符号位(1位, a): 表示数的符号(正或负)。 0 表示正数,1 表示负数 • 指数部分(8位, c): 用于表示浮点数的指数部分,并采用偏移量(偏置数)表示法。 偏移量是 127,即实际的指数值 = 存储的指数 - 127 • 尾数部分(23位, b): 用于表示浮点数的尾数部分(小数部分),也称为有效数字(Mantissa 或 Significand)。 隐式的 1 位最左边的“1”不存储,只存储小数部分。 • 对于 double,各部分占 bit 数为1,11,52。
-
对于 float,实际情况例如: 6.5 = (-1)^0 x 1.625 x 2^2 = 0 10000001 10100000000000000000000 • 符号位 = 0(表示正数) • 指数部分 = 10000001(129),实际的指数值 = 129 - 127 = 2 • 尾数部分 = 1.101BIN(隐含的最左边的“1”,尾数为 1 + 0.5 + 0.125 = 1.625DEC)
在 Java 中,整数是绝对精确的,整数计算也是完全精确的(向下取整)。而浮点数无法在二进制下被精确表示,所以可能产生误差:
//...
float a = 0.1 / 3 * 3
float b = 0.1
System.out.println(a == b)
//...
(terminal output) > False
对于一个占 n byte 的整型数据类型,能表示的数的区间为[-2^(n-1), 2^(n-1)-1]。
- 为什么不是[-2^n, 2^n-1]?
计算机系统用二补法表示正负,比如:+6DEC = 00000110BIN,而负数要先对其绝对值的二进制数取反再加一:-6DEC = 11111010BIN。其中第一位的1与0表示了这个二进制数的正负,形式与上文所述的浮点数一样。这也与下文即将要说的溢出有关。
强制转换类型:
- byte b = 42,
b = (int) b::一个变量只能有一个数据类型,不能自己给自己转换类型。 - byte b = 42,
long c = (int) b:前后不一,到底要 long 还是 int 类? - byte b = 42, int c = (int) b:合理。
- 强制转换类型也会产生溢出错误。
计算法则
我们借用(24年) TUT session1 的例子
public class rules {
public static void main(String[] args) {
int a = 7 / 5;
double b = 7 / 5
double c = 7.0 / 5
double d = 4.00 + 5.00
System.out.print(a + ", " + b + ", " + c + ", " + d);
}
}
(terminal output) > 1, 1.0, 1.4, 9.0
-
Java 中,JVM 无法直接像 PVM 一样将所有数字同样处理,在向变量赋值时需要声明数据类型,比如
int x = 42将整型的 42 赋给了x。同时,数字本身也是自带默认类型的,比如 42 一定是一个 int 类型的数据,42.0 一定是一个 double 类型的数据(所有小数直接默认是 double)。所以对于上方程序中的 “b”: 7int / 5int = 1int,1int = 1.0double = bdouble。(7 / 5 是两个整型的计算,输出值必然为整型)。对于"c"也同理,7.0 是 double,所以输出值也是 1.4double。参与运算的两个数其中一个是整型,一个是浮点型,那么整型可以自动提升到浮点型。 -
未经特殊指定的浮点数常量(如 42.0)在 Java 中默认是 double 类型。如果需要指定为 float 类型,则必须添加 f 或 F 后缀,如 float a = 42.0f。同理 long a = 42L。
-
在 Java 中,7.00 / 5 的结果是 1.4 而不是 1.40,这是因为在默认情况下,Java 输出浮点数时,只显示必要的小数位。也就是说,Java 会自动去掉没有意义的零,只显示结果的最小精度。比如对于"d",表达式 4.00 + 5.00 的结果就是 9.0(double 类型)。
-
关于整型计算的溢出:
**整数运算溢出(integer overflow)**是 Java 语言特有的计算缺陷,当计算结果超出了该数据类型的表示范围就会发生计算错误。
此处引用李文烨教授讲座中的例子:
//...... byte m = 100; byte n = 70; byte p = m + n System.out.print(p) //......(terminal output) > -86将 170 赋值给 byte 类型的变量 p 时,发生了溢出,因为 byte 类型的最大值是 127,而 170 超出了这个范围。Java 会按 byte 类型的范围(-128 到 127)来截断数值。byte 的范围是 256(从 -128 到 127 共 256 个值),计算时会对减去256,因此结果是 -86。可以理解为超出一侧范围后从另一个端点开始继续。
-
逻辑运算:
AND 与:&&,OR 或:||,NOT 非:!=。
-
位运算(二进制):
AND 与:&,OR 或:|,XOR 异或:^。
Reference
随堂小测
1、float a = 0.11 * 100 计算的结果是11.0吗?
2、以下中 b 输出值为多少?
short a = 30000:
short b = a + a
System.out.println(b)
3、如果用 Java 语言模拟子网掩码的功能要用到什么运算符?
4、args 是保留名吗?
5、double x = 1.00 + 2 计算结果是什么?
6、如何用强制转换类型随机抽取一个 0 到 100 之间的整数并赋给 a ?
answer
1、不一定 2、-5536 3、& 4、否 5、3.0
6、int a = (int) (Math.random() * 101)