基本数据类型与包装类型

基本数据类型

Java中有哪8种数据类型

基本类型位数字节默认值
int3240
short1620
long6480L
byte810
char162'u0000'
float3240f
double6480d
boolean1 false

对于 boolean ,官方未明确定义,它依赖于JVM厂商的具体实现。逻辑上占用1位,实际中考虑计算机高效存储因素。

注意:

  1. Java中使用long类型的数据一定要在数值后面加L,否则将作为整型解析。
  2. char a = ‘h’ char:单引号 , String a = “hello” Sting:双引号

包装类型(8中基本类型与之对应)

Integer、Short、Long、Byte、Character、Float、Double、Boolean。
除了int和char改变外其余首字母变大写。

基本类型与包装类型的区别

基本类型有默认值且不是Null,包装类型不赋值是Null
从JVM层面讲:
基本数据类型直接存放在Java虚拟机栈中的局部变量表中,而包装类型属于对象类型,对象实例都存在与堆中,相比对象类型,基本数据类型占用的空间较小。

局部变量表主要存放了编译期间可知的基本数据类型,对象引用类型,它不同于对象本身,可能是一个指向对象起始地址的引用指针,也可能是指向一个代表对象的句柄或其他与此对象的相关位置。

包装类型的常量池技术

Java基本类型的包装类型的大部分都实现了常量池技术。
Byte、Short、Integer、Long这4种包装类型默认创建了数值 [-128,127] 的相关类型的缓存数据,Character创建了数值在 [0,127] 范围的缓存数据,Boolean直接返回 TrueFalse

Integer缓存源码:

/**
*此方法将始终缓存-128到127(包括端点)范围内的值,并可以缓存此范围之外的其他值。
**/
public static Integer valueOf(int i) {
	if (i >= IntegerCache.low && i <= IntegerCache.high)
		return IntegerCache.cache[i + (-IntegerCache.low)];
	return new Integer(i);
}



public class Main {
	public static void main(String[] args) {
		Integer a = 100;
		Integer b = 100;
		Integer c = 128;
		Integer d = 128;
		System.out.println(a==b);   //true
		System.out.println(c==d);	//false
		}
}
/*
说的就是在通过valueOf 方法创建Integer 对象的时候,如果数值在[-128,127]之间,
便返回指向IntegerCache.cache 中已经存在的对象的引用;否则创建一个新的Integer对象。
所以上面代码中a 与b 相等, c 与d 不相等。*/

public class Main {
	public static void main(String[] args) {
		Integer i1 = 40;
		Integer i2 = new Integer(40);
		System.out.println(i1==i2);
	}
}

/* Integer i1=40这行代码会发生拆箱,等价于Integer i1 = Integer.valueOf(40)。
所以i1使用的是常量池中的对象,而Integer i1 = Integer(40)会直接创建新的对象。
所以答案位 false。*/

Character缓存源码:

public static Character valueOf(char c) {
    if (c <= 127) { // must cache
      return CharacterCache.cache[(int)c];
    }
    return new Character(c);
}

private static class CharacterCache {
    private CharacterCache(){}

    static final Character cache[] = new Character[127 + 1];
    static {
        for (int i = 0; i < cache.length; i++)
            cache[i] = new Character((char)i);
    }
}	

Boolean缓存源码:

public static Boolean valueOf(boolean b) {
    return (b ? TRUE : FALSE);
}


public class Main {
public static void main(String[] args) {
	Boolean a = false;
	Boolean b = false;
	Boolean c = true;
	Boolean d = true;
	System.out.println(a==b);		//true
	System.out.println(c==d);		//true
	}
}

如果超出对应范围会创建新的对象,缓存的范围区间的大小只是在性能和资源之间的权衡。

两种浮点数据类型包装类Float,Double并没有实现常量池技术。

public static Double valueOf(double d) {
	return new Double(d);
}

public class Main {
	public static void main(String[] args) {
		Integer i1 = 33;
		Integer i2 = 33;
		System.out.println(i1 == i2);// 输出 true
		Float i11 = 333f;
		Float i22 = 333f;
		System.out.println(i11 == i22);// 输出 false
		Double i3 = 1.2;
		Double i4 = 1.2;
		System.out.println(i3 == i4);// 输出 false
		}
}

最后看看下面这段程序的输出结果

public class Main {
	public static void main(String[] args) {
		Integer a = 1;
		Integer b = 2;
		Integer c = 3;
		Long g = 3L;
		int int1 = 12;
		int int2 = 12;
		Integer integer1 = new Integer(12);
		Integer integer2 = new Integer(12);
		Integer integer3 = new Integer(1);
		System.out.println("c==(a+b) ->"+ (c==(a+b)));
		System.out.println("g==(a+b) ->" + (g==(a+b)));
		System.out.println( "c.equals(a+b) ->" + (c.equals(a+b)));
		System.out.println( "g.equals(a+b) ->" + (g.equals(a+b)));
		System.out.println("int1 == int2 -> " + (int1 == int2));
		System.out.println("int1 == integer1 -> " + (int1 == integer1));
		System.out.println("integer1 == integer2 -> " + (integer1 ==
		integer2));
		System.out.println("integer3 == a1 -> " + (integer3 == a));
	}
}

/*
c==(a+b) ->true
g==(a+b) ->true
c.equals(a+b) ->true
g.equals(a+b) ->false
int1 == int2 -> true
int1 == integer1 -> true
integer1 == integer2 -> false
integer3 == a1 -> false
*/
1."=="运算符的两个操作数都是包装器类型的引用,则是比较指向的是否是同一个对象,而如果
其中有一个操作数是表达式(即包含算术运算)则比较的是数值(即会触发自动拆箱的过程)。所
以c==a+b , g==a+b 为true2.而对于equals 方法会先触发自动拆箱过程,再触发自动装箱过程。也就是说a+b,会先各自调
用intValue 方法,得到了加法运算后的数值之后,便调用Integer.valueOf 方法,再进行
equals 比较。所以c.equals(a+b)true 。而对于g.equals(a+b) , a+b 会先拆箱进行相加
运算,在装箱进行equals 比较,但是装箱后为Integer , g 为Long ,所以g.equals(a+b)false3. int1 == int2 为true 无需解释, int1 == integer1 ,在进行比较时, integer1 会先进行
一个拆箱操作变成int 型在进行比较,所以int1 == integer1 为true4. integer1 == integer2 -> false 。integer1 和integer2 都是通过new 关键字创建的,可以
看成两个对象,所以integer1 == integer2 为false 。integer3 == a1 -> false ,
integer3 是一个对象类型,而a1 是一个常量它们存放内存的位置不一样,所以integer3 ==
a1 为false ,具体原因可学习下java的内存模型。

注意:所有整型包装类对象之间值的比较,全部使用equals方法比较。
对于Integer var = ? 在 -128至127 之间的赋值,Integer对象在IntegerCache.cache产生会复用对象,这个区间可以直接用==判断,但是区间外的所有数据,都会在堆上产生,不会复用已有的对象,所以推荐用equals方法进行判断。

结论 : Integer 、Short 、Byte 、Character 、Long 这几个类的valueOf 方法的实现是类似
的。Double 、Float 的valueOf 方法的实现是类似的。然后是Boolean 的valueOf 方法是单独一组
的。

包装类型的存在理由

Java是面向对象的语言,对象就是其灵魂,除了定义一些常量与局部变量外,在很多比方比如方法参数、对象属性中很少会使用基本类型来定义变量。
若一个对象中的属性使用了基本类型,那这个属性就存在默认值,这个逻辑不正确,因为很多业务下,对象的某些属性没有赋值,我们希望它为null。除此之外泛型参数不能为基本类型,因为基本类型不是 Object 子类,应该用基本类型对应的包装类型代替。
比如:

public class HashMap<K,V> extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable {
}

Map<Integer, Set<String>> map = new HashMap<>();

自动拆箱与装箱

基本类型与包装类型的转换:

Integer i = 100;  //装箱
int n = i;   //拆箱

装箱就是调用了包装类的 valueOf() 方法,拆箱就是调用了 xxxValue() 方法。
所以

  • Integer i = 10等价于 Integer i = Integer.valueOf(10)
  • int n = i 等价于 int n = i.intValue();

自动拆箱引发的NPE问题

《阿里巴巴开发手册》有条规定

关于基本数据类型与包装数据类型的使用标准如下:
1)【强制】所有的POJO类属性必须使用包装数据类型。
2)【强制】RPC方法的返回值和参数必须使用包装数据类型。
3)【推荐】所有局部变量使用基本数据类型。
说明:POJO类属性没有初值是提醒使用者在需要使用时,必须自己显示地进行赋值,任何NPE问题,或者入库检查,都由使用者来保证。
正例:数据库查询结果可能为Null,因为自动拆箱,用基本数据类型接收有NPE风险。
反例:业务的交易报表上显示成交总额涨跌情况,即正负x%,x为基本数据类型,调用的RPC服务,调用不成功时,返回的默认值,页面显示为0%,这是不合理的,因该显示成中划线-。所以包装数据类型的null值,能够表示额外的信息,如:远程调用失败,异常退出。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

As_theWind

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值