java string 1_java 常用类-String-1

一、字符串相关的类

1.1 String 的特性

String类:代表字符串。Java 程序中的所有字符串字面值(如 "abc" )都作为此类的实例实现。

String是一个final类,代表不可变的字符序列。

final修饰的类不能被继承

字符串是常量,用双引号引起来表示。它们的值在创建之后不能更改。

String对象的字符内容是存储在一个字符数组value[]中的。

数据存储结构中,有链式存储结构和顺序存储结构两种。显然String底层选择了char型数组类型的顺序存储结构。

1.2 String部分源码

Windows电脑,idea中。双击shift,输入String,即可出来。

public final class String

implements java.io.Serializable, Comparable, CharSequence {

/** The value is used for character storage. */

private final char value[];

/** Cache the hash code for the string */

private int hash; // Default to 0

/** use serialVersionUID from JDK 1.0.2 for interoperability */

private static final long serialVersionUID = -6849794470754667710L;

....

}

从这部分源码中,我们就可以很好的理解“1.1 String 的特性”中的内容了!!!字符串,字符串,就是字符构建起来的串。

1.3 String再次理解

1.3.1 上代码

public class StringTest {

@Test

public void test1() {

// String name = "abc"; 字面量的定义方式

String name = "abc";

String des = "abc";

name = "hell world";

System.out.println("name:"+name);// hello world

System.out.println("des:"+des);// abc

}

@Test

public void test2() {

final int a = 2;

a = 3; // 编译不通过

}

}

第一个问题:为什么name = "hell world",编译成功,并能运行,不是说“它们的值在创建之后不能更改”吗?;而test2()中却编译失败?

首先,String属性引用类型,而int是基本类型。

引用类型变量存储地址值,而a是一个指向int类型的引用,指向2这个字面值。因此final int 修饰的变量就变为常量了,常量是不能修改其值的,所以test2()编译失败。

那么怎么理解String 底层存储使用final修饰的char型数组,更改其值为什么编译成功,并能运行?简单的JVM走一波。图一:

ffe00ee1b5019336e06a9e12b1de11bd.png

从图中我们可以看出:

常量池当中,是不会存储两个相同的字符串的。

name、des存储的是地址值。name和des都指向0x8888这个地址

图二:

17e77671c24f61d4da7693e5be879c74.png

从图中我们可以看出:

这就很好解释了为什么输出结果是name为hello world 而des依然为abc。

同时也解释了“它们的值在创建之后不能更改”,原始的值“abc”的确没有被修改,而是在字符串常量池中重新创建了一份。

这也可以看出频繁的对字符串进行增删改操作的话,很耗费内存资源。

1.3.2 初步总结

String:字符串,使用一对""引起来表示。

String声明为final的,不可被继承。

String实现了Serializable接口:表示字符串是支持序列化的。

实现了Comparable接口:表示String可以比较大小

String内部定义了final char[] value用于存储字符串数据

String:代表不可变的字符序列。简称:不可变性。

当对字符串重新赋值时,需要重写指定内存区域赋值,不能使用原有的value进行赋值。

当对现有的字符串进行连接操作时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值。

当调用String的replace()方法修改指定字符或字符串时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值。

通过字面量的方式(区别于new)给一个字符串赋值,此时的字符串值声明在字符串常量池中。

字符串常量池中是不会存储相同内容的字符串的。

1.4 判断对错

@Test

public void test3() {

String s1="javaWeb";

String s2="javaWeb";

String s3 = new String("javaWeb");

String s4 = new String("javaWeb");

System.out.println(s1==s2);

System.out.println(s1==s3);

System.out.println(s1==s4);

System.out.println(s3==s4);

}

直观判断:

s1==s2。在字符串常量池中相同的字符串只会存储一份,s1和s2存储相同的地址。所以为true

s1==s3。他们分别指向不同地方,所以为false

s1==s4。他们分别指向不同地方,所以为false

s3==s4。只要是new便会在堆空间中开辟一份空间,JVM才不会管你new的内容是否和前面的相同。所以为false

集合JVM判断:

1f6860efb07b6914c359c4b94697792b.png

结合JVM图形就可以很清楚的明白到底为什么错,为什么对了。

1.5 判断对象的属性

public class Person {

String name;

int age;

public Person(String name, int age) {

this.name = name;

this.age = age;

}

}

@Test

public void test4() {

Person p1 = new Person("Tom", 12);

Person p2 = new Person("Tom", 12);

System.out.println(p1.name==p2.name);// true?false?

}

显然为true,Tom存储在字符串常量池中,有且仅有一份。故p1.name==p2.name必然为true

6452c9360e1b61562efb97e476b4a56b.png

再来:

@Test

public void test4() {

Person p1 = new Person("Tom", 12);

Person p2 = new Person("Tom", 12);

System.out.println(p1.name==p2.name);

p2.name="cxk";

System.out.println(p1.name==p2.name);// true?false

}

肯定是false, p2.name="cxk";会先在字符串常量池中查找,没有就在常量池中建一个, p2.name地址指向它即可。【字符串的不可变性】

1.6 拓展:

String s = new String("abc");方式创建对象,在内存中创建了几个对象?

答:2个。一个是堆空间中new结构,另一个是char[ ]对应的常量池中的数据:”abc“;

来,搞一下这个。

public void test3(){

String s1 = "javaEE";

String s2 = "hadoop";

String s3 = "javaEEhadoop";

String s4 = "javaEE" + "hadoop";

String s5 = s1 + "hadoop";

String s6 = "javaEE" + s2;

String s7 = s1 + s2;

System.out.println(s3 == s4);

System.out.println(s3 == s5);

System.out.println(s3 == s6);

System.out.println(s3 == s7);

System.out.println(s5 == s6);

System.out.println(s5 == s7);

System.out.println(s6 == s7);

String s8 = s6.intern();

System.out.println(s3 == s8);

}

s3 == s4为true。字面量或者字面量之间的连接,指向同一个对象,在常量池中声明。

s5、s6、s7都是字面量拼接变量或者变量拼接变量,他们不是在常量池中声明而是在堆中声明。可以把它想象为new,从而尽管内容相同,但是地址值却是是不相同的。

结论:

常量与常量的拼接结果在常量池。且常量池中不会存在相同内容的常量。  只要其中有一个是变量,结果就在堆中

如果拼接的结果调用intern()方法,返回值就在常量池中

1.7 面试题

代码一:

public class StringTest2 {

String str = new String("good");

char[] ch = {'t', 'e', 's', 't'};

public void change(String str, char ch[]) {

str = "test ok";

System.out.println("======"+str);

ch[0] = 'b';

}

public static void main(String[] args) {

StringTest2 ex = new StringTest2();

ex.change(ex.str, ex.ch);

System.out.print(ex.str + " and ");

System.out.println(ex.ch);

}

}

输出结果是多少?

解析:做这道题必须明白几个知识点

java中方法参数的传递机制——值传递。

值传递。即将实际参数的副本(复制品)传入到方法内,而参数本身不受影响。

形参是基本数据类型:将实参基本数据类型变量的“数据值”传递给形参【原始值不受影响】

形参是引用数据类型:将实参引用数据类型变量的“地址值”传递给形参【因为传递副本为地址值,所以某些原始值会受影响,某些不会,如String类型具有不可变性】

String类型具有不可变性。

分析:

代码二:

public class StringTest2 {

String str = new String("good");

char[] ch = {'t', 'e', 's', 't'};

public void change(String str, char ch[]) {

// 第二步:

//对于方法中的str,由于第一步的赋值操作,它的地址值和“String str = new String("good");”中的str一样,内容都为good。

//接下来方法中对它进行赋值操作“test ok”。

//由于String类型的不可变性(看源码类为final修饰,数据存储结构为也为final修饰的char型数组),不可修改。

//因此JVM执行'str ="test ok";'时会在字符串常量池中新建一个“test ok”并且更新方法中str的地址值。

//至此两个str的地址值不同了,指向的内容也不同了。 可以看“代码三”中的对比结果。

str = "test ok";

// 第二步;

//对于方法中的ch,由于第一步的赋值操作,它的地址值和“char[] ch = {'t', 'e', 's', 't'};”中的ch一样,内容都为test。

//接下来,在方法中执行"ch[0] = 'b';" 数组中的第一个元素被赋值为b。由于是引用类型,此时类中成员变量char[] ch = {'t', 'e', 's', 't'};值也变为best。

ch[0] = 'b';

}

public static void main(String[] args) {

StringTest2 ex = new StringTest2();

// 第一步

// 传递的两个实参,参数一,实际上是把StringTest2类中成员变量str的地址值复制一份给StringTest2类中change方法中的形参变量str

// 同理,StringTest2类中成员变量ch(数组:也是引用类型)的地址值复制一份给StringTest2类中change方法中的形参变量ch[]

ex.change(ex.str, ex.ch);

System.out.print(ex.str + " and ");

System.out.println(ex.ch);

}

}

代码三:

public class StringTest2 {

String str = new String("good");

char[] ch = {'t', 'e', 's', 't'};

public void change(String str, char ch[]) {

System.out.print("赋值之前两个str的地址值比较为:");

System.out.println(this.str==str);

str = "test ok";

System.out.print("赋值之后两个str的地址值比较为:");

System.out.println(this.str==str);

System.out.println("====================分割线========================");

ch[0] = 'b';

}

public static void main(String[] args) {

StringTest2 ex = new StringTest2();

ex.change(ex.str, ex.ch);

System.out.print(ex.str + " and ");

System.out.println(ex.ch);

}

}

结果:

07c31520fabb1db4b4f4fde2a0859dd9.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值