Java String 类
字符串的构造:
常用的3种方式:
public class Create_string {
public static void main(String[] args) {
String s1="qwdj1238385";//直接创建;编译器会根据值创建对象
String s2=new String("nfihwj2393-");//通过构造方法创建
char []arr={'a','s','c'};//字符数组
String s3=new String(arr);
System.out.println(s3);//输出asc
//java的字符串是不会像c语言那样后面有个/0结尾
}
}
String使用
字符串的比较:
1:是否相等
引用类型的比较不能使用 = =;得使用equals方法。
数组是Object对象,如果直接使用equals方法,实质上还是使用==比较。我们的String,Arrays类都重写了equles方法。
import java.util.Arrays;
public class test7 {
public static void main(String[] args) {
String s1="qqq";
String s2=new String("qqq");
System.out.println(s1==s2); //false
System.out.println(s1.equals(s2)); //true
System.out.println(s1.equalsIgnoreCase(s2));//忽略大小写比较
int []arr1={1,2,3};
int []arr2={1,2,3};
System.out.println(arr1==arr2); //false
System.out.println(arr1.equals(arr2)); //false
System.out.println(Arrays.equals(arr1,arr2)); //true
}
}
2:比较大小
String已经实现Comparable接口且重写compareTo方法
所以我们直接使用就行;返回int 类型,他们的差值。是先比较第一个,如果第一个相等就继续比较第二个字符。直到比完如果相等就返回0。
public class test8 {//字符串的比较大小
public static void main(String[] args) {
String s1="abe";
String s2=new String("abc");
System.out.println(s1.compareTo(s2)); //2
}
}
如果是前面都相等的情况下,那么谁的字符个数多则比较大。返回相差的字符个数字符
public class test8 {
public static void main(String[] args) {
String s1="abceeee";
String s2=new String("abc");
System.out.println(s1.compareTo(s2)); //4
System.out.println(s1.compareToIgnoreCase(s2));//忽略大小写比较
}
}
字符串查找
1:从前往后找
public class test9 {//查看字符串
public static void main(String[] args) {
String s1="abccdefg";
System.out.println(s1.indexOf('p'));//在字符串查找某个字符的位置,如果没有返回-1
System.out.println(s1.indexOf('c',3));//从下标为3开始找
System.out.println(s1.indexOf("bcc")); //查找字符串的位置
System.out.println(s1.indexOf("bcc",3)); //从3下标开始查找字符串的位置
}
}
2:从后往前找
public class test9 {//查看字符串
public static void main(String[] args) {
String s1="abccdefg";
System.out.println(s1.lastIndexOf('c'));
System.out.println(s1.lastIndexOf('c',5)); //从5下标开始往前找
System.out.println(s1.lastIndexOf("abc"));
System.out.println(s1.lastIndexOf("ajff",5)); //从5下标开始往前找
}
}
转化为字符串类型
public class test10 {
public static void main(String[] args) {
//类名调用,static方法
String s1=String.valueOf(123);//数字转字符
System.out.println(s2);//123
String s2=String.valueOf(new student(18,"str"));
System.out.println(s2); //这时候打印是地址,没转化成功
}
}
class student {
int age=100;
String name;
}
转化的前提是得重写了tostring方法。源码是如果自己没有重写toString方法它则去调用Object的。
重写的代码:(步骤:鼠标右键,Generate,选择toString)
@Override
public String toString() {
return "student{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
重写后输出结果:
字符串类型转成其它类型
public class test11 {
public static void main(String[] args) {
int a1=Integer.parseInt("123"); //转int类型
int a2=Integer.valueOf("123");
double a3=Double.parseDouble("12.3");//其它类型都是类似方法
}
}
大小写的英文字母互相转换
public class tset12 {
public static void main(String[] args) {
String s="abc";
String s1=s.toUpperCase();//小转大
System.out.println(s);
System.out.println(s1);
String s3=s1.toLowerCase();//大转小
System.out.println(s3);
}
}
输出:
abc
ABC
abc
注意:String 类是不可改变的,所以你一旦创建了 String 对象,那它的值就无法改变了,我们并不是在原来的那一份上修改。
字符串转数组
public class test13 {
public static void main(String[] args) {
String s1="1ewjdh";
String s2="djff";
char []arr1=s1.toCharArray();
}
}
格式化字符串(使用比较少)
字符串替换
public class test13 {
public static void main(String[] args) {
String s1 = "abcccddecdg";
String s2 = s1.replace('c', 'd');//把所有的c换成d
System.out.println(s2);
String s3 = s1.replace("cd", "sq");//把所有的cd换成sq
System.out.println(s3);
String s4 = s1.replaceAll("cd", "sq");//也是把所有的cd换成sq,这个方法得是字符串才能用
System.out.println(s4);
String s5 = s1.replaceAll("c", "d");
System.out.println(s5);
String s6=s1.replaceFirst("cd","sq");//把第一个出现的cd换成sq,也是只能用字符串
System.out.println(s6);
}}
输出结果:
abdddddeddg
abccsqdesqg
abccsqdesqg
abdddddeddg
abccsqdecdg
字符串拆分
把一个完整的字符串按照指定的分隔符划分为若干个子字符串;返回的结果是字符串数组。
import java.util.Arrays;
public class test14 {
public static void main(String[] args) {
String s1="wnjksnaasjeda";
String []arr=s1.split("s");
String []arr1=s1.split("s",2);//设置只划分为两个子字符串
System.out.println(Arrays.toString(arr));
char [] a={'1','2'};
System.out.println(a);
}
}
特殊情况:需要加转义字符。
import java.util.Arrays;
public class test14 {
public static void main(String[] args) {
String s1="127.123.455";
String []arr=s1.split("\\.");
System.out.println(Arrays.toString(arr));
}
}
转义字符也需要转义字符来修饰,所以就是两个\最终发挥一个作用。如果我们要使用\来划分就需要\\(| * +都得需要转义字符)
多个分隔符的情况。使用| 分隔开
import java.util.Arrays;
public class test14 {
public static void main(String[] args) {
String s1="127.123.4&55";
String []arr=s1.split("\\.|&");
System.out.println(Arrays.toString(arr));
}
}
输出:[127, 123, 4, 55]
字符串截取
public class test12 {
public static void main(String[] args) {
String s1="helooworld";
String s2=s1.substring(5);//从5位置往后截取
System.out.println(s2);
String s3=s1.substring(5,9);//截取【5-10)区间字符
System.out.println(s3);
}
}
输出:
world
worl
去掉字符串两边空格(空格, 换行, 制表符等)
public class test12 {
public static void main(String[] args) {
String s1=" hel ooworld ";
String s2=s1.trim();
System.out.println(s2);
}
}
输出:hel ooworld
判断字符串是否包含子字符串
public class test12 {
public static void main(String[] args) {
String s1=" hel ooworld ";
System.out.println(s1.contains("h"));//true
}
}
判断以…字符串开始
public class test12 {
public static void main(String[] args) {
String s1="hel ooworld ";
System.out.println(s1.startsWith("hel"));//判断是否从这个字符串开始
}
}
字符串类型你能想到的字符串操作,他都有实现的方法,我们在后续练习和学习还是会和数组就是操作字符串打很多交道。需要用到其它方法的时候
官方文档查看
字符串内存储存
String源码:String是引用类型里边并不储存字符串,而是储的它的引用。
调试一下,可以看到确实是如此。
字符串常量池:
String内存储存是比较特殊;因为他里面有一个字符串常量池;字符串常量池在堆里边;底层是一个String Table哈希表(数组与链表的组合):
当我们使用字面值或使用String类的intern()方法创建字符串常量时,如果字符串常量池中已经存在相同内容的字符串,JVM会返回该字符串对象的引用。如果字符串常量池中不存在这个字符串,JVM会创建一个新的字符串对象,并将其添加到字符串常量池中。
字符串常量池的影响下:
我们可以使用==和equals比较它们各自的关系;最后通过图片的总结展示内存效果。
s1和s2:JVM会首先检查字符串常量池中是否已经存在相同内容的字符串对象,如果存在,则s1和s2会被指向同一个字符串对象。(带双引号号的在常量池只有一份;0x00这个节点还可以有其它的存有字符串对象;因为是一个链表)
s3和s1:直接常量池判断有相同的引号字符串;就返回这个0x89的对象
s4和s5:对象是不一样;但是对象里面的value是一样。对象就不再是0x98;而是new新的;但是里面的value都是0x11。它们使用的都是同一份”hello”
s6也是和s4、s5如此
题目1:
题目2:
题目3:
“abc”+“def”编译的时候就是相当于”abcdef”,所以4执行,6不执行。
intern方法
intern()方法是String类提供的一个方法,它的作用是在字符串常量池中查找当前字符串对象,如果常量池中存在相同内容的字符串,则返回常量池中的对象引用,如果常量池中不存在相同内容的字符串,则将当前字符串对象添加到常量池中,并返回常量池中的对象引用。
字符数组对象不会直接存储在字符串常量池中;如果我们使用new (字符串数组)的方式创建字符串;字符串不会自动存储到字符串常量池中;我们可以使用该方法手动将创建的String对象添加到常量池中。
public static void main(String[] args) {
char[] ch = new char[]{'a', 'b', 'c'};
String s1 = new String(ch); // s1对象并不在常量池中,
s1.intern(); // s1.intern();调用之后,会将s1对象的引用放入到常量池中
String s2 = "abc"; // "abc" 在常量池中存在了,s2创建时直接用常量池中"abc"的引用
System.out.println(s1 == s2); //输出true
}
三种创建方式内存不同的总结
-
String str = “hello”:在堆开辟一块内存空间,然后保存到字符串常量池,再给String引用。创建两个对象数组+String对象
-
String str = new String(“hello”) : 会开辟两块堆内存空间,1:字符串”hello”保存在字符串常量池中 2: 然后用常量池中的String对象给新开辟的String对象赋值。创建3个对象(常量池啥也没有的情况)
-
String str = new String(new char[]{‘h’, ‘e’, ‘l’, ‘l’, ‘o’}) :先在堆上创建一个String对象,然后利用copyof将重新开辟数组空间,将参数字符串数组中内容拷贝到String对象中(把hello字符数组拷贝一份给String)。创建数组,数组的拷贝,String对象
(char数组是不会直接放入常量池的,在这个构造中:this.value = Arrays.copyOf (value, value.length),是对实参变量做了拷贝,存储在堆中)
字符串的不可变
1:我们操作的字符串原来的是不会改变的,都是新建。看一下源码分析。
字符串的修改
不建议在String类型对象进行修改,因为每一次修改都会创建很多对象.
public class test22 {
public static void main(String[] args) {
String s1="hello";
s1+="world";
}
} //一个简简单单拼接光String对象就创建了3个(hello,world,helloworld)
可以看到两个代码的时间差别是非常的大
StringBuilder和StringBuffer
1 :StringBuilder和StringBuffer类。这两个类大部分功能是相同的
2 :String和StringBuilder最大的区别在于String的内容无法修改,而StringBuilder的内容可以修改。频繁修改字符串的情况考虑使用StringBuilder。
public class test22 {
public static void main(String[] args) {
StringBuffer s=new StringBuffer("abc");
long start=System.currentTimeMillis();
for (int i = 0; i <100000 ; i++) {
s.append(i);//追加i字符串
}
long end=System.currentTimeMillis();
System.out.println(end-start);//13
}
}
两者之间转换:
String变为StringBuilder: 利用StringBuilder的构造方法或append()方法
StringBuilder变为String: 调用toString()方法。
String str = "abcde";
StringBuilder str0 = new StringBuilder(str);
//StringBuilder str0= new StringBuilder();
//sb.append(str);
String str = "abcde";
StringBuilder str0 = new StringBuilder();
str0.append(str);
str = s1.toString();
StringBuilder官方文档
String、StringBuffer、StringBuilder的区别
1: String的内容不可修改,StringBuffer与StringBuilder的内容可以修改.
2: StringBuffer与StringBuilder大部分功能是相似的
3: StringBuffer采用同步处理,属于线程安全操作;而StringBuilder未采用同步处理,属于线程不安全操作