Java中String的变量为啥不是String对象

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java中String的变量为啥不是String对象相关的知识,希望对你有一定的参考价值。

如,String a="SS";那么a是String类的对象吗?
1为什么我去寻求答案的时候,有人说a只是引用变量,不是对象,可是String是个类啊,a为什么不是String类的对象呢?
2为什么a不需要实例化就可以直接使用呢?

比较懒,即网上复制一段给你吧,这个比我自己将要详细很多。
字符串类(java.lang.String)是Java中使用最多的类,也是最为特殊的一个类,很多时候,我们对它既熟悉又陌生。一、从根本上认识java.lang.String类和String池首先,我建议先看看String类的源码实现,这是从本质上认识String类的根本出发点。从中可以看到:1、String类是final的,不可被继承。public final class String。2、String类是的本质是字符数组char[], 并且其值不可改变。PRivate final char value[];然后打开String类的API文档,可以发现:3、String类对象有个特殊的创建的方式,就是直接指定比如String x = "abc","abc"就表示一个字符串对象。而x是"abc"对象的地址,也叫做"abc"对象的引用。4、String对象可以通过“+”串联。串联后会生成新的字符串。也可以通过concat()来串联,这个后面会讲述。6、Java运行时会维护一个String Pool(String池),JavaDoc翻译很模糊“字符串缓冲区”。String池用来存放运行时中产生的各种字符串,并且池中的字符串的内容不重复。而一般对象不存在这个缓冲池,并且创建的对象仅仅存在于方法的堆栈区。5、创建字符串的方式很多,归纳起来有三类:其一,使用new关键字创建字符串,比如String s1 = new String("abc");其二,直接指定。比如String s2 = "abc";其三,使用串联生成新的字符串。比如String s3 = "ab" + "c"; 二、String对象的创建String对象的创建也很讲究,关键是要明白其原理。原理1:当使用任何方式来创建一个字符串对象s时,Java运行时(运行中JVM)会拿着这个X在String池中找是否存在内容相同的字符串对象,如果不存在,则在池中创建一个字符串s,否则,不在池中添加。原理2:Java中,只要使用new关键字来创建对象,则一定会(在堆区或栈区)创建一个新的对象。原理3:使用直接指定或者使用纯字符串串联来创建String对象,则仅仅会检查维护String池中的字符串,池中没有就在池中创建一个,有则罢了!但绝不会在堆栈区再去创建该String对象。原理4:使用包含变量的表达式来创建String对象,则不仅会检查维护String池,而且还会在堆栈区创建一个String对象。另外,String的intern()方法是一个本地方法,定义为public native String intern(); intern()方法的价值在于让开发者能将注意力集中到String池上。当调用 intern 方法时,如果池已经包含一个等于此 String 对象的字符串(该对象由 equals(Object) 方法确定),则返回池中的字符串。否则,将此 String 对象添加到池中,并且返回此 String 对象的引用。三、不可变类不可改变的字符串具有一个很大的优点:编译器可以把字符串设置为共享。 不可变类String有一个重要的优点-它们不会被共享引用。是这样的,JAVA为了提高效率,所以对于String类型进行了特别的处理---为string类型提供了串池 定义一个string类型的变量有两种方式: string name= "tom "; string name =new string( "tom ") 使用第一种方式的时候,就使用了串池, 使用第二中方式的时候,就是一种普通的声明对象的方式 如果你使用了第一种方式,那么当你在声明一个内容也是 "tom "的string时,它将使用串池里原来的那个内存,而不会重新分配内存,也就是说,string saname= "tom ",将会指向同一块内存 另外关于string类型是不可改变的问题: string类型是不可改变的,也就是说,当你想改变一个string对象的时候,比如name= "madding " 那么虚拟机不会改变原来的对象,而是生成一个新的string对象,然后让name去指向它,如果原来的那个 "tom "没有任何对象去引用它,虚拟机的垃圾回收机制将接收它。 据说这样可以提高效率!!!

最后总结下,String是一个很特殊的类,它有一个字符串池。
参考技术A 从java语言使用层面上说a就是一个String对象,这可以通过 a instanceof String来判断
从jvm实现层面上说,真正的对象是"SS",它存放在堆里,a是指向"SS"的地址,是个栈上变量
a不需要实例化直接使用那是a未初始化时是null追问

谢谢你哈,回答得真经典,如果从jvm实现层面上来说,真正的对象是“SS”,那么"SS"是存放在字符串池中的吗?还是存放在堆中的呢?堆和字符串池是在同一个内存中不同的区域吗?

追答

你说的没说,我忘了,"SS"是常量,应该是常量池中,而不是堆中,new String("SS")才会在堆中

本回答被提问者采纳

为啥 java.util.Properties 实现 Map<Object,Object> 而不是 Map<String,String>

【中文标题】为啥 java.util.Properties 实现 Map<Object,Object> 而不是 Map<String,String>【英文标题】:Why does java.util.Properties implement Map<Object,Object> and not Map<String,String>为什么 java.util.Properties 实现 Map<Object,Object> 而不是 Map<String,String> 【发布时间】:2010-10-26 18:55:09 【问题描述】:

java.util.Properties 类用于表示键和值都是字符串的映射。这是因为Properties 对象用于读取.properties 文件,这些文件是文本文件。

那么,为什么在 Java 5 中他们改造这个类来实现 Map&lt;Object,Object&gt; 而不是 Map&lt;String,String&gt;

javadoc 声明:

因为 Properties 继承自 Hashtable,所以 put 和 putAll 方法可以应用于 Properties 对象。强烈建议不要使用它们,因为它们允许调用者插入键或值不是字符串的条目。应该改用 setProperty 方法。如果对包含非字符串键或值的“受损”属性对象调用 store 或 save 方法,则调用将失败。

既然键和值都应该是字符串,那么为什么不使用适当的泛型类型来静态地强制执行呢?

我猜想使 Properties 实现 Map&lt;String,String&gt; 不会完全向后兼容为 Java 5 之前编写的代码。如果您有将非字符串粘贴到 Properties 对象中的旧代码,那么该代码将不再编译Java 5。但是……这不是一件好事吗?泛型的重点不是在编译时捕获此类类型错误吗?

【问题讨论】:

【参考方案1】:

因为他们在 Java 早期就匆匆忙忙地做了,并没有意识到四个版本之后会有什么影响。

从一开始就应该将泛型作为 Java 设计的一部分,但由于过于复杂且在当时没有必要,该功能被放弃了。结果,标准库中的许多代码都是在假设非泛型集合的情况下编写的。 Martin Odersky 的原型语言“Pizza”展示了它们如何在保持近乎完美的向后兼容性的同时与 Java 代码和字节码兼容。原型导致了 Java 5,其中集合类被改造为泛型,以允许旧代码继续工作。

不幸的是,如果他们追溯使 Properties 继承自 Map&lt;String, String&gt;,那么以下先前有效的代码将停止工作:

Map<Object, Object> x = new Properties()
x.put("flag", true)

为什么有人会这样做,我无法理解,但 Sun 对 Java 向后兼容性的承诺已经超越了英雄主义,变得毫无意义。

现在大多数受过教育的观察者都认为Properties 根本不应该继承自Map。相反,它应该环绕 Map,只显示 Map 中有意义的那些功能。

自重新发明 Java 以来,Martin Odersky 继续创建新的 Scala 语言,该语言更简洁,继承的错误更少,并在许多领域开辟了新天地。如果你觉得 Java 的琐事很烦人,那就看看吧。

【讨论】:

实际上 Map x = new Properties() 可以两种方式工作。它的人把 properties.put("flag", Boolean.TRUE);我见过人们将各种数据放入一个 Properties 对象中,但从来没有一个非字符串键。 ;) 等一下! Map x = new Properties() 在 Java 5 之前是不合法的, 语法是在 Java 5 中引入的。所以“以前有效的代码”的说法是不正确的。 再读一遍我的句子。我说的是他们无法修复它,因为这样做会破坏人们的代码。【参考方案2】:

原本打算Properties 确实会扩展Hashtable&lt;String,String&gt;。不幸的是,桥接方法的实现引起了问题。 Properties 以这种方式定义会导致 javac 生成合成方法。 Properties 应该定义一个返回 String 但需要覆盖返回 Object 的方法的 get 方法。所以增加了合成桥接法。

假设你有一门课是在糟糕的 1.4 天写的。你已经覆盖了Properties 中的一些方法。但是你没有做的是覆盖新方法。这会导致意外行为。为了避免这些桥接方法,Properties 扩展了Hashtable&lt;Object,Object&gt;。同样,Iterable 不会返回(只读)SimpleIterable,因为这会为Collection 实现添加方法。

【讨论】:

【参考方案3】:

从属性创建地图的单行(无警告的两行):

@SuppressWarnings( "unchecked", "rawtypes" )
Map<String, String> sysProps = new HashMap(System.getProperties());

【讨论】:

不回答问题。但确实首先回答了导致这个问题的原因。直奔主题。 这里需要“rawtypes”吗? (至少在我的 IntelliJ IDEA 中,“未选中”足以抑制任何警告。)【参考方案4】:

向后兼容性。

【讨论】:

【参考方案5】:

原因:Liskov substitution principle 和向后兼容性。 Properties 扩展 Hashtable,因此必须接受 Hashtable 将接受的所有消息 - 这意味着接受 put(Object, Object)。而且它必须扩展普通的Hashtable 而不是Hashtable&lt;String, String&gt;,因为泛型是通过type erasure 以向下兼容的方式实现的,所以一旦编译器完成了它的工作,就没有泛型了。

【讨论】:

我认为 Liskov 替换原则与此无关。当你继承一个类时,Java 很高兴地让你细化泛型类型。这就是存在 语法的原因。

以上是关于Java中String的变量为啥不是String对象的主要内容,如果未能解决你的问题,请参考以下文章

为啥 java 中只有 String[] args 而不是 Object[] args?

java类名直接访问静态变量为啥不通过,提示Demo3.java:11: 不是语句

java中的String类型的对象为啥可以自动转换成Object类型的?而Object却要强制转换成String类型的

为啥 java.util.Properties 实现 Map<Object,Object> 而不是 Map<String,String>

final类 引用变量不能变 String也是final的 为啥string的引用变量能变

c++程序 我为啥不能将string类的变量赋给char数组,我按书上打的?