面向过程篇
前面我们已经认识了Java语言的相关特性,并且已经成功配置好了开发环境,从这节课开始,我们就可以正式进入到Java语言的学习当中了。Java语言是一门面向对象的语言,但是在面向对象之前,我们还得先学会如何面向过程编程。
Java程序基础
首先我们还是从最基本的Java程序基础开始讲解。
程序代码基本结构
还记得我们之前使用的示例代码吗?
public class Main {
public static void main(String[] args) {
System.out.println("Hello World!");
}
}
这段代码要实现的功能很简单,就是将 Hello World 输出到控制台就行。
由于我们还没有学习到类的相关性质,所以在第二章之前,各位小伙伴直接记住固定模式即可,首先我们创建的源文件名称需要为Main.java
然后编写的代码第一行:
public class Main {
}
注意需要区分大小写,Java语言严格区分大小写,如果我们没有按照规则来编写,那么就会出现红色波浪线报错:
只要源代码中存在报错的地方,就无法正常完成编译得到二进制文件,会提示构建失败:
注意最后还有一个花括号,并且此花括号是成对出现的,一一对应。
所以说各位小伙伴在编写代码时一定要注意大小写。然后第二行,准确的说是最外层花括号内部就是:
public static void main(String[] args) {
}
这是我们整个Java程序的入口点,我们称为主方法(如果你学习过C肯定能够联想到主函数,只不过Java中不叫函数,叫方法)最后也会有一个花括号成对出现,而在主方法的花括号中编写的代码,就是按照从上往下的顺序依次执行的。
比如我们之前编写的:
System.out.println("Hello World!");
这段代码的意思就是将双引号括起来的内容(字符串,我们会在后面进行讲解)输出(打印)到控制台上,可以看到最后还加上了一个;
符号,表示这一句代码结束。我们每一段代码结束时都需要加上一个分号表示这一句的结束,就像我们写作文一样。
比如下面的代码,我们就可以实现先打印Hello World!,然后再打印YYDS!到控制台。
public class Main {
public static void main(String[] args) {
System.out.println("Hello World!");
System.out.println("YYDS!");
}
}
效果如下:
如果两段代码没有加上分号分割,那么编译器会认为这两段代码是同一句代码中的,即使出现换行或者是空格:
这里IDEA很聪明,会提醒我们这里少加了分号,所以说这个IDEA能够在初期尽可能地帮助新手。
再比如下面的代码:
这里我们尝试在中途换行和添加空格,因为没有添加分号,所以说编译器依然会认为是一行代码,因此编译不会出现错误,能够正常通过。当然,为了代码写得工整和规范,我们一般不会随意进行换行编写或者是添加没必要的空格。
同样的,如果添加了分号,即使在同一行,也会被认为是两句代码:
如果在同一行就是从左往右的顺序,得到的结果跟上面是一样的。
注释
我们在编写代码时,可能有些时候需要标记一下这段代码表示什么意思:
但是如果直接写上文字的话,会导致编译不通过,因为这段文字也会被认为是程序的一部分。
这种情况,我们就可以告诉编译器,这段文字是我们做的笔记,并不是程序的一部分,那么要怎么告诉编译器这不是代码呢?很简单,我们只需要在前面加上双斜杠就可以了:
添加双斜杠之后(自动变成了灰色),后续的文本内容只要没有发生换行,那么都会被认为是一段注释,并不属于程序,在编译时会被直接忽略,之后这段注释也不会存在于程序中。但是一旦发生换行那就不行了:
那要是此时注释很多,一行写不完,我们想要编写很多行的注释呢?我们可以使用多行注释标记:
public class Main {
public static void main(String[] args) {
/*
这里面的内容
无论多少行
都可以
*/
System.out.println("Hello World!");
}
}
多行可以使用/*
和*/
的组合来囊括需要编写的注释内容。
当然还有一种方式就是使用/**
来进行更加详细的文档注释:
这种注释可以用来自动生成文档,当我们鼠标移动到Main上时,会显示相关的信息,我们可以自由添加一些特殊的注释,比如作者、时间等信息,也可以是普通的文字信息。
变量与常量
我们的程序不可能永远都只进行上面那样的简单打印操作,有些时候可能需要计算某些数据,此时我们就需要用到变量了。
那么,什么是变量呢?我们在数学中其实已经学习过变量了:
变量,指值可以变的量。变量以非数字的符号来表达,一般用拉丁字母。变量的用处在于能一般化描述指令的方式。结果只能使用真实的值,指令只能应用于某些情况下。变量能够作为某特定种类的值中任何一个的保留器。
比如一个公式 x 2 + 6 = 22 x^2 + 6 = 22 x2+6=22 此时x
就是一个变量,变量往往代表着某个值,比如这里的x
就代表的是4这个值。在Java中,我们也可以让变量去代表一个具体的值,并且变量的值是可以发生变化的。
要声明一个变量,我们需要使用以下格式:
[数据类型] [变量名称];
这里的数据类型我们会在下节课开始逐步讲解,比如整数就是int
类型,不同类型的变量可以存储不同的类型的值。后面的变量名称顾名思义,就像x
一样,这个名称我们可以随便起一个,但是注意要满足以下要求:
- 标识符可以由大小写字母、数字、下划线(_)和美元符号($)组成,但是不能以数字开头。
- 变量不能重复定义,大小写敏感,比如A和a就是两个不同的变量。
- 不能有空格、@、#、+、-、/ 等符号。
- 应该使用有意义的名称,达到见名知意的目的(一般我们采用英文单词),最好以小写字母开头。
- 不可以是 true 和 false。
- 不能与Java语言的关键字或是基本数据类型重名,关键字列表如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WM2GoXHs-1663474327557)(/Users/nagocoler/Library/Application Support/typora-user-images/image-20220916224014438.png)]
当然各位小伙伴没必要刻意去进行记忆,我们会在学习的过程中逐步认识到这些关键字。新手要辨别一个单词是否为关键字,只需要通过IDEA的高亮颜色进行区分即可,比如:
深色模式下,关键字会高亮为橙色,浅色模式下会高亮为深蓝色,普通的代码都是正常的灰白色。
比如现在我们想要定义一个变量a
,那么就可以这样编写:
public class Main {
public static void main(String[] args) {
int a; //声明一个整数类型变量a
}
}
但是这个变量一开始没有任何值,比如现在我们要让这个变量表示10,那么就可以将10赋值给这个变量:
public static void main(String[] args) {
int a = 10; //直接在定义变量后面添加 = 10,表示这个变量的初始值为10,这里的10就是一个常量数字
}
或者我们可以在使用时再对其进行赋值:
public static void main(String[] args) {
int a;
a = 10; //使用时再赋值也可以
}
是不是感觉跟数学差不多?这种写法对于我们人来说,实际上是很好理解的,意思表达很清晰。
我们可以一次性定义多个变量,比如现在我们想定义两个int
类型的变量:
public static void main(String[] args) {
int a, b; //定义变量a和变量b,中间使用逗号隔开就行了
}
或者两个变量单独声明也可以:
public static void main(String[] args) {
int a; //分两句进行声明
int b;
}
为了更直观地查看变量的值,我们可以直接将变量的值也给打印到控制台:
public static void main(String[] args) {
int a = 666;
System.out.println(a); //之前我们在小括号写的是"",现在我们直接将变量给进去就可以打印变量的值了
System.out.println(888); //甚至直接输出一个常量值都可以
}
得到结果:
变量的值也可以在中途进行修改:
public static void main(String[] args) {
int a = 666;
a = 777;
System.out.println(a); //这里打印得到的值就是777了
}
变量的值也可以直接指定为其他变量的值:
public static void main(String[] args) {
int a = 10;
int b = a; //直接让b等于a,那么a的值就会给到b
System.out.println(b); //这里输出的就是10了
}
我们还可以让变量与数值之间做加减法(运算符会在后面详细介绍):
public static void main(String[] args) {
int a = 9; //a初始值为9
a = a + 1; //a = a + 1也就是将a+1的结果赋值给a,跟数学是一样的,很好理解对吧
System.out.println(a); //最后得到的结果就是10了
}
有时候我们希望变量的值一直保持不变,我们就可以将其指定为常量,这里我们介绍Java中第一个需要认识的关键字:
public static void main(String[] args) {
final int a = 666; //在变量前面添加final关键字,表示这是一个常量
a = 777; //常量的值不允许发生修改
}
编译时出现:
常量的值只有第一次赋值可以修改,其他任何情况下都不行:
public static void main(String[] args) {
final int a;
a = 777; //第一次赋值
}
至此,Java的基础语法部分介绍完毕,下一部分我们将开始介绍Java中的几大基本数据类型。
基本数据类型
我们的程序中可能需要表示各种各样的数据,比如整数、小数、字符等等,这一部分我们将探索Java中的八大基本数据类型。只不过在开始之前,我们还需要先补充一点简单的计算机小知识。
计算机中的二进制表示
在计算机中,所有的内容都是二进制形式表示。十进制是以10为进位,如9+1=10;二进制则是满2进位(因为我们的计算机是电子的,电平信号只有高位和低位,你也可以暂且理解为通电和不通电,高电平代表1,低电平代表0,由于只有0和1,因此只能使用2进制表示我们的数字!)比如1+1=10=2^1+0,一个位也叫一个bit,8个bit称为1字节,16个bit称为一个字,32个bit称为一个双字,64个bit称为一个四字,我们一般采用字节来描述数据大小。
注意这里的bit跟我们生活中的网速MB/s是不一样的,小b代表的是bit,大B代表的是Byte字节(8bit = 1Byte字节),所以说我们办理宽带的时候,100Mbps这里的b是小写的,所以说实际的网速就是100/8 = 12.5 MB/s了。
十进制的7 -> 在二进制中为 111 = 2^2 + 2^1 + 2^0
现在有4个bit位,最大能够表示多大的数字呢?
- 最小:0000 => 0
- 最大:1111 => 23+22+21+20 => 8 + 4 + 2 + 1 = 15
在Java中,无论是小数还是整数,他们都要带有符号(和C语言不同,C语言有无符号数)所以,首位就作为我们的符号位,还是以4个bit为例,首位现在作为符号位(1代表负数,0代表正数):
- 最小:1111 => -(22+21+2^0) => -7
- 最大:0111 => +(22+21+2^0) => +7 => 7
现在,我们4bit能够表示的范围变为了-7~+7,这样的表示方式称为原码。虽然原码表示简单,但是原码在做加减法的时候,很麻烦!以4bit位为例:
1+(-1) = 0001 + 1001 = 怎么让计算机去计算?(虽然我们知道该去怎么算,但是计算机不知道!)
我们得创造一种更好的表示方式!于是我们引入了反码:
- 正数的反码是其本身
- 负数的反码是在其原码的基础上, 符号位不变,其余各个位取反
经过上面的定义,我们再来进行加减法:
1+(-1) = 0001 + 1110 = 1111 => -0 (直接相加,这样就简单多了!)
思考:1111代表-0,0000代表+0,在我们实数的范围内,0有正负之分吗?0既不是正数也不是负数,那么显然这样的表示依然不够合理!根据上面的问题,我们引入了最终的解决方案,那就是补码,定义如下:
- 正数的补码就是其本身 (不变!)
- 负数的补码是在其原码的基础上, 符号位不变, 其余各位取反, 最后+1(即在反码的基础上+1,此时1000表示-8)
- 对补码再求一次补码就可得该补码对应的原码。
比如-7原码为1111,反码为1000,补码就是1001了,-6原码为1110,反码为1001,补码就是1010。所以在补码下,原本的1000就作为新增的最小值-8存在。
所以现在就已经能够想通,-0已经被消除了!我们再来看上面的运算:
1+(-1) = 0001 + 1111 = (1)0000 => +0 (现在无论你怎么算,也不会有-0了!)
所以现在,1111代表的不再是-0,而是-1,相应的,由于消除-0,负数多出来一个可以表示的数(1000拿去表示-8了),那么此时4bit位能够表示的范围是:-8~+7(Java使用的就是补码!)在了解了计算机底层的数据表示形式之后,我们再来学习这些基本数据类型就会很轻松了。
整数类形
整数类型是最容易理解的类型!既然我们知道了计算机中的二进制数字是如何表示的,那么我们就可以很轻松的以二进制的形式来表达我们十进制的内容了。
在Java中,整数类型包括以下几个:
- byte 字节型 (8个bit,也就是1个字节)范围:-128~+127
- short 短整形(16个bit,也就是2个字节)范围:-32768~+32767
- int 整形(32个bit,也就是4个字节)最常用的类型:-2147483648 ~ +2147483647
- long 长整形(64个bit,也就是8个字节)范围:-9223372036854775808 ~ +9223372036854775807
这里我们来使用一下,其实这几种变量都可以正常表示整数:
public static void main(String[] args) {
short a = 10;
System.out.println(a);
}
因为都可以表示整数,所以说我们可以将小的整数类型值传递给大的整数类型:
public static void main(String[] args) {
short a = 10;
int b = a; //小的类型可以直接传递给表示范围更大的类型
System.out.println(b);
}
反之会出现报错:
这是由于我们在将小的整数类型传递给大的整数类型时发生了隐式类型转换,只要是从存储范围小的类型到存储范围大的类型,都支持隐式类型转换,它可以自动将某种类型的值,转换为另一种类型,比如上面就是将short类型的值转换为了int类型的值。
隐式类型转换不仅可以发生在整数之间,也可以是其他基本数据类型之间,我们后面会逐步介绍。
实际上我们在为变量赋一个常量数值时,也发生了隐式类型转换,比如:
public static void main(String[] args) {
byte b = 10; //这里的整数常量10,实际上默认情况下是int类型,但是由于正好在对应类型可以表示的范围内,所以说直接转换为了byte类型的值
}
由于直接编写的整数常量值默认为int
,这里需要特别注意一下,比如下面这种情况:
按照long
类型的规定,实际上是可以表示这么大的数字的,但是为什么这里报错了呢?这是因为我们直接在代码中写的常量数字,默认情况下就是int
类型,这么大肯定是表示不下的,如果需要将其表示为一个long类型的常量数字,那么需要在后面添加大写或是小写的L
才可以。
public static void main(String[] args) {
long a = 922337203685477580L; //这样就可以正常编译通过了
}
当然,针对于这种很长的数字,为了提升辨识度,我们可以使用下划线分割每一位:
public static void main(String[] args) {
int a = 1_000_000; //当然这里依然表示的是1000000,没什么区别,但是辨识度会更高
}
我们也可以以8进制或是16进制表示一个常量值:
public static void main(String[] args) {
System.out.println(0xA);
System.out.println(012);
}
- **十六进制:**以
0x
开头的都是十六进制表示法,十六进制满16进一,但是由于我们的数学只提供了0-9这十个数字,10、11、12…15该如何表示呢,我们使用英文字母A按照顺序开始表示,A表示10、B表示11…F表示15。比如上面的0xA实际上就是我们十进制中的10。 - **八进制:**以0开头的都是八进制表示法,八进制就是满8进一,所以说只能使用0-7这几个数字,比如上面的012实际上就是十进制的10。
我们最后再来看一个问题:
public static void main(String[] args) {
int a = 2147483647; //int最大值
a = a + 1; //继续加
System.out.println(a);
}
此时a的值已经来到了int
类型所能表示的最大值了,那么如果此时再继续+1
,各位小伙伴觉得会发生什么?可以看到结果很奇怪:
什么情况???怎么正数加1还变成负数了?请各位小伙伴回想一下我们之前讲解的原码、反码和补码。
我们先来看看,当int为最大值时,二进制表示形式为什么:
- 2147483647 = 01111111 11111111 11111111 11111111(第一个是符号位0,其他的全部为1,就是正数的最大值)
那么此时如果加1,会进位成:
- 10000000 00000000 00000000 00000000
各位想一想,符号位为1,那么此时表示的不就是一个负数了吗?我们回想一下负数的补码表示规则,瞬间就能明白了,这不就是补码形式下的最小值了吗?
所以说最后的结果就是int
类型的最小值:-2147483648,是不是感觉了解底层原理会更容易理解这是为什么。
浮点类型
前面我们介绍了整数类型,我们接着来看看浮点类型,在Java中也可以轻松地使用小数。
首先来看看Java中的小数类型包含哪些:
- float 单精度浮点型 (32bit,4字节)
- double 双精度浮点型(64bit,8字节)
那么小数在计算机中又是如何存放的呢?
根据国际标准 IEEE 754,任意一个二进制浮点数 V 可以表示成下面的形式:
V = ( − 1 ) S × M × 2 E V = (-1)^S \times M \times 2^E V=(−1)S×M×2E
- ( − 1 ) S (-1)^S (−1)S 表示符号位,当 S=0,V 为正数;当 S=1,V 为负数。
- M 表示有效数字,大于等于 1,小于 2,但整数部分的 1 不变,因此可以省略。