java中的字符串简介,字符串的优化以及如何高效率的使用字符串

  • 简介  

  String最为java中最重要的数据类型。字符串是软件开发中最重要的对象之一,通常,字符串对象在内存中总是占据着最大的空间块。所以,高效处理字符串,将提高系统的整个性能。  

  在java语言中,String对象可以认为是char数组的衍生和进一步的封装。它的主要组成部分是:char数组、偏移量和string的长度。char数组表示string的内容,它是string对象所表示字符串的超集。String的真实内容还需要偏移量和长度在这个char数组中进一步定位和截取。(查看java源代码可以看到char数组、偏移量和长度定义)

  String对象的三个基本特点:

  1、不变性;String对象一旦生成,则不能对它进行改变。String对象的这个特性可以泛指为不变模式,即一个对象的状态在对象被创建之后就不再发生变化。另外多说一点,不变模式主要作用在当一个对象需要被多线程共享,并且访问频繁时,可以省略同步和锁的等待时间,从而大幅度的提高系统的性能。

  2、针对常量池的优化;当两个string对象拥有相同的值的时候,他们只引用常量池中同一个拷贝。当同一个字符串反复出现时,可以大幅度的节省内存空间。

  3、类的final的定义。final类的String对象在系统中不可能有任何子类,这是对系统安全性的保护。

  String 类包括的方法可用于检查序列的单个字符、比较字符串、搜索字符串、提取子字符串、创建字符串副本并将所有字符全部转换为大写或小写等,熟练使用这些方法在企业开发中会有很大的帮助。

  • 截取子字符串

  截取子字符串是java中最常用的操作之一,在java中提供了两个截取子字符串的方法:

1 substring(int beginIndex, int endIndex)
2 substring(int beginIndex)

  查看substring(int beginIndex, int endIndex)的源码:

 1 public String substring(int beginIndex, int endIndex) {
 2         if (beginIndex < 0) {
 3             throw new StringIndexOutOfBoundsException(beginIndex);
 4         }
 5         if (endIndex > value.length) {
 6             throw new StringIndexOutOfBoundsException(endIndex);
 7         }
 8         int subLen = endIndex - beginIndex;
 9         if (subLen < 0) {
10             throw new StringIndexOutOfBoundsException(subLen);
11         }
12         return ((beginIndex == 0) && (endIndex == value.length)) ? this
13                 : new String(value, beginIndex, subLen);
14     }

  在方法的最后有一个返回一个新建的String对象,查看其构造函数:

1 public String(char value[], int offset, int count) {
2        this.value=value,
3        this.offset=offset,
4        this.count=count
5 }

  在源码中,这是一个包作用域的构造函数,其目的是为了能高效且快速的共享String内的char数组对象。但是这种通过偏移量来截取字符串的方法中,String原生内容的value数组会被复制到新的子字符串中。设想,如果子字符串长度很短,但是原来字符串长度却很长,那么截取的子字符串包含原生字符串中的所有内容,并占据了相应的内存空间,而仅仅通过偏移量和长度来决定自己的实际取值。这种方式提高了运算速度却浪费了空间,是一种以时间换空间的解决方案。

  实例代码:

 1 public class Test {
 2
 3     public static void main(String[] args) {
 4         List<String> hander = new ArrayList<String>();
 5         for (int i = 0; i < 100000; i++) {
 6             HugeStr str = new HugeStr();
 7             //ImpHugeStr str = new ImpHugeStr();
 8             hander.add(str.getSubString(0, 5));
 9         }
10     }
11
12 }
13
14 static class ImpHugeStr {
15             private String str = new String(new char[10000]);
16             public String getSubString(int begin,int end){//截取子字符串并且重新生成新的字符串
17                 return new String(str.substring(begin, end));
18             }
19 }
20 static class HugeStr {
21             private String str = new String(new char[10000]);
22             public String getSubString(int begin,int end){//截取子字符串
23                 return str.substring(begin, end);
24             }
25 }

  ImpHugeStr使用没有内存泄漏的String构造函数重新生成String对象,使得由subString()方法返回的,存在内存泄漏的String对象失去强引用,从而被垃圾回收机制当中垃圾回收,从而保证了系统内存的稳定。

  subString()之所以引起内存泄漏是因为使用了String(int offset,int count,char[] value)构造函数,此构造函数采用以空间换时间的策略。

  以上构造函数在java1.5中采用,在java1.6中采用以下构造函数:

 1 public String(char value[], int offset, int count) {
 2         if (offset < 0) {
 3             throw new StringIndexOutOfBoundsException(offset);
 4         }
 5         if (count < 0) {
 6             throw new StringIndexOutOfBoundsException(count);
 7         }
 8         // Note: offset or count might be near -1>>>1.
 9         if (offset > value.length - count) {
10             throw new StringIndexOutOfBoundsException(offset + count);
11         }
12         this.value = Arrays.copyOfRange(value, offset, offset+count);
13     }

  代码是采用this.value = Arrays.copyOfRange(value, offset, offset+count),使value失去强引用被垃圾回收,所有也不存在内存溢出的问题。

  • 字符串的分割和查找

  字符串的分割和查找也是字符串处理中最常用的方法之一,字符串分割是将原始字符串根据某一个分割符,切割成一组小的字符串。

  字符串的分割大致有三种方法:

  1、最原始的字符串分割:

  split()是最原始的字符串分割方法,但是它提供了非常强大的字符串分割功能。传入的参数可以是一个正则表达式,从而实现复杂逻辑的字符串分割。但是,对于简单字符串的风格,split()的性能却不尽如人意。所有在性能敏感的系统使用是不可取的。

  2、使用StringTokenizer类分割字符串:

  StringTokenizer是JDK提供的专门用于字符串分割子串的工具类。

  其构造函数:new StringTokenizer(String str,String delim),其中str参数为要处理的分割字符串,delim是分割符。当一个StringTokenizer对象生成后,可以通过nextToken()方法得到下一个要分隔的字符串,通过hasMoreTokens()方法可以知道是否有更多的子字符串需要处理。StringTokenizer的更多具体用法查看API。

  3、自己实现字符串分割方式:

  通过使用String对象的两个方法:indexOf()和subString()。前面提到subString()是通过以空间换时间的策略,它的执行速度很快,同时indexOf也是一个执行效率特别快的方式。

  实现代码:

1 String str="";//待分割字符串
2 while(true){
3      String subStr = null;
4      int j = str.indexOf(";");//分割符
5      if(j<0) break;
6      subStr = str.subString(0,j);//截取子字符串
7      str = str.subString(j+1);//剩下待截取子串
8 }

  三种方式的比较,第一种split功能强大,但是效率最差;第二种StringTokenizer的效率由于split,因此可以使用StringTokenizer的地方一般尽量使用StringTokenizer;第三种执行效率最好,但是可读性比较差。

  另外,String对象还提供了一个charAt(int index)方法,它返回指定字符串中位于index的字符,它的功能和indexOf()相反,但是它的执行效率同样十分高。例如,经常在项目组遇到判断字符串以XX开头或XX结尾的问题,我们第一想到的是String对象提供的startWith()和endWith()方法,如果改用charAt()方法实现,效率会快很多。例如:"abcd".startWith("abc"),改为"abcd".charAt(1) == "a"&&"abcd".charAt(1) == "b"&&"abcd".charAt(1) == "c"在大量使用的场景下会提高系统效率。

  • StringBuffer和StringBuilder

  String对象是不可变对象,在需要对string对象进行修改操作时,string对象总是会生成新的对象,所有性能会比较差。因此,JDK就提供了专门用于创建和修改字符串的工具,就是StringBuffer和StringBuilder。

  1、String常量的累计操作:

  String对象具有不可变性,因此,一旦String对象实例生成,就不能再改变。如下代码:

1 String str = "abc"+"de"+"f"+"gh";

  首先,"abc"和"de"两个字符串生成"abcde"对象,再依次生成"abcdef"和"abcdefgh"对象,理论上这样做的效率不会高。

  通过StringBuilder实现上诉功能:

1 StringBuffer sb = new StringBuffer();
2 ab.append("abc");
3 ab.append("de");
4 ab.append("f");
5 ab.append("gh");

  对于静态字符串的连接操作,java在编译时会进行彻底的优化,将多个连接操作的字符串在编译时生成一个单独长的字符串,因此效率会高。

  2、String变量的累加操作:

  String对变量字符串的累加:

1 String str1 = "abc";
2 String str2 = "de";
3 String str3 = "f";
4 String str4 = "gh";
5 String str = str1+str2+str3+str4;

  java编译时,会会对字符串处理进行一定的优化,对于变量字符串的累加,使用了StringBuilder对象来实现字符串的累加。但是在写代码的时候,建议显示的使用StringBuffer或StringBuilder进行优化。另外在java中类似于"+="或"+"的方法效率低于String对象的concat()方法,而concat()方法又远远低于StringBuilder类。因此,在项目中尽量使用StringBuilder来提升效率。

  3、StringBuffer和StringBuilder的选择

  StringBuffer对几乎所有方法都做了同步,而StringBuilder几乎没做任何同步,同步方法需要消耗一定的系统资源,因此StringBuilder的效率要好于StringBuffer。但是在多线程环境下,StringBuilder无法保证线程安全,因此在不考虑线程安全的情况下使用性能较好的StringBuilder,若系统有线程安全的要求则使用StringBuffer。

  另外,StringBuffer和StringBuilder都可以设置容量大小。

 1 void expandCapacity(int minimumCapacity) {
 2         int newCapacity = value.length * 2 + 2;//扩大容量
 3         if (newCapacity - minimumCapacity < 0)
 4             newCapacity = minimumCapacity;
 5         if (newCapacity < 0) {
 6             if (minimumCapacity < 0) // overflow
 7                 throw new OutOfMemoryError();
 8             newCapacity = Integer.MAX_VALUE;
 9         }
10         value = Arrays.copyOf(value, newCapacity);//数组复制
11     }

  预先评估出容量的大小,能有效的减少数组扩容的操作,从而提升系统性能。

时间: 2024-08-05 19:35:26

java中的字符串简介,字符串的优化以及如何高效率的使用字符串的相关文章

JAVA中的正则表达式简介

正则表达式 作用:验证某个字符串是否符合某种规则 代码: //1.正则表达式验证的规则 String reg = "正则表达式": //Pattern,Matcher在java.util包中 //2.编译正则表达式规则 Pattern pattern = Pattern.compile(reg); //3.规则和字符串进行验证比较 Matcher m = pattern.matcher("比较的字符串"); //4.验证 //.matches()/全匹配..fing

java中的xml简介与DOM解析xml

1. xml简介 XML:指可扩展标记语言, Extensible Markup Language:类似HTML.XML的设计宗旨是传输数据,而非显示数据. 一个xml文档实例: 1 <?xml version="1.0" encoding="UTF-8"?> 2 <company name="Tencent" address="深圳市南山区"> 3 <department deptNo=&quo

java中System类简介(转)

上次面试中遇到的一个问题,问到System.out.println()中的out是不是内部类[不是内部类],当时就给问蒙了,直观感觉out应该是System类的一个属性,跟内部类有什么关系?而且之前整理IO部分的时候记得有个PrintStream的类用于标准输出的,但是从没看过System的源码,也不敢随便再说了.后来看了下源码,发现的确是PrintStream,可能当时想问的是内部类的用法吧(真心感觉面试待靠缘分,很多面试官喜欢引导着问问题,方式很好,但很多时候可能让面试者搞不清你到底想问什么

Java中Properties类

Java中Properties类简介 知识学而不用,就等于没用,到真正用到的时候还得重新再学. Properties类继承自Hashtable,如下: 它主要用于读取Java的配置文件,由于配置文件中的很多变量时经常改变的,通过这个类可以让用户脱离程序本身去修改相关的变量配置.在Java中,其配置文件常为.properties文件,格式为文本文件,内容的格式为“键=值”的格式,#打头的是注释行,Properties会忽略注释.允许只有key没有value,没有value时,value会被set成

Java中compareto的用法

java中的compareto方法,返回参与比较的前后两个字符串的asc码的差值,看下面一组代码 String a="a",b="b"; System.out.println(a.compareto.b); 则输出-1: 若a="a",b="a"则输出0: 若a="b",b="a"则输出1: 单个字符这样比较,若字符串比较长呢?? 若a="ab",b="b&

9.JAVA中的正则表达式

一.JAVA中的正则表达式 1.概念:以某种特定的方式描述字符串 1.Java中正则表达式的规则 ?          #{0,1}-?有一个-或者没有 \\           #表示一个”\" \\d         #表示一个数字 \\D       #非数字 f\\W+    #表示f开头的单词 \\W       #表示一个非单词字符 \\w       #表示一个单词字符[0-9A-Za-z-]包含’-’ \\s        #空白符号 \\S       #非空白符号 (-|\\

Java中compareto的用法。

java中的compareto方法,返回参与比较的前后两个字符串的asc码的差值,看下面一组代码 String a="a",b="b"; System.out.println(a.compareto.b); 则输出-1: 若a="a",b="a"则输出0: 若a="b",b="a"则输出1: 单个字符这样比较,若字符串比较长呢?? 若a="ab",b="b&

java中的compareto用法

java中的compareto方法,返回参与比较的前后两个字符串的asc码的差值,看下面一组代码 String a="a",b="b"; System.out.println(a.compareto.b); 则输出-1: 若a="a",b="a"则输出0: 若a="b",b="a"则输出1: 单个字符这样比较,若字符串比较长呢?? 若a="ab",b="b&

Java中常用加减密方式

1.加密概述: 加密就是是以某种特殊的算法改变原有的信息数据,使得未授权的用户即使以获得了加密的信息,但因不知解密方式,仍无法了解信息的内容.大体上又分为双向加密和单向加密. 2.单项加密 2.1.概述: 单向加密又称为不可逆加密算法,在加密过程中不使用密钥,明文由系统加密成密文,密文无法破解,一般都是采用验证的方式,具体是:在验证过程中,重新输入明文,并经过同样的加密算法后,得到相同的密文.单向加密广泛用于口令加密. 2.2.特点: (1)对同一消息反复执行加密得到相同的密文: (2)加密算法