String类源码学习

String类源码学习

一、初始String类:

  1. String类是final类型的,表示该类不能被继承。如下图所示:
    在这里插入图片描述
  2. 这是一个字符数组,且是final的,用于存储字符串的内容。
    本质:String是用char[ ]实现的。
     /** The value is used for character storage. */
        private final char value[];
    
    <blockquote><pre>
     *     String str = "abc";
     * </pre></blockquote><p>
     * is equivalent to:
     * <blockquote><pre>
     *     char data[] = {'a', 'b', 'c'};
     *     String str = new String(data);
    
  3. 因为String实现了Serializable接口,所以支持序列化和反序列化。Java的序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体(类)的serialVersionUID进行比较,如果相同就认为是一致的,可以反序列化,否则会出现序列化版本不一致的异常(InvalidCastException)。
    /** 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[0];
    

二、构造方法:
String的底层是由char[ ]实现的:通过一个char[]类型的value属性。

  1. 使用字符数组、字符串构造一个String
    /**
         * Allocates a new {@code String} so that it represents the sequence of
         * characters currently contained in the character array argument. The
         * contents of the character array are copied; subsequent modification of
         * the character array does not affect the newly created string.
         *
         * @param  value
         *         The initial value of the string
         */
        public String(char value[]) {
            this.value = Arrays.copyOf(value, value.length);
        }
    
  2. 使用字节数组构造一个String:
      public String(byte bytes[], Charset charset) {
            this(bytes, 0, bytes.length, charset);
        }
    
  3. 使用StringBuffer和StringBuilder 构造一个String
    /**
         * Allocates a new string that contains the sequence of characters
         * currently contained in the string buffer argument. The contents of the
         * string buffer are copied; subsequent modification of the string buffer
         * does not affect the newly created string.
         *
         * @param  buffer
         *         A {@code StringBuffer}
         */
        public String(StringBuffer buffer) {
            synchronized(buffer) {
                this.value = Arrays.copyOf(buffer.getValue(), buffer.length());
            }
        }
    
     /**
         * Allocates a new string that contains the sequence of characters
         * currently contained in the string builder argument. The contents of the
         * string builder are copied; subsequent modification of the string builder
         * does not affect the newly created string.
         *
         * <p> This constructor is provided to ease migration to {@code
         * StringBuilder}. Obtaining a string from a string builder via the {@code
         * toString} method is likely to run faster and is generally preferred.
         *
         * @param   builder
         *          A {@code StringBuilder}
         *
         * @since  1.5
         */
        public String(StringBuilder builder) {
            this.value = Arrays.copyOf(builder.getValue(), builder.length());
        }
    

三、String 中常用的方法:

	/*String a = "abc";//放在常量池里面
	String b = "abc";
	String c = new String("abc");//在对内存开辟空间
	System.out.println(a == b);//true
	System.out.println(a == c);//false
	  */	
	//测试String_StringBuilder_StringBuffer速度:
	long start = System.currentTimeMillis();
	//String s ="abc" + "de";// 等同于 String s ="abcde";
	String s1 = "abc";
	s1 += "de";
	//StringBuffer sb = new StringBuffer().append("abc").append("de");
	//StringBuilder sdr = new StringBuilder().append("abc").append("de");
	long end = System.currentTimeMillis();
	System.out.println(end - start) ;
	
	//String类中常用的方法:
	String str = new String(" wuxia is Perfect....");
	System.out.println(str.length());//1.返回字符串的长度
	System.out.println(str.isEmpty());//2.判断是否为空
	System.out.println(str.substring(1, 4));//3.左闭右开,返回子串
	System.out.println(str.charAt(2));//4.返回下标为2的字符
	System.out.println(str.toUpperCase());//5.转大写
	System.out.println(str.toLowerCase());//6.转小写
	System.out.println(str.trim());//7.去掉两端空格,注意中间的没有去掉
	char[] cha = str.toCharArray();//8.转为 字符数组 char[]
	System.out.print("转为数组:[");
	for(int i = 0;i < cha.length ; i ++) {
		if(i == cha.length - 1) {
			System.out.print(cha[i]+"]");
		}else {
			System.out.print(cha[i] +",");
		}
	}
	System.out.println();
	System.out.println(str.concat("专业有可能骗人,但是认知和格局是不会骗人的!"));//9.拼接字符串
	System.out.println(str.replace("...", "!!!"));//10.替换字符串
	System.out.println(str.contains("w"));//11.判断是否包含某个字符串
	System.out.println(str.matches("."));//12.是否匹配给定的正则表达式
	String[] strs = str.split(" ");//13.按照字符分成分割,返回String数组
	System.out.print("转为数组:[");
	for(int i = 0;i < strs.length ; i ++) {
		if(i == strs.length - 1) {
			System.out.print(strs[i]+"]");
		}else {
			System.out.print(strs[i] +"|");
		}
	}
	System.out.println();
	
	String str1 = "ABC";
	byte[] bytes = str1.getBytes();//14.将字符串转成字节数组
	for(int i = 0;i < bytes.length ; i ++) {
		if(i == bytes.length - 1) {
			System.out.print(bytes[i]+"]");
		}else {
			System.out.print(bytes[i] +"|");
		}
	}
	System.out.println();
	System.out.println(str1.equals("你好,吴霞"));//15.
	System.out.println(str1.contentEquals(new StringBuffer("你好,吴霞")));
	System.out.println(str1.equalsIgnoreCase("abc"));
	System.out.println(str1.compareTo("abc"));
	System.out.println(str1.compareToIgnoreCase("abc"));
	System.out.println(str1.regionMatches(1, "c", 1, 1));
	System.out.println(str1.substring(1));//重新new 了 一个String 避免了内存泄露,但牺牲了性能
	System.out.println(str1.replaceFirst("A", "吴霞"));
	System.out.println(str1.replaceAll("AB", "你好"));
	
	char[] c = {'w','c'};
	//valueOf
	System.out.println(str.valueOf("qqq"));
	System.out.println(str.copyValueOf(c));//new String(data);
	
	//intern()方法
	/**
	When the intern method is invoked, if the pool already contains a
	    * string equal to this {@code String} object as determined by
	    * the {@link #equals(Object)} method, then the string from the pool is
	    * returned. Otherwise, this {@code String} object is added to the
	    * pool and a reference to this {@code String} object is returned.*/
	String s = new String("intern");
	System.out.println(s.intern());//重要,当该方法被调用时,如果对象池包含这个字符串,则返回
									//对象池的实例,否则添加字符串到对象池并返回该字符串的引用
	String s2 = new String("java1");
	String s3 = new String("吴霞");
	String s4 = "吴霞";
	System.out.println("java1" == s2);//false  
	System.out.println("java1" == s2.intern());//true  思考:为什么?
	System.out.println(s3 == s3.intern());//false  思考:为什么? s3是堆上的引用,s3.intern()是从常量池返回的
	System.out.println(s3.intern() == s3);//false  思考:为什么?
	System.out.println(s4 == s3.intern());//true  思考:为什么?

2.重载运算符:

	//String 对 "+"的重载 ,"+"是java 唯一一个重载运算符
	//反编译后: 
	String a = "abc";
	String a1 =a + "def";
	System.out.println(a1);
	
	int i = 5;
	String i1 = "" + i;//相当于  String i1 =(new StringBuilder()).append(i).toString();
	String i2 = String.valueOf(i);//调的是 Integer.toString(i); toString(i) 是new 的一个新对象
	String i3 = Integer.toString(i);
	System.out.println( i2 == i1);//false
	System.out.println( i2 == i3);//false

在这里插入图片描述
在这里插入图片描述
字符串常量池:
思考:
①字符串常量池的设计意图是什么?
②字符串常量池在哪里?
③如何操作字符串常量池?

字符串常量池的设计思想:
a.字符串的分配,和其他对象分配一样,耗费高昂的时间和空间代价,作为最基础的数据类型,大量频繁的创建字符串,极大程度影响程序的性能
b.JVM为了提高性能和减少内存开销,在实例化字符串常量的时候进行了一些优化:
- 为字符串开辟了一个字符串常量池,类似缓冲区;
- 创建字符串常量时,先检查字符串常量池是否存在该字符串;
- 存在该字符串,返回引用实例,不存在,实例化该字符串并放入池中
c.实现的基础:

  • 实现该优化的基础是因为字符串是不可变的,不用担心数据冲突进行共享
  • 运行时实例创建的全局字符串常量池中有一个表,总是为池中每个唯一的字符串对象维护一个引用,即它们一直引用着字符串常量池中的对象,故,在常量池中的这些字符串不会被垃圾收集器回收

字符串常量池在哪里:
堆:

  • 存储的是对象,每个对象都包含一个与之对应的class
  • JVM只有一个人堆区,被所有线程共享;堆中只存放对象本身,不存放基本类型和对象引用
  • 对象由垃圾回收器负责回收

栈:

  • 每个线程包含一个栈区,栈中只保存基础数据类型的对象和自定义对象的引用
  • 栈中数据(原始类型和对象引用)都是私有的

元空间:

字符串常量池存在于元空间

操作字符串常量池的方式:
String.intern()

字面量和常量池初探:
字符串对象内部是用字符数组存储的
思考:
String m = “Hello,world”;
String n = “Hello,world”;
String u = new String(m);
String v = new String(“Hello,world”);
①会分配一个11长度的char数据,并在常量池分配一个由这个char数组组成的字符串,然后m去引用这个字符串
②用n去引用常量池里边的字符串,即n和m是同一个对象
③生成一个新的字符串,但内部的字符数组引用着m内部的字符数组
④生成一个新的字符串,但内部的字符数组引用常量池中的字符串内部的字符数组,和u是同样的字符数组
如下图:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值