一、字符串

字符串效果上是字符数组(char[]),但底层是字节数组(byte[])

String类 声明与初始化

String字符串的声明与初始化有如下两种:
① String s1=new String(“abc”);

中创建字符串对象abc

如果再String s2=new String(“abc”);则会再次在堆中创建abc对象;
也就是说s1、s2是不同的两个对象;

String s1=new String(“abc”);语句执行过程可以分为两部分;
第一部分:创建对象,即 new String(“abc”) ,调用了String类的构造函数
第二部分:赋值,即String s1=,定义了一个名为s1的String变量,并将一个String类型的引用赋值给s1;
String 变量=对象的引用;

对于该过程,若常量池中没有abc对象,则会先在常量池中先创建abc对象,再在堆中创建abc对象,即该过程创建了两个对象;
若常量池中已经存在abc对象,则直接在堆中创建一个新的abc,该过程只创建了一个对象;

② String s3=“abc”;

常量池中创建字符串对象abc(常量池中的对象是共享的)

如果再String s4=“abc”;则不会在常量池中再次创建abc对象;
所以s3、s4是相等的,是同一个对象;

String s3=“abc”;

对于该过程会先在常量池中查找是否有相同的字符串被定义,其判断的依据是equals()方法;
若常量池中已有abc,则直接将该对象引用给变量s3; 若没有则会在常量池中先创建abc对象,再赋值;

对于String类用""(双引号)声明的对象,都在常量池中
new出来的东西都是放在堆中的
变量都存放在栈中,这些变量指向堆或常量池中的对象


String s=null与String s="“是不相同的
前者s不指向任何一个字符串;
后者指向字符串”"只不过该字符串的值是空的;


什么是字符串常量池?

字符串常量池位于堆内存中,专门用来存储字符串常量,可以提高内存的使用率,避免开辟多块空间存储相同的字符串,在创建字符串时 JVM 会首先检查字符串常量池,如果该字符串已经存在池中,则返回它的引用,如果不存在,则实例化一个字符串放到池中,并返回其引用。


"==" 与 equals()与hashCode

对于基本数据类型:== 就是比较他们的值是否相等;

对于引用类型:
①、==
比较的是两个变量的引用是否相同,即它们指向的对象在内存中的首地址,也就是比较两个变量是否指向同一个对象
②、equals()
equals()方法是Object类提供的一个方法;
每一个Java类都继承Object,都具有equals()这个方法,可以进行重写;

对于没有重写equals()的类中,equals()实质上就是==,比较的是变量的值(保存的对象的引用)是否相等;

对于重写了equals()的类中,如String:
其作用是比较变量的指向对象的内容是否相等;

③、hashCode()
hashCode()也是Object类提供的;
其作用也是用来比较俩个对象是否相等,该方法会根据对象在内存中的地址返回一个int值,根据这个int值判断这两个对象是否相等;
hashCode() 的默认行为是对堆上的对象产生独特值。如果没有重写 hashCode(),则该 类的两个对象无论如何都不会相等(即使这两个对象有相同的数据);


一般在自定义类中,如果重写了equals()方法,也要重写hashCode()方法,否则会违反Object.hashCode的通用约定,导致该类无法与所有基于散列值(hash)的集合类结合在一起正常使用,如HashMap、HashSet等;

拿HashMap来说,由于key是不可以重复的,加入元素时就要先使用hashCode方法(指的是存放在集合中key位置的对象的hashCode方法、而不是HashMap里面的)返回一个int值,如果该int值与集合中已有的某元素的hashcode值相同,则再使用equals方法,若返回false,就可以判定key没有重复,可以放进集合,若返回true则说明这两个元素相同,不可以放入集合;

hashCode与equals重写后应有如下关系:
如果x.equals(y)返回true,那么它们hashCode方法返回的值一定也要相等;但x.equals(y)返回false,hashCode的值不一定要是false;

也就是说:

equals为true,hashCode一定要相等;
hashCode不相等,equals一定为false;

String s1="a";
String s2="b";
String s3=s1+s2;
final String s4="a";
final String s5="b";
String s6=s4+s5;
String s7="ab";
final String s8=s4+s5;
String s9=new String("a");
String s10=new String("b");
String s11=s9+s10;
System.out.println(s1==s4);//true
System.out.println(s3==s6);//false
System.out.println(s7==s6);//true
System.out.println(s7==s3);//false
System.out.println(s7==s8);//true
System.out.println(s7==s11);//false
//final修饰的String对象效果上等同于双引号创建的对象,存在于栈内

String、StringBuffer、StringBuilder区别

①、String:
可被共享、不可变类,String对象一旦被创建,其值将不能被修改;

String a="abc";
a="abc"+"def";(a="abcdef")
看似变量a发生改变,由abc变成abcdef,
但实际上abcdef是一个新的对象,
变量a由指向”abc“变成指向”abcedf“,并不是String对象发生改变;

其修改原理:
 1.首先创建一个StringBuilder对象
 StringBuilder sb=new StringBuilder(a);
 2.再调用该类的append()方法
 sb.append("def");
 3.然后调用toString()方法将结果返回给b变量a;

【可见String类在需要大量修改字符串的情况下,非常耗费资源,程序执行效率低】

②、StringBuffer
可变类、线程安全,适合在多线程中使用;

它可以被修改,不会在修改过程中创建新的对象;
只能通过构造方法来初始化:StringBuffer s=new StringBuffer(“abc”);

③、StringBuilder
可变类、线程不安全,适合在单线程中使用;

常用方法:
1、StringBuilder( String s):构造方法,也可用来将String类型变量转换成StringBuilder类型;
2、String toString( ):用来将StringBuilder类型变量转换成String类型;
3、StringBuilder append(content):追加数据并返回数据本身;追加的内容可以是任何类型的数据,int、double等;
4、StringBuilder reverse():反转字符串
5、insert(int x,content):i是插入的位置,content为内容,同样可以是任意类型;
6、delete(int start,int end):删除从start开始到end-1的字符,即【start,end)

总结:
执行效率方面:StringBuilder>StringBuffer>String
一般情况下,数据量小优先考虑String;
如果在线程情况下,优先考虑StringBuilder;
如果在线程情况下,优先考虑StringBuffer;

小拓展:StringTokenizer是用来分割字符串的工具类;

String常用的方法

1、equals():比较两个字符串是否相等
2、equalsIgnoreCase( ):忽略大小写的两个字符串是否相等
3、toString():转换成String类型,通常派生类会覆盖Object里的toString()方法。
4、length():返回字符串长度;
5、endsWith(String str):是否以str结尾;
6、startsWith(String str):是否以str开始;

7、byte[] getBytes():将String转化为byte数组;
8、char[] toCharArray():将String转化为char数组;

9、String[] split(String sign):分隔字符串符,参数(分隔符)可以是字符串也可以是正则表达式;

  • 如果用“|”作为分隔的话,必须是如下写法,String.split("\|")
  • 如果用“.”作为分隔的话,必须是如下写法,String.split("\.")
    “.”、“|”、"" 和"+“都是转义字符,必须得加”\";
    重载:String[] split(String sign,int limit):在分割的基础上,限定了拆分的次数,即拆分后最大的段数为limit;

如果在一个字符串中有多个分隔符,可以用“|”作为连字符,比如,“acount=? and uu =? or
n=?”,把三个都分隔出来,可以用String.split(“and|or”);
例如:String[] aa =“aaa|bbb|ccc”.split("\|");
for (int i = 0 ; i <aa.length ; i++ ) {
System.out.println("–"+aa[i]); }

10、subString():截取字符串中的一段字符串
①str=str.substring(int beginIndex);【beginIndex,最后】
将beginIndex开始到最后字符串赋值给str;
②str=str.substring(int beginIndex,int endIndex);【beginIndex,endIndex)
截取str中从beginIndex开始至endIndex结束时的字符串,并将其赋值给str;

11、charAt(int i):返回指定索引i处char值

12、toLowerCase():将所有在此字符串中的字符转化为小写
13、toUpperCase():将所有在此字符串中的字符转化为大写

14、indexOf(String s):指出 String 对象内子字符串的开始位置
①int indexOf(String str) :返回第一次出现的指定子字符串在此字符串中的索引,若没有则返回-1。
②int indexOf(String str, int startIndex):从指定的索引处开始,返回第一次出现的指定子字符串在此字符串中的索引。
③int lastIndexOf(String str) :返回在此字符串中最后出现的指定子字符串的索引。
④int lastIndexOf(String str, int startIndex) :从指定的索引处开始向后搜索,返回在此字符串中最后一次出现的指定子字符串的索引。

注意:如果没有找到子字符串,则返回-1。
如果 startindex 是负数,则 startindex 被当作零。如果它比最大的字符位置索引还大,则它被当作最大的可能索引。字符串下标与数组下标一样从0开始;

例如:String s = “xXccxxxXX”;
// 从头开始查找是否存在指定的字符 //结果如下
System.out.println(s.indexOf(“c”)); //2
// 从第四个字符位置开始往后继续查找,包含当前位置
System.out.println(s.indexOf(“c”, 3)); //3
//若指定字符串中没有该字符则系统返回-1
System.out.println(s.indexOf(“y”)); //-1
System.out.println(s.lastIndexOf(“x”)); //6

15、trim():返回字符串副本,忽略前导空格和尾部空格;
16、replace和replaceAll
①replace的参数是char和CharSequence,即可以支持字符的替换,也支持字符串的替换(CharSequence即字符串),更换所有匹配的字符;

②replaceAll的参数是regex,即基于规则表达式的替换,比如:可以通过replaceAll("\d", “")把一个字符串所有的数字字符都换成星号;

相同点:都是全部替换,即把源字符串中的某一字符或字符串全部换成指定的字符或字符串;
不同点:
①replaceAll支持正则表达式,因此会对参数进行解析(两个参数均是),如replaceAll("\d", “”),而replace则不会,replace(”\d","*")就是替换"\d"的字符串,而不会解析为正则。
②“\”在java中是一个转义字符,所以需要用两个代表一个。例如System.out.println( “\” ) ;只打印出一个""。但是“\”也是正则表达式中的转义字符,需要用两个代表一个。所以:\被java转换成\,\又被正则表达式转换成\,因此用replaceAll替换“\”为"",就要用replaceAll("\","\\"),而replace则replace("","\")。
③如果只想替换第一次出现的,可以使用replaceFirst(),这个方法也是基于规则表达式的替换,但与replaceAll()不同的是,只替换第一次出现的字符串。
17、compareTo():按字典顺序比较两个字符串,比较基于字符串中各个字符的Unicode值;

String s1="a";String s2="b";
s1.compareTo(s2);/返回-1
a-b=97-98=-1;

18、matches(String regex):用于字符串与某一正则表达式是否匹配;

正则表达式

正则表达式通常用于判断语句中,用来检查某一字符串是否满足某一格式。正则表达式是含有一些具有特殊意义字符的**字符串;**这些特殊字符称为表达式的元字符。正则表达式是对字符串操作的一种逻辑公式;

元字符

正则表达式写法

意义

.

.

代表任意一个字符

\d

\\d

代表0~9任意一个数字

\D

\\D

代表任何一个非数字字符

\s

\\s

代表空白字符

\S

\\S

代表非空白字符

\w

\\w

代表可作为标识符的字符

\W

\\W

代表不可作为标识符的字符

\p{Lower}

\\p{Lower}

代表小写字母a~z

\p{Upper}

\\p{Upper}

代表大写字母A~Z

\p{Alpha}

\\p{Alpha}

代表字母

\p{Digit}

\\p{Digit}

代表数字0~9

\p{Alnum}

\\p{Alnum}

代表字母或数字

.是代表任何一个字符,若要使用普通意义的.需要使用转义字符\,即"\\."

【方括号[]括起来的若干个字符表示一个元字符,如:[^123]表示1、2、3之外的任何字符,[a-zA-Z]表示任何一个英文字母

限定修饰符

意义

示例


0次或1次

A?

*

0次或多次

A*

+

一次或多次

A+

{n}

出现正好n次

A{3}

{n,}

出现至少n次

A{3,}

{n,m}

出现n~m次

A{2~3}

组合在一起则:\\d+\\p{Alpha}可以把元字符加限定修饰符看成一个整体,方便阅读, 将其看成\\d+与\\p{Alpha}两部分,表示一个或多个数字后面加上一个字母的字符串

String regex="\\w+@\\w+(\\.\\w{2,3})*\\.\\w{2,3}"
String E-mail="anna@tom.cn.com";
boolean x=E-mail.matches(regex);
判断字符串变量是否是一个规范的E-mail格式;

String regex="\\d{5,10}@qq\\.com"
boolean x=Q-mail.matches(regex);
判断字符串变量是否是一个规范的QQ邮箱格式;注意"."在正则表达式中要写成"\\."