目录
最近有需求要整理五道面试题,就顺手在这也记录一下,毕竟有部分还是花了点心血。本人菜鸡一只,可能会存在错漏,若有发现请帮我指正一下,谢谢
一、String是什么类型?String类型值可以被改变吗?为什么?
String属于引用类型。
不可以,因为从源码中,我们可以看到String的本质其实是一个char类型的数组,并且被final修饰。JDK6中, value是String封装的数组,offset是String在这个value数组中的起始位置,count是String所占的字符的个数。而且我们在源码中可以看到这三个值都是是被private修饰的,并且没有提供公共的set方法来让我们访问它,这就代表着我们无法在String外部修改String。并且这三个变量也都被final修饰,一旦初始化就不能修改,也就是说,一旦这三个值在String的内部初始化了就不能改变。而被String被final修饰是个“断子绝孙”类,不能被继承,因此我们可以认为String对象是不可变的。
很多同学试过如下方式更改String类型的值:
/**
*测试
**/
@Test
public void testString() throws Exception{
String a="你好吗?";
a="我很好。";
System.out.println(a);
}
但我们会发现他内部的引用地址变了。而实际上当我们给a重新赋值时,并不是修改了字符串,而是重新创建了一个字符串并将引用地址指向了a。
参考资料:
https://www.zhihu.com/question/20618891
https://blog.youkuaiyun.com/goudu7509/article/details/81088090
二、被final修饰的变量可以修改吗?
如果被修饰的是基本数据类型的变量,它的值一旦初始化后就不能更改
如果被修饰的是引用数据类型的对象,在其初始化后就不能修改他的引用地址,但是能修改地址中所指向的内容
注意
正常情况下,一二问题的回答都是对的,但是总是有“年轻人,不讲武德”,这个年轻人就是反射。通过反射的方式我们是可以修改String的值(莫得代码,请自行尝试),也可以修改被final修饰的变量(如下)
import java.lang.reflect.Field;
import java.util.ArrayList;
/**
* @Author AzureSky_X
* @Date 2021/3/6 15:13
* @Version 1.0
*/
public class FinalTest {
public static void main(String[] args) {
Book book=new Book();
System.out.println("修改前:"+"booksNames--->"+book.getValue()+" || "+"类--->"+book);
try {
//获取这个类的所有属性值
Field nameField;
//获取指定的属性值
nameField = Book.class.getDeclaredField("booksNames");
//设置访问权限为true 暴力反射
nameField.setAccessible(true);
ArrayList<String> other = new ArrayList<>();
other.add("三国演义");
nameField.set(book,other );
System.out.println("修改后:"+"booksNames--->"+book.getValue()+" || "+"类--->"+book);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
}
final class Book {
//被final修饰的数组
private final ArrayList<String> booksNames;
public String getValue() {
return booksNames.toString();
}
public Book() {
booksNames=new ArrayList<>();
booksNames.add("西游记");
}
}
输出结果:
修改前:booksNames--->[西游记] || 类--->Book@2ff4acd0
修改后:booksNames--->[三国演义] || 类--->Book@2ff4acd0
Process finished with exit code 0
参考资料:
https://blog.youkuaiyun.com/i_am_tomato/article/details/46762081
三、解释内存中的栈(stack)、堆(heap)和静态存储区的用法。
答:通常我们定义一个基本数据类型的变量,一个对象的引用,还有就是函数调用的现场保存都使用内存中的栈空间;而通过new关键字和构造器创建的对象放在堆空间;程序中的字面量(literal)如直接书写的100、“hello”和常量都是放在静态存储区中。栈空间操作最快但是也很小,通常大量的对象都是放在堆空间,整个内存包括硬盘上的虚拟内存都可以被当成堆空间来使用。
String str = new String(“hello”);
上面的语句中str放在栈上,用new创建出来的字符串对象放在堆上,而“hello”这个字面量放在静态存储区。
四、Mybatis中#{}和${}的区别是什么?
简易回答:
#{}是预编译处理,${}是字符串替换。
Mybatis在处理#{}时,会将sq|中的#{}替 换为?号,调用PreparedStatement的set方法来赋值;
Mybatis在处理时,就是把{}替换成变量的值。
使用#{}可以有效的防止SQL注入,提高系统安全性。
详细回答:
参考资料:
https://www.cnblogs.com/AzureSky-X/p/14231697.html
五、通常一个Xm1映射文件,都会写一个Dao接口与之对应,请问,这个Dao接口的工作原理是什么? Dao接口里的方法,参数不同时,方法能重载吗?
Dao接口,就是人们常说的Mapper接口,接口的全限名,就是映射文件中的namespace的值,接口的方法名,就是映射文件中MappedStatement的id值,接口方法内的参数,就是传递给sq|的参数。Mapper接口是没有实现类的,当调用接口方法时,接口全限名+方法名拼接字符串作为key值,可唯一定位一 个MappedStatement, 举例: com.mybatis3.mappers.StudentDao.findStudentByld, 可以唯一找到namespace为com.mybatis3.mappers.StudentDao 下面id=findStudentByld的MappedStatement。
Dao接口里的方法,是不能重载的,因为是全限名+方法名的保存和寻找策略。
Dao接口的工作原理是JDK动态代理,Mybatis运行时 会使用JDK动态代理为Dao接口生成代理proxy对象,代理对象proxy会拦截接口方法,转而执行MappedStatement所代表的sq|,然后将sq|执行结果返回。