廖雪峰Java教程笔记-Java程序基础 变量和数据类型


变量和数据类型

变量

1
在Java中,变量分为两种:基本类型的变量和引用类型的变量。
1
2
3
4
*先定义再应用*
*可以一次性多个定义和赋值*
*没有赋值,将自动赋默认值(基本数据类型)*
*可以将一个基本数据类型变量赋值给另一个基本类型变量。不是指向同一个地址*

基本数据类型有

1
2
3
4
5
6
7
8
9
10
11

~~~
+ 整型 byte,short,int,long
+ 浮点型 float,double
float要加上f或F,double可以省略
+ 字符型 char
用单引号
+ 布尔型 false,true
~~~

```不同的数据类型占用的字节数不一样。我们看一下Java基本数据类型占用的字节数

HtXtqe.png

整型

1
2
3
4
5
6
7
8
对于整型类型,Java只定义了带符号的整型,因此,最高位的bit表示符号位(0表示正数,1表示负数)。各种整型能表示的最大范围如下

byte:-128 ~ 127
short: -32768 ~ 32767
int: -2147483648 ~ 2147483647
long: -9223372036854775808 ~ 9223372036854775807

对于float类型,需要加上f后缀。
1
2
3
4
5
6
7
8
9
10
11
12
public class Main {
public static void main(String[] args) {
int i = 2147483647;
int i2 = -2147483648;
int i3 = 2_000_000_000; // 加下划线更容易识别
int i4 = 0xff0000; // 十六进制表示的16711680
int i5 = 0b1000000000; // 二进制表示的512
long l = 9000000000000000000L; // long型的结尾需要加L
}
}

特别注意:同一个数的不同进制的表示是完全相同的,例如15=0xf0b1111

浮点型

因为小数用科学计数法表示的时候,小数点是可以“浮动”的,所以称为浮点数

1
2
3
4
5
float f1 = 3.14f;
float f2 = 3.14e38f; // 科学计数法表示的3.14x10^38
double d = 1.79e308;
double d2 = -1.79e308;
double d3 = 4.9e-324; // 科学计数法表示的4.9x10^-324

布尔类型

1
2
3
布尔类型boolean只有true和false两个值,布尔类型总是关系运算的计算结果

Java语言对布尔类型的存储并没有做规定,因为理论上存储布尔类型只需要1 bit,但是通常JVM内部会把boolean表示为4字节整数

字符类型

1
字符类型char表示一个字符。Java的char类型除了可表示标准的ASCII外,还可以表示一个Unicode字符:
1
2
3
4
5
6
7
8
public class Main {
public static void main(String[] args) {
char a = 'A';
char zh = '中';
System.out.println(a);
System.out.println(zh);
}
}
1
注意char类型使用单引号',且仅有一个字符,要和双引号"的字符串类型区分开。

引用类型

除了上述基本类型的变量,剩下的都是引用类型

字符串

1
2
3
4
5
引用类型最常用的就是String字符串:

String s = "hello";

引用类型的变量类似于C语言的指针,它内部存储一个“地址”,指向某个对象在内存的位置,后续我们介绍类的概念时会详细讨论。

常量

1
2
3
4
5
6
7
8
9
定义变量的时候,如果加上final修饰符,这个变量就变成了常量

final double PI = 3.14; // PI是一个常量
double r = 5.0;
double area = PI * r * r;
PI = 300; // compile error!

常量在定义时进行初始化后就不可再次赋值,再次赋值会导致编译错误。
根据习惯,常量名通常全部大写。

var关键字

1
2
3
4
5
6
定义变量时,变量类型太长,可以用var
StringBuilder sb = new StringBuilder();

这个时候,如果想省略变量类型,可以使用var关键字:
var sb = new StringBuilder();
编译器会根据赋值语句自动推断出变量sb的类型是StringBuilder

变量的作用范围

1
定义变量时,要遵循作用域最小化原则,尽量将变量定义在尽可能小的作用域,并且,不要重复使用变量名。

小结

1
2
3
4
Java提供了两种变量类型:基本类型和引用类型
基本类型包括整型,浮点型,布尔型,字符型。
变量可重新赋值,等号是赋值语句,不是数学意义的等号。
常量在初始化后不可重新赋值,使用常量便于理解程序意图。

整数运算

1
2
3
4
5
6
7
整数的数值表示不但是精确的,而且整数运算永远是精确的,即使是除法也是精确的,因为两个整数相除只能得到结果的整数部分

int x = 12345 / 67; // 184
求余运算使用%:

int y = 12345 % 67; // 12345÷67的余数是17
特别注意:整数的除法对于除数为0时运行时将报错,但编译不会报错

溢出

1
要特别注意,整数由于存在范围限制,如果计算结果超出了范围,就会产生溢出,而溢出不会出错,却会得到一个奇怪的结果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Main {
public static void main(String[] args) {
int x = 2147483640;
int y = 15;
int sum = x + y;
System.out.println(sum); // -2147483641
}
}

0111 1111 1111 1111 1111 1111 1111 1000
+ 0000 0000 0000 0000 0000 0000 0000 1111
-----------------------------------------
1000 0000 0000 0000 0000 0000 0000 0111
由于最高位计算结果为1,因此,加法结果变成了一个负数
1
2
3
4
还有一种简写的运算符,即+=,-=,*=,/=,它们的使用方法如下:

n += 100; // 3409, 相当于 n = n + 100;
n -= 100; // 3309, 相当于 n = n - 100;

自增/自减

1
2
3
4
5
++

--

*写在变量前面和后面是不同的,前面(先加减在运算),后面(先运算再加减)*

移位运算符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
*数值的最高位是一个符号位*
1.
>>: 右位移
int n = 7; // 00000000 00000000 00000000 00000111 = 7
int a = n >> 1; // 00000000 00000000 00000000 00000011 = 3
int b = n >> 2; // 00000000 00000000 00000000 00000001 = 1
int c = n >> 3; // 00000000 00000000 00000000 00000000 = 0
如果对一个负数进行右移,最高位的1不动,结果仍然是一个负数:
int n = -536870912;
int a = n >> 1; // 11110000 00000000 00000000 00000000 = -268435456
int b = n >> 2; // 11111000 00000000 00000000 00000000 = -134217728
int c = n >> 28; // 11111111 11111111 11111111 11111110 = -2
int d = n >> 29; // 11111111 11111111 11111111 11111111 = -1

2.
<<: 左位移
int n = 7; // 00000000 00000000 00000000 00000111 = 7
int a = n << 1; // 00000000 00000000 00000000 00001110 = 14
int b = n << 2; // 00000000 00000000 00000000 00011100 = 28
int c = n << 28; // 01110000 00000000 00000000 00000000 = 1879048192
int d = n << 29; // 11100000 00000000 00000000 00000000 = -536870912

*上面两种不会改变符号位*

3.无符号的右移运算
使用>>>,它的特点是不管符号位,右移后高位总是补0,因此,对一个负数进行>>>右移,它会变成正数,原因是最高位的1变成了0
int n = -536870912;
int a = n >>> 1; // 01110000 00000000 00000000 00000000 = 1879048192
int b = n >>> 2; // 00111000 00000000 00000000 00000000 = 939524096
int c = n >>> 29; // 00000000 00000000 00000000 00000111 = 7
int d = n >>> 31; // 00000000 00000000 00000000 00000001 = 1


对byte和short类型进行移位时,会首先转换为int再进行位移。
仔细观察可发现,左移实际上就是不断地×2,右移实际上就是不断地÷2

位运算

1
2
3
4
5
6
7
8
9
位运算是按位进行与、或、非和异或的运算

&(与): 同1才为1

|(或): 有1则为0

~(非): 01互换

^(异或): 不同才为1
1
2
3
4
5
6
7
8
对两个整数进行位运算,实际上就是按位对齐,然后依次对每一位进行运算。
public class Main {
public static void main(String[] args) {
int i = 167776589; // 00001010 00000000 00010001 01001101
int n = 167776512; // 00001010 00000000 00010001 00000000
System.out.println(i & n); // 167776512
}
}

运算优先级

1
2
3
4
5
6
7
8
9
10
11
12
在Java的计算表达式中,运算优先级从高到低依次是:

()
! ~ ++ --
* / %
+ -
<< >> >>>
&
|
+= -= *= /=

记不住也没关系,只需要加括号就可以保证运算的优先级正确

类型自动提升(整与整)

1
在运算过程中,如果参与运算的两个数类型不一致,那么计算结果为较大类型的整型。例如,short和int计算,结果总是int,原因是short首先自动被转型为int
1
2
3
4
5
6
7
8
public class Main {
public static void main(String[] args) {
short s = 1234;
int i = 123456;
int x = s + i; // s自动转型为int
short y = s + i; // 编译错误!
}
}

强制转换(整与整)

1
2
3
4
也可以将结果强制转型,即将大范围的整数转型为小范围的整数。强制转型使用(类型),例如,将int强制转型为short:

int i = 12345;
short s = (short) i; // 12345
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
要注意,超出范围的强制转型会得到错误的结果,原因是转型时,int的两个高位字节直接被扔掉,仅保留了低位的两个字节

public class Main {
public static void main(String[] args) {
int i1 = 1234567;
short s1 = (short) i1; // -10617
System.out.println(s1);
int i2 = 12345678; //short 32767
short s2 = (short) i2; // 24910
System.out.println(s2);
}
}

-10617
24910

// 因此,强制转型的结果很可能是错的

小结

1
2
3
4
整数运算的结果永远是精确的;
运算结果会自动提升;
可以强制转型,但超出范围的强制转型会得到错误的结果;
应该选择合适范围的整型(int或long),没有必要为了节省内存而使用byte和short进行整数运算。

浮点数计算

1
2
*无法精确表示数值,不能做移位和位运算*
由于浮点数存在运算误差,所以比较两个浮点数是否相等常常会出现错误的结果。正确的比较方法是判断两个浮点数之差的绝对值是否小于一个很小的数
1
2
3
4
5
6
7
8
// 比较x和y是否相等,先计算其差的绝对值:
double r = Math.abs(x - y);
// 再判断绝对值是否足够小:
if (r < 0.00001) {
// 可以认为相等
} else {
// 不相等
}

类型提升

1
如果参与运算的两个数其中一个是整型,那么整型可以自动提升到浮点型

溢出

1
2
3
4
5
6
7
8
9
10
11
12
*整数在除零时编译时不出错,运行时出错*
*浮点数除零不会报错,会返回特殊值:*

NaN:not a number
Infinity:无穷大
-Infinity:负无穷大

double d1 = 0.0 / 0; // NaN
double d2 = 1.0 / 0; // Infinity
double d3 = -1.0 / 0; // -Infinity

这三种特殊值在实际运算中很少碰到,我们只需要了解即可

强制转换(整与浮)

1
可以将浮点数强制转型为整数。在转型时,浮点数的小数部分会被丢掉。如果转型后超过了整型能表示的最大范围,将返回整型的最大值。
1
2
3
4
5
int n1 = (int) 12.3; // 12
int n2 = (int) 12.7; // 12
int n2 = (int) -12.7; // -12
int n3 = (int) (12.7 + 0.5); // 13
int n4 = (int) 1.2e20; // 2147483647
1
2
3
4
5
6
7
8
9
如果要进行四舍五入,可以对浮点数加上0.5再强制转型

public class Main {
public static void main(String[] args) {
double d = 2.6;
int n = (int) (d + 0.5);
System.out.println(n);
}
}

小结

1
2
3
4
浮点数常常无法精确表示,并且浮点数的运算结果可能有误差;
比较两个浮点数通常比较它们的差的绝对值是否小于一个特定值;
整型和浮点型运算时,整型会自动提升为浮点型;
可以将浮点型强制转为整型,但超出范围后将始终返回整型的最大值。

布尔运算

1
2
3
4
5
6
7
对于布尔类型boolean,永远只有true和false两个值。
布尔运算是一种关系运算,包括以下几类:

比较运算符:>,>=,<,<=,==,!=
与运算 &&
或运算 ||
非运算 !
1
2
3
4
5
6
boolean isGreater = 5 > 3; // true
int age = 12;
boolean isZero = age == 0; // false
boolean isNonZero = !isZero; // true
boolean isAdult = age >= 18; // false
boolean isTeenager = age >6 && age <18; // true

关系运算符的优先级

1
2
3
4
5
!
>,>=,<,<=
==,!=
&&
||

短路运算

true&&任意

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
布尔运算的一个重要特点是短路运算。如果一个布尔运算的表达式能提前确定结果,则后续的计算不再执行,直接返回结果。

因为false && x的结果总是false,无论x是true还是false,因此,与运算在确定第一个值为false后,不再继续计算,而是直接返回false

public class Main {
public static void main(String[] args) {
boolean b = 5 < 3;
boolean result = b && (5 / 0 > 0);
System.out.println(result);
}
}

false

如果没有短路运算,&&后面的表达式会由于除数为0而报错,但实际上该语句并未报错,原因在于与运算是短路运算符,提前计算出了结果false

HNRoOx.png

true || 任意

1
2
3
类似的,对于||运算,只要能确定第一个值为true,后续计算也不再进行,而是直接返回true:

boolean result = true || (5 / 0 > 0); // true

三元运算符

1
2
3
Java还提供一个三元运算符b ? x : y,它根据第一个布尔表达式的结果,分别返回后续两个表达式之一的计算结果

b为true返回x; b为false返回y
1
注意到三元运算b ? x : y会首先计算b,如果b为true,则只计算x,否则,只计算y。此外,x和y的类型必须相同,因为返回值不是boolean,而是x和y之一。

小结

1
2
与运算和或运算是短路运算;
三元运算b ? x : y后面的类型必须相同,三元运算也是“短路运算”,只计算x或y。

字符和字符串

1
在Java中,字符和字符串是两个不同的类型

字符

1
2
3
4
5
6
7
8
9
10
11
字符类型char是基本数据类型,它是character的缩写。一个char保存一个Unicode字符

因为Java在内存中总是使用Unicode表示字符,所以,一个英文字符和一个中文字符都用一个char类型表示,它们都占用两个字节。要显示一个字符的Unicode编码,只需将char类型直接赋值给int类型即可:

int n1 = 'A'; // 字母“A”的Unicodde编码是65
int n2 = '中'; // 汉字“中”的Unicode编码是20013
还可以直接用转义字符\u+Unicode编码来表示一个字符:

// 注意是十六进制:
char c3 = '\u0041'; // 'A',因为十六进制0041 = 十进制65
char c4 = '\u4e2d'; // '中',因为十六进制4e2d = 十进制20013

字符串

1
2
3
4
5
6
和char类型不同,字符串类型String是引用类型,我们用双引号"..."表示字符串。一个字符串可以存储0个到任意个字符:

String s = ""; // 空字符串,包含0个字符
String s1 = "A"; // 包含一个字符
String s2 = "ABC"; // 包含3个字符
String s3 = "中文 ABC"; // 包含6个字符,其中有一个空格
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
因为字符串使用双引号"..."表示开始和结束,那如果字符串本身恰好包含一个"字符怎么表示?例如,"abc"xyz",编译器就无法判断中间的引号究竟是字符串的一部分还是表示字符串结束。这个时候,我们需要借助转义字符\:

String s = "abc\"xyz"; // 包含7个字符: a, b, c, ", x, y, z
因为\是转义字符,所以,两个\\表示一个\字符:

String s = "abc\\xyz"; // 包含7个字符: a, b, c, \, x, y, z
常见的转义字符包括:

\" 表示字符"
\' 表示字符'
\\ 表示字符\
\n 表示换行符
\r 表示回车符
\t 表示Tab
\u#### 表示一个Unicode编码的字符
例如:

String s = "ABC\n\u4e2d\u6587"; // 包含6个字符: A, B, C, 换行符, 中, 文

字符串连接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Java的编译器对字符串做了特殊照顾,可以使用+连接任意字符串和其他数据类型,这样极大地方便了字符串的处理。例如:

public class Main {
public static void main(String[] args) {
String s1 = "Hello";
String s2 = "world";
String s = s1 + " " + s2 + "!";
System.out.println(s);
}
}

如果用+连接字符串和其他数据类型,会将其他数据类型先自动转型为字符串,再连接:
public class Main {
public static void main(String[] args) {
int age = 25;
String s = "age is " + age;
System.out.println(s);
}
}

小结

1
2
3
4
5
6
7
8
9
10
11
12
public class Main {
public static void main(String[] args) {
// 请将下面一组int值视为字符的Unicode码,把它们拼成一个字符串:
int a = 72;
int b = 105;
int c = 65281;
// FIXME
String s = ""+'\u0048' +'\u0069' + '\uff01';
System.out.println(s);

}
}
1
2
3
4
5
6
7
8
9
10
public class array {    
public static void main(String[] args){
int a = 72;
int b = 105;
int c = 65281;
// FIXME:
String s = ""+(char)a + (char)b + (char)c;
System.out.println(s);
}
}

多行字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
如果我们要表示多行字符串,使用+号连接会非常不方便:

String s = "first line \n"
+ "second line \n"
+ "end";
从Java 13开始,字符串可以用"""..."""表示多行字符串(Text Blocks)了
public class Main {
public static void main(String[] args) {
String s = """
SELECT * FROM
users
WHERE id > 100
ORDER BY name DESC
""";
System.out.println(s);
}
}
1
2
3
4
5
6
7
上述多行字符串实际上是5行,在最后一个DESC后面还有一个\n。如果我们不想在字符串末尾加一个\n,就需要这么写:

String s = """
SELECT * FROM
users
WHERE id > 100
ORDER BY name DESC""";
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
还需要注意到,多行字符串前面共同的空格会被去掉,即:

String s = """
...........SELECT * FROM
........... users
...........WHERE id > 100
...........ORDER BY name DESC
...........""";
用.标注的空格都会被去掉。

如果多行字符串的排版不规则,那么,去掉的空格就会变成这样:

String s = """
......... SELECT * FROM
......... users
.........WHERE id > 100
......... ORDER BY name DESC
......... """;
即总是以最短的行首空格为基准。

不可变特性

HUnuM8.md.png

空值

1
2
3
4
5
6
7
引用类型的变量可以指向一个空值null,它表示不存在,即该变量不指向任何对象。例如:

String s1 = null; // s1是null
String s2; // 没有赋初值值,s2也是null
String s3 = s1; // s3也是null
String s4 = ""; // s4指向空字符串,不是null
注意要区分空值null和空字符串"",空字符串是一个有效的字符串对象,它不等于null

小结

1
2
3
4
Java的字符类型char是基本类型,字符串类型String是引用类型;
基本类型的变量是“持有”某个数值,引用类型的变量是“指向”某个对象;
引用类型的变量可以是空值null;
要区分空值null和空字符串""

本文标题:廖雪峰Java教程笔记-Java程序基础 变量和数据类型

文章作者:TTYONG

发布时间:2023年03月29日 - 22:03

最后更新:2023年03月30日 - 01:03

原始链接:http://tianyong.fun/%E5%BB%96%E9%9B%AA%E5%B3%B0Java%E6%95%99%E7%A8%8B%E7%AC%94%E8%AE%B0-Java%E7%A8%8B%E5%BA%8F%E5%9F%BA%E7%A1%80-%E5%8F%98%E9%87%8F%E5%92%8C%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B.html

许可协议: 转载请保留原文链接及作者。

多少都是爱
0%