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

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

经典面试题收集一

第一章

1. 运算符

  • 运算符&和&&、|和||的区别?
Dart 复制代码
//& 按位与操作,只有对应的两个二进制数为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次方
java 复制代码
//原理:将一个数左移n位,就是将这个数乘以2的n次方
2 << 3 = 16

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

//直接是二进制操作了,表示将1左移4位,变成10000,变成十进制就是16
  • 写个方法,传递两个非0的int数值进去,实现变量交换,有几种方式?
java 复制代码
//方式一
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=%d", a, b);
}

//方式二 异或运算(一个数与另一个数异或两次是其本身,一个数和自身异或结果是0)
public static void swap2(int a, int b) {
    System.out.printf("a=%d,b=%d", 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=%d", a, b);

2. 数据类型

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

3. try-catch-finally

  • 1.下面代码的 try-catch-finally 语句,try里面有个return,finally里面也有一个return,结果会返回什么?为什么?
java 复制代码
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 复制代码
//需要关闭的资源只要实现了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和递归

  • 找出某目录下的所有子目录和子文件,并打印到控制台上
java 复制代码
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"); 创建了几个对象?
Dart 复制代码
//创建一个对象:常量池存在,则直接new一个对象
//创建两个对象:常量池不存在,则在常量池创建一个对象,在堆中也创建一个对象
  • 2.下面是比较什么?输出结果是什么?为什么是这样的结果?
java 复制代码
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,应该怎么修改?
java 复制代码
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的区别?分别在哪些场景下使用?
matlab 复制代码
//三者都是final, 不允许被继承, 本质都是char[]字符数组实现
//String、StringBuffer与StringBuilder中,String是不可变对象,另外两个是可变的

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

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

2. 面向对象

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

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

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

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

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

3. 接口

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

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

第三章(集合框架List)

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

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

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

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

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

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

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

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

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

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

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

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

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

java 复制代码
//计算容量+确保容量
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,0,objects,0,elementData.length);
        //修改引⽤
        elementData = objects;
    }  
}
  • 总结重点,鼓励读者在日常工作中不断深化对 Java 的理解,持续学习和实践。

期望与后续更新

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

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

感谢阅读

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

相关推荐
独自破碎E1 分钟前
Java是怎么实现跨平台的?
java·开发语言
To Be Clean Coder8 分钟前
【Spring源码】从源码倒看Spring用法(二)
java·后端·spring
xdpcxq102927 分钟前
风控场景下超高并发频次计算服务
java·服务器·网络
想用offer打牌30 分钟前
你真的懂Thread.currentThread().interrupt()吗?
java·后端·架构
橘色的狸花猫41 分钟前
简历与岗位要求相似度分析系统
java·nlp
独自破碎E1 小时前
Leetcode1438绝对值不超过限制的最长连续子数组
java·开发语言·算法
Bohemian1 小时前
kafka学习笔记系列——小试牛刀
面试·kafka
用户91743965391 小时前
Elasticsearch Percolate Query使用优化案例-从2000到500ms
java·大数据·elasticsearch
不想秃头的程序员1 小时前
吃透 JS 事件委托:从原理到实战,解锁高性能事件处理方案
前端·面试
yaoxin5211231 小时前
279. Java Stream API - Stream 拼接的两种方式:concat() vs flatMap()
java·开发语言