你说呢 努力努力再努力吖!
冲冲冲!!
String类提供的构造方法非常多,常用的有以下三种:
public static void main(String[] args) {
// 使用常量串构造
String s1 = "hello bit";
System.out.println(s1);
// 直接newString对象
String s2 = new String("hello bit");
System.out.println(s1);
// 使用字符数组进行构造
char[] array = {'h','e','l','l','o','b','i','t'};
String s3 = new String(array);
System.out.println(s1);
}
其他方法参考:String官方文档
// 打印"hello"字符串(String对象)的长度
System.out.println("hello".length());
// 注意:字符串长度是方法.length(); 有括号!
注意:对于内置类型,== 比较的是变量中的值;(即:对于基本类型,比较的是值)
对于引用类型, ==比较的是引用中的地址
字典序:字符大小的顺序
String类重写了父类Object中equals方法,Object中equals默认按照==比较;String重写equals方法后,是按照字典序从前往后逐个比较的。
与equals不同的是,equals返回的是boolean类型,而compareTo返回的是int类型。compareTo(String s) 具体比较方式:
字符串查找也是字符串中非常常见的操作,String类提供的常用查找的方法:

public static void main(String[] args) {
String s = "aaabbbcccaaabbbccc";
System.out.println(s.charAt(3)); // 'b'
System.out.println(s.indexOf('c')); // 6
System.out.println(s.indexOf('c', 10)); // 15
System.out.println(s.indexOf("bbb")); // 3
System.out.println(s.indexOf("bbb", 10)); // 12
System.out.println(s.lastIndexOf('c')); // 17
System.out.println(s.lastIndexOf('c', 10)); // 8
System.out.println(s.lastIndexOf("bbb")); // 12
System.out.println(s.lastIndexOf("bbb", 10)); // 3
字符串转数值
Integer.parseInteger(“字符串类型数值”);
Double.parseDouble(“字符串类型数值”);
数值转字符串
String.valueOf(其他类型数值);
String str;
大写转小写
str.toLowerCase();
小写转大写
str.toUpperCase();
字符串转数组
String str = “hello”;
char[] ch = str.toCharArray();
然后进行遍历就可
数组转字符串
String str = new String(数组名);
举例如下:
public static void main(String[] args) {
String s = String.format("%d-%d-%d", 2019, 9,14);
System.out.println(s);
}


String str = "192.168.1.1" ;
String[] result = str.split("\\.") ;
// .需要使用\进行转义,而\又需要\进行转义
for(String s: result) {
System.out.println(s);
}
注意事项
1)字符"|", “*”, "+“都得加上转义字符,前面加上 “\\” .
2) 而如果是 “\\” ,那么就得写成 “\\\\” .
3) 如果一个字符串中有多个分隔符,可以用”|"作为连字符.
String str = "name=zhangsan&age=18" ;
String[] result = str.split("&") ;
for (int i = 0; i < result.length; i++) {
String[] temp = result[i].split("=") ;
System.out.println(temp[0]+" = "+temp[1]);
}
从一个完整的字符串之中截取出部分内容。可用方法如下:

注意事项:
1)索引从0开始
2)注意前闭后开区间的写法, substring(0, 5) 表示包含 0 号下标的字符, 不包含 5 号下标
其他操作方法:

trim 会去掉字符串开头和结尾的空白字符(空格, 换行, 制表符等)。
大小写转换这两个函数只转换字母。
public static void main(String[] args) {
String s1 = "hello";
String s2 = "hello";
String s3 = new String("hello");
String s4 = new String("hello");
System.out.println(s1 == s2); // true
System.out.println(s1 == s3); // false
System.out.println(s3 == s4); // false }
上述程序创建方式类似,为什么s1和s2引用的是同一个对象,而s3和s4不是呢?
在Java程序中,类似于:1, 2, 3,3.14,“hello”等字面类型的常量经常频繁使用,为了使程序的运行速度更快、
更节省内存,Java为8种基本数据类型和String类都提供了常量池。
“池” 是编程中的一种常见的, 重要的提升效率的方式, 我们会在未来的学习中遇到各种 “内存池”, “线程池”, “数据库连接池” …
对于池 举个例子:
如把钱存在银行卡上,随用随取,效率比较高。
为了节省存储空间以及程序的运行效率,Java中引入了:
1)Class文件常量池:每个.Java源文件编译后生成.Class文件中会保存当前类中的字面常量以及符号信息
2)运行时常量池:在.Class文件被加载时,.Class文件中的常量池被加载到内存中称为运行时常量池,运行时常量池每个类都有一份
3) 字符串常量池
字符串常量池在JVM中是StringTable类,实际是一个固定大小的HashTable(一种高效用来进行查找的数据结构),不同JDK版本下字符串常量池的位置以及默认大小是不同的:

由于不同JDK版本对字符串常量池的处理方式不同,此处在Java8 HotSpot上分析。
public static void main(String[] args) {
String s1 = "hello";
String s2 = "hello";
System.out.println(s1 == s2); // true
}
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);
}
// 输出false
// 将上述方法打开之后,就会输出true
注意:在Java6 和 Java7、8中Intern的实现会有些许的差别。
注:String对象调用intern()方法后,如果是该字符串已经在常量池中存在,则返回常量池对象;若是不存在则将该字符串存入常量池。
面试题:请解释String类中两种对象实例化的区别
JDK1.8中
1)String str = “hello”;
只会开辟一块堆内存空间,保存在字符串常量池中,然后str共享常量池中的String对象
String是一种不可变对象。 字符串中的内容是不可改变。字符串不可被修改,是因为:
//String类的实现:
public final class String {
...
//用于存储字符串地址
private final char[] value;
// 用于存储hash值,默认是0
private int hash;}
String类中的字符实际保存在内部维护的value字符数组中:
1) String类被final修饰,表明该类不能被继承。
2) value被final修饰,表明value自身的值不能改变,即不能引用其它字符数组,但是其引用空间中的内容可以修改。(即:引用指向的地址不可以改变,但是其地址指向的内容可以修改)
public static void main(String[] args) {
final int array[] = {1,2,3,4,5};
array[0] = 100; // 内容可以改变
System.out.println(Arrays.toString(array));
// array = new int[]{4,5,6}; // 编译报错:Error:(19, 9) java: 无法为最终变量array分配值 即:创建新的对象会让指向地址改变,但是这不可能实现
}
- 方便实现字符串对象池。如果 String 可变, 那么对象池就需要考虑写时拷贝的问题了。
- 不可变对象是线程安全的。
- 不可变对象更方便缓存 hash code, 作为 key 时可以更高效的保存到 HashMap 中。
注意:尽量避免直接对String类型对象进行修改,因为String类是不能修改的,所有的修改都会创建新对象,效率非常低下。
public static void main(String[] args) {
String s = "hello";
s += " world"; //进行拼接
System.out.println(s);
// 输出:hello world
}
过程:
1.创建一个StringBuilder对象,假设为temp
2.将s对象append(追加)到temp之后
3.将“world”字符串append(追加)到temp之后
4.结果为StringBuilder对象,则调用temp的toString方法构造一个新的String对象
5.将新的String对象的引用赋值给s
可以看出:利用拼接+=方法对String类进行修改时,效率是非常慢的,因此:尽量避免对String的直接修改,如果要修改建议尽量使用StringBuffer或者StringBuilder。
Java字符容量capacity()方法:
- Java字符容量计算:比如StringBuffer sb=new StringBuffer(“Good”);输出sb.capacity();长度为20
- 因为StringBuffer在为对象分配长度的时候,起始会分配一个字,也就是两个字节长度即(16位)
- 每增加一个字符,长度就会在16的基础上加 1 ;Good 意思: 16+4 = 20

public class Test {
public static void main(String[] args) {
StringBuilder sb1 = new StringBuilder("hello");
StringBuilder sb2 = sb1; // str1 & str2 指向同一个地址
// 追加:即尾插-->字符、字符串、整形数字
sb1.append(' '); // hello // StringBuilder追加直接改变原字符串
sb1.append("world");// hello world
sb1.append(123); // hello world123
System.out.println(sb1); // hello world123 // 原字符串直接改变
System.out.println(sb1 == sb2); // true // 但是地址指向不变!
System.out.println(sb1.charAt(0)); // 获取0号位上的字符 h
System.out.println(sb1.length()); // 获取字符串的有效长度14
System.out.println(sb1.capacity()); // 获取底层数组的总大小 21
sb1.setCharAt(0, 'H'); // 设置任意位置的字符 Hello world123
sb1.insert(0, "Hello world!!!"); // Hello world!!!Hello world123
System.out.println(sb1); // Hello world!!!Hello world123
System.out.println(sb1.indexOf("Hello")); // 获取Hello第一次出现的位置0
System.out.println(sb1.lastIndexOf("hello")); // 获取hello最后一次出现的位置 -1
sb1.deleteCharAt(0); // 删除首字符 ello world!!!Hello world123
sb1.delete(0,5); // 删除[0, 5)范围内的字符 world!!!Hello world123
String str = sb1.substring(0, 5); // 截取[0, 5)区间中的字符以String的方式返回 world
System.out.println(str); // world
sb1.reverse(); // 字符串逆转 str1是StringBuilder形式,所以直接在原来上改变 321dlrow olleH!!!dlrow
str = sb1.toString(); // 将StringBuffer以String的方式返回
System.out.println(str); // 321dlrow olleH!!!dlrow
}
}
String str = new String("ab"); // 会创建多少个对象
// 2个对象:常量池“”+堆中对象new
String str = new String("a") + new String("b"); // 会创建多少个对象
// 6个对象:拼接时会创建StringBuilder对象,而最后是将StringBuilder对象赋值给String对象,需要调用toString ()方法,此时会new一个String对象
-'a'是为了使数组空间开辟小一点
String字符串类:三种方式进行构造–
String存储路径:栈区寸地址,堆区(value存字符串地址-其本质是一个字符串,hash存hash值,默认是0)
求长度:注意区分数组与字符串–字符串是方法()
基本数据类型:==就是比较值大小 引用数据类型:==比较地址
new对象都是new一个新的对象,则存储的都是新的地址,即使存储的内容一样也不影响
字符串中equals重写了Object中的方法,则字符串比较内容用这个
字符串compareTo()是比较大小:按照字典顺序比较
java中字符串没有所谓的\0 结尾一说
忽略大小写比较
charAt(int index) – 获取字符
indexOf(int ch) – 用来查找第一次出现ch的下标
转化: String.valueOf()–把其他类型数据转为String
String转为int:Integer.valueOf(字符串"") or Interger.parseInt(“字符串”)–常用(解析为)
大小写转换:改变大小写并不是在本身当前的字符串上改变,而是产生了一个新的对象
字符串变为字符数组:s.toCharArray(); 格式化输出
字符串替换: (old,new)替换后原来字符串不变
字符串分隔:split(); 注意ip地址分割时以\.作为参数–转义字符 写\时也要注意再加一个\,防止转义 多个分隔符则|进行分隔–alt+回车可以改为正则表达式
多次拆分
截取字符串 substring --new了一个对象 一般左闭右开!!
s.trim(); // 去掉字符串左右的空格
19.字符串常量池:双引号引起来的String值放到常量池中 , 常量池中只会存一份相同的值
第二次存储引号的字符串时,会去常量池中检查是否已经存在,若是存在则返回地址,不存在则进行创建
new一个对象 一定是进行创建的 只要双引号引起的就会在常量池创建对象
【补】字符串不可修改性
s.intern(); 检查s所指向的对象在常量池中是否存在,若是不存在则把当前对象入池;存在则返回常量池对象。
private final char value[]; // 字符串不可变与final无关,private修饰,类外拿不到这个变量,所以字符串是不可修改的。 final修饰只是证明value所指向的对象不变,即地址不变,但是可以通过下标引用来修改某个值。
当字符串进行拼接(s += “”;)时是重新创建一个全新的对象!
字符串的修改:在.class目录下,按住shift打开PowerShell窗口, javap -c 类名 进行反编译
(反编译时: 构造方法 O:无参 V:无返回值
当字符串进行拼接(s += “”;)时是重新创建一个全新的StringBuilder对象,每一次拼接+=都要创建!
利用StringBuilder 对象调用.append()方法时,所指向对象一直保持不变,每次拼接都放在所指向的同一个堆区
StringBuffer与StringBuilder中append区别:StringBuffer用于保护线程安全,由synchronized修饰,即 类似于上锁功能,只有在使用完成后才释放开锁,安全。多线程中最常使用StringBuffer类。但并不意味着StringBuffer就好,因为上锁、开锁需要耗费资源。
作业: