专业的编程技术博客社区

网站首页 > 博客文章 正文

Java面试问题(一)—— java 基础(java面试问题大全及答案大全)

baijin 2024-11-12 09:38:13 博客文章 4 ℃ 0 评论

在接下来的时间,我会分多篇给大家把Java方方面面的面试题整理出来,这些都是我自己面试遇到的,自己在网上整理出来的精华题,还有就是自己每次在学习每一部分知识点的时候,会作为站在面试官的角度去提问自己的问题。不管是否需要面试,亦或作为知识点的回顾,希望能够对大家有所帮助。

同时后续也会更新大数据方面的面试题,希望大家多多关注。

一、java 基础

JDK 和 JRE 有什么区别?

  • JDK:Java Development Kit 的简称,java 开发工具包,提供了 java 的开发环境和运行环境。
  • JRE:Java Runtime Environment 的简称,java 运行环境,为 java 的运行提供了所需环境。

具体来说 JDK 其实包含了 JRE,同时还包含了编译 java 源码的编译器 javac,还包含了很多 java 程序调试和分析的工具。简单来说:如果你需要运行 java 程序,只需安装 JRE 就可以了,如果你需要编写 java 程序,需要安装 JDK。

== 和 equals 的区别是什么?

== 解读

对于基本类型和引用类型 == 的作用效果是不同的,如下所示:

  • 基本类型:比较的是值是否相同;
  • 引用类型:比较的是引用是否相同;
String x = "string";
String y = "string";
String z = new String("string");
System.out.println(x==y); // true
System.out.println(x==z); // false
System.out.println(x.equals(y)); // true
System.out.println(x.equals(z)); // true

代码解读:因为 x 和 y 指向的是同一个引用,所以 == 也是 true,而 new String()方法则重写开辟了内存空间,所以 == 结果为 false,而 equals 比较的一直是值,所以结果都为 true。

equals 解读
equals 本质上就是 ==,只不过 String 和 Integer 等重写了 equals 方法,把它变成了值比较。看下面的代码就明白了。

首先来看默认情况下 equals 比较一个有相同值的对象,代码如下:

class Cat {
public Cat(String name) {
this.name = name;
}

private String name;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}

Cat c1 = new Cat("王磊");
Cat c2 = new Cat("王磊");
System.out.println(c1.equals(c2)); // false

输出结果出乎我们的意料,竟然是 false?这是怎么回事,看了 equals 源码就知道了,源码如下:

public boolean equals(Object obj) {
return (this == obj);
}

原来 equals 本质上就是 ==。

那问题来了,两个相同值的 String 对象,为什么返回的是 true?代码如下:

String s1 = new String("老王");
String s2 = new String("老王");
System.out.println(s1.equals(s2)); // true

同样的,当我们进入 String 的 equals 方法,找到了答案,代码如下:

public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}

原来是 String 重写了 Object 的 equals 方法,把引用比较改成了值比较。
总结 :== 对于基本类型来说是值比较,对于引用类型来说是比较的是引用;而 equals 默认情况下是引用比较,只是很多类重新了 equals 方法,比如 String、Integer 等把它变成了值比较,所以一般情况下 equals 比较的是值是否相等。

两个对象的 hashCode()相同,则 equals()也一定为 true,对吗?

不对,两个对象的 hashCode()相同,equals()不一定 true。
因为在散列表中,hashCode()相等即两个对象的哈希值相等,然而哈希值相等,并不一定能得出键值对相等。

final 在 java 中有什么作用?

  • final 修饰的类叫最终类,该类不能被继承。
  • final 修饰的方法不能被重写。
  • final 修饰的变量叫常量,常量必须初始化,初始化之后值就不能被修改。

java 中的 Math.round(-1.5) 等于多少?

等于 -1,向上取整最接近的整数。

java 中操作字符串都有哪些类?它们之间有什么区别?

操作字符串的类有:String、StringBuffer、StringBuilder。

String 和 StringBuffer、StringBuilder 的区别在于 String 声明的是不可变的对象,每次操作都会生成新的 String 对象,然后将指针指向新的 String 对象,而 StringBuffer、StringBuilder 可以在原有对象的基础上进行操作,所以在经常改变字符串内容的情况下最好不要使用 String。

StringBuffer 和 StringBuilder 最大的区别在于,StringBuffer 是线程安全的,而 StringBuilder 是非线程安全的,但 StringBuilder 的性能却高于 StringBuffer,所以在单线程环境下推荐使用 StringBuilder,多线程环境下推荐使用 StringBuffer。

String 属于基础的数据类型吗?

String 不属于基础类型,基础类型有 8 种:byte、boolean、char、short、int、float、long、double,而 String 属于对象。

String str="i"与 String str=new String("i")一样吗?

不一样,因为内存的分配方式不一样。String str="i"的方式,java 虚拟机会将其分配到常量池中;而 String str=new String("i") 则会被分到堆内存中。

当用new关键字创建字符串对象时, 不会查询字符串常量池; 当用双引号直接声明字符串对象时, 虚拟机将会查询字符串常量池. 说白了就是: 字符串常量池提供了字符串的复用功能, 除非我们要显式创建新的字符串对象, 否则对同一个字符串虚拟机只会维护一份拷贝。

new String("a") + new String("b") 会创建几个对象?

总共6个。
对象1:new StringBuilder()
对象2:new String("a")
对象3:常量池中的"a"
对象4:new String("b")
对象5:常量池中的"b"
对象6:new String("ab"),通过StringBuilder中的toString()生成的
强调一下,toString()的调用,在字符串常量池中,没有生成"ab"。

new String("ab")到底创建了几个对象?

创建了2个。
第一个是在堆中开辟了一块空间,存放String对象。
第二个是在字符串常量池中放入了一个"ab"

如何将字符串反转?

使用 StringBuilder 或者 stringBuffer 的 reverse() 方法。

String 类的常用方法都有那些?

常见String类的获取功能

length:获取字符串长度;
charAt(int index):获取指定索引位置的字符;
indexOf(int ch):返回指定字符在此字符串中第一次出现处的索引;
substring(int start):从指定位置开始截取字符串,默认到末尾;
substring(int start,int end):从指定位置开始到指定位置结束截取字符串;

常见String类的判断功能

equals(Object obj): 比较字符串的内容是否相同,区分大小写;
contains(String str): 判断字符串中是否包含传递进来的字符串;
startsWith(String str): 判断字符串是否以传递进来的字符串开头;
endsWith(String str): 判断字符串是否以传递进来的字符串结尾;
isEmpty(): 判断字符串的内容是否为空串"";

常见String类的转换功能

byte[] getBytes(): 把字符串转换为字节数组;
char[] toCharArray(): 把字符串转换为字符数组;
String valueOf(char[] chs): 把字符数组转成字符串。valueOf可以将任意类型转为字符串;
toLowerCase(): 把字符串转成小写;
toUpperCase(): 把字符串转成大写;
concat(String str): 把字符串拼接;

常见String类的其他常用功能

replace(char old,char new) 将指定字符进行互换
replace(String old,String new) 将指定字符串进行互换
trim() 去除两端空格
int compareTo(String str) 会对照ASCII 码表 从第一个字母进行减法运算 返回的就是这个减法的结果,如果前面几个字母一样会根据两个字符串的长度进行减法运算返回的就是这个减法的结果,如果连个字符串一摸一样 返回的就是0。

String能被继承吗?

不可以,因为被final修饰,不能有子类。

如何将对象按照指定的字段排序?

使用stream流里面的sorted方法,然后利用Comparator中的comparing方法指定排序字段。默认是升序,降序使用reversed()。

//按年龄排序(Integer类型)
List<StudentInfo> studentsSortName = studentList.stream().sorted(Comparator.comparing(StudentInfo::getAge)).collect(Collectors.toList());

抽象类必须要有抽象方法吗?

不需要,抽象类不一定非要有抽象方法。

示例代码:

abstract class Cat {
public static void sayHi() {
System.out.println("hi~");
}
}

普通类和抽象类有哪些区别?

  • 抽象类不能被实例化;
  • 抽象类可以有抽象方法,只需申明,无须实现;
  • 有抽象方法的类一定是抽象类;
  • 抽象类的子类必须实现抽象类中的所有抽象方法,否则子类仍然是抽象类;
  • 抽象方法不能声明为静态、不能被static、final修饰。

抽象类能使用 final 修饰吗?

不能,定义抽象类就是让其他类继承的,如果定义为 final 该类就不能被继承,这样彼此就会产生矛盾,所以 final 不能修饰抽象类。

接口和抽象类有什么区别?

接口

  • 接口使用interface修饰
  • 接口不能实例化;
  • 类可以实现多个接口
  • java8之前,接口中的方法都是抽象方法,省略了public abstract。
  • java8之后;接口中可以定义静态方法,静态方法必须有方法体,普通方法没有方法体,需要被实现;

抽象类

  • 抽象类使用abstract修饰
  • 抽象类不能被实例化;
  • 抽象类只能单继承
  • 抽象类中可以包含抽象方法和非抽象方法,非抽象方法需要有方法体;
  • 如果一个类继承了抽象类:如果实现了所有的抽象方法,子类可以不是抽象类;如果没有实现所有的抽象方法,子类仍然是抽象类。

总结:
接口主要体现的是一种规范,实现接口的类只能去实现这种规范,但用户通过与统一接口对接实现了规范与实现的分离,极大地降低了模块间的耦合度;
而通过模板方法模式使用抽象类,可以继承某些具体方法,实现了规范并增加了代码的可重用性,而继承抽象方法与钩子方法使实现类可以灵活地扩展抽象模板类,接口并不能有这样的灵活扩展特性,是非常常用而有意义的一种设计模式.

java 中 IO 流分为几种?

按功能来分:输入流(input)、输出流(output)。

按类型来分:字节流和字符流。

字节流和字符流的区别是:

  • 字节流操作的基本单元为字节;字符流操作的基本单元为Unicode码元。
  • 字节流默认不使用缓冲区;字符流使用缓冲区。
  • 字节流通常用于处理二进制数据,实际上它可以处理任意类型的数据,但它不支持直接写入或读取Unicode码元;字符流通常处理文本数据,它支持写入及读取Unicode码元。

BIO、NIO、AIO 有什么区别?

  • BIO:Block IO 同步阻塞式 IO
    它其实就是服务端创建一个ServerSocket, 然后客户端用一个Socket去连接服务端的ServerSocket, ServerSocket接收到一个连接请求就创建一个服务端Socket和线程与客户端Socket进行通讯。然后客户端和服务端就进行阻塞式的通信,客户端发送一个请求,服务端Socket进行处理后返回响应。在响应返回前,客户端处于阻塞等待状态。
    缺点:
    (1)每一个客户端接入,都需要在服务端创建一个线程来服务这个客户端,这样大量客户端来的时候,就会造成服务端的线程数量可能达到了几千甚至几万,可能会造成服务端过载过高,最后崩溃死掉。
    (2)在服务端响应之前,客户端一直处于阻塞等待状态,无法处理其他业务。
  • - NIO:**New IO 同步非阻塞 IO**

NIO是一种同步非阻塞IO, 基于Reactor模型来实现的。客户端和服务器端通过 Channel(通道)通讯,实现了多路复用。
- AIO:Asynchronous IO 是 NIO 的升级,也叫 NIO2,实现了异步非堵塞 IO ,异步 IO 的操作基于事件和回调机制。

Files的常用方法都有哪些?

  • Files.exists():检测文件路径是否存在。Files.createFile():创建文件。Files.createDirectory():创建文件夹。Files.delete():删除一个文件或目录。Files.copy():复制文件。Files.move():移动文件。Files.size():查看文件个数。Files.read():读取文件。Files.write():写入文件。

Object的常用方法都有哪些?

clone方法

保护方法,实现对象的浅复制,只有实现了Cloneable接口才可以调用该方法,否则抛出CloneNotSupportedException异常。

getClass方法

final方法,获得运行时类型。

toString方法

该方法用得比较多,一般子类都有覆盖。

finalize方法

该方法用于释放资源。因为无法确定该方法什么时候被调用,很少使用。

equals方法

该方法是非常重要的一个方法。一般equals和==是不一样的,但是在Object中两者是一样的。子类一般都要重写这个方法。

hashCode方法

该方法用于哈希查找,重写了equals方法一般都要重写hashCode方法。这个方法在一些具有哈希功能的Collection中用到。

一般必须满足obj1.equals(obj2)==true。可以推出obj1.hash- Code()==obj2.hashCode(),但是hashCode相等不一定就满足equals。不过为了提高效率,应该尽量使上面两个条件接近等价。

wait方法

wait方法就是使当前线程等待该对象的锁,当前线程必须是该对象的拥有者,也就是具有该对象的锁。wait()方法一直等待,直到获得锁或者被中断。wait(long timeout)设定一个超时间隔,如果在规定时间内没有获得锁就返回。
调用该方法后当前线程进入睡眠状态,直到以下事件发生。

  • 其他线程调用了该对象的notify方法。
  • 其他线程调用了该对象的notifyAll方法。
  • 其他线程调用了interrupt中断该线程。
  • 时间间隔到了。
    此时该线程就可以被调度了,如果是被中断的话就抛出一个InterruptedException异常。

notify方法

该方法唤醒在该对象上等待的某个线程。

notifyAll方法

该方法唤醒在该对象上等待的所有线程。

&和&&的区别说一下?

  • 当&作为逻辑与的时候:
    &&具有短路功能,而&不具有短路功能,当&运算符两侧的表达式的结果均为真时,整个运算结果才为真。当&&操作符第一个表达式为 false时,结果为 false,并且不再计算第二个表达式。
  • 当&作为按位与的时候:
    a&b是把a和b都转换成二进制数然后再进行与的运算。

什么是 java 序列化?什么情况下需要序列化?

序列化就是一种用来处理对象流的机制。将对象的内容流化,将流化后的对象进行网络传输。例如:

  1. 序列化一个对象,然后通过HTTP在客户端和服务器之间传递该对象,然后在另一端,反序列化将从流中构建成对象。
  2. 在程序运行过程中,产生对象,但是这些对象随着程序的停止而消失,有时候我们会需要将对象持久化进行序列化,然后保存在磁盘上。

Java的序列化是通过实现serializable接口,该接口没有需要实现的方法,implement Serializable只是为了标注该对象是可被序列化的

底层实现:使用一个输出流(FileOutputStream)来构造一个ObjectOutputStream对象,接着使用ObjectOutputStream对象的writeObejct(Object object)方法就可以将obj对象写到磁盘,需要恢复的时候使用输入流。

为什么要使用克隆?如何实现对象克隆?深拷贝和浅拷贝区别是什么?

什么要使用克隆

想对一个对象进行复制,又想保留原有的对象进行接下来的操作,这个时候就需要克隆了。

如何实现对象克隆

实现Cloneable接口,重写clone方法;
实现Serializable接口,通过对象的序列化和反序列化实现克隆,可以实现真正的深克隆。
BeanUtils,apache和Spring都提供了bean工具,只是这都是浅克隆。hutool的ObjectUtil.clone()实现了深克隆,前提是对象必须实现Serializable接口

深拷贝和浅拷贝区别是什么

浅拷贝:仅仅克隆基本类型变量,不克隆引用类型变量;
深克隆:既克隆基本类型变量,又克隆引用类型变量;

throw 和 throws 的区别?

throw

作用在方法内,表示抛出具体异常,由方法体内的语句处理;
一定抛出了异常;

throws

作用在方法的声明上,表示抛出异常,由调用者来进行异常处理;
可能出现异常,不一定会发生异常;

final、finally、finalize 有什么区别?

  • final可以修饰类,变量,方法,修饰的类不能被继承,修饰的变量不能重新赋值,修饰的方法不能被重写。
  • finally用于抛异常,finally代码块内语句无论是否发生异常,都会在执行finally,常用于一些流的关闭。
  • finalize方法用于垃圾回收。

一般情况下不需要我们实现finalize,当对象被回收的时候需要释放一些资源,比如socket链接,在对象初始化时创建,整个生命周期内有效,那么需要实现finalize方法,关闭这个链接。

但是当调用finalize方法后,并不意味着gc会立即回收该对象,所以有可能真正调用的时候,对象又不需要回收了,然后到了真正要回收的时候,因为之前调用过一次,这次又不会调用了,产生问题。所以,不推荐使用finalize方法。

try-catch-finally 中,如果 catch 中 return 了,finally 还会执行吗?

执行的顺序是:catch->finally->catch中的return。

常见的异常类有哪些?

  1. NullPointerException:空指针异常;
  2. SQLException:数据库相关的异常;
  3. IndexOutOfBoundsException:数组下角标越界异常;
  4. FileNotFoundException:打开文件失败时抛出;
  5. IOException:当发生某种IO异常时抛出;
  6. ClassCastException:当试图将对象强制转换为不是实例的子类时,抛出此异常;
  7. NoSuchMethodException:无法找到某一方法时,抛出;
  8. ArrayStoreException:试图将错误类型的对象存储到一个对象数组时抛出的异常;
  9. NumberFormatException:当试图将字符串转换成数字时,失败了,抛出;
  10. IllegalArgumentException:抛出的异常表明向方法传递了一个不合法或不正确的参数。
  11. ArithmeticException:当出现异常的运算条件时,抛出此异常。例如,一个整数“除以零”时,抛出此类的一个实例。

hashcode是什么?有什么作用?

Java中Object有一个方法,名字就叫hashcode(),它的主要作用是返回该对象的哈希码值。

java 中都有哪些引用类型?

强引用

普通变量赋值即为强引用,如 A a = new A();
回收特点:
只要强引用存在,垃圾回收器将永远不会回收被引用的对象。如果想被回收,可以将对象置为null;

软引用(SoftReference)

SoftReference a = new SoftReference(new A());引用和对象通过SoftReference建立关联
回收特点:
内存不够就回收,内存充足不回收,适合做缓存。

弱引用(WeakReference)

WeakReference a = new WeakReference(new A());
回收特点:
只要发生GC(垃圾回收),一定被回收。

虚引用(PhantomReference)

PhantomReference a = new PhantomReference(new A(), referenceQueue);
它是最弱的引用关系。无法通过虚引用来取得一个对象实例

总结

  1. 强引用,平时在编写代码时会经常使用,其他三种类型的引用,使用得最多就是软引用和弱引用,他们都来描述非必须对象。
  2. 被软引用关联的对象只有在内存不足时才会被回收,而被弱引用关联的对象在JVM进行垃圾回收时总会被回收。
  3. Java中4种引用的级别由高到低依次为:强引用 > 软引用 > 弱引用 > 虚引用。

在 Java 中,为什么不允许从静态方法中访问非静态变量?

  1. 静态变量属于类本身,在类加载的时候就会分配内存,可以通过类名直接访问;
  2. 非静态变量属于类的对象,只有在类的对象产生时,才会分配内存,通过类的实例去访问;
  3. 静态方法也属于类本身,但是此时没有类的实例,内存中没有非静态变量,所以无法调用。

总结:就是静态方法,静态变量是在类加载的时候就已经分配了内存,而非静态变量只有在类对象产生的时候,才会分配内存,两个产生的时间并不同步,在静态方法加载的时候内存中并没有非静态变量,所以无法调用。

在 Java 中,什么时候用重载,什么时候用重写?

  • 重载是多态的集中体现,在类中,要以统一的方式处理不同类型数据的时候,可以用重载。
  • 重写的使用是建立在继承关系上的,子类在继承父类的基础上,增加新的功能,可以用重写。

实例化对象有哪几种方式

  • new
  • clone()
  • 通过反射机制创建
//用 Class.forName方法获取类,在调用类的newinstance()方法
Class<?> cls = Class.forName("com.dao.User");
User u = (User)cls.newInstance();
  • 序列化反序列化
//将一个对象实例化后,进行序列化,再反序列化,也可以获得一个对象(远程通信的场景下使用)
ObjectOutputStream out = new ObjectOutputStream (new FileOutputStream("D:/data.txt"));
//序列化对象
out.writeObject(user1); 
out.close();
//反序列化对象
ObjectInputStream in = new ObjectInputStream(new FileInputStream("D:/data.txt"));
User user2 = (User) in.readObject();
System.out.println("反序列化user:" + user2);
in.close();

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表