zoukankan      html  css  js  c++  java
  • String的那些事

    String的特点?

    通过查看String类的源码我们得知:String类被final关键字修饰,这即是说明String类的特点就是:字符串对象一旦被初始化就不会被改变。注意:此处是字符串对象而不是字符串引用。也即是说:

    
    String s = "abc";   // s引用变量指向了值为"abc"的对象
    
    s = "cba";    // s引用变量又指向了值为"cba"的对象,但是上面值为"abc"的对象的值并未改变
    
    

    我们需要注意所有对String的操作(包括增删改等)都是新建对象(参考String类的源码)。那么String类被final修饰有什么好处呢?第一个好处是安全,因为final保证不管怎样操作,它的值都是不变的;第二个好处就是高效,因为只有String类是不可变类时,我们才能实现字符串常量池。试想如果String类是可变类,当多个字符串引用变量指向同一个字符串对象时,字符串对象改变后就会引起多个字符串引用变量内容的改变,这显然不符合我们的预期。我们可以通过下面的代码来验证字符串常量池的存在:

    
    class Demo {
        public static void main(String[] args) {
            String s1 = "abc";
            String s2 = "abc";
    
            Demo d1 = new Demo();
            Demo d2 = new Demo();
            System.out.println(s1 == s2);     // 1
            System.out.println(d1 == d2);
        }
    }
    
    

    我们知道:如果是两个引用变量使用"=="进行比较,那么比较的是两个对象的地址值,1处的代码输出结果为"true",说明s1引用变量和s2引用变量指向的是同一个对象,也就验证了字符串常量池的存在。字符串常量池其实就是字符串的一个缓冲区,而"缓存"可以提高系统性能,那么即是说字符串常量池的使用可以提高系统性能。常量池的特点就在于:如果池中没有则创建,如果池中有就直接使用池中的。

    String内部实际存储结构为char数组。下面为jdk1.8版本的String源码:

    
    public final class String
            implements java.io.Serializable, Comparable<String>, CharSequence {
    
        // 用于存储字符串的值
        private final char value[];
    
        // 用于缓存字符串的hashcode
        private int hash; // Default to 0
    
        // else code
        // ... ...
    }
    
    

    我们需要注意编译器会对String做一些优化,比如下列代码:

    
    class Demo {
        public static void main(String[] args) {
            String s1 ="Ja"+"va";
            String s2 = "Java";
            System.out.println(s1 == s2);
        }
    }
    
    

    String的构造方法?

    String是一个类,所以我们除了使用String s1 = "abc"方式创建字符串对象之外,还可以通过String类的构造方法进行创建。通过查看jdk文档我们发现String类
    有下面这样的构造函数:

    我们需要注意这两种创建方式的区别:

    
    class Demo {
        public static void main(String[] args) {
            String s1 = newString("Java");   // 1
            String s2 = s1.intern();         // 2
            String s3 = "Java";              // 3
            System.out.println(s1 == s2);
            System.out.println(s2 == s3);
        }
    }
    
    

    1处代码的含义是:在堆内存中使用new的方式创建了一个字符串对象并把地址值赋给了引用变量s1,并且该对象在创建的时候接收了一个字符串对象;而2处代码的含义是:在堆内存中创建一个变量s2,如果调用intern()才会把此字符串保存到字符串常量池中;3处代码的含义是:在字符串常量池中创建了一个值为"Java"的字符串对象,并把该对象的地址值赋给了引用变量s3;所以3处的代码最终只涉及到一个对象,而1处的代码有可能涉及一个(字符串常量池中有字符串对象"Java")也有可能涉及两个(字符串常量池中没有字符串对象"Java")。这三个引用变量在JVM中存储的位置如下:

    注意:在JDK6.0及之前版本,字符串常量池是放在方法区中,而在JDK7.0版本,字符串常量池被移到了堆中。

    我们可以看到引用变量s1和引用变量s2的内容其实是相等的,都是"Java",于是如果我们只想比较两者的内容时,就可以使用equals()。

    
    class Demo {
        public static void main(String[] args) {
            String s1 = "abc";
            String s2 = new String("abc");
    
            System.out.println(s1 == s2);
            System.out.println(s1.equals(s2));
        }
    }
    
    

    这里需要注意,Object类中equals()的源码是下面这样的:

    
    public boolean equals(Object obj) {
        return (this == obj);   // 比较的还是对象的地址值
    }
    
    

    而String类对该方法进行了覆盖,源码是这样的:

    
    public boolean equals(Object anObject) {
        // 对象引用相同直接返回 true
        if (this == anObject) {
            return true;
        }
    
        // 判断需要对比的值是否为String类型,如果不是则直接返回false
        if (anObject instanceof String) {   
            String anotherString = (String)anObject;
            int n = value.length;
            if (n == anotherString.value.length) {
                // 把两个字符串都转换为 char 数组对比
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
    
                // 循环比对两个字符串的每一个字符
                while (n-- != 0) {
    
                    // 如果其中有一个字符不相等就 true false,否则继续对比
                    if (v1[i] != v2[i])
                        return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }
    
    

    String类有几个很常用的构造方法,如下:

    
    // char[]为参数的构造方法
    public String(char value[]) {
        this.value = Arrays.copyOf(value, value.length);
    }
    
    // byte[]为参数的构造方法
    public String(byte bytes[]) {
        this(bytes, 0, bytes.length);
    }
    
    // string为参数的构造方法
    public String(String original) {
        this.value = original.value;
        this.hash = original.hash;
    }
    
    // StringBuffer为参数的构造方法 
    public String(StringBuffer buffer) {
        synchronized(buffer) {
            this.value = Arrays.copyOf(buffer.getValue(), buffer.length());
        }
    }
    
    // StringBuilder为参数的构造方法  
    public String(StringBuilder builder) {
        this.value = Arrays.copyOf(builder.getValue(), builder.length());
    }
    
    

    String的常用方法?

    通过查看jdk文档,我们可以看到String类有很多方法,将其中常用的方法分为下面几类:

    • 用于获取:
    
    1.1 获取字符串中字符的个数(长度):
            int length();
    1.2 根据位置获取字符:
            char charAt(int index);
    1.3 根据字符获取在字符串中的第一次出现的位置:
            int indexOf();    // 注意此方法的有多种重载形式
    
    
    • 用于转换
    
    1.1 将字符串变成字符串数组(字符串的切割):
            String[]  split(String regex);   // 注意此方法涉及到正则表达式
    1.2 将字符串中的字母转成大小写:
            String toUpperCase():大写
            String toLowerCase():小写
    1.3 将字符串中的内容进行替换:
            String replace();
    1.4 将字符串两端的空格去除:
            String trim();
               
    
    • 用于判断
    
    1.1 判断字符串中是否包含指定字符串:
            boolean contains(string str);
    
    
    • 用于比较
    
    1.1 比较两个字符串:
            int compareTo(String anotherString)
    
    // 下面为compareTo方法的源码
    public int compareTo(String anotherString) {
        int len1 = value.length;
        int len2 = anotherString.value.length;
        int lim = Math.min(len1, len2);
        char v1[] = value;
        char v2[] = anotherString.value;
    
        int k = 0;
    
        // 对比每一个字符
        while (k < lim) {
            char c1 = v1[k];
            char c2 = v2[k];
    
            // 如果字符不相等就返回差值
            if (c1 != c2) {
                return c1 - c2;
            }
            k++;
        }
        return len1 - len2;
    }
    
    

    可以看出compareTo()和equals()都是用于比较两个字符串的,并且当equals()返回true或者是compareTo()返回0时,则表示两个字符串完全相同。这两个方法的区别在于:compareTo()接收的是String类型的参数,而equals()可以接收一个Object类型的参数;compareTo()的返回值为int,而equals()返回值为Boolean。

  • 相关阅读:
    [sql查询] 重复数据只取一条
    SSIS,参数坑
    数据仓库之建立多维数据库
    数据仓库之SSIS开发
    开发规范
    页面以base64输出图片
    内嵌iframe
    T-Sql编程基础
    MVC3.0----整理之一
    原生JS 表单提交验证器
  • 原文地址:https://www.cnblogs.com/syhyfh/p/12512902.html
Copyright © 2011-2022 走看看