Java基础之Integer使用的注意事项及面试题
JAVA中Integer对象的引用
JAVA中没有指针一说,但也有引用的概念。这里要说的主要是Integer是不是同一个对象。
1、先看一段代码:
publicstaticvoidmain(String[]args){ Integera1=100; Integerb1=a1;//另一种也可以b1=100 Fieldfield=null; try{ field=a1.getClass().getDeclaredField("value"); }catch(NoSuchFieldExceptione){ //TODOAuto-generatedcatchblock e.printStackTrace(); }catch(SecurityExceptione){ //TODOAuto-generatedcatchblock e.printStackTrace(); } field.setAccessible(true); try{ field.set(a1,5000); }catch(IllegalArgumentExceptione){ //TODOAuto-generatedcatchblock e.printStackTrace(); }catch(IllegalAccessExceptione){ //TODOAuto-generatedcatchblock e.printStackTrace(); } System.out.println("b1="+b1); Integerc1=100; System.out.println("c1="+c1); }
结果:
b1=5000
c1=5000
从上面,首先这里要说明几个,
1)、对于Integer来说,-128-127之间的整型已经初始化放在IntegerCache中,如果是装箱的话,就会从这里面取对象。
2)、b1=a1到底是数字赋值还是同一个对象?这个从结果实际就可以看出来,b1和a1指向同一个对象,而不是同一个数值
3)、c1=100,说明对于-128-127之间的数值,都是从IntegerCache中获取的对象,100对应的Integer对象被改变后,后续对于100的装箱都被改变。因为获取cache中对象时用的是数组索引,而不是数值比较获取的。
不过修改这个缓存会比较危险,不介意。谁知道什么jar包或者什么平台来个100的装箱,但得到结果又不是100,到时就崩溃了。
2、通过上面描述,那么如果改成这样又是什么答案
publicstaticvoidmain(String[]args){ Integera1=200; Integerb1=a1; Fieldfield=null; try{ field=a1.getClass().getDeclaredField("value"); }catch(NoSuchFieldExceptione){ //TODOAuto-generatedcatchblock e.printStackTrace(); }catch(SecurityExceptione){ //TODOAuto-generatedcatchblock e.printStackTrace(); } field.setAccessible(true); try{ field.set(a1,5000); }catch(IllegalArgumentExceptione){ //TODOAuto-generatedcatchblock e.printStackTrace(); }catch(IllegalAccessExceptione){ //TODOAuto-generatedcatchblock e.printStackTrace(); } System.out.println("b1="+b1); Integerc1=200; System.out.println("c1="+c1); }
3、那么再改一下
publicstaticvoidmain(String[]args){ Integera1=newInteger(100); Integerb1=a1; Fieldfield=null; try{ field=a1.getClass().getDeclaredField("value"); }catch(NoSuchFieldExceptione){ //TODOAuto-generatedcatchblock e.printStackTrace(); }catch(SecurityExceptione){ //TODOAuto-generatedcatchblock e.printStackTrace(); } field.setAccessible(true); try{ field.set(a1,5000); }catch(IllegalArgumentExceptione){ //TODOAuto-generatedcatchblock e.printStackTrace(); }catch(IllegalAccessExceptione){ //TODOAuto-generatedcatchblock e.printStackTrace(); } System.out.println("b1="+b1); Integerc1=100; System.out.println("c1="+c1); }
这又是什么答案。对于new的操作,是不进行装箱的,而是在堆中生成对象的。
理解了装箱、缓存、引用就不难理解了。可以自己试试。
先来点基础的知识
基本类型和包装类的对应 byteByte shortShort intInteger longLong floatFloat doubleDouble charCharacter booleanBoolean
上述的八中基本数据类型的对应关系只有int->Integer char->Character 两个变化较大,其余都只是将首字母转换为小写。
再来了解一下JDK5的新特性:自动装箱和拆箱
自动装箱:把基本类型转换为包装类类型
自动拆箱:把包装类类型转换为基本类型
publicclassDemo_Integer{ publicstaticvoidmain(String[]args){ //JDK1.5之前 inta=100; Integera1=newInteger(a);//将基本数据类型包装成对象,装箱 intb=a1.intValue();//将对象转换为基本数据类型,拆箱 //JDK1.5之后 intx=100; Integerx1=x;//自动装箱,把基本数据类型转换为对象 inty=x1+x;//自动拆箱,把对象转换为基本数据类型 } }
注意事项
publicclassDemo_Integer{ publicstaticvoidmain(String[]args){ Integera=null; intb=a+100;//自动拆箱底层将会调用a.intValue(),a为null,自然会抛出NullPointerException System.out.println(b); } }
面试题
publicclassDemo_Integer{ publicstaticvoidmain(String[]args){ Integeri1=newInteger(97); Integeri2=newInteger(97); System.out.println(i1==i2); System.out.println(i1.equals(i2)); System.out.println("-----------"); Integeri3=newInteger(197); Integeri4=newInteger(197); System.out.println(i3==i4); System.out.println(i3.equals(i4)); System.out.println("-----------"); } }
Output: false true ----------- false true -----------
原因:
new是在堆内存开辟空间的,自然比较地址值(==)都为false.
由于Integer重写了equals方法,所以equals输出都为true.
你可能感觉太简单了,没有任何技术含量,因为上面的不是重点,看下面代码
publicclassDemo_Integer{ publicstaticvoidmain(String[]args){ Integeri1=127; Integeri2=127; System.out.println(i1==i2); System.out.println(i1.equals(i2)); System.out.println("-----------"); Integeri3=128; Integeri4=128; System.out.println(i3==i4); System.out.println(i3.equals(i4)); System.out.println("-----------"); } }
Output: true true ----------- false true -----------
原因:
为什么当int大于127就是两个对象,127这个数字是不是觉得很熟悉?
-128到127是byte的取值范围,如果在这个取值范围内,自动装箱就不会创建新对象了,而从常量池中获取
超过了byte的取值范围就会在创建新对象
自动装箱其底层会调用valueOf()方法,简单源码分析(JDK1.8):
publicfinalclassIntegerextendsNumberimplementsComparable{ publicstaticIntegervalueOf(inti){ //当i>=-128且i<=127时,会直接将取缓冲区中的对象 if(i>=IntegerCache.low&&i<=IntegerCache.high) returnIntegerCache.cache[i+(-IntegerCache.low)]; returnnewInteger(i);//超过了byte取值范围会在堆内存创建 } //内部类充当缓冲区 privatestaticclassIntegerCache{ staticfinalintlow=-128; staticfinalinthigh; staticfinalIntegercache[]; static{ //highvaluemaybeconfiguredbyproperty inth=127; StringintegerCacheHighPropValue= sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high"); if(integerCacheHighPropValue!=null){ try{ inti=parseInt(integerCacheHighPropValue); i=Math.max(i,127); //MaximumarraysizeisInteger.MAX_VALUE h=Math.min(i,Integer.MAX_VALUE-(-low)-1); }catch(NumberFormatExceptionnfe){ //Ifthepropertycannotbeparsedintoanint,ignoreit. } } high=h; cache=newInteger[(high-low)+1]; intj=low; for(intk=0;k =127; } privateIntegerCache(){} } }
8种基本类型的包装类和对象池
java中基本类型的包装类的大部分都实现了常量池技术,这些类是Byte,Short,Integer,Long,Character,Boolean,另外两种浮点数类型的包装类则没有实现。另外Byte,Short,Integer,Long,Character这5种整型的包装类也只是在对应值小于等于127时才可使用对象池,也即对象不负责创建和管理大于127的这些类的对象
扩展知识
在jvm规范中,每个类型都有自己的常量池。常量池是某类型所用常量的一个有序集合,包括直接常量(基本类型,String)和对其他类型、字段、方法的符号引用。之所以是符号引用而不是像c语言那样,编译时直接指定其他类型,是因为java是动态绑定的,只有在运行时根据某些规则才能确定具体依赖的类型实例,这正是java实现多态的基础。
在JVM中,类从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期包括:加载、验证、准备、解析、初始化、使用和卸载7个阶段。而解析阶段即是虚拟机将常量池内的符号引用替换为直接引用的过程。
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对毛票票的支持。