数据类型
这条没有什么好多说的,无非就是Java中的基本类型和对象类型的掌握。可以再了解一些JDK如何自动转换方面的知识,包括装箱拆箱等,还要注意避免装箱之后的类型相等的判断。主要知识点:
基本类型:byte,short,int, long, float, double, boolean ,char
类型 | 类别 | 字节 | 取值范围 | 存储结构 |
---|---|---|---|---|
byte | 整型 | 1 | -27 ~ 27-1 | |
short | 整型 | 2 | -215 ~ 215-1 | |
int | 整型 | 4 | -231 ~ 231-1 | |
long | 整型 | 8 | -263 ~ 263-1 | |
float | 浮点型 | 4 | 3.402823e+38 ~ 1.401298e-45 | 1bit(符号位) 8bits(指数位) 23bits(尾数位) |
double | 浮点型 | 8 | 1.797693e+308~ 4.9000000e-324 | 1bit(符号位) 11bits(指数位) 52bits(尾数位) |
char | 字符型 | 2 | 0~216-1 | |
boolean | 布尔型 | 1 | true/false |
自动类型转换
-
两种类型是彼此兼容的
-
转换后的目标类型占的空间范围一定要大于被转化的源类型由低字节向高字节自动转换(黑线表示无数据丢失的自动数据转换,红线表示转换中可能发生精度丢失) <关于int转float发生精度丢失,而int转double可以无数据丢失,可参考 https://blog.youkuaiyun.com/qq_31778159/article/details/79284113
>
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3Aag1SZD-1589447120106)(en-resource://database/7921:1)]
强制数据转换
将容纳更多信息的数据类型转换成一个容量更小的数据类型,可能存在精度损失的风险,编译器要求程序员进行强制类型转换。强制转换过程中可能发生数据溢出,必须警惕。
数据类型自动提升
-
如果两个操作数其中有一个是double类型,另一个操作就会转换为double类型。
-
否则,如果其中一个操作数是float类型,另一个将会转换为float类型。
-
否则,如果其中一个操作数是long类型,另一个会转换为long类型。
-
否则,两个操作数都转换为int类型。
对应的对象类型:Integer 等类型到基本类型的转换, 装箱和拆箱
简单一点说,装箱就是自动将基本数据类型转换为包装器类型;拆箱就是自动将包装器类型转换为基本数据类型。
Integer举例:
public static Integer valueOf(int i) {
return i >= 128 || i < -128 ? new Integer(i) : SMALL_VALUES[i + 128];
}
public class Main {
public static void main(String[] args) {
Integer i1 = 100;
Integer i2 = 100;
Integer i3 = 200;
Integer i4 = 200;
System.out.println(i1==i2); //true
System.out.println(i3==i4); //false
}
}
代码的后面,我们可以看到它们的执行结果是不一样的,为什么,在看看我们上面的说明。
1、i1和i2会进行自动拆箱,执行了valueOf函数,它们的值在(-128,128]这个范围内,它们会拿到SMALL_VALUES数组里面的同一个对象SMALL_VALUES[228],它们引用到了同一个Integer对象,所以它们肯定是相等的。
2、i3和i4也会进行自动装箱,执行了valueOf函数,它们的值大于128,所以会执行new Integer(200),也就是说它们会分别创建两个不同的对象,所以它们肯定不等。
Double举例:
public class Main {
public static void main(String[] args) {
Double i1 = 100.0;
Double i2 = 100.0;
Double i3 = 200.0;
Double i4 = 200.0;
System.out.println(i1==i2); //false
System.out.println(i3==i4); //false
}
}
public static Double valueOf(double d) {
return new Double(d);
}
Double直接返回对象,因为在一定范围内整数是有限的,而浮点数可以说是无限的。
Boolean
直接返回内部已经创建好的对象,因为只有两个值。
总结:
1、需要知道什么时候会引发装箱和拆箱
2、装箱操作会创建对象,频繁的装箱操作会消耗许多内存,影响性能,所以可以避免装箱的时候应该尽量避免。
3、equals(Object o) 因为原equals方法中的参数类型是封装类型,所传入的参数类型(a)是原始数据类型,所以会自动对其装箱,反之,会对其进行拆箱 。
4、当两种不同类型用==
比较时,包装器类的需要拆箱, 当同种类型用==比较时,会自动拆箱或者装箱。
Object类型:equals, hashcode
hashCode()介绍
hashCode() 的作用是获取哈希码,也称为散列码;它实际上是返回一个 int 整数。这个哈希码的作用是确定该对象在哈希表中的索引位置。hashCode() 定义在 JDK 的 Object.java 中,这就意味着 Java 中的任何类都包含有 hashCode() 函数。
散列表存储的是键值对(key-value),它的特点是:能根据“键”快速的检索出对应的“值”。这其中就利用到了散列码!(可以快速找到所需要的对象) 。
equals() 的作用
用来判断两个对象是否相等。
重写equals要求
- 对称性:如果
x.equals(y)
返回是"true"
;,那么y.equals(x)
也应该返回是"true"
;。 - 反射性:
x.equals(x)
必须返回是"true"。 - 类推性:如果
x.equals(y)
返回是"true",而且y.equals(z)返回是"true",那么z.equals(x)也应该返回是"true"。 - 一致性:如果
x.equals(y)
返回是"true",只要x和y内容一直不变,不管你重复x.equals(y)
多少次,返回都是"true"。 - 非空性,
x.equals(null)
,永远返回是"false";x.equals(和x不同类型的对象)永远返回是"false"。
hashCode()与 equals()的相关规定
- 如果两个对象相等,则 hashcode 一定也是相同的
- 两个对象相等,对两个对象分别调用 equals 方法都返回 true
- 两个对象有相同的 hashcode 值,它们也不一定是相等的
- 因此,equals 方法被覆盖过,则 hashCode 方法也必须被覆盖
- hashCode() 的默认行为是对堆上的对象产生独特值。如果没有重写 hashCode(),则该 class 的两个对象无论如何都不会相等(即使这两个对象指向相同的数据)
String,Stringbuilder,StringBuffer
* 可变性:
-
String 类中使用 final 关键字修饰字符数组来保存字符串,
private final char value[]
( 在 Java 9 之后,String 类的实现改用 byte 数组存储字符串private final byte[] value;
),所以 String 对象是不可变的。 -
StringBuilder 与 StringBuffer 都继承自 AbstractStringBuilder 类,在 AbstractStringBuilder 中也是使用字符数组保存字符串char[]value 但是没有用 final 关键字修饰,所以这两种对象都是可变的。
线程安全性:
-
String 中的对象是不可变的,也就可以理解为常量,线程安全;
-
StringBuffer 对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。
-
StringBuilder 并没有对方法进行加同步锁,所以是非线程安全的。
性能:
每次对 String 类型进行改变的时候,都会生成一个新的 String 对象,然后将指针指向新的 String 对象。StringBuffer 每次都会对 StringBuffer 对象本身进行操作,而不是生成新的对象并改变对象引用。相同情况下使用 StringBuilder 相比使用 StringBuffer 仅能获得 10%~15% 左右的性能提升,但却要冒多线程不安全的风险。
总结:
-
操作少量的数据: 适用 String
-
单线程操作字符串缓冲区下操作大量数据: 适用 StringBuilder
-
多线程操作字符串缓冲区下操作大量数据: 适用 StringBuffer