《面试专题-----经典高频面试题收集一》解锁 Java 面试的关键:深度解析常见高频经典面试题(第一篇)

大家好,我是码农阿豪,一位热爱 Java 编程的程序员。今天我想和大家分享一些常见的 Java 面试题,通过收集解析这些问题,希望能够帮助大家更好地准备面试,突破技术瓶颈,把面试官按在地上摩擦

第一章

1. 运算符

  • 运算符&和&&、|和||的区别?
//& 按位与操作,只有对应的两个二进制数为1时,结果位才为1
1&1=1
1&0=0
0&1=0
0&0=0

//| 按位或操作,有一个为1的时候,结果位就为1
1|1=1
1|0=1
0|1=1
0|0=0

//& 和 && 都能实现 和 这个功能
//区别:& 两边都运算,而&&先算左侧,若左侧为false,那么右边就不运算,判断语句中推荐&&,效率高

//| 和 || 和上面的类型
//区别:|| 只要满足第一个条件,后面的条件就不再判断,而|要对所有条件进行判断

//把&&和||称为短路运算符

  • 用最有效率的方法计算2乘以2的3次方
//原理:将一个数左移n位,就是将这个数乘以2的n次方
2 << 3 = 16

//扩展:常见的JDK源码里面HashMap的默认容量是16
int DEFAULT_INITAL_CAPACITY = 1 << 4; //16

//直接是二进制操作了,表示将1左移4位,变成10000,变成十进制就是16

  • 写个方法,传递两个非0的int数值进去,实现变量交换,有几种方式?
//方式一
public static void swap(int a, int b) {
    System.out.printf("a=%d,b=%d", a, b);
    a = a + b;
    b = a - b; //b = a + b - b = a
    a = a - b; //a = a + b - a = b
    System.out.printf("a=%d, b);
}

//方式二 异或运算(一个数与另一个数异或两次是其本身,一个数和自身异或结果是0)
public static void swap2(int a, b);
    a = a ^ b; //a1 = a^b
    b = b ^ a; //b = b^a^b = a
    a = a ^ b; //a = a1^b = a^b^a = b
    System.out.printf("a=%d, b);

2. 数据类型

  • 说下Java数据类型分类
//基础数据类型:byte、short、int、long、float、double、char、boolean
//引用类型:其它都是引用类型
//String和Enum也是引用类型
  • 运算:定义变量int i = 5,那么return i++;和return ++i; 返回的结果是什么?
//i++ 返回 5,先返回后增加
//++i 返回 6,先增加后返回
  • ==和equals的区别
//基本数据类型的比较,要用==判断是否相等
//引用数据类型:==比较的是内存地址是否一样,不同对象的内存地址不一样,equals比较的是具体内容,也可以自定义什么条件去判断两个对象是否一样

3. try-catch-finally

  • 1.下面代码的 try-catch-finally 语句,try里面有个return,finally里面也有一个return,结果会返回什么?为什么?
public static int test1() {  
    int a = 1; 
    try {  
        System.out.println(a / 0);  
        a = 2;  
    } catch (ArithmeticException e) {  
        a = 3;  
        return a;  
    } finally {  
        a = 4;  
    }  
    
    return a;  
}

public static int test2() {  
    int a = 1; 
    try {  
        System.out.println(a / 0);  
        a = 2;  
    } catch (ArithmeticException e) {  
        a = 3;  
        return a;  
    } finally {  
        a = 4;  
        return a;  
    }  
}

//test1()返回3,test2()返回4
//在执行catch中的return之前一定会先执行finally中的代码(如果有finally),如果finally中有return语句就直接执行return方法

4. try-with-resource

  • 1.使用新版的JDK处理IO流,编写一下基础代码,将一个txt文本里面的内容拷贝到另一个txt文本中
//需要关闭的资源只要实现了java.lang.AutoCloseable,就可以⾃动被关闭
//try()⾥⾯可以定义多个资源,它们的关闭顺序是最后在try()定义的资源先关闭
try (
    FileInputStream fis = new FileInputStream("/Users/lcz/Desktop/test.txt");
    BufferedInputStream bis = new BufferedInputStream(fis);
    FileOutputStream fos = new FileOutputStream("/Users/lcz/Desktop/copy.txt");
    BufferedOutputStream bos = new BufferedOutputStream(fos);
) {
    int size;
    byte[] buf = new byte[1024];
    while ((size=bis.read(buf) != -1)) {
        bos.write(buf,0,size);
    }
} catch (Exception e) {
    e.printStackTrace();
}

5. 文件API和递归

  • 找出某目录下的所有子目录和子文件,并打印到控制台上
public static void main(String[] args) {
    List<String> paths = new ArrayList<>();
    getAllFilePaths(new File("/Users/lcz/Desktop/demo"), paths);
    
    for (String path: paths) {
        System.out.println(path);
    }
}

private static void getAllFilePaths(File filePath, List<String> paths) {
    File[] files = filePath.listFiles();
    if (files == null) {
        return;
    }
    
    for (File f: files) {
        if (f.isDirectory()) {
            paths.add(f.getPath());
            getAllFilePaths(f, paths);
        } else {
            paths.add(f.getPath());
        }
    }
}

第二章

1. 字符串(重点)

  • 1.String str = new String(“xxx”); 创建了几个对象?
//创建一个对象:常量池存在,则直接new一个对象
//创建两个对象:常量池不存在,则在常量池创建一个对象,在堆中也创建一个对象
  • 2.下面是比较什么?输出结果是什么?为什么是这样的结果?
String str1 = new String("s1");
String str2 = "s2";
String str3 = "s2";

System.out.println(str1 == str2); //false
System.out.println(str2 == str3); //true

//比较引用的内存地址是否一样
//第一个是false:new 创建新的对象会开辟新的内存空间,所以地址不一样
//第二个是true:都是从常量池里面获取,"s2"存在于常量池中

  • 写出下面代码的各个结果,如果需要两个都为true,应该怎么修改?
String s1 = "s1";
String s2 = s1 + "s2"; //变量+常量 来自堆
String s3 = "s1" + "s2"; //常量+常量 来自常量池

System.out.println(s2 == "s1s2"); //false
System.out.println(s3 == "s1s2"); //true

//第⼀条语句打印的结果为false, s2 = s1 + ".net",变量+常量=堆,构建了⼀个新的string对象,并将对象引⽤赋予s2变量,常量池中的地址不⼀样,但是值⼀样。
//第⼆条语句打印的结果为true,javac编译可以对【字符串常量】直接相加的表达式进⾏优化,不⽤等到 运⾏期再去进⾏加法运算处理,⽽是直接将其编译成⼀个这些常量相连的结果

//如果需要第⼀个输出为true,只需要把变量改为常量即可 fianl String s1 = "s1"; 不管是new String("XXX")和直接常量赋值,都会在字符串常量池创建.只是new String("XXX")⽅式会在堆中创建⼀个对象去指向常量池的对象,普通的常量赋值是直接赋值给变量
  • String、StringBuffer和StringBuilder的区别?分别在哪些场景下使用?
//三者都是final, 不允许被继承, 本质都是char[]字符数组实现
//String、StringBuffer与StringBuilder中,String是不可变对象,另外两个是可变的

//StringBuilder 效率更快,因为它不需要加锁,不具备多线程安全
//StringBuffer ⾥⾯操作⽅法⽤synchronized ,效率相对更低,是线程安全的

//使⽤场景: 
//操作少量的数据⽤String,但是常改变内容且操作数据多情况下最好不要⽤String,因为每次⽣成中间对象性能会降低
//单线程下操作⼤量的字符串⽤StringBuilder,虽然线程不安全但是不影响
//多线程下操作⼤量的字符串,且需要保证线程安全 则⽤StringBuffer

2. 面向对象

  • 面向对象的四大特性是?分别解释下
//抽象、封装、继承、多态

//抽象:
//关键词abstract声明的类叫作抽象类,abstract声明的⽅法叫抽象⽅法;
//⼀个类⾥包含了⼀个或多个抽象⽅法,类就必须指定成抽象类;
//抽象⽅法属于⼀种特殊⽅法,只含有⼀个声明,没有⽅法体;

//封装:
//封装是把过程和数据包围起来,对数据的访问只能通过已定义的接⼝即⽅法;
//在java中通过关键字private,protected和public实现封装;
//封装把对象的所有组成部分组合在⼀起,封装定义程序如何引⽤对象的数据,封装实际上使⽤⽅法将类的数据隐藏起来,控制⽤户对类的修改和访问数据的程度;

//继承:
//⼦类继承⽗类的特征和⾏为,使得⼦类对象具有⽗类的⽅法和属性,⽗类也叫 基类,具有公共的⽅法和属性;

//多态:
//同⼀个⾏为具有多个不同表现形式的能⼒;
//优点:减少耦合、灵活可拓展;
//⼀般是继承类或者重写⽅法实现;

3. 接口

  • Overload和Override的区别?
//重载Overload:表示同⼀个类中可以有多个名称相同的⽅法,但这些⽅法的参数列表各不相同,参数个数或类型不同;
//重写Override:表示⼦类中的⽅法可以与⽗类中的某个⽅法的名称和参数完全相同;
  • 接口是否可以继承接口?接口是否支持多继承?类是否支持多继承?接口里面是否可以有方法实现?
//接口里可以有静态方法和方法体
//接口中所有的方法必须是抽象方法(JDK8之后就不是了)
//接口不是要被类继承了,而是实现
//接口支持多继承,类不支持多个类继承

//一个类只能继承一个类,但是能实现多个接口,接口能继承另一个接口,接口的继承使用exstends关键字,和类继承一样
  • 是否了解JDK8里面接口新特性?
//interface中可以有static方法,但必须有方法体,该方法只属于接口,接口名直接调用该方法
//接口中新增default关键字修饰的⽅法,default⽅法只能定义在接⼝中,可以在⼦类或⼦接⼝中被重写,default定义的⽅法必须有⽅法体
//⽗接⼝的default⽅法如果在⼦接⼝或⼦类被重写,那么⼦接⼝实现对象、⼦类对象,调⽤该⽅法,以重写为准
//本类、接⼝如果没有重写⽗类(即接⼝)的default⽅法,则在调⽤default⽅法时,使⽤⽗类 (接⼝) 定义的default⽅法逻辑

第三章(集合框架List)

1. 说下Vector和ArrayList、LinkedList联系和区别?分别的使用场景

//线程安全:
//ArrayList:底层是数组实现,线程不安全,查询和修改非常快,但是新增和删除慢(特殊场景除外:尾端新增和删除)
//LinkedList:底层是双向链表实现,线程不安全,查询和修改速度慢(特殊场景除外:首端的查询和修改),新增和删除速度快
//Vector:底层是数组实现,线程安全的,操作的时候用synchronized进行加锁

//使用场景:
//Vector已经很少用了
//增加和删除的场景多,用LinkedList
//查询和修改的场景多,用ArrayList

2. 如果要保证线程安全,ArrayList应该怎么做,有几种方式?

//自己写一个包装类,根据业务对List操作进行加锁
//Collections.synchronizedList(new ArrayList<>());使用synchronized加锁
//CopyOnWriteArrayList<>() 使⽤ReentrantLock加锁

3.了解CopyOnWriteArrayList吗?它和Collections.synchronizedList实现线程安全有什么区别,使用场景是怎样的?

//CopyOnWriteArrayList:执行修改操作时,会拷贝一份新的数组进行操作,代价十分昂贵,在执行完成后将原来的集合指向新的集合来完成修改操作,源码里面使用ReentrantLock可重入锁来保证不会有多个线程同时拷贝一份数组
//场景:读高性能,适用读操作远远大于写操作的场景中使用(读的时候是不需要加锁的,直接获取,删除和新增是需要加锁的,读多写少)

//Collections.synchronizedList:几乎在每个方法上就加了synchronized同步锁
//场景:写操作性能比CopyOnWriteArrayList好,读操作不如

4.CopyOnWriteArrayList的设计思想是怎样的,有什么缺点?

//设计思想:读写分离+最终一致
//缺点:内存占用问题,写时的复制机制,内存会同时驻扎两个对象的内存,旧对象和新的写入对象,如果对象大,则容易发生Yong GC和Full GC

5.说一下ArrayList的扩容机制是怎样的?

//未指定集合容量,默认是0,若已经指定⼤⼩则集合⼤⼩为指定的
//当集合第⼀次添加元素的时候,集合⼤⼩扩容为10
//当ArrayList的元素个数⼤于其容量,扩容的⼤⼩=原始⼤⼩+原始⼤⼩/2

6.设计一个简单的ArrayList(需要包含构造函数、add(e)、扩容机制)

//计算容量+确保容量
private void ensureCapacityInternal(int minCapacity){  
    //如果是初次扩容,则使⽤默认的容量  
    if(elementData == EMPTY_ELEMENT_DATA){
        minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); 
    }
    //是否需要扩容,需要的最少容量⼤于现在数组的⻓度则要扩容    
    if(minCapacity - elementData.length > 0){
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        //如果新容量 < 最⼩容量, 则将最新的容量赋值给新的容量
        if(newCapacity - minCapacity < 0){
            newCapacity = minCapacity;
        }
        //创建新数组
        Object[] objects = new Object[newCapacity];
        //将旧的数组复制到新的数组⾥⾯
        System.arraycopy(elementData,objects,elementData.length);
        //修改引⽤
        elementData = objects;
    }  
}

  • 总结重点,鼓励读者在日常工作中不断深化对 Java 的理解,持续学习和实践。

期望与后续更新

我希望这些 Java 面试题的分享能够对你有所帮助,为你的面试之路提供有力支持。未来,我将持续更新类似的内容,涵盖更多深入的主题,如Map,并发编程基础与进阶,中间件,数据库,通信协议,框架等,帮助大家更全面地了解 Java 生态系统。

如果你有任何关于 Java 面试题、技术深度解析或其他方面的建议和期望,欢迎在评论区分享。我将根据大家的反馈,优先处理感兴趣的话题,并确保内容的实用性和深度。

感谢阅读

最后,感谢大家花时间阅读我的博客。如果你喜欢这类内容,记得关注我的博客,我们一起在技术的海洋中不断前行,共同成长。

原文地址:https://blog.csdn.net/weixin_44976692/article/details/135145322

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


文章浏览阅读2.2k次,点赞6次,收藏20次。在我们平时办公工作中,很多时候我们经常会使用到虚拟机来进行环境的测试,我们平时在虚拟机上接触的最多的莫过于Linux和Winwdos。不过虚拟机环境和物理机环境是无法直接传输的,那么有的时候呢,同学们又想要在两者之间相互传输文件,可能就会使用QQ邮箱等形式来传输,这样的效率又慢而且繁琐,今天我就为大家带来一种非常便捷的传输方式。通过XFTP工具来进行文件传输。_xftp连接windows
文章浏览阅读1k次。解决 Windows make command not found 和 安装 GCC 环境_windows下载gcc
文章浏览阅读3.2k次,点赞2次,收藏6次。2、鼠标依次点击“计算机配置“ - ”管理模板“ - ”网络“ - ”Lanman工作站”,点击右侧的“启用不安全的来宾登录”策略。Windows访问samba共享时,提示“你不能访问此共享文件夹,因为你组织的安全策略阻止未经身份验证的来宾访问”1、键盘按下window+R键,输入gpedit.msc,启动本地组策略编辑器。首先在终端中输入sudo ufw status查看当前防火墙状态。默认状态是“未配置”,修改为“已启用”。示例:创建一个narada的目录在/home下。1.更新apt储存库列表。_ubuntu samba 目标文件夹访问被拒绝
文章浏览阅读1.3w次。蓝光版属于高清版的一种。BD英文全名是Blu-ray Disc,一种高清的电影版本,这种电影十分清晰但是数据量巨大,占数十G甚至上百G的容量,只有蓝光光碟才能装得下,所以这种高清电影被称为BD版。一般的高清电影多半是从蓝光电影、国外的高清电视频道上压制而来的,可以通过网络下载,多数都经过二次压缩,画质要逊于原视频,不过压缩后的容量从蓝光的25G-50G会减少成4G-8G等(15G-20G不等)。众所周知,视频有两种常见的清晰度,BD和HD,在看电影的时候最常出现这两个标志,那么BD和HD具体指的是什么呢?_bd hd
文章浏览阅读974次,点赞7次,收藏8次。提供了更强大的功能,因为它允许直接访问当前元素,而不需要类型转换。接口,它可以提供一个迭代器,用于按顺序访问集合中的元素。接口是只读的,它只能支持前向迭代,不能修改集合中的元素。类型的集合实例,并向其中添加了几个元素。接口,可以创建一个能够迭代访问泛型集合中元素的迭代器。接口,我们可以在 C# 中实现可迭代的集合,并使用。循环和迭代器手动遍历集合,并输出每个元素的值。接口表示一个可枚举的集合,它定义了一个方法。属性,用于获取集合中当前位置的元素。存储集合中的元素,并实现了。的泛型集合类,它实现了。
文章浏览阅读1.4w次,点赞5次,收藏22次。如果使用iterator的remove方法则会正常,因为iterator的remove方法会在内部调用List的remove方法,但是会修改excepedModCount的值,因此会正常运行。因为遍历过程中进行remove 操作时,该位置后面的元素会挤到前面来,这时候会发生一种情况就是原来元素的位置会被他后面的元素取代,而该位置已经遍历过了,所以该元素不会背遍历。当我们倒序遍历元素的时候,无论删除元素之后的元素怎么移动,之前的元素对应的索引(index)是不会发生变化的,所以在删除元素的时候不会发生问题。_list删除某个元素
文章浏览阅读2.9w次,点赞45次,收藏192次。Windows下配置Visual Studio _vs2022环境变量配置
文章浏览阅读7w次,点赞162次,收藏778次。pip 是Python包管理工具,提供了对 Python 包的查找、下载、安装、卸载的功能,目前Python 3.4 和 2.7 及以上版本都有配套安装,一般pip的位置在...pythonScripts文件夹里面,而在其他版本需要自行下载。_python pip install安装
文章浏览阅读5.8k次,点赞2次,收藏12次。①此电脑右击----->选择属性----->高级系统设置----->环境变量----->path----->编辑----->新建。第一个选项意思就是将安装路径填入到系统环境变量中,这里勾选,后面使用可能会出现问题,建议不要勾选,安装好之后手动添加环境变量。注意:如果提示conda不是内部或外部命令,原因是Anaconda的环境变量没配置好。如果不想立即打开anaconda,不勾选直接finish就好。②输入 conda --version ,查看conda环境。②直接按win键,搜索“环境变量”_windows安装anaconda
文章浏览阅读5.1k次,点赞8次,收藏55次。Windows 系统从零配置 Python 环境,安装CUDA、CUDNN、PyTorch 详细教程_windows cuda cudnn配置
文章浏览阅读1.5w次,点赞54次,收藏68次。macOS系统自带有VNC远程桌面,我们可以在控制端上安装配置VNC客户端,以此来实现远程控制macOS。但通常需要在不同网络下进行远程控制,为此,我们可以在macOS被控端上使用cpolar做内网穿透,映射VNC默认端口5900,通过所生成的公网地址,来实现在公网环境下远程控制VNC。_vnc mac
文章浏览阅读2.4k次,点赞5次,收藏11次。进入后根据自己的电脑系统下载,这是python 3.10版本下载地址,如果想要下载其它版本可进入此链接(下载完成后点击进行安装点击下一步,到这一步时,可以选择将Anaconda添加我的PATH环境变量中,这样就不用自己手动配置和环境变量。安装完成后,打开终端,输出 python 命令可查看是否安装成功。如果显示自己刚才安装的版本号说明安装成功。查看conda版本命令:conda info。_paddlespeech下载
文章浏览阅读3.3k次。所以如果要删除之前新增的课程编译原理,只需输入命令del Course:8:Cname,同时还应该把本课程的学分删除del Course:8:Ccredit,如下图所示;Redis并没有修改数据的命令,所以如果在Redis中要修改一条数据,只能在使用set命令时,使用同样的键值,然后用新的value值来覆盖旧的数据。先调用get命令,输出原先的值,然后set新的值,最后再get得到新值,所以修改成功。输入命令后没有报错,表示成功了,刷新windows的服务,多了一个redis服务。_redis windows服务
文章浏览阅读2.1w次,点赞9次,收藏56次。​​接着在【工作负荷】中,选择【使用C++桌面开发】 ,右边【安装详细信息】去除其它可选项,只勾选【MSVCv142 】和 【Windows 10 SDK】,按图示修改,然后右下角点击安装,之后会有提示让你重启电脑。重启电脑之后,再进行pip安装。报错原因是pip所安装的包需要使用C++编译后才能够正常安装,但是当前安装环境中缺少完整的C++编译环境,因此安装失败。3.安装Microsoft Visual C++ Build Tool离线安装包(1个多G),CSDN资源很多,需要积分下载,_error: microsoft visual c++ 14.0 or greater is required. get it with "micros
文章浏览阅读1.1w次,点赞3次,收藏7次。Step 3: 在右侧窗口中找到名称为“LongPathsEnabled”的“DWORD (32 位) 值”条目,并双击它。通过注册表方法或组策略方法启用长路径支持后,您将能够在 Windows 中使用长路径,并能够访问和处理长路径下的文件和文件夹。Step 2: 依次选择“计算机配置” > “管理模板” > “系统” > “文件资源管理器”。Step 3: 找到“启用 Win32 长路径”设置,双击它。Step 4: 选择“已启用”选项按钮,然后选择“应用”按钮。_windows长路径支持
文章浏览阅读2.5k次,点赞81次,收藏86次。
文章浏览阅读1.3k次,点赞65次,收藏50次。顺序表,链表,栈,队列,ArrayList,LinkedList,Stack,Queue
文章浏览阅读2.3k次,点赞2次,收藏2次。AnyTXTSearcher是一款能够帮助我们对文档以及文本内容进行快速搜索和管理的工具,通过该软件能够搜索各种Office文档,文本文件,代码,PDF文档等,顶级的全文搜索引擎1秒钟之内即可完成搜索。_anytxt searcher
文章浏览阅读8.8k次,点赞73次,收藏70次。有时,在删除/移动/重命名文件夹/文件时,会遇到如下警告,即使将打开的程序关闭了,后台也可能会有没关干净的相关进程。_解除占用
文章浏览阅读4.3w次,点赞91次,收藏102次。JDK(Java Development Kit)是Java开发工具包的缩写,包含了Java编译器、Java虚拟机、Java类库等众多组件,是Java开发的基石,提供了编写、编译和运行Java程序所必需的工具。同时,为了让系统能够正确识别Java环境,在开始使用JDK进行Java开发之前,需要先把JDK安装到本地计算机,并配置好相应的环境变量。本文将介绍JDK安装与环境变量配置的方法。_windows安装jdk并配置环境变量