【3】java之string类

String 是一个字符串类型的类,使用双引号定义的内容都是字符串,但是 String 本身是一个类,使用上会有一些特殊。

一、 String类对象的两种实例化方式

1.1 直接赋值

public class StringDemo{
	public static void main(String args[]){
		String str = "Hello World!";
		System.out.println(str);
}

 String 对象的直接赋值,代码并没有使用关键字 new 进行。

直接赋值相当于将一个匿名对象设置了一个名字。但是唯一的区别是: String 类的匿名对象是由系统自动生成的,不再由用户自己创建。

举例:观察字符串是匿名对象的验证

public class StringDemo{
	public static void main(String args[]){
		String str = "Hello";
		System.out.println("Hello".equals(str)); 
}

1.2 构造方法赋值

构造方法:public String(String str),在构造方法里面依然要接收一个本类对象;

举例:利用构造方法实例化

public class StringDemo{
	public static void main(String args[]){
		String str = new String("Hello World");
		System.out.println(str);
}

​ String 类有两种形式,主观上会认为第二种构造方法的形式更加适合于我们,因为只要是类就要用关键字 new 的做法似乎很符合道理的。

1.3 两种实例化方式的区别(重点)

1.3.1 分析直接赋值

直接赋值就是将一个字符串的匿名对象设置了一个名字。

String str = "Hello";

此时在内存之中会开辟一块堆内存,并且由一块栈内存指向此堆内存。

public class StringDemo{
	public static void main(String args[]){
		String strA = "Hello";
		String strB = "Hello";
		String strC = "Hello";
		System.out.println(strA == strB);  // true
		System.out.println(strA == strC);  // true
		System.out.println(strB == strC);  // true
}

​ 所有采用直接赋值的 String 类对象的内存地址完全相同,即 strA、strB、strC指向了同一块堆内存空间。

​ 共享设计模式:在 JVM 底层实际上会存在一个对象池(不一定只保存 String 对象),当代码之中使用了直接赋值的方式定义了一个 String 类对象时,会将此字符串对象所使用的匿名对象入池保存,如果后续还有其它 String 类对象也采用直接赋值方式,那么将不会开辟新的堆内存空间,而是使用已有的对象引用的分配,继续使用。

1.3.2 采用构造方法实例化

构造方法如果要使用一定要用关键字 new ,一旦使用了关键字 new 就表示要开辟新的堆内存空间。

public static void main(String args[]){
	String strA = new String("Hello");
	String strB = new String("Hello");
	String strC = new String("Hello");
	System.out.println(strA == strB);  // false
	System.out.println(strA == strC);  // false
	System.out.println(strB == strC);  // false
}

使用构造方法方式进行 String 类对象实例化的时候,开辟了两块堆内存空间,并且其中有一块堆内存空间将成为垃圾空间。

​ 除了内存的浪费之外,如果使用了构造方法定义的 String 类对象,其内容不会保存到对象池之中,因为是使用了关键字 new 开辟了新内存。如果希望开辟的新内存数据也可以进行对象池的保存,那么可以采用 String 类定义的手工入池的方法: public String intern();

举例:手工入池

public static void main(String args[]){
	String strA = new String("Hello").intern();
	String strB = "Hello";
	System.out.println(strA == strB);  // true
}

面试题:请解释 String 类对象实例化的两种方式的区别?

  • 直接赋值,只会开辟一块堆内存空间,并且匿名对象会自动保存到对象池中,以供下次重复使用。
  • 构造方法赋值,会开辟两块堆内存空间,其中有一块空间将成为垃圾,并且不会自动入池,可以使用intern()方法手工入池。

工作中,使用直接赋值方式。

 

二、字符串内容不可改变

2.1 字符串变更

字符串一旦定义则不可改变。观察一段代码:

public static void main(String args[]){
	String str = "Hello";
	str += " World";
	str += "!!!";
	System.out.println(str);  
}

运行结果是: Hello World!!! 以上代码最终结果实际上 str 对象的内容被改变了。

所谓的字符串的内容根本就没有改变( Java 就定义好了 String 的内容不能够改变),而对于字符串对象内容的改变是利用了引用关系的变化而实现的,但是每一次的变化都会产生垃圾空间。所以 String 类的内容不要频繁的修改。

2.2 StringBuffer

 String 类有一个问题:字符串一旦声明了就不能改变,只能改变 String 类对象的引用。为此,Java 里提供了另外一个类:StringBuffer 类,其内容可以修改。String 类的对象可以使用 “+” 进行字符串的连接操作,但是在 StringBuffer 类里必须使用 append() 方法进行追加。

  • 方法:public StringBuffer append(数据类型 参数)
public class TestDemo {
    public static void main(String args[]){
        // String 类可以直接赋值实例化,但是 StringBuffer 类不可以.
        StringBuffer buf = new StringBuffer();
        buf.append("Hello").append("World").append("!!");
        change(buf);    // 引用传递
        System.out.println(buf);
    }
    public static void change(StringBuffer temp){
        temp.append("\\n").append(true);
    }
}

​ 由于 append() 方法的返回值类型仍是 StringBuffer,所以 StringBuffer 类的内容是可以进行修改的,而String 类的内容是不可以修改的。

2.3 StringBuffer、String 类对比

  • String 类
public final class String
extends Object
implements Serializable, Comparable<String>, CharSequence
  • StringBuffer 类
public final class StringBuffer
extends Object
implements Serializable, CharSequence

​ 发现两个类都是 CharSequence 接口的子类。在以后的开发中,如果看见某些方法的操作上出现的是 CharSequence 接口,那么应该立刻清楚:只需要传递字符串即可。

public class TestDemo {
    public static void main(String args[]){
        CharSequence seq = "hello"; // 向上转型
        System.out.println(seq); // String 类覆写的 toString 方法
    }
}

​ 虽然 String 和 StringBuffer 类有着共同的接口,但是这两个类对象之间如果要转换,不能直接转换

2.4 转换

将 String 对象转换为 StringBuffer 对象,有 种方式:

  • 构造方法:public StringBuffer(String str)
  • append() 方法: buf.append(str)。

将 StringBuffer 对象转换为 String 对象,也有两种方式:

  • toString() 方法;
  • 构造方法: public String(StringBuffer buffer)。

在 String 类里提供了一个和 StringBuffer 比较的方法:public boolean contentEquals(StringBuffer sb)

2.5 StringBuffer 类常用方法

​ String 类定义了很多方法便于用户的开发,而在 StringBuffer 类里也定义了许多的操作方法,而且部分方法与 String 类正好互补。

  • 字符串反转:public StringBuffer reverse();
  • 在指定的索引位置增加数据:public StringBuffer insert(int offset,数据类型 变量);
  • 删除部分数据:public StringBuffer delete(int start,int end)。

2.6 StringBuilder 类

​ 在 JDK 1.5 后增加了一个字符串操作类:StringBuilder 类。这个类的定义结构和 StringBuffer 类非常类似,几乎连方法都一样。

面试题:请解释 String、StringBuffer、StringBuilder 的区别?

  • String 的内容一旦创建则不可以改变,而 StringBuffer、StringBuilder 声明的内容可以改变;
  • StringBuffer 类提供的方法都是同步方法,属于安全的线程操作,而 StringBuilder 方法都属于异步方法,属于非线程安全的操作。

在日后开发中,如果见到了字符串的应用,不需要思考 95% 使用的都是 String 类,只有在需要频繁修改的时候才会使用 StringBuffer 和 StringBuilder 类。

String 类仍然是最常用的字符串描述类,而 StringBuffer 类由于出现的时间较长,所以要比 StringBuilder 类使用的多(习惯了)。

三、String 类的常用方法

​ 对于系统类的方法,一定要去查询文档,一些不常用的方法允许不知道,但是一定要会查。但是对于 String 类的一些方法由于使用的情况比较多,为了方便开发必须背住。

3.1 字符与字符串

No. 方法名称 说明 描述 | :--: | :--------------------------------------: | :--: -------------------- 

public String(char[] value) 构造 将字符数组变为 String 类对象 

public String(char[] value, int offset, int count) 构造 将部分字符数组变为 String 类对象 

public char charAt(int index) 普通 返回指定索引对应的字符 

public char[] toCharArray() 普通 将字符串变为字符数组 |

很多语言中都是利用了字符数组的概念来描述字符串的信息,这一点在 String 类的方法上也都有所体现。

举例:返回字符串指定索引的字符

	String str = "hello";
	char s = str.charAt(0);
	System.out.println(s);

举例:将字符串以字符数组返回

	String str = "Hello";
	char data[] = str.toCharArray();
	for(int i=0;i<data.length;i++){
		System.out.print(data[i]+"、");  
	}

举例:将字符串转大写

	public class StringDemo{
	public static void main(String args[]){
		String str = "hello";
		char data[] = str.toCharArray();
		for(int i=0;i<data.length;i++){
			data[i] -= 32;
		}
		str = new String(data); // 将字符数组变为String
		System.out.println(str);

		String str1 = new String(data,1,3); // 将字符数组部分内容变为String
		System.out.println(str1);
	}
}

3.2 字节与字符串

No. 方法名称 说明 描述 | :--: | :--------------------------------------: | :--: -------------------- 

public String(byte[] bytes) 构造 将全部字节数组变为 String 类对象 

public String(byte[] bytes,int offset,int length) 构造 将部分字节数组变为 String 类对象 

public byte[] getBytes() 普通 将字符串变为字节数组 

public byte[] getBytes(String charsetName) throws UnsupportedEncodingException 普通 进行编码转换 |

​ 字节使用 byte 描述,字节一般主要用于数据的传输或者进行编码转换。将字符串变为字节数组的操作,目的就是为了传输以及编码转换。

举例:观察字符串与字节数组的转换

public class StringDemo{
public static void main(String args[]){
	String str = "helloworld";
	byte data [] = str.getBytes(); // 将String对象转换为byte数组
	for(int i=0; i<data.length; i++){
		System.out.print(data[i]+"、");
		data[i] -= 32;	// 小写字母转为大写字母
	}
	System.out.println();
	System.out.println(new String(data));      // 将byte数组转换为String对象
	System.out.println(new String(data,1,3));  // 将byte数组部分内容转换为String对象
}
}

​ 因为现在操作的是英文字母,所以感觉与字符类似。在以后的IO操作的时候会牵扯到这种字节数组的操作,在后续的开发中会逐步遇到乱码需要转码的问题。

3.3 字符串比较

No. 方法名称 说明 描述 | :--: | :--------------------------------------: | :--: ---------------------------------------- 

public boolean equals(String anotherString)) 普通 进行相等判断,区分大小写 

public boolean equalsIgnoreCase(String anotherString) 普通 进行相等判断,不区分大小写 

public int compareTo(String anotherString) 普通 判断两个字符串的大小(按照字符编码比较),返回值有如下 3 种:(1) =0 表示要比较的两个字符串内容相等;(2)>0 表示大于的结果;(3)<0 表示小于的结果 |

3.3.1 ==比较内存地址

判断两个基本数据类型的数据是否相等,可以使用 “ == ” 来完成。但是在 String 上也可以使用“ == ” 比较,那比较的结果呢?

举例:

public class StringDemo{
	public static void main(String args[]){
		String strA = "Hello World";
		String strB = new String("Hello World");
		String strC = strB;
		System.out.println(strA == strB);  // false
		System.out.println(strA == strC);  // false
		System.out.println(strB == strC);  // true
}

从内存关系分析来看,== 比较的是内存地址的数值,并不是字符串包含的内容。所以 == 属于数值比较,比较的是内存地址

3.3.2 equals比较字符串内容

  • 比较内容(与原始定义有一些差别):public boolean equals(String str);

    举例:实现内容比较

    public class StringDemo{
    	public static void main(String args[]){
    		String strA = "Hello World";
    		String strB = new String("Hello World");
    		String strC = strB;
    
    		System.out.println(strA.equals(strB));  // true
    		System.out.println(strA.equals(strC));	// true
    		System.out.println(strB.equals(strC));	// true
    	}
    }
    

开发之中,只要是进行字符串的判断,千万不要使用 == 完成

面试题:请解释在字符串比较中, == 与 equals() 的区别。

  • “ == ” 是 java 提供的关系运算符,主要的功能是进行数值相等判断,如果用在了 String 对象上则表示的是内存地址数值的比较。

  • “ equals() ” 是由 String 类提供的一个方法,此方法专门负责进行字符串内容的比较

3.3.3 其他比较方法

如果要进行字符串内容相等的判断使用 equals(),但是在 String 类里面定义的字符串判断相等的不止这一个。

举例:

public class StringDemo{
	public static void main(String args[]){
		String strA = "helloworld";
		String strB = "HelloWorld";
		System.out.println(strB.equals(strA));   // false
		System.out.println(strB.equalsIgnoreCase(strA));   //true
	}
}

举例:观察 compareTo() 方法

public class StringDemo{
	public static void main(String args[]){
		String strA = "helloworld";
		String strB = "HelloWorld";
		System.out.println(strA.compareTo(strB));   // 32
		if(strA.compareTo(strB) > 0){
			System.out.println("大于");
		}
	}
}

3.4 字符串查找

从一个完整的字符串之中要判断某一个子字符串是否存在

No. 方法名称 说明 描述 | :--: | :--------------------------------------: | :--: ---------------------------------------- 

public boolean contains(String s) 普通 判断指定的内容是否存在 

public int indexOf(String str) 普通 由前向后查找指定字符串的位置,如果查找到了则返回第一个字母的位置索引;如果找不到则返回-1. 

public int indexOf(String str, int fromIndex) 普通 由指定位置从前向后查找指定字符串的位置,如果查找到了则返回第一个字母的位置索引;如果找不到则返回-1. 

public int lastIndexOf(String str) 普通 由后向前查找指定字符串的位置,如果查找到了则返回第一个字母的位置索引;如果找不到则返回-1. 

public int lastIndexOf(String str, int fromIndex) 普通 由指定位置从后向前查找指定字符串的位置,如果查找到了则返回第一个字母的位置索引;如果找不到则返回-1. 

public boolean startsWith(String prefix) 普通 判断是否以指定的字符串开头,如果是则返回true,否则返回false 

public boolean startsWith(String prefix, int toffset) 普通 在指定位置以指定字符串开始 

public boolean endsWith(String suffix) 普通 以指定的字符串结尾 |

举例:使用 indexOf() 等功能查找

public class StringDemo{
	public static void main(String args[]){
		String str = "helloworld";

		// 返回满足条件单词的第一个字母的索引
		System.out.println(str.indexOf("l"));     // 2
		System.out.println(str.indexOf("l",5));   // 8
		// System.out.println(str.indexOf("world",6));   // -1

		System.out.println(str.lastIndexOf("l")); // 8
	// 	System.out.println(str.lastIndexOf("orld",4)); // -1
	// 	System.out.println(str.lastIndexOf("orld",7)); //6
	}
}

​ 上面的功能只返回了位置。但是在一些程序之中需要查找有没有,最早的做法是判断查询结果是否是 -1 来实现的。

public class StringDemo{
	public static void main(String args[]){
		String str = "helloworld";
		if(str.indexOf("world") != -1){
			System.out.println("能查询到数据");   
		}else{
			System.out.println("不能查询到数据");   
		}
	}
}

但是从 JDK 1.5 及之后出现了 contains() 方法,可直接返回 boolean。

public static void main(String args[]){
	String str = "helloworld";
	if(str.contains("world")){
		System.out.println("能查询到数据");   
	}else{
		System.out.println("不能查询到数据");   
	}
}

使用 contains() 更加的简单,并且在整个 java 里面,contains 已经成为了查找的代名词。

举例:开头或结尾判断

public class StringDemo{
	public static void main(String args[]){
		String str = "##@@helloworld**";
		System.out.println(str.startsWith("##"));   
		System.out.println(str.startsWith("@",2));   
		System.out.println(str.endsWith("*")); 			  
	}
}

这些开头和结尾的判断往往可以作为一些标记在程序之中出现。

3.5 字符串替换

指的使用一个新的字符串替换掉旧的字符串数据,支持的方法有如下几个:

No. 方法名称 说明 描述 | :--: | :--------------------------------------: | :--: -------------- 

public String replaceAll(String regex,String replacement) 普通 用新的内容替换掉全部旧的内容 

public String replaceFirst(String regex,String replacement) 普通 替换首个满足条件的内容 |

举例:观察替换的结果

public class StringDemo{
	public static void main(String args[]){
		String str = "##@@helloworld**";
		System.out.println(str.replace("l","U"));        // ##@@heUUoworUd**
		System.out.println(str.replaceFirst("@","!"));   // ##!@helloworld**
	}
}

参数 regex 是正则

3.6 字符串截取

从一个完整的字符串之中,截取部分子字符串的数据,支持的方法如下:

No. 方法名称 说明 描述 | :--: | :--------------------------------------: | :--: ----------- 

public String substring(int beginIndex) 普通 从指定索引截取到结尾 

public String substring(int beginIndex,int endIndex) 普通 截取部分子字符串的数据 |

举例:

public class StringDemo{
	public static void main(String args[]){
		String str = "##@@helloworld**";
		System.out.println(str.substring(4));        // helloworld**
		System.out.println(str.substring(4,13));   // helloworld
	}
}

不能使用负数作为截取的开始点。

3.7 字符串拆分

将一个完整的字符串,按照指定的内容拆分为字符串数组(对象数组,String 类对象),方法如下:

No. 方法名称 说明 描述 | :--: | :--------------------------------------: | :--: ---------------------------------------- 

public String[] split(String regex) 普通 按照指定的字符串进行全部拆分 

public String[] split(String regex,int limit) 普通 按照指定的字符串进行部分拆分,最后的数组长度由 limit 决定(如果能拆分的结果很多,数组长度才由 limit 决定),即前面拆后面不拆作为整体。 |

举例:全部拆分

	String str = "hello world nihao ma";
	String data [] = str.split(" ");   
	for(int i=0; i<data.length; i++){
		System.out.println(data[i]);
	} 

如果在拆分的时候只是写了一个空字符串(“” 不是null),表示按照每一个字符进行拆分。

举例:部分拆分

	String str = "hello world nihao ma";	
	String data[] = str.split(" ",2);   
	for(int i=0; i<data1.length; i++){
		System.out.println(data1[i]);
	} 

举例:实现IPv4地址拆分

String str = "192.68.15.238";	
String data[] = str.split(".");   
for(int i=0; i<data.length; i++){
	System.out.println(data[i]);
} 

​ 如果写成上面,你会发现无法拆分。如果是一些敏感字符(正则标记),严格来讲是拆分不了的。如果真的遇见拆分不了的就使用 " \\ \\(就是 \\ )“,进行转义后才可以拆分。

​ 字符串的拆分是非常常见的,因为很多时候会传递一组数据到程序中。现在有如下的一个字符串:”张三:20|李四:21|王五:22“ (姓名:年龄|)。

	String str = "张三:20|李四:21|王五:22";	
	String data[] = str.split("\\\\|");   
	for(int i=0; i<data.length; i++){
		String temp []= data[i].split(":");
		System.out.println("姓名: " + temp[0]+", 年龄: " + temp[1]);
	} 

3.8 其他操作

以上给出的方法是可以归类的,但是 String 类里面有部分代码是没法归类的。

No. 方法名称 说明 描述 | :--: | :------------------------------: | :--: ---------------------------- 

public String concat(String str) 普通 字符串的连接,与 ” “ 类似 

public String toLowerCase() 普通 转小写 

public String toUpperCase() 普通 转大写 

public String trim() 普通 去掉字符串中左右两边的空格 

public int length() 普通 取得字符串的长度 

public String intern() 普通 对象入池 

public boolean isEmpty() 普通 判断是否是空字符串(不是 null,而是”“,长度为0) |

举例:转小写与大写

	String strA = "hello!!!&&";	
	System.out.println(strA.toUpperCase());

所有的非字母数据不会进行任何的转换操作。

	String str = "   hello world ";
	System.out.println("【" + str + "】");
	System.out.println("【" + str.trim() + "】");	
	String str = "   hello world ";

​ 一般在用户进行输入的时候有可能会携带有无用的空格内容,那么接收到这些数据后就需要消除掉所有的无用空格。

举例:取得字符串的长度

String str = "   hello world ";
System.out.println(str.length());

​ 用途:由于用户输入的数据长度是有限制的,可以利用此方式判断。数组中也有一个 length 属性,但是调用的形式不同。

  • 数组对象.length;
  • String对象.length();

举例:判断是否是空字符串

	String str = "   hello world ";
	System.out.println(str.isEmpty());  // false
	System.out.println("".isEmpty());   // true

以后如果觉得 isEmpty() 不方便,可以使用 ” ”“.isEquals(str)“。

​ String 类提供了大量的支持的方法,但是却少了一个重要的方法 —— initcap() 功能:首字母大写其余字母小写,这样的功能只能自己实现。

举例:

public class StringDemo{
	public static void main(String args[]){
		String str = "hEllo world";
		System.out.println(initcap(str));	
	}
	public static String initcap(String str){
		String strA = str.substring(0,1);
		String strB = str.substring(1);
		return strA.toUpperCase() + strB.toLowerCase();
	}
}

虽然 Java 的类库里没有此功能,但是一些第三方的组件包会提供,例如:Apache。

 

posted @ 2022-06-17 11:03  才华充电中  阅读(65)  评论(0编辑  收藏  举报