关于String实例的trim()方法学习

738 阅读2分钟

最近在学习GitHub上的一个项目,看到该项目的登录功能有对前端传入的密码字符串调用了trim()方法,用意在于去除用户输入数据首部和尾部的空格。部分代码如下:

            // 用户名
            String username = loginData.get(getUsernameParameter());
            // 密码
            String password = loginData.get(getPasswordParameter());
            // 预防后面的空指针异常
            if (username == null) {
                username = "";
            }
            if (password == null) {
                password = "";
            }
            // 除去首尾的空格
            username = username.trim();

trim()是String的一个方法,百度了一下发现trim()并不仅仅是去掉字符串“首尾空格”那么简单,以下是学习笔记。

P.S:
参考1:string.trim()究竟去掉了什么?
参考2:String源码中的注释“avoid getfield opcode”

直接上源码

public String trim() {
        // len表示实例字符串的长度
        int len = value.length;
        // st表示一个计数器(游标)
        int st = 0;
        // 在一个方法中需要大量引用实例域变量的时候,使用方法中的局部变量代替引用可以减少getfield操作的次数,提高性能
        char[] val = value;  
        
        // 第1个while
        while ((st < len) && (val[st] <= ' ')) {
            st++;
        }
        //第2个while
        while ((st < len) && (val[len - 1] <= ' ')) {
            len--;
        }
        return ((st > 0) || (len < value.length)) ? substring(st, len) : this;
    }

观察发现:

  1. 无论第1个还是第2个while都有字符char类型之间的比较,如val[st] <= ' 'val[len - 1] <= ' '
  2. trim()方法的最后调用了subString()方法,说明trim()最后实际上执行的是一个截取字符串的动作

分析:

  1. 我们知道,因为char类型在ASCII等字符编码表中有对应的数值,char类型之间的比较实际上可直接当做ASCII表对应的整数的比较。我们可以先参考(ASCII码和Unicode字符编码的对照表)[ascii.911cha.com/]。通过编码对照表,我们可以得出的结论是trim()方法实际上trim掉(除掉)了字符串两端Unicode编码小于等于32(\u0020)的所有字符。大白话一点,trim()实际上就是去除掉了字符串中所有的ASCII的控制字符(这是具有实际意义的,因为我们基本在键盘上是没办法敲出这些字符的),让字符串仅保留ASCII可显示字符,如'a'、'B'等。
  2. 对于subString()方法:
public String substring(int beginIndex, int endIndex) {
        if (beginIndex < 0) {
            throw new StringIndexOutOfBoundsException(beginIndex);
        }
        if (endIndex > value.length) {
            throw new StringIndexOutOfBoundsException(endIndex);
        }
        int subLen = endIndex - beginIndex;
        if (subLen < 0) {
            throw new StringIndexOutOfBoundsException(subLen);
        }
        return ((beginIndex == 0) && (endIndex == value.length)) ? this
                : new String(value, beginIndex, subLen);
    }

我们发现最终返回的String对象是new出来的,new出来的对象位于Heap内存中,而不在方法区的常量池中。这也就说明了一个结果:当String实例调用了trim()方法之后,返回的将是一个新的对象。


总结:

  1. trim()方法除掉的不仅仅是空格,而是除掉了字符串两端Unicode编码小于等于32(\u0020)的所有字符
  2. trim()调用后,返回的将是一个全新的对象