当前位置: 首页 > 编程笔记 >

Java String创建对象实例解析

甘明朗
2023-03-14
本文向大家介绍Java String创建对象实例解析,包括了Java String创建对象实例解析的使用技巧和注意事项,需要的朋友参考一下

本文研究的主要是Java String创建对象的问题,具体介绍如下。

首先我们要明白两个概念,引用变量和对象,对象一般通过new在堆中创建,String只是一个引用变量。

所有的字符串都是String对象,由于字符串常量的大量使用,java中为了节省时间,在编译阶段,会把所有字符串常量放在字符串常量池中,字符串常量池的一个好处就是可以把相同的字符串合并,占用一个空间。

虽然在Java中无法直接获取变量的地址,但是可以用==判断一下两个引用变量是否指向了一个地址即一个对象。

栈内存 堆内存
基础类型,对象引用( 堆内存地址 ) 由new 创建的对象和数组
存取速度快 相对于栈内存较慢
数据大小在声明周期必须确定 分配的内存由java 虚拟机自动垃圾回收器管理。动态分配内存大小
共享特性,栈中如果有字符串,则直接引用;如果没有,开辟新的空间存入值 每new一次都在堆内存中生成一个新的对象。不存在任何复用

package com.demo.test;

import java.lang.reflect.Field;

public class StringDemo {

  public static void main(String[] args) {
    //先在内存中查找有没有这个字符串对象存在,如果存在就指向这个字符串对象;
    String str1 = "abc";
    String str2 = "abc";
    /*
     public String toString() {
      return this;
     }
     */
    String str3 = "abc".toString();
    //不论内存中是否已经存在这个字符串对象,都会新建一个对象。
    String str4 = new String("abc");
    String str5 = new String("abc");
    String str6 = str5;
    String str7 = "a" + "b" + "c";
    String str8 = "a" + "b" + new String("c");
    //String是不可变字符串对象,StringBuilder和StringBuffer是可变字符串对象(其内部的字符数组长度可变),StringBuffer线程安全,StringBuilder非线程安全
    String str9 = new StringBuilder().append("a").append("b").append("c").toString();
    String str10 = new StringBuffer().append("a").append("b").append("c").toString();

    System.out.println("--------> ==");
    System.out.println("---> 1");
    System.out.println(str1==str2);//true

    System.out.println("---> 3");
    System.out.println(str3==str1);//true

    System.out.println("---> 4");
    System.out.println(str4==str1);//false
    System.out.println(str4==str3);//false
    System.out.println(str4==str5);//false
    System.out.println(str4==str6);//false

    System.out.println("---> 7");
    System.out.println(str7==str1);//true
    System.out.println(str7==str3);//true
    System.out.println(str7==str4);//false

    System.out.println("---> 8");
    System.out.println(str8==str1);//false
    System.out.println(str8==str3);//false
    System.out.println(str8==str4);//false
    System.out.println(str8==str7);//false

    System.out.println("---> 9");
    System.out.println(str9==str1);//false
    System.out.println(str9==str3);//false
    System.out.println(str9==str4);//false
    System.out.println(str9==str7);//false
    System.out.println(str9==str8);//false

    System.out.println("---> 10");
    System.out.println(str10==str1);//false
    System.out.println(str10==str3);//false
    System.out.println(str10==str4);//false
    System.out.println(str10==str7);//false
    System.out.println(str10==str8);//false
    System.out.println(str10==str9);//false

    System.out.println("--------> equals");
    System.out.println(str1.equals(str4));//true
    System.out.println(str1.equals(str7));//true
    System.out.println(str1.equals(str8));//true

    System.out.println("--------> hashCode");
    /*
     hashCode计算公式: s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
      因此hashCode都是一样的,而且是每次运行都一样
     */
    System.out.println(str1.hashCode());//96354
    System.out.println(str2.hashCode());
    System.out.println(str3.hashCode());
    System.out.println(str4.hashCode());
    System.out.println(str5.hashCode());
    System.out.println(str6.hashCode());
    System.out.println(str7.hashCode());

    System.out.println("--------> normal change value");
    //String是不可变类,string只是指向堆内存中的引用,存储的是对象在堆中的地址,而非对象本身,给string赋值只是改变其引用对象而非对象本身
    str6 = "123";
    System.out.println(str5);//abc
    System.out.println(str6);//123

    System.out.println("--------> reflect change value");
    /*
     如果非要改变String的值,也不是不可行。只能使用反射了。
     public final class String implements java.io.Serializable, Comparable<String>, CharSequence {
       // The value is used for character storage.
       private final char value[];
       ……
     }
    */
    str6 = str5;
    try {
      Field field = String.class.getDeclaredField("value");
//     Field field = str6.getClass().getDeclaredField("value");
      if(!field.isAccessible()) {
        field.setAccessible(true);
      }
      char[] value = (char[])field.get(str6);
      value[0] = '0';
      System.out.println(str5);//0bc
      System.out.println(str6);//0bc
    } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
      e.printStackTrace();
    }

    System.out.println("--------> obj.toString()");
    Object obj = new Object();
    /*
    public String toString() {
      return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }
    */
    System.out.println(obj.toString());//java.lang.Object@15db9742

    String[] arr1 = {"0"};
    String[] arr2 = {"0"};
    System.out.println(arr1.equals(arr2));//false
  }
}

总结

  • 如果String指向的是一个字符串常量,那么会先在字符串常量池(栈)中查找,如果有就直接指向它;没有则在字符串常量池中创建该常量,然后String指向该常量。
  • 如果String使用关键字new初始化,则会在堆中开辟固定的空间存放该字符串的值,然后String指向该常量。
  • 使用字符串常量拼接,由于表达式先计算右值,因此相当于将String指向一个新拼接好的字符串常量。同样会在字符串常量池中查找,如果有就直接指向它;没有则在字符串常量池中创建该常量,然后String指向该常量。但是如果拼接中存在使用new生成的字符串,则新的字符串就等价于使用new在堆中创建的一样。
  • 修改String的值,只能改变String的指向,不会改变String指向的对象本身。如果非要改变指向的对象本身,可以使用反射。
  • 如果是数组,由于它是对象,那么equals只比较其数组指针地址,并不会去比较其中的元素是否相等。

以上就是本文关于Java String创建对象实例解析的全部内容,希望对大家有所帮助。感兴趣的朋友可以继续参阅本站其他相关专题,如有不足之处,欢迎留言指出。感谢朋友们对本站的支持!

 类似资料:
  • 本文向大家介绍JavaScript中创建字典对象(dictionary)实例,包括了JavaScript中创建字典对象(dictionary)实例的使用技巧和注意事项,需要的朋友参考一下 对于JavaScript来说,其自身的Array对象仅仅是个数组,无法提供通过关键字来获取保存的数据,jQuery源码中提供了一种非常好的方式来解决这个问题,先看一下源码: 上述源码是创建一个编译结果的缓存,代码

  • 问题内容: 当我创建一个说类Employee的模拟对象时。它不调用Employee对象的构造函数。我知道Mockito在内部使用CGLIb和反射,创建了一个代理类,将该类扩展为模拟。如果未调用employee的构造函数,那么如何创建employee类的模拟实例? 问题答案: Mockito使用CGLib生成类对象。但是,要实例化此类对象,它使用Objenesis http://objenesis.

  • JavaScript对每个创建的对象都会设置一个原型,指向它的原型对象。 当我们用obj.xxx访问一个对象的属性时,JavaScript引擎先在当前对象上查找该属性,如果没有找到,就到其原型对象上找,如果还没有找到,就一直上溯到Object.prototype对象,最后,如果还没有找到,就只能返回undefined。 例如,创建一个Array对象: var arr = [1, 2, 3]; 其

  • 使用saveOrUpdate创建新的ebject时,hibernate将对象存储在数据库中并正确返回。但在方法的同一调用中创建了一个带有一些空列的附加对象。 数据库中的对象如下所示: 这是数据类: 这是我将对象存储到数据库的方式:public class DataUtils{private Session Session;private static DataUtils DataUtils; 和我

  • 实例(Instance)是运行在云中的虚拟机。 在创建新实例之前,您要提前知晓以下参数: 实例的源可以是镜像,快照,或者包含镜像或快照的块存储设备。 实例的名字。 您实例的型号,这个型号决定了您nova实例的CPU,内存和磁盘空间情况。型号(flavor)是您虚拟机的硬件可用配置。它决定了您能创建的虚拟机的大小。 任意的用户数据文件。用户数据文件时在元数据服务中的一个特殊的键,它保存了一份能给虚拟

  • 完成初始设置并已登录 Navicat Monitor 后,即可创建要监控的实例。Navicat Monitor 使用无代理体系结构来监控数据库服务器并定期收集数据。它不需要在被监控的服务器上安装任何代理软件。 你可以在以下页面创建新实例,点击“+ 新建实例”并选择服务器类型。 概览 配置 在新建实例窗口中,在“实例名”输入一个恰当名称描述你的实例,并选择实例的“组”。如果你要添加新组,请点击“新建