一、字符串相关的类

1.1 String 的特性

  1. String类:代表字符串。Java 程序中的所有字符串字面值(如 "abc" )都作为此类的实例实现。
  2. String是一个final类,代表不可变的字符序列。
    • final修饰的类不能被继承
  3. 字符串是常量,用双引号引起来表示。它们的值在创建之后不能更改。
  4. String对象的字符内容是存储在一个字符数组value[]中的。
    • 数据存储结构中,有链式存储结构和顺序存储结构两种。显然String底层选择了char型数组类型的顺序存储结构。

1.2 String部分源码

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

public final class String
implements java.io.Serializable, Comparable<String>, 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; // 编译不通过
}
}
  1. 第一个问题:为什么name = "hell world",编译成功,并能运行,不是说“它们的值在创建之后不能更改”吗?;而test2()中却编译失败?

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

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

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

      从图中我们可以看出:

      • 常量池当中,是不会存储两个相同的字符串的。
      • name、des存储的是地址值。name和des都指向0x8888这个地址

      图二:

      从图中我们可以看出:

      • 这就很好解释了为什么输出结果是name为hello world 而des依然为abc。
      • 同时也解释了“它们的值在创建之后不能更改”,原始的值“abc”的确没有被修改,而是在字符串常量池中重新创建了一份。
      • 这也可以看出频繁的对数组进行增删改操作的话,很耗费内存资源。
1.3.2 初步总结
  1. String:字符串,使用一对""引起来表示。
  2. String声明为final的,不可被继承。
  3. String实现了Serializable接口:表示字符串是支持序列化的。
    • 实现了Comparable接口:表示String可以比较大小
  4. String内部定义了final char[] value用于存储字符串数据
  5. String:代表不可变的字符序列。简称:不可变性。
    • 当对字符串重新赋值时,需要重写指定内存区域赋值,不能使用原有的value进行赋值。
    • 当对现有的字符串进行连接操作时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值。
    • 当调用String的replace()方法修改指定字符或字符串时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值。
  6. 通过字面量的方式(区别于new)给一个字符串赋值,此时的字符串值声明在字符串常量池中。
  7. 字符串常量池中是不会存储相同内容的字符串的。

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);
}

直观判断:

  1. s1==s2。在字符串常量池中相同的字符串只会存储一份,s1和s2存储相同的地址。所以为true
  2. s1==s3。他们分别指向不同地方,所以为false
  3. s1==s4。他们分别指向不同地方,所以为false
  4. s3==s4。只要是new便会在堆空间中开辟一份空间,JVM才不会管你new的内容是否和前面的相同。所以为false

集合JVM判断:

结合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

再来:

 @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 拓展:

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

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

  2. 来,搞一下这个。

    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);
}
}

输出结果是多少?

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

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

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

      • 形参是基本数据类型:将实参基本数据类型变量的“数据值”传递给形参【原始值不受影响】
      • 形参是引用数据类型:将实参引用数据类型变量的“地址值”传递给形参【因为传递副本为地址值,所以某些原始值会受影响,某些不会,如String类型具有不可变性】
  2. 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);
}
}

结果:

java 常用类-String-1的更多相关文章

  1. Java 常用类String类、StringBuffer类

    常用类 String类.StringBuffer类 String代表不可变的字符序列 "xxxxxxx"为该类的对象 举例(1) public class Test { publi ...

  2. 深入理解Java常用类----String

         Java中字符串的操作可谓是最常见的操作了,String这个类它封装了有关字符串操作的大部分方法,从构建一个字符串对象到对字符串的各种操作都封装在该类中,本篇我们通过阅读String类的源码 ...

  3. 深入理解Java常用类----String(二)

    上篇介绍了String类的构造器,获取内部属性等方法,最后留下了最常用的局部操作函数没有介绍,本篇将接着上篇内容,从这些最常见的函数的操作说起,看看我们日常经常使用的这些方法的内部是怎么实现的.第一个 ...

  4. java常用类-String类

    * 字符串:就是由多个字符组成的一串数据.也可以看成是一个字符数组. * 通过查看API,我们可以知道 * A:字符串字面值"abc"也可以看成是一个字符串对象. * B:字符串是 ...

  5. java常用类String

    String: String类: 代表字符串 是一个final类,代表不可变的字符序列 字符串是常量,用双引号引起来表示.值在创建后不可更改 String对象的字符内容是存储在一个字符数组Value[ ...

  6. Java常用类String的面试题汇总

    比较两个字符串时使用"=="还是equals()方法? 当然是equals方法."=="测试的是两个对象的引用是否相同,而equals()比较的是两个字符串的值 ...

  7. Java常用API(String类)

    Java常用API(String类) 概述: java.lang.String 类代表字符串.Java程序中所有的字符串文字(例如 "abc" )都可以被看作是实现此类的实例 1. ...

  8. Java常用类:包装类,String,日期类,Math,File,枚举类

    Java常用类:包装类,String,日期类,Math,File,枚举类

  9. Java基础 —— Java常用类

    Java常用类: java.lang包: java.lang.Object类: hashcode()方法:返回一段整型的哈希码,代表地址. toString()方法:返回父类名+"@&quo ...

  10. Java常用类学习笔记总结

    Java常用类 java.lang.String类的使用 1.概述 String:字符串,使用一对""引起来表示. 1.String声明为final的,不可被继承 2.String ...

随机推荐

  1. Macbook pro 下修改MySQL数据库密码

    步骤: 1. 打开终端 Terminal, 找到Mysql 的安装路径,一般默认安装路径为:/usr/local/mysql-5.7.12-osx10.11-x86_64/bin [注:我安装的Mys ...

  2. 启动tomcat出现闪退的原因

    出现闪退的可能有几点: 1.没有安装jdk或者配置jdk是否配置成功 2.找不到jdk安装的路径 3.tomcat环境配置失败 如果是第二点原因(确保第一第三点配置都正确无误)找不到jdk路径的话,可 ...

  3. WordCount (Python)

    Github项目地址:https://github.com/w1036933220/WordCount 一.解题思路 把项目需求理清楚,画一个思维导图 考虑各部分功能所需要的大概实现思路 然后完成了计 ...

  4. Python中pytesseract库的使用以及注意事项

    当我们在使用pytesseract库的时候,使用 pip install pytesseract安装完成后,发现它并不能识别出图片内容,并且会抛出异常pytesseract.pytesseract.T ...

  5. Spring Boot入门系列(十九)整合mybatis,使用注解实现动态Sql、参数传递等常用操作!

    前面介绍了Spring Boot 整合mybatis 使用注解的方式实现数据库操作,介绍了如何自动生成注解版的mapper 和pojo类. 接下来介绍使用mybatis 常用注解以及如何传参数等数据库 ...

  6. JS事件——添加、移除事件

    element.addEventListener(event, function, useCapture) 方法用于向指定元素添加事件句柄.   event: 必须.字符串,指定事件名.注意: 例 使 ...

  7. 【转】Echarts 数据绑定

    Echarts 数据绑定 简单的统计表已经可以生成,不过之前图标数据都是直接写在参数里面的,而实际使用中,我们的数据一般都是异步读取的.EChart.js对于数据异步读取这块提供了异步加载的方法. 绑 ...

  8. 阿里面试竟如此轻松,2招带你过关斩将拿下offer

    在找工作之前首先是要认清一个问题,虽然这个问题比较俗,但是很现实,就是为什么追求高工资? 这个问题我想不用说大家心里也清楚.大部分人都不是当前城市的本地人,说好听了叫来上班,说的不好听其实叫“外来务工 ...

  9. solrcloud集群版的搭建

    说在前面的话 之前我们了解到了solr的搭建,我们的solr是搭建在tomcat上面的,由于tomcat并不能过多的承受访问的压力,因此就带来了solrcloud的时代.也就是solr集群. 本次配置 ...

  10. 2020重新出发,MySql基础,性能优化

    @ 目录 MySQL性能优化 MySQL性能优化简述 使用 SHOW STATUS 命令 使用慢查询日志 MySQL 查询分析器 EXPLAIN DESCRIBE 索引对查询速度的影响 MySQL优化 ...