第三章 Java基础语法----动力节点最新Java零基础视频笔记(java17)

标识符(掌握)

什么是标识符

在Java中,标识符是用来给变量、方法、类和包等命名的字符序列。标识符由字母、数字、下划线和美元符号组成,但是第一个字符必须是字母、下划线或美元符号。标识符不能包含空格或其他特殊字符,也不能与Java关键字相同。标识符的长度没有限制,但是建议使用有意义的、简洁的标识符,以提高代码的可读性和可维护性。

标识符可以标识什么

Java中的标识符可以标识以下内容:

  1. 变量(Variable)
  2. 方法(Method)
  3. 类(Class)
  4. 接口(Interface)
  5. 枚举(Enum)
  6. 注解(Annotation)
  7. 包(Package)
  8. 类型参数(Type Parameter)
  9. 类型名称(Type Name)
  10. 常量(Constant)

以上是Java中常见的标识符,其中变量、方法、类、接口、枚举、注解和常量都是Java中的关键元素,而包和类型参数则是Java中的辅助元素。类型名称则是指Java中的基本类型和引用类型的名称,例如int、String、Object等。

标识符命名规则

Java中合法的标识符需要满足以下要求:

  1. 标识符可以由字母、数字、下划线(_)和美元符号($)组成,不能含有其他符号。(java支持全球所有语言,所以这里的 字母 指的是任何一个国家的语言都可以)
  2. 标识符不能以数字开头。
  3. 标识符不能是Java中的关键字,如public、class、void等。
  4. 标识符是区分大小写的,即Foo和foo是两个不同的标识符。
  5. 标识符的长度没有限制,但是Java建议使用有意义的、简短的标识符。

例如,以下是合法的标识符:

  1. name
  2. _name
  3. $name
  4. name123
  5. Name
  6. MyClassName

而以下是不合法的标识符:

  1. 123name(以数字开头)
  2. public(关键字)
  3. my-name(中间包含横线)
  4. MyClassName!(包含非法字符)

标识符命名规范

  1. 见名知意
  2. 驼峰式命名方式
  3. 类名、接口名、枚举、注解:首字母大写,后面每个单词首字母大写。(StudentService,UserService)
  4. 变量名和方法名:首字母小写,后面每个单词首字母大写。(doSome,doOther)
  5. 常量名:全部大写,每个单词用下划线连接。(LOGIN_SUCCESS,SYSTEM_ERROR)
  6. 包名:全部小写

判断标识符是否合法

判断以下标识符中哪些是合法的,哪些是不合法的

  1. myVariable
  2. 2name
  3. first.name
  4. studentName
  5. public
  6. public1
  7. address#
  8. MAX_SIZE
  9. id#
  10. _id
  11. @age
  12. $money
  13. 你好

关键字(理解)

什么是关键字

Java关键字是Java编程语言中预定义的具有特殊含义的保留字,这些保留字不能被用作标识符或变量名,而是在语法中有特定的用法和限制。Java关键字的作用是控制程序的逻辑和结构,这些关键字通常用于声明变量、定义类、控制程序流程、实现面向对象编程等。

Java关键字的种类有很多,包括基本数据类型关键字(如int、double、boolean等)、控制流程关键字(如if、for、while等)、访问权限关键字(如public、private、protected等)、类和对象关键字(如class、new、extends、super等)、异常处理关键字(如try、catch、finally等)等。不同的关键字有不同的作用和用法,程序员需要根据具体的需求选择合适的关键字来编写代码。

Java关键字的使用规则也有一些限制,例如关键字不能作为变量名、方法名等标识符的名称,也不能在不同的上下文中使用不同的含义。在编写Java代码时,需要遵守Java关键字的使用规则,以保证程序的正确性和可读性。

java中有哪些关键字

java中所有的关键字都是小写的。包括:
abstract, assert, boolean, break, byte, case, catch, char, class, const, continue, default, do, double, else, enum, extends, final, finally, float, for, goto, 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中,字面量指的是在程序中直接使用的数据,字面量是Java中最基本的表达式,不需要进行计算或转换,直接使用即可。

Java中都有哪些字面量

  • 整数型:10、-5、0、100
  • 浮点型:3.14、-0.5、1.0
  • 布尔型:true、false
  • 字符型:‘a’、‘b’、‘c’、‘1’、‘2’、‘国’
  • 字符串型:“Hello”、“World”、“Java”、“你好呀”

加号运算符

  1. 加号“+”在java中是一种运算符,有两个作用:
    1. 求和
    2. 字符串拼接:+的任意一边只要是字符串类型,做字符串拼接操作,拼接之后的结果还是一个字符串。其它情况一律是求和操作。
  2. 判断以下程序的输出结果
System.out.println(5 + 6);
System.out.println("5" + "6");
System.out.println("5" + 6 + 7);
System.out.println("5" + (6 + 7));
System.out.println(5 + 6 + "7");
System.out.println(5 + "6" + 7);
System.out.println(5 + (6 + "7")); // 添加小括号优先级较高

变量(掌握)

什么是变量


变量可以看做是一个盒子,这个盒子可以存储数据。本质上,变量是内存当中的一块空间,这块空间有三要素(变量的三要素):

  • 要素一:数据类型(决定了空间大小)。例如有一种数据类型叫做整数型:int
  • 要素二:名字(只要是合法的标识符就行)。例如:age(年龄)
  • 要素三:值(盒子中具体存储的数据)。例如:20

例如以下代码则表示声明了一个整数类型的变量age,值为20

int age = 20;

以及以下代码则表示声明了一个字符串类型的变量name,值为"jack"

String name = "jack";

数据类型后面小节会详细讲解。目前只需要知道int代表整数类型,String代表字符串类型即可。
另外,变量的“变”体现在哪里呢?体现在变量这个盒子中的数据是可以改变的。例如,通过“=”赋值运算符,可以改变盒子中存储的数据:

age = 30;

这个操作用专业术语表达叫做:给变量重新赋值。
重新赋值时也是有要求的,值的类型要和变量的类型一致,不然就会报错,例如:

age = "30";

报错信息如下:

编译器找到等号右边的数据,发现是String类型,然后发现age这个盒子只能存储int类型,类型不匹配,表示这种int盒子不能存放String类型的数据。

变量的作用

有这样一个需求:请用你当前所学知识,分别计算100和111、222、666、888、999的和,你该怎么编写代码?

System.out.println(100 + 111);
System.out.println(100 + 222);
System.out.println(100 + 666);
System.out.println(100 + 888);
System.out.println(100 + 999);

现在需求变化了,要求计算234和111、222、666、888、999的和,你需要将以上代码中所有的100全部进行修改:

System.out.println(234 + 111);
System.out.println(234 + 222);
System.out.println(234 + 666);
System.out.println(234 + 888);
System.out.println(234 + 999);

修改了5个位置,如果求和的数据更多,那么修改的位置也会更多,显然:可维护性太差。怎么解决?使用变量可以解决。

int num = 100;
System.out.println(num + 111);
System.out.println(num + 222);
System.out.println(num + 666);
System.out.println(num + 888);
System.out.println(num + 999);

如果需求变化了,只需要修改一个位置即可:

int num = 234;
System.out.println(num + 111);
System.out.println(num + 222);
System.out.println(num + 666);
System.out.println(num + 888);
System.out.println(num + 999);

通过以上内容的学习,可以得知,变量的存在,可以让程序更加易维护

再比如,又有这样一个需求:现在有三个圆,半径分别是10cm,20cm,30cm,π取值3.14,请分别计算他们的面积,如果不使用变量,程序是这样的:

System.out.println(3.14 * 10 * 10); // 314
System.out.println(3.14 * 20 * 20); // 1256
System.out.println(3.14 * 30 * 30); // 2826

上面程序存在的最大问题就是:可读性太差。使用变量可以提高程序的可读性:

double π = 3.14;
int r1 = 10;
int r2 = 20;
int r3 = 30;
System.out.println(π * r1 * r1);
System.out.println(π * r2 * r2);
System.out.println(π * r3 * r3);

因此变量的出现可以提高程序的可读性。

变量的声明、赋值、访问

变量的声明

语法如下:

数据类型 变量名;

例如:

int age;
String name;
double π;

数据类型后面详细讲。
变量名只要是合法的标识符即可。规范中要求:变量名首字母小写,后面每个单词首字母大写。

变量的赋值

使用赋值运算符“=”完成赋值,例如:

age = 20;
name = "jack";
π = 3.14;

注意:等号运算符叫做赋值运算符,“=”右边表达式优先级高,先执行右边,将执行结果赋值给左边的变量。
变量中的“变”是因为变量赋值后可以重新赋值:

age = 30;
name = "lucy";

需要注意的是:变量虽然可以重新赋值,但在赋值的时候,值的数据类型一定要和变量的数据类型一致,不能这样:

age = "张三";

另外,变量的声明和赋值也是可以在一行代码中完成的,例如:在声明的时候直接赋值:

int num = 200;

变量的访问

变量的访问不外乎包括两种情况:

  • 读取
  • 修改

读取变量的值:

int age = 20;
System.out.println(age);

修改变量的值:

age = 30;

将变量的值读取出来,复制一份传递给另一个变量:

int num1 = 10;
int num2 = num1;
int num3 = num1 + num2;

变量的小细节

  1. 变量必须先声明,再赋值,才能访问。
int age;
System.out.println(age); // 报错,原因是变量age没有赋值
  1. 方法体当中的代码遵循自上而下的顺序依次逐行执行,变量先访问,再声明肯定是不行的。
System.out.println(num);
int num = 20;
  1. 一行代码上可以同时声明多个变量。
int a, b, c = 300; // 表示声明三个int类型变量,分别起名a b c,但是a和b没有赋值,c赋值300
  1. 在同一个作用域当中,变量名不能重名,可以重新赋值。
public static void main(String[] args){
    
    
    int a = 100;
    // 重新赋值没问题
    a = 200;
    a = 300;
    // 重复声明肯定不行
    int a = 900; // 报错
}

变量的作用域

什么是变量作用域

作用域就是变量的有效范围。变量的作用域是怎样的呢?用一句大白话就可以概括了:出了大括号就不认识了。

public class MyClass {
    
    
    static int e = 100;
    public static void main(String[] args){
    
    
        int i = 100;
        System.out.println(i);
        for(int k = 0; k < 10; i++){
    
    
            int f = 100;
        }
        // 这里是无法访问f变量的
        System.out.println(f);

        // 这里是可以访问e的
        System.out.println(e);
    }
    public static void m(){
    
    
        // 这里无法访问main方法中的i
        System.out.println(i);
    }
}

作用域的不同主要是因为声明在不同位置的变量具有不同的生命周期。所谓的生命周期是:从内存开辟到内存释放。

Java的就近原则

public class MyClass {
    
    
    
    static int num = 10;
    
    public static void main(String[] args){
    
    
        int num = 200;
        // 输出结果是200,这就是就近原则。
        System.out.println(num);
    }
    
}

变量的分类

Java中的变量可以按照作用域的不同划分为以下几类:

  1. 局部变量:定义在方法、语句块、形式参数中的变量。
  2. 成员变量:定义在类中,但在方法之外的变量。
  3. 静态变量:使用static关键字定义的变量。
  4. 实例变量:没有使用static关键字定义的变量。

二进制(掌握)

计算机底层只能识别二进制。计算机底层只识别二进制是因为计算机内部的电子元件只能识别两种状态,即开和关,或者高电平和低电平。二进制正好可以用两种状态来表示数字和字符,因此成为了计算机最基本的表示方法。在计算机内部,所有的数据都被转化为二进制形式进行处理和存储。虽然计算机可以通过不同的编程语言和程序来处理不同的数据类型和格式,但最终都需要将其转化为二进制形式才能被计算机底层识别和处理。

什么是二进制

十进制:满十进一。
二进制:满二进一。

十进制 1 2 3 4 5 6 7 8 9 10
二进制 1 10 11 100 101 110 111 1000 1001 1010

二进制和十进制的转换

十进制转换为二进制

要将一个十进制数转换为二进制数,可以使用以下步骤:

  1. 将十进制数除以2,得到商和余数。
  2. 将余数记录下来,然后将商作为新的十进制数,重复步骤1,直到商为0为止。
  3. 将记录的余数从下往上排列,得到的就是对应的二进制数。

例如,将十进制数27转换为二进制数:
27 ÷ 2 = 13 … 1
13 ÷ 2 = 6 … 1
6 ÷ 2 = 3 … 0
3 ÷ 2 = 1 … 1
1 ÷ 2 = 0 … 1
所以27的二进制数为11011。

二进制转换为十进制

将二进制数每一位权值找出来,然后每个权值与对应二进制位相乘,最后将它们相加,即可得到十进制数。

什么是权值?
在二进制中,权值指的是每个位所代表的数值大小,即二进制中每个位的位置所代表的数值大小。例如,在二进制数1101中,最高位的权值为8,次高位的权值为4,第三位的权值为2,最低位的权值为1。

例如,二进制数1101转换为十进制数的计算过程如下:
1×2³ + 1×2² + 0×2¹ + 1×2⁰ = 8 + 4 + 0 + 1 = 13
因此,二进制数1101转换为十进制数为13。

练习一下

将以下十进制的数字转换为二进制:

  • 243:11110011
  • 165
  • 89

将以下二进制的数字转换为十进制:

  • 101010
  • 111100
  • 011001

八进制与十六进制(了解)

什么是八进制

八进制:满八进一。

八进制与十进制的转换

十进制转换为八进制

将十进制数除以8,直到商为0,然后将每次的余数从下往上排列即为该十进制数的八进制表示。
例如,将十进制数27转换为八进制:
27 ÷ 8 = 3 … 3
3 ÷ 8 = 0 … 3
所以27的八进制表示为33。

八进制转换为十进制

八进制转换为十进制的方法如下:

  1. 将八进制数的每一位按权展开,权值分别为8的0次方、8的1次方、8的2次方,以此类推。
  2. 将每一位的值乘以对应的权值,然后将所有结果相加。

例如,将八进制数 346 转换为十进制数:
3×8^2 + 4×8^1 + 6×8^0 = 3×64 + 4×8 + 6×1 = 198
因此,八进制数 346 转换为十进制数为 198。

什么是十六进制

满十六进一。

十六进制与十进制的转换

十进制转换为十六进制

  1. 首先将十进制数除以16,得到商和余数。
  2. 将余数转换为对应的十六进制数,如果余数小于10,则直接写下来,否则用A、B、C、D、E、F表示10、11、12、13、14、15。
  3. 将商作为新的十进制数,重复步骤1和2,直到商为0为止。
  4. 将每一步得到的十六进制数倒序排列,即为最终的十六进制数。

例如,将十进制数255转换为十六进制数:

  1. 255 ÷ 16 = 15 余 15
  2. 余数15对应的十六进制数为F,所以最后一位为F。
  3. 15 ÷ 16 = 0 余 15
  4. 余数15对应的十六进制数为F,所以第二位为F。
  5. 最终的十六进制数为FF。

十六进制转换为十进制

将十六进制转换为十进制的方法是将每一位的十六进制数值乘以对应的权值,再将各位的结果相加。
例如,将十六进制数ABCD转换为十进制数:

  1. 将A、B、C、D分别转换为对应的十进制数值,即10、11、12、13。
  2. 根据十六进制的权值规则,从右往左依次乘以16的0次方、1次方、2次方、3次方,即1、16、256、4096。
  3. 将各位的乘积相加,即:13×1 + 12×16 + 11×256 + 10×4096 = 43981。
  4. 所以,十六进制数ABCD转换为十进制数为43981。

另一种简便的方法是,将十六进制数中的每一位转换为4位的二进制数,再将这些二进制数转换为十进制数,最后将各位的结果相加。

二进制与十六进制的转换

二进制转换为十六进制

二进制转换为十六进制的方法如下:

  1. 将二进制数从右往左每四位一组,不足四位则在左侧补0,得到若干个四位二进制数。

  2. 将每个四位二进制数转换为对应的十六进制数,可以使用下表进行转换:
    | 二进制数 | 十六进制数 |
    | — | — |
    | 0000 | 0 |
    | 0001 | 1 |
    | 0010 | 2 |
    | 0011 | 3 |
    | 0100 | 4 |
    | 0101 | 5 |
    | 0110 | 6 |
    | 0111 | 7 |
    | 1000 | 8 |
    | 1001 | 9 |
    | 1010 | A |
    | 1011 | B |
    | 1100 | C |
    | 1101 | D |
    | 1110 | E |
    | 1111 | F |

  3. 将每个四位二进制数对应的十六进制数按照从左往右的顺序排列,得到最终的十六进制数。

例如,将二进制数1101011010111011转换为十六进制数:

  1. 从右往左每四位一组,得到1101 0110 1011 1011。
  2. 将每个四位二进制数转换为对应的十六进制数,得到D 6 B B。
  3. 将每个四位二进制数对应的十六进制数按照从左往右的顺序排列,得到最终的十六进制数:D6BB。

十六进制转换为二进制

将每个十六进制数位转换为四位二进制数即可。
例如:将十六进制数 AF 转换为二进制数。
A 对应的二进制数为 1010,F 对应的二进制数为 1111,因此 AF 对应的二进制数为 10101111。

原码反码补码(掌握)

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

原码反码补码

原码、反码和补码都是计算机二进制的表示方式。在这三种编码方式中,原码是最简单的,也是最直接的表示方式;反码可以解决原码在计算过程中的问题;补码可以解决反码在计算过程中的问题。
计算机在底层是采用补码形式表示数据的。
在二进制当中,最高位表示符号位,0表示正数,1表示负数。

正数的原码反码补码

正数的原码、反码和补码都是相同的。
例如,一个十进制数+5的二进制原码为00000101,反码为00000101,补码为00000101。
原码:将正数的二进制表示直接写下来,最高位为0。
反码:正数的反码就是其原码本身。
补码:正数的补码也就是其原码本身。

127的原码反码补码

127的原码为01111111,其反码和补码均与原码相同。

负数的原码反码补码

负数的原码运算规则:将绝对值转换为二进制后,最高位改为1。
-5的原码:10000101
-5的反码:11111010(原则是:以原码作为参考,符号位不变,其他位取反。)
-5的补码:11111011(原则是:以反码作为参考,符号位不变,加1)

-128的原码反码补码

-128的原码为10000000,其反码为11111111,补码为10000000。注意,对于-128这个特殊的数,它的补码和原码相同。

已知负数的补码怎么得到原码

虽然计算机底层是采用补码的形式存储的,但最终显示给人类的时候是以原码的形式显示的。所以大家需要具备这个能力!!!
将负数的补码形式除符号位外的所有位按位取反,再加1即可得到原码。
已知补码:10000001
它的原码是:11111111
结果是:-127
通过这个可以得出,对于一个字节来说,最大值127,最小值-128。

计算机底层为什么采用补码

计算机采用补码形式进行数值计算的原因有以下几点:

  1. 可以简化电路设计:采用补码形式可以将加减法运算转化为相同的操作,从而简化电路设计。
  2. 解决了0的正负问题:在原码中,0有两个表示,+0和-0,这样会导致计算结果不唯一,而在补码中,0只有一种表示,即全0,可以避免这个问题。
  3. 解决了负数溢出问题:在原码中,负数的表示范围比正数少1,这样在进行减法运算时容易出现负数溢出的情况,而在补码中,负数的表示范围与正数相同,可以避免负数溢出的问题。
  4. 方便计算机进行运算:补码形式可以方便计算机进行加减法运算,而且可以使用相同的电路进行运算,从而提高了计算机的运算效率。

下面是一个简单的数据演示:
假设我们要计算-3+2的结果,使用原码进行计算:
-3的原码为10000011,2的原码为00000010,进行加法运算得到的结果为10000101,转换成十进制为-5,这个结果是错误的。
使用补码进行计算:
-3的补码为11111101,2的补码为00000010,进行加法运算得到的结果为11111111,转换成十进制为-1,这个结果是正确的。

数据类型(掌握)

数据类型:决定了变量在内存中的空间大小。不同的数据类型,在内存中占用空间大小不同。
为什么需要学习数据类型?为了定义变量。

Java数据类型概述

Java语言包括两种数据类型:

  • 基本数据类型
    • 整数类型:包括byteshortintlong四种类型,用于表示整数。
    • 浮点类型:包括floatdouble两种类型,用于表示带小数点的数值。
    • 布尔类型:boolean类型,只有true和false两个值,用于表示逻辑值。
    • 字符类型:char类型,用于表示单个字符,它是基于Unicode编码的。
  • 引用数据类型
    • 类、接口、数组、枚举等。(或者你也可以这样记:除了8种基本数据类型之外,其他都是引用数据类型,包括String。)

现阶段重点研究基本数据类型,以后再说引用数据类型。

下面详细介绍一下每种类型的特点和使用方法:

  1. 整数类型:
  • byte类型:占用1个字节,范围是-128到127,常用于存储小整数。 (byte类型的1:00000001)
  • short类型:占用2个字节,范围是-32768到32767,常用于存储中等大小的整数。 (short类型的1:00000000 00000001)
  • int类型:占用4个字节,范围是-2147483648到2147483647,是Java中最常用的整数类型。
  • long类型:占用8个字节,范围是-9223372036854775808到9223372036854775807,用于存储极大或极小的整数。

为什么设计出这么多整数?目的是合适的数据选择合适的类型,可以节省空间,但实际开发中不必斤斤计较,大部分采用int。另外,如果数据过大,超过了long,可以使用BigInteger,它就不是基本数据类型了,属于引用数据类型。后面再说。

  1. 浮点类型:
  • float类型:占用4个字节,范围是1.4E-45到3.4028235E38,精度为7位小数,常用于科学计算和工程计算。
  • double类型:占用8个字节,范围是4.9E-324到1.7976931348623157E308,精度为15位小数,是Java中最常用的浮点类型。 (如果超出了double,可以使用BigDecimal,同样它也是一种引用数据类型。
  1. 布尔类型:
  • boolean类型:只有两个值,true和false,用于表示逻辑值,例如判断语句、循环语句等。
  1. 字符类型:
  • char类型:占用2个字节,用于表示单个字符,例如’A’、‘B’、'C’等,也可以表示Unicode编码中的任意字符。

这是一个直观的列表:

数据类型 占用字节数 取值范围 具体取值范围 默认值
byte 1 -2^7 ~ 2^7-1 -128 ~ 127 0
short 2 -2^15 ~ 2^15-1 -32768 ~ 32767 0
int 4 -2^31 ~ 2^31-1 -2147483648 ~ 2147483647 0
long 8 -2^63 ~ 2^63-1 -9223372036854775808 ~ 9223372036854775807 0L
float 4 1.4E-45 ~ 3.4028235E38 1.4E-45 ~ 3.4028235E38 0.0f
double 8 4.9E-324 ~ 1.7976931348623157E308 4.9E-324 ~ 1.7976931348623157E308 0.0d
boolean 1 true / false true / false false
char 2 0 ~ 2^16-1 0 ~ 65535 ‘\u0000’

关于默认值:Java语言中变量必须先声明,再赋值,才能使用。对于局部变量来说必须手动赋值,而对于成员变量来说,如果没有手动赋值,系统会自动赋默认值。例如:

public class DefaultValue {
    
    
    // 成员变量有系统默认值
    static int i;
    public static void main(String[] args){
    
    
        System.out.println(i); // 0
        // 成员变量没有系统默认值
        int k;
        System.out.println(k); // 编译报错
    }
}

注意:对于引用数据类型来说,默认值null,例如:

public class DefaultValue {
    
    
    static String name;
    public static void main(String[] args){
    
    
        // String是引用数据类型。
        System.out.println(name); // null
    }
}

整数型详解

整数型字面量的四种表示形式

Java中整数型字面量有以下四种表示形式:

  1. 十进制表示法:以数字0-9组成的常数,默认为十进制表示法。

例如:int a = 10;

  1. 二进制表示法:以0b或0B开头的常数,由0和1组合而成。

例如:int b = 0b101;

  1. 八进制表示法:以0开头的常数,由数字0-7组成。

例如:int c = 012;

  1. 十六进制表示法:以0x或0X开头的常数,由0-9和A-F(大小写均可)组成。

例如:int d = 0x1F;

整数型字面量默认当做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;

自动类型转换


在Java中,对于基本数据类型来说,小容量是可以直接赋值给大容量的,这被称为自动类型转换。对于数字类型来说大小关系为:byte < short < int < long < float < double。

下面是一些自动类型转换的示例:

double a = 10;      // 将int类型自动转换为double类型
int b = 100;
float c = b;        // 将int类型自动转换为float类型
long d = b;         // 将int类型自动转换为long类型
byte e = 10;
short f = e;        // 将byte类型自动转换为short类型

需要注意的是,自动类型转换只适用于基本数据类型之间的转换。

强制类型转换

强制类型转换:Java中大容量是无法直接转换成小容量的。因为这种操作可能会导致精度损失,所以这种行为交给了程序员来决定,当然这种后果自然是程序员自己去承担。因此在代码中需要程序员自己亲手加上强制类型转换符,程序才能编译通过。
以下程序编译器就会报错:

int num = 10L;

解决方案两个:要么把L去掉,要么使用强制类型转换符,例如:

int num = (int)10L;

这样编译器就能编译通过了。

强制类型转换时,底层二进制是如何变化的?原则:砍掉左侧多余的二进制。例如以上程序的二进制变化是这样的:
long类型的10对应的二进制:00000000 00000000 00000000 00000000 00000000 00000000 00000000 00001010
强制转换为int类型的10是这样的:00000000 00000000 00000000 00001010
因此,强制类型转换时,精度可能会损失,也可能不会损失,这要看具体的数据是否真正的超出了强转后的类型的取值范围。如下图:水可能溢出,也可能不会溢出,这要看真实存放的水有多少!!!

如果你理解了强制类型转换,那么下面这个程序的执行结果可以推算出来吗?

byte b = (byte)150;

int类型的150的补码(150是正数:原码反码补码一样):00000000 00000000 00000000 10010110
强转砍掉前三个多出的字节,结果是:10010110(这个是最终存储在计算机中的,注意:存储在计算机中的是补码)
将以上补码10010110推算出原码:11101010(结果是:-106)
因此int类型的150强转为byte类型之后,结果是-106

当整数字面量没有超出byte的范围

在Java中有这样一个规定,当整数型字面量没有超出byte的范围:可以直接赋值给byte类型的变量。

byte b = 127; // 这是允许的

很显然,这是一种编译优化。同时也是为了方便程序员写代码。
如果超出了范围,例如:

byte b = 128; // 编译报错

这样就会报错,需要做强制类型转换,例如:

byte b = (byte)128;

它的执行结果你知道吗?可以尝试推算一下,最终结果是:-128
在整数类型中,除了byte有这个待遇之外,short同样也是支持的。也就是说:如果整数型字面量没有超出short取值范围时,也是支持直接赋值的。

两个int类型做运算

两个int类型的数据做运算,最终的结果还是int类型:

int a = 10;
int b = 3;
int c = a / b; 
System.out.println(c); // 3

多种数据类型混合运算

在Java中,多种数据类型混合运算时,各自先转换成容量最大的类型,再做运算。

byte a = 100;
int b = 200;
long c = 300L;
long d = a + b + c;

你可以测试一下,如果d变量是int类型则编译器会报错。

编译器的小心思

以下程序编译通过:

byte x = 10 / 3;

为什么编译通过?这种情况下都是字面量的时候,编译器可以在编译阶段得出结果是3,而3没有超出byte取值范围。可以直接赋值。
以下程序编译报错:

int a = 10;
int b = 3;
byte x = a / b;

为什么编译失败?这种a和b都是变量的情况下,编译器是无法在编译阶段得出结果的,编译器只能检测到结果是int类型。int类型不能直接赋值给byte类型变量。
怎么解决?要么把x变量声明为int类型,要么强制类型转换,例如:

int a = 10;
int b = 3;
byte x = (byte)(a / b);

这里需要注意的是:注意小括号的添加,如果不添加小括号,例如:

int a = 10;
int b = 3;
byte x = (byte)a / b;

这样还是编译报错,因为只是将a强转为byte了,b还是int。byte和int混合运算,结果还是int类型。

浮点型详解

浮点型类型包括:

  • 单精度(float):4字节(32位)
  • 双精度(double):8字节(64位),精度较高,实际开发中double用的多。

浮点型字面量默认被当做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);

浮点型数据两种表示形式

第一种形式:十进制

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的平方

浮点型数据存储原理

以单精度float为例:

符号位:0表示整数。1表示负数。
指数位:比如小数0.123E30,其中30就是指数。表示0.123 * 10的30次幂。所以也有把指数位叫做偏移量的。最大偏移量127。
尾数位:浮点数的小数部分的有效数字。例如:0.00123,那么尾数位存储123对应的二进制。

从浮点型数据存储原理上可以看到,二进制中的指数位决定了数字呈指数级增大。因此float虽然是4个字节,但却可以表示比long更大的数值。因此float容量比long的容量大。

使用浮点数的注意事项

计算机的二进制位有限,现实世界中有无限循环的数字,例如3.333333333333333333…,因此计算机这种有限资源去存储无限数据是不可能的,所以浮点型数据在底层真实存储的时候都是采用近似值的方式存储的。尾数位越多精度越高。
实际上包括0.1这样简单的数字,浮点型数据也是无法精确存储的。(想了解更多,请查看相关文献)
这样就会有一个问题,请看以下程序:

double x = 6.9;
double y = 3.0;
double z = x / y;
System.out.println(z);

它的执行结果是:2.3000000000000003
并不是2.3
因此一旦有浮点型数据参与运算得出的结果,一定不要使用“==”与其它数字进行“相等比较”。例如,以下代码就存在问题:

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("相等");
}

也就是说,如果这两个数字之间的差小于0.000001,我就认为是相等的。
因此:如果有浮点型数据参与运算得出了结果,不要拿着这个结果和另一个数据进行“==”相等比较。

字符型详解

char:字符型,占用2个字节。取值范围065535。和short(-3276832767)所表示的个数相同。但char可以表示更大的整数。
字符型字面量采用单引号括起来,例如:‘a’、‘A’、‘0’、‘国’ 等。
字符型字面量只能是单个字符,不能是多个字符。
Java中char类型可以存储一个汉字。

char c1 = 'A';
char c2 = 'a';
char c3 = '0';
char c4 = '国';
char c5 = '¥';

// 编译报错
//char c6 = 'ab';

再看下面程序:

char x = '';

编译报错。由于单引号中没有任何字符,因此无法给 c 赋值,所以会导致编译报错,提示无效的字符字面量。
如果要赋给 c 一个空的字符,可以使用转义字符 ‘\u0000’ 来表示。如下所示:

char c = '\u0000'; // 赋给 c 一个空字符

注意:空字符与空格字符完全是两码事。

转义字符

Java 中的转义字符是一些在字符串中具有特殊含义的字符序列,它们以反斜线(\)开始。以下是 Java 中的一些常用转义字符:

  • \t: 表示制表符,相当于按下 Tab 键
  • \n: 表示换行符
  • \r: 表示回车符
  • “: 表示双引号(”)
  • ‘: 表示单引号(’)
  • : 表示反斜线(\)本身

这些转义序列可以用于不同的 Java 数据类型,如字符串、字符等。在 Java 编程中,转义字符可以帮助我们在字符串中表示一些特殊的字符,例如制表符、换行符、引号等。例如,下面的代码演示了如何使用转义字符来创建包含制表符和换行符的字符串:

String str = "Hello\tworld\nHow are you?";
System.out.println(str);

这个例子中,\t 和 \n 分别表示字符串中的制表符和换行符。输出结果是:

Hello   world
How are you?

字符编码的理解

字符编码(Character encoding)是计算机系统中使用的一种将字符集中的字符转换为二进制数据的方式,从而方便计算机的存储和传输。在计算机内部,所有的信息都是以二进制形式存储和处理的,因此字符编码是将字符和二进制数据之间的转换方式。每一个字符在计算机中都有其对应的二进制代码。不同的字符编码可以采用不同的编码方式将字符映射到二进制代码,最终这些二进制代码被存储在计算机内部。
在早期计算机系统中,字符编码主要采用的是 ASCII 编码,采用1个字节编码。最多可以表示256个字符。(实际上ASCII码表只用了128个。)
以下是ASCII码表:

十进制 字符 十进制 字符 十进制 字符 十进制 字符
0 NUL 32 SPACE 64 @ 96 `
1 SOH 33 ! 65 A 97 a
2 STX 34 " 66 B 98 b
3 ETX 35 # 67 C 99 c
4 EOT 36 $ 68 D 100 d
5 ENQ 37 % 69 E 101 e
6 ACK 38 & 70 F 102 f
7 BEL 39 71 G 103 g
8 BS 40 ( 72 H 104 h
9 HT 41 ) 73 I 105 i
10 LF 42 * 74 J 106 j
11 VT 43 + 75 K 107 k
12 FF 44 , 76 L 108 l
13 CR 45 - 77 M 109 m
14 SO 46 . 78 N 110 n
15 SI 47 / 79 O 111 o
16 DLE 48 0 80 P 112 p
17 DC1 49 1 81 Q 113 q
18 DC2 50 2 82 R 114 r
19 DC3 51 3 83 S 115 s
20 DC4 52 4 84 T 116 t
21 NAK 53 5 85 U 117 u
22 SYN 54 6 86 V 118 v
23 ETB 55 7 87 W 119 w
24 CAN 56 8 88 X 120 x
25 EM 57 9 89 Y 121 y
26 SUB 58 : 90 Z 122 z
27 ESC 59 ; 91 [ 123 {
28 FS 60 < 92 |124
29 GS 61 = 93 ] 125 }
30 RS 62 > 94 ^ 126 ~
31 US 63 ? 95 _ 127 DEL

作为程序员,我们应当记住以下几个常用字符的ASCII码:

  • a 对应ASCII码 97(b是98,以此类推)
  • A 对应ASCII码 65(B是66,以此类推)
  • 0 对应ASCII码 48(1是49,以此类推)

什么是解码?什么是编码?乱码是如何产生的?

在计算机系统中,解码(Decoding)和编码(Encoding)是两个常用的概念,分别表示将二进制数据转换为字符和将字符转换为二进制数据。
编码是将字符转换为二进制数据的过程。解码是将二进制数据转换为字符的过程。例如:

  • ‘a’ ---------按照ASCII码表编码-----------> 01100001
  • 01100001 --------按照ASCII码表解码------------> ‘a’

乱码是指在字符编码和解码的过程中,由于编码和解码所采用的字符集不一致,或者编码和解码所采用的字符集不支持某些字符,导致最终显示的字符与原始字符不一致。为了避免乱码的问题,我们需要统一使用一个字符集,并且在进行字符编码和解码时要保持一致。

常见的字符编码

常见的字符编码方式如下:

  1. ASCII 编码(American Standard Code for Information Interchange:美国信息交换标准编码):采用1个字节编码,包括字母、数字、符号和控制字符等。
  2. Latin-1编码(ISO 8859-1),采用1个字节编码。该编码方式是为了表示欧洲语言(如荷兰语、西班牙语、法语、德语等)中的字符而设计的,共支持 256 个字符。
  3. ANSI 编码(American National Standards Institute:美国国家标准协会):采用1个字节编码,支持英文、拉丁文等字符。
  4. Unicode 编码:可表示所有语言的字符。采用了十六进制表示,占用 2 个字节或 4 个字节,最多可表示超过一百万个字符。 (使用这种方式是有点浪费空间的,例如英文字符’a’其实采用一个字节存储就够了。)
  5. UTF-8 编码(Unicode Transformation Format,8-bit):基于 Unicode 编码的可变长度字符编码,能够支持多语言和国际化的需求,使用 1~4 个字节来表示一个字符,是目前 Web 开发中最常用的字符编码方式。 (一个英文字母1个字节,一个汉字3个字节。)
  6. UTF-16 编码:基于 Unicode 编码的可变长度字符编码,使用 2 或 4 个字节来表示一个字符,应用于很多较早的系统和编程语言中。 (一个英文字母2个字节。一个汉字4个字节。)
  7. UTF-32编码:基于Unicode编码的固定长度字符编码,其特点是每个字符占用4个字节。
  8. GB2312 编码(小):是中国国家标准的简体中文字符集,使用 2 个字节来表示一个汉字,是 GBK 编码的前身。
  9. GBK 编码(Guo Biao Ku)(中):是针对中文设计的一个汉字编码方式,使用 2 个字节来表示一个汉字,能够表示中国内地的所有汉字。
  10. GB18030编码(大):是中国国家标准GB 18030-2005《信息技术 中文编码字符集》中规定的字符集编码方案,用于取代GB2312和GBK编码。
  11. Big5 编码(大五码):是台湾地区的繁体中文字符集,使用 2 个字节来表示一个汉字,适用于使用繁体中文的应用场景。

每种编码方式都有其特点和适用场景。在进行软件开发、网站开发和数据存储时,需要根据实际情况选择适合的编码方式。

注意:Java语言中的字符char和字符串String,都是采用Unicode编码。

Unicode码表

Unicode码表的一部分:

十六进制码 字符 名称 符号
U+0020 空格 (space)
U+0021 ! 感叹号 (exclamation mark)
U+0022 " 双引号 (quotation mark)
U+0023 # 井号 (number sign)
U+0024 $ 美元 (dollar sign)
U+0025 % 百分号 (percent sign)
U+0026 & 和号 (ampersand)
U+0027 单引号 (apostrophe)
U+0028 ( 左括号 (left parenthesis)
U+0029 ) 右括号 (right parenthesis)
U+002A * 星号 (asterisk)
U+002B + 加号 (plus sign)
U+002C , 逗号 (comma)
U+002D - 减号 (hyphen,-minus sign)
U+002E . 句点 (full stop,period)
U+002F / 斜杠 (slash,forward slash)
U+0030 0 (digit zero)
U+0031 1 (digit one)
U+0032 2 (digit two)
U+0033 3 (digit three)
U+0034 4 (digit four)
U+0035 5 (digit five)
U+0036 6 (digit six)
U+0037 7 (digit seven)
U+0038 8 (digit eight)
U+0039 9 (digit nine)
U+003A : 冒号 (colon)
U+003B ; 分号 (semicolon)
U+003C < 小于号 (less than sign)
U+003D = 等于号 (equals sign)
U+003E > 大于号 (greater than sign)
U+003F ? 问号 (question mark)
U+0040 @ 艾特符号 (commercial at)
U+0041 A 拉丁大写字母A (Latin capital letter A)
U+0042 B 拉丁大写字母B (Latin capital letter B)
U+0043 C 拉丁大写字母C (Latin capital letter C)
U+0044 D 拉丁大写字母D (Latin capital letter D)
U+0045 E 拉丁大写字母E (Latin capital letter E)
U+0046 F 拉丁大写字母F (Latin capital letter F)
U+0047 G 拉丁大写字母G (Latin capital letter G)

在Java程序中也可以使用Unicode码来指定char变量的值:

char c = '\u0041';

输出结果是:A

网络上也有很多在线转码工具,例如:http://www.jsons.cn/unicode/

char参与的运算

Java中允许将一个整数赋值给char类型变量,但这个整数会被当做ASCII码值来处理,例如:

char c = 97;
System.out.println(c);

会将97当做ASCII码值,该码值转换char类型是字符’a’,所以输出结果是:a
但需要特别注意的是,这个码值有要求,不能超出char的取值范围。如果是这样的,编译会报错:

// 编译报错
char c = 65536;

所以结合之间的byte和short,可以有这样一个结论(记住):只要没有超出byte short char的取值范围,是可以直接赋值给byte short char类型变量的。例如:

byte b = 1;
short s = 1;
char c = 1;

再看以下程序输出结果:

System.out.println('a' + 1);

输出结果是:98。这是因为1是int类型,所以’a’会被转换为int类型。
再看以下程序输出结果:

char c = 'a' + 1;
System.out.println(c);

输出结果是:b。这是因为c的类型是char类型。
再看以下程序输出结果:

byte b = 1;
short s = 1;
char c = 1;
short num = b + s + c;

编译报错:第4行的等号右边是int类型,int类型无法赋值给short类型的变量。
这里有一个结论需要记住:byte short char混合运算时,各自会先转换成int再做运算。

布尔型详解

关于布尔型的值

Java中的布尔型,关键字:boolean
只有两个值:true、false。没有1和0这一说。
true表示真,false表示假。

布尔值通常使用在哪

Java中的布尔值(boolean)通常用于表示一些逻辑上的真假值,并在程序中进行逻辑控制。以下是布尔值在Java中常用的场景:

  1. 条件语句,if和while等语句中需要进行条件判断时,通常使用布尔类型的变量作为条件表达式,根据条件的真假情况执行不同的代码逻辑。
  2. 逻辑运算,布尔值是逻辑运算的基础,Java中的逻辑运算符有:与(&&)、或(||)、非(!)等,常用于对布尔值的运算和操作。
  3. 方法返回值,可以将布尔值作为方法的返回值,表示某种条件是否满足。
  4. 开关标记,布尔变量在程序中常用于开关标记的判断和设置,例如,当某个功能开启或关闭时,我们可以用布尔类型的变量来表示。

综上所述,Java中的布尔值在程序中有很多用途,可以在很多场景下提供非常便利的逻辑控制和判断能力。

下面是一个使用布尔值的简单案例:

boolean gender = true;
if(gender){
    
    
    System.out.println("男");
}else{
    
    
    System.out.println("女");
}

基本数据类型转换规则总结

  1. 八种基本数据类型,除布尔型之外,其它类型都可以互相转换。
  2. 小容量转换为大容量,叫做自动类型转换,容量从小到大的排序为:
    1. byte < short(char) < int < long < float < double
    2. 注意char比short可以表示更大的整数
  3. 大容量转换为小容量,叫做强制类型转换,需要加强制类型转换符才能编译通过,运行时可能损失精度,也可能不会损失。
  4. 整数字面量如果没有超出byte short char的取值范围,可以直接赋值给byte short char类型的变量。
  5. byte short char混合运算,各自先转换为int再做运算。
  6. 多种类型混合运算,各自先转换成容量最大的类型,再做运算。

变量和数据类型的作业题

  1. 请定义合理的变量用来存储个人信息(姓名、年龄、性别、联系电话),并编写程序定义这些变量,给变量赋值,并打印输出。输出效果如下:

姓名 年龄 性别 联系电话
张三 20 男 12545457585
李四 30 女 15622525855

  1. 有两个变量a和b,a变量中存储的数据100,b变量中存储的数据200,请编写程序交换两个变量中的数据。让a变量存储200,让b变量存储100。并且计算两个int类型数据的和,要求最终输出200+100=300的效果。
  2. 请分析以下程序中哪些是可以编译通过的,哪些是报错的
short s = 100;
s = s - 99;

byte b = 100;
b = b + 1;

char c = 'a';
int i = 20;
float f = .3F;
double d = c + i + f;

byte b1 = 11;
short s1 = 22;
short x = b1 + s1;

运算符(掌握)

运算符总览及优先级

下面是完整的Java运算符列表:

运算符类型 运算符 说明
算术运算符 + 加法
- 减法
* 乘法
/ 除法
% 取模
++ 自增
自减
关系运算符 == 相等
!= 不等于
> 大于
< 小于
>= 大于等于
<= 小于等于
逻辑运算符 & 逻辑与
| 逻辑或
! 逻辑非
&& 短路与
|| 短路或
按位运算符 & 按位与
| 按位或
^ 按位异或
~ 取反
<< 左移
>> 右移
>>> 无符号右移
赋值运算符 = 赋值
+= 加等于
-= 减等于
*= 乘等于
/= 除等于
%= 取模等于
&= 按位与等于
|= 按位或等于
^= 按位异或等于
<<= 左移等于
>>= 右移等于
>>>= 无符号右移等于
条件运算符 ? : 如果条件为真,则返回第一个值,否则返回第二个值
instanceof 运算符 instanceof 测试对象是否为特定类的实例
new 运算符 new 通过创建一个对象或数组来分配新的内存
. 运算符 . 对象成员访问,使用“对象.属性”或“对象.方法(参数)”

以上列出了在Java中可用的所有运算符。需要根据操作数来选择适当的运算符。并且运算符是有优先级的,优先级如下:由高到低:

运算符类型 运算符 优先级
后缀运算符 ++ –
一元运算符 + - ~ !
乘性运算符 * / %
加性运算符 + -
移位运算符 << >> >>>
关系运算符 < <= > >= instanceof
相等运算符 == !=
按位与运算符 &
按位异或运算符 ^
按位或运算符 |
逻辑与运算符 &&
逻辑或运算符 ||
三目运算符 ? :
赋值运算符 = += -= *= /= %= &= |= ^= <<= >>= >>>=

注意,优先级高的运算符会比优先级低的先执行,如果有多个操作符在同一个表达式中,则按照优先级解析。在表达式中使用圆括号可以明确调整优先级

算术运算符

算术运算符用法

+(加法、正数、字符串拼接)、-(减法、负数)、*(乘法)、/(除法)、%(取模)、++(自增1)、–(自减1)

// +
int num1 = +10;
int num2 = 3;
System.out.println(num1 + "+" + num2 + "=" + (num1 + num2));

// -
int num3 = 10;
int num4 = -3;
System.out.println(num3 - num4);

// *
int num5 = 10;
int num6 = 3;
System.out.println(num5 * num6);

// /
System.out.println(num5 / num6);
double num7 = 10.0;
int num8 = 3;
System.out.println(num7 / num8);
int num9 = 10;
int num10 = 3;
double num11 = num9 / num10;
System.out.println(num11);
    
// %
System.out.println(num5 % num6);
int x = 10;
int y = 3;
// 取模的运算公式: x - x / y * y
System.out.println(x % y); // 1
x = -10;
System.out.println(x % y); // -1
x = 10;
y = -3;
System.out.println(x % y); // 1

自增/自减

以自增为例讲解,自减自行研究。
++ 可以出现在变量前,也可以出现在变量后。单独使用时,都会让变量自加1。

int i = 10;
i++; 
System.out.println(i); // 11

int k = 10;
++k;
System.out.println(k); // 11

无论是i 还是 i,都等同于:i = i + 1;
如果出现在表达式当中,出现在变量前和变量后是不同的:
变量前:先自加1,后赋值。

int i = 10;
int k = ++i; // i = i + 1; k = i;
System.out.println("i = " + i); // 11
System.out.println("k = " + k); // 11

变量后:先赋值,后自加1。

int i = 10;
int k = i++; // k = i; i = i + 1;
System.out.println("i = " + i); // 11
System.out.println("k = " + k); // 10

接收用户键盘输入

System.out.println()可以完成输出,怎么在控制台接收用户键盘输入呢?以下代码先照抄,后期学完面向对象就明白了:

public class KeyInput {
    
    
    public static void main(String[] args){
    
    
        java.util.Scanner s = new java.util.Scanner(System.in);
        // 接收一个整数
        int num1 = s.nextInt();
        System.out.println("您输入的整数是:" + num1);
        // 接收一个浮点数
        double num2 = s.nextDouble();
        System.out.println("您输入的浮点数是:" + num2);
        // 接收一个字符串
        String str = s.next();
        System.out.println("您输入的字符串是:" + str);
    }
}

解读字节码指令

public class ReadClass{
    
    
    public static void main(String[] args){
    
    
        int i = 10;
    }
}

编译生成:ReadClass.class
如何查看字节码?javap -c ReadClass.class,以上程序字节码如下:

public class ReadClass {
    
    
  public ReadClass();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: bipush        10
       2: istore_1
       3: return
}

重点研究main方法中的字节码含义:
bipush指令:将字面量压入操作数栈。
istore_1指令:将操作数栈中顶部数据弹出,然后将该数据存放到局部变量表的第1个位置。

什么是局部变量表?什么是操作数栈?
每个方法在被调用时都会分配一个独立的空间,该空间中又包括局部变量表和操作数栈两个部分。
局部变量表用来存储方法中定义的局部变量、方法参数等等,它是在编译时确定大小的,具体的大小可以在字节码中看到。
操作数栈用来存储方法执行中的操作数据,操作数栈是一个后进先出(LIFO)的数据结构,Java 虚拟机在执行指令时会将数据压入操作数栈中,然后再从栈中取出数据进行计算。


public class ReadClass{
    
    
    public static void main(String[] args){
    
    
        int i = 10;
        int j = i;
    }
}

编译生成:ReadClass.class
javap -c ReadClass.class,以上程序字节码如下:

public class ReadClass {
    
    
  public ReadClass();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: bipush        10
       2: istore_1
       3: iload_1
       4: istore_2
       5: return
}

iload_1 指令:将局部变量表中第1个位置存储的数据复制一份,放到操作数栈当中。
istore_2 指令:将操作数栈顶部数据弹出,将其存放到局部变量表的第2个位置上。

public class ReadClass{
    
    
    public static void main(String[] args){
    
    
        int i = 10;
        i++;
    }
}

编译生成:ReadClass.class
javap -c ReadClass.class,以上程序字节码如下:

public class ReadClass {
    
    
  public ReadClass();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: bipush        10
       2: istore_1
       3: iinc          1, 1
       6: return
}

iinc指令:将局部变量表中第1个位置数据加1

算术运算符相关作业题

题目1:采用字节码解读的方式分析以下代码的区别

int i = 10;
int k = i++;
int i = 10;
int k = ++i;

题目2:以下程序输出结果是?

int a = 5;
int b = a++;
b = a++;
System.out.println("a = " + a);
System.out.println("b = " + b);

int c = 10;
int d = --c;
System.out.println("c = " + c);
System.out.println("d = " + d);

题目3:以下程序输出结果是?

int i = 10;
int k = i++ + ++i;
System.out.println(k);

int f = 10;
int m = f++ +f;
System.out.println(m);
System.out.println(f);

题目4:以下程序输出结果是?经典面试题

int i = 10;
i = i++;
System.out.println(i);

int i = 10;
i = ++i;
System.out.println(i);

题目5:从键盘上接收一个整数三位数,请分别输出它的个位、十位、百位。
题目6:681分钟是多少个小时+多少分钟。

关系运算符

关系运算符又叫做比较运算符。包括:>、 >=、 <、 <=、 ==、 !=
所有关系运算符的运算结果都是布尔类型,不是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

逻辑运算符

逻辑运算符:&(逻辑与)、 |(逻辑或)、 !(逻辑非)、^(逻辑异或)、 &&(短路与)、 ||(短路或)
逻辑运算符特点:逻辑运算符两边的操作数要求必须是布尔类型,并且最终运算结果也一定是布尔类型。

逻辑与&:两边操作数都是true,结果才是true。可以翻译为“并且”。
逻辑或|:两边操作数只要有一个是true,结果就是true。可以翻译为“或者”。
逻辑非!: !false就是true,!true就是false。
逻辑异或^:咱俩不一样,结果就是true。

短路与&&:和逻辑与&的运算结果相同。只是存在一种短路现象。(左边操作数为false时,右边操作数不执行)
短路或||:和逻辑或|的运算结果相同。只是存在一种短路现象。(左边操作数为true时,右边操作数不执行)

虽然短路与&&效率高于逻辑与&,但逻辑与&也有用武之地,具体看需求是怎样的。

按位运算符

按位运算符概述

按位运算符用于在二进制位级别上处理整数数据。主要包括:

  • 按位与 &
  • 按位或 |
  • 按位异或 ^
  • 按位取反 ~
  • 左移 <<
  • 右移 >>
  • 无符号右移 >>>

注意:按位运算符的操作数要求必须是整数。否则会出现编译错误。

按位与 &

将两个整数的二进制表示按位进行与运算,只有当相应的二进制位都为1时,结果才为1,否则结果为0

int a = 32;
int b = 25;
System.out.println(a & b); // 0

a的二进制:00100000
b的二进制:00011001
按位与之后:00000000

应用一下:请使用按位与运算符判断某个数字是否为奇数?思路:拿着这个数字和1进行按位与,如果结果是1,则表示该数字为奇数。

按位或 |

将两个整数的二进制表示按位进行或运算,只有当相应的二进制位都为0时,结果才为0,否则结果为1

int a = 32;
int b = 25;
System.out.println(a | b); // 57

a的二进制:00100000
b的二进制:00011001
按位或之后:00111001

应用一下:请将0这个数字中第2、4、6位的二进制位设置为1(这属于标志位设置的具体应用)

int flag = 0;
flag = flag | (1 << 1);
System.out.println(flag);

flag = flag | (1 << 3);
System.out.println(flag);

flag = flag | (1 << 5);
System.out.println(flag);

按位异或 ^

将两个整数的二进制表示按位进行异或运算,只有当相应的二进制位不同,结果才为1,否则结果为0

int a = 100;
int b = 200;
System.out.println(a ^ b); // 172

a的二进制:01100100
b的二进制:11001000
按位异或之后:10101100

应用一下:按位异或可以实现简单的加密和解密。

按位取反 ~

将整数的二进制表示按位进行取反运算,即0变为1,1变为0

System.out.println(~100); // -101

100的二进制:01100100
取反后:10011011(这是一个补码哦)
将补码转为原码:11100101 (-101)

应用一下:将某个数字指定位设置为0,例如有这样一个二进制:0b01101101,将其4个低位全部清除为0

// 待清除数据
int value = 0b01101101;
// 需要清除的是4个低位
int mask = 0b00001111;
// 使用以下方式运算进行位清除
int result = value & (~mask);
System.out.println(result);

左移 <<

它能够将一个二进制数的所有位向左移动指定的位数。左移运算符的运算规则如下:

  1. 将二进制数左移n位,相当于将数值乘以2的n次方。

例如,将二进制数0b1011左移2位,即为0b101100,相当于将11乘以2的2次方(即4),得到44。

  1. 左移运算符不会改变操作数的符号。

无论操作数是正数、负数还是零,左移运算符都只进行位级移动,不会改变符号。

  1. 左移运算符会对溢出进行截断。

应用一下:如何将2快速变成8?

System.out.println(2 << 2);

右移 >>

它能够将一个二进制数的所有位向右移动指定的位数。右移运算符的运算规则如下:

  1. 将二进制数右移n位,相当于将数值除以2的n次方。

例如,将二进制数0b101100右移2位,即为0b1011,相当于将44除以2的2次方(即4),得到11。

  1. 右移运算符对正数、负数和零的处理方式不同。
  • 对于正数,符号位不变,右移时左补0
  • 对于负数,符号位不变,右移时左补1。
  • 对于零,右移运算符操作后结果仍为零。
  1. 右移运算符会对溢出进行截断。

无符号右移 >>>

它能够将一个二进制数的所有位向右移动指定的位数,而不考虑符号位。无符号右移运算符的运算规则如下:

  1. 将二进制数右移n位,相当于将数值除以2的n次方,并将最高位填充为0。
  2. 任意一个数字经过无符号右移之后,最终结果一定是非负数(0或正整数)
  3. 无符号右移运算符对溢出进行截断。

赋值运算符

赋值运算符包括:

  • 基本赋值运算符
    • =
  • 扩展赋值运算符
    • +=、-=、*=、/=、%=、&=、|=、^=、>>=、<<=、>>>=

基本赋值运算符

=
等号右边先执行,将直接结果赋值给左边的变量。

int a = 10;
int b = 20;
int c = a + b;

扩展赋值运算符

+=、-=、*=、/=、%=、&=、|=、^=、>>=、<<=、>>>=
以 += 为例。i += 3; 表示 i = i + 3;
+= 就是先+后=,也就是先求和,然后将求和的结果重新赋值。
对于扩展赋值运算符来说,有一个非常重要的运算规则需要注意:扩展赋值运算符不会改变运算结果的类型。(即使精度损失了,也不会改变运算结果类型。)

byte b = 100;
b += 1000; // 运算完之后,虽然精度损失了,可是b类型仍然是byte类型

条件运算符

Java 语言中的条件运算符由 ? 和 : 组成,也被称为三元运算符。它的语法格式为:

布尔表达式 ? 表达式1 : 表达式2

当布尔表达式的值为 true 时,条件运算符的结果为表达式1的值,否则为表达式2的值。这种运算符常用于简化 if-else 语句的代码量。
使用条件运算符的主要步骤为:

  1. 编写带有条件判断的布尔表达式。
  2. 编写布尔表达式为 true 时执行的代码,即为表达式1。
  3. 编写布尔表达式为 false 时执行的代码,即为表达式2。

下面是一个条件运算符的简单示例:

int a = 5, b = 7;
int max = (a > b) ? a : b;
System.out.println("最大值为:" + max);

在上述代码中,首先定义了两个变量 a 和 b,然后使用条件运算符比较这两个变量的大小,取其中较大值作为变量 max 的值,最后输出结果。
当 a > b 的结果为 false 时,条件运算符的结果为表达式2,即 b 的值为变量 max 的值。当 a > b 的结果为 true 时,条件运算符的结果为表达式1,即 a 的值为变量 max 的值。
总的来说,条件运算符在 Java 中的使用相对简单,能够减少代码重复和代码量,常用于简单的条件处理和表达式值的判断。

运算符作业题

  1. 编写 Java 代码,输入一个半径值,计算圆的面积和周长,并输出结果。注意:圆的面积公式为 pi _ r _ r,周长公式为 pi _ 2 _ r,其中 pi 取 3.14。
public class Circle {
    
    
    public static void main(String[] args) {
    
    
        java.util.Scanner input = new java.util.Scanner(System.in);   //创建Scanner对象
        double pi = 3.14;   //定义圆周率为3.14
        System.out.print("请输入圆的半径值:");
        double r = input.nextDouble();   //输入半径值
        double S = pi * r * r;   //计算圆的面积
        double C = pi * 2 * r;   //计算圆的周长
        System.out.println("半径为" + r + "的圆的面积为:" + S);
        System.out.println("半径为" + r + "的圆的周长为:" + C);
    }
}
  1. 假设变量 a、b、c 分别为 6、9、10,请编写 Java 代码输出它们的最大值。
public class MaxValue {
    
    
    public static void main(String[] args) {
    
    
        int a = 6, b = 9, c = 10;
        int max = (a > b) ? ((a > c) ? a : c) : ((b > c) ? b : c);
        System.out.println("三个数中的最大值是:" + max);
    }
}
  1. 假设变量 n 为整数,请编写 Java 代码判断它是不是一个偶数。
public class IsEven {
    
    
    public static void main(String[] args) {
    
    
        java.util.Scanner input = new java.util.Scanner(System.in);   //创建Scanner对象
        System.out.print("请输入一个整数:");
        int n = input.nextInt();   //输入整数n
        String result = (n % 2 == 0) ? "是一个偶数" : "是一个奇数";
        System.out.println(n + " " + result);
    }
}
  1. 编写 Java 代码,输入三个整数,分别判断第一个数是否大于 0,第二个数是否小于 10,第三个数是否是偶数。如果都满足条件,则输出“三个条件都满足”,否则输出“不满足所有条件”。
public class CheckConditions {
    
    
    public static void main(String[] args) {
    
    
        java.util.Scanner input = new java.util.Scanner(System.in);   //创建Scanner对象
        System.out.print("请输入三个整数,以空格或回车分隔:");
        int a = input.nextInt();
        int b = input.nextInt();
        int c = input.nextInt();

        String result = (a > 0 && b < 10 && c % 2 == 0) ? "三个条件都满足" : "不满足所有条件";
        System.out.println(result);
    }
}
  1. 编写 Java 代码,输入一个年份,判断它是否是闰年。若该年份能被 4 整除且不能被 100 整除,或者能被 400 整除,则该年份为闰年。输出结果为“该年是闰年”或“该年不是闰年”。
public class LeapYear {
    
    
    public static void main(String[] args) {
    
    
        java.util.Scanner input = new java.util.Scanner(System.in);   //创建Scanner对象
        System.out.print("请输入一个年份:");
        int year = input.nextInt();

        if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) {
    
    
            System.out.println("该年是闰年");
        } else {
    
    
            System.out.println("该年不是闰年");
        }
    }
}

控制语句(掌握)

  1. 假设变量 x 为整数,请编写 Java 代码输出 x 的二进制表示中 1 的个数。

猜你喜欢

转载自blog.csdn.net/weixin_54585403/article/details/131088017