String
API文档:public final class String extends Object
深入理解:
先继承 Object类加上自己特有的东西成为String类,此时才被final修饰,表明String类不能被继承
问题1:打印String对象s为什么输出的对象本身而不是地址?
答: 对于Student student =new Student(),运行System.out.println(student)打印的是地址
原因:其实这涉及到println(Object object)的源码(底层调用了toSting()方法),因为Student默认继承Object,当然继承了Object的toString()方法,又没有重写toString()方法,所以当然输出的是地址;而对于println(String str),由于重写了toString()方法,返回的是对象本身,具体看下面toString()的源码:
/**
* String 类型的toString()源码:
* public String toString() {
* return this;//this代表当前的字符串对象
* }
* */
① public String(byte[] bytes,Charset ch);------ 解码(解密)(构造方法)
String()构造方法,创建对象时使用;
返回值类型:String
功能:将字节数组转换成对应的字符串,首先利用解码集(GBK还是UTF-8)通过ASCII码表将字节转化为对应的字符(具体是几个 字节对应一个字符),然后拼接成字符串;不同的字符集用的ASCII码的范围不一样
② public byte[]getBytes(String charsetName);---编码(加密)
功能:得到一个系统指定编码格式的字节数组(将字符串转换为字节数组)
返回值类型:byte[] ;
参数列表(编码格式的字符集)("GBK"或"utf-8",如果是GBK格式,可以不写参数)
语法:对象名.getBytes(参数)
说明:GBK格式下:一个中文字符2个字节
UTF-8模式下:一个中文字符3个字节
举例:“你好”字符串的说明:
[-60, -29, -70, -61] :GBK模式
[-28, -67, -96, -27, -91, -67]:utf-8模式
补充(回顾):对Arrays.toString()说明:public static String toString(byte [] a);
功能:返回指定数组内容的字符串表示形式将字节数组转化为对应的字符串;
所谓指定内容的字符串:先将字节直接加''转化为字符,再进行拼接,同时拼接了'['和']')得到得到字符串(从源码分析得知)
③ public String(char[] value);----构造方法
功能:将字符数组构造成一个字符串
如何构造:拼接(感兴趣的话可以看源码)
④ public String(String original)---构造方法
功能:通过字符串常量构造一个字符串对象,创建对象时使用
说明:除了在常量池开辟空间,还在堆内存开辟空间(耗内存)
补充:必须掌握此种创建对象完成的过程
⑤ public int length();
功能:获取字符串中字符的长度
⑥ public String(char[] value, int offset, int count)---构造方法
参数列表:(字符数组,首索引位置,字符串长度)
功能:获取一个子字符串
索引:前后均包括
实例1:
package org.westos.string_01;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;//学会自动导入:ctrl+shift+o
public class StringDemo {
public static void main(String[] args) throws UnsupportedEncodingException {
// 创建一个String对象
String s1 = new String("Hello World!");
System.out.println("s1.length():" + s1.length());
System.out.println("s1:" + s1);
System.out.println("----------------------");
byte[] bys = { 97, 98, 99, 100, 101 };
String s2 = new String(bys);
System.out.println("s2:" + s2);//输出结果可以看出:字节---字符---字符串
System.out.println(s2.toString());
System.out.println("s2.length():" + s2.length());
System.out.println("----------------------");
String s = "你好";
byte[] bys2 = s.getBytes();//编码
System.out.println(bys2);//地址值(引用),未重写toString()
System.out.println(Arrays.toString(bys2));//特点:"["+字节值+","+"..."+"]"--原汁原味
System.out.println("加密结束---------------");
String str = new String(bys2);//默认GBK
System.out.println("str:" + str);
System.out.println("解密结束-------------------");
char[] chs = { '我', '爱', '高', '圆', '圆' };//创建字符数组
String s3 = new String(chs);
System.out.println("s3.length():" + s3.length());
System.out.println("s3:" + s3);
System.out.println("--------------------");
String s4 = new String(chs, 1, 4);//构造新的子字符串(相应位置索引,前后均包括)
System.out.println("s4:" + s4);
System.out.println("s4.length():" + s4.length());
System.out.println("--------------------");
String s5 = new String("hello");
System.out.println("s5:" + s5);//本来应该打印地址的,但由于toString()的重写,打印的是对象
System.out.println("s5.length():" + s5.length());
String s6 = "hello";
System.out.println("s6:" + s6);//与上对比,可以看下是否创建了s6的对象
System.out.println("s6.length():" + s6.length());
}
}
那么问题来了:
两种创建字符串对象:String s = new String("hello")和 String s="hello"的区别 ?
了解常量池折叠(常量编译优化机制)、常量池 、拘留字符串(编译时在堆内存创建的) 、堆内存概念
常量池概念:JVM中存在着一个常量池(字符串池),其中保存着很多String对象(由于String是引用类型;实际是字面值),并且可以被共享使用,因此它提高了效率。它的值一经创建就不可改变,因此我们不用担心String对象共享而带来程序的混乱;字符串池由String类维护,我们可以调用intern()方法来访问字符串池。
直接赋值形式:形如String s="hello";在Java中当出现字符串常量("hello")时,JVM会先检查看一看常量池有没有当前对象("hello")
如果已经有这个对象(String s1="hello"),会把当前对象的引用(地址值)给s,相当于s1和s指向对一个对象,二者引用是相同的
相关参考:点击打开链接
分析String s="abc";的执行过程:这行代码被执行的时候,JVM在编译时期首先在字符串池中查找是否已经存在了值为"abc"的这么一个对象,它的判断依据是String 类equals(Object obj)方法的返回值(猜测要遍历字符串常量池的对象)。如果有,则不再创建新的对象,直接返回已存在对象的引用;如果没有,则先创建这个对象,然后把它加入到字符串池中,再将它的引用返回;String常量池中引用的地址值给s
内存形式(后续会上图)
分析String s2=new String( "hello ");的执行过程:JVM首先(编译时)在常量池内寻找是否有字符串 "hello ",找到了不做任何事情,否则的话创建新的string对象,放到字符串池里面。由于遇到了new(运行时),还会在堆内存上开辟内存空间存储 对象"hello",并将堆内存s对象的引用返回给s2;
扩展1:如何判断字符串常量池中是否有该对象(堆内存内容)的引用,通过intern()说明是否在堆内存也开辟了空间
字符串常量池:点击打开链接,点击打开链接,点击打开链接,点击打开链接intern()的深入理解--点击打开链接(深挖)--包含评论
扩展2:理解的话结合线程池的功效去理解
实例2
package org.westos.string_02;
/**
*字符串的一个特点:
* 一旦被赋值(初始化),引用指向的内容是不可变的(注意:是内容(值)不可变---内容根据创建String对象的不同而异)
* 其值不变理解:对象(引用)不变,一旦对其进行操作,必然会创建新的对象
*原来错误的理解:String s="hello";s存储的是"hello",其实还是引用(地址值)
* @author 王志剑
*/
public class StringDemo {
public static void main(String[] args) {
String s1= "hello";//s1实际存储的还是一个地址(引用),由于toString()重写,所以没有打印地址值
String s = "hello";
System.out.println(s==s1); //分析(1)的结果(常量池:效率比较高)
s += "world";//(看源码可知,实际:new StringBuffer----在堆内存创建了一个对象)
//s之前指向的内容(引用对应的值)没有变化,但此时s不再指向原来的那个值了(s的地址值发生变化),
//而是一个新的String变量,原来的变量还在内存中(等待回收),没有执行该对象的引用了
System.out.println("s:"+s);//分析(2)的结果
change(s);//调用方法
System.out.println("s:"+s);//分析(3)的结果
System.out.println("--------------------------");
String vivio=change1(s);
System.out.println("s:"+vivio);分析(4)的结果
//System.out.println("s:"+vivio);//很迷惑(查资料!!!!,哦懂了!!!)
System.out.println("--------------------------");
//未被final修饰,这时会通过new StringBuffer形式,在堆内存开辟新的空间(通过反编译工具可知)
String q="hhh";
String w="hhhh";
String t=q+"h";
System.out.println(t==w);//分析(5)的结果
System.out.println("--------------------");
//补充(常量折叠:被final修饰变量,编译器优化)
final String a="hhh";
String b="hhhh";
String c=a+"h";
System.out.println(c==b);//分析(6)的结果
System.out.println("--------------------");
//常量折叠(编译器优化)
String avii="哈哈w";
String se="哈哈"+"w";//常量+常量
System.out.println(avii==se);//分析(7)的结果
System.out.println("--------------------");
final String zx="1";
final String zc="2";
String zv="12";
String zb=zx+zc;
System.out.println(zb==zv); //分析(8)的结果
System.out.println("--------------------");
String sb=new String("java");
String sb1="java";
System.out.println(sb1==sb.intern());//分析(9)的结果:sb.intern()会在常量池内查询是否存在该字符串(sb的值)
System.out.println(sb==sb.intern());//分析(10)的结果:new 创建了两个对象
/*分析(9):intern()是将sb指向堆对象的内容变成"拘留字符串"(常规字符串转换成拘留字符串)
*即:字符串常量池中是否有该对象的引用
*由于"=="比较的是引用地址,从结果上可以判定:
*通过new是创建了两个对象(先后,内存情况有时间了再补)
*String sb1="java";实际未创建对象,
*原因是:通过String的equals()方法发现常量池已经有该字符串,直接把该应用辅给sb1,无须再创建对象,所以结果true
* */
}
public static void change(String s) {
s += "javaweb" ;
//创建一个新的对象----(在堆内存创建一个对象,其引用指向方法区(不同于栈、堆、常量池),生命周期随方法结束而结束)
}
public static String change1(String s){
s += "javaweb" ;
//System.out.println("s:"+s);
return s;//返回的是新的对象的引用[理解String中的方法:例如产生新字符串等(都是创建了新的对象)]
}
}
/*
* 在常量池保存的其实是一个引用(其实保存的是编译时期在堆内存中“拘留字符串”的地址)
* String s=new String("字符串")其实是在编译时看常量池是否有该字符串,有的话不管,直接把该字符串的引用给s
* 没有的话在堆内存创建一个拘留字符串,并把这个堆内存的拘留字符串的引用返还给常量池
* 此时通过new(运行时)在堆内存开辟空间(把拘留字符串复制过去),创建对象,再把此对象的引用赋给堆内存
*/
⑦ String类的中常用的判断功能:
boolean equals(Object obj):当前该对象是否与obj这个对象是否相等;
boolean equalsIgnoreCase(String str):比较字符串是否相等,忽略大小写
boolean contains(String str):判断str这个字符串是否包含在当前字符串中
boolean startsWith(String str):是否以str子字符串开头
boolean endsWith(String str):判断是否以str子字符串结尾
boolean isEmpty():判断字符串是否为空
说明:
String s = ""; 空字符
String s = " "; 字符串"空格"
String s = null;当前字符串对象为空
注意:开发中这些方法的应用场合(自己慢慢积累)
实例3:
package org.westos.string_03;
/**
*
* @author 玉无心,心落石(谐)出,剑走偏锋
*/
public class StringDemo {
public static void main(String[] args) {
//定义一个字符串
String s1 = "helloworld" ;
String s2 = "HelloWorld" ;
/* 内容1:
* boolean equals(Object obj):
* 功能:当前该对象是否与obj这个对象是否相等;
* 返回值类型:boolean;
* 语法:this.equal(obj);
* 参数:Object类型的obj
* String重写equals(),首先判断是否为空,然后比较的是两个字符串的内容是否相同
* */
System.out.println("equals:"+s1.equals(s2));
/* 内容2:
* boolean equalsIgnoreCase(String str):
* 功能:比较字符串是否相等,忽略大小写
* 测试如下
* */
System.out.println("equalsIgnoreCase:"+s1.equalsIgnoreCase(s2));
/* 内容3:
* boolean contains(String str):
* 功能:判断str这个字符串是否包含在当前字符串中
* 测试如下
* */
System.out.println("contains:"+s1.contains("owo"));
System.out.println("contains:"+s1.contains("ak47"));
/* 内容4:
* boolean startsWith(String str)
* 功能:是否以str子字符串开头
* 测试
* */
System.out.println("starsWith:"+s1.startsWith("hel"));
/*内容5:
* boolean endsWith(String str)
* 功能:判断是否以str子字符串结尾
* 测试(暂不补充)
* */
/*内容6:
* boolean isEmpty()
* 功能:判断字符串是否为空(开发中常用到哪?)
* 测试(如下)
*/
System.out.println("isEmpty:"+s1.isEmpty());
/*
* 问题2:
* 为什么String的一些方法(上述)能改变字符串?我们不是说String对象创建完毕后,它的内容无法改变吗?
* 说明:这些看起来能够改变字符串的方法,实际上是创建了一个带有方法所赋予特性的新字符串
* 也即:这些方法不会改变字符串本身,而是创建并返回一个包含改变后内容的新字符串对象
* 注意:具体自己可以看看源码的形式
* */
}
}
equals方法的测试
实例4:
package 测试2;
/**
* 看程序写结果:
* 对(1)处说明:
* 创建了两个对象,但s1,s2的值是在堆内存中分别创建"hello"对象的地址
* ==比较的是引用
*************************************
* 对(2)处说明:
* String类中也有equals()方法:比较内容
* Object中的equals()方法:比较引用
* String的equals()方法重写了
* 注意:必须了解Object中的方法,因为其它类直接或间接的继承了它,可能会涉及到重写
* ***********************************
* 同理(3)、(4)相同
* 对(3)的说明:常量池的引用和堆内存的内容(对象)是建立一定关系的
* @author
*/
public class StringDemo2 {
public static void main(String[] args) {
//创建字符串对象
String s1 = new String("hello") ;
String s2 = new String("hello") ;
System.out.println(s1==s2); //(1)
System.out.println("------------------");
String s3 = "hello" ;
String s4 = "hello" ;
System.out.println(s3==s4);
System.out.println(s3.equals(s4));//(2)
System.out.println("------------------");
String s5 = new String("world") ;
String s6 = "world" ;
System.out.println(s5==s6); //(3)
System.out.println(s5.equals(s6));//(4)
System.out.println(s6==s5.intern());//(5)
System.out.println(s5==s6.intern());//(6)
String s=s5.intern();//判断常量池是否有该对象(new创建的字符串对象,在堆内存中)的引用,也即用new方式在常量池中存储的是一个引用(而不是内容)System.out.println(s==s6);//true}}
⑧String类常用的获取功能:
int length():获取字符串长度功能(前面介绍过)
char charAt(int index):返回的是索引处对应的字符--按图索骥
int indexOf(int ch):返回指定字符在此字符串中第一次出现处的索引
为什么这里的字符用int来定义:
原因:首先会将int型(97)通过ASCII码表找到对应的字符转化为'a'
int indexOf(int ch,int fromIndex):从指定的索引开始搜索,返回在此字符串中第一次出现指定字符处的索引
int indexOf(String str):返回指定子字符串在此字符串中第一次出现的索引
int indexOf(String str,int fromIndex):返回在此字符串中第一次出现指定字符串处的索引,从指定的索引开始搜索
String substring(int start):从指定位置开始截取字符串,默认截取到末尾
String substring(int start,int end):从指定位置开始截取到指定位置结束,包前(start索引)不包后(end索引)
实例5:
public static void main(String[] args) {
//定义一个字符串:
String str = "helloworld" ;
//int length();获取字符串长度功能
System.out.println("length:"+str.length());
//char charAt(int index):返回的是索引处对应的字符
System.out.println("charAt:"+str.charAt(1));
System.out.println("charAt:"+str.charAt(8));
System.out.println("-------------------------------");
/*
* int indexOf(int ch)
* 说明:返回指定字符在此字符串中第一次出现处的索引
* 如果找不到就提示(返回是:-1)
* */
System.out.println("indexof:"+str.indexOf('l'));
System.out.println("indexof:"+str.indexOf('k'));
/* int indexOf(String str)
* 语法:对象名.indexOf(参数)
* 说明:返回指定子字符串在此字符串中第一次出现的索引
*/
System.out.println("indexOf:"+str.indexOf("owo"));
System.out.println("-------------------------------");
/* int indexOf(int ch,int fromIndex)
* 说明:返回在此字符串中第一次出现指定字符处的索引,从指定的索引开始搜索。
*/
System.out.println("indexOf:"+str.indexOf('l', 4));
/*
* int indexOf(String str,int fromIndex)
* 说明:返回在此字符串中第一次出现指定字符串处的索引,从指定的索引开始搜索
*/
/*
* String substring(int start)
* 功能:从指定位置开始截取字符串,默认截取到末尾(返回一个新的字符串,不再是字符串本身!)
*/
System.out.println("substring:"+str.substring(5));
/* String substring(int start,int end)
* 功能:从指定位置开始截取到指定位置结束,包前(start索引)不包后(end索引)
*/
System.out.println("substring:"+str.substring(4, 8));
}
⑨ String类的常用转换功能(重点)
byte[] getBytes():将字符串转换字节数组(加密:前面提到过)
char[] toCharArray():将字符串转换成字符数组 (开发中经常使用)
注意1:区分转换字符数组(拆)与转化字节数组
static String valueOf(char[] chs):将字符数组转换成字符串
static String valueOf(int i):将一个int类型的数据转换成字符串
String toLowerCase():将字符串全部转成小写
String toUpperCase():将字符串全部转换成大写
String concat(String str):字符串拼接方法
注意:String类中的valueOf()可以将任何数据类型转换成字符串
实例6:
public static void main(String[] args) {
//定义一个字符串
String s = "JavaSE" ;
/*
* byte[] getBytes():将字符串转换字节数组(如何转的)
* 说明:先转化成字符,再找对应的ASCII码表
* */
byte[] bys = s.getBytes() ;
//遍历字节数组
for(int x = 0 ; x < bys.length ; x++){
System.out.print(bys[x]+" ");
}
System.out.println("---------------------");
//char[] toCharArray():将字符串转换成字符数组
char[] chs = s.toCharArray() ;
for (int x = 0; x < chs.length; x++) {
System.out.print(chs[x]+" ");
}
System.out.println("---------------------");
/* static String valueOf(char[] chs)
* 方法:静态的
* 说明:将字符数组转换成字符串(逆转)
* */
String s2 = String.valueOf(chs) ;
System.out.println("s2:"+s2);
/* static String valueOf(int i)
* 功能:将一个int类型的数据转换成字符串
* 转换思路:先转成字符,再拼接成字符串
* */
String s3 = String.valueOf(100) ;
System.out.println("s3:"+s3);
//String toLowerCase():将字符串全部转成小写
System.out.println("toLowerCase:"+s.toLowerCase());
//String toUpperCase():将字符串全部转换成大写
System.out.println("toUpperCase:"+s.toUpperCase());
//String concat(String str):字符串拼接方法
String s4 = "hello" ;
String s5 = "world" ;
System.out.println("contact:"+s4.concat(s5));
System.out.println("contact与如下拼接方法区别?,都在堆内存开辟空间");
String s6 = "java" ;
s6 += "web" ;
System.out.println("s6:"+s6);
}
⑩ String类中的其他功能
替换功能:
public String replace(char oldChar,char newChar):将字符串中某一个字符用新的字符替换
public String replace(String oldStr,String newStr):将字符串中某一个子字符串用新的字符串去替代
去除字符串两端空格:
public String trim()
两个字符串进行比较:
public int compareTo(String anotherString) 是Comparable接口中的方法(该接口可以实现一个自然排序)
Comparator接口可以比较器排序
实例7:
package org.westos.string_01;
public class StringDemo {
public static void main(String[] args) {
//定义字符串
String s1 = "helloworld";
/*
* public String replace(char oldChar,char newChar)
* 单替换功能:将字符串中某一个字符用新的字符替换
*/
String s2 = s1.replace('l', 'k') ;
System.out.println("s:"+s2);
System.out.println("-----------------");
/*
* public String replace(String oldStr,String newStr)
* 全部替换:将字符串中某一个子字符串用新 的字符串去替代
* */
String s3 = s1.replaceAll("owo", "ak47") ;
System.out.println("s3:"+s3);
System.out.println("------------------");
String s4 = " hello world " ;
System.out.println("s4:"+"----"+s4+"---");
/* public String trim();
* 删除两端空格
* */
String s5 = s4.trim() ;
System.out.println("s5:"+"----"+s5+"----");
/*public int compareTo(String anotherString)
* 遇到:看源码:comparTo()的源码(用在哪呢?)
* 对源码解释:
* 1)先得出两个字符串长度较小的长度m
* 2)从二者第一个字符开始比较,如果相等比较下一个字符,直到不同,输出对应字符ASCII码表的差值
* 3)如果字符串遍历到m的地方,则结束此时输出的是两个字符串长度的差值
*/
String s6 = "hello" ;
String s7 = "hell" ;
String s8 = "abc" ;
System.out.println("compareTo():"+s6.compareTo(s7));
System.out.println("compareTo():"+s6.compareTo(s8));
}
}
创建对象说明:点击打开链接
常量池:点击打开链接
大神:点击打开链接
内存:点击打开链接