1.Java面试指南(一): int和Integer的区别
2.为何Mybatis将Integer为0的属性解析成空串?
3.第7讲 | int和Integer有什么区别?
4.优雅的避坑不要轻易使用==比较两个Integer的值
5.聊聊 Integer 吧
Java面试指南(一): int和Integer的区别
即将迎来跳槽的高峰期,也就是常说的“金三银四”。由于本人年前就已经开始了求职面试的征程,直到现在才算尘埃落定,所以应该不会再加入跳槽大军了。“Java面试指南”这个系列的抖音伴侣推流获取源码文章,主要是记录我在面试中整理的知识点和扩展思考,希望能对你有所帮助。
面试中的回答没有标准答案,只要能清楚表达关键点即可,所以文中的答案仅供参考。
今天的问题是:int和Integer的区别是什么?在继续阅读之前,我希望你能停下来思考一下:如果面试时面试官问你这个问题,你会怎么回答?
一、面试回答
int是Java中的整型数字,是Java的8种原始数据类型之一,其他七种是:boolean、byte、short、char、float、long、double。尽管Java中一切都是对象,但这八种数据类型是一个例外。
Integer是Java中int类型的包装类,有一个int类型的类变量value储存数据,并提供了基本的数学运算和类型转换。
Java5引入了自动装箱/拆箱,编译阶段会根据上下文自动进行转换。装箱会调用Valueof(),拆箱会调用intValue。从Java9开始,Integer的构造器被标记为废弃,官方文档要求使用Integer的规律分析源码静态工厂类ValueOf方法。
Integer类的ValueOf方法中有一个缓存机制,会先对数值进行判断,如果数值在默认的[-:]之间,则返回缓存的对象,否则返回一个新的Integer对象。上限可以通过JVM提供的配置(-XX:AutoBoxCacheMax)进行修改。
二、深入理解
一般来说,很少有面试官会在面试时问到int和Integer的区别,而有关缓存的考点更多会出现在笔试题中。但这并不代表我们不需要关注和理解。例如,在日常编码时,什么时候该用int,什么时候该用Integer,你是否真的清楚?
(一)、缓存机制
所谓的缓存机制,就是基本类型的包装类为了提升效率和节约内存而引入的机制。除了Integer类默认情况下缓存了[-:]之间的数值外,其他基本类型(除了Float和Double)都有缓存对象。
(二)、自动装箱/拆箱
谈及基本数据类型与包装类,自然会聊到自动装箱和拆箱。这两种行为是Java中提供的语法糖之一,即Java平台为了方便程序开发,而在底层进行的一些转换操作。这种语法对语言本身功能而言并不会产生什么影响,转换的工作发生在编译阶段。
以int和Integer为例,在日常编码时我们更习惯于直接将整数赋值给变量,而不是使用Integer.Valueof()方法,但代码的运行结果是一致的。
使用javac编译这两行代码后,再用javap查看编译后的源码动态表白class文件,就能看到:
(三)、基本类型的线程安全
由于JVM直接保证的原子性变量操作包括read、write、assign、store、load,大致可以认为基本数据类型的读写是具备原子性的(除了long和double,这两种基本类型的读写原子性依赖于虚拟机的具体实现,因为对于没有使用volatile修饰的位的数据结构,Java虚拟机规范是允许分两次来操作的)。
除了读写操作外,对基本类型的变量操作都是非线程安全的,需要利用相关的并发技术来保证线程安全,比如加锁或者使用JUC下的Atomic开头的线程安全类。
(四)、源码阅读
Integer类从整体上来看,主要包括了用于表示最大值、最小值、位数等信息的常量,将各种其他类型转换为Integer对象的静态工厂方法valueOf()以及将整数转换为其他基本类型的方法,如byteValue()、shortValue()等等,还提供了进制转换的方法,如toBinaryString()用于将整数转换为二进制字符串。
1. Integer源码
Integer是不可变类,使用了final修饰class,表明Integer无法被继承,使用final修饰的value属性来保存数字,即一旦Integer对象创建后,数值就不会再改变。
Integer的最大值为0x7fffffff,转换成十进制为2^-1,最小值为0x,转换成十进制是app源码介绍-2^。使用常量SIZE=来表示int的位数,即位。因为int是有符号数,需要一位来表示正负号,所以剩下的位用来表示实际的数值。
2. Integer的缓存机制
Integer缓存的对象保存在Integer的内部类IntegerCache中,static语句块中是缓存对象的生成逻辑。默认情况下,- ~ 范围内Integer对象保存在Integer数组中。
static语句块会在初始化阶段,由虚拟机执行方法时执行。虚拟机会保证同一时间只会有一个线程执行方法,所以是线程安全的。而cache数组对象是单例,也就是说IntegerCache采用了单例模式。
三、小结
尽管面试中直接问到int和Integer的区别的几率并不大,而且也没有特别多可以展开细说的知识点,但关于Integer的源码,建议还是要去阅读了解。最后,祝君面试顺利,薪资翻倍!
END
为何Mybatis将Integer为0的属性解析成空串?
在一次代码审查中,同事分享了一个有趣的问题:在Mybatis中,Integer类型的age为0时,为什么会解析成空串,导致SQL语句的条件判断失效?
为了解答疑问,作者查阅了Mybatis的源码。首先,从GitHub上的最新版本下载代码,构建测试用例。在SqlSessionFactoryBuilder的构建流程中,经过XMLConfigBuilder解析配置,友数 源码构建Configuration类,进而生成SqlSessionFactory和SqlSession。执行过程中,mybatis使用SimpleExecutor或CachingExecutor,后者涉及动态代理和拦截器的执行,关键在于DynamicSqlSource和IfSqlNode类。
在IfSqlNode的evaluator.evaluateBoolean方法中,使用了OGNLCache来获取值,而问题出在OGNL表达式对空字符串的处理上。在ASTNotEq类的compareWithConversion方法中,当字符串长度为0时,会被解析为0.0,这不仅影响Integer,也影响Float和Double类型。因此,问题的根源在于OGNL表达式对空字符串的解析规则。
第7讲 | int和Integer有什么区别?
典型回答:
int是Java中的一种基本数据类型,属于原始数据类型的一种。它是Java编程语言中的8个基本数据类型之一,包括boolean、byte、short、char、int、float、double、long。
Integer是int类型的包装类,它是一个对象,包含一个存储数据的int类型的字段,并提供了一些基本操作,如数学运算、int与字符串之间的转换等。在Java 5中,引入了自动装箱和自动拆箱功能,简化了相关编程。
关于Integer的值缓存,这涉及Java 5中的一个改进。传统上,构建Integer对象的方式是直接调用构造器,创建一个新的对象。但实践表明,大部分数据操作都集中在有限的、较小的数值范围内。因此,在Java 5中引入了静态工厂方法valueOf,它利用一个缓存机制,提高了性能。按照Javadoc,这个值默认缓存范围是-到之间。
知识扩展:
1. 理解自动装箱、拆箱
自动装箱是一种语法糖,它实际上是一种语法上的简化。简单来说,Java平台为我们自动进行了一些转换,以保证不同的写法在运行时等价。这些转换发生在编译阶段,生成的字节码是一致的。
2. 源码分析
考察是否阅读过、是否理解JDK源代码可能是部分面试官的关注点。阅读并实践高质量代码也是程序员成长的必经之路。下面我来分析下Integer的源码。
3. 原始类型线程安全
前面提到了线程安全设计,你可能想过,原始数据类型操作是否线程安全。
4. Java原始数据类型和引用类型局限性
从Java平台发展的角度来看,原始数据类型和对象的局限性和演进是值得关注的。
再扩展:
你知道对象的内存结构是什么样的吗?比如,对象头的结构。如何计算或获取某个Java对象的大小?
优雅的避坑不要轻易使用==比较两个Integer的值
直接进入主题,来看一段代码,让我们探索Integer比较的奥秘:
许多人可能会理所当然地认为这段代码会打印出 j = ,但背后的原理却值得深入探讨。i作为Integer对象,而j为基本类型int,它们如何协同工作呢?这涉及到Java 5引入的自动装箱和拆箱机制。借助IDEA的jclasslib Bytecode viewer插件,我们可以看到程序运行的底层指令:
这段程序的字节码指令揭示了自动装箱和拆箱的过程。第3行调用Integer的valueOf方法进行自动装箱,第8行则调用intValue方法进行自动拆箱,将Integer对象转换为int。
进一步研究valueOf和intValue的源码,我们发现Integer类中有一个IntegerCache机制,它在虚拟机初始化时预加载了(-,]范围内的整数。这解释了为什么i1 == i2为true,而i3 == i4为false:在缓存范围内,而超出了。
为了避免这类陷阱,正确的比较两个Integer值的方法是使用equals()函数,而不是简单的==。equals会比较两个对象的整数值,不受类型影响。
阿里Java开发手册推荐的策略是,当比较整型包装类对象的值时,始终使用equals()方法,以确保准确无误的比较。
聊聊 Integer 吧
当我们深入探讨Java编程语言时,基础知识的重要性便凸显出来。在忙碌的编码过程中,抽出时间梳理JDK源码,无疑是一种查缺补漏的高效方式。经过阅读,你或许会惊喜地发现,JDK源码中隐藏着许多让你大开眼界的内容。
Java作为一种面向对象的编程语言,遵循万物皆对象的原则。但为了在编程中实现灵活性和方便性,Java引入了基本数据类型对应的包装类型,即所谓的“wrapper class”。作为整型int的包装类,Integer不仅在功能上与int相互补充,还通过自动装箱/拆箱机制实现了二者之间的无缝转换,自Java 5版本开始引入。
为什么需要引入Integer(包装类)?答案在于面向对象思想的贯彻。在Java中,为了将基本类型转换为对象形式进行操作,一种直观的方式是将基本类型作为类属性保存。这样做的初衷,是让基本数据类型更符合面向对象的抽象和封装原则,从而更加适应Java的编程模式。
创建Integer的途径多样,但最常见的方式是直接赋值。尽管第二种和第三种看似遵循Java语法规范,但实际上,Java类型分为基本类型和对象类型两种,int属于基本类型,Integer属于对象类型。对于JVM而言,直接将基本类型赋值给对象类型的操作是不被允许的。不过,在实际开发中,这样的操作并不会导致报错,这是因为编译器采用了语法糖技术,巧妙地在不改变JVM基础语法的前提下,提供了更加便捷的编程体验。
Integer缓存机制的实现,通过Integer类内部定义的私有静态类IntegerCache来完成。这个类负责存储一个特定范围内的静态Integer对象,并在静态代码块中进行初始化。默认情况下,缓存范围限定在[-, ]之间,因此只实例化了个Integer对象。当值落在这个范围内时,程序可以直接从缓存中获取相应的Integer对象,避免了重复实例化和回收,从而节省了资源消耗。此外,通过配置虚拟机参数-Djava.lang.Integer.IntegerCache.high=xxx,可以灵活地调整缓存值的最大值,满足不同的应用需求。
深入剖析Integer的hashCode()方法和equals()方法,我们能更清晰地理解它们的作用。hashCode()方法返回的是对象本身的value值,而equals()方法比较的是两个对象的value值,即两个Integer对象的逻辑数值一致时,equals方法将返回true。
Integer与int的区别主要体现在以下几个方面:1)Integer是int的包装类,而int是Java的基本数据类型;2)Integer变量在使用前必须实例化,而int变量则无需预先声明;3)Integer实际上是对象的引用,new一个Integer时实际上是生成一个指针指向该对象;而int则是直接存储数据值;4)Integer的默认值为null,而int的默认值为0。
理解Integer与int之间的比较,对于深入掌握Java的基本数据类型和对象关系至关重要。通过分析不同类型的比较方法,我们可以更准确地识别出它们的区别。例如,两个Integer对象通过"=="比较时,返回false,因为它们实际上是指向不同内存地址的对象;而使用equals方法时,只要value值相同,就会返回true。
在实际开发中,我们应遵循以下建议:1)在表示一个值时,优先考虑使用int类型,以节省内存空间;2)Integer类型在使用前必须初始化,避免NullPointerException异常;3)针对特定场景,如考试成绩区分未参加考试与0分,使用Integer可以更清晰地区分未赋值与值为0的情况;4)推荐直接赋值而非通过new生成新对象,以提高内存利用率;5)根据实际情况调整JVM启动参数,适当扩展Integer缓存区间,以节省内存和提升性能;6)在进行两个对象的比较时,使用equals方法而非“==”。
Java中与Integer类似的Long也有缓存机制,在[-, ]范围内获取缓存值,而Long与long的比较会先转换成long类型。至于Double类型,它没有缓存机制,但与double的比较会先转换成double类型。
总结而言,基础知识往往蕴含着编程中的智慧与陷阱。保持求知欲,不断巩固基础知识,将使我们在编程之旅中少走弯路,面试时也能游刃有余。