反射
回答面试官问题的时候,可以不用这么一问一答的方式:
比如在讲了什么是反射之后,我们就可以顺手的提一下为什么反射的性能这么低?以及反射的优缺点!
那你既然说了这么多?你知道怎么去用反射去创建一个对象吗?【补】
如果前面在回答的过程中,提到了底层框架用到了反射,那你可以提一下底层框架的哪些地方用到了反射!
什么是反射?
反射就是 Java 在运行期间动态获取类的信息,并操作类的属性、方法和构造方法的机制。
比如可以通过类名获取 Class 对象,再创建对象、调用方法、修改字段。 它的优点是灵活,常用于框架底层,比如 Spring;缺点是性能相对差,并且会破坏封装性。
反射为什么性能这么低?
反射性能低主要是因为它是运行时动态解析和调用,不像普通方法调用那样在编译期就确定好。
普通方法调用可以被 JVM 优化,比如内联;而反射调用需要做权限检查、方法查找、参数封装和类型校验,这些都会带来额外开销。 所以反射更适合框架层使用,不适合在高频业务逻辑里大量调用。
反射的优缺点是什么
反射的优点是灵活性高,可以在运行时动态创建对象、调用方法、访问属性,所以很多框架比如 Spring、MyBatis 底层都会用到反射。
缺点是性能比普通调用低 ,因为需要运行时解析、权限检查和类型校验;另外反射可以访问私有成员,可能会破坏封装性,代码可读性和安全性也会变差。
所以说还是要先去++获取一下对应类的class对象,你都知道有哪些方式吗++?
获取Class对象的三种方式是什么,各自的区别是什么?
获取 Class 对象主要有三种方式:
第一种是:
Class clazz = User.class;
这种方式最安全,编译期 就能检查,一般最常用。
第二种是:
Class clazz = user.getClass();
这种方式必须先有对象,获取的是运行时对象的真实类型。
第三种是:
Class clazz = Class.forName("com.xxx.User");
这种方式通过类的全限定名获取,会触发类加载,常用于配置文件、框架或 JDBC 场景。
所以前面我们获取了class对象的主要目的就是为了在这个地方用,反射的方式来创建一个对象!
如何通过反射创建一个对象
通过反射创建对象,一般是先获取 Class 对象,再获取构造方法,最后调用 newInstance()。【无参构造】
Class<User> clazz = User.class; User user = clazz.getDeclaredConstructor().newInstance();
如果是有参构造,可以这样:
Constructor<User> constructor = clazz.getDeclaredConstructor(String.class, int.class); User user = constructor.newInstance("张三", 18);
现在更推荐用 getDeclaredConstructor().newInstance(),不推荐直接用 clazz.newInstance(),因为后者已经不建议使用了。
既然你都知道用反射去创建一个对象了,++那么怎么用反射来调用私有字段和私有方法呢++?
如何通过反射调用私有字段和私有方法
可以通过 getDeclaredField() 和 getDeclaredMethod() 获取私有成员,然后调用 setAccessible(true) 关闭 Java 的访问检查。
Class<User> clazz = User.class; User user = new User(); Field field = clazz.getDeclaredField("name"); field.setAccessible(true); field.set(user, "张三"); Method method = clazz.getDeclaredMethod("sayHello"); method.setAccessible(true); method.invoke(user);
简单说,getDeclaredXxx() 可以获取本类中声明的私有成员,setAccessible(true) 让反射可以访问私有属性或方法。
你前面提到了底层框架会用反射,那你说说++Spring,mybatis哪些地方用到了反射++?
Spring,mybatis哪些地方用到了反射?
Spring 中反射主要用在 Bean 的创建、属性注入、方法调用 上。
比如 Spring 根据类的 Class 对象,通过反射调用构造方法创建 Bean;然后通过反射给 @Autowired、@Value 标注的字段或方法赋值;AOP、注解解析、生命周期方法调用也会用到反射。
MyBatis 中反射主要用在 对象映射 上,比如把数据库查询结果封装成 Java 对象时,会通过反射创建对象,并调用 setter 方法或直接给字段赋值。 简单说:Spring 用反射管理对象,MyBatis 用反射封装结果。

异常:
既然聊到了异常,请你说说 Error和Exception的区别,以及对应的有哪些异常/错误?
那请你说说你会怎么去进行处理?throw和throws的区别是什么?
Error和Exception的区别
Error 和 Exception 都继承自 Throwable,但含义不一样。
Error 表示程序无法处理的严重问题 ,比如 OutOfMemoryError内存溢出、StackOverflowError栈溢出错误,一般不建议捕获。 Exception 表示程序可以处理的异常情况 ,比如空指针、IO 异常、数据库异常,通常需要通过 try-catch 或抛出处理。
简单说:Error 是系统级严重错误,Exception 是程序级异常。
常见的Error/Exception有哪些?
常见的 Error 有:
OutOfMemoryError:内存不足。 StackOverflowError:栈溢出,常见于递归太深。 NoClassDefFoundError:运行时找不到类。 ClassFormatError:类文件格式错误。
常见的 Exception 有:
NullPointerException:空指针异常。 ArrayIndexOutOfBoundsException:数组越界。 ClassCastException:类型转换异常。 NumberFormatException:数字格式转换异常。 IOException:IO 异常。 SQLException:数据库异常。
简单说:Error 偏 JVM 或系统级问题,Exception 偏代码运行中的可处理问题。

throw和throws的区别
throw 是在方法 内部主动抛出一个具体异常对象。
throw new RuntimeException("参数错误");
throws 是写在方法声明 上,表示这个方法可能会抛出某些异常,让调用者处理。
public void readFile() throws IOException { }
简单说:throw 是真正抛异常,throws 是声明 可能 抛异常。
除了throw抛出的异常,请你聊聊你对於++try-catch-finally的执行顺序的理解?++
try-catch-finally的执行顺序
try-catch-finally 的执行顺序是:先执行 try,如果 try 中出现异常,就执行对应的 catch,最后一定会执行 finally。
如果 try 中没有异常,顺序就是:
try → finally
如果 try 中有异常并被捕获,顺序就是:
try → catch → finally
需要注意:finally 一般都会执行,常用于释放资源,比如关闭流、数据库连接等。只有极端情况,比如 System.exit() 或 JVM 崩溃,finally 才可能不执行。
finally中的代码一定会被执行吗?
不一定。
正常情况下,finally 基本都会执行,比如 try 中有 return、有异常,finally 也会先执行。
但有几种特殊情况不会执行,比如:
System.exit(0);
或者 JVM 崩溃、线程被强制杀死、机器断电等。
所以面试里可以说:finally 通常一定执行,但不是绝对一定执行。
finally 块中如果有 return 语句,会发生什么?
如果 finally 块中有 return,它会覆盖 try 或 catch 中的 return 结果。
比如:
try { return 1; } finally { return 2; }
最终返回的是 2。所以实际开发中不建议在 finally 里写 return,因为它可能会吞掉异常或改变原本的返回结果,导致问题很难排查。
既然你都已经知道异常应该怎么去进行处理了,那++请你说一说不同的异常之间的区别?++
NoClassDefFoundError 和 ClassNotFoundException 的区别?
ClassNotFoundException 是受检异常 ,一般发生在程序通过反射或类加载器主动加载类时,比如:
Class.forName("com.xxx.User");
如果类路径下找不到这个类,就会抛 ClassNotFoundException。
NoClassDefFoundError 是 Error,通常是编译时类存在,但运行时类丢失、依赖缺失,导致 JVM 加载失败。
简单说:ClassNotFoundException 是主动加载找不到类;NoClassDefFoundError 是运行时依赖类缺失。
OutOfMemoryError 和 StackOverflowError 分别是什么原因导致的?
OutOfMemoryError 是堆内存 或方法区等 内存 不足导致的,比如创建了大量对象、集合一直增长、内存泄漏等。
StackOverflowError 是线程栈空间耗尽导致的,最常见原因是递归调用太深或者递归没有出口。
简单说:OOM 是 内存 不够用, StackOverflow 是方法 调用栈 太深。
