Java面试/Java必会题/必刷题(1)

灰太狼 2022-11-11 13:10 285阅读 0赞

1、JDK、JRE、JVM三者之间的关系?

  1. ** JDK**:**J**AVA** D**evelopment**K**it(它是**Java开发运行环境**,在程序员的电脑上当然必要安装JDK) 不仅提供了Java程序运行所需的JRE,**还提供**了一系列的**编译,运行等工具**,如**javac.exejar.exe**等。
  2. ** JRE:** **J**AVA Runtime **E**nvironment(它是**Java运行环境**,如果**你不需要开发**,只需要运行Java程序【**不是绝对的**,比如使用**JSP部署Web应用程序**还是要JDK的】,那么你可以安装JRE)**包含了****java虚拟机****,****java基础类库。**
  3. **JVM**:**J**AVA **V**irtual **M**achine(**JAVA虚拟机**)是**运行Java字节码的虚拟机**。**字节码**和**不同系统的JVM**是实现Java语言”**一次编译,到处运行**"的关键。

三者的包含关系如下图:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzcyNTUxNw_size_16_color_FFFFFF_t_70

JDK = JRE **+**开发工具集

JRE =**JVM + JAVA的核心类库**

2、面向对象和⾯向过程的区别

面向对象:面向对象易维护易复用易扩展。因为面向对象有封装继承多态性,但是面向对象性能比面向过程低

面向过程面向过程性能比面向对象高类需要实例化开辟空间,耗资源。但是面向过程不易维护、易复用、易扩展

3、Java和C++的区别

  • 都是面向对象的语言,都支持封装继承多态
  • Java不支持指针访问内存程序更安全
  • java是单继承(接口可以多继承),C++支持多继承
  • Java有自动内存管理机制,不需要程序猿手动释放内存
  • C语言中的字符串最后有一个额外的 ‘\0’ 来表示结束,Java语言没有结束符。

4、对Java的加载与执行的理解

问题拆解:Java程序非常重要的哪两个阶段?

编译阶段运行阶段

编译期间

Java程序员直接编写的Java代码源程序)是无法执行,无法被JVM识别的。必须得经历一个编译:将这个 “普通文本“ 转为 “字节码“ (JVM能识别的”字节码”

20210506225112464.png

普通文本**转为**字节码的过程是编译。xxx.java源代码符合语法规则就会编译通过,形成xxx.class文件

注意字节码不是二进制文件,如果是二进制文件,那还要JVM干嘛。

运行期间

JRE起作用JVM将.class字节码文件装载进去,对字节码进行解释(解释器逐行执行),转为二进制。后面JVM将生成的二进制码交给OS操作系统执行二进制码,操作系统就会执行二进制码和硬件进行交互。

在windows上可以运行,其他系统也可以运行:如果是在linux上运行,只要把生成的.class文件拷贝过去。在这个过程(转二进制到与硬件交互),没有.java文件任何事,即使现在删除也没关系(最好不要,你还会修改代码的呢),对字节码没有影响。

20210506231115953.png

在这使用CMD编写代码过程:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzcyNTUxNw_size_16_color_FFFFFF_t_70 1

javac 命令负责编译(JDK的bin目录下有javac.exe),java 命令负责运行 (JDK的bin目录下有java.exe)

5、Java变量分为哪几类

在Java中所有的变量分为:成员变量局部变量

先了解它们的区别:成员变量是指这个的变量,局部变量是类中的方法内定义的变量

成员变量包括:实例变量类变量

实例变量是没有static修改的变量,类变量是指以static修饰的。

实例变量 和 类变量的区别:

1)访问:实例变量是通过定义类的对象访问,类变量可以通过类或者类的对象访问。

2)生命周期:实例变量与类的对象生命周期共存亡,类变量与共存亡。

3)位置:实例变量存放在中,类变量存放在方法区

局部变量包括:形参方法局部变量(在方法内定义)存放位置栈中

局部变量与成员变量的区别?

1、定义位置不同:
局部变量:在方法的内部
成员变量:在方法的外面,直接写在类当
2、作用域不同:
局部变量:只有在方法中才能使用,出了方法就不能再用了
成员变量:整个类全部可以用
3、默认值不一样
局部变量:没有默认值,如果使用必须手动进行赋值
成员变量:如果没有赋值会有一个起始值,规则和数组一样

6、&& 与 & 的区别是?|| 和 | 的区别是?

首先这个两者&&和&运算结果没什么区别,完全相同,只不过 && 会发生”短路的现象:比如:Flag1 && Flag2,当Flag1为false是Flag2就不会判断。

|| 和 |也存在上述问题:如果第一个操作数是true,||运算符就返回true,无需考虑第二个操作数的值。

但&和|却不是这样,它们总是要计算两个操作数。性能效率就不如上面的两个。

重要区别简洁描述条件布尔运算符性能比较好。它检查第一个操作数的值,再根据该操作数的值进行操作,可能根本就不处理第二个操作数。

7、++i和i++的小问题

++无论出现在变量**前还是后,只要++运算结束变量一定会自加1**.

  1. int i = 10;
  2. i++;
  3. System.out.println(i); // 11
  4. int k = 10;
  5. ++k;
  6. System.out.println(k); // 11

i++**使用变量的值,在执行++的操作++i**:先**执行++操作在使用变量的值**。

解开以上题目的窍门是什么拆分代码过程:

  1. // ++ 在变量的后面 如 i++
  2. int i = 10;
  3. int a = i++; // 拆分代码 int a = i; i= i++;
  4. System.out.println(i); // 11
  5. System.out.println(a); // 10
  6. // ++ 在变量前面 ++i
  7. int b = 10;
  8. int c = ++b; // 拆分代码 b = b++; int c = b;
  9. System.out.println(b); // 11
  10. System.out.println(c); // 11

8、基本数据类型的运算问题

首先Java的数值有默认的类型

整数 :默认为int类型,比如 4 + 1;返回的类型是int类型

带有小数:默认为double类型,比如 1.2 + 1.2;返回的类型是double类型

理解基本数据类型运算关键:”低级别“数据类型 和 “高级别“数据类型运算得出的结果会自动转向”高级别”数据类型

byte,short,char 类型混合运算时,先各自转换成 int 类型再做运算。

多说也记不住,先看看案例:

  1. //案例1
  2. char a = 97;
  3. char b = 'B';
  4. b = a + 1; //编译不通过的,需要强转;a + 1 这里的"高级别"类型是int,转为int结果,不可以赋值给char的
  5. // 案例2
  6. byte b1 =1;
  7. byte b2 =2;
  8. b1 =b1 + b2;//编译不通过,需要强转为int类型,byte,short,char 类型混合运算时,先各自转换成 int 类型再做运算
  9. b2 +=b1;//编译通过,这个等同于 b2 = (byte)(b2+b1)
  10. // 案例3
  11. float f1 = 1.2; //不可以通过,因为1.2在Java中默认是double类型
  12. float f2 = 1.2f;//通过
  13. float f3 = 1.2f;//通过
  14. f2 = 1.2+f3; //不可以通过,需要强转为double类型,1.2为double类型
  15. f3=1.2+1.4; // 不可以通过编译,1.2+1.4都是double类型相加,需要强转

总结一下大致规则:

  1. 1)**第一条**:八种基本数据类型中,除 boolean 类型不能转换,剩下**七种类型之间都可以进行转换**。
  2. 2)**第二条**:如果整数型字面量没有超出 byte,short,char 的取值范围,可以直接将其赋值给byte char 类,short,型的变量;
  3. 3)**第三条**:**小容量向大容量转换称为自动类型转换**,容量从**小到大的排序**为:byte < short(char) < int < long < float < double,其中 short char 都占用两个字节,但是**char 可以表示更大的正整数**。
  4. 4)**第四条**:大容量转换成小容量,称为强制类型转换,编写时必须添加“强制类型转换符”,但运行时可能出现精度损失,谨慎使用;
  5. 5)**第五条**:**byte,short,char 类型混合运算时,先各自转换成 int 类型再做运算。**
  6. 6)**第六条**:**多种数据类型混合运算**,各自**先转换成容量最大**的那一种**再做运算**;

9、⾃动装箱与拆箱

装箱:将基本类型用它们对应的引用类型包装起来。

拆箱:将包装类型**转换基本类型**。








































基本数据类型 包装类
byte Byte
short Short
int Integer
long Long
float Float
double Double
boolean Boolean
char Character

在老版本的装箱:Integer i = ``new Integer(``10``);后面只需要 Integer i =5;

看一下简单代码:

  1. public class Main {
  2. public static void main(String[] args) {
  3. Integer i =1;//装箱,1是int类型
  4. int n = i; // i是Integer类赋值给int
  5. }
  6. }

装箱过程的debug

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzcyNTUxNw_size_16_color_FFFFFF_t_70 2

拆箱过程的bebug

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzcyNTUxNw_size_16_color_FFFFFF_t_70 3

得知自动调用静态方法Integer.valueof(int)装箱,在拆箱自动调用**Integer.intValue方法。不用使用debug模式查看调用方法情况也可以使用反编译**。

装箱过程调用包装器的valueof方法实现的,拆箱过程是调用包装器的xxxValue方法包装类转基本类型:调用xxxValue()方法

面试问题

  1. public class Main {
  2. public static void main(String[] args) {
  3. Integer i1 =1;// 底层是去方法去缓存池中找创建好的对象
  4. Integer i2 =1;// 底层是去方法去缓存池中找创建好的对象
  5. Integer i3 =300;// 底层还是 Integer i3 = new Integer(300); i3是引用,保存内存地址执行对象
  6. Integer i4 =300;// 底层还是 Integer i4 = new Integer(300); i4是引用,保存内存地址执行对象
  7. System.out.println(i1==i2);//true
  8. System.out.println(i3==i4);//false
  9. }
  10. }

Java中为了提高程序的执行效率,将byte范围的[-128,127]之间所有的包装对象提前创建好,放到方法区的”缓存池”中,目的是 :只要在这个区间的数据不需要再new。直接从缓存池中取出。

i1i2变量保存对象的地址是一样,输出true。JVM图如下

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzcyNTUxNw_size_16_color_FFFFFF_t_70 4

查看源代码分析如下:下面这段代码是Integer的valueOf方法具体实现

  1. @HotSpotIntrinsicCandidate
  2. public static Integer valueOf(int i) {
  3. if (i >= IntegerCache.low && i <= IntegerCache.high)
  4. return IntegerCache.cache[i + (-IntegerCache.low)];
  5. return new Integer(i);
  6. }

IntegerCache类的实现为:

  1. private static class IntegerCache {
  2. static final int low = -128;
  3. static final int high;
  4. static final Integer[] cache;
  5. static Integer[] archivedCache;
  6. static {
  7. // high value may be configured by property
  8. int h = 127;
  9. String integerCacheHighPropValue =
  10. VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
  11. if (integerCacheHighPropValue != null) {
  12. try {
  13. h = Math.max(parseInt(integerCacheHighPropValue), 127);
  14. // Maximum array size is Integer.MAX_VALUE
  15. h = Math.min(h, Integer.MAX_VALUE - (-low) -1);
  16. } catch( NumberFormatException nfe) {
  17. // If the property cannot be parsed into an int, ignore it.
  18. }
  19. }
  20. high = h;
  21. // Load IntegerCache.archivedCache from archive, if possible
  22. VM.initializeFromArchive(IntegerCache.class);
  23. int size = (high - low) + 1;
  24. // Use the archived cache if it exists and is large enough
  25. if (archivedCache == null || size > archivedCache.length) {
  26. Integer[] c = new Integer[size];
  27. int j = low;
  28. for(int i = 0; i < c.length; i++) {
  29. c[i] = new Integer(j++);
  30. }
  31. archivedCache = c;
  32. }
  33. cache = archivedCache;
  34. // range [-128, 127] must be interned (JLS7 5.1.7)
  35. assert IntegerCache.high >= 127;
  36. }

当Integer i1=1; 调用valueOf(1)判断i是否在[-128,127]之间,如果在这范围,去加载内部类IntegerCache,在类加载**同时**执行static代码块,这时就初始化缓存池创建256个对象。返回指向缓存池已经存在的对象引用;不在范围中就创建一个新对象(返回

Integer i2=1;也是和上面一样操作,这时它们会指向同一个对象的地址

触发自动拆箱、自动装箱:(使用debug模式可以清楚看到过程

自动装箱

1)插入基本数据类型到集合中(插入对象)

2)Integer i1 =1

3)基本类型包装类比较equals();变量转为包装类

自动拆箱

1)包装类之间进行 +-*/等运算,拆箱为基本类型

2)包装类基本类型执行==操作。

10、== 与 equals()重要

==:可以使用在基本类型变量引用类型变量,它是一个运算符

如果比较的是基本类型变量,比较的是两个变量保存的数据值是否相等不一定要相同类型

如果比较的是引用类型变量,比较的是两个变量**地址值**是否相等即引用变量是否指向同一个对象实体

案例代码操练

  1. public class Main1 {
  2. public static void main(String[] args) {
  3. A a1 = new A("A",20);
  4. A a2 = new A("A",20);
  5. double d = 5.0;
  6. int i = 5;
  7. System.out.println(d==i);// true
  8. System.out.println(a1==a2);//false,地址值不一样,保存的内容相同的
  9. }
  10. }

是不是有疑问?为什么int类型的和double类型比较会相等,其实又回到上面的第8点,运算符操作前先把类型统一转为”高级别”类型,都变成double类型,在进行比较值是否相等。

equals():它属于java.lang.Object类型的方法,作⽤也是判断两个对象是否相等。一般分为两种情况:

  • 情况一:类没有重写equals()方法覆盖Object类的),则通过equals()比较,等价于”==”
  • 情况二:类重写了equals()方法,一般重写equals()方法比较对象的内容(**类中相应的属性都相等否**)是否相等;内容相等就返回true。

    public class test {

    1. public static void main(String[] args) {
    2. String a = new String("ab"); // a 为⼀个引⽤
    3. String b = new String("ab"); // b为另⼀个引⽤,对象的内容⼀样
    4. String aa = "ab"; // 放在常量池中
    5. String bb = "ab"; // 从常量池中查找
    6. if (aa == bb) // true
    7. System.out.println("aa==bb");
    8. if (a == b) // false,⾮同⼀对象
    9. System.out.println("a==b");
    10. if (a.equals(b)) // true
    11. System.out.println("a equals b");
    12. if (42 == 42.0) { // true
    13. System.out.println("true");
    14. }
    15. }

    }

上面的String类中重写了equals()方法,不是比较两个引用对象的地址值,而是比较两个对象的”实体内容”是否相同。其实还有像FileDate包装类等都重写了equals()方法比较的都变成了实体内容。通常情况下,我们自定义的类如果使用equals()方法也需要重写equals()方法,这样也就比较对象的属性值是否都相等

说了这么多,还是看看Object类中的equals()方法原型:

  1. public boolean equals(Object obj) {
  2. return (this == obj);
  3. }

这个**Object的equals()和==作用相同的,比较对象的地址值。**

11 、hashCode 与 equals(重要)

hashCode()介绍

hashCode()的作用是获取哈希码散列码)返回一个int类型。这个哈希码的作用确定该对象在哈希表中的索引位置

hashCode与equals之间的区别

  • hashCode如果相等的情况下,对象的值不一定相等
  • 但是equals比较对象的内容相同,那么hashCode一定相等

如果重写了equals()方法后,一定要重写hashCode()方法为什么

我们要遵循

hashCoed相等的情况下,对象的值不一定相等

但是equals比较对象的内容相同,那么hashCode一定相等,所有重写了equals()方法需要重写hashCode保证原则不变不然重写了equals()方法不重写hashCode方法两个对象内容相同,但是hashCode不同。

12、在Java中定义一个不干事且没有参数的构造方法的作用

构造方法的作用是为堆区中的对象的属性初始化

Java程序在执行子类的构造器方法之前,如果没有使用super()调用父类的特定的构造方法,则会默认(不写super()也是默认写了调用父类中的无参构造方法,这时父类没有无参构造方法子类所有构造方法又没有调用super()调用父类的其他有参构造方法。子类编译时会报错。除非父类加上无参构造方法或者使用super调用其他有参构造方法(一般使用在子类的有参构造方法中且必须出现在首行)

既然体到super关键字,就顺便提一下:super的使用:子类重写了父类的方法后,在子类调用父类的方法必须加上super.方法,表明调用父类的方法。不加上super关键字默认是在子类中找,没找到再去父类中找

13、数组的初始化方式

第一种静态初始化:创建和赋值同时进行

  1. int [] a = new int[]{1,2,3,4,5}
  2. //也或者
  3. int str []={1,2,3,4};

第二种:动态初始化:数组定义与数组元素赋值分开进行

  1. int a [] = new int[4];
  2. a[0]=1;
  3. a[1]=2;
  4. a[2]=3;
  5. a[3]=4;
  6. //最常见错误方式
  7. int bb [] = new int[2]{1,2};//错误❌

" class="reference-link">watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzcyNTUxNw_size_16_color_FFFFFF_t_70 5

未完待续……

发表评论

表情:
评论列表 (有 0 条评论,285人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Java100

    1.什么是B/S架构?什么是C/S架构 B/S(Browser/Server),浏览器/服务器程序 C/S(Client/Server),客户端/服务端,桌面应用程序

    相关 js面试003

    前言 js面试题系列,不断更新中,力争为广大前端就业者更好的应对面试,也增加自己作为一名前端的技术常识。 new操作符具体干了什么呢 1、创建一个空对象,并且 t