1.String类的重要性
1.c语言中没有String类型,java中有
2.在java开发过程中String类用到是非常多。
1.1String源码
(1).双击shift 搜索String 点击 String 进入源码
(2)实现了compareTo接口
(3)里面有非常多的方法
(4)大家感兴趣点进去看,这里不进行详细讲解。
2.String类的用法
2.1定义String类型的对象和输出对象
2.1.1简单使用
2.1.1.1输出简单字符串
1.第三行代码使用了定义了一个String类型的str对象。这个对象指向的是String字符串可以等价于第四行代码
2.第三行代码和第四行底层是一样的,正常使用的是第三行代码,因为这是一种精简。
3.使用了一个参数的构造方法帮助构造了字符串对象。
代码
public class Test {
public static void main(String[] args) {
String str = "String" ; //第三行
String str_1 = new String("String");//第四行
}
}
4.输出str对象
public class Test {
public static void main(String[] args) {
String str = "String" ;
String str_1 = new String("String");
System.out.println(str);
System.out.println(str_1);
}
}
2.1.2抛出疑问?为什么str属于引用类型存的是对象的地址却输出了这个对象所指引用的内容??
1.String是一个类那么,也就是说重写了ToString中的方法
2.进入源码,进入String类,找到ToString方法。可以看到返会了这个this
Public String tiString(){
return this ;
}
3.点击 out进入源码
可以看到Write方法,这个方法帮助你进行输出,Write又嵌套了一些方法感兴趣可以点开看
(这个sout方法是非常复杂的体现出)
2.1.1.2输出字符串数组
1.代码
public class Test {
public static void main(String[] args) {
// String str = "String" ;
// String str_1 = new String("String");
// System.out.println(str);
// System.out.println(str_1);
char [ ] array = {'a','b','c','d'};
String str3 = new String(array);
System.out.println(str3);
}
}
输出结果
以上就是String类中常用的构造方法,其它在需要的情况下去jdk8在线文档中查找需要的String类的构造方法。
2.2 图解String(放入内存中讲解)
1.代码
public class Test {
public static void main(String[] args) {
String s1 = new String("hello");
String s2 = new String("world");
String s3 = s1;
}
}
2.内存中的样子(图解) ,在内存中如何组织的。
3.左边是虚拟机栈 右边是堆(堆里面又常量池什么的,这里不详细讲解这些,数据结构map部分会讲解)
(1)S1是局部变量(引用变量存的是地址0x12)
(2)S1 new 了String 这个局部变量就指向了这个局部变量(地址相同)
在new 一个String的时候会产生一个空数组,hash 和value
(3)String中有两个占用内存了局部变量分别是hash(默认是0)和value(默认是数组引用(存的数组的地址))
(4)在这里就会把hello存入一个数组中(new String时产生的空数组),而这个数组的地址也存入了value中
(5)这张图省略了常量池,加上常量池会有一点点变化,但是没啥区别,要想知道常量池要先知道hashmap
(1)按照S1的过程再画一遍S2.
(2)S3这个引用指向了S1这个引用指向的对象,相当于内存有了一个S3给S3开辟一个内存
(1)S3引用指向了S1引用,指向同一个对象所以S3和S1是相同的输出结果。
可以看到运行的结果是hello。
3.String常用方法
3.1length()获取字符串长度
1.(字符串类型)+length()。
运行结果是5.
3.1.1:length()底层源码讲解
进入源码,可以看到public int length()所以length是一个返回值类型为int的方法
可以通过int来接受也可以用sout来输出。
3.2 isEmoty()获取字符串长度
3.2.1如何使用isEmoty()
1.和length()是一样的格式,用来判断字符串长度是0还是大于0,大于0时,输出false等于0时输出true
代码
System.out.println("输出:"+str4);//定义一个空的字符串,输出为空
System.out.println(str4.length());//输出为0
System.out.println(str4.isEmpty());//输出为true
运行结果
3.2.2疑问当str4为null时isEmoty输出的是什么?
1.运行会报错
3.2.3这里引出“”和null有什么区别 ,两个空的区别
(1)java是一个面向对象的语言,null在java中的意思是不指向任何一个对象,而“”指向的是一个空的字符串对象,所以isEmoty在字符串对象等于null时会报错。(空指针异常)
3.3 equals方法
3.3.1 定义
(1)equals方法是用来比较字符串是否相同,相同输出true,不相同输出false。
3.3.2 代码实现
public class Test {
public static void main(String[] args) {
String s1 = new String("hello");
String s2 = new String("world");
System.out.println(s1 == s2);//比较的是地址
System.out.println(s1.equals(s2));//比较的是字符是否相同
}
}
运行结果
3.3.3 equals源码讲解
1.进入源码
(1)发生向上转型,也就是重写了equals方法,
(2)将两个字符串放入数组中,再用while循环一个一个进行比较
(3)如果字符不一样就直接return false。
(3)全部一样return true。
2.如果不被String重写,调用的就是Object中的equals方法(一个小细节)
以后比较字符串是都相同就用equls方法
3.3.4疑问新建两个对象,里面的字符相同,地址不同为什么它的输出却是true
String s3 = "hello" ;
String s4 = "hello" ;
System.out.println(s3 == s3);//比较的是地址
System.out.println(s3.equals(s4));
输出结果是两个true
(1)这里会牵扯常量池我会再数据结构中的map文章中进行详细讲解。
(2)java中有一个非常特殊的内存存储再堆上叫做字符串的常量池
(3)这个常量池的作用是只要是双引号的就会放进去,而且当用的时候就会放进去找,相同的只会放一次。
(4)所以这段代码用的是一样的一个hello所以地址也是相同的。
3.4 总结
1.用compareto方法也可以比较字符串大小compareTo比较字符串大小,引出equals方法。
代码
public class Test {
public static void main(String[] args) {
String s1 = new String("hello");
String s2 = new String("world");
System.out.println(s1.compareTo(s2));
//大于返回1 小于返回小于小于0的数字 等于返回0
}
}
2.总结,比较大小的时候对应字符进行比较
(1)对应字符不一样就看对应字符的Ascii值
(2).compareto的返回值是Acsii的差值
(hz字符串的Acsii的值大于hel字符串的Acsii的值那么hz再compareto方法中就是大于hel)
所以我们就引出了equals方法来对字符串大小进行比较
3.总结equals方法。
进入源码
if语句将字符串装入数组一个一个进行比较,只要一个不相同就return false
3.4.1equalsIgnoreCase()后缀忽略大小写进行比较大小(汉字不可以)
1.在输入验证码或者身份证的时候,大写小写都不会影响结果像这样如何实现
2.equalsIgnoreCase()语句进行比较就会忽略大小写,例如用equals比较hello字符串和Hello字符串时,结果一定是false,用了equalsIgnoreCase()就是true。
代码
public class Test {
public static void main(String[] args) {
String s1 = new String("Hello");
String s2 = new String("hello");
System.out.println(s1.equals(s2));
System.out.println(s1.equalsIgnoreCase(s2));
}
}
运行结果
同样的conpareto方法也可以在后面加IgnoreCase后缀来解决大小写问题这里不详细讲解。
3.5字符串查找
3.5.1所有方法
1.实现所有代码和详细讲解
KMP算法记得去看一下,并不是这些代码的源代码,到blibli上去找KMP算法自己了解一下
代码
有详细讲解在代码注释中
public class Test {
public static void main(String[] args) {
String s = "aaabbbcccaaabbbccc";
System.out.println(s.charAt(3)); // 结果'b'
//返回字符串第三个位置是b
System.out.println(s.indexOf('c')); //结果 6 第六个遇到了c
//返回对应字符出现的下标,从头开始一个一个查,碰
// 到
//第一个就输出这个字符的下标
System.out.println(s.indexOf('c', 10)); // 结果15 从指定字符位置查找(10位置)
//指定10位置开始往后面查啥时候遇到10,
//在10之后的15位置遇到了c
System.out.println(s.indexOf("bbb")); // 结果3
//查找bbb连起来这段字符串出现的位置
//输出的角标是bbb中的第一个b的角标
System.out.println(s.indexOf("bbb", 10)); // 结果12
//从指定字符位置查找(10位置)
//指定10位置开始往后面查啥时候遇到bbb,
//在10之后的12位置遇到了bbb输出bbb的第一个b的角
//标
System.out.println(s.lastIndexOf('c')); // 结果17
//字符是一个Ascall码值所以用int接收
//从后往前找,返回第一个C出现的位置
//找不到返回-1.
System.out.println(s.lastIndexOf('c', 10)); //结果 8
//从10位置开始往后面开始找
System.out.println(s.lastIndexOf("bbb")); // 结果12
//从后面开始找bbb字符串出现的位置
//找不到返回-1
System.out.println(s.lastIndexOf("bbb", 10));//结果3
//从10位置往后开始找bbb字符串出现的位置
//找不到返回-1
}
}
不能够越界,不然会报错(越界异常)
3.6字符串转化
3.6.1.数字转化为字符(提了一下序列化和反序列化数据结构会详细讲解)
代码
class Student{
String name = "Frank" ;
int age = 18 ;
public Student(String name, int age){
this.name = name;
this.age = age;
}
public String toString(){
return "name:"+ name +"age"+age;
}
}
public class Frank {
public static void main(String[] args) {
//把整数转化为字符串
String str = String.valueOf(123);
System.out.println(str);
//这个过程,你可以理解为序列化 ||| 序列化和反序列化数据结构章节会详细讲解
String str2 = String.valueOf(new Student("张三",16));
//把一个对象变成了一个字符串
System.out.println(str2);
}
}
3.6.2字符串转整数
1.使用格式
代码
int a = Integer.parseInt("123");//字符串转整数
int a2 = Integer.parseInt("123");
System.out.println(a);
System.out.println(a2);
运行结果
3.7. 字符串相互转换源码讲解(整数相互转换(字符串)
进入parseInt源码,和valueof中的源码
int a = Integer.parseInt("123");//字符串转整数
int a2 = Integer.valueOf("123");//数字转字符串
System.out.println(a);
System.out.println(a2);
运行结果 :123 123
(1)valueof源码
(2)parseInt源码
(3)分析
在valueof方法中的源码调用了parseint源码,其实这两个方法是同一个方法,可以不用valueof,
(————)这就体现出是包装类类里面已经有现成写好的方法有一个或无数个,使用起来非常的方便(我的valueof方法不用自己写直接调用parseint方法就可以)
3.8字符串转数字(double类型)
1和int方法是一样的只不过类型换成Double 即可
代码
// 注意:Integer、Double等是Java中的包装类型,这个后面会讲到
int data1 = Integer.parseInt("1234");
double data2 = Double.parseDouble("12.34");
System.out.println(data1);
System.out.println(data2);
3.9 大小写转换
3.9.1.转化为大写(Str.toUpperCase)
代码
public class Frank {
public static void main(String[] args) {
String str = "Hello";
String ret = str.toUpperCase();
System.out.println(ret);
}
}
用ret接收转为全部大写的str,toUpperCase 中式的方法式int类型,定义一个str对象接收它
3.9.2.疑问:是不是在原来字符串进行修改的?
答案:不是,转变成大写之后,是产生了一个新的对象
进入源码toUpperCase
可以看到在3085行新实例化了一个对象来对全部转为大写的“HELLO”进行接收
总结:字符串的改变都是产生了一个新的对象
3.9.2小写转化(toLowerCase)
1代码
import java.util.Locale;
class Student{
String name = "Frank" ;
int age = 18 ;
public Student(String name, int age){
this.name = name;
this.age = age;
}
public String toString(){
return "name:"+ name +"age"+age;
}
}
public class Frank {
public static void main(String[] args) {
String str2 = "Hello";
String ret2 = str2.toLowerCase(Locale.ROOT);
System.out.println(ret2);
}
}
和转化为大写同理,不进行详细讲解。
运行结果
3.10字符串转数组toCharArray()
3.10.1.代码
import java.util.Locale;
public class Frank {
public static void main(String[] args) {
String str2 = "Hello";
char[] str3 = str2.toCharArray();
for (char ch: str3) {
System.out.println(ch);
}
}
}
运行结果
通过foreach循环输出数组
3.11格式化输出(format)
代码
String s = String.format("%d-%d-%d", 2019, 9,14);
System.out.println(s);
这三个%d分别指的是2019,9,14.
输出结果2019-9-14
3.12字符串的替换(用的较多)(都是产生新的对象)
1.使用指定的新字符替换掉已有字符串数据,可用方法有
(1)replaceAll代码实现(替换全部)
(--)替换单个字母(replace)
import java.util.Locale;
public class Frank { public static void main(String[] args) {
//替换所有老的参数为第二个参数
String ret = "ababababab";
String Frank_1 = ret.replace('a','b');//替换字符a为b,实例化新对象接收新字符串
System.out.println(ret);
System.out.println(Frank_1);
}
}
运行结果
(--)替换多个字符串(replaceAll)
代码
//可以替换所有字符串
String str2 = "ababababababababababa";
String Frank_2 = str2.replaceAll("ab", "pppppp");//替换子字符串ab为pppppp
System.out.println(Frank_2);
运行结果
(2)replaceFirst代码实现(只替换第一个)
代码(也可以只替换一个)
String str2 = "ababababababababababa";
String Frank_2 = str2.replaceFirst("ab", "pppppp");//替换子字符串ab为pppppp
System.out.println(Frank_2);
运行结果
3.12.1字符串的拆分split(用的多)
3.12.1.1.split源码解析:
返回类型是一个字符串类型的数组所以在使用这个方法的时候要定义一个String类型的字符串数组来接收。
3.12.1.2.split的使用
(1)代码 :在#处分割,用foreach循环便利数组产生分割
public class Frank {
public static void main(String[] args) {
String frank = "Frankshishijieshangzui#bangde1024";
String[] splitArray = frank.split("#");
// Print each element of the array
for (String element : splitArray) {
System.out.println(element);
}
}
}
运行结果
(2)也可以分割多个
代码:的意思是只要碰到#就分割,但是规定分割5次。(如果写了1000,虽然不能分割1000次,所以只要不够分割就会结束了)
public class Frank {
public static void main(String[] args) {
String frank = "Frank#shi#shi#jie#shang#zui#bang#de#1024";
String[] splitArray = frank.split("#",5);
// Print each element of the array
for (String element : splitArray) {
System.out.println(element);
}
}
}
运行结果
(3)foreach循环和for循环一样以下是讲解,知道咋用也许。
3.12.1.3.特殊的分割 (IP分割)
定义String类型的变量字符串为"192.168.1.1",(.)进行分割。
运行结果是空的也就是没有分割
public class Frank {
public static void main(String[] args) {
String frank ="192.168.1.1";
String[] splitArray = frank.split(".");
// Print each element of the array
for (String element : splitArray) {
System.out.println(element);
}
}
}
运行结果
当用(.)号分割的时候计算机是用正则表达式识别(想详细了解到blibli搜一下大学老师讲的比我详细)(简单说一下就是,计算机遇到.的时候要进行转义才能识别,这时候就要用正则表达式进行转义),而转义的方法就是在(.)的前面加上两个\\就是这样(\\.),这时候计算机就可以识别(.)号了。
修改完的代码
public class Frank {
public static void main(String[] args) {
String frank ="192.168.1.1";
String[] splitArray = frank.split("\\.");
// Print each element of the array
for (String element : splitArray) {
System.out.println(element);
}
}
}
运行结果:分割完毕(\\.)
3.12.1.3.1特殊符号导致特殊的分割注意事项
1. 字符"|","*","+"都得加上转义字符,前面加上 "\\" .
2. 而如果是 "\" ,那么就得写成 "\\\\" .
3. 如果一个字符串中有多个分隔符,可以用"|"作为连字符.
(
//第三种特殊情况代码解释
String str "192#168=1=1";
String[] ret = str.splite("#|=")//以# 和 = 进行分割
)
3.12.1.4多次分割
代码
public class Frank {
public static void main(String[] args) {
String str = "Frank#deweng&zhang*shizuibangde1";
String []Frank_1 = str.split("#");
for (String x : Frank_1) {
String [] Frank_2 = x.split("&");
for (String xx: Frank_2) {
System.out.println(xx);
}
}
}
}
运行结果
3.13 字符串为什么不能被修改(引出两个新的String方法)
1.在java中String类是被private final 修饰,没有提供get和set方法,它不可以被继承,也不能被重写(这样的目的就是为了安全性)
2.String类是存储在value数组中的(value只能引用当前初始化好的对象)
3.许多人说字符串不能够修改的原因是源码中的两个final,事实是每一个final都是有不同的用处的。
4.字符串加号拼接不建议会大量创建对象消耗时间过多,放入循环中会一直新建对象使得时间利用率大大减少
代码(只要循环一次就创建一个对象,浪费内存)
public static void main(String[] args) {
long start = System.currentTimeMillis();
String s = "";
for(int i = 0; i < 10000; ++i){
s += i;
}
3.14 StringBuilder 和String Buffer
1.由于String的不可更改特性,为了方便字符串的修改,Java中又提供StringBuilder和StringBuffer类。这两个类大 部分功能是相同的,这里介绍 StringBuilder常用的一些方法。
2.格式
public class Frank {
public static void main(String[] args) {
StringBuffer stringBuffer = new StringBuffer();
StringBuilder stringBuilder = new StringBuilder();
}
}
3.用StringBuilder来作为实例进行源码讲解。
StringBuilder实现了Serializable接口,当new了一个StringBuilder的时候,会生成一个数组长度是16,也可以选择自己定义长度.这个源码就简单理解到这里
4.StringBuilder的使用方法 (单线程)
StringBuilder中有非常多的方法,但只要细分就不会那么多,可以看到有append开头的方法,delete开头的方法,insert开头的方法这样就分的没那么多了。
StringBuilder最主要使用的是append开头的方法(添加的意思)
5.StringBuilder中append方法的使用方式
public class Frank {
public static void main(String[] args) {
StringBuffer stringBuffer = new StringBuffer();
StringBuilder stringBuilder = new StringBuilder("HelloWord");
System.out.println(stringBuilder.append("Frank"));
}
}
运行结果
可以发现是对同一个对象进行操作并没有新创建一个对象来接收HelloWordFrank。
还可以这样子拼接字符串.append来增加字符串
5.疑问?为什么不会新建对象?
进入append方法中的(一直是在调用方法进行重写,返回的一直是this,所以一直对应的就是当前对象,所以称之为可变的对象)
还有拼接删除等方法,使用访问时和拼接差不多就不一一讲解了(StringBUilder和StringBUffer差不多,都有这些方法)
6.StringBuffer讲解(多线程使用)
(1)进入源码(StringBuffer.append方法中(其他方法举一反三))
这里有一个单词synchronized(同步的(StringBuilder方法中是没有这个修饰词)),这个单词在java中的意思相当于(你去上厕所,厕所门上有一把锁叫sychronized进去的时候会上锁,你出来的时候会解锁这样排队的那个人就可以来进来上厕所),synchronized的作用在多线程的时候会体现出来,(频繁上锁和开锁会浪费系统的资源当单线程就用StringBuilder)
(2)在String Buffer并不是String类型而是StringBuffer类型,可以用toString方法将StringBuffer转换为字符串类型
String str = new StringBuilder().toString();
(3)String类型转StringBuilder类型
String Frank_3 = "Hello";
StringBuilder str_1 = new StringBuilder(Frank_3);
7.总结
1. String、StringBuffer、StringBuilder的区别 String的内容不可修改,StringBuffer与StringBuilder的内容可以修改. StringBuffer与StringBuilder大部分功能是相似的 StringBuffer采用同步处理,属于线程安全操作;而StringBuilder未采用同步处理,属于线程不安全操 作