前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >某团面试题:String s = new String("111")会创建几个对象?

某团面试题:String s = new String("111")会创建几个对象?

作者头像
java进阶架构师
发布2021-05-08 15:25:07
6360
发布2021-05-08 15:25:07
举报
文章被收录于专栏:Java进阶架构师

★★★建议星标我们★★★

公众号改版后文章乱序推荐,希望你可以点击上方“Java进阶架构师”,点击右上角,将我们设为星标”!这样才不会错过每日进阶架构文章呀。

2020年Java原创面试题库连载中

【000期】Java最全面试题库思维导图

【020期】JavaSE系列面试题汇总(共18篇)

【028期】JavaWeb系列面试题汇总(共10篇)

【042期】JavaEE系列面试题汇总(共13篇)

【049期】数据库系列面试题汇总(共6篇)

【053期】中间件系列面试题汇总(共3篇)

【065期】数据结构与算法面试题汇总(共11篇)

【076期】分布式面试题汇总(共10篇)

【100期】综合面试题系列汇总(共23篇)

【151期】100-150期汇总(共50篇)

【152期】如何应对高并发流量?

【153期】StringBuilder线程安全吗?为什么?

【154期】Redis的过期键删除策略有哪些?

【155期】Spring-Retry重试实现原理是什么?

【156期】数据库分库分表之后,如何解决事务问题?

【157期】为什么 SQL 语句不要过多的 join?

【158期】说说注册中心 zookeeper 和 eureka 中的CP和 AP

【159期】Java中的finally一定会被执行吗?

更多内容,点击上方名片查看

作者:维常 blog.csdn.net/o9109003234/article/details/109523691

String不可变吗?

代码语言:javascript
复制
public class App {
    public static void main(String[] args) {
        String a = "111";
        a = "222";
        System.out.println(a);
    }
}

有的人会认为上面这段代码应该输出:111

这样才和上面的不变性吻合。

哈哈哈,但是并不是这样滴。

222

这不对呀,不是不变吗?怎么变了呢?

其实在JVM的运行中,会单独给一块地分给String。

上面的:

代码语言:javascript
复制
Stirng a="111";

我们知道字符串的分配和其他对象分配一样,是需要消耗高昂的时间和空间的,而且字符串我们使用的非常多。JVM为了提高性能和减少内存的开销,在实例化字符串的时候进行了一些优化:

使用字符串常量池。每当我们创建字符串常量时,JVM会首先检查字符串常量池,如果该字符串已经存在常量池中,那么就直接返回常量池中的实例引用。如果字符串不存在常量池中,就会实例化该字符串并且将其放到常量池中。由于String字符串的不可变性我们可以十分肯定常量池中一定不存在两个相同的字符串。

这里先去JVM给常量池里找,找到了就不用创建对象了,直接把对象的引用地址赋给a。找不到会重新创建一个对象,然后把对象的引用地址赋给a。同理a="222";也是先找,找不到就重新创建一个对象,然后把对象的引用地址赋给a。

搜索公纵号:MarkerHub,关注回复[ vue ]获取前后端入门教程!

大家有没有发现我上面的描述中“引用地址”。比如说 Object obj = new Object();很多人喜欢成obj为对象,其实obj不是对象,他只是一个变量,然后这个变量里保存一个Object对象的引用地址罢了。

引用类型声明的变量是指该变量在内存中实际存储的是一个引用地址,实体在堆中。

所以网上很多文章老喜欢这么说

代码语言:javascript
复制
User user = new User()

创建了一个user对象,老喜欢把user称之为对象。这里不接受反驳。

所以上面String a = “111”;表达的是变量a里保存了“111”这个对象的引用地址。变量是可以变的,不能变的是“111”。

String 为什么是不可变的?

简单的来说,String 类中使用 final 关键字字符数组保存字符串。代码如下:

代码语言:javascript
复制
public final class String implements java.io.Serializable, Comparable<String>, CharSequence {
    private final char value[];
    public String() {
        this.value = "".value;
    }
    public String(String original) {
        this.value = original.value;
        this.hash = original.hash;
    }
    public String(char value[]) {
        this.value = Arrays.copyOf(value, value.length);
    }
}   

从上面的这段源码中可以看出三点:

String 类是final修饰 String存储内容使用的是char数组 char数组是final修饰

这里就得复习一下,final有啥用?

当用final修饰一个类时,表明这个类不能被继承。也就是说,如果一个类你永远不会让他被继承,就可以用final进行修饰。final类中的成员变量可以根据需要设为final,但是要注意final类中的所有成员方法都会被隐式地指定为final方法。

当final修饰的方法表示此方法已经是“最后的、最终的”含义,亦即此方法不能被重写(可以重载多个final修饰的方法)。此处需要注意的一点是:因为重写的前提是子类可以从父类中继承此方法,如果父类中final修饰的方法同时访问控制权限为private,将会导致子类中不能直接继承到此方法,因此,此时可以在子类中定义相同的方法名和参数,此时不再产生重写与final的矛盾,而是在子类中重新定义了新的方法。(注:类的private方法会隐式地被指定为final方法。)

当final修饰一个基本数据类型时,表示该基本数据类型的值一旦在初始化后便不能发生变化。如果final修饰一个引用类型时,则在对其初始化之后便不能再让其指向其他对象了,但该引用所指向的对象的内容是可以发生变化的。本质上是一回事,因为引用的值是一个地址,final要求值,即地址的值不发生变化。另外final修饰一个成员变量(属性),必须要显示初始化。这里有两种初始化方式,(1.在申明的时候给其赋值,否则必须在其类的所有构造方法中都要为其赋值)

比如:

代码语言:javascript
复制
/**
 * Description: final修饰变量
 * @author : 田维常
 * 欢迎关注:java后端技术全栈
 */
public class FinalDemo {
    private final String name;

    public FinalDemo(String name) {
        this.name = name;
    }

    public FinalDemo() {
    }
}

这是会会报错

关于final就简单说到这里

下面来看一个使用String的 案例

代码语言:javascript
复制
/**
 * Description:
 *
 * @author : 田维常
 * @date : 2020/11/3
 * 欢迎关注公众号:java后端技术全栈
 */
public class StringDemo {
    public static void main(String[] args) {
        String name = "老田";
        name.concat("!");
        System.out.println(name);
        System.out.println(name.concat("!"));
    }
}

输出

顺道溜达溜达 String中几个常用方法源码

代码语言:javascript
复制
    public String concat(String str) {
        int otherLen = str.length();
        if (otherLen == 0) {
            //啥都没有,就直接把当前字符串给你
            return this;
        }
        int len = value.length;
        char buf[] = Arrays.copyOf(value, len + otherLen);
        str.getChars(buf, len);
        //看到了吗?返回的居然是新的String对象
        return new String(buf, true);
    }
    void getChars(char dst[], int dstBegin) {
        System.arraycopy(value, 0, dst, dstBegin, value.length);
    }    
    public String replace(char oldChar, char newChar) {
        //如果两个是一样的,那就必要替换了,所以返回this
        if (oldChar != newChar) {
            int len = value.length;
            int i = -1;
            //把当前的char数组复制给val,然后下面基于val来操作
            char[] val = value; 

            while (++i < len) {
                if (val[i] == oldChar) {
                    break;
                }
            }
            if (i < len) {
                //创建一个新的char数组
                char buf[] = new char[len];
                for (int j = 0; j < i; j++) {
                    buf[j] = val[j];
                }
                while (i < len) {
                    char c = val[i];
                    buf[i] = (c == oldChar) ? newChar : c;
                    i++;
                }
                //创建一个新的String对象
                return new String(buf, true);
            }
        }
        return this;
    }

    public String substring(int beginIndex, int endIndex) {
        if (beginIndex < 0) {
            throw new StringIndexOutOfBoundsException(beginIndex);
        }
        if (endIndex > value.length) {
            throw new StringIndexOutOfBoundsException(endIndex);
        }
        int subLen = endIndex - beginIndex;
        if (subLen < 0) {
            throw new StringIndexOutOfBoundsException(subLen);
        }
        //正常返回的都是新new出来的String对象
        return ((beginIndex == 0) && (endIndex == value.length)) ? this
                : new String(value, beginIndex, subLen);
    }

    public String trim() {
        int len = value.length;
        int st = 0;
        char[] val = value;    /* avoid getfield opcode */

        while ((st < len) && (val[st] <= ' ')) {
            st++;
        }
        while ((st < len) && (val[len - 1] <= ' ')) {
            len--;
        }
        //如果是该字符串中包含了空格,调用substring方法,否则就是啥都没干原本返回
        //就是如果字符串里有空格,那么还是新生一个String对象返回
        return ((st > 0) || (len < value.length)) ? substring(st, len) : this;
    }

无论是concat、replace、substring还是trim方法的操作都不是在原有的字符串上进行的,而是重新生成了一个新的字符串对象。也就是说进行这些操作后,最原始的字符串并没有被改变。

得出两个结论:

String对象一旦被创建就是固定不变的了,对String对象的任何改变都不影响到原对象,相关的任何变化性的操作都会生成新的对象。 String对象每次有变化性操作的时候,都会从新new一个String对象(这里指的是有变化的情况)。

回到前面的例子

代码语言:javascript
复制
//String a = "111";相当于
char data [] ={'1','1','1'};
Stirng a = new String(data);
//a = "222";
char data [] ={'2','2','2'};
a = new String(data);

这会变量a里保存的是"222"对应String对象的引用。

继续看下面的代码

代码语言:javascript
复制
public class App {
    public static void main(String[] args) {
        String a = "111";
        String a1 = "111";

        String b = new String("111");

        //对象地址是同一个
        System.out.println(a==a1);
        //对象内容是一样的
        System.out.println(a.equals(a1));
        //对象地址不一样
        System.out.println(a==b);
        //对象内容是一样的
        System.out.println(a.equals(b));
    }
}

输出

代码语言:javascript
复制
true
true
false
true

第一个输出true,说明a和a1两个变量保存的引用地址是同一个。

第二个也输出true,说明a和a1引用地址中内容是一样的。

a和a1放在栈上,存放着对象的引用地址。 new的对象是在堆中。 常量其实是要看jdk版本的。

所以String a = "111"; 在JVM申请内存存放"111"对应的对象,并将对象保存起来。当String a1="1111";的时候,会先去JVM的那块地里寻找是否存在"111",刚好前面保存过,所以找到,然后直接把对象的引用地址给了a1。所以此时的a和a1都保存着同一个引用地址。

接触java后都知道可以new一个对象。所以 String b = new String("111");就是创建一个对象然后把对象引用地址赋给变量b。但是这里有个特殊点,那就是(“111”),这里会先去JVM里的那块地里找找,找到了直接存放引用地址。找不到创建一个对象然后把引用地址给String的有参构造方法里。

所以第三个中输出false,因为a和b所保存的对象引用是不一样的。

最后一个输出true。那是因为两个变量所保存的引用地址中的内容都是“111”.

答案:

如果常量池中存在,则只需创建一个对象,否则需要创建两个对象。

代码语言:javascript
复制
之前,给大家发过三份Java面试宝典,这次新增了一份,目前总共是四份面试宝典,相信在跳槽前一个月按照面试宝典准备准备,基本没大问题。《java面试宝典5.0》(初中级)《350道Java面试题:整理自100+公司》(中高级)《资深java面试宝典-视频版》(资深)《Java[BAT]面试必备》(资深)分别适用于初中级,中高级,资深级工程师的面试复习。内容包含java基础、javaweb、mysql性能优化、JVM、锁、百万并发、消息队列,高性能缓存、反射、Spring全家桶原理、微服务、Zookeeper、数据结构、限流熔断降级等等。获取方式:点“在看”,V信关注上述Java最全面试题库号并回复 【面试】即可领取,更多精彩陆续奉上。
看到这里,证明有所收获必须点个在看支持呀,喵
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-04-25,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 java进阶架构师 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • String不可变吗?
  • 这里就得复习一下,final有啥用?
相关产品与服务
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档