目录
2.1String类的两种实例化方式:(直接赋值/构造方法/池/常量池)
2.2字符串相等比较("=="比较字符串/equals()比较字符串)
2.4char-------String(补充!String类和基本类型的转换)
2.6何时使用 byte[], 何时使用 char[] 呢?-文本数据 vs 二进制数据
1引用:
在栈上开辟了一小块内存空间保存一个地址
可以把引用想象成一个标签, "贴" 到一个对象上. 一个对象可以贴一个标签, 也可以贴多个. 如果一个对象上面一个标签都没有, 那么这个对象就会被 JVM 当做垃圾对象回收掉
修改str2=str1,内存变化:
String str="code";
String str2=str1;
如果改变str1="Student"会不会改变str2呢?
验证一下!
package se.SE.practice;
public class Test {
public static void main(String[] args) {
String str1="code";
String str2=str1;
str1="Student";
System.out.println(str2);
}
}
我们可以发现,即使改变了str1,但是str2不会改变的,对str1的改变只是让 str1 这个引用指向了一个新的 String 对象
看看他们的内存变化吧!
2String类
2.1String类的两种实例化方式:
池
java中有很多池(常量池,线程池,内存池,数据库连接池),池的引入是为了帮助程序猿萌提高编程效率的
举个栗子~~小明同学是个十足的宅男,最喜欢的就是躺在床上,吃零食。所以他在床边的篮子里放了很多种零食,正在吃的零食是A,其他备用的零食是B,为什么要放这么多备用的零食呢?一旦吃完了正在吃的零食,可以迅速的从床边拿备用零食,提高效率~~那么这些备用的零食组成的集合就可以看作是备用池
常量池
JVM内部会维护一个字符串常量池(对象数组)
a.如果采用直接赋值的方式进行String类的实例化操作,那么该对象会自动保存到对象池中。
b.如果下一次继续使用直接赋值实例化String类对象时,先在对象池中寻找是否有指定内容对象。
1如果有,那么直接引用。
2否则创建新空间,将新对象入池以供下次使用。
2.1.1直接赋值:
String str="Hello";//直接赋值,在堆上分配空间
class Test{
public static void main(String[] args) {
String str1="Hello";
String str2="Hello";
System.out.println(str1==str2);
}
}//true
2.1.2构造方法:
String str=new String("Hello");
内存变化:
1如果使用String构造方法就会开辟两块堆内存空间,并且其中一块堆内存将成为垃圾空间
(字符串常量 "hello" 也是一个匿名对象, 用了一次之后就不再使用了, 就成为垃圾空间, 会被 JVM 自动回收掉).
2. 字符串共享问题. 同一个字符串可能会被存储多次, 比较浪费空间.
实例后的对象不会保存在对象池之中,可以使用intern()方法来手工入池
class Test{
public static void main(String[] args) {
String str1="Hello";
String str2=new String("Hello");
System.out.println(str1==str2);
}
}//false
class Test{
public static void main(String[] args) {
String str1="Hello";
String str2=new String("Hello").intern();//调用intern()方法手工入池
System.out.println(str1==str2);
}
}//true
2.2字符串相等比较
2.2.1"=="比较字符串
两个对象所保存的内存地址数值比较,也就是两个引用是否是指向同一个对象.
比较下面两段代码:
代码1:
class Test{
public static void main(String[] args) {
int i1=1;
int i2=1;
System.out.println(i1==i2);
}
}
//输出结果:true
代码1内存变化:
代码2:创建的 String 对象相当于再堆上另外开辟了空间来存储"Hello" 的内容, 也就是内存中存在两份 "Hello".
class Test{
public static void main(String[] args) {
String str1="Hello";
String str2=new String("Hello")
System.out.println(i1==i2);
}
}
输出结果:false
代码2内存变化:
这里你可能会产生一些疑问啦,为什么都是字符串"Hello"的比较,两段代码的输出结果会有所差别呢?
第二段代码new了一个新的内存空间,即str1和str2的地址不同,因此输出为false
2.2.2equals()比较字符串
进行字符串内容的比较
public boolean String(String anothor String)//方法声明
str1.equals(str2);//调用方法equals()
下面我们用equals()来比较str1 和str2的大小
class Test{
public static void main(String[] args) {
String str1="Hello";
String str2=new String("Hello");
System.out.println(str1.equals(str2));
}
}
//输出结果:true
怎样更好的比较两个字符串的内容呢?equals()方法的最优比较
String str = new String("Hello");
// 方式一
System.out.println(str.equals("Hello"));
// 方式二
System.out.println("Hello".equals(str));
这里更推荐方式二!原因:方式一如果str为null会抛出异常,方式二不会
2.3字符串常量不可变更
堆中常量的值无法修改,栈中的指向可以修改。
class Test{
public static void main(String[] args) {
String str="Happy ";
str+="birthday";
str+="!";
System.out.println(str);
}
}//输出结果:Happy birthday!
上述代码运行过程中,内存变化情况:
一些小建议:
字符串不要改变太多次,慎用"+"
2.4char-------String
- char[]->String
public String(char[] value):将字符数组全部转为字符串
public String(char[] value,int offset,int len):将字符数组部分转为字符串
- String->char
public char charAt(int index):取得字符串指定索引字符,从0开始
- String->char[]
public char[] toCharArray():将字符串转为字符数组
class Test{
public static void main(String[] args) {
String str="happy ";
//将字符串转换为字符数组
char[] data=str.toCharArray();
for(int i=0;i<data.length;i++){
data[i]-=32;
}
//将字符数组转换为字符串
System.out.println(new String(data));//全部转换
System.out.println(new String(data,0,4));//部分转换
}
}
/*输出结果
HAPPY
HAPP
*/
应用toCharArray()方法来判断一个字符串是否全部由数字组成
class Test{
public static void main(String[] args) {
//判断一个字符串是否全部由数字组成
String str1="1234";
String str2="123a4";
System.out.println(isNumber(str1));
System.out.println(isNumber(str2));
}
public static boolean isNumber(String str) {
char[] data = str.toCharArray();
for (int i = 0; i < data.length; i++) {//遍历字符数组
char c = data[i];
if (c < '0' || c > '9') {
return false;
}
}
return true;
}
}//输出
true
false
补充!!
String类和基本类型的转换
String->基本类型
1valueOf方法
2intValue()---new一个字符串对象再转换
//String->int
String str1="100";
//方法一:调用Integer的静态方法valueOf
int num1=Integer.valueOf(str1);
//方法二:new了一个字符串对象,再转换成number
int num2=new Integer(str).intValue();
当字符串中包含非数字,在运行时会抛出NumberFormatException
基本类型->String
1."+"
//int->String
int num=10;
//字符串和number相加就可
String str=num+" ";
2.通过String类的构造方法
推荐:使用String.valueOf(所有基本类型)
2.5Byte-------String
字节用在二进制流和网络传输中
- byte[ ]->String
public String(byte[] value):将字符数组全部转为字符串
public String(byte[] value,int offset,int len):将字符数组部分转为字符串
- String->byte[ ]
public byte[] getByte():将字符串转为字节数组[ ]
public byte[] getByte(String charSet):将字符串按照指定的编码格式转化为字节数组。
Linux默认编码为UTF-8 windows默认编码为GBK
class Test{
public static void main(String[] args) {
String str1="happybirthday";
//字符串转为字节数组
byte[] data=str1.getBytes();
for(int i=0;i<data.length;i++){
data[i]-=32;
}
//字节数组转为字符串
System.out.println(new String(data));
}
}
编码:将字符串按照怎样的算法转为字节
2.6何时使用 byte[], 何时使用 char[] 呢?
a.byte[] 是把 String 按照一个字节一个字节的方式处理, 这种适合在网络传输, 数据存储这样的场景下使用. 更适合
针对二进制数据来操..
b.char[] 是吧 String 按照一个字符一个字符的方式处理, 更适合针对文本数据来操作, 尤其是包含中文的时候
文本数据 vs 二进制数据
一个简单粗暴的区分方式就是用记事本打开能不能看懂里面的内容.
如果看的懂, 就是文本数据(例如 .java 文件), 如果看不懂, 就是二进制数据(例如 .class 文件).
2.7其他方法
字符串的比较方法
- 比较相等:
equals(Object anObject)--区分大小写的比较
equalsIngoreCase(String anotherString)--不区分大小写的比较
- 比较大小:
public int compareTo(String anotherString):比较两个字符串的大小关系
负数:小于
0:相等
正数:大于
class Test{
public static void main(String[] args) {
String str1="abc";
String str2="Abc";
String str3="abc";
System.out.println(str1.compareTo(str2));
System.out.println(str2.compareTo(str3));
}
}输出结果:
32
-32
按照ASCII码比较,只要发现不相等的内容算出差值直接返回,不会继续向下。
字符串的查找
public boolean contains(CharSequence) 判断一个字符串是否存在
public boolean startswith(String prefix) 判断是否以指定字符串开头
public boolean endswith(String prefix,int toffset) 判断是否以指定的字符串结尾
特别的:这里有一个比较特别的方法 indexOf() --------返回int 。JDK1.5之前查找字符串,此方法查找字符串时如果内容重复,它只能返回查找的第一个位置。
class Test{
public static void main(String[] args) {
String str="happybirthday!";
System.out.println(str.contains("happy"));
System.out.println(str.startsWith("happy"));
System.out.println(str.endsWith("!"));
}
}
输出结果:
true
true
字符串的替换
由于字符串是不可变对象, 替换不修改当前字符串, 而是产生一个新的字符串.
public String replaceAll(String regax,String relacement):将字符串中所有指定内容替换成新内容。
public String replaceFirst(String regax,String replacement):替换首个内容
class Test{
public static void main(String[] args) {
String str="happybirthday!";
System.out.println(str.replaceAll("pp","00"));
System.out.println(str.replaceFirst("a","0"));
}
}
输出结果:
ha00ybirthday
h0ppybirthday
字符串的拆分(重点!)
public String[] split(String regax):按照指定格式将字符串全部拆分
public String[] split(String regax,int limit):将字符串部分拆分,数组长度为限定limit长度
//全部拆分
class Test{
public static void main(String[] args) {
String str="happy-birthday-!!!";
String[] result=str.split("-",3);//按照--拆分
for(String s:result){
System.out.println(s);
}
}
}
输出结果:
happy
birthday
!!!
注:当limit的值大于等于(regex+1)时,所拆分段数不变。通俗点解释,在本例中happy-birthday-!!!按“-”拆分,当limit的值大于等于三时,最多可分成3段。
//部分拆分
class Test{
public static void main(String[] args) {
String str="happy-birthday-!!!";
String[] result=str.split("-",2);
for(String s:result){
System.out.println(s);
}
}
}
敲重点啦!!一些比较复杂的拆分形式
1. 字符"|","*","+"都得加上转义字符,前面加上"\".
2. 而如果是"",那么就得写成"\\".
3. 如果一个字符串中有多个分隔符,可以用"|"作为连字符.
//拆分IP地址
class Test{
public static void main(String[] args) {
String str="192.156.1.2";
String[] result=str.split("\\.");
for(String s:result){
System.out.println(s);
}
}
}
输出结果:
192
156
1
2
//多次拆分
class Test{
public static void main(String[] args) {
String str="a:12|b:34";
String[] result=str.split("\\|");
for(int i=0;i<result.length;i++){
String[] temp=result[i].split(":");
System.out.println(temp[0]+"="+temp[1]);
}
}
}
输出结果:
a=12
b=34
class Test{
public static void main(String[] args) {
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]);
}
}
}
字符串的截取
public String substring(int beginIndex):从指定索引开始截取到字符串结尾
public String substring(int beginIndex,int endIndex):从指定索引开始到指定结束位置。
class Test{
public static void main(String[] args) {
String str="happybirthday!";
String[] result=str.split("\\|");
System.out.println(str.substring(5));//从第五个截取到末尾(不包括第五)
System.out.println(str.substring(0,5));//从第一个截取到第五个
}
}
输出结果:
birthday!
happy
其他方法:
public String trim():去掉字符串左右两边的空格,换行,制表符,中间保留。
String str = " hello world " ;
System.out.println("["+str+"]");
System.out.println("["+str.trim()+"]");
public String toUpperCase():字符串转大写
public String toLowerCase():字符串转小写
public int length():取得字符串的长度数组长度使用数组名称.length属性,而String中使用的是length()方法
public boolean isEmpty():判断是否为空字符串--------判断标准:长度为0 不是null
String类的首字母大写需要自己实现
public static void main(String[] args) {
System.out.println(fistUpper("yuisama"));
System.out.println(fistUpper(""));
System.out.println(fistUpper("a"));
}
public static String fistUpper(String str) {
if ("".equals(str)||str==null) {
return str ;
}
if (str.length()>1) {
return str.substring(0, 1).toUpperCase()+str.substring(1) ;
}
return str.toUpperCase() ;
}
String类的总结:
1.任何字符串常量都是String对象,并且String的常量一旦声明不可改变,如果改变,只是改变对象引用的指向
2.String类用“+”拼接 不可更改
3StringBuffer类
----------目的:为了方便字符串的修改
- public synchronized StringBuffer append(各种数据类型 b)
class Test{
public static void main(String[] args) {
StringBuffer sb=new StringBuffer();
sb.append("Hello").append("world");
fun(sb);
System.out.println(sb);
}
public static void fun(StringBuffer temp){
temp.append("\n").append("www.github.com");
}
}
输出结果:
Helloworld
www.github.com
3.1String与StringBuffer的互相转换:
源码:
String类
public final class String implements
java.io.Serializable, Comparable<String>,
CharSequence
StringBuffer类
public final class StringBuffer extends
AbstractStringBuilder implements java.io.Serializable,
CharSequence
两个类都是"CharSequence"接口的子类。这个接口描述的是一系列的字符集。所以字符串是字符集的子
类,如果以后看见CharSequence,最简单的联想就是字符串。
String-->StringBuffer: 利用StringBuffer的构造方法或者append()方法
package se.SE.practice;
public class Test1{
public static void main(String[] args) {
String str="hehe";
StringBuffer stringBuffer=new StringBuffer(str);
for(int i=0;i<6;i++){
stringBuffer.append(" b");//实现字符串的拼接,stringBuffer.append("要拼接的字符")
}
str=stringBuffer.toString();
System.out.println(str);
}
}
//输出:
//hehe b b b b b b
StringByffer-->String: 调用toString方法
3.2StringBuffer的方法
线程安全,同步操作(但凡和同步相关联,这个操作就和高性能无关)也就是说,线程安全付出的代价就是性能下降
字符串反转:reverse()
public synchronized StringBuffer reverse()
class Test{
public static void main(String[] args) {
StringBuffer sb=new StringBuffer("happybirthday");
System.out.println(sb.reverse());
}
}
输出结果:
yadhtribyppah
删除指定范围的数据:delete(int start ,int end)
public synchronized StringBuffer delete(int start ,int end)
class Test{
public static void main(String[] args) {
StringBuffer sb=new StringBuffer("happybirthday");
System.out.println(sb.delete(0,3));
}
}
输出结果:
pybirthday
插入数据:insert()
public synchronized StringBuffer insert(int offset,数据类型 b)
class Test{
public static void main(String[] args) {
StringBuffer sb=new StringBuffer("hhappybirthday");
System.out.println(sb.delete(0,1).insert(0,"boom~~~ "));
}
}
输出结果:boom~~~ happybirthday
总结:
从线程安全性比较String/StringBuffer和StringBuilder:
String常量内容不可修改而StringBuffer和StringBulider内容可以修改
StringBuffer采用同步处理,属于线程安全操作。
StringBuilder采用异步处理,属于线程不安全操作。
一般情况使用StringBuilder性能较高
当String对象用“+”进行字符串的拼接时,javac编译器会将String对象变为StringBuilder然后调用append()来修改字符串的内容,减少无用空间的开辟。
String是immutable的,其内容一旦创建好之后,就不可以发生改变。
StringBuffer 是可以变长的,内容也可以发生改变
改变的原理是StringBuffer内部采用了字符数组存放数据,在需要增加长度的时候,创建新的数组,并且把原来的数据复制到新的数组这样的办法来实现。