StringBuilder,Stringbuffer和String相关面试笔试

✌🏼一.StringBuilder和StringBuffer介绍

由于String的不可更改特性,在我们想要改变字符串的时候,都是在new的对象上进行改变,并没有改变字符串本身,为了能在字符串本身上进行的修改,不用创建大量临时对象,Java中提供StringBuilderStringBuffer

  1. 先来看看Stringbuffer的源码实现,以及栈和堆的内存分配:
    在这里插入图片描述
    在这里插入图片描述
    可以看到stringBuffer一直都在内部进行操作,而不是在常量池里面,所以只会返回内部的内容,新加入的内容都会进入内部和常量池中,从而不需要创建大量临时对象来接收新的内容

  2. 我们再看一看Stringbuilder的源码实现,它的栈和堆的内存分配和Stringbuffer是一样的
    在这里插入图片描述
    我们可以发现在StringBuffer的源码实现比StringBuilder实现里面多了synchronized,synchronized代表线程安全,StringBuffer是线程安全的,StringBuilder不是线程安全的

线程安全是啥呢,线程安全指的是内存的安全在每个进程的内存空间中都会有一块特殊的公共区域,通常称为堆(内存)。进程内的所有线程都可以访问到该区域,这就是造成问题的潜在原因。

所以线程安全指的是,在堆内存中的数据由于可以被任何线程访问到,在没有限制的情况下存在被意外修改的风险。即堆内存空间在没有保护机制的情况下,对多线程来说是不安全的地方,因为你放进去的数据,可能被别的线程“破坏”。

在一段函数执行的过程中,具有线程安全的代码不会被外界所影响,相当于把这段函数锁在一个空间里,当函数执行完毕之后,锁就开了
在这里插入图片描述
例如人们上厕所,当第一个人进去上厕所之后,后面的人排队上厕所,当你不锁门的情况下,后面的人就可能会进来,干扰到你,但是当你反锁了门锁之后,后面的人就不能随意进出了,就对你不会有影响,所以在这里的门锁就和我们的线程安全类似,当你具备线程安全后就不用担心被别的线程所干扰了

但是我们这里就会产生一个疑问了,既然StringBuffer比StringBuilder安全,那为什么还需要StringBuilder呢?
线程安全会频繁的加锁和释放锁会消耗系统的资源,而在有些情况下,是不需要线程安全的,所以这时候就用StringBuilder更好,总之得看情况而定

举例我们字符串的拼接

String str = "abc";
str = str + "123";
System.out.println(str);

在这里插入图片描述

在字节码文件里我们可以看到new了一个StringBuilder对象,字符串的拼接也用append的方法来写的,并且调用优化了toString方法
相同情况下,多次拼接就会多次new StringBuilder对象,使得内部执行异常繁琐,多个对象是的程序运行不能最大化执行代码,我们就必须要使用StringBuilder来优化了

String str = "abc";
for (int i = 0; i < 10; i++) {
	str = str +i;
}
System.out.println(str);

优化后的代码

String str = "abc";
StringBuilder sb = new StringBuilder(str);
for (int i = 0; i < 10; i++) {
	sb.append(i);
}
System.out.println(sb.toString());

优化后的代码就只有一个StringBuilder对象,极大的提升了代码的性能,有人会说为什么不用Stringbuffer,在这里没有涉及到线程,用了会浪费资源,所以这两种方法各有优势,使用得看情况而论

🤞🏼 二.相关面试

  1. String、StringBuffer、StringBuilder的区别
    ①String的内容不可修改,StringBuffer与StringBuilder的内容可以修改
    ②StringBuffer与StringBuilder大部分功能是相似的
    ③StringBuffer采用同步处理,属于线程安全操作;而StringBuilder未采用同步处理,属于线程不安全操作

  2. 以下总共创建了多少个String对象【前提不考虑常量池之前是否存在】

String str = new String("ab");

String str1 = new String("a") + new String("b");

第一个str创建了两个对象,第一个对象是字符串"ab",属于常量池里面的,第二个对象是new了一个对象,里面有value和hash
在这里插入图片描述

第二个str创建了六个对象,第一个对象是字符串"a",第二个对象是字符串"b",第三个对象是a new的一个对象,里面有value和hash,第四个对象是b new的一个对象,里面有value和hash,第五个对象是第三第四个对象合起来创建的一个Stringbuilder对象,第六个对象就是Stringbuilder的toString方法生成的一个String对象
在这里插入图片描述

  1. 请解释String类中两种对象实例化的区别

JDK1.8中

  1. String str = “hello”;

只会开辟一块堆内存空间,保存在字符串常量池中,然后str共享常量池中的String对象
在这里插入图片描述

  1. String str = new String(“hello”);

会开辟两块堆内存空间,字符串"hello"保存在字符串常量池中,然后用常量池中的String对象给新开辟的String对象赋值。
在这里插入图片描述

  1. String str = new String(new char[]{‘h’, ‘e’, ‘l’, ‘l’, ‘o’});

现在在堆上创建一个String对象,然后利用copyof将重新开辟数组空间,将参数字符串数组中内容拷贝到String对象中(常量池没有东西,只有双引号的内容才会在常量池里面)
在这里插入图片描述

🤙🏼三.String类oj笔试

  1. 字符串中的第一个唯一字符

给定一个字符串s ,找到它的第一个不重复的字符,并返回它的索引 。如果不存在,则返回-1 。
在这里插入图片描述

思路:

  1. 先根据题目要求列式,字符串为空和长度为0的情况下,返回-1
  2. 因为我们想知道这个字符是不是只出现了一次,所以我们得遍历一次统计出每个字符出现的个数
  3. 再次遍历,从左往右遍历的时候输出统计后第一个只出现了一次的字符
public int firstUniqChar(String s) {
        //对参数进行判断
        if(s == null || s.length() == 0){
            return -1;
        }

        int[] array = new int[26];//0

        for(int i = 0;i < s.length();i++){//先遍历一次数组,把每个元素出现的次数都记录下来
            char ch = s.charAt(i);
            array[ch-'a']++;
        }

        for(int i = 0;i < s.length();i++){//再次遍历数组,从左往右,输出记录后第一个只出现了一次的字符
            char ch = s.charAt(i);
            if(array[ch-'a'] == 1){
                return i;
            }
        }
        return -1;
    }
  1. 字符串最后一个单词的长度

计算字符串最后一个单词的长度,单词以空格隔开,字符串长度小于5000。(注:字符串末尾不以空格为结尾)
在这里插入图片描述

思路:

  1. 我们先输入一串字符串
  2. 合理利用字符串截取里面的库函数,从后往前找到第一个空格,然后返回第一个空格后面的一串字符,在这里需要提醒的是,用下角标找到第一个空格的时候,下角标需要往后挪一位,空格位不算字符
 public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        while(scan.hasNextLine()){
            String str = scan.nextLine();
            int index = str.lastIndexOf(" ");
            String ret = str.substring(index+1);
            System.out.println(ret.length());
        }

    }
  1. 验证回文串

如果在将所有大写字符转换为小写字符、并移除所有非字母数字字符之后,短语正着读和反着读都一样。则可以认为该短语是一个 回文串 。
字母和数字都属于字母数字字符。
给你一个字符串 s,如果它是回文串 ,返回 true ;否则,返回 false 。
在这里插入图片描述

思路:

  1. 先按照题目要求规范字母大小写,都换成小写
  2. 定义最左边和最右边的元素下角标,为了依次进行向中心挪动对比做准备
  3. 考虑下标合法性以及字符合理性:左边下角标要小于右边下角标,i下标或者j下标所对应的是数字或字母,
  4. 判断对应i和j下标的元素是否相同
private boolean isCharacter(char ch){
        if (ch >= 'a' && ch <= 'z' || ch >= '0' && ch <= '9'){//判断字符合理性
            return true;
        }
        return false;
    }

    public boolean isPalindrome(String s){
        //1.把所有的字符变成小写的
        s = s.toLowerCase();
        int i = 0;
        int j = s.length()-1;
        while(i < j){//i和j的下标要合法
            while(i < j && !isCharacter(s.charAt(i))){//此处不用if是因为在第一个字母或数字之前可能会有空格
                i++;//i下标就是一个合法的数字字符或字母
            }
            while(i < j && !isCharacter(s.charAt(j))){//判断j是不是合法的数字字符或字母
                j--;//j下标就是一个合法的数字字符或字母
            }
            if(s.charAt(i) != s.charAt(j)){//当两边字母或数字不一样的时候,返回false
                 return false;
            }else {
                i++;
                j--;
            }
        }
        return true;
    }
  • 36
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 65
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 65
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Redamancy丶早晚

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值