一、数据类型
1.1 两种数据类型
在Java中,数据类型主要分为
基本数据类型和引用数据类型。
1.1.1 基本数据类型
基本数据类型共有四类八种:
- 整型:byte, short, int, long,
- 浮点型:float, double,
- 字符型:char,
- 布尔型:boolean
八种基本数据类型的位数、取值范围和默认值如下表:
| 数据类型 | 占用大小(字节) | 位数 | 取值范围 | 默认值 | 描述 |
|---|---|---|---|---|---|
| byte | 1 | 8 | -128(-2⁷)到 127(2⁷-1) | 0 | 最小的整数类型,适合用于节省内存的场景 |
| short | 2 | 16 | -32768(-2¹⁵)到32767(2¹⁵-1) | 0 | 较少使用,通常用于需要节省内存且数据范围在该区间的场景 |
| int | 4 | 32 | -2147483648(-2³¹)到2147483647(2³¹-1) | 0 | 最常用的整数类型,可以满足大多数日常编程中整数计算的需求,是Java中表示整数的默认类型 |
| long | 8 | 64 | -9223372036854775808(-2⁶³)到9223372036854775807(2⁶³-1) | 0L | 用于表示非常大的整数,当 int 类型无法满足需求时使用,定义时需要在数值后加上 L 或 l |
| float | 4 | 32 | 1.4E - 45 到 3.4028235E38 | 0.0F | 单精度浮点数,用于表示小数,精度相对较低,定义时需要在数值后加上 F 或 f |
| double | 8 | 64 | 4.9E - 324 到 1.7976931348623157E308 | 0.0D | 双精度浮点数,精度比 float 高,是Java中表示小数的默认类型 |
| char | 2 | 16 | '\u0000'(0)到 '\uffff'(65535) | '\u0000' | 用于表示单个字符,采用Unicode编码,可表示各种语言的字符 |
| boolean | 无明确字节大小 | 无明确位数 | true 或 false | false | 用于逻辑判断,只有两个取值,常用于条件判断和循环控制等逻辑场景 |
需要注意以下几点:
- 与C/C++不同,无论在16位系统还是在32位系统,int 类型都占用4字节,long 类型都占用8字节。
- 整型数据和浮点型数据都是默认有符号的。
- 整型数据默认是 int ,浮点型数据默认是 double 。
- 定义 float 类型变量时,赋值的数字后加上 f ,否则将被认为 double 。
- char 类型变量使用 Unicode 表示字符,可以存放整型数据和中文字符。
- boolean 类型变量不可以和 int 类型相转换,不存在 "1 表示 true,0 表示 false" 。
面试题1:int 和 long 是多少位、多少字节的?
- int 类型是32位、占4个字节,是一种有符号的整数类型,其取值范围是:-2³¹到2³¹-1。若在一个简单的计数器程序中使用 int 类型来存储计数值,可以表示的最大值是 2,147,483,647,超过这个值就会发生溢出,导致结果不符合预期。
- long 类型是64位、占8个字节,也是一种有符号的整数类型,其取值范围比 int 类型大,为:-2⁶³到2⁶³-1。在处理较大的整数数值时,若 int 类型不够用,就可以使用 long 类型。如在一个文件传输程序中,文件的大小可能会很大,此时使用 int 类型很有可能溢出导致数据异常,使用 long 类型就可以解决。
面试题2:long 和 int 可以互相转换吗?
- 在Java中,long 和 int 是可以相互转换的。由于 long 的数据范围比 int 的数据范围大,当 int 转换为 long 时是没有问题的,但是当 long 转换为 int 时可能会发生截断导致的溢出问题
- int(小范围类型)转换成 long(大范围类型)是可以自动完成转换的,直接进行赋值即可。如:int valInt = 10; long valLong = valInt;
- long(大范围类型)转换成 int(小范围类型)需要强制手动转换,可能导致数据溢出。如:long valLong = 10000L; int valInt = (int) valLong;
面试题3:数据类型的转换方式有哪些?
- 自动类型转换(隐式转换):当目标类型的数值范围大于原类型的数值范围时,Java会自动将原类型转换为目标类型,无需手动转换。如:将 int 转换为 long,将 float 转换为 double 等。
- 强制类型转换(显示转换):当目标类型的数值范围小于原类型的数值范围时,需要手动强制转换,语法:
目标类型 变量名称 = (目标类型) 原类型变量。由于目标类型数值范围较小,转换后可能导致数据丢失或溢出。如:将 long 转换为 int,将 double 转换为 float 等。- 字符串转换:Java提供了将字符串表示的数据转换为其他类型数据的方法。如:将字符串转换为 int 类型可以使用 Integer.parseInt() 方法;将字符串转换为 double 类型可以使用 Double.parseDouble() 方法等。
- 数值之间的转换:Java提供了一些数值类型之间的相互转换方法,如将整型转换为字符型,将字符型转换为整型等。这些转换方式可以通过包装类来实现如 Character 类、Integer 类等提供了相应的转换方法。
面试题4:基本数据类型互相转换会出现什么问题?
- 当小范围数据类型转换成大范围数据类型 :Java会自动进行转换,这种转换方式一般不会出现问题,是
安全的。- 当大范围数据类型转换成小范围数据类型 :需要手动强行转换,这种转换方式可能会发生
数据溢出或者精度损失的问题:前者主要是由于大范围数据在转换成小范围数据的过程中被截断了高位字节,这将会导致数据错误;后者是由于浮点数类型数据的精度不一致在进行转换时发生的精度损失,同样也会导致数据错误。
1.1.2 引用数据类型
引用数据类型不同于基本数据类型存储值,它们存储的是地址
在Java中,
字符串、八种基本数据类型的包装类和BigDecimal是引用数据类型。
字符串
int 和 字符串间相互转换:
Java
//int转字符串
int num = 10;
String str1 = num + ""; //法1
String str2 = String.valueOf(num); //法2
//字符串转int
String str = "100";
int n = Integer.parseInt(str);
在这里我们只需要掌握字符串和基本数据类型的相互转换就可以了。
八种基本数据类型的对应包装类
包装类的诞生目的:Java中的一些集合要求必须是对象才能进行操作,但是基本数据类型不是对象,因此设计包装类以解决该矛盾。
Java
// 集合类中只能存放对象元素
List<Integer> list = new ArrayList<>();
包装类的本质就是:将基本数据类型封装为对象,使得我们能够通过各种方法进行操作数据
| 基本数据类型 | 对应包装类 |
|---|---|
| byte | Byte |
| short | Short |
| int | Integer |
| long | Long |
| float | Float |
| double | Double |
| char | Character |
| boolean | Boolean |
- 除了 int 和 char ,其他数据类型的包装类均是原数据类型名首字母改大写。
- 由于包装类是引用类型数据(存放地址),因此值可以 为
null,但是基本数据类型不可以为 null
基本数据类型和对应包装类的相互转换方式是装箱和拆箱:
- 装箱 是
将值类型隐式或显式转换为引用类型的操作。 当将一个值类型(如int、float)赋值给对象变量时,系统会自动在堆(Heap)上创建新对象,复制原始值到该对象中,并返回对象的引用。这相当于"包装"值类型成一个独立的对象。- 拆箱 是
将引用类型显式转换回值类型的操作。它与装箱相反,系统将从堆上的对象中提取原始值,复制到栈内存中。拆箱必须显式指定目标类型(不像装箱可能隐式)。
Java
int i = 10;
Integer objInt = i; // 自动装箱
int j = objInt; // 自动拆箱,也可以显式定义目标类:(int) objInt
BigDecimal
在基本数据类型中,浮点数有 float 和 double,它们在某些场景(如资金计算)中可能会出现精度问题:
Java
System.out.println(0.05 + 0.01); // 0.06
System.out.println(1.0 - 0.42); // 0.58
System.out.println(4.015 * 100); // 401.5
System.out.println(123.3 / 100); // 1.233
/*
实际输出:
0.060000000000000005
0.5800000000000001
401.49999999999994
1.2329999999999999
*/
因此在涉及到资金问题时,我们可以使用Java引进的 BigDecimal类。
BigDecimal类时Java提供的用于任意精度计算的类,在java.math包中
使用BigDecimal来计算:
Java
import java.math.BigDecimal
// BigDecimal对象的创建,注意只能用字符串来创建,用浮点数会存在精度问题
BigDecimal a = new BigDecimal("11.1");
BigDecimal b = new BigDecimal("0.8");
// 计算两数之积
System.out.println(b.multiply(a));
// 结果:8.88
面试题1:装箱和拆箱是什么?
- 为了使基本数据类型能够用于集合和泛型等需要操作元素为对象的场景,Java允许通过"装箱"和"拆箱"操作实现基本数据类型和引用数据类型 (对象)的相互转换。
- Java会在赋值时会自动进行装箱和拆箱操作。在Java 1.5以后,编译器支持在给变量赋值时自动进行装箱或拆箱操作。
- 在调用方法时,编译器同样也会自动进行装箱或拆箱操作。
- 需要注意自动装箱和拆箱有一定的开销,在循环这种重复同一个操作的场景不适用,否则性能开销太大。
面试题2:Java为什么有Integer?
- Integer 是基本数据类型 int 的包装类,即把 int 类型数据包装陈 object 对象。
- 封装成对象有很多好处,可以将数据和操作数据的方法结合在一块。如 Integer 中有 parseInt() 等方法用于专门处理 int 类型数据。
- 此外另一个原因是Java中一切皆是对象,集合类必须要求首先是对象才能操作,而基本数据类型不是对象,导致集合类无法对基本数据类型进行处理,因此有包装类是很有必要的。
面试题3:Integer相比int来说,有哪些优点?
- Integer作为类封装了很多操作数据的方法(parseInt()、compareTo()等),而int只能存储数值
- 支持在集合类和泛型中使用:Java的集合框架(如ArrayList、HashMap等)要求存储对象而非基本数据类型,Integer作为对象可以直接用于集合,而int无法使用。使用集合能够高效管理数据,极大地简化数据存储和处理逻辑。
- 提供自动装箱/拆箱功能:Java提供了自动装箱和自动拆箱的机制,能够允许基本数据类型和对象类型随时转换,提高了开发的效率。
- Integer作为对象成员变量时,默认值为 null ,能够明确表示"缺失值
"或"未初始化",避免与零值混淆。当对未初始化的Integer变量进行操作时会抛出空指针异常。
面试题4:既然Integer这么好用,为什么还要保留int类型呢?
- 性能方面,int类型比Integer类型要快。int是基本数据类型,Integer是引用数据类型。Java中的基本数据类型是预定义的,不需要实例化就可以使用;而引用类型数据需要实例化成对象后才能使用。这意味着,使用int来存储一个整数时,不需要为其分配额外内存,但是使用Integer时必须为其分配额外内存以存储对象。因此,int类型在读写方面的效率比Integer类型要高效。此外,在进行条件比较时,int类型可以直接使用"=="来比较,但是Integer类型只能通过".equals()或.intValue() "来比较,增加了复杂度。
- 存储方面,int类型占用的字节数比Integer类型要少。在64位JVM上,开启引用压缩情况下,一个Integer对象占用至少16字节的内存空间,但是一个int类型数据只占用4字节的内存空间。
面试题5:Integer的缓存机制是什么?
- 当通过
valueOf()方法或自动装箱方式创建Integer对象时,对于特定范围内的值(默认是−128到127),Java会优先从缓存池中返回已有的实例,而非每次都创建新对象。这样做有利于显著提升小整数操作效率(尤其在集合类中),同时降低了对象创建的成本。
面试题6:为什么精确计算时使用BigDecimal而不用double?
- 在涉及精确计算场景时通常不使用double的原因在于:计算机中的二进制浮点数无法精确表示十进制小数。double等浮点数采用IEEE 754标准的二进制小数表示法,部分十进制分数在二进制中是无限循环小数(如0.1=1/10在二进制中是0.0001100110011...),这将会导致存储时被截断。同时,使用double进行连续计算时,会将误差扩大,导致结果不准确。
- BigDecimal在存储数值时采用
十进制整数+缩放比例的结构,这样保存的小数比二进制浮点数要精确。
二、运算符
2.1 算术运算符
Java中的算术运算符有:
- 四则运算符(+ - * / %)
- 增量运算符(+= -= *= /= %=)
- 自增/自减运算符(++ --)。
Java
int a = 20;
int b = 10;
System.out.println(a + b); // 30
System.out.println(a - b); // 10
System.out.println(a * b); // 200
System.out.println(a / b); // 2
System.out.println(a % b); // 0,求余数,得数为零表示 a 能被 b 整除
System.out.println(a += b); // 相当于 a = a + b,30
System.out.println(a -= b); // 相当于 a = a - b,10
System.out.println(a *= b); // 相当于 a = a * b,200
System.out.println(a /= b); // 相当于 a = a / b,2
System.out.println(a %= b);// 相当于 a = a % b,0
System.out.println(a ++);// 先使用 a 再运算,相当于 a = a + 1,21
System.out.println(a --);// 先使用 a 再运算,相当于 a = a - 1,19
System.out.println(++ a);// 先运算再使用 a ,相当于 a = a + 1,21
System.out.println(-- a);// 先运算再使用 a ,相当于 a = a - 1,19
需要注意的是:
- 四则运算符均是二元运算符,必须有两个操作数
- 除法运算的时候,int 类型和 int 类型的除法运算得到的结果仍然是 int 类型(舍去小数部分)。
- 进行取模运算时,右操作数不可以是 0 ,并且可以对浮点型数据进行取模。
- 当多种类型的数据进行混合运算时,结果会自动向数据范围大的类型转型。
2.2 关系运算符
关系运算符主要有:== != < > <= >= 六种,它们的运算结果均是布尔值
Java
int a = 10;
int b = 20;
System.out.println(a == b); // 对于基本数据类型来说,比较的是数值,false
System.out.println(a != b); // true
System.out.println(a < b); // true
System.out.println(a > b); // false
System.out.println(a <= b); // true
System.out.println(a >= b); // false
面试题:Java中的"=="和 equals()方法有什么区别?
- ==:用于比较基本数据类型 时,比较的是两个数据的
数值大小;用于比较引用数据类型 (对象)的时候,比较的是两个引用存储的地址,若地址相同说明两个引用指向的是同一个对象。- equals():源自Object类的方法,很多类重写了该方法使其能够对引用数据类型进行内容的比较。
2.3 逻辑运算符
逻辑运算符主要有:&& || ! ,它们的运算结果均是布尔值true或false。
其中,&&和 || 两个运算符是二元运算符,必须有两个操作数;!是一元运算符,只需要一个操作数即可。
它们的逻辑功能如下:
- 逻辑与&&:两个表达式都是真(true)结果才是真(true),否则结果均是假(false)。
- 逻辑或||:两个表达式都是假(false)结果才是假(false),否则结果均是真(true)。
- 逻辑非!:真变假,假变真。
短路现象:
- 当&&左侧表达式的值是false时,整个结果一定是false,此时右侧表达式不会被执行;
- 当 || 左侧表达式的值是true时,整个结果一定是true,此时右侧表达式不会被执行。
2.4 位运算符
位运算符主要包括:& | ~ ^ ,除了 ~ 是一元运算符,其余都是二元运算符。
按位运算就是以二进制位进行运算。
- 按位与 & :如果两个二进制位都是1,则结果也是1,否则为0。
- 按位或 | : 如果两个二进制位都是0,则结果也是0,否则为1。
- 按位取反 ~ :1变0,0变1。
- 按位异或 ^ : 如果两个二进制位数字相同,则结果是0,否则为1。
注意:
- 以0x开头的数字是十六进制数字,0xf表示十六进制数字15,换算成二进制数字是1111。
- 按位与 & 和按位或 | 不支持短路求值。
2.5 移位运算符
移位运算符主要有三个:<< >> >>> ,且全部都是二元运算符,都是按照二进制位运算的。
- 左移运算符 << :将二进制位的最左侧的数去掉,右侧补0。(正负可能改变)
- 右移运算符 >> :将二进制位的最右侧的数去掉,左侧补0(正数)或1(负数)。
- 无符号右移运算符 >>> :将二进制位的最右侧的数去掉,左侧补0。
注意:
左移一位,相当于该数 * 2;左移 N 位,相当于该数 * 2的 N 次方。
右移一位,相当于该数 / 2;右移 N 位,相当于该数 / 2的 N 次方。
2.6 条件运算符
条件运算符是一个三目运算符,其形式是:表达式1 ?表达式2 : 表达式3。
它的运算逻辑是:判断表达式1的真值,若真值为1就返回表达式2,否则返回表达式3
Java
int a = 10;
int b = 20;
int max = a>b ? a : b; //返回a和b的最大值
System.out.println(max); // 返回 20
本文到此就该一段落了,若有错误请尽管指出!😊🌹
完