写在最后面

这个我的项目是从20年末就立好的 flag,通过几年的学习,回过头再去看很多知识点又有新的了解。所以趁着找实习的筹备,联合以前的学习储备,创立一个次要针对应届生和初学者的 Java 开源常识我的项目,专一 Java 后端面试题 + 解析 + 重点常识详解 + 精选文章的开源我的项目,心愿它能随同你我始终提高!

阐明:此我的项目我的确有很用心在做,内容全副是我参考了诸多博主(已注明出处),材料,N本书籍,以及联合本人了解,从新绘图,从新组织语言等等所制。集体之力菲薄,或有不足之处,在劫难逃,但更新/欠缺会始终进行。大家的每一个 Star 都是对我的激励 !心愿大家能喜爱。

注:所有波及图片未应用网络图床,文章等均开源提供给大家。

我的项目名: Java-Ideal-Interview

Github 地址: Java-Ideal-Interview - Github

Gitee 地址:Java-Ideal-Interview - Gitee(码云)

继续更新中,在线浏览将会在前期提供,若认为 Gitee 或 Github 浏览不便,可克隆到本地配合 Typora 等编辑器舒服浏览

若 Github 克隆速度过慢,可抉择应用国内 Gitee 仓库

  • 三 Java 常见对象

    • 1. 根本演绎

      • 1.1 Object 类
      • 2.2 String 类
    • 2. 题目总结

      • 2.1 == 和 equals 的区别?
      • 2.2 如何比拟两个对象内容是否雷同?(重写 equals)
      • 2.3 hashCode() 和 equals()

        • 2.3.1 什么是 hashCode() 和 equals()
        • 2.3.2 equals() 曾经实现性能了,还须要 hashCode() 做什么?
        • 2.3.3 为什么不全副应用高效率的 hashCode(),还要用 equals()?
        • 2.3.4 hashCode() 和 equals() 是如何一起判断保障高效又牢靠的?
        • 2.3.5 为什么重写 equals 时必须重写 hashCode 办法?
      • 2.4 深拷贝和浅拷贝的区别?
      • 2.5 为什么重写 toString() 办法?
      • 2.6 字符串应用 += 赋值后,原始的String对象中的内容会扭转吗?
      • 2.7 字符串构造函数赋值和间接赋值的区别?
      • 2.8 String、StringBuffer、StringBuilder的区别
      • 2.9 字符串 “+” 和 StringBuilder 抉择用哪个?

三 Java 常见对象

阐明:本章次要波及到了:Object类、Scanner类、String类、StringBuffer和StringBuilder、Arrays工具类、根本类型包装类、正则表达式、System类、Math、Random类、BigInteger和BigDecimal类、Date、DateFormat和Calendar类

补充:因为 Object 以及 String 类属于高频内容,所以总结题目以及小点常识之前,会对其做一个根本的演绎温习。

1. 根本演绎

在解说这些常见类之前,咱们不得不简略的提一下什么是API,先贴一组百度百科的解释:

API(Application Programming Interface,应用程序编程接口)是一些事后定义的函数,目标是提供应用程序与开发人员基于某软件或硬件得以拜访一组例程的能力,而又无需拜访源码,或了解外部工作机制的细节。

简略的说:就是 Java 中有好多现成的类库,其中封装了许多函数,只提供函数名和参数,但暗藏了函数的具体实现,这些可见的局部作为与外界分割的桥梁,也就是咱们所称的 API ,不过因为Java是开源的,所以这些暗藏的实现咱们也是能够看到的。

1.1 Object 类

  • Object 是类层次结构的根类,所有的类都隐式的(不必写extends)继承自Object类。
  • Java 所有的对象都领有Object默认办法
  • Object 类的构造方法有一个,并且是无参结构

这就对应了后面学习中的一句话,子类构造方法默认拜访父类的结构是无参结构

咱们须要理解的办法又有哪些呢?

A:hashCode() B:getClass() C: finalize() D:clone() E:wait() F:notify() G:notifyAll()

咱们须要把握的办法又有哪些呢?

A:toString() B:equals()

办法总结:

// 1. 返回此Object的运行时类,是一个 native办法,同时因为应用了final关键字润饰,故不容许子类重写。public final native Class<?> getClass()// 2. 用于返回对象的哈希码,是一个native办法,例如次要波及在 HashMap 中。public native int hashCode()     // 3. 比拟两个对象是否雷同,默认比拟的是地址值是否雷同。而比拟地址值是没有意义的,所以,个别子类也会重写该办法。public boolean equals(Object obj)// 4. 实现对象的克隆,包含成员变量的数据复制,分为深浅克隆两种。是一个native办法。protected native Object clone() throws CloneNotSupportedException// 5. 返回类的名字@该实例16进制的哈希码字符串。因而倡议Object 所有的子类都重写此办法。    public String toString() // 6. 唤醒一个在此对象监视器上期待的线程(监视器了解为锁)。若有多个线程在期待只会任意唤醒一个。是一个 native办法,且不能重写。public final native void notify()// 7. 同 notify(),区别是会唤醒在此对象监视器上期待的所有线程。public final native void notifyAll()// 8. 意为暂停线程的执行.是一个native办法。留神:开释了锁,而sleep办法不开释锁。timeout是等待时间。public final native void wait(long timeout) throws InterruptedException    // 9. 多了一个nanos参数,代表额定工夫(以毫微秒为单位,范畴是 0-999999)。 所以工夫最初要计算总和。public final void wait(long timeout, int nanos) throws InterruptedException// 10同前两个 wait() 只不过该办法始终期待public final void wait() throws InterruptedException// 11. 在对象将被垃圾回收器革除前调用,但不确定工夫  protected void finalize() throws Throwable { }

2.2 String 类

String 是一个很罕用的类,简略演绎一下常见的办法

构造方法

// 1. 空结构public String()// 2. 把字节数组转换成字符串public String(byte[] bytes)// 3. 把字节数组的一部分转换成字符串public String(byte[] bytes,int offset,int length)// 4. 把字符数组转换成字符串public String(char[] value)// 5. 把字符数组的一部分转换成字符串public String(char[] value,int offset,int count)// 6. 把字符串常量值转换成字符串    public String(String original)// 7. 上面的这一个尽管不是构造方法,然而后果也是一个字符串对象String s = "hello";

简略总结:String类的构造方法能够将 字节、字符数组、字符串常量(全副或者局部)转换为字符串类型

判断办法

// 1. 比拟字符串的内容是否雷同,辨别大小写boolean equals(Object obj)// 2. 比拟字符串的内容是否雷同,不辨别大小写boolean equalsIgnoreCase(String str)// 3. 判断大字符串中是否蕴含小字符串boolean contains(String str)// 4. 判断某个字符串是否以某个指定的字符串结尾boolean startsWith(String str)// 5. 判断某个字符串是否以某个指定的字符串结尾boolean endsWith(String str)// 6. 判断字符串是否为空boolean isEmpty()留神:String s = “”;    // 字符串内容为空String s = null;  // 字符串对象为空

获取办法

// 1. 获取字符串的长度int length()// 2. 获取指定索引的字符char charAt(int index)// 3. 返回指定字符在此字符串中第一次呈现的索引int indexOf(int ch)// 为什么这里是int而不是char?// 起因是:‘a’和‘97’其实都能代表‘a’ int不便// 4. 返回指定字符串在此字符串中第一次呈现的索引int indexOf(String str)// 5. 返回指定字符在此字符串中从指定地位后第一次呈现的索引int indexOf(int ch,int fromIndex)// 6. 返回指定字符串在此字符串中从指定地位后第一次呈现的索引int indexOf(String str, int fromIndex)// 7. 从指定地位开始截取字符串,默认到开端String substring(int start)// 8. 从指定地位开始指定地位完结截取字符串String substring(int start, int end)

转换方法

// 1. 把字符串转换为字节数组byte[] getBytes()// 2. 把字符串转换成字符数组char[] toCharArray()// 3. 把字符数组转换成字符串static String valueOf(char[] chs)// 3. 把int类型的数据转换成字符串static String valueOf(int i)// 留神:String类的valueOf办法能够把任何类型的数据转换成字符串!  // 4. 把字符串转换成小写    String toLowerCase()// 5. 把字符串转换成大写String toUpperCase()// 7. 把字符串拼接String concat(String str)

其余办法

// 1. 替换性能 String replace(char old,char new)String replace(String old,String new)// 2. 去除字符串两端空格String trim()// 3. 按字典比拟性能int compareTo(String str)int compareToIgnoreCase(String str) 

2. 题目总结

2.1 == 和 equals 的区别?

== :如果比拟的对象是根本数据类型,则比拟的是数值是否相等;如果比拟的是援用数据类型,则比拟的是对象

的地址值是否相等。

equals():equals 办法不能用于根本数据类型的变量,如果没有对 equals 办法进行重写,则比拟的是援用类型的变量所指向的对象的地址。个别会抉择重写此办法,来比拟两个对象的内容是否相等,相等则返回 true。

2.2 如何比拟两个对象内容是否雷同?(重写 equals)

例如一个 Student 类,new 两个对象进去,单纯的想比拟内容是否雷同如何做呢。

public class Student {    private String name;    public int age;    // get set ... }

通过 equals() 比拟两个对象是否雷同,默认状况下,比拟的是地址值是否雷同。而比拟地址值是没有意义的,所以,个别子类也会重写该办法。在诸多子类,如String、Integer、Date 等均重写了 equals() 办法

改良思路:咱们能够将比拟地址值转变为比拟成员变量

  • 因为 name 为 String 类型,而 String 类型为援用类型,所以不可能用 == 比拟,应该用 equal()
  • String 中默认重写过的 equal() 办法是用来比拟字符串内容是否雷同
  • 咱们要应用的是学生类的成员变量,所以父类 Object不能调用子类Student的特有性能,所以应用向下转型
//重写v1.0public boolean equals(Object o) {    Student s = (Student) o;    if (this.name.equals(s.name) && this.age == s.age) {        return true;    } else {        return false;    }}
//重写v2.0 (可作为最终版)public boolean equals(Object o) {    if (this.name == o) {        return true;    }    //测试它右边的对象是否是它左边的类的实例,返回 boolean 的数据类型。    if (!(o instanceof Student)) {        return false;    }    Student s = (Student) o;    return this.name.equals(s.name) && this.age == s.age;}
// IDEA主动生成版@Overridepublic boolean equals(Object o) {    if (this == o) return true;    if (o == null || getClass() != o.getClass()) return false;    Student student = (Student) o;    return age == student.age &&        Objects.equals(name, student.name);}

2.3 hashCode() 和 equals()

2.3.1 什么是 hashCode() 和 equals()

hashCode() 办法是 Object 类中的一个本地办法(用 c 语言或 c++ 实现的),会返回该对象的哈希码,也称为散列码;其本质是返回一个 int 整数。哈希码的作用是确定该对象在哈希表中的索引地位。能够通过散列码,在散列表中依据“键”疾速的检索出对应的“值”。从而疾速找到须要的对象,而后进行判断是不是同一个对象。

public native int hashCode();

equals() 办法是Object 类中的一个办法,如果没有对 equals 办法进行重写,则比拟的是援用类型的变量所指向的对象的地址。个别会抉择重写此办法,来比拟两个对象的内容是否相等,相等则返回 true。

总结:单思考目标两者是差不多的,都是用来比照两个对象是否相等统一。

2.3.2 equals() 曾经实现性能了,还须要 hashCode() 做什么?

重写 equals() 外面的内容个别比拟全面周详,然而效率就比拟低,例如:如果汇合中当初曾经有2000个元素,那么第2001个元素退出汇合时,它就要调用 2000次 equals办法。

而应用 hashCode() ,其应用的哈希算法也称为散列算法,是将数据依特定算法间接指定到一个地址上,所以 hashCode() 这种造成 hash 码的形式比拟是比拟高效的。

2.3.3 为什么不全副应用高效率的 hashCode(),还要用 equals()?

hashCode() 办法不是一个 100% 牢靠的办法,个别情况下,不同的对象生成的 hashcode 也可能会雷同。

2.3.4 hashCode() 和 equals() 是如何一起判断保障高效又牢靠的?

如果大量内容都是用 equals() 去比对,效率显然是比拟低的,所以每次比对之前都去应用 hashCode() 去比照,如果返回的 hashCode 不同,代表两个对象必定不雷同,就能够间接返回后果了。如果 hashCode 雷同,又为了保障其相对牢靠,所以应用 equals() 再次进行比对,同样是雷同,就保障了这两个对象相对雷同。

2.3.5 为什么重写 equals 时必须重写 hashCode 办法?

如果重写了 equals() 而未重写 hashcode() 办法,可能就会呈现两个字面数据雷同的对象(例如上面 stu1 和 stu2) equals 雷同(因为 equals 都是依据对象的特色进行重写的),但 hashcode 不雷同的状况。

public class Student {    private String name;    public int age;    // get set ...     // 重写 equals() 不重写 hashcode()}--------------------------------------------Student stu1 = new Student("BWH_Steven",22);Student stu2 = new Student("BWH_Steven",22);--------------------------------------------stu1.equals(stu2); // truestu1.hashCode();  // 和 stu2.hashCode(); 后果不统一stu2.hashCode();

如果把对象保留到 HashTable、HashMap、HashSet 等中(不容许反复),这种状况下,去查找的时候,因为都是先应用 hashCode() 去比照,如果返回的 hashCode 不同,则会认为对象不同。能够存储,从内容上看,显著就反复了。

所以个别的中央不须要重写 hashcode() ,只有当类须要放在 HashTable、HashMap、HashSet 等hash 构造的汇合时才会去重写。

补充:阿里巴巴 Java 开发手册对于 hashCode 和 equals 的解决遵循规定:

  • 只有重写 equals,就必须重写 hashCode。
  • 因为 Set 存储的是不反复的对象,根据 hashCode 和 equals 进行判断,所以 Set 存储的对象必须重写这两个办法。
  • 如果自定义对象做为 Map 的键,那么必须重写 hashCode 和 equals。
  • String 重写了 hashCode 和 equals 办法,所以咱们能够十分欢快地应用 String 对象作为 key 来应用。

2.4 深拷贝和浅拷贝的区别?

浅拷贝(浅克隆):根本数据类型为值传递,对象类型为援用传递(两者同生共死)

深拷贝(深克隆):对于对象或者数值,所有元素或者属性均齐全复制,与原对象脱离(真正意义上的复制, 两者独立无关)

举例:

public class Book {    private String name; // 姓名    private int price; // 价格    private Partner partner; // 合作伙伴    // 省略构造函数、get set、toString 等}
public class Partner{    private String name;    // 省略构造函数、get set、toString 等}

浅拷贝用到拷贝,首先就对 Book 类进行解决

  • 首先实现 Cloneable 接口
  • 接着重写 clone 办法
public class Book implements Cloneable{    private String name; // 姓名    private int price; // 价格    private Partner partner; // 合作伙伴    @Override    protected Object clone() throws CloneNotSupportedException {        return super.clone();    }        // 省略构造函数、get set、toString 等}

再来测试一下

public class Test {    public static void main(String[] args) throws CloneNotSupportedException {        // 初始化一个合作伙伴类型        Partner partner = new Partner("张三");        // 带参赋值        Book bookA = new Book("现实二旬不止", 66, partner);        // B 克隆 A        Book bookB = (Book) bookA.clone();        System.out.println("A: " + bookA.toString());        System.out.println("A: " + bookA.hashCode());        System.out.println("B: " + bookB.toString());        System.out.println("B: " + bookB.hashCode());    }}

执行后果

A: Book{name='现实二旬不止', price=66, partner=Partner{name=张三}}
A: 460141958
B: Book{name='现实二旬不止', price=66, partner=Partner{name=张三}}
B: 1163157884

后果非常明显,书籍信息是统一的,然而内存地址是不一样的,也就是说的确克隆胜利了,打印其 hashCode 发现两者并不相同,阐明不止指向同一个,也是满足咱们要求的

到这里并没有完结,你会发现还是有问题,当你刊印的过程中批改一些值的内容的时候,你看看成果

public class Test {    public static void main(String[] args) throws CloneNotSupportedException {        // 初始化一个合作伙伴类型        Partner partner = new Partner("张三");        // 带参赋值        Book bookA = new Book("现实二旬不止", 66, partner);        // B 克隆 A        Book bookB = (Book) bookA.clone();        // 批改数据        bookB.getPartner().setName("李四");        bookB.setPrice(44);        System.out.println("A: " + bookA.toString());        System.out.println("A: " + bookA.hashCode());        System.out.println("B: " + bookB.toString());        System.out.println("B: " + bookB.hashCode());    }}

执行后果

A: Book{name='现实二旬不止', price=66, partner=Partner{name=李四}}
A: 460141958
B: Book{name='现实二旬不止', price=66, partner=Partner{name=李四}}
B: 1163157884

???这不对啊,B 明明是克隆 A 的,为什么我在克隆后,批改了 B 中两个值,然而 A 也变动了啊

这就是典型的浅克隆,在 Book 类,当字段是援用类型,例如 Partner 这个合作伙伴类,就是咱们自定义的类,这种状况不复制援用的对象,因而,原始对象和复制后的这个Partner对象是援用同一个对象的。而作为根本类型的的值就没事。

如何解决下面的问题呢,咱们须要重写主类的 clone 的内容(改为深拷贝),同时在援用类型中也实现浅拷贝

A:被援用类型实现浅克隆

public class Partner implements Cloneable {    private String name;    @Override    protected Object clone() throws CloneNotSupportedException {        return super.clone();    }    // 省略构造函数、get set、toString 等}

B:批改援用类 cloen 办法

public class Book implements Cloneable{    private String name; // 姓名    private int price; // 价格    private Partner partner; // 合作伙伴    @Override    protected Object clone() throws CloneNotSupportedException {        Object clone = super.clone();        Book book = (Book) clone;        book.partner =(Partner) this.partner.clone();        return clone;    }    // 省略构造函数、get set、toString 等}

C:测试一下

public class Test {    public static void main(String[] args) throws CloneNotSupportedException {        // 初始化一个合作伙伴类型        Partner partner = new Partner("张三");        // 带参赋值        Book bookA = new Book("现实二旬不止", 66, partner);        // B 克隆 A        Book bookB = (Book) bookA.clone();        // 批改数据        partner.setName("李四");        System.out.println("A: " + bookA.toString());        System.out.println("A: " + bookA.hashCode());        System.out.println("B: " + bookB.toString());        System.out.println("B: " + bookB.hashCode());    }}

执行成果

A: Book{name='现实二旬不止', price=66, partner=Partner{name=李四}}
A: 460141958
B: Book{name='现实二旬不止', price=66, partner=Partner{name=张三}}
B: 1163157884

能够看到,B 克隆 A 后,批改 A 中 合作伙伴 的值,没有受到影响,这也就是咱们通常意义上想要实现的成果了。

2.5 为什么重写 toString() 办法?

次要目标还是为了简化输入

  1. 在类中重写toString()后,输入类对象就变得有了意义(输入s 和 s.toString()是一样的 ,不写也会默认调用),变成了咱们实实在在的信息 ,例如 Student{name='admin', age=20},而不是下面的 cn.ideal.pojo.Student@1b6d3586
  2. 如果咱们想要屡次输入 类中的成员信息,就须要屡次书写 ge t办法(每用一次就得写)
toString() 办法,返回该对象的字符串示意。

Object 类的 toString 办法返回一个字符串,该字符串由类名(对象是该类的一个实例)at 标记符 @ 和此对象哈希码的无符号十六进制示意组成。换句话说,该办法返回一个字符串,它的值等于:

代码:getClass().getName()+ '@' + Integer.toHexString(hashCode())

通常咱们心愿, toString 办法会返回一个“以文本形式示意” 此对象的字符串。后果应是一个扼要但易于读懂的信息表达式。因而倡议所有子类都重写此办法。

2.6 字符串应用 += 赋值后,原始的String对象中的内容会扭转吗?

答案:不会

/* *  字符串特点:一旦被赋值,就不能扭转 */public class StringDemo {    public static void main(String[] args) {        String s = "Hello";        s += "World";        System.out.println("s:" + s);    }}//运行后果:s:HelloWorld

解释:不能扭转是指字符串对象自身不能扭转,而不是指对象的援用不能扭转,上述过程中,字符串自身的内容是没有任何变动的,而是别离创立了三块内存空间,(Hello) (World) (HelloWorld) Hello + World 拼接成 HelloWorld 这时,s 不指向原来那个 “Hello” 对象了,而指向了另一个String对象,内容为 “HelloWorld ” ,原来那个对象还存在内存中,只是 s 这个援用变量不再指向它了。

总结:开发中,尽量少应用 + 进行字符串的拼接,尤其是循环内,咱们更加举荐应用StringBuild、StringBuffer。

2.7 字符串构造函数赋值和间接赋值的区别?

通过 new 构造函数创立字符串对象。String s = new String("Hello"); 零碎会先创立一个匿名对象 "Hello" 存入堆内存,而后 new 关键字会在堆内存中又开拓一块新的空间,而后把"Hello"存进去,并且把地址返回给栈内存中的 s, 方才的匿名对象 "Hello" 就变成了一个垃圾对象,因为它没有被任何栈中的变量指向,会被GC主动回收。

间接赋值。如String str = "Hello"; 首先会去字符串常量池中找有没有一个"Hello"对象,如果没有,则新建一个,并且入池,所以此种赋值有一个益处,下次如果还有 String 对象也用间接赋值形式定义为“Hello”, 则不须要开拓新的堆空间,而依然指向这个池中的"Hello"。

//两者的区别String s = new String("Hello");String s = "Hello";

总结:前者new一个对象,“hello”隐式创立一个对象,后者只有“Hello”创立一个对象,在开发中,尽量应用 String s = "Hello" 的形式,效率比另一种高。

2.8 String、StringBuffer、StringBuilder的区别

后面咱们用字符串做拼接,比拟耗时并且也耗内存(每次都会结构一个新的string对象),而这种拼接操作又是比拟常见的,为了解决这个问题,Java就提供了两个字符串缓冲区类。StringBuffer和StringBuilder供咱们应用。

简略比拟

String:长度大小不可变

StringBuffer:长度可变、线程平安、速度较慢

StringBuilder:长度可变、线程不平安、速度最快

解释:

  1. 在执行速度方面的比拟:StringBuilder > StringBuffer
  2. StringBuffer与StringBuilder,他们是字符串变量,是可扭转的对象,每当咱们用它们对字符串做操作时,实际上是在一个对象上操作的,不像String一样创立一些对象进行操作,所以速度就快了。
  3. StringBuilder:线程非平安的

    StringBuffer:线程是平安的(synchronized关键字进行润饰)

当咱们在字符串缓冲区被多个线程应用时,JVM 不能保障 StringBuilder 的操作是平安的,尽管他的速度最快,然而能够保障 StringBuffer 是能够正确操作的。当然大多数状况下就是咱们是在单线程下进行的操作,所以大多数状况下是倡议用StringBuilder而不必StringBuffer的,就是速度的起因。

对于三者应用的总结:

  1. 如果要操作大量的数据用 String
  2. 单线程操作字符串缓冲区 下操作大量数据 StringBuilder
  3. 多线程操作字符串缓冲区 下操作大量数据 StringBuffer

2.9 字符串 “+” 和 StringBuilder 抉择用哪个?

首先java并不反对运算符重载(String类中的 “+” 和 “+=” 是 Java 中仅有的两个重载过的运算符),所以咱们能够通过 “+” 符号 将多个字符串进行拼接

将图中代码(应用了 “+” 符号)利用 javap -c filename 反编译

咱们能够看到代码被编译器主动优化成应用StringBuilder形式拼接,运行效率失去了保障

上面一个案例 数组拼接成指定格局的字符串 代码中应用了循环语句

// 在循环中通过String拼接字符串public class StringBuilderDemo {   public static void main(String[] args) {       String[] arr = {"Hello", "World", "!!!"};       String s1 = arrayToString(arr);       System.out.println(s1);   }   public static String arrayToString(String[] arr) {       String s = "";       s += "[";       for (int x = 0; x < arr.length; x++) {           if (x == arr.length - 1) {               s += arr[x];           } else {               s += arr[x];               s += ", ";           }       }       s += "]";       return s;   }}//运行后果[Hello, World, !!!]

应用String形式进行拼接,咱们反编译能够看到,StringBuilder被创立在循环的外部,这意味着每循环一次就会创立一次StringBuilder对象,这可是一个蹩脚的事件。

// 在循环中应用StringBuilder拼接字符串public class StringBuilderDemo2 {    public static void main(String[] args) {        String[] arr = {"Hello", "World", "!!!"};        String s1 = arrayToString(arr);        System.out.println(s1);    }    public static String arrayToString(String[] arr) {        StringBuilder s = new StringBuilder();        s.append("[");        for (int x = 0; x < arr.length; x++) {            if (x == arr.length - 1) {                s.append(arr[x]);            } else {                s.append(arr[x]);                s.append(", ");            }        }        s.append("]");        return s.toString();    }}//运行后果[Hello, World, !!!]

应用StringBuilder形式进行拼接,自行去看一下汇编代码中,不仅循环局部的代码更为简洁,而且它只生成了一个StringBuilder对象。显式的创立StringBuilder对象还容许你事后为其指定大小。能够防止屡次重新分配缓冲。

总结:

如果字符串操作比较简单,就能够应用 “+” 运算符操作,编译器会为你正当的结构出最终的字符串后果

如果应用循环语句 最好本人手动创立一个StringBuilder对象,用它来构最终后果