int是我们常说的整形数字,是Java的8个原始数据类型(Primitive Types,boolean、byte 、short、char、int、foat、double、long)之一。 Java语言虽然号称一切都是对象,
但原始数据类型是例外。
Integer 是 int 对应的包装类,它有一个 int 类型的字段存储数据,并且提供了基本操作,比如数学运算、 int 和字符串之间转换等。在 Java 5 中,引入了自动装箱和自动拆箱功能
( boxing/unboxing ), Java 可以根据上下文,自动进行转换,极大地简化了相关编程。
demo:
自动装箱:Integer a = 5; --→ Integer a = new Integer(5);
自动拆箱:Integer a = 5; --→ 可以直接赋值给基本数据类型 int n = a;
从反编译得到的字节码内容可以看出,在装箱的时候自动调用的是Integer的valueOf(int)方法。而在拆箱的时候自动调用的是Integer的intValue方法,其他类型也一样。
关于 Integer 的值缓存,这涉及 Java 5 中另一个改进。构建 Integer 对象的传统方式是直接调用构造器,直接 new 一个对象。但是根据实践,我们发现大部分数据操作都是集中在有
限的、较小的数值范围,因而,在Java 5中新增了静态工厂方法valueOf,在调用它的时候会利用一个缓存机制,带来了明显的性能改进。按照Javadoc, 这个值默认缓存是-128到127之间,当值较小且频繁使用时,推荐优先使用整型池方法(时间与空间性能俱佳)。
但是根据源码:
Integer a = 1;//自动走缓存装箱
Integer a = new Integer(1);//在堆区内存中生成新实例,是不走缓存的
这种缓存行为不仅适用于Integer对象。我们针对所有的整数类型的类都有类似的缓存机制。
有ByteCache用于缓存Byte对象
有ShortCache用于缓存Short对象
有LongCache用于缓存Long对象
有CharacterCache用于缓存Character对象
Byte
, Short
, Long
有固定范围: -128 到 127。对于Character
, 范围是 0 到 127。
根据源码除了Integer
以外,其他都是final修饰这个范围都不能改变。
Integer的cache范围可以通过修改JVM
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high")
配置实现。
有些整形是无符号的,例如年龄,有些整形需要符号,类似库存,在C语言里面都是有Unsign变量的,但是在Java里是没有的,但是在Java8之后 在增加了无符号转换。
有符号数 二进制 首位代表 正负,1为负数,0位正数。无符号数字没有代表正负的首位
Byte、Integer有符号转换成无符号,范围都变大了。
有符号Integer:范围 -2^16 ~ 2^16 - 1
无符号Integer:范围 0 ~ 2^32 - 1
因此源码中Integer转换成无符号就变成有符号的Long了
public static long toUnsignedLong(int x) { return ((long) x) & 0xffffffffL; }
@Native public static final long MAX_VALUE = 0x7fffffffffffffffL;//01111111111111111111111111111111 = 2^32次方 - 1
其中0xffffffffL 是Long.MAXVALUE 也就是Long有符号数的最大值 ,然后进行&运算
0101101001与011111111111进行与运算,根据0 1 得 0 ,1 1得1的原则就得到了无符号数;
Byte与此类似
public static int toUnsignedInt(byte x) { return ((int) x) & 0xff; }
<<左移运算:
byte a = 2; a=<<1 表示 a 向左移一位表示乘2一次方也就是 00000010 → 00000100 = 4,左边丢弃,右边补0,以此类推,a=<<2 乘以2的二次方= 8;
>>右移运算:
与上相反,a=a>>1代表除以2的一次方,a=a>>2代表除以2的二次方,右边丢弃,左边补充一个符号位,正数补0,负数补1;
>>>无符号位移运算:
>>>运算符把 expression1 的各个位向右移expression2 指定的位数。右移后左边空出的位用零来填充。移出右边的位被丢弃。
20 >>> 2 补码(由原码按位取反,末尾加1):00000000,00010100 右移:00000000,00000101(高位补0) 结果:5
&与运算: 两位都是1得1,两位有一个0都得0;
|或运算:两个有一个为1就是1;
^异或运算:两个相同(11,00)为0.两个不一样为1;
~取反运算符: ~0=1,~1=0;
在HotSpot虚拟机中,对象在内存中存储的布局可以分为3块区域:对象头(Header)、实例数据(Instance Data)和对齐填充(Padding), Class对象指针。
HotSpot虚拟机的对象头包括两部分信息,第一部分用于存储对象自身的运行时数据,如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等,
这部分数据的长度在32位和64位的虚拟机(未开启压缩指针)中分别为32bit(4字节)和64bit(8字节),官方称它为"Mark Word"。对象头的另外一部分是类型指针,即对象指向它的类元数据的指针,虚拟机
通过这个指针来确定这个对象是哪个类的实例。第三部分对齐填充并不是必然存在的,也没有特别的含义,它仅仅起着占位符的作用由于HotSpot VM的自动内存管理系统要求对象起始地址必须是8字节的整数倍,换句话说,就是对象的大小必须是8字节的整数倍。
1. Mark Word:标记位 4字节,类似轻量级锁标记位,偏向锁标记位等。
2. Class对象指针:4字节,指向对象对应class对象的内存地址。
3. 对象实际数据:对象所有成员变量。
4. 对齐:对齐填充字节,按照8个字节填充。
Integer占用内存大小,4+4+4+4=16字节。
原始数据类型以及引用数据类型的整型变量,显然要使用并发相关手段,例如volatile等修饰,才能保证线程安全,如果有线程安全的计算需要建议使用AutomicInteger、AutomicLong等这样子的线程安全类;
[1] 基本类型均具有取值范围,在大数*大数的时候,有可能会出现越界的情况。
[2] 基本类型转换时,使用声明的方式。例:long result= 1234567890 * 24 * 365;结果值一定不会是你所期望的那个值,因为1234567890 * 24已经超过了int的范围,如果修改
为:long result= 1234567890L * 24 * 365;就正常了。
[3] 慎用基本类型处理货币存储。如采用double常会带来差距,常采用BigDecimal、整型(如果要精确表示分,可将值扩大100倍转化为整型)解决该问题。
[4] 优先使用基本类型。原则上,建议避免无意中的装箱、拆箱行为,尤其是在性能敏感的场合,
[5] 如果有线程安全的计算需要,建议考虑使用类型AtomicInteger、AtomicLong 这样的线程安全类。