重新认识String

Java常用类——String

1、什么是String

在Java中String类是一个使用非常频繁的类,它不是java八个基本数据类型的其中之一,而是Java提供的在java.lang包下的用于创建和操作字符串的类。本文章通过搜集的几个常见面试题来学习String类的简单使用。

2、String的特点
  • 使用final修饰:不可被继承,并且内部一些方法也被final修饰。
public final class String
  • **不可变性:**String的不可变性主要有两方面决定,首先是String类使用fianl来修饰,决定了String不能被继承,另外一个原因是因为String用于存储字符串值得char行数组value[]也是使用final修饰的。
private final char value[];
  • **常量池优化:**常量池的出现是为了解决对象快速创建问题。当一个String对象创建之后,会在字符串常量池中进行缓存,等到下次创建相同对象的时候将直接返回已缓存对象的引用,从而优化对象的创建过程。
3、String对象的创建方式
  1. 使用常量赋值的形式实例化对象

    String str = "java";
    
  2. 使用构造方法的形式实例化对象

    String str = new String("java");
    

这两种实例化方式到底有什么区别呢?我们先从存放的位置来分析。

两种实例化方式都会在栈中创建变量str。

第一种方式实例化对象,虚拟机首先会在字符串常量池中通过String的equals方法来查找是否存在“java”常量,如果存在,虚拟机会将该常量的引用地址直接复制给str变量,如果不存在,会将字符串常量“java”存放在字符串常量池中,并将其引用地址复制给str变量。

第二种方式创建的对象会在堆中开辟内存空间,这里需要注意的是,jvm并不是将字符串变量存放到了堆内存中,它只是在堆中开辟了一个内存块,用于存放指向内存本身的引用地址。它的字符串常量“java”也会存放到字符串常量池中,所以使用第二种方式创建对象的时候具体流程如下:

虚拟机首先会在字符串常量池中通过String的equals方法来查找是否存在“java”常量,无论常量池中是否存在“java”字符串虚拟机都会在堆空间开辟内存空间存放新对象,如果存在就直接使用,如果不存在,会将字符串常量“java”存放在字符串常量池中,并将其引用地址复制给堆中新开辟的地址。

在这里插入图片描述

总结:

  1. 常量赋值的形式实例化,字符常量内容存于常量池,变量存于栈中,直接指向常量池。
  2. 构造方法实例化,会先在堆中创建实例对象,引用对象存于栈中,然后再去常量 区寻找需要的字符常量,如果找到了,直接使用,没找到则开辟新的空间并存储内容。
4、“==”与equals的区别

在解释上述结果之前需要先明确一个概念,那就是“==”符号到底比较的是什么?这个分为两种情况

  • 基本数据类型

对于基本数据类型来讲,"=="是判断左右两边的值是否相等

  • 引用类型

对于引用类型的变量,"=="是判断左右两边所指向的地址是否一致

//Object的equals方法
public boolean equals(Object obj) {
    return (this == obj);
}

从Object的equals方法源码可知,Object的equals方法其实也是直接比较对象的地址,和“==”没有任何区别。那我们怎么去比较两个对象的内容而不是对象地址是否一致呢?现在就需要我们去重写equals方法,下面以String的equals方法为例。

//String的equals方法
public boolean equals(Object anObject) {
    if (this == anObject) {
        return true;
    }
    if (anObject instanceof String) {
        String anotherString = (String)anObject;
        int n = value.length;
        if (n == anotherString.value.length) {
            char v1[] = value;
            char v2[] = anotherString.value;
            int i = 0;
            while (n-- != 0) {
                if (v1[i] != v2[i])
                    return false;
                i++;
            }
            return true;
        }
    }
    return false;
}

首先我们先来看下String中equals方法源码,从中我们可以看出调用String的equals方法对两个变量进行比较时,首先是判断两个对象的地址是否相等,如果不相等还会对两个string变量进行逐字符比较,如果相同那么就返回true,否则返回false。当然对于我们自定义的类我们也可以根据需求自定义重写他们的equals方法。

5、String常用方法
方法名称描述
public String (char[] value,int offset,int count)将组部分字符数组变为String类
public String (char[] value)将组部分字符数组变为String类
public char cahrAt(int index)返回指定所索引对应的字符信息
public char[] toCharArrary()将字符串以字符数组的形式返回
public boolean equals(String anObject)进行相等判断,区分大小写
Public boolean equalsIgnoreCase(String anotherString)进行相等判断,不区分大小写
public int compareTo(String anoterString)判断两个字符串的大小(按照字符编码比较)
=0:表示要比较的两个字符内容相等
>0:表示大于的结果
<0:表示小于的结果
public boolean contains(String s)判断指定的内容是否内存
public int indexOf(String str)由前向后查找指定字符串的位置,
如果查找到了则返回(第一个字母)位置的索引,
如果找不到返回-1
public int indexOf(String str,int fromIndex)从指定位置由前向后查找指定字符串的位置,找不到返回-1
public boolean starsWith(String perfix)判断是否以指定的字符串开头,如果是返回true,否则返回false
public boolean endsWith(String suffix)判断是否以指定的字符串结尾
public String repaceAll(String regex(正则),String replacement)用新的内容替换掉全的旧的
public String substring(int beginIndex)从指定索引截取到结尾
public String substring(int beginIndex,int endIndex)截取部分字符串的数据
public String split(String regex)按照指定的字符串进行全部拆分
public String concat(String str)追加字符
public String toLowerCase()转小写
public String toUpperCase()转大写
public String trim()去掉字符串左右两边的空格,中间空格保留
public intern()数据入池
public boolean isEmpty()判断是否是空字符串(不是null,而是””长度是0)
6、面试题目解析
  1. 考察String的实例化方式

    String str1 = "java";
    String str2 = "java";
    String str3 = new String("java");
    String str4 = new String("java");
    System.out.println(str1==str2);
    System.out.println(str3==str4);
    System.out.println(str3.equals(str3));
    
    输出结果:true false true
    

    str1与str2是通过赋值的方式进行的实例化,引用地址相同,所以结果为true

    str3与str4是通过构造方法来进行的实例化,引用地址不同,所以结果为false

    第三个结果为true是因为String的equals方法并不是直接比较两者指向的地址,还会判断内容是否相同。

  2. String不可变性

    String str3 = new String("java");
    String str4 = str3;
    System.out.println(str3==str4);
    str3 = "dart";
    System.out.println(str3==str4);
    
    输出结果:true false
    

    因为String对象创建后,它会将字符串值保存在final修饰的一个字符数组里面,所以String是不可变的

    /** The value is used for character storage. */
    private final char value[];
    

    一旦字符串的值发生改变,那么该对象就变成了一个新的对象,其地址也会发生改变,所以最终结果应该为true和false。

  3. String字符串拼接

    String str1 = "java dart";
    String str2 = "java ";
    String str3 = "dart";
    String str4 = str2+str3;
    String str5 = "java "+"dart";
    System.out.println(str1==str4);
    System.out.println(str1==str5);
    
    输出结果:false true
    

    拼接后得到的str4与str5字符串内容是一样的,但是输出结果为什么不同呢?

    str2与str3均为变量,变量与变量进行拼接那么str4就会在堆中开辟内存地址,而str1则是保存在常量池中,所以str4与str5内存地址一致。

    “java ”与“dart”均为常量,拼接后的结果将保存在常量池中,所以str5与str1内存地址一致

  4. final修饰符的使用

    String str1 = "java dart";
    final String str2 = "java ";
    final String str3 = new String("java ");
    String str4 = str2+"dart";
    String str5 = str3+"dart";
    System.out.println(str1==str4);
    System.out.println(str1==str5);
    
    输出结果:true false
    

    这个结果似乎有点意外,为什么3中str1==str4为false,这里就变成了true?因为这里的str2使用final修饰,如果在编译期都可以知道确切值(定义变量的时候就初始化),那么在编译器会将其当做常量使用,所有用到该变量的地方就相当于直接使用该常量,所以常量与常量拼接还将保存在常量池中,而str3在编译期并不知道确切值,所以输出结果为false

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值