当前位置: 代码迷 >> 综合 >> java面试必备--JAVA基础篇(五) 之 HashCode String解读
  详细解决方案

java面试必备--JAVA基础篇(五) 之 HashCode String解读

热度:76   发布时间:2023-09-28 21:35:53.0

   相信很多同行小伙伴会因为许多原因想跳槽,不论是干得不开心还是想跳槽涨薪,在如此内卷的行业,我们都面临着“面试造火箭,上班拧螺丝”的局面,鉴于当前形势博主呕心沥血整理的干货满满的造火箭的技巧来了,本博主花费2个月时间,整理归纳java全生态知识体系常见面试题!总字数高达百万! 干货满满,每天更新,关注我,不迷路,用强大的归纳总结,全新全细致的讲解来留住各位猿友的关注,希望能够帮助各位猿友在应付面试笔试上!当然如有归纳总结错误之处请各位指出修正!如有侵权请联系博主QQ1062141499! 
  

目录

1  两个对象的hashCode()相同,则equals()也一定为true,对吗?

2 HashCode的作用

   2.1、HashCode的特性

   2.2、HashCode作用

3 为什么要重载equals方法?

4 为什么重载hashCode方法?

5  为什么equals()相等,hashCode就一定要相等,而hashCode相等,却不要求equals相等?

6  为什么需要hashCode?

7  两个对象值相同(x.equals(y)==true),但却可有不同的hashCode,这句话对不对?

8 为什么重写equals方法时,必须重写hashcode方法

 

1 String 属于基础的数据类型吗?

2 Java中操作字符串都有哪些类?它们之间有什么区别?

3 String str="i"与 String str=new String("i")一样吗?

4 如何将字符串反转?

?????5 String 类的常用方法都有那些?

6 String s = "Hello";s = s + " world!";这两行代码执行后,原始的String对象中的内容到底变了没有?

7 是否可以继承String?

8 数组有没有length()方法?String有没length()方法

9 请说出下面程序的输出

10 判断字符串是否包含中文

11  为什么 String 在 Java 中是不可变的?

12 为什么char数组比Java中的String更适合存储密码?

13 String对象中的replace和replaceAll的区别?

14 GB2312编码的字符串如何转换为ISO-8859-1编码?

15 String s="a"+"b"+"c"+"d";创建了几个对象?


???????

1  两个对象的hashCode()相同,则equals()也一定为true,对吗?

     不对,两个对象的hashCode()相同,equals()不一定为true。

     代码示例:

Stringstr1="通话";
Stringstr2="重地";
System.out.println(String.format("str1:%d|str2:%d",str1.hashCode(),str2.hashCode()));
System.out.println(str1.equals(str2));

执行的结果:

str1:1179395|str2:1179395	
false

     代码解读:很显然“通话”和“重地”的hashCode()相同,然而equals()则为false,因为在散列表中,hashCode()相等即两个键值对的哈希值相等,然而哈希值相等,并不一定能得出键值对相等。

关于hashCode()和equals()是方法是有一些常规协定:

1、两个对象用equals()比较返回true,那么两个对象的hashCode()方法必须返回相同的结果。

2、两个对象用equals()比较返回false,不要求hashCode()方法也一定返回不同的值,但是最好返回不同值,以提高哈希表性能。

3、重写equals()方法,必须重写hashCode()方法,以保证equals()方法相等时两个对象hashcode()返回相同的值。

2 HashCode的作用

   2.1、HashCode的特性

        (1)HashCode的存在主要是用于查找的快捷性,如Hashtable,HashMap等,HashCode经常用于确定对象的存储地址。

        (2)如果两个对象相同,equals方法一定返回true,并且这两个对象的HashCode一定相同。

        (3)两个对象的HashCode相同,并不一定表示两个对象就相同,即equals()不一定为true,只能够说明这两个对象在一个散列存储结构中。

        (4)如果对象的equals方法被重写,那么对象的HashCode也尽量重写。

   2.2、HashCode作用

     Java中的集合有两类,一类是List,再有一类是Set。前者集合内的元素是有序的,元素可以重复;后者元素无序,但元素不可重复。

     equals方法可用于保证元素不重复,但如果每增加一个元素就检查一次,若集合中现在已经有1000个元素,那么第1001个元素加入集合时,就要调用1000次equals方法。这显然会大大降低效率。于是,Java采用了哈希表的原理。

     哈希算法也称为散列算法,是将数据依特定算法直接指定到一个地址上。

       这样一来,当集合要添加新的元素时,先调用这个元素的HashCode方法,就一下子能定位到它应该放置的物理位置上。

     (1)如果这个位置上没有元素,它就可以直接存储在这个位置上,不用再进行任何比较了。

     (2)如果这个位置上已经有元素了,就调用它的equals方法与新元素进行比较,相同的话就不存了。

     (3)不相同的话,也就是发生了Hashkey相同导致冲突的情况,那么就在这个Hashkey的地方产生一个链表,将所有产生相同HashCode的对象放到这个单链表上去,串在一起(很少出现)。

       这样一来实际调用equals方法的次数就大大降低了,几乎只需要一两次。

如何理解HashCode的作用:

        从Object角度看,JVM每new一个Object,它都会将这个Object丢到一个Hash表中去,这样的话,下次做Object的比较或者取这个对象的时候(读取过程),它会根据对象的HashCode再从Hash表中取这个对象。这样做的目的是提高取对象的效率。若HashCode相同再去调用equals。

3 为什么要重载equals方法?

      因为Object的equals方法默认是两个对象的引用的比较,意思就是指向同一内存,地址则相等,否则不相等;如果你现在需要利用对象里面的值来判断是否相等,则重载equals方法。

4 为什么重载hashCode方法?

       一般的地方不需要重载hashCode,只有当类需要放在HashTable、HashMap、HashSet等等hash结构的集合时才会重载hashCode,那么为什么要重载hashCode呢?

       如果你重写了equals,比如说是基于对象的内容实现的,而保留hashCode的实现不变,那么很可能某两个对象明明是“相等”,而hashCode却不一样。这样,当你用其中的一个作为键保存到hashMap、hashTable或hashSet中,再以“相等的”找另一个作为键值去查找他们的时候,则根本找不到。

5  为什么equals()相等,hashCode就一定要相等,而hashCode相等,却不要求equals相等?

      1、因为是按照hashCode来访问小内存块,所以hashCode必须相等。

      2、HashMap获取一个对象是比较key的hashCode相等和equals为true。

      之所以hashCode相等,却可以equals不等,就比如ObjectA和ObjectB他们都有属性name,那么hashCode都以name计算,所以hashCode一样,但是两个对象属于不同类型,所以equals为false。

6  为什么需要hashCode?

    1、通过hashCode可以很快的查到小内存块。

    2、通过hashCode比较比equals方法快,当get时先比较hashCode,如果hashCode不同,直接返回false。

7  两个对象值相同(x.equals(y)==true),但却可有不同的hashCode,这句话对不对?

     不对,如果两个对象x和y满足x.equals(y)==true,它们的哈希码(hashCode)应当相同。Java对于eqauls方法和hashCode方法是这样规定的:

    (1)如果两个对象相同(equals方法返回true),那么它们的hashCode值一定要相同;

    (2)如果两个对象的hashCode相同,它们并不一定相同。当然,你未必要按照要求去做,但是如果你违背了上述原则就会发现在使用容器时,相同的对象可以出现在Set集合中,同时增加新元素的效率会大大下降(对于使用哈希存储的系统,如果哈希码频繁的冲突将会造成存取性能急剧下降)。

      关于equals和hashCode方法,很多Java程序员都知道,但很多人也就是仅仅知道而已,在JoshuaBloch的大作《EffectiveJava》(很多软件公司,《EffectiveJava》、《Java编程思想》以及《重构:改善既有代码质量》是Java程序员必看书籍,如果你还没看过,那就赶紧去买一本吧)中是这样介绍equals方法的。

      首先equals方法必须满足自反性(x.equals(x)必须返回true)、对称性(x.equals(y)返回true时,y.equals(x)也必须返回true)、传递性(x.equals(y)和y.equals(z)都返回true时,x.equals(z)也必须返回true)和一致性(当和y引用的对象信息没有被修改时,多次调用x.equals(y)应该得到同样的返回值),而且对于任何非null值的引用x,x.equals(null)必须返回false。实现高质量的equals方法的诀窍包括:

      1.使用==操作符检查"参数是否为这个对象的引用";

      2.使用instanceof操作符检查"参数是否为正确的类型";

      3.对于类中的关键属性,检查参数传入对象的属性是否与之相匹配;

      4.编写完equals方法后,问自己它是否满足对称性、传递性、一致性;

      5.重写equals时总是要重写hashCode;

      6.不要将equals方法参数中的Object对象替换为其他的类型,在重写时不要忘掉@Override注解。

8 为什么重写equals方法时,必须重写hashcode方法

    1.维护 hashCode 方法的常规协定,该协定声明相等对象必须具有相等的哈希码。

    2.hashcode是用于散列数据的快速存取,如利用hash结构集合类来存储数据时,都是根据存储对象的hashcode值来进行判断是否相同的。所以会出现一种可能,当重写equals方法后,判断对象相等,但其hashcode却不一致,这种相等可以看作为逻辑相等。当使用hash集合类时,存放时会根据该类的hashcode方法,来确定其存放位置,如hashset,将无法过滤相同对象,因为不重写hashcode,会默认调用Object类的hashcode方法,计算出来的存 地址不一样,会导致能同时存放两个值相等的对象,产生混淆。


 

1 String 属于基础的数据类型吗?

    String 不属于基础类型,基础类型有 8 种:byte、boolean、char、short、int、float、long、double,而 String 属于对象。

2 Java中操作字符串都有哪些类?它们之间有什么区别?

    操作字符串的类有:StringStringBufferStringBuilder

    String 和 StringBuffer、StringBuilder 的区别在于 String 声明的是不可变的对象,每次操作都会生成新的 String 对象,然后将指针指向新的 String 对象而 StringBuffer、StringBuilder 可以在原有对象的基础上进行操作,所以在经常改变字符串内容的情况下最好不要使用 String。

    StringBuffer 和 StringBuilder 最大的区别在于,StringBuffer 是线程安全的,而 StringBuilder 是非线程安全的,但 StringBuilder 的性能却高于 StringBuffer,所以在单线程环境下推荐使用 StringBuilder,多线程环境下推荐使用 StringBuffer。

3 String str="i"与 String str=new String("i")一样吗?

    不一样,因为内存的分配方式不一样。String str="i"的方式,Java 虚拟机会将其分配到常量池中;而 String str=new String("i") 则会被分到堆内存中。

String str="i";
String str2 = new String("i");
System.out.println(str==str2);//false
System.out.println(str.equals(str2));//true

4 如何将字符串反转?

    使用 StringBuilder 或者 stringBuffer 的 reverse() 方法。

    示例代码:

// StringBuffer reverse
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append("abcdefg");
System.out.println(stringBuffer.reverse()); // gfedcba
// StringBuilder reverse
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("abcdefg");
System.out.println(stringBuilder.reverse()); // gfedcba

?????5 String 类的常用方法都有那些?

  1. equals:字符串是否相同
  2. equalsIgnoreCase:忽略大小写后字符串是否相同
  3. compareTo:根据字符串中每个字符的Unicode编码进行比较
  4. compareToIgnoreCase:根据字符串中每个字符的Unicode编码进行忽略大小写比较
  5. indexOf:目标字符或字符串在源字符串中位置下标
  6. lastIndexOf:目标字符或字符串在源字符串中最后一次出现的位置下标
  7. valueOf:其他类型转字符串
  8. charAt:获取指定下标位置的字符
  9. codePointAt:指定下标的字符的Unicode编码
  10. concat:追加字符串到当前字符串
  11. isEmpty:字符串长度是否为0
  12. contains:是否包含目标字符串
  13. startsWith:是否以目标字符串开头
  14. endsWith:是否以目标字符串结束
  15. format:格式化字符串
  16. getBytes:获取字符串的字节数组
  17. getChars:获取字符串的指定长度字符数组
  18. toCharArray:获取字符串的字符数组
  19. join:以某字符串,连接某字符串数组
  20. length:字符串字符数
  21. matches:字符串是否匹配正则表达式
  22. replace:字符串替换
  23. replaceAll:带正则字符串替换
  24. replaceFirst:替换第一个出现的目标字符串
  25. split:以某正则表达式分割字符串
  26. substring:截取字符串
  27. toLowerCase:字符串转小写
  28. toUpperCase:字符串转大写
  29. trim:去字符串首尾空格

6 String s = "Hello";s = s + " world!";这两行代码执行后,原始的String对象中的内容到底变了没有?

    没有。因为 String 被设计成不可变(immutable)类,所以它的所有对象都是不可变对象。在这段代码中,s  原先指向一个 String 对象,内容是 "Hello",然后我们对 s 进行了“+”操作,那么 s  所指向的那个对象是否发生了改变呢?答案是没有。这时,s 不指向原来那个对象了,而指向了另一个 String 对象,内容为"Hello world!",原来那个对象还存在于内存之中,只是 s  这个引用变量不再指向它了。

   通过上面的说明,我们很容易导出另一个结论,如果经常对字符串进行各种各样的修改,或者说,不可预见的修,那么使用 String来代表字符串的话会引起很大的内存开销。因为 String对象建立之后不能再改变,所以对于每一个不同的字符串,都需要一个 String 对象来表示。这时,应该考虑使用 StringBuffer类,它允许修改,而不是每个不同的字符串都要生成一个新的对象。并且,这两种类的对象转换十分容易。同时,我们还可以知道,如果要使用内容相同的字符串,不必每次都 new 一个 String。例如我们要在构造器中对一个名叫 s 的 String  引用变量进行初始化,把它设置为初始值,应当这样做:

public class Demo {private String s;...s = "Initial Value";...
}

而非:

s = new String("Initial Value");

    后者每次都会调用构造器,生成新对象,性能低下且内存开销大,并且没有意义,因为 String  对象不可改变,所以对于内容相同的字符串,只要一个 String  对象来表示就可以了。也就说,多次调用上面的构造器创建多个对象,他们的 String 类型属性 s  都指向同一个对象。

    上面的结论还基于这样一个事实:对于字符串常量,如果内容相同,Java 认为它们代表同一个String对象。而用关键字 new 调用构造器,总是会创建一个新的对象,无论内容是否相同。 至于为什么要把 String  类设计成不可变类,是它的用途决定的。其实不只 String,很多Java标准类库中的类都是不可变的。在开发一个系统的时候,我们有时候也需要设计不可变类,来传递一组相关的值,这也是面向对象思想的体现。不可变类有一些优点,比如因为它的对象是只读的,所以多线程并发访问也不会有任何问题。当然也有一些缺点,比如每个不同的状态都要一个对象来代表,可能会造成性能上的问题。所以 Java 标准类库还提供了一个可变版本,即 StringBuffer。

7 是否可以继承String?

    String类是final类,不可以被继承。

    继承String本身就是一个错误的行为,对String类型最好的重用方式是关联关系(Has-A)和依赖关系(Use-A)而不是继承关系(Is-A)。

8 数组有没有length()方法?String有没length()方法

   数组没有length()方法,而是有length的属性。String有length()方法。JavaScript中,获得字符串的长度是通过length属性得到的,这一点容易和Java混淆。

9 请说出下面程序的输出

String s1 = "Programming";
String s2 = new String("Programming");
String s3 = "Program";
String s4 = "ming";
String s5 = "Program" + "ming";
String s6 = s3 + s4;
System.out.println(s1 == s2); //false
System.out.println(s1 == s5); //true
System.out.println(s1 == s6); //false
System.out.println(s1 == s6.intern());  //true
System.out.println(s2 == s2.intern());//false

补充:解答上面的面试题需要知道如下两个知识点:

   1.String对象的intern()方法会得到字符串对象在常量池中对应的版本的引用(如果常量池中有一个字符串与String对象的equals结果是true),如果常量池中没有对应的字符串,则该字符串将被添加到常量池中,然后返回常量池中字符串的引用;

   2.字符串的+操作其本质是创建了StringBuilder对象进行append操作,然后将拼接后的StringBuilder对象用toString方法处理成String对象,这一点可以用javap-cStringEqualTest.class命令获得class文件对应的JVM字节码指令就可以看出来。

10 判断字符串是否包含中文

/*** 根据字符串长度判断字符串中是否包含中文* 由于中文字符占用的字节数是2或者3个,英文字符占用的字节数是1,因此转换成字节后,如果有中文字符,那么字节长度会大于字符串长度*/
public static boolean isContainChineseByLength(String str) {if (str.getBytes().length == str.length()) {return false;}return true;
}
/*** 根据正则判断字符串中是否包含中文*/
public static boolean isContainChineseByRegex(String str) {Pattern p = Pattern.compile("[\u4e00-\u9fa5]");Matcher m = p.matcher(str);if (m.find()) {return true;}return false;
}

11  为什么 String 在 Java 中是不可变的?

     我最喜欢的Java面试问题,很棘手,但同时也非常有用。一些面试者也常问这个问题,为什么String在Java中是final的。

      字符串在Java中是不可变的,因为String对象缓存在String池中。由于缓存的字符串在多个客户之间共享,因此始终存在风险,其中一个客户的操作会影响所有其他客户。例如,如果一段代码将String“Test”的值更改为“TEST”,则所有其他客户也将看到该值。由于String对象的缓存性能是很重要的一方面,因此通过使String类不可变来避免这种风险。

      同时,String是final的,因此没有人可以通过扩展和覆盖行为来破坏String类的不变性、缓存、散列值的计算等。String类不可变的另一个原因可能是由于HashMap。

     由于把字符串作为HashMap键很受欢迎。对于键值来说,重要的是它们是不可变的,以便用它们检索存储在HashMap中的值对象。

      由于HashMap的工作原理是散列,因此需要具有相同的值才能正常运行。如果在插入后修改了String的内容,可变的String将在插入和检索时生成两个不同的哈希码,可能会丢失Map中的值对象。

      如果你是印度板球迷,你可能能够与我的下一句话联系起来。字符串是Java的VVSLaxman,即非常特殊的类。我还没有看到一个没有使用String编写的Java程序。这就是为什么对String的充分理解对于Java开发人员来说非常重要。

     String作为数据类型,传输对象和中间人角色的重要性和流行性也使这个问题在Java面试中很常见。

     为什么String在Java中是不可变的是Java中最常被问到的字符串访问问题之一,它首先讨论了什么是String,Java中的String如何与C和C++中的String不同,然后转向在Java中什么是不可变对象,不可变对象有什么好处,为什么要使用它们以及应该使用哪些场景。

     这个问题有时也会问:“为什么String在Java中是final的”。在类似的说明中,如果你正在准备Java面试,我建议你看看《Java程序员面试宝典(第4版)》,这是高级和中级Java程序员的优秀资源。它包含来自所有重要Java主题的问题,包括多线程,集合,GC,JVM内部以及Spring和Hibernate框架等。

      正如我所说,这个问题可能有很多可能的答案,而String类的唯一设计者可以放心地回答它。我在JoshuaBloch的EffectiveJava书中期待一些线索,但他也没有提到它。我认为以下几点解释了为什么String类在Java中是不可变的或final的:

     1)想象字符串池没有使字符串不可变,它根本不可能,因为在字符串池的情况下,一个字符串对象/文字,例如“Test”已被许多参考变量引用,因此如果其中任何一个更改了值,其他参数将自动受到影响,即假设

String A="Test";
String B="Test";

      现在字符串B调用"Test".toUpperCase(),将同一个对象改为“TEST”,所以A也是“TEST”,这不是期望的结果。

   下图显示了如何在堆内存和字符串池中创建字符串。

java面试必备--JAVA基础篇(五) 之 HashCode String解读

      2)字符串已被广泛用作许多Java类的参数,例如,为了打开网络连接,你可以将主机名和端口号作为字符串传递,你可以将数据库URL作为字符串传递,以打开数据库连接,你可以通过将文件名作为参数传递给FileI/O类来打开Java中的任何文件。如果String不是不可变的,这将导致严重的安全威胁,我的意思是有人可以访问他有权授权的任何文件,然后可以故意或意外地更改文件名并获得对该文件的访问权限。由于不变性,你无需担心这种威胁。这个原因也说明了,为什么String在Java中是最终的,通过使java.lang.Stringfinal,Java设计者确保没有人覆盖String类的任何行为。

     3)由于String是不可变的,它可以安全地共享许多线程,这对于多线程编程非常重要.并且避免了Java中的同步问题,不变性也使得String实例在Java中是线程安全的,这意味着你不需要从外部同步String操作。关于String的另一个要点是由截取字符串SubString引起的内存泄漏,这不是与线程相关的问题,但也是需要注意的。

     4)为什么String在Java中是不可变的另一个原因是允许String缓存其哈希码,Java中的不可变String缓存其哈希码,并且不会在每次调用String的hashcode方法时重新计算,这使得它在Java中的HashMap中使用的HashMap键非常快。简而言之,因为String是不可变的,所以没有人可以在创建后更改其内容,这保证了String的hashCode在多次调用时是相同的。

     5)String不可变的绝对最重要的原因是它被类加载机制使用,因此具有深刻和基本的安全考虑。如果String是可变的,加载“java.io.Writer”的请求可能已被更改为加载“mil.vogoon.DiskErasingWriter”.安全性和字符串池是使字符串不可变的主要原因。顺便说一句,上面的理由很好回答另一个Java面试问题:“为什么String在Java中是最终的”。要想是不可变的,你必须是最终的,这样你的子类不会破坏不变性。你怎么看?

12 为什么char数组比Java中的String更适合存储密码?

     另一个基于String的棘手Java问题,相信我只有很少的Java程序员可以正确回答这个问题。这是一个真正艰难的核心Java面试问题,并且需要对String的扎实知识才能回答这个问题。

     这是最近在Java面试中向我的一位朋友询问的问题。他正在接受技术主管职位的面试,并且有超过6年的经验。如果你还没有遇到过这种情况,那么字符数组和字符串可以用来存储文本数据,但是选择一个而不是另一个很难。但正如我的朋友所说,任何与String相关的问题都必须对字符串的特殊属性有一些线索,比如不变性,他用它来说服访提问的人。在这里,我们将探讨为什么你应该使用char[]存储密码而不是String的一些原因。

字符串:

    1)由于字符串在Java中是不可变的,如果你将密码存储为纯文本,它将在内存中可用,直到垃圾收集器清除它.并且为了可重用性,会存在String在字符串池中,它很可能会保留在内存中持续很长时间,从而构成安全威胁。

      由于任何有权访问内存转储的人都可以以明文形式找到密码,这是另一个原因,你应该始终使用加密密码而不是纯文本。由于字符串是不可变的,所以不能更改字符串的内容,因为任何更改都会产生新的字符串,而如果你使用char[],你就可以将所有元素设置为空白或零。因此,在字符数组中存储密码可以明显降低窃取密码的安全风险。

     2)Java本身建议使用JPasswordField的getPassword()方法,该方法返回一个char[]和不推荐使用的getTex()方法,该方法以明文形式返回密码,由于安全原因。应遵循Java团队的建议,坚持标准而不是反对它。

      3)使用String时,总是存在在日志文件或控制台中打印纯文本的风险,但如果使用Array,则不会打印数组的内容而是打印其内存位置。虽然不是一个真正的原因,但仍然有道理。

  String strPassword =“Unknown”; char [] charPassword = new char [] {'U','n','k','w','o','n'}; System.out.println(“字符密码:”+ strPassword);System.out.println(“字符密码:”+ charPassword);

输出:

字符串密码:Unknown字符密码:[C @110b053

    我还建议使用散列或加密的密码而不是纯文本,并在验证完成后立即从内存中清除它。因此,在Java中,用字符数组用存储密码比字符串是更好的选择。虽然仅使用char[]还不够,还你需要擦除内容才能更安全

13 String对象中的replace和replaceAll的区别?

   replace方法:支持字符和字符串的替换。

public String replace(char oldChar, char newChar)
public String replace(CharSequence target, CharSequence replacement)

replaceAll方法:基于正则表达式的字符串替换。

public String replaceAll(String regex, String replacement)

测试代码:

String str = "Hello Java. Java is a language.";
System.out.println(str.replace("Java.", "c++"));//打印 Hello c++ Java is a language.
System.out.println(str.replaceAll("Java.", "c++"));//打印 Hello c++ c++is a language.

???????‘

14 GB2312编码的字符串如何转换为ISO-8859-1编码?

String str = "B仔";
String strIso = new String(str.getBytes("GB2312"), "ISO-8859-1");
System.out.println(strIso);

???????15 String s="a"+"b"+"c"+"d";创建了几个对象?

   1个

      Java 编译器对字符串常量直接相加的表达式进行优化,不等到运行期去进行加法运算,在编译时就去掉了加号,直接将其编译成一个这些常量相连的结果。

     所以 "a"+"b"+"c"+"d" 相当于直接定义一个 "abcd" 的字符串。

  相关解决方案