Java随堂笔记课【七】:引用计数、字符串函数

前章回顾

上次说了字符串的构造方法

String str1 = new String("abcdefg");
String str2 = new String("abcdefg");

这种做法强行在java中开了两个空间,所以str1和str2不相等。

String s1 = "xyz";
String s2 = "xyz"; 

这种做法下系统会认为你新开内存的欲望没有上一段代码强烈,于是会让s1s2同时指向xyz。所以s1s2相等。


#
那么问题来了,
现在有几个变量指向”xyz”?2个对吧。
如果下面有这样的代码

String s1 = s1 + "w";

之后还有几个变量指向”xyz”?就剩一个(s2)了对吧。
没错,这就是引用计数


引用计数

一个对象身上引用变量的数量,叫做引用计数。
一个对象身上的引用计数大于0,那么这个对象是可控的,说明栈中可以向他发指令。
当这个对象引用为0时呢?将失去控制,栈将无法对其继续控制,该对象成为垃圾对象

垃圾对象还将继续占用堆空间,如果不回收掉这个空间,它将不能再被使用,很有可能被黑客利用,这种现象称为内存泄漏。

Java提供了垃圾回收机制,通过特定算法,在堆空间内存紧张的情况下,对所有的Java对象进行轮询,只要发现引用计数为0。则立即销毁释放空间。

所以理论上说Java不存在内存泄漏,程序员无需关注内存管理。(写C++是不是经常Delete?Java不需要,自动完成)


字符串函数

Java的每个String都提供了一大堆函数用来进行字符串的各种操作

1.String

我们可以打开String.class看一下具体的代码,前五行就有这么一句话:

 private final char value[];

看到没?String本身就是一个字节数组。

2.String.length()

String.length用于求字符串的长度。
这是String.class中的length函数:

public int length() {
    return value.length;
}

看到没?其实就是对字节数组求长度。

3.String.trim()

trim的作用是去掉字符串的头尾空格。

String str = " abcdefg  ";
// str.length() = 10;
// str.trim().length() = 7;

头尾切割的代码在String.class里。简单看一下就好,其实也很容易看懂。

    public String trim() {
        int len = value.length;
        int st = 0;
        char[] val = value;    /* avoid getfield opcode */

        while ((st < len) && (val[st] <= ' ')) {
            st++;
        }
        while ((st < len) && (val[len - 1] <= ' ')) {
            len--;
        }
        return ((st > 0) || (len < value.length)) ? substring(st, len) : this;
    }

4.String.toUpperCase()/String.toLowerCase()

把字符串所有字符转为大写/小写。Java函数写的很复杂,不表。(写那么复杂更多是为了提高兼容性,原理并不难)

5.String.indexOf()

获取String字符(串的第一个字符)的位置。
String.class中代码非常简单。就是用for循环寻找一下,找到就返回。
下面这段代码是搜字符的从字符串的第fromIndex个字符开始搜索是否存在字符ch(ch转成了int类型方便计算)。搜字符串的,省篇幅不表。

    public int indexOf(int ch, int fromIndex) {
        final int max = value.length;
        if (fromIndex < 0) {
            fromIndex = 0;
        } else if (fromIndex >= max) {
            // Note: fromIndex might be near -1>>>1.
            return -1;
        }

        if (ch < Character.MIN_SUPPLEMENTARY_CODE_POINT) {
            // handle most cases here (ch is a BMP code point or a
            // negative value (invalid code point))
            final char[] value = this.value;
            for (int i = fromIndex; i < max; i++) {
                if (value[i] == ch) {
                    return i;
                }
            }
            return -1;
        } else {
            return indexOfSupplementary(ch, fromIndex);
        }
    }

6.String.lastIndexOf()

反过来找相对应的字符(串的第一个字符——注意不是最后一个)。原理和indexOf类似。

7.String.equals()

比较两个字符串的值是否相等。

注:值得一提的是,在API中,String.equals()用到了instanceof。不懂这里的Java知识点正好可以补充一下如果 object 是 class 的一个实例,则 instanceof 运算符返回 true。如果 object 不是指定类的一个实例,或者 object 是 null,则返回 false。

8.String.contains()

查看字符串中有没有这个字符(串)。代码只有三行:

public boolean contains(CharSequence s) {
    return indexOf(s.toString()) > -1;
}

所以contains完全是一个装饰方法。就是为了方便不明白的人用,更加书面化。

9.String.charAt()

获得字符串在第x个位置的字符。代码如下:
封装完全是为了多写个if,做安全性考虑(如果超出范围会抛出一个数组上下标越界异常)。

public char charAt(int index) {
    if ((index < 0) || (index >= value.length)) {
        throw new StringIndexOutOfBoundsException(index);
    }
    return value[index];
}

10.String.equalsIgnoreCase()

比较两个字符串,和普通的equals相比,忽略了大小写。
其实和下面这个式子等价:

strA.toUpperCase().equals(B.toUpperCase());

但是,这样折腾一下就多了两个对象出来,并且代码更长。所以Java也对忽略大小写做了封装。

11.String.toCharArray();

把字符串转成原始的字符数组。String.class代码如下:

public char[] toCharArray() {
    // Cannot use Arrays.copyOf because of class initialization order issues
    char result[] = new char[value.length];
    System.arraycopy(value, 0, result, 0, value.length);
    return result;
}

System.arraycopy也很好理解对吧,把字符串从0到length的所有字符赋值给字符数组result[]。
什么,你要打开看一下——不好意思,看不到啦。因为它是长这样子的

public static native void arraycopy(Object src,  int  srcPos,
                                    Object dest, int destPos,
                                    int length);

有没有发现一个native方法,这表明这段代码使用了底层的C语言进行编写。在JDK中是看不到了,我们可以离开JDK的范围去更底层看,那就是后话啦。

12.String.Concat()

链接两个字符串。比如:

s = "abc".concat("efg");

s为”abcefg”;
另外为了迎合传统程序员,Java中重载了String类型的加号,使其变为concat方法的另一种形式。简单说,平常看到的

s = "abc"+"efg";

其实就是concat的另一种形式。

s = "abc".concat("efg");

concat的代码具体实现方法:用了Arrays的静态方法copyOf,在字符数组的形态下,才能进行复制操作。然后,重新创建一个new String出来。

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);
}

13.String.valueOf()

把字符串转为数字。就是内部封装了Integet.toString(),作用相同。

14.String.CompareTo()

字符串之间相互比较大小。

strA.compartTo(strB);

A小于B返回-1,等于返回0,大于返回1。
注意,String类并没有重载<、=、>,不要在比较的时候用。

15.String.CompareToIgnoreCase()

CompareTo的忽略字符串版。

16.String.startsWith()/String.endsWith()

判断字符串是否以”xx”开头/结尾。比如:

"abcdefg".startsWith("abc");    //true
"abcdefg".endsWith("efg");    //true

17.String.isEmpty()

判断字符串是否为空。封装代码只有一行,就判断下长度是否为0就可以了。也是一个装饰方法。

public boolean isEmpty() {
    return value.length == 0;
}

18.String.replace()

String.replace("a", "bb");

把一段字符串中的所有”a”替换成”bb”。
String.class源代码如下。代码合理运用了内存,一直到出现不相等字符的时候,才在内存中新开辟空间存储替换字符。

public String replace(char oldChar, char newChar) {
    if (oldChar != newChar) {
        int len = value.length;
        int i = -1;
        char[] val = value; /* avoid getfield opcode */

        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;
}

17.String.subString()

String.subString(3, 7);

返回字符串的第3-6位(就是第4-7个)字符。
源代码中可以看到,subString函数首先进行了安全处理,确定不会异常后,重新做了一个new String。(仔细想想其实和引用计数,构造函数的关系都很密切)

public String substring(int beginIndex) {
    if (beginIndex < 0) {
        throw new StringIndexOutOfBoundsException(beginIndex);
    }
    int subLen = value.length - beginIndex;
    if (subLen < 0) {
        throw new StringIndexOutOfBoundsException(subLen);
    }
    return (beginIndex == 0) ? this : new String(value, beginIndex, subLen);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值