> Java > java지도 시간 > 2w 단어 자세한 설명 문자열, yyds

2w 단어 자세한 설명 문자열, yyds

풀어 주다: 2023-08-24 15:56:33
앞으로
1086명이 탐색했습니다.


머리말

안녕하세요 여러분, 오늘은 Java - String의 기본 지식을 여러분과 공유하겠습니다.

String 클래스의 중요성은 말할 필요도 없이 백엔드 개발에서 가장 많이 사용되는 클래스라고 할 수 있으므로 이에 대해 이야기할 필요가 있습니다.

이 글의 주요 내용은 다음과 같습니다.

2w 단어 자세한 설명 문자열, yyds


String 소개

먼저 Java의 8가지 주요 데이터 유형에 대해 이야기한 다음 String에 대해 이야기해 보겠습니다.

8가지 기본 데이터 유형

바이트: 8비트, 최대 저장 데이터 양은 255, 저장 데이터 범위는 -128~127입니다.

짧게: 16비트, 최대 데이터 저장 용량은 65536, 데이터 범위는 -32768~32767입니다.

int: 32비트, 최대 데이터 저장 용량은 2의 32승 -1이며, 데이터 범위는 음수 2의 31승 - 양수 2의 31승 -1입니다.

long: 64비트 최대 데이터 저장 용량은 2의 64승 - 1입니다. 데이터 범위는 -2의 63승 - +2의 63승 -1입니다.

float: 32비트, 데이터 범위는 3.4e-45~1.4e38, 직접 할당 시 숫자 뒤에 f 또는 F를 추가해야 합니다.

double: 64비트, 데이터 범위는 4.9e-324~1.8e308, 값을 할당할 때 d 또는 D를 추가하거나 추가하지 않을 수 있습니다.

boolean: true와 false라는 두 가지 값만 있습니다.

char: 16비트, 유니코드 코드를 저장하고 작은따옴표로 값을 할당합니다.

이 8가지 주요 데이터 유형(8가지 주요 데이터 유형에는 해당 캡슐화 유형도 있음) 외에도 Java에는 문자 그대로 문자열을 의미하는 특수 유형인 문자열도 있습니다.

String 공식 소개

영어 버전

2w 단어 자세한 설명 문자열, yyds


주소: https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/String.htmlhttps://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/String.html

看不懂吗?没事,我们可以借用翻译工具,浏览器自带的,更希望的是你能看懂原版英文。

String 存在于咱们安装的JDK目录下rt.ar包中,全路径名为:java.lang.String

할 수 없습니다. 이해하다 ? 괜찮습니다. 브라우저에 포함된 번역 도구를 빌릴 수 있습니다. 게다가 영어 원어를 이해할 수 있기를 바랍니다.

문자열은 JDK 아래 디렉토리rt.ar 패키지, 모두 경로 이름은 다음과 같습니다: java.lang.String . 문자열은 다음과 같이 Java 코드에서 문자열을 나타내는 데 사용됩니다.

String str = "中国梦,我的梦";
String name = "zhangsan";
로그인 후 복사
지금은 이것만 알아두세요. 문자열 사용

유형 정의

문자열은 일상적인 개발에서 특히 변수 및 상수 유형을 정의하는 데 너무 많은 곳에서 사용됩니다. .

예: 사용자 정보는 엔터티 클래스 User로 표시됩니다.

public class User{
    private Long id;
    private String userName;
    private String address;
    private String password;
    ....
}
로그인 후 복사

일반적인 메서드 데모🎜🎜String 클래스에는 20개 이상의 메서드가 있습니다. 아래에 사용 예가 나와 있습니다(대부분의 메서드가 여기에 설명되어 있으며 나머지는 직접 시도해 볼 수 있습니다). 🎜
//案例代码,来源于网络
public class StringDemo {
    public static void main(String[] args) throws Exception {
        String str1 = "Hello World";
        String str2 = "Hello World";
        String str3 = "hello world";
        String str4 = " hello world ";
        //返回字符串的长度
        System.out.println("r1: " + str1.length());
        //比较两个字符串的大小compareTo(返回的是int),0相等,复数小于,正数大于
        System.out.println("r2 : " + str1.compareTo(str2));
        //比较两个字符串的大小compareTo(返回的是int),0相等,复数小于,正数大于
        System.out.println("r3 : " + str1.compareTo(str3));
        //字符串比较compareToIgnoreCase,忽略大小写。0相等,复数小于,正数大于
        System.out.println("r4 : " + str1.compareToIgnoreCase(str3));
        //字符串查找indexOf,返回的是找到的第一个的位置,没找到返回-1。从0开始
        System.out.println("r5 : " + str1.indexOf("o"));
        //查找字符串最后一次出现的位置lastIndexOf
        System.out.println("r6 : " + str1.lastIndexOf("o"));
        //删除字符串中的一个字符,字符串从0开始的 substring(a, b)
        //返回指定起始位置(含)到结束位置(不含)之间的字符串
        System.out.println("r7 : " + str1.substring(0, 5) + str1.substring(6));

        //字符串替换,替换所有
        System.out.println("r8 : " + str1.replace("o", "h"));
        //字符串替换,替换所有
        System.out.println("r9 : " + str1.replaceAll("o", "h"));
        //字符串替换,替换第一个
        System.out.println("r10 : " + str1.replaceFirst("o", "h"));
        //字符串反转
        System.out.println("r11 : " + new StringBuffer(str1).reverse());
        //字符串反转
        System.out.println("r11’: " + new StringBuilder(str1).reverse());
        //字符串分割
        String[] temp = str1.split("\\ ");
        for (String str : temp) {
            System.out.println("r12 : " + str);
        }
        //字符串转大写
        System.out.println("r13 : " + str1.toUpperCase());
        //字符串转小写
        System.out.println("r14 : " + str1.toLowerCase());
        //去掉首尾空格
        System.out.println("r15 : " + str4.trim());
        //是否包含,大小写区分
        System.out.println("r16 : " + str1.contains("World"));
        //返回指定位置字符
        System.out.println("r17 : " + str1.charAt(4));
        //测试此字符串是否以指定的后缀结束
        System.out.println("r18 : " + str1.endsWith("d"));
        //测试此字符串是否以指定的前缀开始
        System.out.println("r19 : " + str1.startsWith("H"));
        //测试此字符串从指定索引开始的子字符串是否以指定前缀开始
        System.out.println("r20 : " + str1.startsWith("ll", 2));
        //将指定字符串连接到此字符串的结尾。等价于用“+”
        System.out.println("r21 : " + str1.concat("haha"));
        //比较字符串的内容是否相同
        System.out.println("r22 : " + str1.equals(str2));
        //与equals方法类似,忽略大小写
        System.out.println("r23 : " + str1.equalsIgnoreCase(str2));
        //判断是否是空字符串
        System.out.println("r24:  " + str1.isEmpty());

    }
}
로그인 후 복사

我们开发中差不多也就是这么使用了,但是如果你仅仅是使用很牛了,貌似遇到面试照样会挂。所以,学知识,不能停留在使用层面,需要更深层次的学习。

下面我们就来深层次的学习String,希望大家带着一颗平常的心学习,不要害怕什么,灯笼是张纸,捅破不值钱。

String核心部分源码分析

备注:JDK版本为1.8+,因为JDK9版本中和旧版本有细微差别。

String类源码注释

/**
 * The {@code String} class represents character strings. All
 * string literals in Java programs, such as {@code "abc"}, are
 * implemented as instances of this class.
 * 这个String类代表字符串。java编程中的所有字符串常量。
 * 比如说:"abc"就是这个String类的实例
 * <p>
 * Strings are constant; their values cannot be changed after they
 * are created. 
 * 字符串是常量,他们一旦被创建后,他们的值是不能被修改。(重点)
 * String buffers support mutable strings.
 * String缓存池支持可变的字符串,
 * Because String objects are immutable they can be shared. For example:
 * 因为String字符串不可变,但他们可以被共享。比如:
 * <blockquote><pre class="brush:php;toolbar:false">
 *     String str = "abc";
 * 

* is equivalent to: *

 *     char data[] = {&#39;a&#39;, &#39;b&#39;, &#39;c&#39;};
 *     String str = new String(data);
 * 

* Here are some more examples of how strings can be used: * String使用案例 * System.out.println("abc"); * String cde = "cde"; * System.out.println("abc" + cde); * String c = "abc".substring(2,3); * String d = cde.substring(1, 2); *

* The class {@code String} includes methods for examining * individual characters of the sequence, for comparing strings, for * searching strings, for extracting substrings, and for creating a * copy of a string with all characters translated to uppercase or to * lowercase. Case mapping is based on the Unicode Standard version * specified by the {@link java.lang.Character Character} class. * 这个String类包含了一些测评单个字符序列的方法,比如字符串比较,查找字符串, * 提取字符串,和拷贝一个字符串的大小写副本。 * 大小写映射的是基于Character类支持的Unicode的字符集标准版本。 *

* The Java language provides special support for the string * concatenation operator ( + ), and for conversion of * other objects to strings. * java语言提供了对字符串的特殊支持,如:可以通过"+"号来进行字符串的拼接操作, * 为其他类提供了与字符串转换的操作 * String concatenation is implemented * through the {@code StringBuilder}(or {@code StringBuffer}) * class and its {@code append} method. * 字符串的+号拼接操作是通过StringBuilder或者StringBuffer类的append()方法 * 来实现的 * String conversions are implemented through the method * {@code toString}, defined by {@code Object} and * inherited by all classes in Java. * 对象与字符串的转换操作是通过所有类的父类Object中定义的toString()方法来实现的 * For additional information on * string concatenation and conversion, see Gosling, Joy, and Steele, * The Java Language Specification. * *

Unless otherwise noted, passing a null argument to a constructor * or method in this class will cause a {@link NullPointerException} to be * thrown. * 除非有特殊说明,否则传一个null给String的构造方法或者put方法,会报空指针异常的 *

A {@code String} represents a string in the UTF-16 format * in which supplementary characters are represented by surrogate * pairs (see the section Unicode * Character Representations in the {@code Character} class for * more information). * 一个String 对象代表了一个UTF-16编码语法组成的字符串 * Index values refer to {@code char} code units, so a supplementary * character uses two positions in a {@code String}. *

The {@code String} class provides methods for dealing with * Unicode code points (i.e., characters), in addition to those for * dealing with Unicode code units (i.e., {@code char} values). * 索引值指向字符码单元,所以一个字符在一个字符串中使用两个位置, * String 类提供了一些方法区处理单个Unicode编码,除了那些处理Unicode代码单元。 * @since JDK1.0 */

로그인 후 복사

以上便是String类注释的整个片段,后面剩下的就是作者、相关类、相关方法以及从JDK哪个版本开始有的。

String类定义

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
 ....   
 }
로그인 후 복사

类图

2w 단어 자세한 설명 문자열, yyds


String类被final修饰,表示String不可以被继承。下面我们来说说String实现三个接口有什么用处:

  • 实现Serializable,可以被序列化
  • 实现Comparable,可以用于比较大小(按顺序比较单个字符的ASCII码)
  • 实现CharSequence,表示是一个有序字符的序列,(因为String的本质是一个char类型数组)

简单介绍final

修饰类:类不可被继承,也就是说,String类不可被继承了

修饰方法:把方法锁定,以访任何继承类修改它的涵义

修饰遍历:初始化后不可更改

重要成员

 /** The value is used for character storage. */
// 来用存储String内容的
private final char value[];
// 存储字符串哈希值,默认值为0
private int hash; // Default to 0
// 实现序列化的标识
private static final long serialVersionUID = -6849794470754667710L;
로그인 후 복사

char value[]被final修饰,说明value[]数组是不可变的。

构造方法

/**
 * Initializes a newly created {@code String} object so that it represents
 * an empty character sequence.  Note that use of this constructor is
 * unnecessary since Strings are immutable.
 * 初始化新创建的String对象,时期表示空字符串序列。
 * 注意:这个构造方法的用法是没必要的,因为字符串是不可变的
 */
public String() {
        this.value = "".value;
}
로그인 후 복사

无参构造方法中是将一个空字符串的value值赋给当前value。

 /**
  * Initializes a newly created {@code String} object so that it represents
  * the same sequence of characters as the argument; in other words, the
  * newly created string is a copy of the argument string. Unless an
  * explicit copy of {@code original} is needed, use of this constructor is
  * unnecessary since Strings are immutable.
  * 初始化创建的String对象,时期表示与参数相同的字符串序列。
  * 换句话说:新创建的字符串是参数自粗糙的副本。
  * 除非,如果需要original的显示副本,否则也是没有必要使用此构造方法的
  * 因为字符串是不可变的
  * @param  original
  *         A {@code String}
  */
 public String(String original) {
     this.value = original.value;
     this.hash = original.hash;
 }
//案例:  String str=new String("abc");
로그인 후 복사

把original的value赋给当前的value,并把original的hash赋给当前的hash。

/**
 * Allocates a new {@code String} so that it represents the sequence of
 * characters currently contained in the character array argument. The
 * contents of the character array are copied; subsequent modification of
 * the character array does not affect the newly created string.
 * 分配一个新的{@code String},以便它表示字符数组参数中当前包含的字符。这个
 * 复制字符数组的内容;随后修改字符数组不影响新创建的字符串。
 * @param  value
 *         The initial value of the string
 */
public String(char value[]) {
    //注:将传过来的char数组copy到value数组里
    this.value = Arrays.copyOf(value, value.length);
}
//Arrays类中的copyOf方法
public static char[] copyOf(char[] original, int newLength) {
    //创建一个新的char数组
    char[] copy = new char[newLength];
    //把original数组中内容拷贝到新建的char数组中
    System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength));
    //返回新建的char数组
    return copy;
}
로그인 후 복사

使用Arrays类的copyOf方法,新建一个char数组,将original的内容放到新建的char数组中。

然后,把新建的char数组赋给当前的vlaue。

public String(StringBuffer buffer) {
   synchronized(buffer) {
            this.value = Arrays.copyOf(buffer.getValue(), buffer.length());
   }
}
로그인 후 복사

因为StringBuffer是线程安全类,所以,这里加了同步锁,保证线程安全。

public String(StringBuilder builder) {
        this.value = Arrays.copyOf(builder.getValue(), builder.length());
}
로그인 후 복사

StringBuilder是非线程安全的,这里也就没有做线程安全处理,其他内容和前面一样。

注:很多时候我们不会这么去构造,因为StringBuilder跟StringBuffer有toString方法如果不考虑线程安全,优先选择StringBuilder

这里就讲这么多构造方法,其他很复杂,也基本不用,所以,了解这些就够了。如果对其他感兴趣的,可以自行去研究研究。

常用方法分析

前面的使用案例中,我们已经对String的大部分方法进行演示一波,这里我们就挑几个相对重要的方法进行深度解析。

hashCode方法

hashCode()方法是在Object类中定义的,String对其进行了重写。

public int hashCode() {
        int h = hash;
        if (h == 0 && value.length > 0) {
            char val[] = value;
            //hash算法,s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
            //使用{@codeint}算法,其中{@codes[i]}是<i> i</i>字符串的第个字符,
            //{@code n}是字符串,{@code^}表示指数运算。
            for (int i = 0; i < value.length; i++) {
                h = 31 * h + val[i];
            }
            hash = h;
        }
        return h;
}
로그인 후 복사

hashCode的一个具体实现,由于java体系中每个对象都可以转换成String,因此他们应该都是通过这个hash来实现的

接着,我们看看equals()方法;

equals()方法

equals()方法也是Object类中定义的,String类对其进行了重写。

public boolean equals(Object anObject) {
    //首先会判断是否是同一个对象
     if (this == anObject) {
         return true;
     }
    //判断是否为String类型
     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) {
                 //不相等,直接返回false
                 if (v1[i] != v2[i])
                     return false;
                 i++;
             }
             return true;
         }
     }
     return false;
}
로그인 후 복사

补充:==比较

==比较基本数据类型,比较的是值
==比较引用数据类型,比较的是地址值
로그인 후 복사

substring()方法

substring方法在工作使用的也是相当的多,作用就是截取一段字符串。

public String substring(int beginIndex) {
    if (beginIndex < 0) {
        throw new StringIndexOutOfBoundsException(beginIndex);
    }
    int subLen = value.length - beginIndex;
    if (subLen < 0) {
        throw new StringIndexOutOfBoundsException(subLen);
    }
    //如果beginIndex==0,返回的是当前对象,
    //否则这里是new的一个新对象,其实String中的很多函数都是这样的操作
    return (beginIndex == 0) ? this : new String(value, beginIndex, subLen);
}
로그인 후 복사

intern()方法

intern()方法是native修饰的方法,表示该方法为本地方法。

/*
 * When the intern method is invoked, if the pool already contains a
 * string equal to this {@code String} object as determined by
 * the {@link #equals(Object)} method, then the string from the pool is
 * returned. Otherwise, this {@code String} object is added to the
 * pool and a reference to this {@code String} object is returned.
 */
public native String intern();
로그인 후 복사

方法注释会有写到,意思就是调用方法时,如果常量池有当前String的值,就返回这个值,没有就加进去,返回这个值的引用。

案例如下

public class StringDemo {
    public static void main(String[] args) throws Exception {
        String str1 = "a";
        String str2 = "b";
        String str3 = "ab";
        String str4 = str1 + str2;
        String str5 = new String("ab");

        System.out.println(str5 == str3);//堆内存比较字符串池
        //intern如果常量池有当前String的值,就返回这个值,没有就加进去,返回这个值的引用
        System.out.println(str5.intern() == str3);//引用的是同一个字符串池里的
        System.out.println(str5.intern() == str4);//变量相加给一个新值,所以str4引用的是个新的
        System.out.println(str4 == str3);//变量相加给一个新值,所以str4引用的是个新的

    }
}
로그인 후 복사

运行结果

false
true
false
false
로그인 후 복사

length()方法

获取字符串长度,实际上是获取字符数组长度 ,源码就非常简单了,没什么好说的。

public int length() {
    return value.length;
}
로그인 후 복사

isEmpty() 方法

判断字符串是否为空,实际上是盼复字符数组长度是否为0 ,源码也是非常简单,没什么好说的。

public boolean isEmpty() {
    return value.length == 0;
}
로그인 후 복사

charAt(int index) 方法

根据索引参数获取字符 。

public char charAt(int index) {
    //索引小于0或者索引大于字符数组长度,则抛出越界异常
    if ((index < 0) || (index >= value.length)) {
        throw new StringIndexOutOfBoundsException(index);
    }
    //返回字符数组指定位置字符
    return value[index];
}
로그인 후 복사

getBytes()方法

获取字符串的字节数组,按照系统默认字符编码将字符串解码为字节数组 。

public byte[] getBytes() {
    return StringCoding.encode(value, 0, value.length);
}
로그인 후 복사

compareTo()方法

这个方法写的很巧妙,先从0开始判断字符大小。如果两个对象能比较字符的地方比较完了还相等,就直接返回自身长度减被比较对象长度,如果两个字符串长度相等,则返回的是0,巧妙地判断了三种情况。

public int compareTo(String anotherString) {
    //自身对象字符串长度len1
    int len1 = value.length;
    //被比较对象字符串长度len2
    int len2 = anotherString.value.length;
    //取两个字符串长度的最小值lim
    int lim = Math.min(len1, len2);
    char v1[] = value;
    char v2[] = anotherString.value;
 
    int k = 0;
    //从value的第一个字符开始到最小长度lim处为止,如果字符不相等,
    //返回自身(对象不相等处字符-被比较对象不相等字符)
    while (k < lim) {
        char c1 = v1[k];
        char c2 = v2[k];
        if (c1 != c2) {
            return c1 - c2;
        }
        k++;
    }
    //如果前面都相等,则返回(自身长度-被比较对象长度)
    return len1 - len2;
}
로그인 후 복사

startsWith()方法

public boolean startsWith(String prefix, int toffset) {
    char ta[] = value;
    int to = toffset;
    char pa[] = prefix.value;
    int po = 0;
    int pc = prefix.value.length;
    // Note: toffset might be near -1>>>1.
    //如果起始地址小于0或者(起始地址+所比较对象长度)大于自身对象长度,返回假
    if ((toffset < 0) || (toffset > value.length - pc)) {
        return false;
    }
    //从所比较对象的末尾开始比较
    while (--pc >= 0) {
        if (ta[to++] != pa[po++]) {
            return false;
        }
    }
    return true;
}
 
public boolean startsWith(String prefix) {
    return startsWith(prefix, 0);
}
 
public boolean endsWith(String suffix) {
    return startsWith(suffix, value.length - suffix.value.length);
}
로그인 후 복사

起始比较和末尾比较都是比较经常用得到的方法,例如:在判断一个字符串是不是http协议的,或者初步判断一个文件是不是mp3文件,都可以采用这个方法进行比较。

concat()方法

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);
    return new String(buf, true);
}
로그인 후 복사

concat方法也是经常用的方法之一,它先判断被添加字符串是否为空来决定要不要创建新的对象。

replace()方法

public String replace(char oldChar, char newChar) {
    //新旧值先对比
    if (oldChar != newChar) {
        int len = value.length;
        int i = -1;
        char[] val = value; 
 
        //找到旧值最开始出现的位置
        while (++i < len) {
            if (val[i] == oldChar) {
                break;
            }
        }
        //从那个位置开始,直到末尾,用新值代替出现的旧值
        if (i < len) {
            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++;
            }
            return new String(buf, true);
        }
    }
    return this;
}
로그인 후 복사

这个方法也有讨巧的地方,例如最开始先找出旧值出现的位置,这样节省了一部分对比的时间。replace(String oldStr,String newStr)方法通过正则表达式来判断。

trim()方法

public String trim() {
    int len = value.length;
    int st = 0;
    char[] val = value;    /* avoid getfield opcode */
 
    //找到字符串前段没有空格的位置
    while ((st < len) && (val[st] <= &#39; &#39;)) {
        st++;
    }
    //找到字符串末尾没有空格的位置
    while ((st < len) && (val[len - 1] <= &#39; &#39;)) {
        len--;
    }
    //如果前后都没有出现空格,返回字符串本身
    return ((st > 0) || (len < value.length)) ? substring(st, len) : this;
}
로그인 후 복사

trim方法就是将字符串中的空白字符串删掉。

valueOf()方法

public static String valueOf(boolean b) {
   //如果b为true就返回"true"否则返回"false"
   return b ? "true" : "false";
}
public static String valueOf(char c) {
    //创建data[]数组 并把c添加进去
    char data[] = {c};        
     //创建一个新的String对象并进行返回
    return new String(data, true); 
}
public static String valueOf(int i) {
    //调用Integer对象的toString()方法并进行返回
    return Integer.toString(i);  
}
//Integer类中的toString(i)方法
public static String toString(int i) {
    //是否为Integer最小数,是直接返回
    if (i == Integer.MIN_VALUE)
       return "-2147483648";
    //这个i有多少位
    int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i);
    //创建一个char数组
    char[] buf = new char[size];
    //把i内容方法char数组中区
    getChars(i, size, buf);
    //返回一个String对象
    return new String(buf, true);
}
로그인 후 복사

split() 方法

public String[] split(String regex) {
    return split(regex, 0);
}
//使用到了正则表达式
public String[] split(String regex, int limit) {
      //....
    //源码有点多了,反正就是里面使用到了正则表达式,进行切分
    }
로그인 후 복사

split() 方法用于把一个字符串分割成字符串数组,返回一个字符串数组返回的数组中的字串不包括 regex自身。可选的“limit”是一个整数,第一个方法中默认是0,允许各位指定要返回的最大数组的元素个数。

常见方法源码分析就这么多了,下面我们再回顾到使用场景中来,尤其是面试中。

String在面试中常见问题

如何比较字符串相同?

在java中比较对象是否相同,通常有两种方法:

  • ==
  • equals方法

注意==用于基本数据类型的比较和用于引用类型的比较的区别。

==比较基本数据类型,比较的是值

==比较引用数据类型,比较的是地址值

另外,Stringequals方法进行了重写,所以比较字符串咱们还是要使用equals方法来比较。主要是Stringequals方法里包含了==的判断(请看前面源码分析部分)。

案例

public class StringDemo {
   public static void main(String[] args) {
     String st1 = "abc";
     String st2 = "abc";
     System.out.println(st1 == st2);
     System.out.println(st1.equals(st2)); 
   }
}
로그인 후 복사

输出

true
true
로그인 후 복사

String str=new String("abc");这行代码创建了几个对象?

看下面这段代码:

String str1 = "abc";  // 在常量池中
String str2 = new String("abc"); // 在堆上
로그인 후 복사

关于这段代码,创建了几个对象,网上答案有多重,1个,2个还有3个的。下面我们就来聊聊到底是几个?

먼저 str1이든 str2이든 모두 객체가 아닌 String형 변수라는 점을 분명히 해야 합니다. 일반적으로 str2 객체라고 부르지만, 본질적으로 str2라고 합니다. str1은 객체가 아닙니다.

두 번째로, String str="abc";</ code>에서 문자열 "abc"는 1개의 복사본으로 문자열 상수 풀에 저장됩니다. 이때 할당 작업은 0 또는 1개의 개체를 생성하는 것과 같습니다. "abc"가 상수 풀에 이미 있으면 개체가 생성되지 않고 참조가 str1에 직접 할당됩니다. "abc"가 상수 풀에 없으면 개체가 생성되고 참조가 str1에 할당됩니다. str1. <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">String str="abc";的时候,字符串“abc”会被存储在字符串常量池中,只有1份,此时的赋值操作等于是创建0个或1个对象。如果常量池中已经存在了“abc”,那么不会再创建对象,直接将引用赋值给str1;如果常量池中没有“abc”,那么创建一个对象,并将引用赋值给str1。

那么,通过new String("abc");的形式又是如何呢?

答案是1个或2个。

当JVM遇到上述代码时,会先检索常量池中是否存在“abc”,如果不存在“abc”这个字符串,则会先在常量池中创建这个一个字符串。然后再执行new操作,会在堆内存中创建一个存储“abc”的String对象,对象的引用赋值给str2。此过程创建了2个对象。

当然,如果检索常量池时发现已经存在了对应的字符串,那么只会在堆内创建一个新的String对象,此过程只创建了1个对象。

最后,如果单独问String str=new String("abc");

그렇다면 new String("abc");의 형태는 어떨까요?

답은 1 또는 2입니다.

JVM은 위 코드를 발견하면 먼저 상수 풀에 "abc"가 있는지 검색합니다. 문자열 "abc"가 존재하지 않으면 먼저 상수 풀에 이 문자열을 생성합니다. 그런 다음 새 작업을 수행하면 "abc"를 저장하는 문자열 개체가 힙 메모리에 생성되고 개체의 참조가 str2에 할당됩니다. 이 프로세스에서는 2개의 개체가 생성됩니다. 물론, 상수 풀을 조회할 때 해당 문자열이 이미 존재하는 것으로 확인되면 힙에는 새로운 String 객체만 생성되며, 이 과정에서 객체는 하나만 생성됩니다.

마지막으로 따로 문의하시면문자열 str=new 문자열 ( "abc"); 여러 개체가 생성됩니다. "abc"가 상수 풀에 존재하는지 여부를 기억하십시오. 존재한다면 하나의 개체를 생성하고, 존재하지 않으면 두 개의 개체를 생성하십시오.

String과 StringBuilder 및 StringBuffer의 차이점

스레드 안전성

String의 객체는 불변입니다. 이는 상수이자 스레드로부터 안전한 것으로 이해될 수 있습니다. AbstractStringBuilder는 StringBuilder 및 StringBuffer의 공통 상위 클래스이며 ExpandCapacity, 추가, 삽입, indexOf 및 기타 공용 메소드와 같은 몇 가지 기본 문자열 작업을 정의합니다. StringBuffer는 메서드에 동기화 잠금을 추가하거나 호출 메서드에 동기화 잠금을 추가하므로 스레드로부터 안전합니다. StringBuilder는 메서드에 동기화 잠금을 추가하지 않으므로 스레드로부터 안전하지 않습니다.

🎜🎜Performance🎜🎜🎜String 유형이 변경될 때마다 새 String 개체가 생성되고 포인터가 새 String 개체를 가리킵니다. StringBuffer는 새 개체를 생성하고 개체 참조를 변경하는 대신 매번 StringBuffer 개체 자체에서 작동합니다. 동일한 상황에서 StringBuilder를 사용하면 StringBuffer를 사용할 때보다 약 10~15% 정도의 성능 향상만 얻을 수 있지만 멀티스레딩이 불안정해질 위험이 있습니다. 🎜🎜세 가지 용도 요약:🎜
  • 操作少量的数据 ,推荐使用String
  • 单线程操作字符串缓冲区下操作大量数据,推荐使用 StringBuilder
  • 多线程操作字符串缓冲区下操作大量数据 ,推荐使用 StringBuffer

String 和 JVM有什么关系?

String 常见的创建方式有两种,new String() 的方式和直接赋值的方式,直接赋值的方式会先去字符串常量池中查找是否已经有此值,如果有则把引用地址直接指向此值,否则会先在常量池中创建,然后再把引用指向此值;而 new String() 的方式一定会先在堆上创建一个字符串对象,然后再去常量池中查询此字符串的值是否已经存在,如果不存在会先在常量池中创建此字符串,然后把引用的值指向此字符串。

JVM中的常量池

2w 단어 자세한 설명 문자열, yyds


字面量—文本字符串,也就是我们举例中的 public String s = " abc "; 中的 "abc"。

用 final 修饰的成员变量,包括静态变量、实例变量和局部变量。

请看下面这段代码:

String s1 = new String("Java");
String s2 = s1.intern();
String s3 = "Java";
System.out.println(s1 == s2); // false
System.out.println(s2 == s3); // true
로그인 후 복사

它们在 JVM 存储的位置,如下图所示:

2w 단어 자세한 설명 문자열, yyds


注意:JDK 1.7 之后把永生代换成的元空间,把字符串常量池从方法区移到了 Java 堆上。

除此之外编译器还会对 String 字符串做一些优化,例如以下代码:

String s1 = "Ja" + "va";
String s2 = "Java";
System.out.println(s1 == s2);
로그인 후 복사

虽然 s1 拼接了多个字符串,但对比的结果却是 true,我们使用反编译工具,看到的结果如下:

Compiled from "StringExample.java"
public class com.lagou.interview.StringExample {
  public com.lagou.interview.StringExample();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return
    LineNumberTable:
      line 3: 0
  public static void main(java.lang.String[]);
    Code:
       0: ldc           #2                  // String Java
       2: astore_1
       3: ldc           #2                  // String Java
       5: astore_2
       6: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
       9: aload_1
      10: aload_2
      11: if_acmpne     18
      14: iconst_1
      15: goto          19
      18: iconst_0
      19: invokevirtual #4                  // Method java/io/PrintStream.println:(Z)V
      22: return
    LineNumberTable:
      line 5: 0
      line 6: 3
      line 7: 6
      line 8: 22
}
로그인 후 복사

从编译代码 #2 可以看出,代码 "Ja"+"va" 被直接编译成了 "Java" ,因此 s1==s2 的结果才是 true,这就是编译器对字符串优化的结果。

如何判断两个字符串中含有几个相同字符

  • 将字符串转化成数组
  • HashMap 方法
  • 字符串直接进行比较
  • 正则表达式
  • HashSet 方法

String有没有长度限制?是多少?为什么?

下面先看看length方法源码:

private final char value[];
public int length() {
        return value.length;
}
로그인 후 복사

length()方法返回的是int类型,那可以得知String类型的长度肯定不能超过Integer.MAX_VALUE的。

答:首先字符串的内容是由一个字符数组 char[] 来存储的,由于数组的长度及索引是整数,且String类中返回字符串长度的方法length() 的返回值也是int ,所以通过查看java源码中的类Integer我们可以看到Integer的最大范围是2^31 -1,由于数组是从0开始的,所以**数组的最大长度可以使【0~2^31】**通过计算是大概4GB。

但是通过翻阅java虚拟机手册对class文件格式的定义以及常量池中对String类型的结构体定义我们可以知道对于索引定义了u2,就是无符号占2个字节,2个字节可以表示的最大范围是2^16 -1 = 65535

但是由于JVM需要1个字节表示结束指令,所以这个范围就为65534了。超出这个范围在编译时期是会报错的,但是运行时拼接或者赋值的话范围是在整形的最大范围。

字符串对象能否用在switch表达式中?

JDK7开始的话,我们就可以在switch条件表达式中使用字符串了,也就是说7之前的版本是不可以的。

switch (str.toLowerCase()) {
      case "tian":
           value = 1;
           break;
      case "jiang":
           value = 2;
           break;
}
로그인 후 복사

说说String中intern方法

JDK7之前的版本,调用这个方法的时候,会去常量池中查看是否已经存在这个常量了,如果已经存在,那么直接返回这个常量在常量池中的地址值,如果不存在,则在常量池中创建一个,并返回其地址值。

但是在JDK7以及之后的版本中,常量池从perm区搬到了heap区。intern检测到这个常量在常量池中不存在的时候,不会直接在常量池中创建该对象了,而是将堆中的这个对象的引用直接存到常量池中,减少内存开销。

下面的案例

public class InternTest {
  
  public static void main(String[] args) {
    String str1 = new String("hello") + new String("world");
    str1.intern();
    String str2 = "helloworld";
    System.out.println(str1 == str2);//true
    System.out.println(str1.intern() == str2);//true
  }
}
로그인 후 복사

好了,关于Stirng类的分享就到此,欢迎找我探讨更多技术问题。

위 내용은 2w 단어 자세한 설명 문자열, yyds의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

관련 라벨:
원천:Java后端技术全栈
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿