Java基本数据类型
# Java 基本数据类型
# Java 中的几种基本数据类型是什么?对应的包装类型是什么?各自占用多少字节?
8 种基本数据类型:byte 1,short 2,int 4,long 8,float 4,dobule 8,char 2,boolean 1
对应的包装类型:Byte,Short,Interger,Long,Float,Double,Character,Boolean
对应占用的字节:1,2,4,8,4,8,2,1
注意:short 是 2 字节
# 讲讲 int 的取值范围是多少?
想想字节和取值范围的关系。
- int 有 4 个字节,也就是占 32 个比特位,
- 所以取值范围就是【-2 的 31 次方到 2 的 31 次方减 1】,即约为 -2,147,483,648 到 2,147,483,647。
需要注意的是,这个范围是针对有符号的整数类型。
如果使用【无符号】的整数类型(如 Java 中的 unsigned int),则范围将从 0 到 2^32 - 1,即约为 0 到 4,294,967,295。
# 基本类型和包装类型的区别?
五大区别:
用途:
- 基本类型一般用来定义一些常量和局部变量,我们在其他地方比如方法参数、对象属性中很少会使用基本类型来定义变量。
- 包装类型可用于泛型,而基本类型不可以。
存储方式:
- 基本数据类型的【局部变量】存放在 Java 虚拟机栈中的局部变量表中,基本数据类型的【成员变量】(未被
static
修饰 )存放在 Java 虚拟机的堆中。 - 包装类型属于对象类型,几乎所有对象实例都存在于堆中。
占用空间:
相比于包装类型(对象类型),基本数据类型占用的空间往往非常小。
默认值:
成员变量包装类型的默认值是
null
,基本类型有默认值,但不固定。
- byte:0
- short:0
- int:0
- long:0L
- float:0.0f
- double:0.0d
- char:'\u0000'
- boolean:false
比较方式:
- 对于基本数据类型来说,
==
比较的是值。 - 对于包装数据类型来说,
==
比较的是对象的内存地址。所有整型包装类对象之间值的比较,全部使用equals()
方法。
# 包装类型的缓存机制了解吗?
另一种说法是:包装类型的常量池技术。
Java
基本数据类型的包装类型大部分都用到了缓存机制来提升性能(除了浮点型)。即会默认创建相应类型的缓存数据。
例如:Byte
,Short
,Interger
,Long
这 4 种包装类默认创建了数值 [-128,127] 的相应类型的缓存数据,Character
创建了数值在 [0,127] 范围的缓存数据,Boolean
直接返回 True
or False
。
当使用自动装箱的方式将一个基本数据类型的值转换为包装类型时,如果这个值在缓存范围内,那么就直接返回缓存中的对象,否则就创建一个新的对象。
由于包装类型的值是存储在堆内存中的,因此在进行大量的数值计算时,使用包装类型会比直接使用基本数据类型更加耗时和占用内存。为了提高程序的性能和效率,Java 提供了包装类型的缓存机制。
# 为什么要有包装类型?
在 Java 中,包装类型是将基本数据类型(如 int、double 等)封装成对象的一种机制。
Java 中引入包装类型的主要原因是为了提供一些额外的功能和灵活性,这些功能在基本数据类型上不可用。
以下是一些包装类型的常见用途:
- 包装类型可以使基本数据类型具有对象特性。例如,可以将包装类型存储在集合中(如 List、Map 等),而基本数据类型不能。
- 包装类型具有面向对象的行为。例如,可以使用
toString()
方法将包装类型转换为字符串,而基本数据类型不能。 - 包装类型可以为 null。例如,如果尝试将基本数据类型赋值为 null,则会引发空指针异常。但是,将包装类型赋值为 null 是可以的。(一般声明变量时用引用类型,而参数类型用基本数据类型)
- 包装类型支持自动装箱和拆箱。自动装箱是指将基本数据类型自动转换为相应的包装类型,而自动拆箱是指将包装类型自动转换为相应的基本数据类型。这使得代码更加简洁和易于编写。
总之,包装类型可以使 Java 代码更加灵活和易于维护。它们可以使基本数据类型具有对象特性,并提供了许多基本数据类型上不可用的功能。此外,Java 中的包装类型还可以用于处理空值和进行类型转换,这些在基本数据类型上是不可能实现的。
# 自动装箱与拆箱了解吗?原理是什么?
装箱就是将基本类型用它们对应的引用类型包装起来,原理是调用了包装类的 valueOf()
方法;
拆箱是将包装类型转换为基本数据类型,相应的调用的是 xxxValue()
方法,比如 intValue()
方法。
eg:
Integer i = 10
等价于Integer i = Integer.valueOf(10)
-- 装箱int n = i
等价于int n = i.intValue()
-- 拆箱
原理:
基本类型与相应的包装类型用 ==
号比较会怎么样?
Integer a = new Integer(10);
Integer b = new Integer(10);
System.out.println(a == b); // 输出 false,因为比较的是引用
int c = 10;
System.out.println(a == c); // 输出 true,因为自动拆箱后比较的是值
2
3
4
5
6
7
# 为什么浮点数运算的时候会有精度丢失的风险?
浮点数运算精度丢失代码演示:
float a = 2.0f - 1.9f;
float b = 1.8f - 1.7f;
System.out.println(a); // 0.100000024
System.out.println(b); // 0.099999905
System.out.println(a == b); // false
2
3
4
5
这是因为计算机在表示一个数字时,宽度是有限的,无限循环的小数存储在计算机时,只能被截断,所以就会导致小数精度发生损失的情况。
# 如何解决浮点数运算的精度丢失问题?
使用 BigDecimal
可以实现对浮点数的运算,并且不会造成精度丢失。
通常情况下,大部分需要浮点数精确运算结果的业务场景(比如涉及到钱的场景)都是通过 BigDecimal
来做的。
BigDecimal a = new BigDecimal("1.0");
BigDecimal b = new BigDecimal("0.9");
BigDecimal c = new BigDecimal("0.8");
BigDecimal x = a.subtract(b); // sub 减法
BigDecimal y = b.subtract(c);
System.out.println(x); /* 0.1 */
System.out.println(y); /* 0.1 */
System.out.println(Objects.equals(x, y)); /* true */
2
3
4
5
6
7
8
9
10
# 超过 long 整型的数据应该如何表示?
基本数值类型都有一个表达范围,如果超过这个范围就会有数值溢出的风险。
在 Java 中,64 位(8字节) long 整型是最大的整数类型。
如果需要表示超过 long 整型的数据,可以使用 Java 提供的 BigInteger
类。
import java.math.BigInteger;
public class BigIntegerDemo {
public static void main(String[] args) {
BigInteger a = new BigInteger("12345678901234567890");
BigInteger b = new BigInteger("98765432109876543210");
BigInteger c = a.add(b);
System.out.println(c);
}
}
2
3
4
5
6
7
8
9
BigInteger
类的加、减、乘、除等运算都是通过调用方法来实现的,而不是像基本数据类型那样使用运算符。BigInteger
内部使用int[]
数组来存储任意大小的整形数据。- 需要注意的是,BigInteger 类的运算时间和空间成本比基本数据类型高得多,即运算的效率相对较低。
# 什么是引用类型?
在 Java 中,除了基本数据类型和 void
类型以外,其它所有类型都是引用类型。
void
类型是一种特殊的类型,它表示没有返回值的方法或表达式的类型。- 在 Java 中,
void
类型不属于基本数据类型,也不属于引用类型,而是一种独立的类型。
常见的引用类型包括:
- 类类型(Class):代表类的类型。
- 接口类型(Interface):代表接口的类型。
- 数组类型(Array):代表数组的类型。
- 枚举类型(Enum):代表枚举的类型。
- 泛型类型(Generics):代表泛型类或泛型方法的类型。
- 注解类型(Annotation):代表注解的类型。
- 自定义类型(Custom):在程序中自定义的类型,如自定义的类、接口、枚举等。
MyClass obj = new MyClass(); // 使用 new 关键字创建 MyClass 类的对象,并将对象赋值给 obj 变量
此时,obj 变量存储的是对象的内存地址,即对象的引用。因此,通过 obj 变量可以访问对象的属性和方法。
# NPE 问题?
NPE 问题就是:空指针异常(NullPointerException,NPE)问题。
在 Java 中,自动拆箱是将包装类型自动转换为相应的基本数据类型的过程,而如果包装类型为null,自动拆箱就会引发空指针异常(NullPointerException,NPE)。
这是因为基本数据类型不支持为 null 值,因此在尝试使用为 null 的包装类型时,Java 会尝试将其转换为基本数据类型,从而引发 NPE 异常。
以下是一个简单的示例,展示了自动拆箱引发 NP E问题的情况:
Integer num = null;
int i = num; // 自动拆箱,将null转换为int类型,引发NPE异常
2
为了避免自动拆箱引发的 NPE 问题,可以使用条件语句或显式拆箱来检查包装类型是否为 null。例如,可以使用以下代码检查包装类型是否为 null:
Integer num = null;
// 条件语句
int i = (num != null) ? num : 0;
2
3
使用显式拆箱的代码如下:
Integer num = null;
// 显示拆箱
int i = num != null ? num.intValue() : 0;
2
3
可以看出来,两者代码差不多,在这里其实 num
等价于 num.intValue()
。
最后这个代码没加括号是因为:在 Java 中,三目运算符(?:
)的优先级比赋值运算符(=
)高,而比相等运算符(==
)和不等运算符(!=
)低。因此,num != null
会先执行,代码可以不加括号而直接编写。