精华内容
下载资源
问答
  • 如果你有这个类的来源,那我的做法是:>获取JAR文件>获取单个类的源代码>在类路径中使用JAR编译源代码(这样就不必...使用jar u或Ant任务用新的JAR替换JAR中的类文件蚂蚁任务示例:compress="true" update...

    如果你有这个类的来源,那我的做法是:

    >获取JAR文件

    >获取单个类的源代码

    >在类路径中使用JAR编译源代码(这样就不必编译任何其他东西;它不会伤害到JAR已经包含二进制文件).您可以为此使用最新的Java版本;只需使用-source和-target降级编译器.

    >使用jar u或Ant任务用新的JAR替换JAR中的类文件

    蚂蚁任务示例:

    compress="true" update="true" duplicate="preserve" index="true"

    manifest="tmp/META-INF/MANIFEST.MF"

    >

    在这里我也更新清单.先放入新类,然后添加原始JAR中的所有文件. duplicate =“preserve”将确保新的代码不会被覆盖.

    如果代码未签名,您还可以尝试替换字节,如果新字符串的长度与旧字符的长度完全相同. Java对代码进行了一些检查,但是有no checksum in the .class files.

    你必须保留长度;否则类加载器会变得困惑.

    展开全文
  • 有人说字符串常量池在java堆中,可又有人说常量池存在元空间中。 分享几篇知乎文章 关于jvm运行时数据区的模型: 1、面试官 | JVM 为什么使用元空间替换了永久代? 2、Java方法区与元空间 为了解决这个问题,下面...

      在网上看了很多博客,解释也比较多,关于字符串常量池的具体位置难以分辨谁真谁假。

      对于jdk8以后的版本有人说字符串常量池在元空间中,也有人说字符串常量池存在堆中。

      到底谁说的对?他们的说法有依据吗?


      今天让我们来一起探讨一下这个问题

    有人说字符串常量池在java堆中,可又有人说常量池存在元空间中。

    分享几篇知乎文章 关于jvm运行时数据区的模型:
    1、面试官 | JVM 为什么使用元空间替换了永久代?
    2、Java方法区与元空间

    在这里插入图片描述


    为了解决这个问题,下面我们通过Idea、VisualVm、JDK(我用的是jdk14) 和 一段测试代码来探讨一下字符串常量池的位置

    将下面代码粘贴到Idea中

    import java.util.ArrayList;
    
    public class StringConstantPoolTest {
    
        public static void main(String[] args) {
            ArrayList<String> arrayList = new ArrayList<>();
            for (long i = 1; ; i++) {// 死循环
                arrayList.add(String.valueOf(i).intern());
                if (i % 1000_0000 == 0) {
                    arrayList.clear();// 清除引用,让前面产生的对象进行回收,因为内存往往不足
                    System.out.println(i);
                }
            }
        }
    }
    

    在启动参数上面我们设置内存空间的值
    一个是设置堆内存最大值1G,另一个是设置元空间内存最大10M

    -Xmx1G -XX:MaxMetaspaceSize=10M
    

    在这里插入图片描述

    然后我们执行这段程序打开VisualVM看下内存占用
    下面是i执行到这个值时的的情况
    在这里插入图片描述
    程序启动后,就需要一直观看我框出来的这些参数
    在这里插入图片描述

    宿主机内存情况
    在这里插入图片描述

    在我写的测试代码中,利用死循环,不断向ArrayList中添加对象,然后每隔1000_0000次集合被清空一次,因为考虑内存原因我才在这来这样做,不然内存很快被消耗完,清空引用后内存不足就会进行gc。
    arrayList.add(String.valueOf(i).intern());

    是利用String的native修饰的intern()方法,将字符串存入常量池中,当然这是有一个过程的,首先需要在堆中创建一个字符串对象也就是String.valueOf(i),因为字符串常量池没有这个字符串对象所以会将堆中这个字符串对象存入字符串常量池中,但是现在我们还不知道常量池的位置


    排除字符串常量池在虚拟机栈、程序计数器、本地方法栈的情况(Java虚拟机规范要求的),字符串常量池要么在堆中要么就在方法区中


    假设字符串常量池在堆中

      通过看VisualVM我们应该是判断不出字符串常量池是否在堆中的,因为字符串对象也在堆中创建,我们无法根据内存变化判断除字符串常量池在堆中。这种假设就没法继续推断了,进行另外一种假设

    假设字符串常量池在元空间

      元空间有一个特点,那就是使用的是本地内存,也就是宿主机的直接内存,如果没有设置最大值10M,那么只受宿主机内存限制。
      通过观察,我们发现元空间不会像堆内存变化那么明显,它是一点一点增加的,而且我们设置的内存最大值才10M,按照old区的变化早就撑爆了,可是并没有发生OOM。

      那么元空间中应该不存在字符串常量池

    假设字符串常量池在方法区

      如果字符串常量引用被去除了,那么内存不够会触发gc回收字符串常量池中的对象,下面的测试代码就是想让字符串常量池的对象不被回收(又要保证不OOM导致程序退出终止),如果常量池在方法区,那么方法区应该会增大,那么宿主机的内存就会被使用。

    通过两个线程来完成,A线程就是前面的逻辑,而B线程则是从字符串常量池中去取出常量。

    import java.util.ArrayList;
    
    public class StringConstantPoolTest {
    
        public static void main(String[] args) {
    
            new Thread(()->{
                ArrayList<String> arrayList = new ArrayList<>();
                for (long i = 1; ; i++) {
                    arrayList.add(String.valueOf(i).intern());
                    if (i % 1000_0000 == 0) {
                        arrayList.clear();// 清除引用,让前面产生的对象进行回收,因为内存往往不足
                        System.out.println(i);
                    }
                }
            },"A").start();
    
            new Thread(()->{
                for (long i = 1; ; i++) {
                    String.valueOf(i).intern();
                    if (i % 1000_0000 == 0) {
                        System.out.println(i);
                    }
                }
            },"B").start();
        }
    }
    

    测试结果是任务栏管理器的内存没有太大的变化,推测字符串常量池不在方法区。
    在看《深入理解java虚拟机》第三版时关于运行时常量池的说明 运行时常量池 != 字符串常量池是两样东西。
    在这里插入图片描述

    public class TestDemo {
        
        @Test
        public void test01() {
            //
            String str1 = new StringBuilder("hello").append("World").toString();
            System.out.println(str1.intern());
            System.out.println(str1 == str1.intern());
    
            String str2 = new StringBuilder("ja").append("va").toString();
            System.out.println(str2.intern());
            System.out.println(str2 == str2.intern());
    
            String str3 = new StringBuilder("hello").toString();
            System.out.println(str3.intern());
            System.out.println(str3 == str3.intern());
        }
        
    }
    
    

    为什么打印出

    helloWorld
    true
    java
    false
    hello
    false
    

      根据VisualVM和宿主机内存的数据,我们大致可以推断出字符串常量池是在元空间,因为我们通过intern()会不断往字符串常量池中添加数据,因此存有字符串常量池的那块空间,整体占用空间是不断增大的(不考虑GC的情况,GC一般不会发生,只会在需要的时候才会主动进行gc)。

      元空间存的字符串常量只是一个地址引用,因此占用空间很小,10M内存执行了很久都没有报OOM,并且宿主机内存始终在一个稳定值,并没有因为不断添加常量进常量池而导致宿主机内存被占用内存一直增大。

      程序一直执行,元空间最终肯定也会被占满,但关于堆中常量的引用已经被gc回收,那么元空间应该也会回收一部分空间(清除元空间引用关于堆中被gc的对象),然后就会维持在一个值的范围波动起伏而不会一直增然后OOM。

    最终结论

      字符串常量池在堆中,运行时常量池在方法区中。元空间不可能是字符串常量池位置,因为几乎没有什么变化,字符串常量池也不在方法区因为宿主机内存没有太大变化。

      根据变化情况,推出字符串常量池在堆的old区,字符串在Young的Eden区产生,调用intern()就是先看old区有没有,如果没有将这个对象存入old区中,而字符串之所以通过==比较会返回true可能是jvm底层做的一件事情,移动了对象,再次查找对象时会找到移动后的那个对象(gc也会导致对象的移动,会将Eden区的对象移动到S0或S1,甚至可能移动到Old区。因此gc导致的移动至少jvm是肯定要做处理的,至于怎么处理的则需要继续深入才能探究,故推断intern()方法的作用就是将字符串移动到常量池中)


    字符串常量池再深入

    运行时常量池的再深入,从jvm的内存分配角度谈谈这道字符串常量池的面试题。

    展开全文
  • 字符串常量池:程序当中直接写上的双引号字符串,就在字符串常量池中。 对于基本类型来说:是进行数值的比较。 **对于引用类型来说是进行【地址值】的比较 ==## 是进行对象的地址值比较,如果确实需要字符串的内容...

    关注公众号凡花花的小窝,收获更多的考研计算机专业编程相关的资料
    字符串常量池:程序当中直接写上的双引号字符串,就在字符串常量池中。 对于基本类型来说:是进行数值的比较。 **对于引用类型来说是进行【地址值】的比较
    ==## 是进行对象的地址值比较,如果确实需要字符串的内容比较,可以使用两个方法:
    public boolean equals(Objext obj):参数可以是任何的对象,只有参数是一个字符串并且内容相同才会使true否则返回false备注:任何对象都能用Oject进行接收2.equals方法具有对称性,也就是a.equals(b)和b.euqals(a)是效果一样的3.如果比较双方一个常量一个变量,推荐把常量字符串写在前面。推荐:”abc".equals(str)不推荐str.equals(abc);
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    String当中与获取相关的常用方法有:
    public int length():获取字符床当中含有的字符个数,拿到字符串的长度
    public String concat(String str):将当前字符串和参数字符串拼接成返回值新的字符串
    public char charAt(int index):获取指定索引位置的单个字符。(索引从0开始)
    public int indexOf(String str):查找参数字符串在本字符串当中首次出现的索引位置,如果没有返回-1值
    在这里插入图片描述
    在这里插入图片描述
    字符串截取的方法:
    public String subString(int index):截取从参数位置一直到字符串末尾,返回返回返回新的字符串
    public String subString(int begin,int end)截取的是左闭右开的区间
    在这里插入图片描述
    在这里插入图片描述
    public char[] toCharArray()将当前字符串拆分成为字符数组作为返回值
    public byte[]getBytes():获得当前字符串底层的字节数组
    public String replace(CharSequence oldString,CharSequence newString)将所有出现的老字符串替换成为新的字符串,返回替换之后的结果新字符串注意ChaeSquence可以接受字符串类型
    在这里插入图片描述
    在这里插入图片描述
    字符串的分割:
    public String[]split(String regex)按照参数的规则,将字符串切分成为若干的部分。
    注意事项:
    split方法的参数其实就是一个正则表达式,如果按照英文句点"."进行切分,必须写”、、。
    在这里插入图片描述

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    展开全文
  • 如string str=”JA”+”VA”,在编译阶段会直接合并成语句String str=”JAVA”,于是会去常量池中查找是否存在”JAVA”,从而进行创建或引用 ④ 对于final字段,「编译期直接进行了常量替换」(而对于非final字段则是...
    4e7f22d064e2c5e361b12f9123186076.png

    equals 和 == 的区别

    • 基本数据类型

    又称为原始数据类型,byte,short,char,int,long,float,double,boolean,他们之间的比较应该使用(==),比较的是他们的值。

    • 复合数据类型

    当复合数据类型用(==)进行比较,比较的是他们在内存中的存放地址。

    当复合数据类型之间进行equals比较时,这个方法的初始行为是比较对象在堆内存中的地址,但在一些诸如String,Integer,Date类中把Object中的这个方法覆盖了,作用被覆盖为比较内容是否相同。

    String

    「规则总结」

    ①  new String() 都是在堆上创建字符串对象。当调用 intern() 方法时,编译器会将字符串添加到常量池中(stringTable维护),并返回指向该常量的引用

    ② 通过字面量赋值创建字符串(如:String str=”twm”)时,会先在常量池中查找是否存在相同的字符串,若存在,则将栈中的引用直接指向该字符串;若不存在,则在常量池中生成一个字符串,再将栈中的引用指向该字符串

    ③ 常量字符串的“+”操作,编译阶段直接会合成为一个字符串。如string str=”JA”+”VA”,在编译阶段会直接合并成语句String str=”JAVA”,于是会去常量池中查找是否存在”JAVA”,从而进行创建或引用

    ④ 对于final字段,「编译期直接进行了常量替换」(而对于非final字段则是在运行期进行赋值处理的)。final String str1=”ja”; final String str2=”va”; String str3=str1+str2; 在编译时,直接替换成了String str3=”ja”+”va”,根据第三条规则,再次替换成String str3=”JAVA”

    ⑤ 常量字符串和变量拼接时(如:String str3=baseStr + “01”;)会调用stringBuilder.append()在堆上创建新的对象

    「画图解释」

    9033befc3b6e0a632f827446ca076820.png
    new String("xxx") & str.intern()
    65107d1ccbccec6f6a39715fb9b5b704.png
    str = "xxx"

    JDK 1.7后,intern 方法还是会先去查询常量池中是否有已经存在,如果存在,则返回常量池中的引用,这一点与之前没有区别,区别在于,如果在常量池找不到对应的字符串,则不会再将字符串拷贝到常量池,而只是在常量池中生成一个对原字符串的引用。简单的说,就是往常量池放的东西变了:原来在常量池中找不到时,复制一个副本放到常量池(「并将引用指向这个常量」),1.7后则是将在「堆上的地址引用复制到常量池」

    「常见试题解答(重点)」

    有了对以上的知识的了解,我们现在再来看常见的面试或笔试题就很简单了:
    Q:下列程序的输出结果:
    String s1 = “abc”;
    String s2 = “abc”;
    System.out.println(s1 == s2);
    A:true,均指向常量池中对象。

    Q:下列程序的输出结果:
    String s1 = new String(“abc”);
    String s2 = new String(“abc”);
    System.out.println(s1 == s2);
    A:false,两个引用指向堆中的不同对象。

    Q:下列程序的输出结果:
    String s1 = “abc”;
    String s2 = “a”;
    String s3 = “bc”;
    String s4 = s2 + s3;
    System.out.println(s1 == s4);
    A:false,因为s2+s3实际上是使用StringBuilder.append来完成,会生成不同的对象。

    Q:下列程序的输出结果:
    String s1 = “abc”;
    final String s2 = “a”;
    final String s3 = “bc”;
    String s4 = s2 + s3;
    System.out.println(s1 == s4);
    A:true,因为final变量在编译后会直接替换成对应的值,所以实际上等于s4=”a”+”bc”,而这种情况下,编译器会直接合并为s4=”abc”,所以最终s1==s4。

    Q:下列程序的输出结果:
    String s = new String(“abc”);
    String s1 = “abc”;
    String s2 = new String(“abc”);
    System.out.println(s == s1.intern());
    System.out.println(s == s2.intern());
    System.out.println(s1 == s2.intern());
    A:falsefalsetrue

    基本数据类型常量池

    内存中有 java 基本类型封装类的常量池。这些类包括Byte, Short, Integer, Long, Character, Boolean。需要注意的是,「Float和Double这两个类并没有对应的常量池」

    这些包装类的常量池是存在范围限定的;「范围在 -128~127 存在在常量池,范围以外则在堆区进行分配」

    以 Integer 为例,内部有一个 IntegerCache 内部类作为常量池用作缓存,范围就是 -128~127。

    另外需要注意:

    • Long的equals方法会先判断是否是Long类型
    • 无论是Integer还是Long,他们的equals方法比的是数值

    「典型示例」

    Integer a = 1;
    Integer b = 2;
    Integer c = 3;
    Integer d = 3;
    Integer e = 321;
    Integer f = 321;
    Long g = 3L;

    System.out.println(c == d); //  true
    //解析:由于常量池的作用,c与d指向的是同一个对象(注意此时的==比较的是对象,也就是地址,而不是数值)。

    System.out.Println(e == f); // false
    //由于321超过了127,因此常量池失去了作用,所以e和f数值虽然相同,但不是同一个对象。

    System.out.println(c == (a + b)); // true
    //此时==两边有算术运算,会进行拆箱,因此此时比较的是数值,而并非对象。

    System.out.println(c.equals(a+b)); // true
    //c与a+b的数值相等。

    System.out.println(g == (a + b)); // true。
    //由于==两边有算术运算,所以比较的是数值。

    System.out.println(g.equals(a + b)); // false
    //Long类型的equal在比较是时候,会先判断a+b是否为Long类型,显然a+b不是.

    参考文章

    https://blog.csdn.net/qunqunstyle99/article/details/94005430

    以上就是本节内容,欢迎大家关注???

    94e5f02eee9ebaa6c7a32ccb7556db5e.png
    长按关注
    展开全文
  • Scala 字符串以下实例将字符串赋值给一个常量:..."def main(args: Array[String]) {println( greeting )}}以上实例定义了变量 greeting,为字符串常量,它的类型为 String (java.lang.String)。在 Scala 中,字符串...
  • 好程序员Java教程分享常见的转义字符,在Java字符常量中,反斜杠()是一个特殊的字符,被称为转义字符,它的作用是用来转义后面一个字符。转义后的字符通常用于表示一个不可见的字符或具有特殊含义的字符,例如换行()...
  • 01、什么是不可变对象不可变对象在创建后,它的内部状态会保持不变,这就意味着,一旦我们将一个...1)字符串常量池字符串恐怕是 Java 中最常用的数据形式了,如果字符串非要谦虚地说自己是老二,就没有人敢说自己是...
  • 目录一、Java中的字符串二、不可变字符串(1)String(2)字符串池(3)字符串拼接(4)字符串查找(5)字符串比较比较相等比较...Java中的字符串是由双引号括起来的多个字符,下面示例都是表示字符串常量: "Hello ...
  • java字符串连接

    万次阅读 2018-09-08 09:37:43
    String常量字符串的累加 比如我们使用如下代码进行字符串连接: String str = "hello"+"world"+"!"; 先有hello和world2个字符串生成helloworld,然后再生成helloworld!。 将...
  • Java字符串

    2016-11-30 17:41:55
    String不可变字符序列,实体内容确定后,包括实体堆内存和常量池均不可改,若要修改,...// replace用于替换串中指定字符,但不是在原实体内修改,而是重新创建生成一个新字符串 String s = "hello"; System.out.prin
  • P106-105String类-常量池-字符串的比较package hello;public class TestString { public static void main(String[]args) { String str="abc"; String str2=new String("def"); String str3="abc"+"deffgh"; ...
  • java中的字符数组以’\0’ 结尾,我们可以利用这个特性来找到字符串的末尾,而且为了节省内存,java常量字符串存储在单独的一个区域,我们新申请内存的字符串存储在另外一个地方,总结就是:基本类型的的变量数据...
  • 原文链接:java中,字符...String常量的累加操作:对于静态字符串的连接操作,Java在编译时会进行彻底的优化,将多个连接操作的字符串在编译时合成一个单独的长字符串。 其实String变量的累加操作:底层使用了String...
  • 在使用计算机的过程当中,我们经常会遇到设计字符串的一些操作问题。假设你需要编写一个程序,该程序用一...Java语言中相同的字符串常量属于同一个对象,占用同一块内存空间,这和C中处理方式不同。 2.字符串处理类 J
  • String常量的累加操作:对于静态字符串的连接操作,Java在编译时会进行彻底的优化,将多个连接操作的字符串在编译时合成一个单独的长字符串。 String变量的累加操作:底层使用了StringBuilder...
  • 我希望能够为GTK ComboBox声明一个固定长度字符串常量数组 . 是否可以在ADA中使用两个参数?此代码允许通过声明字符串来设置数组的大小 . 是否有可能以某种方式用参数替换规范中的常量?一个简单的数组不允许判别 ...
  • 我们介绍Java数据类型string ...适用于字符串的内容不经常改变的情况,如常量声明,少量变量运算。 StringBuffer是线程安全的可变字符序列,直接对StringBuffer对象进行操作,但不能修改。所有操作类似于串行顺序...
  • String常量的累加操作:对于静态字符串的连接操作,Java在编译时会进行彻底的优化,将多个连接操作的字符串在编译时合成一个单独的长字符串。 String变量的累加操作:底层使用了StringBuilder的功能。 String
  • String常量的累加操作:对于静态字符串的连接操作,Java在编译时会进行彻底的优化,将多个连接操作的字符串在编译时合成一个单独的长字符串。String变量的累加操作:底层使用了StringBuilder的功能。StringBuffer和...
  • 承接JVM-2 运行时数据区域(上),本文主要介绍堆内存、方法区以及常量池部分。 1.堆内存 Java堆是虚拟机管理的最大的一块内存,在JVM进程启动时创建,所有线程共享,主要目的是存放对象实例。 《Java虚拟机规范》中...
  • java字符串拼接方式

    2020-06-23 19:50:59
     String常量的累加操作:对于静态字符串的连接操作,Java在编译时会进行彻底的优化,将多个连接操作的字符串在编译时合成一个单独的长字符串。  String变量的累加操作:底层使用了StringBuilder的功能。  ...
  • Java的String字符串

    2020-03-14 21:48:13
    字符串常量字符串的常用方法 1.equal比较字符串的值 2.获取字符串的值 3.截取字符串 4.字符串的转换与替换 5.字符串的分割方法 不变的字符串 字符串的内容是不可以变的 创建字符串的4种...
  • SoloCompany2016-06-04 02:16:22 +08:00jdk 自带的命令 javap 就可以列出常量表比如javap -v java.lang.System | grep ' = String'#14 = String #213 // setIO#17 = String #217 // java...
  • Java(03)字符串

    千次阅读 2020-03-12 20:50:58
    字符串常量二.创建字符串2.1.创建对象2.2.其他构造方法三.基本方法3.1.常用方法1.获取长度2.判断是否相等3.判断首/末开始的字符串是否相等4.变大小写5.返回首次出现的位置6.与指定字符串比较大小7.根据索引获得字串1...
  • Java字符串

    2016-04-30 20:41:23
    对于经常改变的字符串,不要使用String,因为每次改变都会生成新的实体,自然无引用的实体就会增多,导致GC开始工作,从而影响系统的性能。 String的replace方法,用于替换String中指定的字符,但不是在原Strin
  • 常见字符串方法: 1.长度 2.字符串索引位置的字符 3.提取子串 4.拆分 5.合并 6.转换成字符数组 7.字符数组转换成字符串 8.把其他数据类型转换成...且字符串类型是不能继承的,也就是说,字符串就是常量。 String 方...
  • java如何高效拼接字符串

    万次阅读 2019-06-11 14:24:23
     String常量的累加操作:对于静态字符串的连接操作,Java在编译时会进行彻底的优化,将多个连接操作的字符串在编译时合成一个单独的长字符串。  String变量的累加操作:底层使用了StringBuilder的功能。  ...
  • 常见字符串求解: 1.长度 用于获取有关对象的信息的方法...可以对字符串常量使用 concat() 方法,或者使用’+'操作符来连接字符串 6.转换成字符数组 7.字符数组转换成字符串 8.把其他数据类型转换成字符串 9.替换 10...
  • java中,实现长JSON常量遇到问题使用字符串替换解决 遇到问题 在调用子系统时,需要传入的json参数特别长,而且json参数内部对象嵌套也特别深, 比如:这么一个json数据 kind: Deployment apiVersion: v1 metadata: name...
  • import java.util.Arrays; /** * final修饰变量为常量(只可赋值一次,不能再修改的) * final修饰类,这个类是... * 字符串比较:==地址 ;equal值 //常量池 String str1 = "hello"; // String...

空空如也

空空如也

1 2 3 4 5 ... 8
收藏数 158
精华内容 63
关键字:

java字符串常量替换

java 订阅