1. Java 语言都有哪些特点?
答:Java 语言包含以下特点。
- 面向对象,程序容易理解、开发简单、方便;
- 跨平台,可运行在不同服务器类型上,比如:Linux、Windows、Mac 等;
- 执行性能好,运行效率高;
- 提供大量的 API 扩展,语言强大;
- 有多线程支持,增加了响应和实时交互的能力;
- 安全性好,自带验证机制,确保程序的可靠性和安全性。
2. Java 跨平台实现的原理是什么?
答:要了解 Java 跨平台实现原理之前,必须先要了解 Java 的执行过程,Java 的执行过程如下:
Java 执行流程:Java 源代码(.java)-> 编译 -> Java 字节码(.class) ->通过 JVM(Java 虚拟机)运行 Java
程序。每种类型的服务器都会运行一个 JVM,Java 程序只需要生成 JVM 可以执行的代码即可,JVM
底层屏蔽了不同服务器类型之间的差异,从而可以在不同类型的服务器上运行一套 Java 程序。
3. JDK、JRE、JVM 有哪些区别?
答:了解了 JDK、JRE、JVM 的定义也就明白了它们之间的区别,如下所述。
- JDK:Java Development Kit(Java 开发工具包)的简称,提供了 Java 的开发环境和运行环境;
- JRE:Java Runtime Environment(Java 运行环境)的简称,为 Java 的运行提供了所需环境;
- JVM:Java Virtual Machine(Java虚拟机)的简称,是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的,简单来说就是所有的 Java 程序都是运行在 JVM(Java 虚拟机)上的。
总体来说,JDK 提供了一整套的 Java 运行和开发环境,通常使用对象为 Java 的开发者,当然 JDK 也包含了 JRE;而 JRE 为 Java
运行的最小运行单元,一般安装在 Java 服务器上,所以 JDK 和 JRE 可以从用途上进行理解和区分。JVM 不同于 JDK 和 JRE,JVM 是
Java 程序运行的载体,Java 程序只有通过 JVM 才能正常的运行。
4. Java 中如何获取明天此刻的时间?
答:JDK 8 之前使用 Calendar.add()
方法获取,代码如下:
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.DATE, 1);
System.out.println(calendar.getTime());
JDK 8 有两种获取明天时间的方法。
方法一,使用 LocalDateTime.plusDays()
方法获取,代码如下:
LocalDateTime today = LocalDateTime.now();
LocalDateTime tomorrow = today.plusDays(1);
System.out.println(tomorrow);
方法二,使用 LocalDateTime.minusDays()
方法获取,代码如下:
LocalDateTime today = LocalDateTime.now();
LocalDateTime tomorrow = today.minusDays(-1);
System.out.println(tomorrow);
minusDays()
方法为当前时间减去 n 天,传负值就相当于当前时间加 n 天。
5. Java 中如何跳出多重嵌套循环?
答:Java 中跳出多重嵌套循环的两种方式。
- 方法一:定义一个标号,使用 break 加标号的方式
- 方法二:使用全局变量终止循环
方法一,示例代码:
myfor:for (int i = 0; i < 100; i++) {
for (int j = 0; j < 100; j++) {
System.out.println("J:" + j);
if (j == 10) {
// 跳出多重循环
break myfor;
}
}
}
方法二,示例代码:
boolean flag = true;
for (int i = 0; i < 100 && flag; i++) {
for (int j = 0; j < 100; j++) {
System.out.println("J:" + j);
if (j == 10) {
// 跳出多重循环
flag = false;
break;
}
}
}
6. char 变量能不能存贮一个中文汉字?为什么?
答:char 变量可以存贮一个汉字,因为 Java 中使用的默认编码是 Unicode ,一个 char 类型占 2 个字节(16
bit),所以放一个中文是没问题的。
7. Java 中会存在内存泄漏吗?请简单描述一下。
答:一个不再被程序使用的对象或变量一直被占据在内存中就造成了内存泄漏。
Java 中的内存泄漏的常见情景如下:
- 长生命周期对象持有短生命的引用,比如,缓存系统,我们加载了一个对象放在缓存中,然后一直不使用这个缓存,由于缓存的对象一直被缓存引用得不到释放,就造成了内存泄漏;
- 各种连接未调用关闭方法,比如,数据库 Connection 连接,未显性地关闭,就会造成内存泄漏;
- 内部类持有外部类,如果一个外部类的实例对象的方法返回了一个内部类的实例对象,这个内部类对象被长期引用了,即使那个外部类实例对象不再被使用,但由于内部类持有外部类的实例对象,这个外部类对象将不会被垃圾回收,这也会造成内存泄露;
- 改变哈希值,当一个对象被存储进 HashSet 集合中以后,就不能修改这个对象中的那些参与计算哈希值的字段了,否则对象修改后的哈希值与最初存储进 HashSet 集合中时的哈希值就不同了,在这种情况下,即使在 contains 方法使用该对象的当前引用作为的参数去 HashSet 集合中检索对象,也将返回找不到对象的结果,这也会导致无法从 HashSet 集合中单独删除当前对象,造成内存泄露。
1. 以下 Integer 代码输出的结果是?
Integer age = 10;
Integer age2 = 10;
Integer age3 = 133;
Integer age4 = 133;
System.out.println((age == age2) + "," + (age3 == age4));
答:true,false
题目解析:
Integer:缓存区 -128~127,Integer 为 10 时复用了已有对象,当值为 133 时,重新在堆上生成了新对象。
2. 以下 Double 代码输出的结果是?
Double num = 10d;
Double num2 = 10d;
Double num3 = 133d;
Double num4 = 133d;
System.out.println((num == num2) + "," + (num3 == num4));
答:false,false
3. 以下程序输出结果是?
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
。
4. 以下程序执行的结果是?
final int iMax = Integer.MAX_VALUE;
System.out.println(iMax + 1);
A:2147483648
B:-2147483648
C:程序报错
D:以上都不是
答:B
题目解析:这是因为整数在内存中使用的是补码的形式表示,最高位是符号位 0 表示正数,1 表示负数,当执行 +1 时,最高位就变成了 1,结果就成了
-2147483648。
5. 以下程序执行的结果是?
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:以上都不是
答:5
题目解析:Short 类型 -1 之后转换成了 Int 类型,remove() 的时候在集合中找不到 Int
类型的数据,所以就没有删除任何元素,执行的结果就是 5。
6. short s=2;s=s+1;
会报错吗?short s=2;s+=1;
会报错吗?
答:s=s+1 会报错,s+=1 不会报错,因为 s=s+1 会导致 short 类型升级为 int 类型,所以会报错,而 s+=1 还是原来的 short
类型,所以不会报错。
7. float f=3.4;
会报错吗?为什么?
答:会报错,因为值 3.4 是 double 类型,float 类型级别小于 double 类型,所以会报错。如下图所示:
8. 为什么需要包装类?
答:需要包装类的原因有两个。
① Java 的设计思想是万物既对象,包装类体现了面向对象的设计理念;
② 包装类包含了很多属性和方法,比基本数据类型功能多,比如提供的获取哈希值(hashCode)或获取类(getClass)的方法等。
9. 基本类 int 和包装类 Integer,在 -128~127 之间都会复用已有的缓存对象,这种说法正确吗?
答:不正确,只有包装类高频区域数据才有缓存。
10. 包装类 Double 和 Integer 一样都有高频区域数据缓存,这种说法正确吗?
答:不正确,基本数据类型的包装类只有 Double 和 Float 没有高频区域的缓存。
11. 包装类的值比较要使用什么方法?
答:包装类因为有高频区域数据缓存,所以推荐使用 equals() 方法进行值比较。
12. 包装类有哪些功能?
答:包装类提供的功能有以下几个。
- 功能丰富:包装类包含了有 hashCode、getClass 、max、min 等方法;
- 可定义泛型类型参数:例如
List<Integer> list = new ArrayList<>();
;
- 序列化:包装类实现了 Serializable 接口,所以包装类天然支持序列化和反序列化;
- 类型转换:包装类提供了方便的类型转换方法,比如 Integer 的 parseInt() 方法;
- 高频区域数据缓存:高频区域可使用已有的缓存对象。
详见正文“包装类型”部分内容。
13. 泛型可以为基本类型吗?为什么?
答:泛型不能使用基本数据类型。泛型在 JVM(Java虚拟机)编译的时候会类型檫除,比如代码 List<Integer> list
在 JVM
编译的时候会转换为 List list
,因为泛型是在 JDK 5 时提供的,而 JVM
的类型檫除是为了兼容以前代码的一个折中方案,类型檫除之后就变成了 Object,而 Object
不能存储基本数据类型,但可以使用基本数据类型对应的包装类,所以像 List<int> list
这样的代码是不被允许的,编译器阶段会检查报错,而
List<Integer> list
是被允许的。
14. 选择包装类还是基本类的原则有哪些?
答:我们知道正确的使用包装类,可以提供程序的执行效率,可以使用已有的缓存,一般情况下选择基本数据类型还是包装类原则有以下几个。
① 所有 POJO 类属性必须使用包装类;
② RPC 方法返回值和参数必须使用包装类;
③ 所有局部变量推荐使用基本数据类型。
15. 基本数据类型在 JVM 中一定存储在栈中吗?为什么?
答:基本数据类型不一定存储在栈中,因为基本类型的存储位置取决于声明的作用域,来看具体的解释。
- 当基本数据类型为局部变量的时候,比如在方法中声明的变量,则存放在方法栈中的,当方法结束系统会释放方法栈,在该方法中的变量也会随着栈的销毁而结束,这也是局部变量只能在方法中使用的原因;
- 当基本数据类型为全局变量的时候,比如类中的声明的变量,则存储在堆上,因为全局变量不会随着某个方法的执行结束而销毁。
16. 以下程序执行的结果是?
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);
A:false,false,false
B:false,false,true
C:false,true,true
D:true,false,false
答:B
题目解析:new Integer(10) 每次都会创建一个新对象,Integer.valueOf(10) 则会使用缓存池中的对象。
17. 3*0.1==0.3 返回值是多少?
答:返回值为:false。
题目解析:因为有些浮点数不能完全精确的表示出来,如下代码:
System.out.println(3 * 0.1);
返回的结果是:0.30000000000000004。