在 Java 中,数据类型转换是一个常见的操作,主要分为 自动类型转换(隐式转换) 和 强制类型转换(显式转换)。Java 的数据类型可以分为两大类:
-
基本数据类型:如 byte
、short
、int
、long
、float
、double
、char
和boolean
。 -
引用数据类型:如类、接口、数组等。
以下是关于 Java 中所有数据类型的转换规则和示例。
一、基本数据类型的转换
1. 自动类型转换(隐式转换)
当从小范围的数据类型赋值给大范围的数据类型时,Java 会自动进行类型转换(隐式转换),不会丢失精度。
转换规则:
-
byte
→short
→int
→long
→float
→double
-
char
→int
示例代码:
publicclassImplicitConversion{
publicstaticvoidmain(String[] args){
byte b = 100;
short s = b; // 自动从 byte 转为 short
int i = s; // 自动从 short 转为 int
long l = i; // 自动从 int 转为 long
float f = l; // 自动从 long 转为 float
double d = f;// 自动从 float 转为 double
System.out.println("Byte: " + b);
System.out.println("Short: " + s);
System.out.println("Int: " + i);
System.out.println("Long: " + l);
System.out.println("Float: " + f);
System.out.println("Double: " + d);
}
}
2. 强制类型转换(显式转换)
当从大范围的数据类型赋值给小范围的数据类型时,需要手动进行强制类型转换。这种转换可能会导致数据溢出或精度丢失。
转换规则:
-
double
→float
→long
→int
→short
→byte
-
int
→char
示例代码:
publicclassExplicitConversion{
publicstaticvoidmain(String[] args){
double d = 123.456;
float f = (float) d; // 强制将 double 转为 float
long l = (long) f; // 强制将 float 转为 long
int i = (int) l; // 强制将 long 转为 int
short s = (short) i; // 强制将 int 转为 short
byte b = (byte) s; // 强制将 short 转为 byte
System.out.println("Double: " + d);
System.out.println("Float: " + f);
System.out.println("Long: " + l);
System.out.println("Int: " + i);
System.out.println("Short: " + s);
System.out.println("Byte: " + b);
}
}
注意事项:
-
如果数值超出目标类型的范围,会导致溢出。例如: int num = 129;
byte b = (byte) num; // 溢出,结果为 -127
System.out.println(b); // 输出 -127
int j=(int)10000000000L; //上面的代码相当于把long强制转换为int类型 //int 4个字节 一个字节8个比特位 那就是32位 //将10000000000转为2进制为10 0101 0100 0000 1011 1110 0100 0000 0000->34位,所以我们要将前面两位去掉->0101 0100 0000 1011 1110 0100 0000 0000->1410065408
byte,short定义时如果右边是整数常量,不超过他们的精度,不需要我们强转,jvm自动转型如果右边有变量参与,byte、short自动提升为int,然后结果再次赋值给byte或short变量,需要我们自己手动强转
---
### 3. **布尔类型(`boolean`)**
`boolean` 类型不能与其他任何数据类型进行转换,包括隐式和显式转换。
---
### 4. **字符类型(`char`)**
- `char` 是无符号的 16 位整数类型,表示 Unicode 字符(范围是 `0` 到 `65535`)。
- 可以与整数类型(如 `int`、`short` 等)互相转换。
#### 示例代码:
```java
public class CharConversion {
public static void main(String[] args) {
char c = 'A'; // 字符 'A' 的 ASCII 值是 65
int asciiValue = c; // 自动将 char 转为 int
System.out.println("Char to Int: " + asciiValue);
int num = 66;
char convertedChar = (char) num; // 强制将 int 转为 char
System.out.println("Int to Char: " + convertedChar);
}
}
二、引用数据类型的转换
1. 向上转型(隐式转换)
子类对象可以自动转换为父类类型,称为向上转型。这是安全的,因为子类对象始终包含父类的所有成员。
示例代码:
classAnimal{
voideat(){
System.out.println("Animal is eating");
}
}
classDogextendsAnimal{
voidbark(){
System.out.println("Dog is barking");
}
}
publicclassUpcastingExample{
publicstaticvoidmain(String[] args){
Dog dog = new Dog();
Animal animal = dog; // 向上转型:Dog -> Animal
animal.eat(); // 调用父类方法
// animal.bark(); // 错误:无法调用子类特有的方法
}
}
2. 向下转型(显式转换)
父类对象可以强制转换为子类类型,称为向下转型。需要注意的是,只有当父类对象实际上是子类实例时,向下转型才是安全的。
示例代码:
publicclassDowncastingExample{
publicstaticvoidmain(String[] args){
Animal animal = new Dog(); // 向上转型
Dog dog = (Dog) animal; // 向下转型:Animal -> Dog
dog.bark(); // 调用子类方法
}
}
注意事项:
-
如果父类对象不是子类实例,则向下转型会抛出 ClassCastException
。 -
使用 instanceof
检查类型是否兼容:if (animal instanceof Dog) {
Dog dog = (Dog) animal;
dog.bark();
} else {
System.out.println("Not a Dog instance");
}
三、字符串与其他数据类型的转换
1. 基本数据类型转字符串
使用 String.valueOf()
或直接拼接字符串。
示例代码:
int num = 123;
String str1 = String.valueOf(num); // 方法 1
String str2 = "" + num; // 方法 2
System.out.println(str1); // 输出 "123"
System.out.println(str2); // 输出 "123"
2. 字符串转基本数据类型
使用包装类的 parseXxx()
方法。
示例代码:
String str = "123";
int num = Integer.parseInt(str); // 将字符串转为 int
double d = Double.parseDouble(str); // 将字符串转为 double
System.out.println(num); // 输出 123
System.out.println(d); // 输出 123.0
注意事项:
-
如果字符串格式不正确,会抛出 NumberFormatException
。
进制运算
进制的分类
-
十进制 -
数字组成:0~9 -
进位规则:满十并不区分进一 -
二进制 -
数字组成:0-1 -
进位规则:满二进一,以0b或0B开头 -
八进制 -
数字组成:0-7 -
进位规则:满八进一,以数字0开头 -
十六进制 -
数字组成:0-9、a-f -
进位规则:满十六进一,以0x或0X开头表示,此处a-f不区分大小写
电脑打印出来统一转换为10进制
计算机数据的存储使用二进制补码形式存储
二进制转十进制
二进制如何表示整数
-
计算机数据的存储使用二进制补码形式存储,并且最高位是符号位
-
正数:最高位是0 -
负数:最高位是1 -
规定
-
负数的原码:把十进制转为二进制,然后最高位设置为1 -
负数的反码:在原码的基础上,最高位不变,其余位按位取反 -
负数的补码:反码+1 -
正数的补码和反码、原码一样,称为三码合一 -
负数的补码反码、原码不一样
十进制转二进制
除二取余的逆
二进制与八进制
三个二进制数代表一个八进制数(整数部分前面添0,小数小数点旁边添0) 8421规则
二进制与十六进制
四个二进制代表一个十六进制数(整数部分前面添0,小数小数点旁边添0)
位运算符
位运算符用于直接对整数的二进制表示进行操作,它们在处理底层数据、优化性能以及特定算法实现中有重要作用。结合之前提到的按位与(&)、按位或(|)、按位异或(^)、按位取反(~)、左移(<<)、右移(>>),再加上无符号右移(>>>),这里总结一下所有这些位运算符:
-
按位与(&):
-
对两个操作数的每一位执行与操作。只有当两个相应的二进制位都为1时,结果位才为1。 -
示例: 5 & 3
结果是1
。因为 (0101_2) 和 (0011_2) 按位与得到 (0001_2)。 -
按位或(|):
-
对两个操作数的每一位执行或操作。只要两个相应的二进制位有一个为1,结果位就为1。 -
示例: 5 | 3
结果是7
。因为 (0101_2) 和 (0011_2) 按位或得到 (0111_2)。 -
按位异或(^):
-
对两个操作数的每一位执行异或操作。当且仅当两个相应的二进制位不相同时,结果位为1。 -
示例: 5 ^ 3
结果是6
。因为 (0101_2) 和 (0011_2) 按位异或得到 (0110_2)。 -
按位取反(~):
-
对操作数的每一位执行取反操作,即把1变为0,把0变为1。 -
示例: ~5
在考虑有符号整数和补码表示的情况下,会将正数转换为其负数形式的补码表示。
左移运算符 (<<
)
左移运算符将一个数的所有位向左移动指定的位数,并在右边用0填充。无论操作数是正数还是负数,左移操作的行为基本一致,但需要注意的是,左移可能会导致数值超出其数据类型所能表示的最大范围,从而导致溢出。
-
正数:对于正数来说,左移n位相当于乘以2n2n。 -
示例:假设我们有一个8位系统, 5 << 1
(即 000001012000001012 左移一位)得到 000010102000010102,也就是十进制的10。 -
负数:对于使用补码表示的负数,左移同样会按照规则执行,但由于符号位(最高位)也被参与了计算,因此可能导致符号变化或数值溢出。 -
示例:在8位系统中, -5 << 1
(即 111110112111110112 补码形式的-5)左移一位后变为 111101102111101102,如果考虑溢出,这可能不再直接对应于某个特定的负数值,而是取决于具体环境如何处理溢出情况。
右移运算符
右移分为两种:带符号右移(>>
)和无符号右移(>>>
)。
带符号右移 (>>
)
带符号右移保留了数字的符号位,这意味着它执行算术右移。对于正数和负数,其行为有所不同:
-
正数:对于正数,右移n位相当于除以2n2n(向下取整)。 -
示例: 8 >> 1
(即 000010002000010002 右移一位)得到 000001002000001002,即4。 -
负数:对于负数,右移n位时,左边用符号位(通常是1)填充,保持数值为负。 -
示例:在8位系统中, -8 >> 1
(即 111110002111110002 补码形式的-8)右移一位后变为 111111002111111002,这是补码形式的-4。
无符号右移 (>>>
)
无符号右移不考虑符号位,总是用0填充左边被移出的高位,无论原数的最高位是什么。这对于处理无符号整数特别有用。
-
正数:对于正数,无符号右移与带符号右移效果相同,因为它不会改变数值的正性。 -
示例: 8 >>> 1
结果同样是4。 -
负数:对于负数,由于左边总是用0填充,这会导致原本代表负数的位模式变成一个很大的正数。 -
示例:在8位系统中, -8 >>> 1
(即 111110002111110002 补码形式的-8)无符号右移一位后变为 011111002011111002,这是一个非常大的正数(具体值取决于系统的字长和如何处理超出的位)。
1. 左移 (<<
)
假设我们有一个8位系统,为了简化示例,我们将使用8位来表示数值。
-
正数的例子:
5 << 1
-
原始的二进制形式: 00000101
(即十进制的5) -
左移一位后: 00001010
(即十进制的10) -
解释: 左移相当于乘以2(对于每次左移)。 -
负数的例子:
-5 << 1
-
原始的二进制形式(补码表示): 11111011
(即十进制的-5) -
左移一位后: 11110110
(如果考虑溢出,具体值取决于系统如何处理溢出) -
解释: 注意到符号位也被移动了,因此结果可能不再是简单的原数乘以2,特别是当涉及到溢出时。
2. 带符号右移 (>>
)
-
正数的例子:
8 >> 1
-
原始的二进制形式: 00001000
(即十进制的8) -
右移一位后: 00000100
(即十进制的4) -
解释: 对于正数,右移一位相当于除以2并向下取整。 -
负数的例子:
-8 >> 1
-
原始的二进制形式(补码表示): 11111000
(即十进制的-8) -
右移一位后: 11111100
(即十进制的-4,因为左边用1填充保持负号) -
解释: 负数右移时,左边用符号位(这里是1)填充,确保结果仍然为负数。
3. 无符号右移 (>>>
)
-
正数的例子:
8 >>> 1
-
原始的二进制形式: 00001000
(即十进制的8) -
无符号右移一位后: 00000100
(与带符号右移相同,在这种情况下) -
解释: 对于正数,无符号右移和带符号右移的结果是一样的。 -
负数的例子:
-8 >>> 1
-
原始的二进制形式(补码表示): 11111000
(即十进制的-8) -
无符号右移一位后: 01111100
(在8位系统中,这代表一个大正数,具体值取决于系统的解释) -
解释: 对于负数,由于左边总是用0填充,原本代表负数的位模式变成了一个非常大的正数。
总结
-
左移:对正数和负数都适用,主要影响是数值大小的变化;需要注意潜在的溢出问题。 -
带符号右移:适用于需要保留数值符号的情况,特别是处理负数时;左边用符号位填充。 -
无符号右移:适用于不需要考虑符号的场景,例如处理无符号整数;左边总是用0填充。
在计算机科学中,整数的二进制表示通常涉及三种编码方式:原码、反码和补码。这三种编码方式主要用于处理有符号整数(即包括正数、负数和零)。以下是每种编码方式的详细解释:
1. 原码(Sign-Magnitude)
-
定义:原码是最直观的表示法,首位(最左边的位)表示符号,0代表正数,1代表负数;其余位表示该数的绝对值。 -
示例: -
对于 +5(假设8位系统),原码为 00000101
-
对于 -5,原码为 10000101
2. 反码(One's Complement)
-
定义:对于一个给定的数值,其反码是通过将原码中的每个位取反得到的(即将1变为0,将0变为1)。特别地,对于正数,其反码与原码相同;对于负数,则需要对其非符号位进行取反操作。 -
示例: -
对于 +5(假设8位系统),反码为 00000101
(与原码相同) -
对于 -5,反码为 11111010
(对 +5 的原码除符号位外的所有位取反)
3. 补码(Two's Complement)
-
定义:补码是目前大多数计算机系统用来表示有符号整数的方法。它是基于反码的一个简单变体,即在反码的基础上加1。这种编码方式允许使用相同的硬件来执行加法和减法运算,并且解决了反码中存在的“正零”和“负零”的问题。 -
计算方法: -
正数的补码与其原码相同。 -
负数的补码可以通过对其对应正数的原码先求反码再加1得到。 -
示例: -
对于 +5(假设8位系统),补码为 00000101
-
对于 -5,补码为 11111011
(首先对 +5 的原码求反得到11111010
,然后加1)
特点与优势
-
补码的优势在于它简化了加法和减法运算的实现,因为可以使用同样的电路设计来处理这两种运算。此外,它还唯一地表示了零(即不存在+0和-0的区别),并且使得数值范围更高效地覆盖整个可能的表示范围。 -
在n位的二进制补码系统中,可以表示的数值范围是从 (-2^{n-1}) 到 (2^{n-1}-1)。
运算符优先级
-
后缀运算符:如后缀自增 i++
和后缀自减i--
-
一元运算符(前缀):如前缀自增 ++i
、前缀自减--i
、正号+
、负号-
、逻辑非!
、按位非~
-
算术运算符: -
乘法 *
、除法/
、取模%
(乘性) -
加法 +
、减法-
(加性) -
移位运算符:左移 <<
、带符号右移>>
、无符号右移>>>
-
关系运算符:小于 <
、大于>
、小于等于<=
、大于等于>=
、instanceof
-
相等运算符:等于 ==
、不等于!=
-
按位与 &
-
按位异或 ^
-
按位或 |
-
逻辑与 &&
-
逻辑或 ||
-
条件运算符(三元运算符): ?:
如boolean ? valueIfTrue : valueIfFalse
-
赋值运算符:简单赋值 =
、复合赋值+=
,-=
,*=
,/=
,&=
,^=
,|=
,<<=
,>>=
,>>>=
原文始发于微信公众号(安全泡泡鱼):JAVA
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论