Object String StringBuffer/StringBuilder

API简介

  • API(Application Programming Interface,应用程序接口)是一些预先定义的接口(如函数、HTTP接口),或指软件系统不同组成部分衔接的约定。 用来提供应用程序与开发人员基于某软件或硬件得以访问的一组例程,而又无需访问源码,或理解内部工作机制的细节。

Object

1. 概述

  • Object是顶级父类,是所有Java类的父类。每个类都使用Object作为超类,所有对象(包括数组)都实现这个类的方法,即默认继承Object
  • 存在于java.lang.Object包下,不需要导包

2. 测试

  • 测试Object类
    package cn.tedu.api;
    
    import java.util.Objects;
    
    //本类用于测试顶级父类Object
    public class TestObject {
        public static void main(String[] args) {
            //4.创建对象进行测试
            Student s = new Student("张三",18);
            Student s2 = new Student();
            Student s3 = new Student("张三", 18);
            /* 测试toString() */
            //5.打印对象的属性值
            /* 重写toString()方法前,打印的是地址值,重写后,打印对象的类型和属性值 */
            System.out.println(s);
            System.out.println(s2);
    
            //6.测试hashCode()
            /**
             * 返回该对象的哈希码值,用来区分不同的对象,不同的对象生成的哈希码值不同
             */
            System.out.println(s.hashCode());
            System.out.println(s2.hashCode());
            System.out.println(s3.hashCode());
            System.out.println(s == s3);
            /* 测试equals() */
            /**
             * Object底层的equals也是==比较,比较的是地址值
             * 所以一开始打印false,要比较s和s3两个引用类型的对象
             * 要重写equals和hashCode方法
             */
            System.out.println(s.equals(s3));
    
        }
    }
    //1.创建学生类用于测试
    class Student{
        //2.定义属性
        String name;
        int age;
    
    
        //3.1生成无参及全参构造
        public Student() {
        }
    
        public Student(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
        //5.1重写toString()
        @Override
        public String toString() {
            return "Student{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    '}';
        }
    
        //7.2添加重写的equals()
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (!(o instanceof Student)) return false;
            Student student = (Student) o;
            return age == student.age &&
                    Objects.equals(name, student.name);
        }
    
        @Override
        public int hashCode() {
            return Objects.hash(name, age);
        }
    }
    
    

3. 方法摘抄

  • boolean | equals(Object obj):Object提供的equals方法,默认使用的是==比较,比较的是地址值。如果要比较具体内容,要重写equals方法(通常也要重写hashCode)方法
    • 源码摘抄
      public boolean equals(Object obj) {
              return (this == obj);
          }
      
  • int | hashCode():返回对象的哈希码值
  • String | toString():返回对象的地址值(重写前,打印的默认是地址值,重写后,打印的是对象的类型和属性值)

String

4. 概述

  • String底层封装了一个char[]数组
  • String 是不可变的字符串,是一个常量
    • 源码摘抄
      public final class String
      private final char value[];
      

5. 创建对象的方式

package cn.tedu.api;

//本类用于练习String的创建和常用方法
public class TestString {
    public static void main(String[] args) {
        //1.练习创建String的方式一
        /**
         * 字符串底层维护的是char[],存放在堆内存中
         */
        char[] value = {'a','b','c'};
        //触发含参构造来创建String类对象
        String s1 = new String(value);
        String s11 = new String(value);

        //2.练习创建String的方式二
        /**
         * 此种方式创建,底层也会new String(),存放在堆内存中的 常量池中,效率高
         */
        String s2 = "abc";
        String s22 = "abc";

        //3.测试
        /**
         * 使用方式二,直接定义String对象,会在常量池中存放对象,如果值相同,就不再创建
         * 而是把该值重新指给另一个对象,因此如果两个String值相同,比较时为true
         * == 比较的是两个对象的地址值
         * Object类中equals()默认实现也是 == 比较,比较的是地址值
         * 而String类中,重写了equals(),不再比较地址值,而是比较两个串的具体内容
         * 也就是说,只要两个值相同,就返回true
         */
        System.out.println(s1 == s2); //false
        System.out.println(s1 == s11); //false
        System.out.println(s2 == s22); //true
        System.out.println(s1.equals(s11)); //true
        System.out.println(s1.equals(s2)); //true
    }
}

  • 小结:
    • 第一种创建对象的方式
      • 存放在堆内存中,每次都会分配一个新的String对象
      • 每次的地址值都不一样
    • 第二种创建对象的方式
      • 简单高效,存放在堆内存的常量池中
      • 如果第一次使用字符串,在堆中常量池创建一个对象
      • 如果两次赋值相同,就不再创建新的对象,而是把值 重新赋给另一个对象

6. 常用方法测试

package cn.tedu.api;

import java.util.Arrays;

//本类用于练习String类的常用方法
public class TestString2 {
    public static void main(String[] args) {
        //1.创建字符串
        String s = "abc";

        char [] value = {'a','b','c'};
        String ss = new String(value);

        //2.测试常用方法
        /* charAt  获取指定下标处的字符*/
        System.out.println(s.charAt(1));

        /* concat  用于拼接指定的字符串,但注意,不会改变原串,而是重新建一个新串存放*/
        String s2 = s.concat("cxy");
        System.out.println(s); //abc
        System.out.println(s2); //abccxy

        /*  */
        System.out.println(s.concat("cxy")); //abccxy
        System.out.println(s.endsWith("y")); //false
        System.out.println(s.startsWith("a")); //true

        System.out.println(s == ss); //false
        System.out.println(s.equals(ss)); //true

        //获取指定元素首次出现的下标值
        System.out.println(ss.indexOf("b")); //1
        ss = "abcbb";
        //获取指定元素最后一次出现的下标值
        System.out.println(ss.lastIndexOf("b")); //4

        //获取指定字符串的长度(和数组的不同,数组没有(),数组是属性,这个是方法)
        System.out.println(ss.length()); //5

        String s3 = "a b c d e";
        // split  根据指定规则分割字符串,主要返回值类型为String[]
        // 需要使用Arrays.toString数组工具类方法来打印
        System.out.println(s3.split(" "));
        System.out.println(Arrays.toString(s3.split(" ")));

        // substring  从指定位置截取子串,如果只有一个下标,那就从指定位置开始截取,包含指定位置
        // 如果有两个下标,截取两个下标之间的部分,左闭右开
        System.out.println(s3.substring(3)); // c d e------[3,结束]
        System.out.println(s3.substring(1, 5)); // b c-----[1,5)

        //转为全大写和全小写
        System.out.println(s3.toUpperCase()); //A B C D E
        System.out.println(s3.toLowerCase()); //a b c d e

        s3 = "    abcde    ";
        //返回一个副本,去除首尾空格
        System.out.println(s3.trim()); //abcde

        // 把其他类型的值 转为 String
        System.out.println("20"+10); //2010
        System.out.println(String.valueOf(10)); //10
        System.out.println(String.valueOf(80)+10); //8010
        System.out.println(10+10); //20


    }
}

7. 常用方法摘抄

  • char | charAt(int index):获取指定下标处的字符
  • int | compareTo(String anotherString):按字典序比较两个字符串(比较是基于字符串中每个字符的Unicode值。)
  • String | concat(String str):用于拼接字符串,在末尾拼接(但要注意,不会改变原串,而是返回一个新串)
  • boolean | contains(CharSequence s):当且仅当此字符串包含指定字符时返回true
  • boolean | endsWith(String suffix):查看字符串是否以指定字符串结尾
  • boolean | equals(Object anObject):查看当前字符串与指定字符串是否相等(string默认重写了equals(),比较的是两个字符串的内容,两者相等则返回true)
  • byte[] | getBytes(Charset charset):使用指定字符编码将当前字符串转换,并存入一个新的字节数组中
  • int | hashCode():返回一个哈希码值
  • int | indexOf(String str):返回指定元素首次出现的下标值
  • boolean | isEmpty():查看当前字符串是否为空
  • int | lastIndexOf(String str):获取指定元素最后一次出现的下标值
  • int | length():获取指定字符串的长度(注意这个是length()方法,而数组是length属性)
  • String | replace(char oldChar, char newChar):替换指定字符
  • String[] | split(String regex):根据指定规则分割字符串,返回值类型为String[ ](该方法需要Arrays.toString()数组工具类来打印)
  • boolean | startsWith(String prefix):查看字符串是否以指定字符串开头
  • String | substring(int beginIndex):从指定位置处截取子串,如果只有一个下标,从下标位置截取到最后([下标,结束]);如果有两个下标,截取两个下标直接的部分,左闭右开([下标,另一个下标-1])
  • char[] | toCharArray():将当前字符串转为一个字符数组
  • String | toLowerCase():全部转为小写
  • String | toString():返回一个字符串
  • String | toUpperCase():全部转为大写
  • String | trim():返回一个副本,去除首位空格
  • static | String valueOf(Object obj):将其他类型的数据转为String类型,或者可以直接拼接一个" "

8. == 和 equals() 的比较

  • ==:当两者都是数值类型,比较的是两者的具体数值;当两者都是引用类型,比较的是地址值(物理地址/内存地址)
  • equals():object中还是比较的地址值,重写后比较的是具体的属性值
  • String源码中的equals(),String中默认重写了equals()
	/**
     * Compares this string to the specified object.  The result is {@code
     * true} if and only if the argument is not {@code null} and is a {@code
     * String} object that represents the same sequence of characters as this
     * object.
     *
     * @param  anObject
     *         The object to compare this {@code String} against
     *
     * @return  {@code true} if the given object represents a {@code String}
     *          equivalent to this string, {@code false} otherwise
     *
     * @see  #compareTo(String)
     * @see  #equalsIgnoreCase(String)
     */
    public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        if (anObject instanceof String) {
            String anotherString = (String)anObject;
            int n = value.length;
            if (n == anotherString.value.length) {
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                while (n-- != 0) {
                    if (v1[i] != v2[i])
                        return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }

StringBuffer/StringBuilder

9. 概述

  • 两者都是可变长的字符串
  • 底层封装了一个char[],初始容量为16,如果传入的是一个int类型,则以指定的值作为初始容量,如果传入的是String类型,则会获取字符串的长度后面再加16作为初始容量
    	AbstractStringBuilder(int capacity) {
            value = new char[capacity];
        }
    
  • 可使用append方法拼接字符串
  • StringBuffer线程安全,StringBuilder更高效

10. 测试

package cn.tedu.api;

//本类用于练习字符串的拼接
public class TestString3 {
    public static void main(String[] args) {
        //method(); //普通字符串拼接
        method2(); //高效字符串拼接
    }
    //普通字符串拼接
    private static void method() {
        //需求:拼接打印26个小写字母10000次
        //1.定义字符串保存26个字母
        String str = "abcdefghijklmnopqrstuvwxyz";
        //2.将指定的字符串拼接10000次
        //2.1先定义一个变量保存结果
        String result = "";
        //4我们可以给程序添加一个计时的功能
        //4.1在循环之前获取系统当前时间作为开始时间
        long t1 = System.currentTimeMillis();
        //2.2循环10000次进行拼接
        for (int i=1;i<=10000;i++){
            result += str;
        }
        //4.2在循环结束后获取系统当前时间作为结束时间
        long t2 = System.currentTimeMillis();
        //3.打印拼接的结果
        System.out.println("拼接的结果为"+result);
        //4.3打印消耗时间: 结束时间 - 开始时间 = 消耗时间
        System.out.println("花费的时间为"+(t2 - t1));


    }
    //高效字符串拼接
    private static void method2() {
        //1.定义字符串保存26个字母
        String str = "abcdefghijklmnopqrstuvwxyz";

        //2.优化:String -> StringBuilder / StringBuffer
        //StringBuilder 速度更快,线程不安全,单线程操作字符串
        //StringBuffer 线程安全,多线程操作字符串
        StringBuilder sb = new StringBuilder();
        StringBuffer sb2 = new StringBuffer();
        //2.将指定的字符串拼接10000次
        //2.1先定义一个变量保存结果
//        String result = "";
        //4我们可以给程序添加一个计时的功能
        //4.1在循环之前获取系统当前时间作为开始时间
        long t1 = System.currentTimeMillis();
        System.out.println(t1);
        //2.2循环10000次进行拼接
        for (int i=1;i<=10000;i++){
            //优化成使用 append 进行拼接
            //sb.append(str);
            sb2.append(str);
        }
        //4.2在循环结束后获取系统当前时间作为结束时间
        long t2 = System.currentTimeMillis();
        System.out.println(t2);
        //3.打印拼接的结果
        System.out.println("拼接的结果为"+sb2);
        //4.3打印消耗时间: 结束时间 - 开始时间 = 消耗时间
        System.out.println("花费的时间为"+(t2 - t1));
    }
}

11.String/StringBuffer/StringBuilder的比较

  • String:不可变长字符串
public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
    private final char value[];
/**
     * Initializes a newly created {@code String} object so that it represents
     * the same sequence of characters as the argument; in other words, the
     * newly created string is a copy of the argument string. Unless an
     * explicit copy of {@code original} is needed, use of this constructor is
     * unnecessary since Strings are immutable.
     *
     * @param  original
     *         A {@code String}
     */
    public String(String original) {
        this.value = original.value;
        this.hash = original.hash;
    }    
  • StringBuffer:线程安全
	@Override
    public synchronized StringBuffer append(Object obj) {
        toStringCache = null;
        super.append(String.valueOf(obj));
        return this;
    }
  • StringBuilder:线程不安全,但是更高效
	@Override
    public StringBuilder append(Object obj) {
        return append(String.valueOf(obj));
    }
  • Java中操作字符串都有哪些类?它们之间有什么区别?
    • String,StringBuffer,StringBuilder。区别String声明的是不可变的对象,每次操作必然产生一个新的对象;StringBuffer和StringBuilder都继承自抽象类AbstractStringBuilder;StringBuffer加了同步关键字,具备线程安全性;
    • 在使用场景上,并发必选StringBuffer,迭代必选StringBuilder,普通场景选String,避免中途不必要的类型转换开销。

12. 扩容机制

  • 初始容量是16
    	/**
         * Constructs a string buffer with no characters in it and an
         * initial capacity of 16 characters.
         */
        public StringBuffer() {
            super(16);
        }
    
  • 扩容机制
	/**
     * Ensures that the capacity is at least equal to the specified minimum.
     * If the current capacity is less than the argument, then a new internal
     * array is allocated with greater capacity. The new capacity is the
     * larger of:
     * <ul>
     * <li>The {@code minimumCapacity} argument.
     * <li>Twice the old capacity, plus {@code 2}.
     * </ul>
     * If the {@code minimumCapacity} argument is nonpositive, this
     * method takes no action and simply returns.
     * Note that subsequent operations on this object can reduce the
     * actual capacity below that requested here.
     *
     * @param   minimumCapacity   the minimum desired capacity.
     */
    public void ensureCapacity(int minimumCapacity) {
        if (minimumCapacity > 0)
            ensureCapacityInternal(minimumCapacity);
    }

    /**
     * For positive values of {@code minimumCapacity}, this method
     * behaves like {@code ensureCapacity}, however it is never
     * synchronized.
     * If {@code minimumCapacity} is non positive due to numeric
     * overflow, this method throws {@code OutOfMemoryError}.
     */
    private void ensureCapacityInternal(int minimumCapacity) {
        // overflow-conscious code
        /*若所需长度大于已有长度,才继续进行扩容*/
        if (minimumCapacity - value.length > 0) {
        	/*通过Arrays.copyOf(),将旧value数组内容先复制到newCapacity大小的数组,再赋值给新value*/
            value = Arrays.copyOf(value,
                    newCapacity(minimumCapacity));
        }
    }

    /**
     * The maximum size of array to allocate (unless necessary).
     * Some VMs reserve some header words in an array.
     * Attempts to allocate larger arrays may result in
     * OutOfMemoryError: Requested array size exceeds VM limit
     */
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

    /**
     * Returns a capacity at least as large as the given minimum capacity.
     * Returns the current capacity increased by the same amount + 2 if
     * that suffices.
     * Will not return a capacity greater than {@code MAX_ARRAY_SIZE}
     * unless the given minimum capacity is greater than that.
     *
     * @param  minCapacity the desired minimum capacity
     * @throws OutOfMemoryError if minCapacity is less than zero or
     *         greater than Integer.MAX_VALUE
     */
    private int newCapacity(int minCapacity) {
        // overflow-conscious code
        /*默认扩容:newCapacity = 两倍的原长度 + 2*/
        int newCapacity = (value.length << 1) + 2;
        if (newCapacity - minCapacity < 0) { /*默认扩容后还是小于所需长度*/
            newCapacity = minCapacity; /*直接补充至所需长度*/
        }
        return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0)
            ? hugeCapacity(minCapacity)
            : newCapacity;
    }

    private int hugeCapacity(int minCapacity) {
    	/*大小超出Integer范围爆异常*/
        if (Integer.MAX_VALUE - minCapacity < 0) { // overflow
            throw new OutOfMemoryError();
        }
        /*返回minCapacity与MAX_ARRAY_SIZE最大值*/
        return (minCapacity > MAX_ARRAY_SIZE)
            ? minCapacity : MAX_ARRAY_SIZE;
    }
  • 拼接后字符长度 <= 初始容量,则容量不变依然是16
  • 拼接后字符长度 >= 初始容量后,开启扩容,会按照当前(初始)容量 * 2 + 2扩容一次
    • 为什么要+2 ,可能的原因如下:
      • 在创建StringBuffer/StringBuilder时,考虑到如果设置的初始长度不大时(例如1),+2 可以很大地提升扩容的效率,减少扩容的次数
      • newCapacity(int)的传入参数有可能是0,那么在参数是0的情况下,0<<1运算结果也是0,如果没+2,可能出现问题
  • 当一次追加扩容即使按照n * 2 + 2扩容一次也不够时,其容量会直接扩容到与所添加的字符串长度相等的长度。即当k > 2n + 2时,容量就变为k。之后再追加,还会按照 当前容量 * 2 + 2进行扩容
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值