在Java中,标识符是用来给变量、方法、类和包等命名的字符序列。标识符由字母、数字、下划线和美元符号组成,但是第一个字符必须是字母、下划线或美元符号。标识符不能包含空格或其他特殊字符,也不能与Java关键字相同。标识符的长度没有限制,但是建议使用有意义的、简洁的标识符,以提高代码的可读性和可维护性。
Java中的标识符可以标识以下内容:
以上是Java中常见的标识符,其中变量、方法、类、接口、枚举、注解和常量都是Java中的关键元素,而包和类型参数则是Java中的辅助元素。类型名称则是指Java中的基本类型和引用类型的名称,例如int、String、Object等。
Java中合法的标识符需要满足以下要求:
例如,以下是合法的标识符:
而以下是不合法的标识符:
判断以下标识符中哪些是合法的,哪些是不合法的
Java关键字是Java编程语言中预定义的具有特殊含义的保留字,这些保留字不能被用作标识符或变量名,而是在语法中有特定的用法和限制。Java关键字的作用是控制程序的逻辑和结构,这些关键字通常用于声明变量、定义类、控制程序流程、实现面向对象编程等。
Java关键字的种类有很多,包括基本数据类型关键字(如int、double、boolean等)、控制流程关键字(如if、for、while等)、访问权限关键字(如public、private、protected等)、类和对象关键字(如class、new、extends、super等)、异常处理关键字(如try、catch、finally等)等。不同的关键字有不同的作用和用法,程序员需要根据具体的需求选择合适的关键字来编写代码。
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中最基本的表达式,不需要进行计算或转换,直接使用即可。
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")); // 添加小括号优先级较高
变量可以看做是一个盒子,这个盒子可以存储数据。本质上,变量是内存当中的一块空间,这块空间有三要素(变量的三要素):
例如以下代码则表示声明了一个整数类型的变量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;
int age;
System.out.println(age); // 报错,原因是变量age没有赋值
System.out.println(num);
int num = 20;
int a, b, c = 300; // 表示声明三个int类型变量,分别起名a b c,但是a和b没有赋值,c赋值300
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);
}
}
作用域的不同主要是因为声明在不同位置的变量具有不同的生命周期。所谓的生命周期是:从内存开辟到内存释放。
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 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
---|---|---|---|---|---|---|---|---|---|---|
二进制 | 1 | 10 | 11 | 100 | 101 | 110 | 111 | 1000 | 1001 | 1010 |
要将一个十进制数转换为二进制数,可以使用以下步骤:
例如,将十进制数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。
将以下十进制的数字转换为二进制:
将以下二进制的数字转换为十进制:
八进制:满八进一。
将十进制数除以8,直到商为0,然后将每次的余数从下往上排列即为该十进制数的八进制表示。
例如,将十进制数27转换为八进制:
27 ÷ 8 = 3 … 3
3 ÷ 8 = 0 … 3
所以27的八进制表示为33。
八进制转换为十进制的方法如下:
例如,将八进制数 346 转换为十进制数:
3×8^2 + 4×8^1 + 6×8^0 = 3×64 + 4×8 + 6×1 = 198
因此,八进制数 346 转换为十进制数为 198。
满十六进一。
例如,将十进制数255转换为十六进制数:
将十六进制转换为十进制的方法是将每一位的十六进制数值乘以对应的权值,再将各位的结果相加。
例如,将十六进制数ABCD转换为十进制数:
另一种简便的方法是,将十六进制数中的每一位转换为4位的二进制数,再将这些二进制数转换为十进制数,最后将各位的结果相加。
二进制转换为十六进制的方法如下:
将二进制数从右往左每四位一组,不足四位则在左侧补0,得到若干个四位二进制数。
将每个四位二进制数转换为对应的十六进制数,可以使用下表进行转换:
| 二进制数 | 十六进制数 |
| — | — |
| 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 |
将每个四位二进制数对应的十六进制数按照从左往右的顺序排列,得到最终的十六进制数。
例如,将二进制数1101011010111011转换为十六进制数:
将每个十六进制数位转换为四位二进制数即可。
例如:将十六进制数 AF 转换为二进制数。
A 对应的二进制数为 1010,F 对应的二进制数为 1111,因此 AF 对应的二进制数为 10101111。
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的原码为01111111,其反码和补码均与原码相同。
负数的原码运算规则:将绝对值转换为二进制后,最高位改为1。
-5的原码:10000101
-5的反码:11111010(原则是:以原码作为参考,符号位不变,其他位取反。)
-5的补码:11111011(原则是:以反码作为参考,符号位不变,加1)
-128的原码为10000000,其反码为11111111,补码为10000000。注意,对于-128这个特殊的数,它的补码和原码相同。
虽然计算机底层是采用补码的形式存储的,但最终显示给人类的时候是以原码的形式显示的。所以大家需要具备这个能力!!!
将负数的补码形式除符号位外的所有位按位取反,再加1即可得到原码。
已知补码:10000001
它的原码是:11111111
结果是:-127
通过这个可以得出,对于一个字节来说,最大值127,最小值-128。
计算机采用补码形式进行数值计算的原因有以下几点:
下面是一个简单的数据演示:
假设我们要计算-3+2的结果,使用原码进行计算:
-3的原码为10000011,2的原码为00000010,进行加法运算得到的结果为10000101,转换成十进制为-5,这个结果是错误的。
使用补码进行计算:
-3的补码为11111101,2的补码为00000010,进行加法运算得到的结果为11111111,转换成十进制为-1,这个结果是正确的。
数据类型:决定了变量在内存中的空间大小。不同的数据类型,在内存中占用空间大小不同。
为什么需要学习数据类型?为了定义变量。
Java语言包括两种数据类型:
现阶段重点研究基本数据类型,以后再说引用数据类型。
下面详细介绍一下每种类型的特点和使用方法:
为什么设计出这么多整数?目的是合适的数据选择合适的类型,可以节省空间,但实际开发中不必斤斤计较,大部分采用int。另外,如果数据过大,超过了long,可以使用BigInteger,它就不是基本数据类型了,属于引用数据类型。后面再说。
这是一个直观的列表:
数据类型 | 占用字节数 | 取值范围 | 具体取值范围 | 默认值 |
---|---|---|---|---|
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中整数型字面量有以下四种表示形式:
例如:int a = 10;
例如:int b = 0b101;
例如:int c = 012;
例如:int d = 0x1F;
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
在Java中有这样一个规定,当整数型字面量没有超出byte的范围:可以直接赋值给byte类型的变量。
byte b = 127; // 这是允许的
很显然,这是一种编译优化。同时也是为了方便程序员写代码。
如果超出了范围,例如:
byte b = 128; // 编译报错
这样就会报错,需要做强制类型转换,例如:
byte b = (byte)128;
它的执行结果你知道吗?可以尝试推算一下,最终结果是:-128
在整数类型中,除了byte有这个待遇之外,short同样也是支持的。也就是说:如果整数型字面量没有超出short取值范围时,也是支持直接赋值的。
两个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类型。
浮点型类型包括:
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 中的一些常用转义字符:
这些转义序列可以用于不同的 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码:
什么是解码?什么是编码?乱码是如何产生的?
在计算机系统中,解码(Decoding)和编码(Encoding)是两个常用的概念,分别表示将二进制数据转换为字符和将字符转换为二进制数据。
编码是将字符转换为二进制数据的过程。解码是将二进制数据转换为字符的过程。例如:
乱码是指在字符编码和解码的过程中,由于编码和解码所采用的字符集不一致,或者编码和解码所采用的字符集不支持某些字符,导致最终显示的字符与原始字符不一致。为了避免乱码的问题,我们需要统一使用一个字符集,并且在进行字符编码和解码时要保持一致。
常见的字符编码方式如下:
每种编码方式都有其特点和适用场景。在进行软件开发、网站开发和数据存储时,需要根据实际情况选择适合的编码方式。
注意:Java语言中的字符char和字符串String,都是采用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/
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中常用的场景:
综上所述,Java中的布尔值在程序中有很多用途,可以在很多场景下提供非常便利的逻辑控制和判断能力。
下面是一个使用布尔值的简单案例:
boolean gender = true;
if(gender){
System.out.println("男");
}else{
System.out.println("女");
}
姓名 年龄 性别 联系电话
张三 20 男 12545457585
李四 30 女 15622525855
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."":()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."":()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."":()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);
它能够将一个二进制数的所有位向左移动指定的位数。左移运算符的运算规则如下:
例如,将二进制数0b1011左移2位,即为0b101100,相当于将11乘以2的2次方(即4),得到44。
无论操作数是正数、负数还是零,左移运算符都只进行位级移动,不会改变符号。
应用一下:如何将2快速变成8?
System.out.println(2 << 2);
它能够将一个二进制数的所有位向右移动指定的位数。右移运算符的运算规则如下:
例如,将二进制数0b101100右移2位,即为0b1011,相当于将44除以2的2次方(即4),得到11。
它能够将一个二进制数的所有位向右移动指定的位数,而不考虑符号位。无符号右移运算符的运算规则如下:
赋值运算符包括:
=
等号右边先执行,将直接结果赋值给左边的变量。
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 语句的代码量。
使用条件运算符的主要步骤为:
下面是一个条件运算符的简单示例:
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 中的使用相对简单,能够减少代码重复和代码量,常用于简单的条件处理和表达式值的判断。
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);
}
}
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);
}
}
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);
}
}
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);
}
}
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("该年不是闰年");
}
}
}