参考学习资料:
https://mp.weixin.qq.com/s/nWC8e2GfvFq7W4MEdg-OPQ
https://www.bilibili.com/video/BV1fh411y7R8?vd_source=daa349a1e72350b1581b6e1dc635171f
https://c.runoob.com/compile/10/
https://www.liaoxuefeng.com/wiki/1252599548343744/1255878730977024
1、Java程序基本结构
1)类名要求:
- 类名必须以英文字母开头,后接字母,数字和下划线的组合
- 习惯以大写字母开头
注意到public是访问修饰符,表示该class是公开的。
不写public,也能正确编译,但是这个类将无法从命令行执行。
2)方法
在class内部,可以定义若干方法(method):
public class Hello {
public static void main(String[] args) { // 方法名是main
// 方法代码...
} // 方法定义结束
}
方法定义了一组执行语句,方法内部的代码将会被依次顺序执行。
这里的方法名是main,返回值是void,表示没有任何返回值。
我们注意到public除了可以修饰class外,也可以修饰方法。而关键字static是另一个修饰符,它表示静态方法,后面我们会讲解方法的类型,目前,我们只需要知道,Java入口程序规定的方法必须是静态方法,方法名必须为main,括号内的参数必须是String数组。
方法名也有命名规则,命名和class一样,但是首字母小写:
3)注释
Java有3种注释,第一种是单行注释,以双斜线开头,直到这一行的结尾结束:
// 这是注释...
而多行注释以/* 星号开头,以 * /结束,可以有多行:
/*
这是注释
blablabla...
这也是注释
*/
还有一种特殊的多行注释,以/ * *开头,以 * /结束,如果有多行,每行通常以星号开头:
/**
* 可以用来自动创建文档的注释
*
* @auther liaoxuefeng
*/
public class Hello {
public static void main(String[] args) {
System.out.println("Hello, world!");
}
}
这种特殊的多行注释需要写在类和方法的定义处,可以用于自动创建文档。
Java程序对格式没有明确的要求,多几个空格或者回车不影响程序的正确性,但是我们要养成良好的编程习惯,注意遵守Java社区约定的编码格式。
2、变量和数据类型
1) 变量
在Java中,变量分为两种:基本类型的变量和引用类型的变量。
我们先讨论基本类型的变量。
在Java中,变量必须先定义后使用,在定义变量的时候,可以给它一个初始值。例如:
int x = 1;
上述语句定义了一个整型int类型的变量,名称为x,初始值为1。
不写初始值,就相当于给它指定了默认值。默认值总是0。
变量不但可以重新赋值,还可以赋值给其他变量。
2)基本数据类型
基本数据类型是CPU可以直接进行运算的类型。Java定义了以下几种基本数据类型:
- 整数类型:byte,short,int,long
- 浮点数类型:float,double
- 字符类型:char
- 布尔类型:boolean
Java定义的这些基本数据类型有什么区别呢?要了解这些区别,我们就必须简单了解一下计算机内存的基本结构。
计算机内存的最小存储单元是字节(byte),一个字节就是一个8位二进制数,即8个bit。它的二进制表示范围从00000000 ~ 11111111,换算成十进制是0 ~ 255,换算成十六进制是00 ~ ff。
不同的数据类型占用的字节数不一样。我们看一下Java基本数据类型占用的字节数:
byte恰好就是一个字节,而long和double需要8个字节。
3)整型
对于整型类型,Java只定义了带符号的整型,因此,最高位的bit表示符号位(0表示正数,1表示负数)。各种整型能表示的最大范围如下:
- byte:-128 ~ 127
- short: -32768 ~ 32767
- int: -2147483648 ~ 2147483647
- long: -9223372036854775808 ~ 9223372036854775807
特别注意:同一个数的不同进制的表示是完全相同的,例如15=0xf=0b1111。
4)浮点型
5)布尔型
Java语言对布尔类型的存储并没有做规定,因为理论上存储布尔类型只需要1 bit,但是通常JVM内部会把boolean表示为4字节整数。
6)字符类型
字符类型char表示一个字符。Java的char类型除了可表示标准的ASCII外,还可以表示一个Unicode字符:
注意char类型使用单引号’,且仅有一个字符,要和双引号"的字符串类型区分开。
7) 引用类型
除了上述基本类型的变量,剩下的都是引用类型。例如,引用类型最常用的就是String字符串:
String s = "hello";
引用类型的变量类似于C语言的指针,它内部存储一个“地址”,指向某个对象在内存的位置,后续我们介绍类的概念时会详细讨论。
8) 常量
定义变量的时候,如果加上final修饰符,这个变量就变成了常量:
final double PI = 3.14; // PI是一个常量
double r = 5.0;
double area = PI * r * r;
PI = 300; // compile error!
常量在定义时进行初始化后就不可再次赋值,再次赋值会导致编译错误。
常量的作用是用有意义的变量名来避免魔术数字(Magic number),例如,不要在代码中到处写3.14,而是定义一个常量。如果将来需要提高计算精度,我们只需要在常量的定义处修改,例如,改成3.1416,而不必在所有地方替换3.14。
根据习惯,常量名通常全部大写。
9) var关键字
因此,使用var定义变量,仅仅是少写了变量类型而已。
10) 变量的作用范围
只要正确地嵌套这些{ },编译器就能识别出语句块的开始和结束。而在语句块中定义的变量,它有一个作用域,就是从定义处开始,到语句块结束。超出了作用域引用这些变量,编译器会报错。
{
...
int i = 0; // 变量i从这里开始定义
...
{
...
int x = 1; // 变量x从这里开始定义
...
{
...
String s = "hello"; // 变量s从这里开始定义
...
} // 变量s作用域到此结束
...
// 注意,这是一个新的变量s,它和上面的变量同名,
// 但是因为作用域不同,它们是两个不同的变量:
String s = "hi";
...
} // 变量x和s作用域到此结束
...
} // 变量i作用域到此结束
定义变量时,要遵循作用域最小化原则,尽量将变量定义在尽可能小的作用域,并且,不要重复使用变量名。
11)小结
Java提供了两种变量类型:基本类型和引用类型
基本类型包括整型,浮点型,布尔型,字符型。
变量可重新赋值,等号是赋值语句,不是数学意义的等号。
常量在初始化后不可重新赋值,使用常量便于理解程序意图。
3、整数运算
1)整数运算
整数的数值表示不但是精确的,而且整数运算永远是精确的,即使是除法也是精确的,因为两个整数相除只能得到结果的整数部分:
特别注意:整数的除法对于除数为0时运行时将报错,但编译不会报错。
2)溢出
要特别注意,整数由于存在范围限制,如果计算结果超出了范围,就会产生溢出,而溢出不会出错,却会得到一个奇怪的结果:
public class Main {
public static void main(String[] args) {
int x = 2147483640;
int y = 15;
int sum = x + y;
System.out.println(sum); // -2147483641
}
}
还有一种简写的运算符,即+=,-=,*=,/=
3) 自增/自减
Java还提供了++运算和- -运算,它们可以对一个整数进行加1和减1的操作
注意++写在前面和后面计算结果是不同的,++n表示先加1再引用n,n++表示先引用n再加1。不建议把++运算混入到常规运算中,容易自己把自己搞懵了。
4) 移位运算
还有一种无符号的右移运算,使用>>>,它的特点是不管符号位,右移后高位总是补0
对byte和short类型进行移位时,会首先转换为int再进行位移。
仔细观察可发现,左移实际上就是不断地×2,右移实际上就是不断地÷2。
5) 位运算
位运算是按位进行与、或、非和异或的运算。
对两个整数进行位运算,实际上就是按位对齐,然后依次对每一位进行运算
上述按位与运算实际上可以看作两个整数表示的IP地址10.0.17.77和10.0.17.0,通过与运算,可以快速判断一个IP是否在给定的网段内。
6) 运算优先级
7) 类型自动提升与强制转型
因此,强制转型的结果很可能是错的。
4、浮点数运算
1)浮点数运算
浮点数运算和整数运算相比,只能进行加减乘除这些数值计算,不能做位运算和移位运算。
在计算机中,浮点数虽然表示的范围大,但是,浮点数有个非常重要的特点,就是浮点数常常无法精确表示。
举个栗子:
浮点数0.1在计算机中就无法精确表示,因为十进制的0.1换算成二进制是一个无限循环小数,很显然,无论使用float还是double,都只能存储一个0.1的近似值。但是,0.5这个浮点数又可以精确地表示。
因为浮点数常常无法精确表示,因此,浮点数运算会产生误差:
由于浮点数存在运算误差,所以比较两个浮点数是否相等常常会出现错误的结果。正确的比较方法是判断两个浮点数之差的绝对值是否小于一个很小的数:
浮点数在内存的表示方法和整数比更加复杂。Java的浮点数完全遵循IEEE-754标准,这也是绝大多数计算机平台都支持的浮点数标准表示方法。
2)类型提升
3)溢出
整数运算在除数为0时会报错,而浮点数运算在除数为0时,不会报错,但会返回几个特殊值:
注意:浮点数在除数为0的时候不会报错,会返回一些特殊值。
4)强制转型
可以将浮点数强制转型为整数。在转型时,浮点数的小数部分会被丢掉。如果转型后超过了整型能表示的最大范围,将返回整型的最大值。例如:
如果要进行四舍五入,可以对浮点数加上0.5再强制转型:
5) 小结
-
浮点数常常无法精确表示,并且浮点数的运算结果可能有误差;
-
比较两个浮点数通常比较它们的差的绝对值是否小于一个特定值;
-
整型和浮点型运算时,整型会自动提升为浮点型;
-
可以将浮点型强制转为整型,但超出范围后将始终返回整型的最大值。
5、布尔运算
1)布尔运算
对于布尔类型boolean,永远只有true和false两个值。
2)短路运算
布尔运算的一个重要特点是短路运算。如果一个布尔运算的表达式能提前确定结果,则后续的计算不再执行,直接返回结果。
因为false && x的结果总是false,无论x是true还是false,因此,与运算在确定第一个值为false后,不再继续计算,而是直接返回false。
我们考察以下代码:
如果没有短路运算,&&后面的表达式会由于除数为0而报错,但实际上该语句并未报错,原因在于与运算是短路运算符,提前计算出了结果false。
如果变量b的值为true,则表达式变为true && (5 / 0 > 0)。因为无法进行短路运算,该表达式必定会由于除数为0而报错,可以自行测试。
3)三元运算符
Java还提供一个三元运算符b ? x : y,它根据第一个布尔表达式的结果,分别返回后续两个表达式之一的计算结果。示例:
上述语句的意思是,判断n >= 0是否成立,如果为true,则返回n,否则返回-n。这实际上是一个求绝对值的表达式。
注意到三元运算b ? x : y会首先计算b,如果b为true,则只计算x,否则,只计算y。此外,x和y的类型必须相同,因为返回值不是boolean,而是x和y之一。
**注意:**三元运算符中x和y的数据类型必须相同
4)小结
-
与运算和或运算是短路运算;
-
三元运算b ? x : y后面的类型必须相同,三元运算也是“短路运算”,只计算x或y。
6、字符和字符串
在Java中,字符和字符串是两个不同的类型。
1)字符类型
字符类型char是基本数据类型,它是character的缩写。一个char保存一个Unicode字符:
因为Java在内存中总是使用Unicode表示字符,所以,一个英文字符和一个中文字符都用一个char类型表示,它们都占用两个字节。要显示一个字符的Unicode编码,只需将char类型直接赋值给int类型即可:
2)字符串类型
和char类型不同,字符串类型String是引用类型,我们用双引号"…"表示字符串。一个字符串可以存储0个到任意个字符:
因为字符串使用双引号"…"表示开始和结束,那如果字符串本身恰好包含一个"字符怎么表示?例如,“abc"xyz”,编译器就无法判断中间的引号究竟是字符串的一部分还是表示字符串结束。这个时候,**我们需要借助转义字符**:
3) 字符串连接
Java的编译器对字符串做了特殊照顾,可以使用+连接任意字符串和其他数据类型,这样极大地方便了字符串的处理。例如:
如果用+连接字符串和其他数据类型,会将其他数据类型先自动转型为字符串,再连接:
4) 多行字符串
从Java 13开始,字符串可以用"“”…“”"表示多行字符串(Text Blocks)了
去掉的空格总是以最短的行尾空格为基准。
5)不可变特性
观察执行结果,难道字符串s变了吗?其实变的不是字符串,而是变量s的“指向”。
例子:
6) 空值null
引用类型的变量可以指向一个空值null,它表示不存在,即该变量不指向任何对象。例如:
注意要区分空值null和空字符串"",空字符串是一个有效的字符串对象,它不等于null。
空值null表示不指向任何对象
7)练习
请将一组int值视为字符的Unicode编码,然后将它们拼成一个字符串:
8)小结
-
Java的字符类型char是基本类型,字符串类型String是引用类型;
-
基本类型的变量是“持有”某个数值,引用类型的变量是“指向”某个对象;
-
引用类型的变量可以是空值null;
-
要区分空值null和空字符串""。
7、数组类型
1)数组
如果我们有一组类型相同的变量,例如,5位同学的成绩,可以这么写:
int[] ns = new int[5];
定义一个数组类型的变量,使用数组类型“类型[]”,例如,int[]。和单个基本类型变量不同,数组变量初始化必须使用new int[5]表示创建一个可容纳5个int元素的数组。
Java的数组有几个特点:(大小不变和有默认值)
- 数组所有元素初始化为默认值,整型都是0,浮点型是0.0,布尔型是false;
- 数组一旦创建后,大小就不可改变。
要访问数组中的某一个元素,需要使用索引。数组索引从0开始,例如,5个元素的数组,索引范围是0~4。
可以修改数组中的某一个元素,使用赋值语句,例如,ns[1] = 79;。
可以用数组变量.length获取数组大小:
数组变量.length获取数组大小
int[] ns = { 68, 79, 91, 85, 62 };
注意数组是引用类型,并且数组大小不可变。我们观察下面的代码:
数组大小变了吗?看上去好像是变了,但其实根本没变。
数组是引用类型
2)字符串数组
如果数组元素不是基本类型,而是一个引用类型,那么,修改数组元素会有哪些不同?
字符串是引用类型,因此我们先定义一个字符串数组:
3)小结
-
数组是同一数据类型的集合,数组一旦创建后,大小就不可变;
-
可以通过索引访问数组元素,但索引超出范围将报错;
-
数组元素可以是值类型(如int)或引用类型(如String),但数组本身是引用类型;