[Java 面试题] 分享5道面试题

最近有点空,抽了五道面试题研究一下,就顺手在这也记录一下,毕竟有部分还是花了点心血。本人菜鸡一只,可能会存在错漏,若有发现请帮我指正一下,谢谢

一、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.csdn.net/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.csdn.net/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

五、通常一个xml映射文件,都会写一个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|执行结果返回。

posted @ 2021-03-08 16:33  AzureSky_X  阅读(48)  评论(0编辑  收藏  举报