String的这些知识你真的知道?

年纪越大越发现知识要系统性的学习,接下来我们聊下面试中经常被问到的String相关的问题。

一. String 是基本数据类型?占多少字节数?

String 是引用数据类型。

   // 下面两种定义方法
   String str = null;
   String str = new String("hello");

不管你怎么定义,String都是引用数据类型。下面我们来列举下java的两种数据类型。

 

 

 

String 全称是 java.lang.String,是java的一个类,是引用数据类型。
通过查看String源码我们知道,String里面是一个char 数组。 而一般一个char字符默认占一个字节。
那么如果字符串里面有中文,具体占几个字节呢?

        String str = "小叶檀";
        char[] chars = str.toCharArray();
        byte[] bytes = str.getBytes();
        System.out.println("字符串转换成字符数组为:"+ Arrays.toString(chars));
        System.out.println("字符串所占字节数:"+Arrays.toString(bytes));
        // 运行结果为
        字符串转换成字符数组为:[小, 叶, 檀]
        字符串所占字节数:[-27, -80, -113, -27, -113, -74, -26, -86, -128]

从运行结果可以知道,中文是占两个字节

二. 为什么说String是不可变的? 为什么会被设计成不可变? 真的不可变?

我们来看看java.lang.String的源码

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /** The value is used for character storage. */
    private final char value[];

    /** Cache the hash code for the string */
    private int hash; // Default to 0
}
  1. 为什么不可变?

String类 是一个被final修饰的类,里面是一个被final修饰的私有的字符数组。我们知道,被final修饰的类不能被继承,被final修饰的变量不能被重新赋值。另外,也没发现对于这个字符数组的setter()和getter()。所以说String是不可变的

    2. 为什么会被设计成不可变?

看String源码我们发现有一个hash属性,上面注释说缓存的String的hashcode值。所以我们联想如果String是可变的,那么是不是每次改变都要重新计算hashcode,这样会很消耗性能。平时我们会使用String来存储数据库连接等关键变量,如果是可变的,那么是不是对于程序来说很不安全,万一被注入了呢?
所以String设计成不可变是在安全和性能上考虑的。

3. 真的不可变?

不是的,我们可以通过反射机制来改变String的值。

    public static void main(String[] args) {
        String str = "hello";
        System.out.println("更新前:"+str);  // hello
        try {
            // 获取到String里面的私有属性value
            Field field = String.class.getDeclaredField("value");
            // 指定属性的访问权限为true
            field.setAccessible(true);
            // 修改值
            char[] value = (char[])field.get(str);
            value[1]='x';
            System.out.println("更新后:"+str); // hxllo
        }catch (NoSuchFieldException ex){
            ex.printStackTrace();
        }catch (IllegalAccessException e){
            e.printStackTrace();
        }
    } 

三. String的equals()和== 区别

equals() 比较对象里面的值,== 比较内存地址。

考虑到String的不可变,每次new就是新建一个对象放到堆里面,这样太耗内存、性能也不好。 为了提高性能和减少内存开销,在实例化字符串常量的时候进行了一些优化。为 了减少在JVM中创建的字符串的数量,字符串类维护了一个字符串常量池。

jdk1.6及以前字符串常量池放在方法区里面。jdk1.7 常量池被挪到堆里面,应该是考虑到这个池子会比较大,方法区太小。

        String s1 = "abc";
        String s2 = "abc";
        System.out.println(s1==s2);  // true

        String s3 = new String("abc");
        String s4 = new String("xyz");
        System.out.println(s1==s3);    // false
        System.out.println(s1==s3.intern());    // true

当我们用双引号直接定义一个字符串时,jvm会先去检查字符串常量池中是否存在,如果不存在会在常量池里面创建一个String 对象,如果存在会直接引用。

intern() 是native方法,返回字符串对象的规范化表示形式,也就是把对象转化成字符串常量。可以联想下,当我们调用intern(),jvm会去字符串常量池查找或创建一个当前字符串对象的引用。

四. String a = new String("hello") 创建了几个对象?

存在两个对象。 当我们使用new的这种方式来创建一个字符串时,首先会在堆创建一个对象,然后去字符串常量池里面去查找,如果找到就引用,没有就在常量池里面创建一个。

五. String 是被哪个加载器加载的?自定义的String可以被加载?

java.lang.String会被顶级类加载器Bootstrap Classloader 加载。

自定义的String不会被加载。安装jvm的双亲委派原则,当加载到一个新类,加载器会默认让它的父类去加载,当父类返回无法加载时,AppClassLoader 才会加载。所以当加载到String时,AppClassLoader默认会把加载权让给其父类ExtClassLoader,
但是ExtClassLoader在jre/lib/ext目录下没有找到String.class类。接着又会把加载权让给其父类BootStrap ClassLoader,
BootStrap在JRE/lib目录的rt.jar找到了String.class,将其加载到内存中。

六. String, Stringbuffer, StringBuilder 的区别

首先它们都是final类,不能被继承。

String 是字符串常量;
Stringbuffer 是 字符串缓冲池,读写都使用了synchronized来实现线程安全;
StringBuilder 效率最快,但是线程不安全

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值