java void包装类型_Java数据类型——基本类型/包装类型

Java数据类型(基本类型/引用类型)

基本类型

Java语言提供了八种基本类型。六种数字类型(四个整数型,两个浮点型),一种字符类型,还有一种布尔型;Java是一种面向对象语言,为了让基本类型具备对象特性(比如方法调用),Java为每种基本类型提供了一个包装类。

类型

默认值

占用储存空间

存储范围

包装类

byte

0

1*byte

-128(-2^7)~ 127(2^7-1)

Byte

short

0

2*byte

-32768(-2^15)~ 32767(2^15 - 1)

Short

int

0

4*byte

-2,147,483,648(-2^31)~ 2,147,483,647(2^31 - 1)

Integer

long

0L

8*byte

-9,223,372,036,854,775,808(-2^63)~ 9,223,372,036,854,775,807(2^63 -1)

Long

float

0.0f

4*byte

1.4E-45 ~ 3.4028235E38

Float

double

0.0d

8*byte

4.9E-324 ~ 1.7976931348623157E308

Double

boolean

false

1*bit

false、true

Boolean

char

false

2*byte

\u0000 ~ \uffff

Character

因为Java为每种基本类型提供了包装类型,实际工作中,更多的是使用包装类型;那对于包装类型的比较应该是大家都会遇到的一些坑;

装箱与拆箱

package com.sjw.box;

public class DateType {

public static void main(String[] args) {

int i = 100;

// 自动装箱(autoboxing) 等价于 Integer a=Integer.valueOf(100);

Integer a = 100;

Integer b = 100;

// 包装类型与基本类型通过==比较,比较值大小

// 拆箱(unboxing) 等价于 a.intValue()==i

System.out.println(a == i);

// 包装类型通过==比较,比较地址

System.out.println(a == b);

// 包装类型通过equals比较,比较值大小

System.out.println(a.equals(b));

}

}

为了增强说服力,下面我们通过JDK自带的javap反解析工具,分析上述Java代码编译生成的Class文件。

// javap -c -l DateType.class

Compiled from "DateType.java"

public class com.sjw.box.DateType {

public com.sjw.box.DateType();

Code:

0: aload_0

1: invokespecial #1 // Method java/lang/Object."":()V

4: return

LineNumberTable:

line 3: 0

LocalVariableTable:

Start Length Slot Name Signature

0 5 0 this Lcom/sjw/box/DateType;

public static void main(java.lang.String[]);

Code:

// 将int 100 push压入操作数栈

0: bipush 100

// pop出栈(int 100),赋值给局部变量表索引为1的变量 int i

2: istore_1

// 将int 100 push压入操作数栈

3: bipush 100

// pop出栈(int 100),调用Integer.valueOf,并将方法返回值(引用)push压入操作数栈;装箱(autoboxing)

5: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;

// pop出栈(引用地址),赋值给局部变量表索引为2的变量 Integer a

8: astore_2

// 将int 100 push压入操作数栈

9: bipush 100

// pop出栈(int 100),调用Integer.valueOf,并将方法返回值(引用)push压入操作数栈;装箱(autoboxing)

11: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;

// pop出栈(引用地址),赋值给局部变量表索引为3的变量 Integer b

14: astore_3

// 将静态属性PrintStream System.out push压入操作数栈

15: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;

// 将局部变量表索引为2的引用类型变量a push压入操作数栈

18: aload_2

// pop出栈a(引用)调用其intValue方法,并将返回值push压入操作数栈;拆箱(unboxing)

19: invokevirtual #4 // Method java/lang/Integer.intValue:()I

// 将局部变量表索引为1的int类型变量i push压入操作数栈

22: iload_1

// pop操作数栈栈顶2个元素,进行int比较,不相等跳转到Code 30,相等继续

23: if_icmpne 30

// 将int常量1压入操作数栈

26: iconst_1

// 跳转Code 31

27: goto 31

// 将int常量0压入操作数栈

30: iconst_0

// pop操作数栈栈顶2个元素(out,1或0),以栈顶元素(1或0)作为参数调用out引用的方法println

31: invokevirtual #5 // Method java/io/PrintStream.println:(Z)V

34: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;

// 将局部变量表索引为2的引用类型变量a push压入操作数栈

37: aload_2

// 将局部变量表索引为3的引用类型变量b push压入操作数栈

38: aload_3

// pop操作数栈栈顶2个元素,进行引用对象比较(比较地址),不相等跳转到Code 46,相等继续

39: if_acmpne 46

42: iconst_1

43: goto 47

46: iconst_0

47: invokevirtual #5 // Method java/io/PrintStream.println:(Z)V

50: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;

53: aload_2

54: aload_3

55: invokevirtual #6 // Method java/lang/Integer.equals:(Ljava/lang/Object;)Z

58: invokevirtual #5 // Method java/io/PrintStream.println:(Z)V

// 将局部变量表索引为2的引用类型变量a push压入操作数栈

61: aload_2

// pop出栈a(引用)调用其intValue方法,并将返回值push压入操作数栈;拆箱(unboxing)

62: invokevirtual #4 // Method java/lang/Integer.intValue:()I

// pop出栈(int 100),赋值给局部变量表索引为1的变量 int i

65: istore_1

66: return

// Java代码行与Code汇编指令行偏移量映射表

LineNumberTable:

// Java代码第5行代码对用Code汇编指令0-2

line 5: 0

// Java代码第7行代码对用Code汇编指令3-8

line 7: 3

line 8: 9

line 11: 15

line 13: 34

line 15: 50

line 17: 61

line 18: 66

// 局部变量表

LocalVariableTable:

Start Length Slot Name Signature

// 函数参数args

0 62 0 args [Ljava/lang/String;

// int变量 i

3 59 1 i I

// Integer变量 a

9 53 2 a Ljava/lang/Integer;

// Integer变量 b

15 47 3 b Ljava/lang/Integer;

}

注本文重点不是分析javap反解析class字节码文件的具体信息,具体如何分析、汇编指令等参考官方文档。

经过上面简单分析,了解int包装类型Integer的装箱(Integer.valueOf)、拆箱(Integer.intValue)的过程;对于常见的面试题

public void compare(Integer arg) {

int a = 0;

Integer b = 1;

// 拆箱(unboxing),arg.intValue();如果arg为null,会出现NPE(NullPointerException)

if (arg == a) ;

// 不需要拆箱(unboxing),比较引用对象,即地址的比较

if (arg == b) ;

// 如果arg为null,会出现NPE(NullPointerException)

if (arg.equals(b)) ;

// 如果arg为null,不会出现NPE(NullPointerException),

if (b.equals(arg)) ;

}

// Integer.java源码

public boolean equals(Object obj) {

// 如果obj为null,直接返回false

if (obj instanceof Integer) {

return value == ((Integer)obj).intValue();

}

return false;

}

包装对象的缓存

正如前面介绍,包装对象的值大小比较需要使用equals,在一定范围内包装类型==比较是可以的,注意不是比较值,依然比较地址

public void compare() {

// 装箱(autoboxinxg) Integer a = Integer.valueOf(100);

Integer a = 100;

Integer b = 100;

Integer c = new Integer(100);

Integer e = 1000;

Integer f = 1000;

// ture

if (a == b) ;

// false

if (a == c) ;

// ?

if (e == f) ;

}

上述代码ef是ture还是false?不要急向下看,Java如何缓存对象的?如何分析,我们知道new出来的对象用比较,只要没用引用同一个对象就为false;对于通过装箱来的包装对象可能用的是缓存对象,我们知道装箱的过程是通过valueOf()方法的;

// valueOf源码

public static Integer valueOf(int i) {

if (i >= IntegerCache.low && i <= IntegerCache.high)

return IntegerCache.cache[i + (-IntegerCache.low)];

return new Integer(i);

}

private static class IntegerCache {

static final int low = -128;

// 缓存的范围的最大值可配置

static final int high;

static final Integer cache[];

static {

// high value may be configured by property

int h = 127;

// 可通过jvm参数-XX:AutoBoxCacheMax进行配置

String integerCacheHighPropValue =

sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");

if (integerCacheHighPropValue != null) {

try {

int i = parseInt(integerCacheHighPropValue);

// 取127与配置值的最大值

i = Math.max(i, 127);

// Maximum array size is Integer.MAX_VALUE

h = Math.min(i, Integer.MAX_VALUE - (-low) -1);

} catch( NumberFormatException nfe) {

// If the property cannot be parsed into an int, ignore it.

}

}

high = h;

cache = new Integer[(high - low) + 1];

int j = low;

for(int k = 0; k < cache.length; k++)

cache[k] = new Integer(j++);

// range [-128, 127] must be interned (JLS7 5.1.7)

assert IntegerCache.high >= 127;

}

private IntegerCache() {}

}

通过int的包装类型Integer分析了一下装箱(autoboxing)、拆箱(unboxing),以及缓存IntegerCache是如何实现的,并且IntegerCache缓存范围默认是-128~127,max value可以通过jvm参数-XX:AutoBoxCacheMax进行配置;假设jvm参数加上-XX:AutoBoxCacheMax=2000;会缓存value值为-127~2000的封装类型,以供通过装箱生成的对象实例;到这里可以回答e==f是true还是false。

对于其他基本类型long、float、double对应的包装类型Long、Float、Double装箱、拆箱以及是否缓存,感兴趣的可以通过Java源码学习了解一下;本文代码样例采用JDK1.8。

知其然,更要知其所以然。。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值