前言

  在我们开发中经常会用到很多的常用的工具类,这里做一个总结。他们有很多的方法都是我们经常要用到的。所以我们一定要把它好好的掌握起来!

一、String简介

1.1、String(字符串常量)概述

  在API中是这样描述:

    String 类代表字符串。Java 程序中的所有字符串字面值(如 "abc" )都作为此类的实例实现。
    字符串是常量;它们的值在创建之后不能更改。字符串缓冲区支持可变的字符串。因为 String 对象是不可变的,所以可以共享。

  java.lang.String:

    

1.2、分析String源码

  1)String的成员变量

 /** String的属性值 */
private final char value[]; /** The offset is the first index of the storage that is used. */
/**数组被使用的开始位置**/
private final int offset; /** The count is the number of characters in the String. */
/**String中元素的个数**/
private final int count; /** Cache the hash code for the string */
/**String类型的hash值**/
private int hash; // Default to 0 /** use serialVersionUID from JDK 1.0.2 for interoperability */
private static final long serialVersionUID = -6849794470754667710L;
/**
* Class String is special cased within the Serialization Stream Protocol.
*
* A String instance is written into an ObjectOutputStream according to
* <a href="{@docRoot}/../platform/serialization/spec/output.html">
* Object Serialization Specification, Section 6.2, "Stream Elements"</a>
*/   private static final ObjectStreamField[] serialPersistentFields =
new ObjectStreamField[];

String的成员变量

    从源码看出String底层使用一个字符数组来维护的。

    成员变量可以知道String类的值是final类型的,不能被改变的,所以只要一个值改变就会生成一个新的String类型对象,存储String数据也不一定从数组的第0个元素开始的,而是从offset所指的元素开始。

  2)String的构造方法  

String()
初始化一个新创建的 String 对象,使其表示一个空字符序列。
String(byte[] bytes)
通过使用平台的默认字符集解码指定的 byte 数组,构造一个新的 String。
String(byte[] bytes, Charset charset)
通过使用指定的 charset 解码指定的 byte 数组,构造一个新的 String。
String(byte[] bytes, int offset, int length)
通过使用平台的默认字符集解码指定的 byte 子数组,构造一个新的 String。
String(byte[] bytes, int offset, int length, Charset charset)
通过使用指定的 charset 解码指定的 byte 子数组,构造一个新的 String。
String(byte[] bytes, int offset, int length, String charsetName)
通过使用指定的字符集解码指定的 byte 子数组,构造一个新的 String。
String(byte[] bytes, String charsetName)
通过使用指定的 charset 解码指定的 byte 数组,构造一个新的 String。
String(char[] value)
分配一个新的 String,使其表示字符数组参数中当前包含的字符序列。
String(char[] value, int offset, int count)
分配一个新的 String,它包含取自字符数组参数一个子数组的字符。
String(int[] codePoints, int offset, int count)
分配一个新的 String,它包含 Unicode 代码点数组参数一个子数组的字符。
String(String original)
初始化一个新创建的 String 对象,使其表示一个与参数相同的字符序列;换句话说,新创建的字符串是该参数字符串的副本。
String(StringBuffer buffer)
分配一个新的字符串,它包含字符串缓冲区参数中当前包含的字符序列。
String(StringBuilder builder)
分配一个新的字符串,它包含字符串生成器参数中当前包含的字符序列。

二、创建字符串对象两种方式的区别

2.1、直接赋值方式创建对象

  直接赋值方式创建对象是在方法区的常量池

String str="hello";//直接赋值的方式

2.2、通过构造方法创建字符串对象

  通过构造方法创建字符串对象是在堆内存

String str=new String("hello");//实例化的方式

2.3、两种实例化方式的比较

  1)编写代码比较

public class TestString {
public static void main(String[] args) {
String str1 = "Lance";
String str2 = new String("Lance");
String str3 = str2; //引用传递,str3直接指向st2的堆内存地址
String str4 = "Lance";
/**
* ==:
* 基本数据类型:比较的是基本数据类型的值是否相同
* 引用数据类型:比较的是引用数据类型的地址值是否相同
* 所以在这里的话:String类对象==比较,比较的是地址,而不是内容
*/
System.out.println(str1==str2);//false
System.out.println(str1==str3);//false
System.out.println(str3==str2);//true
System.out.println(str1==str4);//true
} }

  2)内存图分析

    

    可能这里还是不够明显,构造方法实例化方式的内存图:String str = new String("Hello");

    首先:

      

    当我们再一次的new一个String对象时:

      

    3)字符串常量池

      在字符串中,如果采用直接赋值的方式(String str="Lance")进行对象的实例化,则会将匿名对象“Lance”放入对象池,每当下一次对不同的对象进行直接赋值的时候会直接利用池中原有的匿名对象,

      这样,所有直接赋值的String对象,如果利用相同的“Lance”,则String对象==返回true;

      比如:对象手工入池

public class TestString {
public static void main(String args[]){
String str =new String("Lance").intern();//对匿名对象"hello"进行手工入池操作
String str1="Lance";
System.out.println(str==str1);//true
}
}

    4)总结:两种实例化方式的区别

      1)直接赋值(String str = "hello"):只开辟一块堆内存空间,并且会自动入池,不会产生垃圾。

      2)构造方法(String str=  new String("hello");):会开辟两块堆内存空间,其中一块堆内存会变成垃圾被系统回收,而且不能够自动入池,需要通过public  String intern();方法进行手工入池。

        在开发的过程中不会采用构造方法进行字符串的实例化。

    5)避免空指向

      首先了解: == 和public boolean equals()比较字符串的区别

      ==在对字符串比较的时候,对比的是内存地址,而equals比较的是字符串内容,在开发的过程中,equals()通过接受参数,可以避免空指向。

      举例:

      String str = null;
      if(str.equals("hello")){//此时会出现空指向异常
        ...
      }
      if("hello".equals(str)){//此时equals会处理null值,可以避免空指向异常
         ...
      }

    6)String类对象一旦声明则不可以改变;而改变的只是地址,原来的字符串还是存在的,并且产生垃圾

      

三、String常用的方法

  

3.1、String的判断功能

  1)常用方法

  boolean equals(Object obj):比较字符串的内容是否相同
  boolean equalsIgnoreCase(String str): 比较字符串的内容是否相同,忽略大小写
  boolean startsWith(String str): 判断字符串对象是否以指定的str开头
  boolean endsWith(String str): 判断字符串对象是否以指定的str结尾

  2)代码测试

 

public class TestString {
public static void main(String[] args) {
// 创建字符串对象
String s1 = "hello";
String s2 = "hello";
String s3 = "Hello"; // boolean equals(Object obj):比较字符串的内容是否相同
System.out.println(s1.equals(s2));
System.out.println(s1.equals(s3));
System.out.println("-----------"); // boolean equalsIgnoreCase(String str):比较字符串的内容是否相同,忽略大小写
System.out.println(s1.equalsIgnoreCase(s2));
System.out.println(s1.equalsIgnoreCase(s3));
System.out.println("-----------"); // boolean startsWith(String str):判断字符串对象是否以指定的str开头
System.out.println(s1.startsWith("he"));
System.out.println(s1.startsWith("ll"));
} }

测试

    结果:

    

3.2、String类的获取功能

  1)常用方法

  int length():获取字符串的长度,其实也就是字符个数
char charAt(int index):获取指定索引处的字符
int indexOf(String str):获取str在字符串对象中第一次出现的索引
String substring(int start):从start开始截取字符串
String substring(int start,int end):从start开始,到end结束截取字符串。包括start,不包括end

  2)代码测试

public class TestString {
public static void main(String[] args) {
String str1 = "Lance";
String str2 = new String("Lance");
String str3 = "LANCE"; // boolean equals(Object obj):比较字符串的内容是否相同
System.out.println(str1.equals(str2));
System.out.println(str1.equals(str3));
System.out.println("-----------------------"); // boolean equalsIgnoreCase(String str):比较字符串的内容是否相同,忽略大小写
System.out.println(str1.equalsIgnoreCase(str3));
System.out.println("-----------------------"); // boolean startsWith(String str):判断字符串对象是否以指定的str开头
// boolean endsWith(String str): 判断字符串对象是否以指定的str结尾
System.out.println(str1.startsWith("La"));
System.out.println(str3.endsWith("CE")); } }
  结果:      3.2、String类的获取功能   )常用方法 int length():获取字符串的长度,其实也就是字符个数
char charAt(int index):获取指定索引处的字符
int indexOf(String str):获取str在字符串对象中第一次出现的索引
String substring(int start):从start开始截取字符串
String substring(int start,int end):从start开始,到end结束截取字符串。包括start,不包括end
  )测试代码 public class TestString {
public static void main(String[] args) {
// 创建字符串对象
String s = "helloworld"; // int length():获取字符串的长度,其实也就是字符个数
System.out.println(s.length());
System.out.println("--------"); // char charAt(int index):获取指定索引处的字符
System.out.println(s.charAt());
System.out.println(s.charAt());
System.out.println("--------"); // int indexOf(String str):获取str在字符串对象中第一次出现的索引
System.out.println(s.indexOf("l"));
System.out.println(s.indexOf("owo"));
System.out.println(s.indexOf("ak"));
System.out.println("--------"); // String substring(int start):从start开始截取字符串
System.out.println(s.substring());
System.out.println(s.substring());
System.out.println("--------"); // String substring(int start,int end):从start开始,到end结束截取字符串
System.out.println(s.substring(, s.length()));
System.out.println(s.substring(, )); } }

测试

  结果:

    

3.3、String的转换功能

  1)常用方法

  char[] toCharArray():把字符串转换为字符数组
  String toLowerCase():把字符串转换为小写字符串
  String toUpperCase():把字符串转换为大写字符串

  2)核心代码

public class TestString {
public static void main(String[] args) {
// 创建字符串对象
String s = "abcde"; // char[] toCharArray():把字符串转换为字符数组
char[] chs = s.toCharArray();
for (int x = ; x < chs.length; x++) {
System.out.println(chs[x]);
} System.out.println("-----------"); // String toLowerCase():把字符串转换为小写字符串
System.out.println("HelloWorld".toLowerCase());
// String toUpperCase():把字符串转换为大写字符串
System.out.println("HelloWorld".toUpperCase()); } }

测试

  结果:

    

  注意:  

    字符串的遍历有两种方式:一是ength()加上charAt()。二是把字符串转换为字符数组,然后遍历数组。

3.4、其他常用方法

  1)常用方法

  去除字符串两端空格:String trim()
  按照指定符号分割字符串:String[] split(String str)

  2)核心代码

public class TestString {
public static void main(String[] args) {
// 创建字符串对象
String s1 = "helloworld";
String s2 = " helloworld ";
String s3 = " hello world ";
System.out.println("---" + s1 + "---");
System.out.println("---" + s1.trim() + "---");
System.out.println("---" + s2 + "---");
System.out.println("---" + s2.trim() + "---");
System.out.println("---" + s3 + "---");
System.out.println("---" + s3.trim() + "---");
System.out.println("-------------------"); // String[] split(String str)
// 创建字符串对象
String s4 = "aa,bb,cc";
String[] strArray = s4.split(",");
for (int x = ; x < strArray.length; x++) {
System.out.println(strArray[x]);
}
} }

测试

  结果:

      

四、String的不可变性

当我们去阅读源代码的时候,会发现有这样的一句话:

  

意思就是说:String是个常量,从一出生就注定不可变。

我想大家应该就知道为什么String不可变了,String类被final修饰,官方注释说明创建后不能被改变,但是为什么String要使用final修饰呢? 

4.1、前言

  了解一个经典的面试题:

public class Apple {
public static void main(String[] args) {
String a = "abc";
String b = "abc";
String c = new String("abc");
System.out.println(a==b); //true
System.out.println(a.equals(b)); //true
System.out.println(a==c); //false
System.out.println(a.equals(c)); //true
}
}

  内存图:

    

4.2、分析

  因为String太过常用,JAVA类库的设计者在实现时做了个小小的变化,即采用了享元模式,每当生成一个新内容的字符串时,他们都被添加到一个共享池中,当第二次再次生成同样内容的字符串实例时,

  就共享此对象,而不是创建一个新对象,但是这样的做法仅仅适合于通过=符号进行的初始化。  

  需要说明一点的是,在object中,equals()是用来比较内存地址的,但是String重写了equals()方法,用来比较内容的,即使是不同地址,只要内容一致,也会返回true,这也就是为什么a.equals(c)返回true的原因了。

4.3、String不可变的好处

  可以实现多个变量引用堆内存中的同一个字符串实例,避免创建的开销。

  我们的程序中大量使用了String字符串,有可能是出于安全性考虑。

  大家都知道HashMap中key为String类型,如果可变将变的多么可怕。

  当我们在传参的时候,使用不可变类不需要去考虑谁可能会修改其内部的值,如果使用可变类的话,可能需要每次记得重新拷贝出里面的值,性能会有一定的损失。

五、字符串常量池

5.1、字符串常量池概述

  1)常量池表(Constant_Pool table)

    Class文件中存储所有常量(包括字符串)的table。
    这是Class文件中的内容,还不是运行时的内容,不要理解它是个池子,其实就是Class文件中的字节码指令。

  2)运行时常量池(Runtime Constant Pool) 

    JVM内存中方法区的一部分,这是运行时的内容
    这部分内容(绝大部分)是随着JVM运行时候,从常量池转化而来,每个Class对应一个运行时常量池
    上一句中说绝大部分是因为:除了 Class中常量池内容,还可能包括动态生成并加入这里的内容

  3)字符串常量池(String Pool)

    这部分也在方法区中,但与Runtime Constant Pool不是一个概念,String Pool是JVM实例全局共享的,全局只有一个
    JVM规范要求进入这里的String实例叫“被驻留的interned string”,各个JVM可以有不同的实现,HotSpot是设置了一个哈希表StringTable来引用堆中的字符串实例,被引用就是被驻留。

5.2、亨元模式

  其实字符串常量池这个问题涉及到一个设计模式,叫“享元模式”,顾名思义 - - - > 共享元素模式
  也就是说:一个系统中如果有多处用到了相同的一个元素,那么我们应该只存储一份此元素,而让所有地方都引用这一个元素
  Java中String部分就是根据享元模式设计的,而那个存储元素的地方就叫做“字符串常量池 - String Pool”

5.3、详细分析

  举例:

int x  = ;
String y = "hello";

  1)首先,10"hello"会在经过javac(或者其他编译器)编译过后变为Class文件中constant_pool table的内容

  2)当我们的程序运行时,也就是说JVM运行时,每个Classconstant_pool table中的内容会被加载到JVM内存中的方法区中各自Class的Runtime Constant Pool。

  3)一个没有被String Pool包含的Runtime Constant Pool中的字符串(这里是"hello")会被加入到String Pool中(HosSpot使用hashtable引用方式),步骤如下:   

    一是:在Java Heap中根据"hello"字面量create一个字符串对象
    二是:将字面量"hello"与字符串对象的引用在hashtable中关联起来,键 - 值 形式是:"hello" = 对象的引用地址。

   另外来说,当一个新的字符串出现在Runtime Constant Pool中时怎么判断需不需要在Java Heap中创建新对象呢?

  策略是这样:会先去根据equals来比较Runtime Constant Pool中的这个字符串是否和String Pool中某一个是相等的(也就是找是否已经存在),如果有那么就不创建,直接使用其引用;反之,如上3

  如此,就实现了享元模式,提高的内存利用效率。

  举例:

      使用String s = new String("hello");会创建几个对象

      会创建2个对象

      首先,出现了字面量"hello",那么去String Pool中查找是否有相同字符串存在,因为程序就这一行代码所以肯定没有,那么就在Java Heap中用字面量"hello"首先创建1个String对象。

      接着,new String("hello"),关键字new又在Java Heap中创建了1个对象,然后调用接收String参数的构造器进行了初始化。最终s的引用是这个String对象.

      

喜欢就点个“推荐”!         

Java常用类(一)String类详解的更多相关文章

  1. 【转】Java魔法堂:String.format详解

    Java魔法堂:String.format详解     目录     一.前言    二.重载方法     三.占位符     四.对字符.字符串进行格式化     五.对整数进行格式化     六. ...

  2. Java魔法堂:String.format详解

      目录     一.前言    二.重载方法     三.占位符     四.对字符.字符串进行格式化     五.对整数进行格式化     六.对浮点数进行格式化     七.对日期时间进行格式化 ...

  3. Java基础-DButils工具类(QueryRunner)详解

    Java基础-DButils工具类(QueryRunner)详解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 如果只使用JDBC进行开发,我们会发现冗余代码过多,为了简化JDBC ...

  4. Java AtomicInteger类的使用方法详解_java - JAVA

    文章来源:嗨学网 敏而好学论坛www.piaodoo.com 欢迎大家相互学习 首先看两段代码,一段是Integer的,一段是AtomicInteger的,为以下: public class Samp ...

  5. Java的常用类——Object类、String类

    Java的JDK中提供给我们很多封装好的类就是Java API,可以直接调用. Java 的API(API: Application(应用) Programming(程序) Interface(接口) ...

  6. Java Object类 和 String类 常见问答 6k字+总结

    写在最前面 这个项目是从20年末就立好的 flag,经过几年的学习,回过头再去看很多知识点又有新的理解.所以趁着找实习的准备,结合以前的学习储备,创建一个主要针对应届生和初学者的 Java 开源知识项 ...

  7. Kotlin——中级篇(一):类(class)详解

    在任何一门面向对象编程的语言里,类(class)是非常基础.但也是非常重要的一项组成,通俗的说就是万般皆对象,而所说的对象就是我们生成的类.Kotlin也是如此,下面详细为大家介绍Kotlin中的类的 ...

  8. java学习笔记之String类

    String类总结 String类概述: java.lang.String 类是字符串操作类 String类的常用构造方法: //1.直接赋值 String str= "hellojava& ...

  9. Java常用的加密解密类(对称加密类)

    Java常用的加密解密类 原文转载至:http://blog.csdn.net/wyc_cs/article/details/8793198 原创 2013年04月12日 14:33:35 1704 ...

  10. Python的Django框架中forms表单类的使用方法详解

    用户表单是Web端的一项基本功能,大而全的Django框架中自然带有现成的基础form对象,本文就Python的Django框架中forms表单类的使用方法详解. Form表单的功能 自动生成HTML ...

随机推荐

  1. 201521123048 《Java程序设计》第5周学习总结

    1. 本周学习总结 1.1 尝试使用思维导图总结有关多态与接口的知识点. 2. 书面作业 1.代码阅读:Child压缩包内源代码 //child public class test extends P ...

  2. 201521123070 《JAVA程序设计》第4周学习总结

    1. 本章学习总结 1.1 尝试使用思维导图总结有关继承的知识点. http://naotu.baidu.com/file/4de6f42e4f4f6cce0531dd9997b04e60?token ...

  3. 201521123055 《Java程序设计》第2周学习总结

     1. 本章学习总结 (1)认识PATH和CLASSPATH (2)SET PATH/CLASSPATH和-cp的用法 (3)了解BigDecimal.BigInteger.ArrayList/Lis ...

  4. 《JAVA程序设计》第9周学习总结

    1. 本章学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结异常相关内容. 2. 书面作业 1.常用异常 题目5-1 1.1 截图你的提交结果(出现学号) 1.2 自己以前编写的代码中经常出现 ...

  5. lintcode 453 将二叉树拆成链表

    将二叉树拆成链表   描述 笔记 数据 评测 将一棵二叉树按照前序遍历拆解成为一个假链表.所谓的假链表是说,用二叉树的 right 指针,来表示链表中的 next 指针. 注意事项 不要忘记将左儿子标 ...

  6. WebSocket In ASP.NET Core(二)

    WebSocket In ASP.NET Core(二) Server in ASP.NET-Core DI in ASP.NET-Core Routing in ASP.NET-Core Error ...

  7. MapReduce极简教程

    一个有趣的例子 你想数出一摞牌中有多少张黑桃.直观方式是一张一张检查并且数出有多少张是黑桃?   MapReduce方法则是: 给在座的所有玩家中分配这摞牌 让每个玩家数自己手中的牌有几张是黑桃,然后 ...

  8. angular-bootstrap ui-date组件问题总结

    使用angular框架的时候,之前用的时间控件是引入My97DatePicker组件实现的,但是因为 1.My97DatePicker样式不太好看以及偶尔会出现底部被遮盖的情况.点击不可编辑input ...

  9. NavigationController的返回按钮自定义

    假设需求时这样: NavigationController下有2个视图,从A视图会Push到B视图,默认情况下,当显示视图B时,视图B的导航bar上会出现返回按钮,按钮标题文字默认为A视图的title ...

  10. Linux - 设置/取消代理

    export http_proxy=118.210.42.251:44367 或: export https_proxy=118.210.42.251:44367   要取消该设置: unset ht ...