JAVA中static与String基础应用有何特点?

2026-05-25 20:331阅读0评论SEO问题
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计4543个文字,预计阅读时间需要19分钟。

JAVA中static与String基础应用有何特点?

目录前言主题static 关键字简介使用String===与 equals() 方法常量与非常量intern() 方法JAVA 源码native 源码使用总结前言跟随同学们在讨论JAVA时,我们经常遇到关于static和String的用法。本文将探讨这些主题,并提供一些实用的信息。

主题本文将深入探讨JAVA中的static关键字和String类的使用。

static 关键字static关键字用于创建类级别的变量和方法,它们不依赖于对象实例。

简介static变量和方法在类加载时初始化,且在所有对象实例中共享。

使用static变量和方法可以在不创建对象实例的情况下直接访问。

String===与 equals() 方法String类的比较方法包括==和equals()。

常量与非常量String常量池是存储所有字符串字面量的地方。

intern() 方法intern()方法用于在字符串常量池中查找或创建字符串。

JAVA 源码JAVA源码中关于static和String的实现。

native 源码native方法通常是用C/C++编写的,它们在JAVA虚拟机外部执行。

使用在实际编程中,了解static和String的用法对于编写高效和可维护的代码至关重要。

总结通过本文,我们了解了static关键字和String类的使用,这对于JAVA开发者来说是非常有用的。希望本文能帮助你在实际项目中更好地应用这些概念。

目录
  • 前言
  • 原题
  • static
    • 简介
    • 使用
  • String
    • == 与 equals()
    • 常量与非常量
    • intern()
      • JAVA 源码
      • native 源码
      • 使用
  • 总结

    前言

    跟同学在讨论 JAVA 期末试题时,对于一些 static 和 String 在对象中的使用方法,若有所思,特此记录一下,也祝没有对象的友友可以自己new一个出来!

    那我们先来看一看试卷里的原题;

    原题

    主要就是两个类MyClass.javaTestMyClass.java,填代码的部分就直接跳过了,然后就是输出结果,看看你是否也能全部正确,

    两个类的具体代码如下:

    MyClass.java

    public class MyClass { private int count; String info; public static String message = "Good"; public MyClass increase() { count++; return this; } private MyClass() { this.count=0; this.info = "GoodLuck"; } public int getCount() { return count; } public static MyClass getInstance() { return new MyClass(); } }

    TestMyClass.java

    public class TestMyClass { public static void main(String[] args) { MyClass mc1 = MyClass.getInstance(); MyClass mc2 = MyClass.getInstance(); mc1.message = "Great"; mc2.message = "Excellent"; MyClass.message = "Nice"; System.out.println(mc1.message+":"+mc2.message+":"+MyClass.message); System.out.println(mc1.info == mc2.info); mc2.info = new String("GoodLuck"); System.out.println(mc1.info == mc2.info); System.out.println(mc1.info.equals(mc2.info)); System.out.println(mc1.increase().increase().getCount()); } }

    运行结果:

    Nice:Nice:Nice
    true
    false
    true
    2

    如果你全部答对了,那么恭喜你,基础很不错哟;答错的小伙伴也不要丧气,接下来听我娓娓道来,扎实基础;

    static

    工欲善其事必先利其器,在开始解析之前,我们先回顾一下一些关于 static 的知识;

    简介

    static 表示 “全局” 或者 “静态” 的意思,用来修饰成员变量和成员方法,也可以形成静态 static 代码块,但是 Java 语言中没有全局变量的概念;

    被 static 修饰的成员变量和成员方法独立于该类的任何对象,也就是说,它不依赖类特定的实例,被类的所有实例共享;

    只要这个类被加载,Java 虚拟机就能根据类名在运行时数据区的方法区内定找到他们,因此,static 对象可以在它的任何对象创建之前访问,无需引用任何对象;

    用 public 修饰的 static 成员变量和成员方法本质是全局变量和全局方法,当声明它类的对象时,不生成 static 变量的副本,而是类的所有实例共享同一个 static 变量;

    static 变量前可以有 private 修饰,表示这个变量可以在类的静态代码块中,或者类的其他静态成员方法中使用,但是不能在其他类中通过类名来直接引用,这一点很重要;

    实际上你需要搞明白,private 是访问权限限定,static 表示不要实例化就可以使用,这样就容易理解多了,static 前面加上其它访问权限关键字的效果也以此类推。

    static 修饰的成员变量和成员方法习惯上称为静态变量和静态方法,可以直接通过类名来访问,访问语法为:

    类名.静态方法名(参数列表…)

    使用

    回顾了 static 相关知识之后,我们来看一下题目中的使用吧;

    // MyClass.java public static String message = "Good"; // TestMyClass.java MyClass mc1 = MyClass.getInstance(); MyClass mc2 = MyClass.getInstance(); mc1.message = "Great"; mc2.message = "Excellent"; MyClass.message = "Nice"; System.out.println(mc1.message+":"+mc2.message+":"+MyClass.message);

    先是用 static 修饰了成员变量message,然后通过下断点调试可以获知,两个对象mcl1mcl2被分配到了两个不同的地址;

    在往下调试时,发现mc1.messagemc2.messageMyClass.message三个成员变量的值是一样的,且都从Great → Excellent → Nice,这就是刚才所回顾的,被 static 修饰的成员变量成了共享变量,被类的所有实例共享;

    接下来我们再做个试验验证一下:

    //修改前 private int count; //修改后 private static int count;

    可以发现,我们只是对mcl1对象进行了操作,但是mcl2的成员变量count也跟着改变了,这就是因为在MyClass类中,成员变量count被 static 修饰,已经成了该类的共享变量了,但凡是该类的对象,都访问的是同一个count变量;

    当然也可以通过身份码进行验证:

    System.out.println("mcl1: " + System.identityHashCode(mc1)); System.out.println("mcl2: " + System.identityHashCode(mc2)); System.out.println("mcl1_count: " + System.identityHashCode(mc1.getCount())); System.out.println("mcl2_count: " + System.identityHashCode(mc2.getCount()));

    mcl1: 940553268 mcl2: 1720435669 mcl1_count: 1020923989 mcl2_count: 1020923989

    因此System.out.println(mc1.message+":"+mc2.message+":"+MyClass.message);输出的是Nice:Nice:Nice

    接下来讲一些关于 String 的小知识;

    String

    关于 String 的话,这里用到啥聊啥,就不全面的进行了;

    == 与 equals()

    先来讲讲关于 String 的比较,一般常见的比较有两种,即==equals()

    其中,==比较的是两个字符串的地址是否为相等(同一个地址),equals()方法比较的是两个字符串对象的内容是否相同(当然,若两个字符串引用同一个地址,使用equals()比较也返回true);

    这里就不得不提第二个知识点了,String 常量与非常量的区别;

    常量与非常量

    那什么是常量,什么是非常量呢,简单了解就是,String name = "sid10t."这个是常量,属于是对name进行赋值,直接存储在常量池中,而String name = new String("sid10t.")这个就是非常量,因为重新创建了一个对象,这会将字符串sid10t.存储在常量池中,然后在 Heap 中创建对象指向name

    那这里为什么要提这个呢?当然是因为他们有较大的区别;

    在 Java语言规范(JavaSE 1.8版本)章节3.10.5 中有做规范,所有的 Java 语言编译、运行时环境实现都必须依据此规范来实现,里面有这么一句话:

    Moreover, a string literal always refers to the same instance of class String. This is because string literals - or, more generally, strings that are the values of constant expressions (§15.28) - are "interned" so as to share unique instances, using the method String.intern.

    大致意思就是凡是内容一样的字符串常数,都要引用同一个字符串对象,换句话说就是内存地址相同;

    因为其值为常量的字符串,都会通过String.intern()函数被限定为共享同一个对象;

    稍后会解析intern()函数,也可以自行参考说明String (Java Platform SE 8 );

    回到正题,看一下语言规范里的这段代码:

    package testPackage; class Test { public static void main(String[] args) { String hello = "Hello", lo = "lo"; System.out.print((hello == "Hello") + " "); System.out.print((Other.hello == hello) + " "); System.out.print((other.Other.hello == hello) + " "); System.out.print((hello == ("Hel"+"lo")) + " "); System.out.print((hello == ("Hel"+lo)) + " "); System.out.println(hello == ("Hel"+lo).intern()); } } class Other { static String hello = "Hello"; }

    以及另一个包中的类:

    package other; public class Other { public static String hello = "Hello"; }

    运行结果:

    true true true true false true

    结论:

    • 同一个包中同一个类中的字符串表示对同一个 String 对象的引用;
    • 同一个包中不同类中的字符串表示对同一个 String 对象的引用;
    • 不同包中不同类中的字符串同样表示对同一 String 对象的引用;
    • 由常量表达式计算的字符串在编译时计算,然后将其视为文字;
    • 在运行时通过连接计算的字符串是新创建的,因此是不同的;
    • 显式地嵌入一个计算出来的字符串的结果与任何现有的具有相同内容的字面值字符串的结果相同;

    如果对结论的理解不是很深刻的话,那就看看接下来的解释:

    System.out.print((hello == "Hello") + " "); System.out.print((Other.hello == hello) + " "); System.out.print((other.Other.hello == hello) + " ");

    “Hello” 和 “lo” 是字符串常量,在编译期就被确定了,先检查字符串常量池中是否含有 “Hello” 和 “lo”,如果没有则添加 “Hello” 和 “lo” 到字符串常量池中,并且直接指向它们,所以hellolo分别直接指向字符串常量池的 “Hello” 和 “lo”,也就是hellolo指向的地址分别是常量池中的 “Hello” 和 “lo” ,因此第一个输出中hello实际就是 “Hello”,所以"Hello" == "Hello"true,前三个输出都是同理的;

    System.out.print((hello == ("Hel"+"lo")) + " ");

    "Hel" 和 "lo" 都是字符串常量,当一个字符串多个字符串常量连接而成时,它自己肯定也是字符串常量,在编译器会被编译器优化成 "Hello",因为 "Hello" 在常量池中了,因此输出为true

    System.out.print((hello == ("Hel"+lo)) + " "); System.out.println(hello == ("Hel"+lo).intern());

    JVM 对于字符串引用,由于在字符串的+连接中,有字符串引用存在,而引用的值在程序编译期是无法确定的,即"Hel"+lo,所以在不执行intern()方法的前提下,"Hel"+lo不会存到常量池中,但 "Hel" 会被存到常量池中去,所以输出一个为true,一个为false

    JAVA中static与String基础应用有何特点?

    intern()

    String.intern()是一个 Native 方法,它的作用是如果字符串常量池已经包含一个等于此 String 对象的字符串,则返回字符串常量池中这个字符串的引用, 否则将当前 String 对象的引用地址(堆中)添加到字符串常量池中并返回。

    JAVA 源码

    /* Returns a canonical representation for the string object. A pool of strings, initially empty, is maintained privately by the class String. When the intern method is invoked, if the pool already contains a string equal to this String object as determined by the equals(Object) method, then the string from the pool is returned. Otherwise, this String object is added to the pool and a reference to this String object is returned. It follows that for any two strings s and t, s.intern() == t.intern() is true if and only if s.equals(t) is true. All literal strings and string-valued constant expressions are interned. String literals are defined in section 3.10.5 of the The Java Language Specification. Returns: a string that has the same contents as this string, but is guaranteed to be from a pool of unique strings. */ public native String intern();

    native 源码

    String.c

    Java_java_lang_String_intern(JNIEnv *env, jobject this) { return JVM_InternString(env, this); }

    jvm.h

    /* * java.lang.String */ JNIEXPORT jstring JNICALL JVM_InternString(JNIEnv *env, jstring str);

    jvm.cpp

    JVM_ENTRY(jstring, JVM_InternString(JNIEnv *env, jstring str)) JVMWrapper("JVM_InternString"); JvmtiVMObjectAllocEventCollector oam; if (str == NULL) return NULL; oop string = JNIHandles::resolve_non_null(str); oop result = StringTable::intern(string, CHECK_NULL); return (jstring) JNIHandles::make_local(env, result); JVM_END

    symbolTable.cpp

    oop StringTable::intern(Handle string_or_null, jchar* name, int len, TRAPS) { unsigned int hashValue = java_lang_String::hash_string(name, len); int index = the_table()->hash_to_index(hashValue); oop string = the_table()->lookup(index, name, len, hashValue); // Found if (string != NULL) return string; // Otherwise, add to symbol to table return the_table()->basic_add(index, string_or_null, name, len, hashValue, CHECK_NULL); } oop StringTable::lookup(int index, jchar* name, int len, unsigned int hash) { for (HashtableEntry<oop>* l = bucket(index); l != NULL; l = l->next()) { if (l->hash() == hash) { if (java_lang_String::equals(l->literal(), name, len)) { return l->literal(); } } } return NULL; }

    它的大体实现结构就是:JAVA 使用 jni 调用 c++ 实现的 StringTable 的intern方法,StringTable 的intern方法跟 Java 中的 HashMap 的实现是差不多的,只是不能自动扩容,默认大小是1009。

    要注意的是,String 的 String Pool 是一个固定大小的 Hashtable,默认值大小长度是1009,如果放进 String Pool 的 String 非常多,就会造成 Hash 冲突严重,从而导致链表会很长,而链表长了后直接会造成的影响就是当调用String.intern时性能会大幅下降(因为要一个一个找)。

    在 JDK6 中StringTable是固定的,就是1009的长度,所以如果常量池中的字符串过多就会导致效率下降很快。

    在 JDK7 中,StringTable 的长度可以通过一个参数指定:-XX:StringTableSize=99991

    使用

    在 JDK1.7 之前的版本,调用这个方法的时候,会去常量池中查看是否已经存在这个常量了,如果已经存在,那么直接返回这个常量在常量池中的地址值,如果不存在,则在常量池中创建一个,并返回其地址值。

    但是在 JDK1.7 以及之后的版本中,常量池从 perm 区搬到了 heap 区。intern()检测到这个常量在常量池中不存在的时候,不会直接在常量池中创建该对象了,而是将堆中的这个对象的引用直接存到常量池中,减少内存开销。

    来看这段代码:

    public static void main(String[] args) { // part1 String s1 = new String("sid10t."); s1.intern(); String s2 = "sid10t."; System.out.println(s1 == s2); // part2 String s3 = new String("Hello ") + new String("World!"); s3.intern(); String s4 = "Hello World!"; System.out.println(s3 == s4); }

    在 JDK7 之前两个都是false,在 JDK7 之后输出分别是falsetrue

    接下来根据 JDK7 进行分析,

    先来看 part1 部分:

    String s1 = new String("sid10t.");这行代码生成了两个最终对象:一个是常量池中的字符串常量sid10t.,另一个是在堆中的 s1 引用指向的对象;

    然后是第二行s1.intern();,返回常量池中的字符串常量sid10t.,因为常量池中已经存在了该常量,所以这里就直接返回即可,因此,在 part1 的此情此景中,这句话可写可不写,对输出结果没有任何影响;

    所以最后输出的肯定是false,一个地址在堆中,一个在常量池里;

    接下来看看 part2 部分:

    String s3 = new String("Hello ") + new String("World!");这行代码生成了三个最终对象:两个分别是常量池中的对象HelloWorld!,还有一个在堆中的 s3 引用指向的对象;

    然后是第二行s3.intern();,由于现在的常量池中不存在字符串常量Hello World!,因此它会直接存储 s3 在堆中的引用地址,而不是拷贝一份;

    这时候,String s4 = "Hello World!";,常量池中已经有了Hello World!常量,也就是 s3 的引用地址,因此 s4 的值就是 s3 的引用地址,所以输出的是true

    根据上述分析,我们将 part2 的代码略作调整,如下:

    String s3 = new String("Hello ") + new String("World!"); // s3.intern(); String s4 = "Hello World!"; s3.intern(); System.out.println(s3 == s4);

    输出的就是false了,但如果是s3.intern() == s4,则输出的就是true

    想必你应该理解了!

    总结

    到此这篇关于JAVA对象中使用static和String基础探究的文章就介绍到这了,更多相关JAVAstatic和String内容请搜索自由互联以前的文章或继续浏览下面的相关文章希望大家以后多多支持自由互联!

    本文共计4543个文字,预计阅读时间需要19分钟。

    JAVA中static与String基础应用有何特点?

    目录前言主题static 关键字简介使用String===与 equals() 方法常量与非常量intern() 方法JAVA 源码native 源码使用总结前言跟随同学们在讨论JAVA时,我们经常遇到关于static和String的用法。本文将探讨这些主题,并提供一些实用的信息。

    主题本文将深入探讨JAVA中的static关键字和String类的使用。

    static 关键字static关键字用于创建类级别的变量和方法,它们不依赖于对象实例。

    简介static变量和方法在类加载时初始化,且在所有对象实例中共享。

    使用static变量和方法可以在不创建对象实例的情况下直接访问。

    String===与 equals() 方法String类的比较方法包括==和equals()。

    常量与非常量String常量池是存储所有字符串字面量的地方。

    intern() 方法intern()方法用于在字符串常量池中查找或创建字符串。

    JAVA 源码JAVA源码中关于static和String的实现。

    native 源码native方法通常是用C/C++编写的,它们在JAVA虚拟机外部执行。

    使用在实际编程中,了解static和String的用法对于编写高效和可维护的代码至关重要。

    总结通过本文,我们了解了static关键字和String类的使用,这对于JAVA开发者来说是非常有用的。希望本文能帮助你在实际项目中更好地应用这些概念。

    目录
    • 前言
    • 原题
    • static
      • 简介
      • 使用
    • String
      • == 与 equals()
      • 常量与非常量
      • intern()
        • JAVA 源码
        • native 源码
        • 使用
    • 总结

      前言

      跟同学在讨论 JAVA 期末试题时,对于一些 static 和 String 在对象中的使用方法,若有所思,特此记录一下,也祝没有对象的友友可以自己new一个出来!

      那我们先来看一看试卷里的原题;

      原题

      主要就是两个类MyClass.javaTestMyClass.java,填代码的部分就直接跳过了,然后就是输出结果,看看你是否也能全部正确,

      两个类的具体代码如下:

      MyClass.java

      public class MyClass { private int count; String info; public static String message = "Good"; public MyClass increase() { count++; return this; } private MyClass() { this.count=0; this.info = "GoodLuck"; } public int getCount() { return count; } public static MyClass getInstance() { return new MyClass(); } }

      TestMyClass.java

      public class TestMyClass { public static void main(String[] args) { MyClass mc1 = MyClass.getInstance(); MyClass mc2 = MyClass.getInstance(); mc1.message = "Great"; mc2.message = "Excellent"; MyClass.message = "Nice"; System.out.println(mc1.message+":"+mc2.message+":"+MyClass.message); System.out.println(mc1.info == mc2.info); mc2.info = new String("GoodLuck"); System.out.println(mc1.info == mc2.info); System.out.println(mc1.info.equals(mc2.info)); System.out.println(mc1.increase().increase().getCount()); } }

      运行结果:

      Nice:Nice:Nice
      true
      false
      true
      2

      如果你全部答对了,那么恭喜你,基础很不错哟;答错的小伙伴也不要丧气,接下来听我娓娓道来,扎实基础;

      static

      工欲善其事必先利其器,在开始解析之前,我们先回顾一下一些关于 static 的知识;

      简介

      static 表示 “全局” 或者 “静态” 的意思,用来修饰成员变量和成员方法,也可以形成静态 static 代码块,但是 Java 语言中没有全局变量的概念;

      被 static 修饰的成员变量和成员方法独立于该类的任何对象,也就是说,它不依赖类特定的实例,被类的所有实例共享;

      只要这个类被加载,Java 虚拟机就能根据类名在运行时数据区的方法区内定找到他们,因此,static 对象可以在它的任何对象创建之前访问,无需引用任何对象;

      用 public 修饰的 static 成员变量和成员方法本质是全局变量和全局方法,当声明它类的对象时,不生成 static 变量的副本,而是类的所有实例共享同一个 static 变量;

      static 变量前可以有 private 修饰,表示这个变量可以在类的静态代码块中,或者类的其他静态成员方法中使用,但是不能在其他类中通过类名来直接引用,这一点很重要;

      实际上你需要搞明白,private 是访问权限限定,static 表示不要实例化就可以使用,这样就容易理解多了,static 前面加上其它访问权限关键字的效果也以此类推。

      static 修饰的成员变量和成员方法习惯上称为静态变量和静态方法,可以直接通过类名来访问,访问语法为:

      类名.静态方法名(参数列表…)

      使用

      回顾了 static 相关知识之后,我们来看一下题目中的使用吧;

      // MyClass.java public static String message = "Good"; // TestMyClass.java MyClass mc1 = MyClass.getInstance(); MyClass mc2 = MyClass.getInstance(); mc1.message = "Great"; mc2.message = "Excellent"; MyClass.message = "Nice"; System.out.println(mc1.message+":"+mc2.message+":"+MyClass.message);

      先是用 static 修饰了成员变量message,然后通过下断点调试可以获知,两个对象mcl1mcl2被分配到了两个不同的地址;

      在往下调试时,发现mc1.messagemc2.messageMyClass.message三个成员变量的值是一样的,且都从Great → Excellent → Nice,这就是刚才所回顾的,被 static 修饰的成员变量成了共享变量,被类的所有实例共享;

      接下来我们再做个试验验证一下:

      //修改前 private int count; //修改后 private static int count;

      可以发现,我们只是对mcl1对象进行了操作,但是mcl2的成员变量count也跟着改变了,这就是因为在MyClass类中,成员变量count被 static 修饰,已经成了该类的共享变量了,但凡是该类的对象,都访问的是同一个count变量;

      当然也可以通过身份码进行验证:

      System.out.println("mcl1: " + System.identityHashCode(mc1)); System.out.println("mcl2: " + System.identityHashCode(mc2)); System.out.println("mcl1_count: " + System.identityHashCode(mc1.getCount())); System.out.println("mcl2_count: " + System.identityHashCode(mc2.getCount()));

      mcl1: 940553268 mcl2: 1720435669 mcl1_count: 1020923989 mcl2_count: 1020923989

      因此System.out.println(mc1.message+":"+mc2.message+":"+MyClass.message);输出的是Nice:Nice:Nice

      接下来讲一些关于 String 的小知识;

      String

      关于 String 的话,这里用到啥聊啥,就不全面的进行了;

      == 与 equals()

      先来讲讲关于 String 的比较,一般常见的比较有两种,即==equals()

      其中,==比较的是两个字符串的地址是否为相等(同一个地址),equals()方法比较的是两个字符串对象的内容是否相同(当然,若两个字符串引用同一个地址,使用equals()比较也返回true);

      这里就不得不提第二个知识点了,String 常量与非常量的区别;

      常量与非常量

      那什么是常量,什么是非常量呢,简单了解就是,String name = "sid10t."这个是常量,属于是对name进行赋值,直接存储在常量池中,而String name = new String("sid10t.")这个就是非常量,因为重新创建了一个对象,这会将字符串sid10t.存储在常量池中,然后在 Heap 中创建对象指向name

      那这里为什么要提这个呢?当然是因为他们有较大的区别;

      在 Java语言规范(JavaSE 1.8版本)章节3.10.5 中有做规范,所有的 Java 语言编译、运行时环境实现都必须依据此规范来实现,里面有这么一句话:

      Moreover, a string literal always refers to the same instance of class String. This is because string literals - or, more generally, strings that are the values of constant expressions (§15.28) - are "interned" so as to share unique instances, using the method String.intern.

      大致意思就是凡是内容一样的字符串常数,都要引用同一个字符串对象,换句话说就是内存地址相同;

      因为其值为常量的字符串,都会通过String.intern()函数被限定为共享同一个对象;

      稍后会解析intern()函数,也可以自行参考说明String (Java Platform SE 8 );

      回到正题,看一下语言规范里的这段代码:

      package testPackage; class Test { public static void main(String[] args) { String hello = "Hello", lo = "lo"; System.out.print((hello == "Hello") + " "); System.out.print((Other.hello == hello) + " "); System.out.print((other.Other.hello == hello) + " "); System.out.print((hello == ("Hel"+"lo")) + " "); System.out.print((hello == ("Hel"+lo)) + " "); System.out.println(hello == ("Hel"+lo).intern()); } } class Other { static String hello = "Hello"; }

      以及另一个包中的类:

      package other; public class Other { public static String hello = "Hello"; }

      运行结果:

      true true true true false true

      结论:

      • 同一个包中同一个类中的字符串表示对同一个 String 对象的引用;
      • 同一个包中不同类中的字符串表示对同一个 String 对象的引用;
      • 不同包中不同类中的字符串同样表示对同一 String 对象的引用;
      • 由常量表达式计算的字符串在编译时计算,然后将其视为文字;
      • 在运行时通过连接计算的字符串是新创建的,因此是不同的;
      • 显式地嵌入一个计算出来的字符串的结果与任何现有的具有相同内容的字面值字符串的结果相同;

      如果对结论的理解不是很深刻的话,那就看看接下来的解释:

      System.out.print((hello == "Hello") + " "); System.out.print((Other.hello == hello) + " "); System.out.print((other.Other.hello == hello) + " ");

      “Hello” 和 “lo” 是字符串常量,在编译期就被确定了,先检查字符串常量池中是否含有 “Hello” 和 “lo”,如果没有则添加 “Hello” 和 “lo” 到字符串常量池中,并且直接指向它们,所以hellolo分别直接指向字符串常量池的 “Hello” 和 “lo”,也就是hellolo指向的地址分别是常量池中的 “Hello” 和 “lo” ,因此第一个输出中hello实际就是 “Hello”,所以"Hello" == "Hello"true,前三个输出都是同理的;

      System.out.print((hello == ("Hel"+"lo")) + " ");

      "Hel" 和 "lo" 都是字符串常量,当一个字符串多个字符串常量连接而成时,它自己肯定也是字符串常量,在编译器会被编译器优化成 "Hello",因为 "Hello" 在常量池中了,因此输出为true

      System.out.print((hello == ("Hel"+lo)) + " "); System.out.println(hello == ("Hel"+lo).intern());

      JVM 对于字符串引用,由于在字符串的+连接中,有字符串引用存在,而引用的值在程序编译期是无法确定的,即"Hel"+lo,所以在不执行intern()方法的前提下,"Hel"+lo不会存到常量池中,但 "Hel" 会被存到常量池中去,所以输出一个为true,一个为false

      JAVA中static与String基础应用有何特点?

      intern()

      String.intern()是一个 Native 方法,它的作用是如果字符串常量池已经包含一个等于此 String 对象的字符串,则返回字符串常量池中这个字符串的引用, 否则将当前 String 对象的引用地址(堆中)添加到字符串常量池中并返回。

      JAVA 源码

      /* Returns a canonical representation for the string object. A pool of strings, initially empty, is maintained privately by the class String. When the intern method is invoked, if the pool already contains a string equal to this String object as determined by the equals(Object) method, then the string from the pool is returned. Otherwise, this String object is added to the pool and a reference to this String object is returned. It follows that for any two strings s and t, s.intern() == t.intern() is true if and only if s.equals(t) is true. All literal strings and string-valued constant expressions are interned. String literals are defined in section 3.10.5 of the The Java Language Specification. Returns: a string that has the same contents as this string, but is guaranteed to be from a pool of unique strings. */ public native String intern();

      native 源码

      String.c

      Java_java_lang_String_intern(JNIEnv *env, jobject this) { return JVM_InternString(env, this); }

      jvm.h

      /* * java.lang.String */ JNIEXPORT jstring JNICALL JVM_InternString(JNIEnv *env, jstring str);

      jvm.cpp

      JVM_ENTRY(jstring, JVM_InternString(JNIEnv *env, jstring str)) JVMWrapper("JVM_InternString"); JvmtiVMObjectAllocEventCollector oam; if (str == NULL) return NULL; oop string = JNIHandles::resolve_non_null(str); oop result = StringTable::intern(string, CHECK_NULL); return (jstring) JNIHandles::make_local(env, result); JVM_END

      symbolTable.cpp

      oop StringTable::intern(Handle string_or_null, jchar* name, int len, TRAPS) { unsigned int hashValue = java_lang_String::hash_string(name, len); int index = the_table()->hash_to_index(hashValue); oop string = the_table()->lookup(index, name, len, hashValue); // Found if (string != NULL) return string; // Otherwise, add to symbol to table return the_table()->basic_add(index, string_or_null, name, len, hashValue, CHECK_NULL); } oop StringTable::lookup(int index, jchar* name, int len, unsigned int hash) { for (HashtableEntry<oop>* l = bucket(index); l != NULL; l = l->next()) { if (l->hash() == hash) { if (java_lang_String::equals(l->literal(), name, len)) { return l->literal(); } } } return NULL; }

      它的大体实现结构就是:JAVA 使用 jni 调用 c++ 实现的 StringTable 的intern方法,StringTable 的intern方法跟 Java 中的 HashMap 的实现是差不多的,只是不能自动扩容,默认大小是1009。

      要注意的是,String 的 String Pool 是一个固定大小的 Hashtable,默认值大小长度是1009,如果放进 String Pool 的 String 非常多,就会造成 Hash 冲突严重,从而导致链表会很长,而链表长了后直接会造成的影响就是当调用String.intern时性能会大幅下降(因为要一个一个找)。

      在 JDK6 中StringTable是固定的,就是1009的长度,所以如果常量池中的字符串过多就会导致效率下降很快。

      在 JDK7 中,StringTable 的长度可以通过一个参数指定:-XX:StringTableSize=99991

      使用

      在 JDK1.7 之前的版本,调用这个方法的时候,会去常量池中查看是否已经存在这个常量了,如果已经存在,那么直接返回这个常量在常量池中的地址值,如果不存在,则在常量池中创建一个,并返回其地址值。

      但是在 JDK1.7 以及之后的版本中,常量池从 perm 区搬到了 heap 区。intern()检测到这个常量在常量池中不存在的时候,不会直接在常量池中创建该对象了,而是将堆中的这个对象的引用直接存到常量池中,减少内存开销。

      来看这段代码:

      public static void main(String[] args) { // part1 String s1 = new String("sid10t."); s1.intern(); String s2 = "sid10t."; System.out.println(s1 == s2); // part2 String s3 = new String("Hello ") + new String("World!"); s3.intern(); String s4 = "Hello World!"; System.out.println(s3 == s4); }

      在 JDK7 之前两个都是false,在 JDK7 之后输出分别是falsetrue

      接下来根据 JDK7 进行分析,

      先来看 part1 部分:

      String s1 = new String("sid10t.");这行代码生成了两个最终对象:一个是常量池中的字符串常量sid10t.,另一个是在堆中的 s1 引用指向的对象;

      然后是第二行s1.intern();,返回常量池中的字符串常量sid10t.,因为常量池中已经存在了该常量,所以这里就直接返回即可,因此,在 part1 的此情此景中,这句话可写可不写,对输出结果没有任何影响;

      所以最后输出的肯定是false,一个地址在堆中,一个在常量池里;

      接下来看看 part2 部分:

      String s3 = new String("Hello ") + new String("World!");这行代码生成了三个最终对象:两个分别是常量池中的对象HelloWorld!,还有一个在堆中的 s3 引用指向的对象;

      然后是第二行s3.intern();,由于现在的常量池中不存在字符串常量Hello World!,因此它会直接存储 s3 在堆中的引用地址,而不是拷贝一份;

      这时候,String s4 = "Hello World!";,常量池中已经有了Hello World!常量,也就是 s3 的引用地址,因此 s4 的值就是 s3 的引用地址,所以输出的是true

      根据上述分析,我们将 part2 的代码略作调整,如下:

      String s3 = new String("Hello ") + new String("World!"); // s3.intern(); String s4 = "Hello World!"; s3.intern(); System.out.println(s3 == s4);

      输出的就是false了,但如果是s3.intern() == s4,则输出的就是true

      想必你应该理解了!

      总结

      到此这篇关于JAVA对象中使用static和String基础探究的文章就介绍到这了,更多相关JAVAstatic和String内容请搜索自由互联以前的文章或继续浏览下面的相关文章希望大家以后多多支持自由互联!