【Java面试】来讲一讲你对String的理解

简介: 【Java面试】来讲一讲你对String的理解

String必看文章

字符型常量和字符串常量的区别

  1. 形式上: 字符常量是单引号引起的一个字符 字符串常量是双引号引起的若干个字符
  2. 含义上: 字符常量相当于一个整形值(ASCII值),可以参加表达式运算 字符串常量代表一个地址值(该字
    符串在内存中存放位置)
  3. 占内存大小 字符常量只占一个字节 字符串常量占若干个字节(至少一个字符结束标志)

什么是字符串常量池

字符串常量池位于堆内存中,专门用来存储字符串常量,可以提高内存的使用率,避免开辟多块空间存储相同的字符串,在创建字符串时 JVM 会首先检查字符串常量池,如果该字符串已经存在池中,则返回它的引用,如果不存在,则实例化一个字符串放到池中,并返回其引用。

String 是最基本的数据类型吗

不是。Java 中的基本数据类型只有 8 个 :byte、short、int、long、float、 double、char、boolean;除了基本类型(primitive type),剩下的都是引用类型(referencetype),Java 5 以后引入的枚举类型也算是一种比较特殊的引用类型。

Java 的 8 种基本数据类型中不包括 String,基本数据类型中用来描述文本数据的是 char,但是它只能表示单个字符,比如 ‘a’,‘好’ 之类的,如果要描述一段文本,就需要用多个char 类型的变量,也就是一个 char 类型数组,比如“你好” 就是长度为2的数组 char[] chars = {‘你’,‘好’};

但是使用数组过于麻烦,所以就有了 String,String 底层就是一个 char 类型的数组,只是使用的时候开发者不需要直接操作底层数组,用更加简便的方式即可完成对字符串的使用。

String有哪些特性

  • 不变性:String 是只读字符串,是一个典型的 immutable (不变的)对象,对它进行任何操作,其实都是创建一个新的对象,再把引用指向该对象。不变模式的主要作用在于当一个对象需要被多线程共享并频繁访问时,可以保证数据的一致性。
  • 常量池优化:String 对象创建之后,会在字符串常量池中进行缓存,如果下次创建同样的对象时,会直接返回缓存的引用。
  • final:使用 final 来定义 String 类,表示 String 类不能被继承,提高了系统的安全性。

String为什么是不可变的?

简单来说就是String类利用了final修饰的char类型数组存储字符,源码如下图所以:

我们可以看到,String类字符其实就是byte数组对象的二次封装,存储变量value[]是被final修饰的,所以一个String对象创建以后是无法被改变值的,这点跟包装类是一样的。

String真的是不可变的吗?

我觉得如果别人问这个问题的话,回答不可变就可以了。 下面只是给大家看两个有代表性的例子:

  1. String不可变但不代表引用不可以变
String str = "Hello";
 str = str + " World";
 System.out.println("str=" + str);**

结果:

str=Hello World

解析:

实际上,原来String的内容是不变的,只是str由原来指向"Hello"的内存地址转为指向"Hello World"的内存地址而已,也就是说多开辟了一块内存区域给"Hello World"字符串。

  1. 通过反射是可以修改所谓的“不可变”对象
// 创建字符串"Hello World", 并赋给引用s
  String s = "Hello World";
    System.out.println("s = " + s); // Hello World
    // 获取String类中的value字段
    Field valueFieldOfString = String.class.getDeclaredField("value");
    // 改变value属性的访问权限
    valueFieldOfString.setAccessible(true);
  // 获取s对象上的value属性的值
  char[] value = (char[]) valueFieldOfString.get(s);
  // 改变value所引用的数组中的第5个字符
  value[5] = '_';
  System.out.println("s = " + s); // Hello_World

结果:

s = Hello World
s = Hello_World

解析:

用反射可以访问私有成员, 然后反射出String对象中的value属性, 进而改变通过获得的value引用改变数组的结构。但是一般我们不会这么做,这里只是简单提一下有这个东西。

String不可变的必要性

String之所以被设计为不可变的,目的是为了效率和安全性:

  • 效率:
    String不可变是字符串常量池实现的必要条件,通过常量池可以避免了过多的创建String对象,节省堆空间。
    String的包含了自身的HashCode,不可变保证了对象HashCode的唯一性,避免了反复计算。
  • 安全性:
    String被许多Java类用来当参数,如果字符串可变,那么会引起各种严重错误和安全漏洞。
    再者String作为核心类,很多的内部方法的实现都是本地调用的,即调用操作系统本地API,其和操作系统交流频繁,假如这个类被继承重写的话,难免会是操作系统造成巨大的隐患。
    最后字符串的不可变性使得同一字符串实例被多个线程共享,所以保障了多线程的安全性。而且类加载器要用到字符串,不可变性提供了安全性,以便正确的类被加载。

是否可以继承 String 类

  • String 类是 final 类,不可以被继承。
  • String str="i"与 String str=new String(“i”)一样吗?
    不一样,因为内存的分配方式不一样。
    String str="i"的方式,java 虚拟机会将其分配到常量池中;
    而 String str=new String(“i”) 则会被分到堆内存中。
  • String s = new String(“xyz”);创建了几个字符串对象?
    两个对象,一个是静态区的"xyz",一个是用new创建在堆上的对象。
    对于这个问题,看下面的代码就懂了。
1 String str1 = "hello"; //str1指向静态区
2 String str2 = new String("hello"); //str2指向堆上的对象
3 String str3 = "hello";
4 String str4 = new String("hello");
5 System.out.println(str1.equals(str2)); //true
6 System.out.println(str2.equals(str4)); //true
7 System.out.println(str1 == str3); //true
8 System.out.println(str1 == str2); //false
9 System.out.println(str2 == str4); //false
10 System.out.println(str2 == "hello"); //false
11 str2 = str1;
12 System.out.println(str2 == "hello"); //true

== 只能单纯的比较地址,而equals会在地址不同的时候比较内容。

数组有没有 length()方法?String 有没有 length()方法

数组没有 length()方法 ,有 length 的属性。String 有 length()方法。 JavaScript中,获得字符串的长度是通过 length 属性得到的,这一点容易和 Java 混淆。

String 类的常用方法都有那些?

indexOf():返回指定字符的索引。

charAt():返回指定索引处的字符。

replace():字符串替换。

trim():去除字符串两端空白。

split():分割字符串,返回一个分割后的字符串数组。

getBytes():返回字符串的 byte 类型数组。

length():返回字符串长度。

toLowerCase():将字符串转成小写字母。

toUpperCase():将字符串转成大写字符。

substring():截取字符串。

equals():字符串比较。

在使用 HashMap 的时候,用 String 做 key 有什么好处?

HashMap 内部实现是通过 key 的 hashcode 来确定 value 的存储位置,因为字符串是不可变的,所以当创建字符串时,它的 hashcode 被缓存下来,不需要再次计算,所以相比于其他对象更快。

如果不理解为什么,看下面的图片就懂了

3

String和StringBuffer、StringBuilder的区别是什么?

  • 可变性
    String类中使用字符数组保存字符串,private final char value[],所以 string对象是不可变的。
    StringBuilder与StringBuffer都继承自AbstractStringBuilder类,在AbstractStringBuilder中也是使用字符数组保存字符串,char[] value,这两种对象都是可变的。
  • 线程安全性
    String中的对象是不可变的,也就可以理解为常量,线程安全。
    AbstractStringBuilder是StringBuilder与StringBuffer的公共父类,定义了一些字符串的基本操作,如expandCapacity、append、insert、indexOf等公共方法。
    StringBuffer对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。
    StringBuilder并没有对方法进行加同步锁,所以是非线程安全
    的。
  • 性能
    每次对String 类型进行改变的时候,都会生成一个新的String对象,然后将指针指向新的String 对象。
    StringBuffer每次都会对StringBuffer对象本身进行操作,而不是生成新的对象并改变对象引用。
    相同情况下使用StirngBuilder 相比使用StringBuffer 仅能获得10%~15% 左右的性能提升,但却要冒多线程不安全的风险。
  • 对于三者使用的总结
    如果要操作少量的数据用 = String
    单线程操作字符串缓冲区下操作大量数据 = StringBuilder
    多线程操作字符串缓冲区下操作大量数据 = StringBuffer

学习更多

String详解


相关文章
|
19天前
|
缓存 Java 关系型数据库
【Java面试题汇总】ElasticSearch篇(2023版)
倒排索引、MySQL和ES一致性、ES近实时、ES集群的节点、分片、搭建、脑裂、调优。
【Java面试题汇总】ElasticSearch篇(2023版)
|
8天前
|
Java 索引
java基础(13)String类
本文介绍了Java中String类的多种操作方法,包括字符串拼接、获取长度、去除空格、替换、截取、分割、比较和查找字符等。
21 0
java基础(13)String类
|
19天前
|
设计模式 Java 关系型数据库
【Java笔记+踩坑汇总】Java基础+JavaWeb+SSM+SpringBoot+SpringCloud+瑞吉外卖/谷粒商城/学成在线+设计模式+面试题汇总+性能调优/架构设计+源码解析
本文是“Java学习路线”专栏的导航文章,目标是为Java初学者和初中高级工程师提供一套完整的Java学习路线。
174 37
|
19天前
|
设计模式 安全 算法
【Java面试题汇总】设计模式篇(2023版)
谈谈你对设计模式的理解、七大原则、单例模式、工厂模式、代理模式、模板模式、观察者模式、JDK中用到的设计模式、Spring中用到的设计模式
【Java面试题汇总】设计模式篇(2023版)
|
19天前
|
存储 关系型数据库 MySQL
【Java面试题汇总】MySQL数据库篇(2023版)
聚簇索引和非聚簇索引、索引的底层数据结构、B树和B+树、MySQL为什么不用红黑树而用B+树、数据库引擎有哪些、InnoDB的MVCC、乐观锁和悲观锁、ACID、事务隔离级别、MySQL主从同步、MySQL调优
【Java面试题汇总】MySQL数据库篇(2023版)
|
19天前
|
存储 缓存 NoSQL
【Java面试题汇总】Redis篇(2023版)
Redis的数据类型、zset底层实现、持久化策略、分布式锁、缓存穿透、击穿、雪崩的区别、双写一致性、主从同步机制、单线程架构、高可用、缓存淘汰策略、Redis事务是否满足ACID、如何排查Redis中的慢查询
【Java面试题汇总】Redis篇(2023版)
|
19天前
|
缓存 前端开发 Java
【Java面试题汇总】Spring,SpringBoot,SpringMVC,Mybatis,JavaWeb篇(2023版)
Soring Boot的起步依赖、启动流程、自动装配、常用的注解、Spring MVC的执行流程、对MVC的理解、RestFull风格、为什么service层要写接口、MyBatis的缓存机制、$和#有什么区别、resultType和resultMap区别、cookie和session的区别是什么?session的工作原理
【Java面试题汇总】Spring,SpringBoot,SpringMVC,Mybatis,JavaWeb篇(2023版)
|
19天前
|
缓存 Java 数据库
【Java面试题汇总】Spring篇(2023版)
IoC、DI、aop、事务、为什么不建议@Transactional、事务传播级别、@Autowired和@Resource注解的区别、BeanFactory和FactoryBean的区别、Bean的作用域,以及默认的作用域、Bean的生命周期、循环依赖、三级缓存、
【Java面试题汇总】Spring篇(2023版)
|
19天前
|
存储 安全 Java
Java——String类详解
String 是 Java 中的一个类,用于表示字符串,属于引用数据类型。字符串可以通过多种方式定义,如直接赋值、创建对象、传入 char 或 byte 类型数组。直接赋值会将字符串存储在串池中,复用相同的字符串以节省内存。String 类提供了丰富的方法,如比较(equals() 和 compareTo())、查找(charAt() 和 indexOf())、转换(valueOf() 和 format())、拆分(split())和截取(substring())。此外,还介绍了 StringBuilder 和 StringJoiner 类,前者用于高效拼接字符串,后者用于按指定格式拼接字符串
19 1
Java——String类详解
|
15天前
|
安全 Java
Java StringBuffer 和 StringBuilder 类详解
在 Java 中,`StringBuffer` 和 `StringBuilder` 用于操作可变字符串,支持拼接、插入、删除等功能。两者的主要区别在于线程安全性和性能:`StringBuffer` 线程安全但较慢,适用于多线程环境;`StringBuilder` 非线程安全但更快,适合单线程环境。选择合适的类取决于具体的应用场景和性能需求。通常,在不需要线程安全的情况下,推荐使用 `StringBuilder` 以获得更好的性能。
下一篇
无影云桌面