Java基本数据按类型能够分为四大类:布尔型,整数型,浮点型,字符型,这四大类包含8中基本数据类型。缓存
8种基本类型取值以下:jvm
数据类型 | 表明含义 | 默认值 | 取值 | 包装类 |
boolean | 布尔型 | false | 0(false)/1(true) | Boolean |
byte | 字节型 | (byte)0 | -128到127 | Byte |
char | 字符型 | '\u0000'(空) | '\u0000'到'\uFFFF' | Character |
short | 短整数型 | (short)0 | -2^15到2^15 | Short |
int | 整数型 | 0 | -2^31到2^31-1 | Integer |
long | 长整数型 | 0L | -2^63到2^63-1 | Long |
float | 单浮点型 | 0.0f | 1.4e-45到3.4e+38 | Float |
double | 双浮点型 | 0.0d | 4.9e-324到1.798e+308 | Double |
除char的包装类Character和int的包装类Integer以外,其余基本数据类型的包装类只须要首字母大写便可。包装类的做用和特色,本文下半部分详细讲解。spa
咱们能够在代码中,查看某种类型的取值范围,代码以下:设计
public static void main(String[] args) { // Byte 取值:-128 ~ 127 System.out.println(String.format("Byte 取值: %d ~ %d", Byte.MIN_VALUE,Byte.MAX_VALUE)); // Integer 取值: -2147483648 ~ 2147483647 System.out.println(String.format("Integer 取值: %s ~ %s", Integer.MIN_VALUE,Integer.MAX_VALUE)); }
咱们知道8种基本数据类型都有其对应的包装类,由于Java的设计思想是万物既对象,有不少时候咱们须要以对象的形式操做器某项功能,好比说获取哈希值(hashcode)或获取类(getClass)等。3d
那包装类特性有哪些呢?code
1.功能丰富orm
包装类本质上是一个对象,对象就包含属性和方法,好比hashCode、getClass、max、min等。对象
2.可定义泛型类型参数blog
包装类能够定义泛型,而基本类型不行。接口
好比使用Integer定义泛型,代码:
List<Integer> list = new ArrayList<>();
若是使用int定义就会报错,代码:
3.序列化
由于包装类都实现了Serializable接口,因此包装类自然支持序列化和反序列化。好比Integer的类图以下:
4.类型转换
包装类提供了类型转换的方法,能够很方便的实现类型之间的转换,好比Integer类型转换代码:
String age = "18"; int ageInt = Integer.parseInt(age) + 2; //输出结果:20 System.out.println(ageInt);
5.高频区间的数据缓存
此特性为包装类很重要的用途之一,用于高频区间的数据缓存,以Integer为例来讲,在数值区间为-128~127时,会直接复用已有对象,在这区间以外的数字才会在堆上产生。
咱们使用 == 对 Integer 进行验证,代码以下:
public static void main(String[] args) { // Integer 高频区缓存范围 -128 ~ 127 Integer num1 = 127; Integer num2 = 127; // Integer 取值127 == 结果为 true (值127 num1==num2 =>true) System.out.println("值127 num1==num2 =>" + (num1 == num2)); Integer num3 = 128; Integer num4 = 128; // Integer 取值128 == 结果为 false (值128 num3==num4 =>false) System.out.println("值128 num3==num4 =>" + (num3 == num4)); }
从上面的代码很明显能够看出,Integer为127时复用了已有的对象,当值为128时,从新在堆上生成了新对象。
为何会产生高频区域数据缓存呢?咱们查看源码就能发现“线索”,源码版本 JDK8,源码以下:
public static Integer valueOf(int i) { if (i >= IntegerCache.low && i <= IntegerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)]; return new Integer(i); }
因而可知,高频区域的数值会直接使用已有对象,非高频区域的数值会从新new一个新的对象。
各包装类高频区域的取值范围:
public static void main(String[] args) { // Integer 高频区缓存范围 -128 ~ 127 Integer num1 = 127; Integer num2 = 127; // Integer 取值127 == 结果为 true (值127 num1==num2 =>true) System.out.println("值127 num1==num2 =>" + (num1 == num2)); Integer num3 = 128; Integer num4 = 128; // Integer 取值128 == 结果为 false (值128 num3==num4 =>false) System.out.println("值128 num3==num4 =>" + (num3 == num4)); // Integer 取值128 equals 结果为 true (值128 num3.equals(num4) =>true) System.out.println("值128 num3.equals(num4) =>" + (num3.equals(num4))); }
-XX:AutoBoxCacheMax=666 即修改缓存最大值为666。
示例代码以下:
public static void main(String[] args) { Integer num1 = 128; Integer num2 = 128; System.out.println("值为-128 =>" + (num1 == num2)); Integer num3 = 666; Integer num4 = 666; System.out.println("值666 num3==num4 =>" + (num3 == num4)); Integer num5 = 667; Integer num6 = 667; System.out.println("值为667 =>" + (num5 == num6)); }
执行结果以下:
值为-128 =>true 值为666 =>true 值为667 =>false
因而可知将Integer最大缓存修改成666以后,667不会被缓存,而-128~666之间的数都被缓存了。
Integer age = 10; Integer age2 = 10; Integer age3 = 133; Integer age4 = 133; System.out.println((age == age2) + "," + (age3 == age4));
答: true, false
Double num = 10d; Double num2 = 10d; Double num3 = 133d; Double num4 = 133d; System.out.println((num == num2) + "," + (num3 == num4));
答: false, false
int i = 100; Integer j = new Integer(100); System.out.println(i == j); System.out.println(j.equals(i));
A: true,true
B: true,false
C: false,true
D: false,false
答: A
题目分析:有人认为这和Integer高速缓存有关系,但你发现把值改成10000结果也是 true,true ,这是由于 Integer 和 int 比较时,会自动拆箱为 int ,至关于两个 int 比较,值必定是 true, true .
final int iMax = Integer.MAX_VALUE; System.out.println(iMax + 1);
A: 2147483648
B: -2147483648
C: 程序报错
D: 以上都不是
答: B
题目解析: 这是由于整数在内存中使用的是补码的形式表示,最高位是符号位 0 表示正数, 1 表示负数, 当执行 +1 时, 最高位就变成了 1, 结果就成了 -2147483648.
Set<Short> set = new HashSet<>(); for(short i = 0; i < 5; i++){ set.add(i); set.remove(i - 1); } System.out.println(set.size());
A: 1
B: 0
C: 5
D: 以上都不是
答: C
题目解析: Short 类型 -1 以后转换成了Int 类型, remove() 的时候在集合中找不到 Int 类型的数据, 因此就没有删除任何元素, 执行的结果就是 5 .
答: s=s+1 会报错, s+=1 不会报错, 由于 s = s + 1 会致使short 类型升级为 int 类型,因此会报错, s+=1 仍是原来的short 类型, 因此不会报错.
其实对此我是有疑问的,继续探究.分别从源码和底层原理两个方面来了解一下这个问题.
从源码角度:
(1) s = s+1报错,这句先执行s+1而后把结果赋给s,因为1为int类型,因此s+1的返回值是int,编译器自动进行了隐式类型转换。因此将一个int类型赋给short就会出错
(2) s += 1这句不报错
经过反编译能够看到源码为
当jvm识别+=且原值为整型时,会先忽略原值的具体数据类型,先用int计算后,若是计算结果是int就直接转为原来的数据类型,若是不是int就强转为int而后再转回原数据类型,例如
反编译后为
输出结果为2,损失了精度.
从底层原理:
而i+1 是将heap中数据直接送到寄存器中进行运算,运算结果会直接存放在heap中。
i+=1 运行的底层Heap申请一个区域存放i,在数据区域开辟一个区域存放1,2个内存段的数据被送入到寄存器中进行运算,运算结果被放到heap中,数据区域运算后被自动释放后由GC回收 。
答: 会报错,由于值3.4是double类型,float类型级别小于double类型,因此会报错.
答: 须要包装类的缘由有两个.
答: 不正确,只有包装类高频区域数据才有缓存.
答: 不正确,基本数据类型的包装类只有Double和Float没有高频区域的缓存.
答: 包装类由于有高频区域数据缓存,因此推荐使用equals()方法进行值比较.
答:包装类提供的功能有如下几个.
详见正文"包装类型"部份内容.
答: 泛型不能使用基本数据类型.泛型在JVM(Java 虚拟机)编译的时候会类型擦除,好比代码 List<Integer> list 在JVM编译的时候会转换为List list, 由于泛型是在JDK5时提供的,而JVM的类型擦除是为了兼容之前代码的一个这种方案,类型擦除以后就变成了Object, 而Object不能存储基本数据类型,但可使用基本数据类型对应的包装类,因此像 List<int> list 这样的代码是不被容许的,编译器阶段会检查报错,而List<Integer> list是被容许的.
答: 咱们知道正确的使用包装类,能够提供程序的执行效率,看可使用已有的缓存,通常状况下选择基本数据类型仍是选择包装类原则有如下几个.
答: 基本数据类型不必定存储在栈中,由于基本类型的存储位置取决于声明的做用域,来看具体的解释.
Integer i1 = new Integer(10); Integer i2 = new Integer(10); Integer i3 = Integer.valueOf(10); Integer i4 = Integer.valueOf(10); System.out.println(i1 == i2); System.out.println(i2 == i3); System.out.println(i3 == i4);
答案: false;false;false
题目解析: new Integer(10) 每次都会建立一个新对象,Integer.valueOf(10)则会使用缓存池中的对象.
答: 返回值为: false.
题目解析: 由于有些浮点数不能彻底精确的表示出来,以下代码:
System.out.println(3 * 0.1);
返回的结果是: 0.30000000000000004