【前言】代码的注释较多,因为还有很多细节的知识点大都写在代码的注释中,我觉得将知识点和代码结合起来可能会更容易理解,所以很多时候还是结合代码来看会更好。
认识String类
C语言当中是没有字符串类型的,但是在Java当中是有字符串类型的。
如何定义一个字符串?
public static void main(String[] args) {
String str1="hello";
System.out.println(str1);
String str2=new String("abcdef");
System.out.println(str2);
char[] val={'h','i','g','k','l','m'};
String str3=new String(val);
System.out.println(str3);
}
字符串的比较
public static void main(String[] args) {
String str1="hello";
String str2=new String("hello");
System.out.println(str1==str2);
String str3="hello";
System.out.println(str1==str3);
}
上述代码为false和true的原因:代码所对应的内存底层的位置如图所示
public static void main(String[] args) {
String str1="hello";
String str2="hel"+"lo";
System.out.println(str1==str2);
String str3=new String("hel")+"lo";
System.out.println(str3==str1);
}
上述代码为false和true的原因:代码所对应的内存底层的位置如图所示
字符串常量池
字符串常量池的特点:
1、在JDK1.7开始,这个常量池被挪到堆里面了
2、在常量池里面所存放的数据是不重复的
3、字符串常量池并不是用来放常量的,它只放字符串常量
代码示例:
public static void main4(String[] args) {
String str1="hello";
String str2=str1;
System.out.println(str1);
System.out.println(str2);
str1="AAA";
System.out.println(str1);
System.out.println(str2);
}
public static void func(String str,char[] array) {
str="aaaaaa";
array[0]='g';
}
public static void main5(String[] args) {
String str1="hello";
char[] val={'a'};
System.out.println(str1);
System.out.println(Arrays.toString(val));
func(str1,val);
System.out.println(str1);
System.out.println(Arrays.toString(val));
}
字符串比较相等
public static void main6(String[] args) {
String str1="hello";
String str2=new String("hello");
System.out.println(str1==str2);
System.out.println(str1.equals(str2));
String str3="hello";
System.out.println(str1==str3);
System.out.println(str1.equals(str3));
}
public static void main7(String[] args) {
String str1=null;
String str2=new String("hello");
System.out.println(str2.equals(str1));
System.out.println(str1.equals(str2));
}
public static void main8(String[] args) {
String str1="hello";
String str2=new String("hello");
System.out.println(str1==str2);
}
public static void main8(String[] args) {
String str1="hello";
String str2=new String("hello").intern();
System.out.println(str1==str2);
}
为false的原因如图:
为true的原因如图:
理解字符串不可变
public static void main9(String[] args) {
String str="hello";
str=str+"world";
str+="!!!";
System.out.println(str);
for(int i=0;i<1000;i++){
str+=1;
}
}
public static void main11(String[] args) throws NoSuchFieldException, IllegalAccessException {
String str="Hello";
Class cl=String.class;
Field field = cl.getDeclaredField("value");
field.setAccessible(true);
char[] value=(char[]) field.get(str);
value[0]='h';
System.out.println(str);
}
public static void main10(String[] args) {
String str = "Hello";
str = "h" + str.substring(1);
System.out.println(str);
}
总结:
1、当遇到字符串,要明白它的内存结构;
2、字符串的不可变是什么意思;
3、在函数的内修改它的指向并不会影响实参的指向。
字符、字节与字符串
字符与字符串
字符串内部包含一个字符数组,String 可以和 char[] 相互转换.。
下面是字符和字符串之间的相互转换用到的方法和代码示例
1??将字符数组中的所有内容变为字符串:使用构造方法,public String(char value[])
public static void main1(String[] args) {
char[] value={'q','w','e','r','t','y'};
String str=new String(value);
System.out.println(str);
}
2??将部分字符数组中的内容变为字符串:使用构造方法public String(char value[],int offset,int count)
public static void main(String[] args) {
char[] value={'q','w','e','r','t','y'};
String str=new String(value,1,3);
System.out.println(str);
}
3??取得指定索引位置的字符,索引从0开始:使用普通方法 public char charAt(int index)
public static void main(String[] args) {
String str2="hello";
char ch=str2.charAt(0);
System.out.println(ch);
}
4??将字符串变为字符串数组返回:使用普通方法 public char[] toCharArray()
public static void main(String[] args) {
String str2="hello";
char[] value2= str2.toCharArray();
System.out.println(Arrays.toString(value2));
}
代码示例: 给定字符串一个字符串, 判断其是否全部由数字所组成.
思路: 将字符串变为字符数组而后判断每一位字符是否是" 0 “~”‘9’"之间的内容,如果是则为数字.
public static boolean isNumber(String str) {
for(int i=0;i<str.length();i++){
char ch=str.charAt(i);
if(ch<'0'||ch>'9'){
return false;
}
}
return true;
}
public static void main(String[] args) {
String str="1234567";
boolean flg=isNumber(str);
System.out.println(flg);
}
字节与字符串
字节常用于数据传输以及编码转换的处理之中,String 也能方便的和 byte[] 相互转换。
字节与字符串的相互转换
1??将字节数组变为字符串:使用构造函数,public String (byte bytes[])
public static void main(String[] args) {
byte[] bytes={97,98,99,100};
String str=new String(bytes);
System.out.println(str);
}
2??将部分字节数组中的内容变为字符串:使用构造函数,public String (byte bytes[],int offset,int length)
3??将字符串以字节数组的形式返回:使用普通函数,public byte[] getBytes()
public static void main(String[] args) {
String str1="abcdef";
byte[] bytes1=str1.getBytes();
System.out.println(Arrays.toString(bytes1));
}
4??编码转换处理:使用普通方法,public byte[] getBytes(String charsetName) throws UnsupportedEncodingException
public static void main(String[] args) throws UnsupportedEncodingException {
String str2="呵呵";
byte[] bytes2=str2.getBytes("utf-8");
System.out.println(Arrays.toString(bytes2));
}
小结:
那么何时使用 byte[], 何时使用 char[] 呢?
- byte[] 是把 String 按照一个字节一个字节的方式处理(如视频、音频), 这种适合在网络传输, 数据存储这样的场景下使用(). 更适合
针对二进制数据来操作.
- char[] 是吧 String 按照一个字符一个字符的方式处理, 更适合针对文本数据来操作, 尤其是包含中文的时候。
回忆概念: 文本数据 vs 二进制数据
一个简单粗暴的区分方式就是用记事本打开能不能看懂里面的内容.
如果看的懂, 就是文本数据(例如 .java 文件), 如果看不懂, 就是二进制数据(例如 .class 文件
字符串常见操作
字符串比较
上面使用过String类提供的equals()方法,该方法本身是可以进行区分大小写的相等判断。除了这个方法之外,String类还提供有如下的比较操作:
NO | 方法名称 | 类型 | 描述 |
---|
1 | public boolean equals(Object anObject) | 普通 | 区分大小写比较 | 2 | public boolean equalsIanoreCase(String anotherString) | 普通 | 不区分大小写比较 | 3 | public int compareTo(String anotherString) | 普通 | 比较两个字符串的大小关系 |
代码示例:
public static void main(String[] args) {
String str1="ABCDEF";
String str2="abcdef";
System.out.println(str1.equals(str2));
System.out.println(str1.equalsIgnoreCase(str2));
String str3="abc";
System.out.println(str3.compareTo(str2));
System.out.println("巴".compareTo("王"));
}
false
true
-3
-5527
在String类中compareTo()方法是一个非常重要的方法,该方法返回一个整型,该数据会根据大小关系返回三类内容:
- 相等:返回0.
- 小于:返回内容小于0.
- 大于:返回内容大于0
compareTo()比较的代码示例:
System.out.println("A".compareTo("a"));
System.out.println("a".compareTo("A"));
System.out.println("A".compareTo("A"));
System.out.println("AB".compareTo("AC"));
System.out.println("刘".compareTo("杨"));
compareTo()是一个可以区分大小关系的方法,是String方法里是一个非常重要的方法。
字符串的比较大小规则, 总结成三个字 “字典序” 相当于判定两个字符串在一本词典的前面还是后面. 先比较第1个字符的大小(根据 unicode 的值来判定), 如果不分胜负, 就依次比较后面的内容.
字符串查找
从一个完整的字符串之中可以判断指定内容是否存在,对于查找方法有如下定义:
NO | 方法名称 | 类型 | 描述 |
---|
1 | public boolean contains(CharSequence s) | 普通 | 判断一个子字符串是否存在 | 2 | public int indexOf(String str) | 普通 | 从头开始查找指定字符串的位置,查到了返回位置的开始索引,如果查不到返回-1 | 3 | public int indexOf(String str,int formIndex) | 普通 | 从指定位置开始查找子字符串的位置 | 4 | public int lastIndexOf(String str) | 普通 | 由后向前查找子字符串的位置 | 5 | public int lastIndexOf(String str,int fromIndex) | 普通 | 从指定位置由后向前查找 | 6 | public boolean startsWith(String prefix) | 普通 | 判断是否以指定字符串开头 | 7 | public boolean startsWith(String prefix,int toffset) | 普通 | 从指定位置开始判断是否以指定字符串开头 | 8 | public boolean endWith(String suffix) | 普通 | 判断是否以指定字符串结尾 |
1??contains()方法:判断一个子字符串是否存在
public static void main(String[] args) {
String str="aaabbbcccddd";
boolean flg= str.contains("abc");
System.out.println(flg);
boolean flg1= str.contains("aaa");
System.out.println(flg1);
}
boolean flg= str.contains(“abc”)这里,为什么可以传字符串呢?
因为contains的返回值是boolean类型,参数是CharSequence
而String的源码实现了CharSequence 接口,所以可以这样写!
2??indexOf()方法
String str1="aaabcabcabcd";
int ret1=str1.indexOf("abc");
System.out.println(ret1);
String str2="aaabcabcabcd";
int ret2=str2.indexOf("abc",6);
System.out.println(ret2);
2
8
3??lastIndexOf()方法
String str3="abbcabcabcd";
int index=str3.lastIndexOf("abc");
System.out.println(index);
int index1=str3.lastIndexOf("bc",3);
System.out.println(index1);
7
2
4??startsWith()方法
String str4="abbcabcabcd";
boolean ret3=str4.startsWith("ab");
System.out.println(ret3);
boolean ret4=str4.startsWith("abc");
System.out.println(ret4);
boolean ret5=str4.startsWith("abc",1);
System.out.println(ret5);
boolean ret6=str4.startsWith("abc",4);
System.out.println(ret6);
true
false
false
true
5??endsWith()方法:判断是否以指定字符串结尾,它只能有一个参数。
String str5="abbcabcabcd";
boolean ret7=str4.endsWith("ab");
System.out.println(ret7);
boolean ret8=str4.endsWith("cd");
System.out.println(ret8);
false
true
面试问题:在主串当中查找子串。把这个东西叫做字符串匹配/查找算法。
他有两种算法:朴素算法和KMP算法
如果让你自己实现一个indexof方法,你该如何实现?
这个问题考的就是这两种算法,自己研究这两种算法并写出代码。
字符串替换
使用一个指定的新的字符串替换掉已有的字符串数据,可用的方法如下:
NO | 方法名称 | 类型 | 描述 |
---|
1 | public String replaceAll(String regex,String replacement) | 普通 | 替换所有的指定内容 | 2 | public String replaceFirst(String regex,String replacement) | 普通 | 替换首个内容 |
public static void main(String[] args) {
String str1="ababcabcd";
String ret=str1.replace('a','A');
System.out.println(ret);
String ret1=str1.replaceAll("ab","AB");
System.out.println(ret1);
String ret2=str1.replaceFirst("ab","WW");
System.out.println(ret2);
}
注意事项: 由于字符串是不可变对象, 替换不修改当前字符串, 而是产生一个新的字符串.
字符串拆分
可以将一个完整的字符串按照指定的分隔符划分为若干个子字符串。
可用方法如下:
NO | 方法名称 | 类型 | 描述 |
---|
1 | public String[] split(String regex) | 普通 | 将字符串全部拆分 | 2 | public String[] split(String regex,int limit) | 普通 | 将字符串部分拆分,该数组长度就是limit极限 |
split这个方法的返回值是一个数组,所以需要拿数组来接收。
public static void main(String[] args) {
String str="abc de f";
String[] strings= str.split(" ");
for (String s:strings) {
System.out.println(s);
}
String str1="abc de f";
String[] strings1= str.split(" ",2);
for (String s:strings1) {
System.out.println(s);
}
}
abc
de
f
abc
de f
String str2="192.168.1.1";
String[] strings2=str2.split("");
for (String s:strings2) {
System.out.println(s);
}
String[] strings3=str2.split( " ");
for (String s:strings3) {
System.out.println(s);
}
1
9
2
.
1
6
8
.
1
.
1
192.168.1.1
错误拆分代码示例:如果直接以点号拆分,在这里是错误的,会什么也不打印,为什么呢?
String str2="192.168.1.1";
String[] strings4=str2.split( ".");
for (String s:strings4) {
System.out.println(s);
}
通过split的源码可知,对于这些特殊的符号的处理,需要加双斜杠才可以进行字符串拆分。
String str2="192.168.1.1";
String[] strings5=str2.split( "\\.");
for (String s:strings5) {
System.out.println(s);
}
String str3="192\\168\\1\\1";
String[] strings6=str3.split( "\\\\");
for (String s:strings6) {
System.out.println(s);
}
String str4="192*168*1*1";
String[] strings7=str4.split( "\\*");
for (String s:strings7) {
System.out.println(s);
}
192
168
1
1
192
168
1
1
192
168
1
1
如何分割多次?
public static void main(String[] args) {
String str = "name=zhangsan&age=18";
String[] strings1 = str.split("&");
for (String s1 : strings1) {
String[] strings2 = s1.split("=");
for (String s2 : strings2) {
System.out.println(s2);
}
}
}
name
zhangsan
age
18
△练习题:字符串连接
题目描述:借用任何字符串库函数实现无冗余的接受两个字符串,然后把他们无冗余的连接起来。
输入描述:每一行包括两个字符串,长度不超过100.
输出描述:可能有多组测试数据,对于每组数据,借用任何字符串库函数实现无冗余的接受两个字符串,然后把他们无冗余的连接起来。输出连接后的字符串。
import java.util.Scanner;
public class Main{
public static String func(String str){
String[] strings=str.split(" ");
String ret="";
for(String s:strings){
ret+=s;
}
return ret;
}
public static void main(String[] args ){
Scanner scan=new Scanner(System.in);
while(scan.hasNext()){
String str=scan.nextLine();
String ret=func(str);
System.out.println(ret);
}
}
}
abc de
abcde
字符串截取
从一个完整的字符串之中截取出部分内容。可用方法如下
NO | 方法名称 | 类型 | 描述 |
---|
1 | public String substring(int beginIndex) | 普通 | 从指定索取截取到结尾 | 2 | public String substring(int beginIndex,int endIndex) | 普通 | 截取部分内容 |
查看帮助手册搜索String:
public static void main(String[] args) {
String str1="ababcabcd";
String ret= str1.substring(1);
System.out.println(ret);
String ret1=str1.substring(1,4);
System.out.println(ret1);
}
注意事项:
1.索引从0开始.
2.注意前闭后开区间的写法, substring(0, 5) 表示包含 0 号下标的字符, 不包含 5 号下标.
其他操作方法
NO | 方法名称 | 类型 | 描述 |
---|
1 | public String trim() | 普通 | 去掉字符串中的左右空格,保留中间空格 | 2 | public String toUpperCase() | 普通 | 字符串转大写 | 3 | public String toLowerCase() | 普通 | 字符串转小写 | 4 | public native String intern() | 普通 | 字符串入池操作 | 5 | public String concat(String str) | 普通 | 字符串连接,等同于”+“,不入池 | 6 | public int length() | 普通 | 取得字符串长度 | 7 | public boolean isEmpty() | 普通 | 判断是否为空字符串,但不是null,而是长度为0 |
1??trim()方法,去掉字符串中的左右空格,保留中间空格
public static void main(String[] args) {
String str1=" aba bacbcd ";
String ret= str1.trim();
System.out.println(ret);
}
2??toUpperCase()方法,将字符串转大写
? toLowerCase方法,将字符串转小写
代码示例:这两个方法只对字母有效
public static void main(String[] args) {
String str2="aBcDef巴";
String ret1=str2.toLowerCase();
System.out.println(ret1);
String ret2=str2.toUpperCase();
System.out.println(ret2);
}
abcdef巴
ABCDEF巴
3??intern()方法,字符串入池操作
5?? length()方法,取得字符串长度
public static void main(String[] args) {
String str2="aBcDef巴";
String ret2=str2.toUpperCase();
System.out.println(ret2.length());
int[] array={1,2,3,4,5};
System.out.println(array.length);
}
注意两个length的区别,数组长度使用数组名称.length属性,而String中使用的是length()方法
6??isEmpty()方法,判断是否为空字符串,但不是null,而是长度为0
String string1="";
String string2=null;
练习题1:首字母大写
String类并没有提供首字母大写操作,需要自己实现。
代码示例:
public static void main(String[] args) {
System.out.println(fistUpper("yuisama"));
System.out.println(fistUpper(""));
System.out.println(fistUpper("a"));
}
public static String fistUpper(String str) {
if ("".equals(str)||str==null) {
return str ;
}
if (str.length()>1) {
return str.substring(0, 1).toUpperCase()+str.substring(1) ;
}
return str.toUpperCase() ;
}
Yuisama
A
练习题2:实现字符串逆置
库里面是没有让字符串逆置的方法的,需要自己手动实现。
代码示例:
public static String reverse(String str,int begin,int end){
char[] value=str.toCharArray();
while(begin<end){
char tmp=value[begin];
value[begin]=value[end];
value[end]=tmp;
begin++;
end--;
}
return String.copyValueOf(value);
}
public static void main(String[] args) {
String str="abcdefg";
String ret= reverse(str,0,str.length()-1);
System.out.println(ret);
}
gfedcba
练习题3:翻转字符串
题目描述:给一个字符类型的数组chas和一个整数size,请把大小为size的左半区整体右移到右半区,右半区整体移动到左边。
输入描述:输入两行,第一行一个整数,代表size,第二行一个字符串,代表chas
输出描述:输出一行字符串,代表翻转后的字符串。
输入
3
abcdefg
输出
defgabc
逻辑分析如图:
代码示例:
import java.util.Scanner;
public class TestDemo3 {
public static String reverse(String str, int begin, int end) {
char[] value = str.toCharArray();
while (begin < end) {
char tmp = value[begin];
value[begin] = value[end];
value[end] = tmp;
begin++;
end--;
}
return String.copyValueOf(value);
}
public static String func(String str, int n) {
if(str==null || n<=0 || n>=str.length()){
return null;
}
str = reverse(str, 0, n - 1);
str = reverse(str, n, str.length() - 1);
str = reverse(str, 0, str.length() - 1);
return str;
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
String str = scanner.next();
String ret = func(str, n);
System.out.println(ret);
}
}
4
abcdefghi
efghiabcd
StringBuffer 和 StringBuilder
首先来回顾下String类的特点:
- 任何的字符串常量都是String对象,而且String的常量一旦声明不可改变,如果改变对象内容,改变的是其引用的指向而已。
- 通常来讲String的操作比较简单,但是由于String的不可更改特性,为了方便字符串的修改,提供了StringBuffer和StringBuilder类。
- StringBuffer 和 StringBuilder 大部分功能是相同的,这里主要介绍 StringBuffer
- 在String中使用"+"来进行字符串连接,但是这个操作在StringBuffer类中需要更改为append()方法:
public static void main(String[] args) {
String str="";
for(int i=0;i<10;i++){
str+='a';
}
System.out.println(str);
}
public static void main(String[] args) {
StringBuilder stringBuilder=new StringBuilder();
stringBuilder.append("abcd");
stringBuilder.append("1234").append("cdef");
}
public static void main(String[] args)
StringBuffer sb1=new StringBuffer();
sb1.append("hello");
sb1.reverse();
System.out.println(sb1);
StringBuffer sb2=new StringBuffer();
sb1.append("hello");
StringBuffer stringBuffer=sb2.reverse();
System.out.println(stringBuffer);
}
olleh
olleh
Stringbuilder和Stringbuffer的区别?
面试题:请解释String、StringBuffer、StringBilder的区别?
String的内容不可修改,StringBuffffer与StringBuilder的内容可以修改.
StringBuffffer与StringBuilder大部分功能是相似的。
|