首先,感谢杜老师的讲解,感谢动力节点,真的受益匪浅!真的是口口相传的Java黄埔军校。
笔记根据老杜《动力节点》视频进行编写,视频地址:动力节点Java零基础视频(上)
笔记有写的不好的地方,恳请在评论区指正批评,谢谢!
第二章 Java基础语法
2.1 标识符
2.1.1 概念
- 什么是标识符?
- 在Java中,标识符是用来给变量、方法、类和包等命名的字符序列。
- 标识符的长度没有限制,但是建议使用有意义的、简洁的标识符,以提高代码的可读性和可维护性。
- 标识符可以标识什么
- 变量名、方法名类名、接口名、枚举名、注解名、包名、常量名……
2.1.2 标识符命名规则
- 标识符可以由字母、数字、下划线(_)和美元符号($)组成,不能含有其他符号。(java支持全球所有语言,所以这里的 字母 指的是任何一个国家的文字都可以)
- 标识符不能以数字开头。
- 标识符不能是Java中的关键字,如public、class、void等。
- 标识符是区分大小写的,即Foo和foo是两个不同的标识符。
- 标识符的长度没有限制,但是Java建议使用有意义的、简短的标识符。
2.1.3 标识符命名规范
- 见名知意
- 驼峰式命名方式
- 类名、接口名、枚举、注解:首字母大写,后面每个单词首字母大写。(StudentService,UserService)
- 变量名和方法名:首字母小写,后面每个单词首字母大写。(doSome,doOther)
- 常量名:全部大写,每个单词用下划线连接。(LOGIN_SUCCESS,SYSTEM_ERROR)
- 包名:全部小写
2.2 关键字
- 什么是关键字
- Java关键字是Java编程语言中预定义的具有特殊含义的单词,这些单词不能被用作标识符,而是在语法中特定的用法和限制。
- Java关键字有哪些
- Java关键字都是小写的。
- abstract, assert, boolean, break, byte, case, catch, char, class, continue, default, do, double, else, enum, extends, final, finally, float, for, if, implements, import, instanceof, int, interface, long, native, new, package, private, protected, public, return, short, static, strictfp, super, switch, synchronized, this, throw, throws, transient, try, void, volatile, while
- Java保留字:goto,const
2.3 字面量
字面量指的是在程序中直接使用的数据,字面量是Java中最基本的表达式,不需要进行计算或转换,直接使用即可。
Java中的字面量:
- 整数型:10、-5、0、100
- 浮点型:3.14、-0.5、1.0
- 布尔型:true、false
- 字符型:‘a’、‘b’、‘c’、‘1’、‘2’、‘国’
- 字符串型:“Hello”、“World”、“Java”、“你好呀”
加号运算符+
- 作用1:求和(当加号两边都是数字时进行求和运算)
- 作用2:字符串拼接(当加号两边有任意一边是字符串类型时会进行字符串拼接,结果还是一个字符串)
2.4 变量
2.4.1 概念
-
变量是内存当中的一块空间,是计算机中存储数据最基本的单元。变量是用来解决数据存储问题的。先把数据存储起来,后续的程序需要使用的时候,可以从变量中取来用。
-
变量三要素:
- 数据类型(决定空间大小)【int,double,String】
- 变量名(只要是合法的标识符即可)
- 变量值(变量中具体存储的数据)
- 注意:数据类型是决定空间大小的。数据类型和值的数据类型要一致。
-
变量的声明、赋值、访问
声明(定义)语法格式:数据类型 变量名;
数据类型:int整数型、double浮点型、String字符串型……
赋值的语法格式:变量名 = 变量值;
访问怎么做?
- 访问包括两种情况:一种是读,一种是修改。
- 读:
System.out.println(变量名);
- 修改:
变量名 = 变量值;
int i; // 声明一个整数型的变量,起名i i = 100; // 给变量i赋值100 System.out.println(i); // 访问i变量:读操作 i = 200; // 访问i变量:改操作【给变量i重新赋值200】
2.4.2 作用
- 变量的存在会让程序更加便于维护。
以上代码的设计就不如以下的代码:System.out.println(100 + 111); System.out.println(100 + 222);
int num = 100; System.out.println(num + 111); System.out.println(num + 222);
- 变量的存在可以增强程序的可读性。
System.out.println(3.14 * 10 * 10);
以上代码的设计就不如以下的代码:double π = 3.14; int r = 10; System.out.println(π * r * r);
2.4.3 变量的小细节
- 变量必须先声明,再赋值,才能访问
- 方法体当中的代码遵循自上而下的顺序依次逐行执行,变量先访问,再声明肯定是不行的
- 一行代码上可以同时声明多个变量
- 在同一个作用域当中,变量名不能重名,可以重新赋值
- 变量值的数据类型必须和变量的数据类型一致,这样是不允许的:String name = 100;
2.4.4 变量的作用域
- 作用域就是变量的有效范围。在Java程序中通常是一个{}对应一个作用域。用一句大白话就可以概括了:出了大括号就不认识了。
- 作用域的不同主要是因为声明在不同位置的变量具有不同的生命周期。所谓的生命周期是:从内存开辟到内存释放。
- Java遵循就近原则
2.4.5 变量的分类
- 局部变量
- 凡是在方法体当中定义的变量,一定是局部变量。
- 局部变量只在当前方法体重有效
- 成员变量
在类体当中定义的变量叫做成员变量。- 静态变量
- 实例变量
2.5 二进制
2.5.1 概述
计算机底层只能识别二进制。计算机底层只识别二进制是因为计算机内部的电子元件只能识别两种状态,即开和关,或者高电平和低电平。二进制正好可以用两种状态来表示数字和字符,因此成为了计算机最基本的表示方法。在计算机内部,所有的数据都被转化为二进制形式进行处理和存储。虽然计算机可以通过不同的编程语言和程序来处理不同的数据类型和格式,但最终都需要将其转化为二进制形式才能被计算机底层识别和处理。
2.5.2 什么是二进制
- 十进制:满十进一
- 二进制:满二进一
2.5.3 进制转换
2.5.3.1 十进制转二进制
- 将十进制数除以2,得到商和余数。
- 将余数记录下来,然后将商作为新的十进制数,重复步骤1,直到商为0为止。
- 将记录的余数从下往上排列,得到的就是对应的二进制数。
2.5.3.2 二进制转十进制
将二进制数每一位权值找出来,然后每个权值与对应二进制位相乘,最后将它们相加,即可得到十进制数。
什么是权值?
在二进制中,权值指的是每个位所代表的数值大小,即二进制中每个位的位置所代表的数值大小。
例如,在二进制数1101中,最高位的权值为8,次高位的权值为4,第三位的权值为2,最低位的权值为1。
例如,二进制数1101转换为十进制数的计算过程如下:
1×2³ + 1×2² + 0×2¹ + 1×2⁰ = 8 + 4 + 0 + 1 = 13
2.6 八进制与十六进制
八进制与十进制互换、十进制与十六进制互换与二进制原理相同。
十进制 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
十六进 1 2 3 4 5 6 7 8 9 A B C D E F 10 11 12
十进制转换十六进制:除16取余,一直到商0为止,最后将所有的余数逆序输出。
十六进制转换十进制:每一位与权值相乘,最后求和。
- 二进制转换为十六进制
- 将二进制数从右往左每四位一组,不足四位则在左侧补0,得到若干个四位二进制数。
- 将每个四位二进制数转换为对应的十六进制数
- 十六进制转换为二进制
- 将十六进制中的每个数字转换成4个二进制位
2.7 原码反码补码
2.7.1 byte与bit
byte(字节)是计算机存储和处理数据的基本单位,通常由8个比特(bit)组成。每个比特(bit)是计算机中最小的存储单位,只能存储0或1两个状态。因此,一个字节(byte)可以存储8个比特(bit)的数据。
两者之间的关系是,1 byte = 8 bit,即8个比特(bit)组成一个字节(byte)。
在计算机中,数据通常以字节(byte)为单位进行存储和传输,而比特(bit)则是用来表示数据的最小单位。
1KB = 1024byte
1MB = 1024KB
1GB = 1024MB
1TB = 1024GB
2.7.2 原码反码补码
- 原码反码补码是计算机二进制的三种表示形式。
- 计算机在底层都是采用二进制补码形式表示的。
- 二进制位最高位称为符号位,0为正数,1为负数。
2.7.3 正数的原码反码补码
- 正数的原码反码补码是相同的。
- 请问127的原码反码补码分别是多少?0111 1111
2.7.4 负数的原码反码补码
负数的原码运算规则:将绝对值转换为二进制后,最高位改为1。
-5的原码:10000101
-5的反码:11111010(原则是:以原码作为参考,符号位不变,其他位取反。)
-5的补码:11111011(原则是:以反码作为参考,符号位不变,加1)
请问-20的原码反码补码是多少?
请问-128的原码反码补码是多少?
2.7.5 已知负数补码怎么得到原码
- 虽然计算机底层是采用补码的形式存储的,但最终显示给人类的时候是以原码的形式显示的。所以大家需要具备这个能力!!!
- 将负数的补码形式除符号位外的所有位按位取反,再加1即可得到原码。
- 已知补码:10000001
- 它的原码是:11111111
- 结果是:-127
- 通过这个可以得出,对于一个字节来说,最大值127,最小值-128。
2.7.6 计算机底层为什么采用补码
- 可以简化电路设计:采用补码形式可以将加减法运算转化为相同的操作,从而简化电路设计。
- 解决了0的正负问题:在原码中,0有两个表示,+0和-0,这样会导致计算结果不唯一,而在补码中,0只有一种表示,即全0,可以避免这个问题。
- 解决了负数溢出问题:在原码中,负数的表示范围比正数少1,这样在进行减法运算时容易出现负数溢出的情况,而在补码中,负数的表示范围与正数相同,可以避免负数溢出的问题。
- 方便计算机进行运算:补码形式可以方便计算机进行加减法运算,而且可以使用相同的电路进行运算,从而提高了计算机的运算效率。
- 可以计算一下 -3 + 2,看看能不能理解!!!
2.8 数据类型
2.8.1 数据类型概述
所有的引用数据类型,默认值是:null
关于默认值:Java语言中变量必须先声明,再赋值,才能使用。对于局部变量来说必须手动赋值,而对于成员变量来说,如果没有手动赋值,系统会自动赋默认值。
2.8.2 整数型
整数型字面量的四种表示形式
-
十进制表示法:以数字0-9组成的常数,默认为十进制表示法。
例如:int a = 10; -
二进制表示法:以0b或0B开头的常数,由0和1组合而成。
例如:int b = 0b101; -
八进制表示法:以0开头的常数,由数字0-7组成。
例如:int c = 012; -
十六进制表示法:以0x或0X开头的常数,由0-9和A-F(大小写均可)组成。
例如:int d = 0x1F;
2.8.2.1 整数型字面量默认当做int处理
Java中整数型字面量默认被当做int类型来处理,如果要表示long类型的整数,需要在字面量后面加上’L’或’l’标记。例如,下面是表示int和long类型整数的字面量的示例:
int x = 10; // 10是一个int类型的字面量
long y = 10L; // 10L是一个long类型的字面量
需要注意的是,大小写字母’L’和’l’的使用没有区别,但是容易被误解为数字1,因此建议使用大写字母。
请看以下代码有什么问题吗?
long z = 2147483648;
编译报错,原因是2147483648被当做int类型处理,而该数字本身已经超出了int最大值,如何修改?
long z = 2147483648L;
2.8.2.2 整数型字面量默认当做int处理
- Java中整数型字面量默认被当做int类型来处理,如果要表示long类型的整数,需要在字面量后面加上’L’或’l’标记。例如,下面是表示int和long类型整数的字面量的示例:
需要注意的是,大小写字母’L’和’l’的使用没有区别,但是容易被误解为数字1,因此建议使用大写字母。int x = 10; // 10是一个int类型的字面量 long y = 10L; // 10L是一个long类型的字面量
- 请看以下代码有什么问题吗?
long z = 2147483648;
编译报错,原因是2147483648被当做int类型处理,而该数字本身已经超出了int最大值,如何修改?
long z = 2147483648L;
2.8.2.3 自动类型转换
在Java中,对于基本数据类型来说,小容量是可以直接赋值给大容量的,这被称为自动类型转换。对于数字类型来说大小关系为:byte < short < int < long < float < double
2.8.2.4 强制类型转换
- Java中大容量是无法直接转换成小容量的。因为这种操作可能会导致精度损失,所以这种行为交给了程序员来决定,当然这种后果自然是程序员自己去承担。因此在代码中需要程序员自己亲手加上强制类型转换符,程序才能编译通过
- 强制类型转换时,底层二进制是如何变化的?原则:砍掉左侧多余的二进制。
- 强制类型转换时,精度可能会损失,也可能不会损失,这要看具体的数据是否真正的超出了强转后的类型的取值范围。如下图:水可能溢出,也可能不会溢出,这要看真实存放的水有多少
- 请推算结果:byte b = (byte)150;
2.8.2.5 当整数字面量没有超出byte的范围
- 在Java中有这样一个规定,当整数型字面量没有超出byte的范围:可以直接赋值给byte类型的变量。
byte b = 127; // 这是允许的
很显然,这是一种编译优化。同时也是为了方便程序员写代码。 - 如果超出了范围,例如:
byte b = 128; // 编译报错
这样就会报错,需要做强制类型转换,例如:
byte b = (byte)128;
它的执行结果你知道吗?可以尝试推算一下 - 在整数类型中,除了byte有这个待遇之外,short同样也是支持的。也就是说:如果整数型字面量没有超出short取值范围时,也是支持直接赋值的。
2.8.2.6 类型运算
- 两个int类型做运算
两个int类型的数据做运算,最终的结果还是int类型 - 多种数据类型混合运算
在Java中,多种数据类型混合运算时,各自先转换成容量最大的类型,再做运算
byte a = 100;
int b = 200;
long c = 300L;
long d = a + b + c;
你可以测试一下,如果d变量是int类型则编译器会报错
2.8.3 浮点型
2.8.3.1 浮点型字面量默认被当做double
- Java中,浮点型字面量默认被当做double类型,如果要当做float类型,需要在数字后面添加 F 或 f。
float f = 3.0; // 编译报错
报错原因是:3.0默认被当做double类型,大容量无法直接赋值给小容量。如何修改:
float f = 3.0F;
- 另外,可以通过以下程序的输出结果看到,double精度高于float:
double d = 1.5656856894; System.out.println(d); float f = 1.5656856894F; System.out.println(f);
2.8.3.2 浮点型数据两种表示形式
- 第一种形式:十进制
double x = 1.23;
double y = 0.23;
double z = .23; - 第二种形式:科学计数法
double x = 0.123E2; // 0.123 * 10的平方
double y = 123.34E-2; // 123.34 / 10的平方
2.8.3.3 浮点型数据存储原理
- 符号位:0表示整数。1表示负数。
- 指数位:比如小数0.123E30,其中30就是指数。表示0.123 * 10的30次幂。所以也有把指数位叫做偏移量的。最大偏移量127。
- 尾数位:浮点数的小数部分的有效数字。例如:0.00123,那么尾数位存储123对应的二进制。
- 从浮点型数据存储原理上可以看到,二进制中的指数位决定了数字呈指数级增大。因此float虽然是4个字节,但却可以表示比long更大的数值。因此float容量比long的容量大。
2.8.3.4 浮点型数据使用注意事项
一旦有浮点型数据参与运算得出的结果,一定不要使用“==”与其它数字进行“相等比较”
不要这样:
double x = 6.9;
double y = 3.0;
double z = x / y;
if(z == 2.3){System.out.println("相等");}
可以这样:
double x = 6.9;
double y = 3.0;
double z = x / y;
if(z - 2.3 < 0.000001){System.out.println("相等");}
2.8.4 字符型
2.8.4.1 字符型char
- 占用两个字节,0~65535,和short容量相同,但char可以取更大的整数
- 单个字符,使用单引号括起来,不能是多个字符
- 可以保存一个汉字
char c = ‘’
; 这是不允许的char c = ‘\u0000’
; 这表示一个空字符,也是char的默认值。\u0000是一个Unicode码。- 空字符与空格字符是不同的。空字符表示什么也没有。空格字符表示一个空格。
2.8.4.2 转义字符
\t
: 表示制表符,相当于按下 Tab 键\n
: 表示换行符\"
: 表示双引号(")\'
: 表示单引号(')\\
: 表示反斜线(\)本身
2.8.4.3 字符编码
- 字符编码是人为规定的文字与二进制之间的转换关系。
- 在早期计算机系统中,字符编码主要采用的是 ASCII 编码,采用1个字节编码。最多可以表示256个字符。(实际上ASCII码表只用了128个。),程序员需要记住这几个:
a 对应ASCII码 97(b是98,以此类推)
A 对应ASCII码 65(B是66,以此类推)
0 对应ASCII码 48(1是49,以此类推)
2.8.4.4 编码、解码、乱码
- 字符在计算机系统中,解码(Decoding)和编码(Encoding)是两个常用的概念,分别表示将二进制数据转换为字符和将字符转换为二进制数据。
- 编码是将字符转换为二进制数据的过程。解码是将二进制数据转换为字符的过程。例如:
‘a’ ---------按照ASCII码表编码-----------> 01100001
01100001 --------按照ASCII码表解码------------> ‘a’ - 乱码是指在字符编码和解码的过程中,由于编码和解码所采用的字符集不一致,或者编码和解码所采用的字符集不支持某些字符,导致最终显示的字符与原始字符不一致。为了避免乱码的问题,我们需要统一使用一个字符集,并且在进行字符编码和解码时要保持一致。
2.8.4.5 常见的字符编码
- ASCII 编码(American Standard Code for Information Interchange:美国信息交换标准编码):采用1个字节编码,包括字母、数字、符号和控制字符等。
- Latin-1编码(ISO 8859-1),采用1个字节编码。该编码方式是为了表示欧洲语言(如荷兰语、西班牙语、法语、德语等)中的字符而设计的,共支持 256 个字符。
- ANSI 编码(American National Standards Institute:美国国家标准协会):采用1个字节编码,支持英文、拉丁文等字符。两个ANSI码可以表示一个汉字。
- Unicode 编码:可表示所有语言的字符。采用了十六进制表示,占用 2 个字节或 4 个字节,最多可表示超过一百万个字符。 (使用这种方式是有点浪费空间的,例如英文字符’a’其实采用一个字节存储就够了。)
- UTF-8 编码(Unicode Transformation Format,8-bit):基于 Unicode 编码的可变长度字符编码,能够支持多语言和国际化的需求,使用 1~4 个字节来表示一个字符,是目前 Web 开发中最常用的字符编码方式。 (一个英文字母1个字节,一个汉字3个字节。)
- UTF-16 编码:基于 Unicode 编码的可变长度字符编码,使用 2 或 4 个字节来表示一个字符,应用于很多较早的系统和编程语言中。 (一个英文字母2个字节。一个汉字4个字节。)
- UTF-32编码:基于Unicode编码的固定长度字符编码,其特点是每个字符占用4个字节。
- GB2312 编码(小):是中国国家标准的简体中文字符集,使用 2 个字节来表示一个汉字,是 GBK 编码的前身。
- GBK 编码(Guo Biao Ku)(中):是针对中文设计的一个汉字编码方式,使用 2 个字节来表示一个汉字,能够表示中国内地的所有汉字。
- GB18030编码(大):是中国国家标准GB 18030-2005《信息技术 中文编码字符集》中规定的字符集编码方案,用于取代GB2312和GBK编码。
- Big5 编码(大五码):是台湾地区的繁体中文字符集,使用 2 个字节来表示一个汉字,适用于使用繁体中文的应用场景。
每种编码方式都有其特点和适用场景。在进行软件开发、网站开发和数据存储时,需要根据实际情况选择适合的编码方式。
Unicode码表的一部分
网络上也有很多在线转码工具,例如:http://www.jsons.cn/unicode/
2.8.4.6 char参与的运算
- Java中允许将一个整数赋值给char类型变量,但这个整数会被当做ASCII码值来处理
- 需要特别注意的是,这个码值有要求,不能超出char的取值范围
- 只要没有超出byte short char的取值范围,是可以直接赋值给byte short char类型变量的
- System.out.println(‘a’ + 1);结果是什么?
- char c = ‘a’ + 1;结果是什么?
- 以下程序结果是什么?
byte b = 1; short s = 1; char c = 1; short num = b + s + c;
- byte short char混合运算时,各自会先转换成int再做运算!!!
2.8.5 布尔型boolean
boolean类型只有两个值:true、false。没有其它值,没有0和1这一说。
通常用于表示一些逻辑上的真假值,并在程序中进行逻辑控制,例如以下代码:
boolean gender = true;
if(gender){
System.out.println("男");
}else{
System.out.println("女");
}
2.8.6 基础数类型转换原则
- 八种基本数据类型,除布尔型之外,其它类型都可以互相转换。
- 小容量转换为大容量,叫做自动类型转换,容量从小到大的排序为:
a. byte < short(char) < int < long < float < double
b. 注意char比short可以表示更大的整数 - 大容量转换为小容量,叫做强制类型转换,需要加强制类型转换符才能编译通过,运行时可能损失精度,也可能不会损失。
- 整数字面量如果没有超出byte short char的取值范围,可以直接赋值给byte short char类型的变量。
- byte short char混合运算,各自先转换为int再做运算。
- 多种类型混合运算,各自先转换成容量最大的类型,再做运算。
2.9 运算符
- 运算符
- 算术运算符:
+、-、*、/、%、++、--
- 关系运算符:
==、!=、>、>=、<、<=
- 逻辑运算符:
&、|、!、&&、||
- 按位运算符:
&、|、^、~、<<、>>、>>>
- 赋值运算符:
=、+=、-=、*=、/=、%=、&=、|=、^=、<<=、>>=、>>>=
- 条件运算符:
?:
- instanceof运算符:
instanceof
- new运算符:
new
- .运算符:
.
注意:运算符有优先级,关于优先级不需要记忆,不确定的添加小括号,添加小括号的优先级高,会先执行
2.9.1 算术运算符
+
:求和、字符串拼接、正数-
:相减、负数*
:乘积/
:商(除法)%
:取模(求余数)
取模公式:x - x / y * y
++
:自加1--
:自减1
2.9.1.1 接受用户键盘输入
java.util.Scanner scanner = new java.util.Scanner(System.in);
String s1 = scanner.next();
String s2 = scanner.nextLine();
int i = scanner.nextInt();
double d = scanner.nextDouble();
2.9.1.2 栈数据结构(Stack)
- 栈结构特点:
先进后出
后进先出 - 相关术语:
入栈、压栈、push
出栈、弹栈、pop
栈帧
栈顶、栈底
2.9.1.3 字节码解读
- 查看字节码的命令:javap -c 字节码文件
- 查看以下程序的字节码
查看字节码:javap -c ReadClass.classpublic class ReadClass{ public static void main(String[] args){ int i = 10; int j = i; j++; }}
- 字节码指令:
bipush指令:将字面量压入操作数栈
istore_1指令:将操作数栈中顶部数据弹出,然后将该数据存放到局部变量表的第1个位置
iload_1指令:将局部变量表1号槽位的数据压入操作数栈
iinc指令:将局部变量表中第1个位置数据加1 - 什么是局部变量表和操作数栈?
2.9.2 关系运算符
- 关系运算符又叫做比较运算符。包括:>、 >=、 <、 <=、 ==、 !=
- 所有关系运算符的运算结果都是布尔类型,不是true,就是false。
int a = 10;
int b = 10;
System.out.println(a > b); // false
System.out.println(a >= b); // true
System.out.println(a < b); // false
System.out.println(a <= b); // true
System.out.println(a == b); // true
System.out.println(a != b); // false
2.9.3 逻辑运算符
- 逻辑运算符:&(逻辑与)、 |(逻辑或)、 !(逻辑非)、^(逻辑异或)、 &&(短路与)、 ||(短路或)
- 逻辑运算符特点:逻辑运算符两边的操作数要求必须是布尔类型,并且最终运算结果也一定是布尔类型。
- 逻辑与&:两边操作数都是true,结果才是true。可以翻译为“并且”。
- 逻辑或|:两边操作数只要有一个是true,结果就是true。可以翻译为“或者”。
- 逻辑非!: !false就是true,!true就是false。
- 逻辑异或^:咱俩不一样,结果就是true。
- 短路与&&:和逻辑与&的运算结果相同。只是存在一种短路现象。(左边操作数为false时,右边操作数不执行)
- 短路或||:和逻辑或|的运算结果相同。只是存在一种短路现象。(左边操作数为true时,右边操作数不执行)
- 虽然短路与&&效率高于逻辑与&,但逻辑与&也有用武之地,具体看需求是怎样的。
2.9.4 按位运算符
- 按位运算符用于在二进制位级别上处理整数数据。主要包括:
- 左移 <<
- 右移 >>
- 无符号右移 >>>
- 按位与 &
- 按位或 |
- 按位异或 ^
- 按位取反 ~
- 注意:按位运算符的操作数要求必须是整数。否则会出现编译错误
2.9.4.1 左移 <<
它能够将一个二进制数的所有位向左移动指定的位数。左移运算符的运算规则如下:
- 将二进制数左移n位,相当于将数值乘以2的n次方。
例如,将二进制数0b1011左移2位,即为0b101100,相当于将11乘以2的2次方(即4),得到44。 - 左移运算符不会改变操作数的符号。左移后,右补0。
无论操作数是正数、负数还是零,左移运算符都只进行位级移动,不会改变符号。 - 左移运算符会对溢出进行截断。
应用一下:如何将2快速变成8?
2.9.4.2 右移 >>
它能够将一个二进制数的所有位向右移动指定的位数。右移运算符的运算规则如下:
- 将二进制数右移n位,相当于将数值除以2的n次方。
例如,将二进制数0b101100右移2位,即为0b1011,相当于将44除以2的2次方(即4),得到11。 - 右移运算符对正数、负数和零的处理方式不同。
对于正数,符号位不变,右移时左补0
对于负数,符号位不变,右移时左补1。
对于零,右移运算符操作后结果仍为零。 - 右移运算符会对溢出进行截断。
2.9.4.3 无符号右移动 >>>
它能够将一个二进制数的所有位向右移动指定的位数,而不考虑符号位。无符号右移运算符的运算规则如下:
- 将二进制数右移n位,相当于将数值除以2的n次方,并将最高位填充为0。
- 任意一个数字经过无符号右移之后,最终结果一定是非负数(0或正整数)
- 无符号右移运算符对溢出进行截断。
2.9.4.4 按位与&
- 将两个整数的二进制表示按位进行与运算,只有当相应的二进制位都为1时,结果才为1,否则结果为0
int a = 32;
int b = 25;
System.out.println(a & b); // 0
a的二进制:00100000
b的二进制:00011001
按位与之后:00000000 - 应用一下:请使用按位与运算符判断某个数字是否为奇数?思路:拿着这个数字和1进行按位与,如果结果是1,则表示该数字为奇数。
2.9.4.5 按位或|
- 将两个整数的二进制表示按位进行或运算,只有当相应的二进制位都为0时,结果才为0,否则结果为1
int a = 32;
int b = 25;
System.out.println(a | b); // 57
a的二进制:00100000
b的二进制:00011001
按位或之后:00111001 - 应用一下:请将0这个数字中第4位的二进制位设置为1(按位或的具体应用,将某个二进制位设置为1)
int flag = 0;
flag = flag | (1 << 3);
2.9.4.6 按位异或 ^
- 将两个整数的二进制表示按位进行异或运算,只有当相应的二进制位不同,结果才为1,否则结果为0
a的二进制:01100100int a = 100; int b = 200; System.out.println(a ^ b); // 172
b的二进制:11001000
按位异或之后:10101100 - 按位异或运算符具有自反性,所谓的自反性是指:数字A连续对数字B进行两次按位异或运算之后,可以得到原始的数字A。因为按位异或运算符具有这样的特征,所以在密码学方面应用广泛。通常使用它可以完成加密和解密操作。
- 应用一下:按位异或可以实现简单的加密和解密。
2.9.4.7 按位取反~
- 将整数的二进制表示按位进行取反运算,即0变为1,1变为0
System.out.println(~100); // -101
100的二进制:01100100
取反后:10011011(这是一个补码哦)
将补码转为原码:11100101 (-101) - 应用一下:位清除操作(将某个二进制位中指定位清除为0),例如有这样一个二进制:0b01101101,将第4个低位清除为0
int value = 0b01101101;// 待清除数据
int flag = 1 << 3; // 需要清除第4个低位
int result = value & (~flag);// 使用这种方式运算进行位清除
2.9.5 赋值运算符
- 基本赋值运算符
=
等号右边先执行,将直接结果赋值给左边的变量 - 扩展赋值运算符
+=、-=、*=、/=、%=、&=、|=、^=、>>=、<<=、>>>=
- 以 += 为例。i += 3; 表示 i = i + 3;
- += 就是先+后=,也就是先求和,然后将求和的结果重新赋值。
- 对于扩展赋值运算符来说,有一个非常重要的运算规则需要注意:扩展赋值运算符不会改变运算结果的类型。(即使精度损失了,也不会改变运算结果类型。)
2.9.6 条件运算符
- Java 语言中的条件运算符由 ? 和 : 组成,也被称为三元运算符。它的语法格式为:
布尔表达式 ? 表达式1 : 表达式2 - 当布尔表达式的值为 true 时,条件运算符的结果为表达式1的值,否则为表达式2的值。这种运算符常用于简化 if-else 语句的代码量。
- 下面是一个条件运算符的简单示例:
在上述代码中,首先定义了两个变量 a 和 b,然后使用条件运算符比较这两个变量的大小,取其中较大值作为变量 max 的值,最后输出结果。当 a > b 的结果为 false 时,条件运算符的结果为表达式2,即 b 的值为变量 max 的值。当 a > b 的结果为 true 时,条件运算符的结果为表达式1,即 a 的值为变量 max 的值。int a = 5, b = 7; int max = (a > b) ? a : b; System.out.println("最大值为:" + max);
- 总的来说,条件运算符在 Java 中的使用相对简单,能够减少代码重复和代码量,常用于简单的条件处理和表达式值的判断。
2.10 控制语句
控制语句:用于控制程序的执行流程,改变程序执行的次序。
- 分支语句
- if语句
- switch语句
- 循环语句
- for循环
- while循环
- do while循环
- 跳转语句
- break语句
- continue语句
2.10.1 分支语句
2.10.1.1 if语句
- if语句的第一种写法:
原理:如果布尔表达式true,则执行分支语句,如果位false,则不执行if(布尔表达式){ 分支语句; }
- if语句的第二种写法:
原理:如果布尔表达式true,则执行分支1.如果为false,则执行分支2.if(布尔表达式){ 分支1; }else{ 分支2; }
- if语句的第三种写法:
原理:从上往下依次判断布尔表达式,只要遇到布尔表达式为true,则执行对应的分支,整个if结束。如果都是false,没有分支执行。if(布尔表达式){ 分支1; }else if(布尔表达式){ 分支2; }else if(布尔表达式){ 分支3; }
- if语句的第四种写法:
原理:从上往下依次判断布尔表达式,只要遇到布尔表达式为true,则执行对应的分支,整个if结束。如果都是false,没则执行最后的else分支。if(布尔表达式){ 分支1; }else if(布尔表达式){ 分支2; }else if(布尔表达式){ 分支3; }else{ 分支4; }
if语句的使用注意事项:
- 对于任何一个if语句来说,最多只能有一个分支执行。
- 分支中如果只有一条Java语句,大括号可以省略。
- 对于以上第2种和第4种,这两种写法是可以保证一定会有一个分支执行的。因为这两种写法都有else分支。
- 对于以上第1种和第3种,这两种写法可能会没有分支执行。
- 在if选择结构中还可以嵌套别的选择结构或循环结构。
2.10.1.2 switch语句
语法格式:
switch(expression){
case value1:
// 当expression的值等于value1时,执行这里的代码
break;
case value2:
// 当expression的值等于value2时,执行这里的代码
break;
case value3:
// 当expression的值等于value3时,执行这里的代码
break;
// ...
default:
// 当expression的值与所有的case语句都不匹配时,执行这里的代码
}
switch语句使用注意事项:
- switch 语句适用于判断固定值。if语句适用于判断范围或区间时使用。switch能做的if肯定能做,if能完成的switch不一定能完成。
- JDK7之前,switch只支持int类型、枚举类型,在JDK7之后,增加了对字符串类型的支持。
- case 语句中的值必须是字面量,不能是变量。
- case 语句中的值必须和switch后面的值是同一类型,或者能够相互转换。
- case可以合并。
- 在每个 case 分支中要加上 break 语句,以避免case穿透现象。
- 在 switch 语句中,一般都应该有一个 default 分支,用于处理一些特殊情况,以避免程序出错。 (当然,default语句不写,也不会编译报错。)
- switch 语句中的 default 分支可以放在 switch 块的任意位置,但是通常建议将 default 分支放在所有 case 分支的最后面。(可读性好)
Java12中switch引入了新特性,代码变得更加简洁:
switch(x){
case 1 -> System.out.println(1);
case 2 -> System.out.println(2);
default -> System.out.println("default");
}
switch(x){
case 1, 2, 3 -> System.out.println("123");
}
switch(x){
case 1 -> {
System.out.println(1);
System.out.println(1);
}
}
2.10.2 循环语句
当某代码片段需要频繁多次执行时,可以采用循环语句。
语法格式:
for(1初始化表达式;2布尔表达式;4更新表达式){
// 3循环体
}
2.10.2.1 for循环嵌套
for 循环嵌套 for 循环指的是在一个 for 循环的循环体中再嵌套另一个 for 循环。通过嵌套 for 循环,可以在外层循环的每次迭代中执行内层循环若干次。
例如,以下代码使用 for 循环嵌套打印九九乘法表:
for (int i = 1; i <= 9; i++) {
for (int j = 1; j <= i; j++) {
System.out.print(j + " x " + i + " = " + i * j + "\t");
}
System.out.println();
}
这段代码中,外层循环的循环变量为 i,它的取值范围是 1 到 9,每次迭代时执行内层循环。内层循环的循环变量为 j,它的取值范围是 1 到 i,这样可以确保每行只打印到当前行数的结果。在内层循环中,打印出 j 和 i 的积,用 tab 键隔开,使结果排列整齐。
2.10.2.2 while循环
while循环语法格式:
while(布尔表达式){
循环体;
}
执行原理:只要布尔表达式为true就会一直循环,直到布尔表达式结果为false,循环结束。
while循环体的执行次数可能是:0 - n次。
for循环适用于循环次数固定的。while循环适用于循环次数不固定的。
2.10.2.3 do while循环
- do while循环语法格式:
do{ 循环体; }while(布尔表达式);
- 执行原理:先执行一次循环体,再判断布尔表达式,为true继续循环,直到布尔表达式结果为false,循环结束。
- do while循环体的执行次数可能是:1 - n次。
- do while循环比较适合用在不管条件是否成立,第一次必须要执行的业务。
2.10.3 跳转语句
- break;
- 出现在switch语句用来终止switch语句的执行。
- 出现在循环中,用来终止循环的执行。
- break; 用来终止离它最近的循环。
- break 循环标记; 用来终止指定的循环。
- continue;
- 终止当前本次循环,直接进入下一次循环继续执行。
- continue; 终止当前本次循环,直接进入离它最近的循环继续。
- continue 循环标记; 终止当前本次循环,直接进入指定的循环继续。
- break;与return;
- break;终止循环。
- return;终止方法。
2.11 方法
方法是什么,有什么用:
方法(Method)是一段可以被重复利用的代码片段。
一个方法一般都是一个独立的功能。
在C语言中叫做函数(Function)。
2.11.1 方法的定义和调用
语法格式:
[修饰符列表] 返回值类型 方法名(形式参数列表){
方法体;
}
- 修饰符列表:可选项。目前固定写法public static,后面再做介绍。
- 返回值类型:用来指定方法返回值的数据类型(方法执行结束后的结果类型)。只要是Java合法的数据类型,都可以,例如:byte,short,int,long,float,double,boolean,char,String…。如果方法执行结束时没有返回任何数据,返回值类型也不能空着,需要写void关键字。
- 方法名:只要是合法的标识符即可。但最好见名知意。方法通常反应行为,所以方法名一般为动词。
- 形式参数列表:简称形参。用来接收数据。参数个数0~N个。如果有多个,使用逗号隔开。例如(int a, double b, long c)。每一个形式参数都可以看做局部变量。
- 每个方法都有方法体,方法体是一对大括号。在大括号中编写Java语句。
- 方法的调用:如果修饰符列表中static关键字,采用“类名.方法名(实际参数列表);”调用方法。
- 调用者和被调用者在同一个类中,“类名.”可以省略。
- 实际参数列表:简称实参,实参和形参要一一对应,个数对应,数据类型对应。
- 调用方法,如果方法执行结束后有返回值,可以采用变量接收该返回值。当然,也可以选择不接收。
2.11.2 方法执行时的内存变化
- 方法只定义不调用是不会分配内存的。只是方法的字节码指令存储在元空间中。
- 方法调用时会给该方法在JVM的栈内存中分配空间,此时发生压栈动作。这个方法的空间被称为栈帧。
- 栈帧中主要包括:局部变量表,操作数栈等。
- 方法执行结束时,该栈帧弹栈,方法内存空间释放。
例子:
/*
1.方法如果只定义,不调用是不会分配内存空间。(从Java8开始,方法的字节码指令存储在元空间metaspace当中。元空间使用的是本地内存。)
2.方法调用的瞬间,会在JVM的栈内存当中分配活动场所,此时发生压栈动作。
3.方法一旦结束,给该方法分配的内存空间就会释放。此时发生弹栈动作。
*/
public class MethodTest08{
public static void main(String[] args){
System.out.println("main begin");
m1();
System.out.println("main over");
}
public static void m1(){
System.out.println("m1 begin");
m2();
System.out.println("m1 over");
}
public static void m2(){
System.out.println("m2 begin");
m3();
System.out.println("m2 over");
}
public static void m3(){
System.out.println("m3 begin");
System.out.println("m3 over");
}
}
运行结果
main begin
m1 begin
m2 begin
m3 begin
m3 over
m2 over
m1 over
main over
2.11.3 方法重载
- 什么情况下构成方法重载?
- 在同一个类中
- 方法名相同
- 参数列表不同
- 类型不同算不同
- 顺序不同算不同
- 个数不同算不同
- 什么时候我们考虑使用方法重载?
- 在同一个类中,如果功能相似,建议使用方法重载。
- 方法重载好处?
- 简化代码调用。
- 更易维护。
- 代码美观。
2.11.4 递归
- 什么是方法的递归调用?
方法自己调用自己。 - 递归调用如果没有结束条件的话,会出现栈内存溢出错误:
java.lang.StackOverflowError - 递归使用注意事项?
- 递归必须要有结束条件。
- 递归和循环都能完成的话,优先选择循环。(递归更耗费内存。)
- 在实际开发中,使用递归调用的时候,即使有的时候,结束条件是存在的,并且结束条件也是合法的。
但仍然会发生栈内存溢出错误,这可能是因为递归太深,栈内存不够了导致的。所以递归调用一般是不建议使用的。只有在不使用递归调用时这个问题解决不了的情况下,才建议使用递归调用。- 原则:能用循环尽量使用循环。
- 因为递归调用太耗费栈内存。
- 在实际开发中,如果因为递归调用发生了栈内存溢出错误,你该怎么办?
- 首先可以调整栈内存的大小。扩大栈内存。
- 如果扩大之后,运行一段时间还是出现了栈内存溢出错误。
- 可能是因为递归结束条件不对。需要进行代码的修改。
// 使用递归计算n的阶乘(经常考的面试题:笔试题)
// 5的阶乘:5 * 4 * 3 * 2 * 1
public class RecursionTest04{
public static void main(String[] args){
int n = 5;
int result = jieCheng(n);
System.out.println("result = " + result);
}
public static int jieCheng(int n){
if(n == 1){
return 1;
}
return n * jieCheng(n - 1);
}
}
2.12 package和import
package
- 包机制作用:便于代码管理。
- 怎么定义包:在java源码第一行编写 package 语句。注意:package语句只能出现在java代码第一行。
- 包名命名规范中要求是全部小写。
- 包名命名规范:公司域名倒序 + 项目名 + 模块名 + 功能名。例如:
com.powernode.oa.empgt.service - 如果带包编译:
javac -d 编译后的存放目录 java源文件路径 - 有了包机制后,完整类名是包含包名的,例如类名是:com.powernode.javase.chapter02.PackageTest
import
- import语句用来引入其他类。
- A类中使用B类,A类和B类不在同一个包下时,就需要在A类中使用import引入B类。
- java.lang包下的不需要手动引入。
- import语句只能出现在package语句之下,class定义之前。
- import语句可以编写多个。
- import语句可以模糊导入:
java.util.*
; - import静态导入:
import static java.lang.System.*
;
2.13 IntelliJ IDEA工具的使用
- IDEA工具的安装。
- 如何新建空工程。
- 如何设置JDK以及编译器版本。
- 如何新建模块。
- 如何新建包,新建类。
- 如何运行Java程序。
- 如何设置字体大小。
2.13.1 常用的IDEA快捷操作
alt+insert
(新建/新增任何东西)- 退出任何窗口ESC
- 编写源码的窗口最大化
ctrl + shift + F12
- psvm生成main方法
- 快速生成输出语句
sout
- IDEA会自动保存,自动编译
- 打开Project窗口
alt + 1
- 查找某个类:敲两次shift,选择classes,输入类名
- 切换选项卡:alt + 左右方向键
- 自动生成变量.var
- 删除一行
ctrl+y
- 复制一行
ctrl+d
- 在一个类当中查找方法
ctrl + F12
- 生成for循环:
fori
- 自动生成if语句
.if
- 单行注释:
ctrl + /
- 多行注释:
ctrl + shift + /
- 查看源码:按ctrl别松手,鼠标移动到对应的类名下方,出现下划线,点击过去,可以查看类源码。
- 多行编辑:按alt别松手,鼠标拖动多行,完成多行编辑。
- 怎么快速生成创建对象语句:
类名.new.var
- 怎么快速生成if语句:
布尔类型值.if
- 怎么快速生成setter和getter方法:
alt + insert
,然后选择setter and getter生成。 - 怎么快速生成构造方法:
alt + insert
,然后选择Constructor。 - 在IDEA当中如何自动纠错:将光标移动到错误的位置,按
atl + enter
。会有提示,根据提示进行纠错。 - 移动代码到上一行:
alt + shift + 上/下方向
- 怎么快速重写方法?
alt + insert
,选择Override… - 怎么快速重写方法?
ctrl + o
- 快速向下转型,并同时生成变量名:
变量名.castvar
- 快速查看方法的参数:
ctrl + p
- 返回上一步:
ctrl + alt + 左方向键
。 下一步:ctrl + alt + 右方向键
。 - 代码格式化:
ctrl + alt + L
。 - 查看继承结构:
ctrl + H
- 自动代码包裹:
ctrl + alt + t
- 每章一句:出发比向往更有意义!
- 恭喜你已阅读完第二章!点个赞证明你已经挑战成功,进入第三章关卡吧!