java基础面试题一

目录

1、Java语言概述

1.1一个".java"源文件中是否可以包括多个类?有什么限制

[1.2Java 的优势](#1.2Java 的优势)

1.3常用的几个命令行操作都有哪些?(至少4个)

[1.4Java 中是否存在内存溢出、内存泄漏?如何解决?举例说明](#1.4Java 中是否存在内存溢出、内存泄漏?如何解决?举例说明)

[1. 内存溢出(OutOfMemoryError)](#1. 内存溢出(OutOfMemoryError))

[2. 内存泄漏(Memory Leak)](#2. 内存泄漏(Memory Leak))

[1.5. 如何看待Java是一门半编译半解释型的语言](#1.5. 如何看待Java是一门半编译半解释型的语言)

2、变量与运算符

[2.1 高效的方式计算2 * 8的值](#2.1 高效的方式计算2 * 8的值)

[2.2 &和&&的区别?](#2.2 &和&&的区别?)

[2.3 Java中的基本类型有哪些?String 是最基本的数据类型吗?](#2.3 Java中的基本类型有哪些?String 是最基本的数据类型吗?)

[2.4 Java开发中计算金额时使用什么数据类型?](#2.4 Java开发中计算金额时使用什么数据类型?)

[2.5 char型变量中能不能存储一个中文汉字,为什么?(*通快递)](#2.5 char型变量中能不能存储一个中文汉字,为什么?(*通快递))

[2.6 代码分析](#2.6 代码分析)

[2.7 int i=0; i=i++执行这两句化后变量 i 的值为](#2.7 int i=0; i=i++执行这两句化后变量 i 的值为)

[2.8 如何将两个变量的值互换](#2.8 如何将两个变量的值互换)

[2.9 boolean 占几个字节](#2.9 boolean 占几个字节)

[2.10 为什么Java中0.1 + 0.2结果不是0.3?](#2.10 为什么Java中0.1 + 0.2结果不是0.3?)

3.流程控制语句

[3.1 break和continue的作用](#3.1 break和continue的作用)

[3.2 if分支语句和switch分支语句的异同之处](#3.2 if分支语句和switch分支语句的异同之处)

[3.3 switch语句中忘写break会发生什么](#3.3 switch语句中忘写break会发生什么)

[3.4 Java支持哪些类型循环](#3.4 Java支持哪些类型循环)

[3.5 while和do while循环的区别](#3.5 while和do while循环的区别)

4.数组

[4.1 数组有没有length()这个方法? String有没有length()这个方法?](#4.1 数组有没有length()这个方法? String有没有length()这个方法?)

[4.2 有数组int[] arr,用Java代码将数组元素顺序颠倒](#4.2 有数组int[] arr,用Java代码将数组元素顺序颠倒)

[4.3 为什么数组要从0开始编号,而不是1](#4.3 为什么数组要从0开始编号,而不是1)

[4.4 数组有什么排序的方式,手写一下](#4.4 数组有什么排序的方式,手写一下)

[4.5 常见排序算法,说下快排过程,时间复杂度?](#4.5 常见排序算法,说下快排过程,时间复杂度?)

[4.6 二分算法实现数组的查找](#4.6 二分算法实现数组的查找)

[4.7 怎么求数组的最大子序列和](#4.7 怎么求数组的最大子序列和)

[4.8 Arrays 类的排序方法是什么?如何实现排序的?](#4.8 Arrays 类的排序方法是什么?如何实现排序的?)


1、Java语言概述

1.1一个".java"源文件中是否可以包括多个类?有什么限制

是!

一个源文件中可以声明多个类,但是最多只能有一个类使用public进行声明。 且要求声明为public的类的类名与源文件名相同。

1.2Java 的优势

  • 跨平台型

  • 安全性高

  • 简单性

  • 高性能

  • 面向对象性

  • 健壮性

1.3常用的几个命令行操作都有哪些?(至少4个)

  • 编译Java文件javac FileName.java

    将Java源文件编译为字节码文件(.class),例如javac HelloWorld.java

  • 运行Java程序java ClassName

    运行编译后的字节码文件。例如,编译生成的HelloWorld.class可以通过java HelloWorld来执行。

  • 查看Java版本java -version

    用于查看当前安装的Java版本,方便检查兼容性等问题。

  • 打包为JAR文件jar cf jarFileName.jar *.class

    将多个.class文件打包成一个JAR文件。例如,将所有的.class文件打包为MyApp.jar,可使用jar cf MyApp.jar *.class

1.4Java 中是否存在内存溢出、内存泄漏?如何解决?举例说明

1.4.1 内存溢出(OutOfMemoryError)

内存溢出通常发生在JVM无法分配足够的内存给应用程序时,表现为OutOfMemoryError。它主要分为以下几种类型:

  • 堆内存溢出:在堆区存放了大量数据,通常是因为对象过多或使用了大对象。

    • 示例:在代码中创建过多的对象或使用了大量的字符串常量,导致内存不足。
    • 解决方法 :增大JVM的堆内存分配,例如通过-Xmx参数来设置堆内存大小;优化代码,避免不必要的大量对象创建。
  • 栈内存溢出:由于方法递归层级太深或无限递归,导致栈区内存耗尽。

    • 示例:函数递归层数过多或无限递归,导致栈空间耗尽。
    • 解决方法 :优化递归逻辑,避免无限递归;增加JVM的栈大小,如-Xss参数。
  • 永久代溢出(元空间):在Java 8之前,类的元数据信息存放在永久代(PermGen),而Java 8之后存放在元空间(Metaspace)。

    • 示例:过多类的加载,或频繁生成和加载动态类。
    • 解决方法 :通过-XX:MaxMetaspaceSize增加元空间大小,或减少动态类生成和加载。

1.4.2 内存泄漏(Memory Leak)

内存泄漏是指程序中存在的无用对象无法被GC(垃圾回收器)清除,导致内存逐渐被占满。

  • 示例 :在集合(如ListMap)中不断添加对象但从未清除,即便这些对象已经不再需要。
    • 解决方法 :避免在集合中无限添加对象,可以手动移除不再使用的对象;另外,使用弱引用(WeakReference)来保存不重要的数据对象,这样当对象不再需要时可以被GC回收。

实际代码示例

java 复制代码
import java.util.ArrayList;
import java.util.List;

public class MemoryLeakExample {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        
        // 模拟内存泄漏
        while (true) {
            list.add("Memory Leak Example"); // 不断向集合中添加数据而不清除
        }
    }
}

此示例代码会导致内存泄漏,因为程序一直在向list中添加字符串而不清理内存,最终会导致OutOfMemoryError

1.5. 如何看待Java是一门半编译半解释型的语言

2、变量与运算符

2.1 高效的方式计算2 * 8的值

使用 <<

2.2 &和&&的区别?

  1. & - 按位与 和 逻辑与
  • 按位与 :如果&用于两个整数或二进制数字之间,它会执行逐位比较。仅当两个位都为1时,结果为1

    • 示例5 & 35的二进制是010130011),结果为0001,即1。
  • 逻辑与 :如果&用于两个布尔表达式之间,它作为逻辑运算符。它会在两边表达式都为true时返回true

    • 示例true & false 返回false

    注意 :使用&进行逻辑运算时,不会短路 。即便左侧表达式为false,右侧表达式也会被计算。

  1. && - 逻辑与(带短路特性)
  • &&只能用于布尔表达式的逻辑运算,表示逻辑与操作,且具有短路特性

  • 短路特性 :如果第一个表达式为false,则整个表达式必定为false,此时右侧表达式不会被计算

    • 示例(a != 0) && (b / a > 1),若a0,则(a != 0)false,右侧的除法操作不会执行,从而避免除零异常。
java 复制代码
int a = 0;
int b = 10;

// 使用 &&,具有短路特性
if (a != 0 && b / a > 1) {
    System.out.println("不会抛出异常");
}

// 使用 &,没有短路特性
if (a != 0 & b / a > 1) {
    System.out.println("此处可能会抛出除零异常");
}

2.3 Java中的基本类型有哪些?String 是最基本的数据类型吗?

  1. 整数类型
  • byte :占用1字节,取值范围为-128127
  • short :占用2字节,取值范围为-32,76832,767
  • int :占用4字节,取值范围为-2^312^31 - 1
  • long :占用8字节,取值范围为-2^632^63 - 1
  1. 浮点数类型
  • float:占用4字节,单精度浮点数。
  • double:占用8字节,双精度浮点数。
  1. 字符类型
  • char :占用2字节,存储单个字符(Unicode编码),取值范围为065,535
  1. 布尔类型
  • boolean :占用1位,取值为truefalse

String 不是基本数据类型

String 在Java中不是基本数据类型 ,而是一个引用数据类型(类)。尽管String的使用方式类似于基本数据类型,但它实际上是一个对象,可以调用方法,例如length()substring()等。String在Java中是不可变的,每次对String的修改都会创建一个新的对象。

2.4 Java开发中计算金额时使用什么数据类型?

不能使用float或double,因为精度不高。

使用BigDecimal类替换,可以实现任意精度的数据的运算。

2.5 char型变量中能不能存储一个中文汉字,为什么?(*通快递)

可以的。char c1 = '中';

char c2 = 'a'。

因为char使用的是unicode字符集,包含了世界范围的所有的字符。

2.6 代码分析

java 复制代码
short s1=1; 
s1=s1+1;  //有什么错?  =右边是int类型。需要强转
java 复制代码
short s1=1;
s1+=1; //有什么错?   没错

2.7 int i=0; i=i++执行这两句化后变量 i 的值为

0。

2.8 如何将两个变量的值互换

java 复制代码
String s1 = "abc";
String s2 = "123";

String temp = s1;
s1 = s2;
s2 = temp;

2.9 boolean 占几个字节

java 复制代码
编译时不谈占几个字节。

但是JVM在给boolean类型分配内存空间时,boolean类型的变量占据一个槽位(slot,等于4个字节)。
细节:true:1  false:0

>拓展:在内存中,byte\short\char\boolean\int\float : 占用1个slot
              double\long :占用2个slot

2.10 为什么Java中0.1 + 0.2结果不是0.3?

在代码中测试0.1 + 0.2,你会惊讶的发现,结果不是0.3,而是0.3000......4。这是为什么?

几乎所有现代的编程语言都会遇到上述问题,包括 JavaScript、Ruby、Python、Swift 和 Go 等。引发这个问题的原因是,它们都采用了IEEE 754标准

IEEE是指"电气与电子工程师协会",其在1985年发布了一个IEEE 754计算标准,根据这个标准,小数的二进制表达能够有最大的精度上限提升。但无论如何,物理边界是突破不了的,它仍然不能实现"每一个十进制小数,都对应一个二进制小数"。正因如此,产生了0.1 + 0.2不等于0.3的问题。

具体的:

整数变为二进制,能够做到"每个十进制整数都有对应的二进制数",比如数字3,二进制就是11;再比如,数字43就是二进制101011,这个毫无争议。

对于小数,并不能做到"每个小数都有对应的二进制数字"。举例来说,二进制小数0.0001表示十进制数0.0625 (至于它是如何计算的,不用深究);二进制小数0.0010表示十进制数0.125;二进制小数0.0011表示十进制数0.1875。看,对于四位的二进制小数,二进制小数虽然是连贯的,但是十进制小数却不是连贯的。比如,你无法用四位二进制小数的形式表示0.125 ~ 0.1875之间的十进制小数。

所以在编程中,遇见小数判断相等情况,比如开发银行、交易等系统,可以采用四舍五入或者"同乘同除"等方式进行验证,避免上述问题。

3.流程控制语句

3.1 break和continue的作用

  • break:终止整个循环。
  • continue:跳过当前循环的剩余部分,直接进行下一次循环。

3.2 if分支语句和switch分支语句的异同之处

  • if-else语句优势

    • if语句的条件是一个布尔类型值,if条件表达式为true则进入分支,可以用于范围的判断,也可以用于等值的判断,使用范围更广

    • switch语句的条件是一个常量值(byte,short,int,char,枚举,String),只能判断某个变量或表达式的结果是否等于某个常量值,使用场景较狭窄

  • switch语句优势

    • 当条件是判断某个变量或表达式是否等于某个固定的常量值时,使用if和switch都可以,习惯上使用switch更多。因为效率稍高。当条件是区间范围的判断时,只能使用if语句。

    • 使用switch可以利用穿透性,同时执行多个分支,而if...else没有穿透性。

3.3 switch语句中忘写break会发生什么

case穿透

3.4 Java支持哪些类型循环

  • for;while;do-while

  • 增强for (或foreach)

3.5 while和do while循环的区别

  • do-while至少会执行一次。

4.数组

4.1 数组有没有length()这个方法? String有没有length()这个方法?

数组没有length(),是length属性。

String有length()

4.2 有数组int[] arr,用Java代码将数组元素顺序颠倒

java 复制代码
public class ReverseArray {
    public static void main(String[] args) {
        int[] arr = {1, 2, 3, 4, 5};
        reverseArray(arr);
        
        // 输出颠倒后的数组
        for (int i : arr) {
            System.out.print(i + " ");
        }
    }
    public static void reverseArray(int[] arr) {
        int left = 0;
        int right = arr.length - 1;

        while (left < right) {
            // 交换 arr[left] 和 arr[right]
            int temp = arr[left];
            arr[left] = arr[right];
            arr[right] = temp;

            // 移动指针
            left++;
            right--;
        }
    }
}

4.3 为什么数组要从0开始编号,而不是1

数组的索引,表示了数组元素距离首地址的偏离量。因为第1个元素的地址与首地址相同,所以偏移量就是0。所以从0开始。

4.4 数组有什么排序的方式,手写一下

冒泡。

快排。

4.5 常见排序算法,说下快排过程,时间复杂度?

快速排序的步骤

  1. 选取基准元素(Pivot):从待排序数组中选择一个基准元素。基准元素的选择可以是第一个元素、最后一个元素、随机元素或中间元素。

  2. 分区(Partition):将数组中的元素重新排序,使得基准元素左边的元素都小于它,右边的元素都大于它。此时,基准元素在其正确的排序位置上。

  3. 递归排序:递归地对基准元素左边和右边的子数组进行快速排序,直到子数组的长度为 1(已排序)。

快速排序代码示例

java 复制代码
public class QuickSort {
    public static void quickSort(int[] arr, int low, int high) {
        if (low < high) {
            int pivotIndex = partition(arr, low, high); // 获取基准元素的正确位置
            quickSort(arr, low, pivotIndex - 1);        // 对左子数组排序
            quickSort(arr, pivotIndex + 1, high);       // 对右子数组排序
        }
    }
    private static int partition(int[] arr, int low, int high) {
        int pivot = arr[high];  // 选择最后一个元素作为基准
        int i = low - 1;        // i 是比基准小的元素的最后位置
        for (int j = low; j < high; j++) {
            if (arr[j] < pivot) {       // 找到比基准小的元素
                i++;
                // 交换 arr[i] 和 arr[j]
                int temp = arr[i];
                arr[i] = arr[j];
                arr[j] = temp;
            }
        }
        // 将基准元素放到正确位置
        int temp = arr[i + 1];
        arr[i + 1] = arr[high];
        arr[high] = temp;

        return i + 1; // 返回基准元素的索引
    }
    public static void main(String[] args) {
        int[] arr = {3, 6, 8, 10, 1, 2, 1};
        quickSort(arr, 0, arr.length - 1);
        for (int i : arr) {
            System.out.print(i + " ");
        }
    }
}

时间复杂度

  • 平均时间复杂度O(n log n),其中 n 是数组的长度。
  • 最坏时间复杂度O(n^2),最坏情况发生在每次分区时选择的基准总是数组的最大或最小值,这会导致分区极不平衡。
  • 最好时间复杂度O(n log n),当每次分区都能将数组平分时。

4.6 二分算法实现数组的查找

java 复制代码
public class BinarySearch {
    public static int binarySearch(int[] arr, int target) {
        int left = 0;
        int right = arr.length - 1;
        while (left <= right) {
            int mid = left + (right - left) / 2; // 避免直接相加可能产生的溢出
            if (arr[mid] == target) {
                return mid; // 找到目标,返回索引
            } else if (arr[mid] < target) {
                left = mid + 1; // 目标在右半部分
            } else {
                right = mid - 1; // 目标在左半部分
            }
        }
        return -1; // 若未找到目标,返回 -1
    }
    public static void main(String[] args) {
        int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; // 必须为已排序数组
        int target = 7;
        int result = binarySearch(arr, target);
        if (result != -1) {
            System.out.println("目标元素在索引位置: " + result);
        } else {
            System.out.println("目标元素未找到");
        }
    }
}

代码说明

  1. left 指针指向数组的起始位置,right 指针指向数组的末尾位置。
  2. 计算中间位置 mid
  3. 如果 arr[mid] 等于 target,返回 mid 索引。
  4. 如果 arr[mid] 小于 target,则将 left 移动到 mid + 1,表示只需在右半部分查找。
  5. 如果 arr[mid] 大于 target,则将 right 移动到 mid - 1,表示只需在左半部分查找。
  6. 如果 left 超过 right,说明目标元素不存在于数组中,返回 -1。

时间复杂度

  • 时间复杂度O(log n),每次查找范围减半。
  • 空间复杂度O(1),只需常量空间。

4.7 怎么求数组的最大子序列和

java 复制代码
/*
 * 输入一个整形数组,数组里有正数也有负数。数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和。
 * 求所有子数组的和的最大值。要求时间复杂度为O(n)。
 例如:输入的数组为1, -2, 3, 10, -4, 7, 2, -5,和最大的子数组为3, 10, -4, 7, 2,
 因此输出为该子数组的和18。
 */
public class ArrDemo {
	public static void main(String[] args) {
		int[] arr = new int[]{1, -2, 3, 10, -4, 7, 2, -5};
		int i = getGreatestSum(arr);
		System.out.println(i);
	}
	public static int getGreatestSum(int[] arr){
		int greatestSum = 0;
		if(arr == null || arr.length == 0){
			return 0;
		}
		int temp = greatestSum;
		for(int i = 0;i < arr.length;i++){
			temp += arr[i];
			if(temp < 0){
				temp = 0;
			}
			if(temp > greatestSum){
				greatestSum = temp;
			}
		}
		if(greatestSum == 0){
			greatestSum = arr[0];
			for(int i = 1;i < arr.length;i++){
				if(greatestSum < arr[i]){
					greatestSum = arr[i];
				}
			}
		}
		return greatestSum;
	}
}

4.8 Arrays 类的排序方法是什么?如何实现排序的?

Arrays.sort() 的常见用法

  1. 基本数据类型排序

    • Arrays.sort(int[] a)
    • Arrays.sort(double[] a)
    • Arrays.sort(char[] a)

    对于基本数据类型数组,Arrays.sort() 使用了双轴快速排序算法(Dual-Pivot Quicksort)。它是一种对传统快速排序进行优化的算法,适用于大多数情况。该算法在 Java 7 中引入,能够有效地减少比较次数和内存访问,具有较好的性能。

  2. 对象数组排序

    • Arrays.sort(T[] a):用于 Comparable 对象数组的排序。
    • Arrays.sort(T[] a, Comparator<? super T> c):用于指定 Comparator 比较器的对象数组排序。

    对于对象数组,Arrays.sort() 使用了Timsort。Timsort 是一种混合排序算法,结合了归并排序和插入排序,适合处理部分已排序的数据。在 Java 8 中,Timsort 被广泛应用于对象数组的排序,因为它具有稳定性(相同元素的顺序不会改变),并且在大多数情况下效率较高。

实现原理

  • 双轴快速排序(Dual-Pivot Quicksort)

    1. 选择两个基准点,将数组划分为三个部分:小于第一个基准点、介于两个基准点之间、大于第二个基准点的部分。
    2. 递归地对这三个部分进行快速排序。
    3. 双轴快速排序在大多数情况下比单轴快速排序更高效,但仍然是不稳定排序。
  • Timsort

    1. 将数组分成若干小块(run)并分别排序。
    2. 在较小块的元素已排序的情况下,使用插入排序。
    3. 然后将小块归并排序,结合了归并排序的效率和插入排序的稳定性。
    4. Timsort 是一种稳定的排序算法,在处理已部分排序的数据时效率更高。

代码示例

java 复制代码
import java.util.Arrays;
import java.util.Comparator;

public class ArraysSortExample {
    public static void main(String[] args) {
        // 基本数据类型排序
        int[] intArray = {3, 1, 4, 1, 5, 9};
        Arrays.sort(intArray);
        System.out.println("排序后的int数组: " + Arrays.toString(intArray));

        // 对象数组排序(Comparable)
        String[] strArray = {"banana", "apple", "cherry"};
        Arrays.sort(strArray);
        System.out.println("排序后的String数组: " + Arrays.toString(strArray));

        // 对象数组排序(Comparator)
        Arrays.sort(strArray, Comparator.reverseOrder());
        System.out.println("按逆序排序的String数组: " + Arrays.toString(strArray));
    }
}
相关推荐
xlsw_2 小时前
java全栈day20--Web后端实战(Mybatis基础2)
java·开发语言·mybatis
神仙别闹3 小时前
基于java的改良版超级玛丽小游戏
java
黄油饼卷咖喱鸡就味增汤拌孜然羊肉炒饭3 小时前
SpringBoot如何实现缓存预热?
java·spring boot·spring·缓存·程序员
暮湫4 小时前
泛型(2)
java
超爱吃士力架4 小时前
邀请逻辑
java·linux·后端
南宫生4 小时前
力扣-图论-17【算法学习day.67】
java·学习·算法·leetcode·图论
转码的小石4 小时前
12/21java基础
java
李小白664 小时前
Spring MVC(上)
java·spring·mvc
GoodStudyAndDayDayUp4 小时前
IDEA能够从mapper跳转到xml的插件
xml·java·intellij-idea
装不满的克莱因瓶5 小时前
【Redis经典面试题六】Redis的持久化机制是怎样的?
java·数据库·redis·持久化·aof·rdb