借参加过的多场Java开发面试,应聘岗位均为Java开发方向,在不断的面试中,又仔细对Java知识点进行复习和总结,也算是重新学习一下Java吧。
推荐收藏链接:Java 面试知识点解析
11)Integer 的缓存机制
解析:考察的是对源码的熟悉程度
-
看一个例子:
图片.png
第一个返回true很好理解,就像上面讲的,a和b指向相同的地址。
第二个返回false是为什么呢?这是因为 Integer 有缓存机制,在 JVM 启动初期就缓存了 -128 到 127 这个区间内的所有数字。
第三个返回false是因为用了new关键字来开辟了新的空间,i和j两个对象分别指向堆区中的两块内存空间。
我们可以跟踪一下Integer的源码,看看到底怎么回事。在IDEA中,你只需要按住Ctrl然后点击Integer,就会自动进入jar包中对应的类文件。
图片.png
跟踪到文件的700多行,你会看到这么一段,感兴趣可以仔细读一下,不用去读也没有关系,因为你只需要知道这是 Java 的一个缓存机制。Integer 类的内部类缓存了 -128 到 127 的所有数字。(事实上,Integer类的缓存上限是可以通过修改系统来更改的,了解就行了,不必去深究。)
12)下述两种方法分别创建了几个 Sring 对象?
// 第一种:直接赋一个字面量
String str1 = "ABCD";
// 第二种:通过构造器创建
String str2 = new String("ABCD");
解析:考察的是对 String 对象和 JVM 内存划分的知识。
答:String str1 = "ABCD";
最多创建一个String对象,最少不创建String对象.如果常量池中,存在”ABCD”,那么str1直接引用,此时不创建String对象.否则,先在常量池先创建”ABCD”内存空间,再引用。
String str2 = new String("ABCD");
最多创建两个String对象,至少创建一个String对象。new关键字绝对会在堆空间创建一块新的内存区域,所以至少创建一个String对象。
我们来看图理解一下:
图片.png
- 当执行第一句话的时候,会在常量池中添加一个新的ABCD字符,str1指向常量池的ABCD
- 当执行第二句话的时候,因为有new操作符,所以会在堆空间新开辟一块空间用来存储新的String对象,因为此时常量池中已经有了ABCD字符,所以堆中的String对象指向常量池中的ABCD,而str2则指向堆空间中的String对象。
String 对象是一个特殊的存在,需要注意的知识点也比较多,需要大家多注意。
13)i++ 与 ++i 到底有什么不同?
解析:对于这两个的区别,熟悉的表述是:
- 前置++是先将变量的值加 1,然后使用加 1 后的值参与运算;
- 后置++则是先使用该值参与运算,然后再将该值加 1 ;
- 但事实上,前置++和后置++一样,在参与运算之前都会将变量的值加 1
答:实际上,不管是前置 ++,还是后置 ++,都是先将变量的值加 1,然后才继续计算的。二者之间真正的区别是:前置 ++ 是将变量的值加 1 后,使用增值后的变量进行运算的,而后置 ++ 是首先将变量赋值给一个临时变量,接下来对变量的值加 1,然后使用那个临时变量进行运算。
14)交换变量的三种方式
答:
-
第一种:通过第三个变量
public class Test{ public static void main(String[] args) { int x = 5; int y = 10; swap(x,y); System.out.println(x); System.out.println(y); Value v = new Value(5,10); swap(v); System.out.println(v.x); System.out.println(v.y); } // 无效的交换:形参的改变无法反作用于实参 public static void swap(int x,int y) { int temp = x; x = y; y = temp; } // 有效的交换:通过引用(变量指向一个对象)来修改成员变量 public static void swap(Value value) { int temp = value.x; value.x = value.y; value.y = temp; } } class Value{ int x; int y; public Value(int x,int y) { this.x = x; this.y = y; } }
输出的结果:
5
10
10
5
这有点类似于C/C++语言中的指针,不过相对来说更加安全。
事实上,其实如果把基础类型int改成对应的包装类的话其实可以更加简单的完成这个操作,不过需要付出更多的内存代价。
-
第二种:通过通过相加的方式(相同的 Value 类不再重复展示)
public class Test{ public static void main(String[] args) { Value v1 = new Value(5,10); swap(v1); System.out.println("v1交换之后的结果为:"); System.out.println(v1.x); System.out.println(v1.y); } public static void swap(Value v) { v.x = v.x + v.y; v.y = v.x - v.y; v.x = v.x - v.y; } }
输出的结果:
v1的交换结果:
10
5
核心的算法就是swap方法:
v.x = v.x + v.y; // 把v.x与v.y的和存储在v.x中
v.y = v.x - v.y; // v.x减掉v.y本来的值即为v.x
v.x = v.x - v.y; // v.x减掉v.y的值也就是以前x.y的值
这样就可以不通过临时变量,来达到交换两个变量的目的,如果觉得上面的方法不太容易理解,我们也可以用另一个参数z来表示上述过程:
int z = v.x + v.y; // 把v.x与v.y的和存储在z中
v.y = z - v.y; // z减掉以前的v.y就等于v.x
v.x = z - v.y; // z减掉现在的v.y即以前的v.x,即为v.y
但并不推荐这种做法,原因在于当数值很大的时候,16进制的求和运算可能造成数据的溢出,虽然最后的结果依然会是我们所期望的那样,但仍然不是十分可取。
- 第三种:通过异或的方式:
位异或运算符(^)有这样的一个性质,就是两个整型的数据x与y,有:
(x ^ y ^ y) == x
这说明,如果一个变量x异或另外一个变量y两次,结果为x。通过这一点,可以实现交换两个变量的值:
public class Test{
public static void main(String[] args) {
Value v1 = new Value(5,10);
swap(v1);
System.out.println("v1交换之后的结果为:");
System.out.println(v1.x);
System.out.println(v1.y);
}
public static void swap(Value v) {
v.x = v.x ^ v.y;
v.y = v.x ^ v.y;
v.x = v.x ^ v.y;
}
}
输出的结果:
v1交换之后的结果为:
10
5
跟上面相加的方式过程几乎类似,只不过运算的方式不同而已。异或的方法比相加更加可取的地方在于,异或不存在数据溢出。
我总结出了很多互联网公司的面试题及答案,并整理成了文档,以及各种学习的进阶学习资料,免费分享给大家。扫描二维码加VX好友,拉你进【程序员面试学习交流群】免费领取。也欢迎各位一起在群里探讨技术。
在这里插入图片描述
网友评论