美文网首页
Java经典面试题最详细版(面试必备)一

Java经典面试题最详细版(面试必备)一

作者: 管彤Java架构师 | 来源:发表于2022-07-07 17:45 被阅读0次

刚刚经历过秋招,看了大量的面经,顺便将常见的Java常考知识点总结了一下,并根据被问到的频率大致做了一个标注。一颗星表示知识点需要了解,被问到的频率不高,面试时起码能说个差不多。两颗星表示被问到的频率较高或对理解Java有着重要的作用,建议熟练掌握。三颗星表示被问到的频率非常高,建议深入理解并熟练掌握其相关知识,方便面试时拓展(方便装逼),给面试官留下个好印象。
@[toc]

JVM、JRE及JDK的关系 **
JDK(Java Development Kit)是针对Java开发员的产品,是整个Java的核心,包括了Java运行环境JRE、Java工具和Java基础类库。

Java Runtime Environment(JRE)是运行JAVA程序所必须的环境的集合,包含JVM标准实现及Java核心类库。

JVM是Java Virtual Machine(Java虚拟机)的缩写,是整个java实现跨平台的最核心的部分,能够运行以Java语言写作的软件程序。

简单来说就是JDK是Java的开发工具,JRE是Java程序运行所需的环境,JVM是Java虚拟机.它们之间的关系是JDK包含JRE和JVM,JRE包含JVM.

JAVA语言特点 **
Java是一种面向对象的语言
Java通过Java虚拟机实现了平台无关性,一次编译,到处运行
支持多线程
支持网络编程
具有较高的安全性和可靠性
JAVA和C++的区别 **
面试时记住前四个就行了
Java 通过虚拟机从而实现跨平台特性,但是 C++ 依赖于特定的平台。
Java 没有指针,它的引用可以理解为安全指针,而 C++ 具有和 C 一样的指针。
Java 支持自动垃圾回收,而 C++ 需要手动回收。
Java 不支持多重继承,只能通过实现多个接口来达到相同目的,而 C++ 支持多重继承。
Java 不支持操作符重载,虽然可以对两个 String 对象执行加法运算,但是这是语言内置支持的操作,不属于操
作符重载,而 C++ 可以。
Java 的 goto 是保留字,但是不可用,C++ 可以使用 goto。
Java的基本数据类型  **
注意String不是基本数据类型


image.png

隐式(自动)类型转换和显示(强制)类型转换 **
隐式(自动)类型转换:从存储范围小的类型到存储范围大的类型。byte→short(char)→int→long→float→double
显示(强制)类型转换:从存储范围大的类型到存储范围小的类型。double→float→long→int→short(char)→byte。该类类型转换很可能存在精度的损失。
看一个经典的代码

short s = 1;
s = s + 1;
这是会报错的,因为1是int型,s+1会自动转换为int型,将int型直接赋值给short型会报错。

做一下修改即可避免报错

short s = 1;
s = (short)(s + 1);
或这样写,因为s += 1会自动进行强制类型转换

short s = 1;
s += 1;
自动装箱与拆箱 **
装箱:将基本类型用包装器类型包装起来
拆箱:将包装器类型转换为基本类型

这个地方有很多易混淆的地方,但在面试中问到的频率一般,笔试的选择题中经常出现,还有一个String创建对象和这个比较像,很容易混淆,在下文可以看到
下面这段代码的输出结果是什么?

public class Main {
public static void main(String[] args) {

        Integer a = 100;
        Integer b = 100;
        Integer c = 128;
        Integer d = 128;

        System.out.println(a==b);
        System.out.println(c==d);
}

}
true
false
很多人看到这个结果会很疑惑,为什么会是一个true一个flase.其实从源码中可以很容易找到原因.首先找到Integer方法中的valueOf方法

public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
可以看到当不满足if语句中的条件,就会重新创建一个对象返回,那结果必然不相等。继续打开IntegerCache可以看到

private static class IntegerCache {
      static final int low = -128;
    static final int high;
      static final Integer cache[];

      static {
          // high value may be configured by property
          int h = 127;
          String integerCacheHighPropValue =
              sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
          if (integerCacheHighPropValue != null) {
              try {
                  int i = parseInt(integerCacheHighPropValue);
                  i = Math.max(i, 127);
                  // Maximum array size is Integer.MAX_VALUE
                  h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
              } catch( NumberFormatException nfe) {
                  // If the property cannot be parsed into an int, ignore it.
              }
          }
          high = h;

          cache = new Integer[(high - low) + 1];
          int j = low;
          for(int k = 0; k < cache.length; k++)
              cache[k] = new Integer(j++);

          // range [-128, 127] must be interned (JLS7 5.1.7)
          assert IntegerCache.high >= 127;
      }

      private IntegerCache() {}
  }

代码挺长,大概说的就是在通过valueOf方法创建Integer对象的时候,如果数值在[-128,127]之间,便返回指向IntegerCache.cache中已经存在的对象的引用;否则创建一个新的Integer对象。所以上面代码中a与b相等,c与d不相等。

在看下面的代码会输出什么

public class Main {
public static void main(String[] args) {

        Double a = 1.0;
        Double b = 1.0;
        Double c = 2.0;
        Double d = 2.0;

        System.out.println(a==b);
        System.out.println(c==d);

}

}
flase
flase
采用同样的方法,可以看到Double的valueOf方法,每次返回都是重新new 一个新的对象,所以上面代码中的结果都不想等。

public static Double valueOf(double d) {
return new Double(d);
}
最后再看这段代码的输出结果

public class Main {
public static void main(String[] args) {

    Boolean a = false;
    Boolean b = false;
    Boolean c = true;
    Boolean d = true;

    System.out.println(a==b);
    System.out.println(c==d);
}

}
true
true
老方法继续看valueOf方法

public static Boolean valueOf(boolean b) {
return (b ? TRUE : FALSE);
}
再看看TRUE和FALSE是个什么东西,是两个静态成员属性。

public static final Boolean TRUE = new Boolean(true);
public static final Boolean FALSE = new Boolean(false);
说下结论 :Integer、Short、Byte、Character、Long这几个类的valueOf方法的实现是类似的。Double、Float的valueOf方法的实现是类似的。然后是Boolean的valueOf方法是单独一组的。

Integer i = new Integer(xxx)和Integer i =xxx的区别

这两者的区别主要是第一种会触发自动装箱,第二者不会

最后看看下面这段程序的输出结果

public class Main {
public static void main(String[] args) {
Integer a = 1;
Integer b = 2;
Integer c = 3;
Long g = 3L;
int int1 = 12;
int int2 = 12;
Integer integer1 = new Integer(12);
Integer integer2 = new Integer(12);
Integer integer3 = new Integer(1);

    System.out.println("c==(a+b) ->"+ (c==(a+b)));
    System.out.println("g==(a+b) ->" + (g==(a+b)));
    System.out.println( "c.equals(a+b) ->" + (c.equals(a+b)));
    System.out.println( "g.equals(a+b) ->" + (g.equals(a+b)));
    System.out.println("int1 == int2 -> " + (int1 == int2));
    System.out.println("int1 == integer1 -> " + (int1 == integer1));
    System.out.println("integer1 == integer2 -> " + (integer1 == integer2));
    System.out.println("integer3 == a1 -> " + (integer3 == a));
}

}
c==(a+b) ->true
g==(a+b) ->true
c.equals(a+b) ->true
g.equals(a+b) ->false
int1 == int2 -> true
int1 == integer1 -> true
integer1 == integer2 -> false
integer3 == a1 -> false
下面简单解释这些结果。

1.当 "=="运算符的两个操作数都是包装器类型的引用,则是比较指向的是否是同一个对象,而如果其中有一个操作数是表达式(即包含算术运算)则比较的是数值(即会触发自动拆箱的过程)。所以c==a+b,g==a+b为true。

2.而对于equals方法会先触发自动拆箱过程,再触发自动装箱过程。也就是说a+b,会先各自调用intValue方法,得到了加法运算后的数值之后,便调用Integer.valueOf方法,再进行equals比较。所以c.equals(a+b)为true。而对于g.equals(a+b),a+b会先拆箱进行相加运算,在装箱进行equals比较,但是装箱后为Integer,g为Long,所以g.equals(a+b)为false。在此我向大家推荐一个架构学习交流圈。交流学习指导伪鑫:1253431195(里面有大量的面试题及答案)里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化、分布式架构等这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多

3.int1 == int2为true无需解释,int1 == integer1,在进行比较时,integer1会先进行一个拆箱操作变成int型在进行比较,所以int1 == integer1为true。

4.integer1 == integer2 -> false。integer1和integer2都是通过new关键字创建的,可以看成两个对象,所以integer1 == integer2 为false。integer3 == a1 -> false , integer3是一个对象类型,而a1是一个常量它们存放内存的位置不一样,所以integer3 == a1为false,具体原因可学习下java的内存模型。

String(不是基本数据类型)
String的不可变性 ***
在 Java 8 中,String 内部使用 char 数组存储数据。并且被声明为final,因此它不可被继承。

public final class String implements java.io.Serializable, Comparable<String>, CharSequence {
private final char value[];
}
为什么String`要设计成不可变的呢(不可变性的好处):

1.可以缓存 hash 值()

因为 String 的 hash 值经常被使用,例如 String 用做 HashMap 的 key。不可变的特性可以使得 hash 值也不可变,
因此只需要进行一次计算。

2.常量池优化

String 对象创建之后,会在字符串常量池中进行缓存,如果下次创建同样的对象时,会直接返回缓存的引用。

3.线程安全

String 不可变性天生具备线程安全,可以在多个线程中安全地使用。

字符型常量和字符串常量的区别 *
形式上: 字符常量是单引号引起的一个字符 字符串常量是双引号引起的若干个字符
含义上: 字符常量相当于一个整形值(ASCII值),可以参加表达式运算 字符串常量代表一个地址值(该字符串在内存中存放位置)
占内存大小 字符常量占两个字节 字符串常量占若干个字节(至少一个字符结束标志)
什么是字符串常量池?*
字符串常量池位于堆内存中,专门用来存储字符串常量,可以提高内存的使用率,避免开辟多块空间存储相同的字符串,在创建字符串时 JVM 会首先检查字符串常量池,如果该字符串已经存在池中,则返回它的引用,如果不存在,则实例化一个字符串放到池中,并返回其引用。

String 类的常用方法都有那些?**
面试时一般不会问,但面试或笔试写字符串相关的算法题经常会涉及到,还是得背一背(以下大致是按使用频率优先级排序)
length():返回字符串长度
charAt():返回指定索引处的字符
substring():截取字符串
trim():去除字符串两端空白
split():分割字符串,返回一个分割后的字符串数组。
replace():字符串替换。
indexOf():返回指定字符的索引。
toLowerCase():将字符串转成小写字母。
toUpperCase():将字符串转成大写字符。
String和StringBuffer、StringBuilder的区别是什么?***
1.可变性

String不可变,StringBuilder和StringBuffer是可变的

2.线程安全性

String由于是不可变的,所以线程安全。StringBuffer对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。 StringBuilder并没有对方法进行加同步锁,所以是非线程安全的。
3.性能

StringBuilder > StringBuffer > String

为了方便记忆,总结如下


image.png

switch 是否能作用在 byte 上,是否能作用在 long 上,是否能作用在 String 上 *
switch可以作用于char byte short int及它们对应的包装类型,switch不可作用于long double float boolean及他们的包装类型。在 JDK1.5之后可以作用于枚举类型,在JDK1.7之后可作用于String类型。

相关文章

网友评论

      本文标题:Java经典面试题最详细版(面试必备)一

      本文链接:https://www.haomeiwen.com/subject/kyuybrtx.html