面试-Java
面试-Java
基础
Java八大基本类型
问:Java八大基本类型
答:Byte(1) Short(2) Int(4) Long(8) Float(4) Double(8) Char(2) Boolean(1)
String类中常用的方法
问:String类中常用的方法
答:indexof()从指定字符提取索引位置
replace()替换
subString()截取字符串
equals()比较
split()把字符串分割成字符串数组
为什么要同时重写HashCode和Equals方法
问:为什么要同时重写HashCode和Equals方法
答:官方规定如果两个对象通过equals()方法比较是相等的,那么它们的hashCode()方法必须返回相同的整数值。反之hashCode相同的对象,equals不一定相等。hashCode不同的对象,equals一定不相等。如果只重写一个就会违反这个约定,导致HashMap或HashSet出现重复数据等情况。
String中 == 和equals的区别
问:String中的 == 和equals的区别
答:对于八大基本类型 == 比较的是值,对于引用类型==比较内存地址。
对于引用类型equals默认比较内存地址。
对于String来说 == 比较的是内存地址,equals重写了比较的是内容。
new String("abc")创建了几个对象
问:new String("abc")创建了几个对象
答:如果"abc"这个字符串常量不存在,则创建两个对象。分别是"abc"这个字符串常量以及new String这个实例对象。
如果"abc"这个字符串常量存在,则只会创建new String这个实例对象。
String、StringBuffer、StringBuilder区别
问:String、StringBuffer、StringBuilder区别
答:可变性:String内部的value值是final修饰的,是不可变类,每次修改String值都会产生一个新对象。StringBuffer、StringBuilder是可变类,字符串的变更不会产生新的对象。
线程安全性:String是不可变类,其是线程安全的。StringBuffer每个操作方法都加了synchronized关键字其也是线程安全的。StringBuilder不是线程安全的。
性能方面:String性能最低,因为不可变意味着在做字符串拼接和修改时需要重新创建新对象以及分配内存。其次是StringBuffer因为其加了同步锁所以比StringBuilder性能低。最后是StringBuilder。
存储方面:String存储在字符串常量池里面。StringBuffer、StringBuilder存储在堆内存空间。
遍历Map的方式
问:遍历Map的方式
package com.tensoflow;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
@SuppressWarnings("all")
public class Main {
public static void main(String[] args) throws Exception {
HashMap<String, Integer> map = new HashMap<>();
map.put("苹果", 5);
map.put("香蕉", 3);
map.put("橙子", 7);
// 遍历KeySet的方式
for (String key : map.keySet()) {
System.out.println(key + ":" + map.get(key));
}
// 遍历EntrySet方式
System.out.println("================");
for (Map.Entry<String, Integer> entry : map.entrySet()) {
System.out.println(entry.getKey() + ":" + entry.getValue());
}
// 使用Stream流方式
System.out.println("================");
map.entrySet().stream().forEach(entry -> {
System.out.println(entry.getKey() + ":" + entry.getValue());
});
// 使用迭代器方式
System.out.println("================");
Iterator<Map.Entry<String, Integer>> iterator = map.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, Integer> entry = iterator.next();
System.out.println(entry.getKey() + ":" + entry.getValue());
}
}
}
Collection和Collections区别
问:Collection和Collections区别
答:Collection是最基本的集合接口,其子接口有List和Set。Collections是一个包装类其包含了各种有关集合操作的静态方法(集合排序、搜索、线程安全化等等)。
说说Final关键字
问:说说Final关键字
答:Final是一个安全修饰符,Final修饰的类不能被继承,Final声明的方法不能被重写。Final声明的变量不能被修改。
说五个开发中常见的异常
问:说五个开发中常见的异常
答:IO异常
null异常
SQL异常
数组下标越界异常
文件未找到异常
向上转型和向下转型
问:向上转型和向下转型
答:向上转型:子类对象赋值给父类引用
向下转型:父类引用强制转换为子类类型,需要显示声明
package com.tensoflow;
// 父类:动物
class Animal {
public void eat() {
System.out.println("动物吃东西");
}
}
// 子类:狗(继承Animal)
class Dog extends Animal {
// 重写父类方法
@Override
public void eat() {
System.out.println("狗吃骨头");
}
// 子类特有方法
public void bark() {
System.out.println("狗汪汪叫");
}
}
public class CastingDemo {
public static void main(String[] args) {
// 向上转型:Dog对象赋值给Animal引用
Animal animal = new Dog();
// 执行的是子类Dog重写后的eat方法
animal.eat(); // 输出:狗吃骨头
// 错误:animal是Animal类型,无法访问子类特有的bark方法
// animal.bark();
// 向下转型:先判断类型,再转换
if (animal instanceof Dog) {
Dog dog = (Dog) animal; // 显式强制转换
dog.bark(); // 输出:狗汪汪叫(可以访问子类特有方法)
dog.eat(); // 输出:狗吃骨头
}
}
}
自动装箱和自动拆箱
问:自动装箱和自动拆箱
答:基本数据类型不是对象,很多场景如集合List、Map只能存储对象,因此Java提供了包装类来封装基本类型。自动装箱就是讲基本数据类型自动转换为对应的包装类对象原理是自动调用valueOf()方法。自动拆箱就是讲包装类对象自动转换为对应的基本数据类型原理是自动调用xxxValue()方法。
package com.tensoflow;
public class BoxUnboxDemo {
public static void main(String[] args) {
// 1. 自动装箱:int -> Integer(底层调用 Integer.valueOf(10))
Integer num1 = 10;
// 2. 自动拆箱:Integer -> int(底层调用 num1.intValue())
int num2 = num1;
// 3. 运算中的自动拆箱:包装类参与算术运算时,先拆箱为基本类型再计算
Integer a = 5;
Integer b = 3;
int sum = a + b; // a和b先拆箱为int,再相加,结果为8
System.out.println("sum = " + sum); // 输出:sum = 8
// 4. 集合中的装箱(List只能存对象,基本类型会自动装箱)
java.util.List<Integer> list = new java.util.ArrayList<>();
list.add(100); // 自动装箱:100(int)→ Integer(100)
int val = list.get(0); // 自动拆箱:Integer(100) → 100(int)
System.out.println("val = " + val); // 输出:val = 100
}
}
深拷贝和浅拷贝
问:深拷贝和浅拷贝
答:浅拷贝只是复制某个对象的指针,而不是复制对象本身,两个引用指针指向被复制对象的同一块内存地址。
深拷贝会完全创建一个一模一样的对象,新对象和老对象不共享内存。
什么是重载和重写
问:什么是重载和重写
答:Java里面方法的重写和重载就是指Java的多态。重写就是父类和子类之间的多态,方法名和参数都一样。重载是一个类中方法的多态,方法名相同而参数不同。
接口和抽象类的异同
问:接口和抽象类的异同
答:
相同点:
接口和抽象类都是抽象的,不能直接实例化,只能被子类实现或继承,并且可以包含抽象方法(没有具体实现),接口和抽象类都用于定义一种规范或者协议,描述类应该具备的行为和功能。
不同点:
接口中的方法都是抽象的,不能有具体的实现;而抽象类中可以包含具体的方法实现,也可以包含抽象方法。Java中一个类只能继承一个父类,但是可以实现多个接口。因此,接口支持多继承,而抽象类不支持多继承。接口中的变量默认是public static final类型的常量,不能被修改;抽象类中可以定义实例变量,并且可以有各种访问控制修饰符。接口中不能包含构造方法;抽象类可以包含构造方法,用于被子类调用。接口主要用于实现类之间的解耦,描述类应该具备的行为,强调规范和契约;而抽象类主要用于作为其他类的基类,提取共性的方法和属性,强调类的继承关系和代码复用。
创建对象的几种方法
问:创建对象的几种方法
答:
第一种:使用new关键字
第二种:通过反射,可以根据类名来创建对象。Class.forName("com.example.MyClass");然后使用Class类的newInstance()方法:该方法会调用类的无参构造方法来创建对象,要求类必须有无参构造函数
第三种:使用clone()方法:通过复制一个现有对象来创建一个新对象,要求被复制的类必须实现Cloneable接口,并且重写clone()方法。
package com.tensoflow;
public class test {
public static void main(String[] args) throws Exception {
// 方式一使用new关键字
A a = new A();
System.out.println(a.name);
// 方式二使用反射
Class<?> aClass = Class.forName("com.tensoflow.A");
// 调用无参构造创建实例(JDK9+不推荐)
Object instance = aClass.newInstance();
// 若需要调用类的方法,需向下转型
if (instance instanceof A) {
A aInstance = (A) instance;
System.out.println(aInstance.name);
}
// 方式三使用Clone克隆, A类需要实现CloneAble接口然后重写clone方法
A clone = (A) a.clone();
System.out.println(clone.name);
}
}
package com.tensoflow;
public class A implements Cloneable{
String name = "TenSoFlow";
// 重写 clone() 方法
@Override
public Object clone() throws CloneNotSupportedException {
// 调用父类Object的 clone() 方法,完成浅拷贝
return super.clone();
}
}
:::
什么是反射,能干嘛
问:什么是反射,能干嘛
答:反射就是将类的各个组成部分封装成其它对象。在Java中只要知道类的名字,通过Class.forName方法可以获得到类对象。通过Class类对象就可以获得类的所有信息。比如类中的成员变量和方法。我们可以对类里的这些方法进行执行或者用动态代理模式进行增强。我们常用的Spring和MyBatis框架就是利用Java反射+动态代理的技术编写的。
package com.tensoflow;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* 反射操作 Student 类的完整示例
*/
public class ReflectDemo {
public static void main(String[] args) {
try {
// 获取 Class 对象
Class<?> studentClass = Class.forName("com.tensoflow.Student");
System.out.println("=== 1. 获取类基本信息 ===");
System.out.println("类名:" + studentClass.getName()); // 完整类名
System.out.println("简单类名:" + studentClass.getSimpleName()); // 仅类名
System.out.println("\n=== 2. 创建 Student 实例 ===");
// 调用公有有参构造创建实例
Constructor<?> publicConstructor = studentClass.getConstructor(String.class, int.class);
Object student1 = publicConstructor.newInstance("张三", 20);
System.out.println("有参构造创建的实例:" + student1);
// 调用私有无参构造创建实例(突破访问权限)
Constructor<?> privateConstructor = studentClass.getDeclaredConstructor();
privateConstructor.setAccessible(true); // 关闭访问检查
Object student2 = privateConstructor.newInstance();
System.out.println("私有构造创建的实例:" + student2);
// ===================== 操作字段 =====================
System.out.println("\n=== 3. 操作字段 ===");
// 3.1 访问公有字段 age
Field ageField = studentClass.getField("age");
ageField.set(student2, 18); // 给 student2 的 age 赋值 18
System.out.println("student2 的 age 字段:" + ageField.get(student2)); // 18
// 3.2 访问私有字段 name(突破访问权限)
Field nameField = studentClass.getDeclaredField("name");
nameField.setAccessible(true); // 关闭访问检查
nameField.set(student2, "李四"); // 给 student2 的 name 赋值 李四
System.out.println("student2 的 name 字段:" + nameField.get(student2)); // 李四
// 3.3 访问静态字段 school
Field schoolField = studentClass.getField("school");
System.out.println("静态字段 school 原值:" + schoolField.get(null)); // 北京大学(静态字段传 null)
schoolField.set(null, "清华大学"); // 修改静态字段
System.out.println("静态字段 school 新值:" + schoolField.get(null)); // 清华大学
// ===================== 4. 调用方法(反射方式) =====================
System.out.println("\n=== 4. 调用方法 ===");
// 4.1 调用公有方法 study
Method studyMethod = studentClass.getMethod("study", String.class);
studyMethod.invoke(student1, "Java编程"); // 张三 正在学习 Java编程
// 4.2 调用私有方法 sayHello(突破访问权限)
Method sayHelloMethod = studentClass.getDeclaredMethod("sayHello", String.class);
sayHelloMethod.setAccessible(true); // 关闭访问检查
String result = (String) sayHelloMethod.invoke(student1, "大家好!");
System.out.println("私有方法返回值:" + result); // [张三]:大家好!
// 4.3 调用静态方法 printSchool
Method printSchoolMethod = studentClass.getMethod("printSchool");
printSchoolMethod.invoke(null); // 学校:清华大学(静态方法传 null)
} catch (Exception e) {
// 捕获所有反射相关异常
e.printStackTrace();
}
}
}
package com.tensoflow;
/**
* 普通学生类
*/
public class Student {
// 私有字段
private String name;
// 公有字段
public int age;
// 静态字段
public static String school = "北京大学";
// 无参构造(私有)
private Student() {}
// 有参构造(公有)
public Student(String name, int age) {
this.name = name;
this.age = age;
}
// 私有方法
private String sayHello(String msg) {
return "[" + name + "]:" + msg;
}
// 公有方法
public void study(String course) {
System.out.println(name + " 正在学习 " + course);
}
// 静态方法
public static void printSchool() {
System.out.println("学校:" + school);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Student{name='" + name + "', age=" + age + "}";
}
}
:::
throw和throws的区别
问:throw和throws的区别
答:throw作用在方法内,表示抛出具体异常,由方法体内的语句处理,一定抛出了异常。
throws作用在方法的声明上,表示抛出异常,由调用者来进行异常处理 ,不确定是否会发生异常
集合
ArrayList的底层
问:ArrayList的底层
答:ArrayList是基于数组实现的,是一个动态数组。底层维护了一个Object类型的数组elementData。如果使用无参构造器,则初始elementData容量为0,第一次添加扩容为10,之后如需再次扩容,则扩容为elementData的1.5倍。如果使用指定大小的构造器则初始elementData容量为指定大小,如果需要扩容,则直接扩容为elementData的1.5倍。另外ArrayList是线程不安全的。
HashMap的底层实现原理
问:HashMap的底层实现原理
答:HashMap的底层是数组+链表+红黑树。数组的初始大小是16,每个数组存储着一个链表。存数据时先根据Key的hashcode值计算出hash值,然后用hash值确定在数组中的位置,如果此位置没有东西则直接放入,如果有就会生成链表,把新的放入链表尾部。当取值时,会先根据Key的hashcode值计算出hash值,确定在数组中的位置,再根据equals方法从该位置上的链表中取出value值。如果数组的长度大于64并且链表的长度大于8就会树化成红黑树。数组扩容倍数是2倍。
项目
Jar包和War包的区别
问:Jar包和War包的区别
答:jar包就是别人写好的一些类,然后对这些类进行打包,开发人员可以将这些jar包引入到自己的项目中,使用这些类和属性。 jar包一般放在lib目录下。war包是一个web模块,可以直接运行,一般开发好的网站,打包后部署到tomcat的网站根目录下,然后重启tomcat,这个包就可以自动解压,相当于代码发布。
四种请求方式
问:四种请求方式
GET
POST
PUT
DELETE
各类默认端口号
问:各类默认端口号
tomcat:8080
MySql: 3306
Redis: 6379
Nacos: 8848
Http:80
Https: 443
Http各类状态码
问:Http各类状态码
1xx 信息性状态码:100 Continue:服务器已收到请求的一部分,客户端应继续发送剩余部分。
101 Switching Protocols:服务器已经理解了客户端的请求,并将通过Upgrade消息头通知客户端更改协议。
其他1xx状态码用于协议交互中。
2xx 成功状态码:
200 OK:请求已成功。
201 Created:请求已经被实现,并且创建了新的资源。
204 No Content:服务器成功处理了请求,但没有返回任何内容。
3xx 重定向状态码:
301 Moved Permanently:请求的资源已永久移动到新位置。
302 Found:请求的资源已暂时移动到新位置。
304 Not Modified:客户端发送的请求已经存在且未修改,服务器告诉客户端使用本地缓存版本。
4xx 客户端错误状态码:
400 Bad Request:请求无效。
401 Unauthorized:请求要求用户身份认证。
403 Forbidden:服务器理解请求,但拒绝执行。
404 Not Found:请求的资源未找到。
5xx 服务器错误状态码:
500 Internal Server Error:服务器遇到了一个未知的错误。
502 Bad Gateway:服务器作为网关或代理时收到了无效的响应。
503 Service Unavailable:服务器暂时过载或维护,无法处理请求。
504 Gateway Timeout:服务器作为网关或代理时未及时从上游服务器收到请求。
