精华内容
下载资源
问答
  • 下列正确的字符串常量
    2021-07-21 14:42:23

    字符常量是由一对单撇号括起来的单个字符

    字符串常量,顾名思义就是多个“字符”串在一起,字符串常量是用“双撇号”括起来的多个字符的序列

    ****** 只要是“双撇号”括起来的,就算只有一个字符也叫字符串,如"a"

    下列为字符常量的是()
    A. "a"  B. 'ab'  C. '\n' D. b
    

    解析:
    这里 A带双撇号,肯定就是字符串常量了
    B带单撇号,但是并不是单个字符
    D单双都不带,所以肯定不是

    更多相关内容
  • 语言中教育对生产力的促进作用主要通过什么来实现字符字符中要解决装配超静定问题所用的三个条件是( )常静定问题也存在装配应力。量存利用Trizol法提取的...常克隆羊多莉的诞生主要采用了下列哪项技术?图(a)示...

    语言中教育对生产力的促进作用主要通过什么来实现

    字符字符中要解决装配超静定问题所用的三个条件是( )

    串常静定问题也存在装配应力。

    251146.jpg

    量存利用Trizol法提取的RNA为mRNA。

    数组束符束符唐代皇帝中并称为“二圣”的是( )。

    个结该结do-while语句中的循环体一般不能是复合语句。

    语言中现代保险之父是 ()。

    字符字符中要以下属于武则天文集是( )。

    串常克隆羊多莉的诞生主要采用了下列哪项技术?

    图(a)示静不定桁架,图(b)、图(c)、图(d)、图(e)表示其四种相当系统,其中正确的是( )。

    量存钢筋在混凝土浇筑之前是受拉的。

    在拉伸实验中引起低碳钢屈服的主要因素是切应力,而引起铸铁断裂的主要原因是拉应力。

    唐代除夕是全民庆祝的重大节日,数组束符束符不仅气氛浓厚、场面宏大,庆祝形式更是多样,包括( )等庆祝活动。

    参与hnRNA的剪接、个结该结转运功能的RNA是

    语言中The study suggests the influence of these names might ______.

    字符字符中要唐代长安的风景名胜有( )。

    串常铸铁的抗压强度低于抗剪强度。

    稳定DNA双螺旋结构的力,横向是配对碱基之间的氢键,纵向是疏水性的碱基堆积力。

    以下写骊山晚照的诗歌有( )。

    展开全文
  • 目录 一、String的基本特性 为什么jdk9String由char[]改为了byte[]去存? String存储结构发生了变更,StringBuffer和...四、字符串拼接操作 实例1:(常量常量的拼接结果在常量池) 实例2:(拼接其中有一

    目录

    一、String的基本特性

    为什么jdk9String由char[]改为了byte[]去存?

    String存储结构发生了变更,StringBuffer和StringBuilder是否改变了?

    实例(因为String不可变性,原来地址的值是无法被改变的):

    二、String的内存分配

    为什么要将StringTable进行调整,从永久代->堆?

    三、String的基本操作

    实例1:

    实例2:

    四、字符串拼接操作

    实例1:(常量与常量的拼接结果在常量池)

    实例2:(拼接其中有一个是变量,结果就在堆中,相当于新new的String)

    实例3:(字符串变量拼接底层是用StringBuilder)

    实例4:

    实例5:练习

    实例6:多个字符串拼接,其实也只new了一个StringBuilder

    实例7:for循环中拼接字符串

    总结:

     五、intern()的使用

    如何保证变量s指向字符串常量池中的数据?

    new String("abc")会创建几个对象?

    StringBuilder的toString()方法:

    经典题目(99%的人会做错!)

    拓展:

    总结String的intern()的使用:

    intern练习1:

    intern练习2:

    六、intern()效率测试

    空间角度(jdk1.8)

    七、StringTable的垃圾回收

    八、G1中的String去重操作

    实现

    命令行选项


    一、String的基本特性

    1.String字符串,使用一对""引起来表示。

    2.String声明为final的,不可被继承。

    3.String实现了Serializable接口,表示字符串是支持序列化的。实现了Comparable接口,表示String可以比较大小。

    4.String在jdk8及以前内部定义了final char[] value用于存储字符串数据。jdk9时改为byte[]

    5.String代表不可变的字符序列。简称:不可变性。

        (1)当字符串重新赋值时,需要重写指定内存区域赋值,不能使用原有的value进行赋值。

        (2)当对现有的字符串进行连接操作时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值。

        (3)当调用String的replace()方法修改指定字符或字符串时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值。

    6.通过字面量的方式(区别于new,String a = 'abc';)给一个字符串赋值,此时字符串值声明在字符串常量池中。

    7.字符串常量池中是不会存储相同内容的字符串的

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

    9.使用-XX:StringTableSize可以设置StringTable的长度。

    10.在jdk6中StringTable是固定的,就是1009的长度,所以如果常量池中的字符串过多就会导致效率下降很快。StringTableSize设置没有要求。

    11.在jdk7中,StringTable的长度默认值是60013,StringTable的设置没有要求。

    12.jdk8中,StringTable的长度默认值是60013,1009是可设置的最小值。

    为什么jdk9String由char[]改为了byte[]去存?

        主要还是节省空间。

        JDK9 之前的库的 String 类的实现使用了 char 数组来存放字符串,char 占用16位,即两字节。

        研究发现String是堆空间的主要部分,同时大部分String都是一些拉丁文,一个字节就能够撑得下,如果使用char的话,因为char占两个字节,所以有一半的空间被浪费掉。

        所以,由char[]改为了byte[],再补一个字符标码集的标识coder;如果是ISO-8859-1、Latin-1就用一个byte去存,如果是UTF-16还是用两个字节去存。

        结论:String再也不用char[]来存储了,改成了byte[]加上编码标记,节约了一些空间。

    String存储结构发生了变更,StringBuffer和StringBuilder是否改变了?

    基于String的类,比如StringBuilder、StringBuffer以及HotSpot虚拟机内部的一些类也进行了修改。

    实例(因为String不可变性,原来地址的值是无法被改变的):

    二、String的内存分配

    1.在Java语言中有8种基本数据类型和一种比较特殊的类型String。这些类型为了使它们在运行过程中速度更快、更节省内存,都提供了一种常量池的概念。

    2.常量池就类似一个Java系统级别提供的缓存。8种基本数据类型的常量池都是系统协调的,String类型的常量池比较特殊。它的主要使用方法有两种

        (1)直接使用双引号声明出来的String对象会直接存储在常量池中。(String a = "aaa";)

        (2)如果不是用双引号声明的String对象,可以使用String提供的intern()方法。

    3.Java 6及以前,字符串常量池存放在永久代。

    4.Java7中Oracle的工程师对字符串池的逻辑做了很大的改变,即将字符串常量池的位置调整到Java堆内

        所有的字符串都保存在堆(Heap)中,和其他普通对象一样,这样可以让你在进行调优应用时仅需要调整堆大小就可以了。

        字符串常量池概念原本使用得比较多,但是这个改动使得我们又足够的理由让我们重新考虑在java7中使用String.intern()。

    5.java8永久代变为元空间,字符串常量还是在堆。

     

    为什么要将StringTable进行调整,从永久代->堆?

    1.永久代空间有限,将字符串放在永久代容易报OOM-永久代内存溢出。

    2.永久代回收频率很低。

    三、String的基本操作

    java语言规范里要求完全相同的字符串字面量,应该包含同样的Unicode字符序列(包含同一份码点序列的常量),并且必须是指向同一个String类实例。

    实例1:

    debug下,可以逐行查看字符串常量池,首次加载1-10之后,再次打印就不会再往字符串常量池里加1-10字符串了:

     

    实例2:

    line3、line4的new的Object()、Memory()都是指向堆空间。

    line6的param也指向堆空间Object()。

    line7的param的toString()方法返回一个字符串,字符串存在字符串常量池里。

    四、字符串拼接操作

    1.常量与常量的拼接结果在常量池,原理是编译期优化。

    2.常量池中不会存在相同内容的常量。

    3.只要其中有一个是变量,结果就在堆中。变量拼接的原理是StringBuilder。

    4.如果拼接的结果调用intern()方法,则主动将常量池中还没有的字符串对象放入池中,并返回此对象地址。

    实例1:(常量与常量的拼接结果在常量池)

    反编译结果:

    实例2:(拼接其中有一个是变量,结果就在堆中,相当于新new的String)

    intern()方法就是判断字符串常量池中是否存在这个字符串,如果存在则返回常量池中字符串地址,如果不存在就在常量池中加载字符串并返回此地址。

    实例3:(字符串变量拼接底层是用StringBuilder)

    根据字节码指令可以看出,拼接字符串变量,先new了StringBuilder,然后使用append操作,然后调用toString()方法。

    StringBuilder的toString()方法,约等于new String("")。具体继续往下看。

    (补充:jdk1.5之后,拼接字符串使用StringBuilder,jdk1.5之前使用StringBuffer)

    实例4:

    字符串拼接操作,不一定是用的StringBuilder。如果拼接符号左右两边都是字符串常量或者常量引用(final修饰),则仍然使用编译期优化,非StringBuilder方式。

    所以,针对于final修饰的类、方法、基本数据类型、引用数据类型的结构时,能使用上final的时候建议使用上。

    反编译:

    实例5:练习

    实例6:多个字符串拼接,其实也只new了一个StringBuilder

    实例7:for循环中拼接字符串

    可以看出,for每循环一次就会new一个StringBuilder。所以,拼接字符串最好还是直接使用StringBuilder,而不是“+”。

     

    总结:

    字符串拼接最好使用StringBuilder调用append来拼接。

    使用加号“+”拼接,会new一个StringBuilder,并且在最后调用toString方法时还会new String()。

    内存中由于创建了较多的StringBuilder和String对象,还有一方面是内存占用,调用GC还会额外花费时间。

    所以,字符串拼接直接使用StringBuilder会大大提高性能,尤其是多个字符串拼接。

    改进的空间:

    如果开发中基本确定拼接后的字符串长度不高于某个限定值,可以使用StringBuilder(int highlevel)构造器来进行创建StringBuilder。

     五、intern()的使用

    以上的解释:

    如果字符串常量池中,通过queals方法对比的时候,已经包含了这个字符串,接下来池子中的字符串将会被返回。否则就会将这个字符串加到池子里,并且返回这个字符串实例。

    假如说s和t是相同的,以下两种情况结果都为true。

    s.intern() == t.intern()

    s.equals(t)

    1.如果不是用双引号声明的String对象,可以使用String提供的intern方法:intern方法会从字符串常量池中查询当前字符串是否存在,若不存在就会将当前字符串放入常量池中。

        比如:String s = new String("I love you").intern();

    2.也就是说,如果在任意字符串上调用String.intern方法,那么其返回结果所指向的那个类实例,必须和直接以常量形式出现的字符串实例完全相同。因此,下列表达式的值必定是true:

        ("a" + "b" + "c").intern() == "abc";

    3.通俗点讲,Interned String就是确保字符串在内存里只有一份拷贝,这样可以节约内存空间,加快字符串操作任务的执行速度。注意,这个值会被存放在字符串内部池(字符串常量池)(String Intern Pool)。

    如何保证变量s指向字符串常量池中的数据?

    1.String s = "abc"; // 字面量方式定义。

    2.String s = new String("abc").intern();

        String s = new StringBuilder("abc").toString().intern(); //调用intern方法。

    new String("abc")会创建几个对象?

    1.创建了两个对象

    一个是通过new关键字在堆空间创建的。

    另一个是ldc创建的字符串常量池中的对象。

    2.创建了5个对象

    第一个new 了StringBuilder对象(1)。

    第二个new了String(4)。

    第三个常量池中的a(6)。

    第四个new了String(9)。

    第五个常量池中的b(11)。

    第六个,StringBuilder的toString()方法,也会new一个String(toString方法的调用,并不会将字符串放到字符串常量池中)。

    StringBuilder的toString()方法:

    toString方法的调用,并不会将字符串放到字符串常量池中。

    我们并没有在字节码指令中发现ldc命令,所以并没有将字符串放到字符串常量中。

    经典题目(99%的人会做错!)

    结果:

    jdk6:false、false。

    jdk7和8:false、true。

    解释:

        Strign s = new String("1");我们已经知道,它不仅会new一个String对象在堆中,而且还会在字符串常量池中放上"1"。

        所以调用s.intern();并没有什么实际意义,因为字符串常量池中已经有"1"了。

        而且s指向的是new String("1")的地址,而不是常量池中的地址。

        如果s.intern() 改为s = s.intern(); 那么s == s2就是true了。

        s3的地址记录的实际是new String("11")。此时,字符串常量池中并不存在"11",只存在"1",因为字符串拼接使用StringBuilder之后调用的toString()方法,并不会将结果存在字符串常量池中。

        s3.intern();会将"11"存在字符串常量池中。jdk6中就是创建了一个常量池中新的对象"11",也就有新的地址;jdk7和8常量池放到堆中了,这里s3调用intern()方法,这里由于空间节省,常量池中并不会创建"11",而是创建了一个指向堆空间new String("11")的地址

        String s4 = "11"; s4指向的地址是上一行代码在常量池中存放的"11"。

        jdk7和8中,本质上s3和s4指向了堆中同一个地址

    详细过程:

        

    拓展:

    原因:

        String s4 = "11"; 是实打实的在字符串常量池中创建了"11",而不是一个引用了。

        s3.intern(); 因为常量池中已经有"11"了。

        此时s3指向堆中的new String("11"); s4指向常量池中的"11",s5也指向常量池中的"11"。

    总结String的intern()的使用:

    1.jdk1.6中,将这个字符串对象尝试放入字符串常量池。

        如果串池中有,则并不会放入,返回已有的串池中的对象的地址。

        如果没有,会把此对象复制一份,放入串池,并返回串池中的对象地址。

    2.jdk1.7起,将这个字符串对象尝试放入串池。   

        如果串池中有,则并不会放入,返回已有的串池中的对象地址。

        如果没有,则会把对象的引用地址复制一份,放入串池,并返回串池中的引用地址。

    intern练习1:

    jdk6:true、false

    jdk7:true、true(jdk7在串池并没有创建"ab"对象,而是创建了一个引用,指向堆中的new String("ab")的地址)

    在代码前面加上String x = "ab";

    intern练习2:

        第一种情况是false,第二种情况是true。

        String s1 = new String("ab"); 会在堆中new 一个String,并且也会在字符串常量池中新建一个"ab"。

        String s1 = new String("a") + new String("b"); 会在字符串常量池中新建"a"、"b",拼接字符串使用StringBuilder,其toString()方法并不会在常量池中创建"ab"。而且调用s1.intern()之后,jdk7由于堆中已经有ab字符串了,在字符串常量池只创建了一个该对象的引用。

    六、intern()效率测试

    空间角度(jdk1.8)

    使用jvisualvm查看结果:

    不使用intern:

    使用intern:

    总结:使用intern更节省内存空间(尤其是程序中有大量重复的字符串时),并且执行速度还快。

    七、StringTable的垃圾回收

    使用jvm参数:

    -XX:+PrintStringTableStatistics,打印字符串常量池信息。

    main函数中什么也不写的字符串常量池打印信息:

    执行以下代码:

    执行以下代码:

    我们会发现发生了GC,并且字符串常量池数据量降了下来

    八、G1中的String去重操作

    1.背景:对许多Java应用(有大的也有小的)做的测试得出以下结果:
        堆存活数据集合里面String对象占了25%。
        堆存活数据集合里面重复的String对象有13.5%。
        String对象的平均长度是45。
    2.许多大规模的Java应用的瓶颈在于内存,测试表明,在这些类型的应用里面,Java堆中存活的数据集合差不多25%是String对象。更进一步,这里面差不多一半String对象是重复的,重复的意思是说:string1.equals(string2)=true。堆上存在重复的String对象必然是一种内存的浪费。这个项目将在G1垃圾收集器中实现自动持续对重复的String对象进行去重,这样就能避免浪费内存。

    实现

    1.当垃圾收集器工作的时候,会访问堆上存活的对象。对每一个访问的对象都会检查是否是候选的要去重的String对象。
    2.如果是,把这个对象的一个引用插入到队列中等待后续的处理。一个去重的线程在后台运行,处理这个队列。处理队列的一个元素意味着从这个队列删除这个元素,然后尝试去重它引用的String对象。
    3.使用一个hashtable来记录所有的被String对象使用的不重复的char数组。当去重的时候,会查这个hashtable,来看堆上是否已经存在一个一模一样的char数组。
    4.如果存在,String对象会被调整引用那个数组,释放对原来的数组的引用,最终会被垃圾收集器回收掉。
    5.如果查找失败,char数组会被插入到hashtable,这样以后的时候就可以共享这个数组了。

    命令行选项

        UseStringDeduplication(bool):开启String去重,默认是不开启的,需要手动开启。
        PrintStringDeduplicationStatistics(bool):打印详细的去重统计信息。
        StringDeduplicationAgeThreshold(uintx):达到这个年龄的String对象被认为是去重的候选对象。

    展开全文
  • 文章目录前言一、String的基本特性**为什么 JDK9 改变了 String 的结构****一道笔试题****字符串常量池怎么保证不会存储相同内容的?**JDK 6环境下的大小设置:JDK 7环境下的大小设置:二、String的内存分配JDK 6环境...

    往期文章

    JVM——(1)为什么学习虚拟机
    JVM——(2)聊聊JVM虚拟机
    JVM——(3)类加载子系统
    JVM——(4)运行时数据区的概述与程序计数器(PC寄存器)
    JVM——(5)运行时数据区的虚拟机栈
    JVM——(6)运行时数据区的本地方法栈
    JVM——(7)运行时数据区的堆空间
    JVM——(8)运行时数据区的方法区
    JVM——(9)对象的实例化与访问定位
    JVM——(10)执行引擎
    JVM——(11)String Table(字符串常量池)
    JVM——(12)垃圾回收概述
    JVM——(13)垃圾回收相关算法
    JVM——(14)垃圾回收相关概念的概述
    JVM——(15)垃圾回收器详细篇
    JVM——(16)Class文件结构一(描述介绍)
    JVM——(17)Class文件结构二(解读字节码)
    JVM——(18)Class文件结构三(JAVAP指令)
    JVM——(19)字节码指令集与解析一(局部变量压栈、常量变量压栈、出栈局部变量表指令)
    JVM——(20)字节码指令集与解析二(算数指令)
    JVM——(21)字节码指令集与解析三(类型转换指令)
    JVM——(22)字节码指令集与解析四(对象创建与访问指令)
    JVM——(23)字节码指令集与解析五(方法调用指令与方法返回指令)
    JVM——(24)字节码指令集与解析六(操作数栈管理指令)

    前言

    我们在实际开发当中使用String非常的广泛,那么对使用String类其实有很多角度可以去学习理解

    那么本篇文章,我们从使用String的层次到开始了解分析String的实现、性能等等

    一、String的基本特性


    对于String我们称为字符串,使用一对 “” 引号起来表示

    那么平常我们的使用有不同的定义方式如下:

    • String s1 = "xiaomingtongxue" ; 称呼为字面量的定义方式
    • String s2 = new String("hello");称呼为对象的方式

    我们可以观察一下String的源代码分析看看
    在这里插入图片描述

    如图可以观察到String被声明为final的:表示它不可被继承

    如图可以观察到String实现了Serializable接口:表示字符串是支持序列化的

    如图可以观察到实现了Comparable接口:表示String可以比较大小

    如图可以观察到String在jdk8及以前内部定义了final char value[]用于存储字符串数据

    但是它在JDK9时改为了byte[],我们可以切换到JDK9的环境去看看String的源码
    在这里插入图片描述

    为什么 JDK9 改变了 String 的结构

    ================================

    可以访问官方文档查看详细的说明:访问入口

    具体我们就粘贴官网的说明进行翻译解释
    在这里插入图片描述
    在这里插入图片描述

    结论:String再也不用char[] 来存储了,改成了byte [] 加上编码标记,节约了一些空间

    同时基于String的数据结构,例如StringBuffer和StringBuilder也同样做了修改

    对于String来说它代表了不可变的字符序列,简称:不可变性。

    接下来我们使用示例体会一下它的不可变性

    public void test2() {
       String s1 = "abc";
       String s2 = "abc";
       System.out.println(s1 == s2);
       System.out.println(s1);
       System.out.println(s2);
    }    
    //运行如下:
    true
    abc
    abc
    

    结论:这两个abc实际共用的是堆空间里的字符串常量池里边的同一个,所以两个引用地址是一样的

    public void test2() {
       String s1 = "abc";
       String s2 = "abc";
       s1 = "hello";
       System.out.println(s1 == s2);
       System.out.println(s1);
       System.out.println(s2);
    }
    //运行如下:
    false
    abc
    abc
    

    结论:当对s1字符串重新赋值时会新建一个指定内存区域并赋值,所以在比较的时候两个引用地址不一样

    public void test2() {
       String s1 = "abc";
       String s2 = "abc";
       s2 += "def";
       System.out.println(s2);
       System.out.println(s1);
    }
    //运行如下:
    abcdef
    abc
    

    结论:当对s1字符串进行连接操作时也会新建一个指定内存区域并赋值,不在原有的value进行赋值

    public void test3() {
        String s1 = "abc";
        String s2 = s1.replace('a', 'm');
        System.out.println(s1);
        System.out.println(s2);
    }
    //运行如下:
    abc
    mbc
    

    结论:当调用string的replace()操作时也会新建一个指定内存区域并赋值,不在原有的value进行赋值

    一道笔试题

    ================================

    public class StringExer {
        String str = new String("good");
        char[] ch = {'t', 'e', 's', 't'};
        public void change(String str, char ch[]) {
            str = "test ok";
            ch[0] = 'b';
        }
        public static void main(String[] args) {
            StringExer ex = new StringExer();
            ex.change(ex.str, ex.ch);
            System.out.println(ex.str);
            System.out.println(ex.ch);
        }
    }
    

    那么当我们运行起来的时候,会输出什么呢?输出:good、best

    原 str 的引用地址的内容并没有变,方法里的str = “test ok” ,其实是字符串常量池中的另一个区域(地址),将它进行赋值操作给str但并没有修改原来 str 指向的引用地址里的内容

    结论:字符串常量池中是不会存储相同内容的字符串的

    字符串常量池怎么保证不会存储相同内容的?

    ================================

    因为String Pool(字符串常量池)是一个固定大小的Hashtable,默认值大小长度是1009

    JDK6中StringTable是固定的就是1009的长度,所以如果常量池中的字符串过多就会导致效率下降很快,StringTablesize设置没有要求

    JDK7中,StringTable的长度默认值是60013

    JDK8中,StringTable的长度默认值是60013,StringTable可以设置的最小值为1009

    如果放进String Pool的String非常多就会造成Hash冲突严重,从而导致链表会很长而链表长了后直接会造成的影响就是当调用String.intern()方法时性能会大幅下降

    所以扩充StringTable的长度,使用-XX:StringTablesize可设置StringTable的长度

    我们使用一个示例来体会一下不同版本的默认长度是多少

    public static void main(String[] args) {
        //测试StringTablesize参数
        System.out.println("我来打个酱油");
        try {
            Thread.sleep(1000000);
        }catch (InterruptedException e){
            e. printStackTrace();
        }
    }
    
    JDK 6环境下的大小设置:

    在这里插入图片描述

    这时将项目跑起来,在打开cmd命令窗口查看一下是否已被修改为:10的大小
    在这里插入图片描述

    JDK 7环境下的大小设置:

    在这里插入图片描述

    这时将项目跑起来,在打开cmd命令窗口查看一下大小是多少
    在这里插入图片描述

    接下来我们再测试不同大小长度的速度是怎么样的,先生成10万个长度不超过10的字符串

    public static void main(String[] args) throws IOException {
        FileWriter fw =  new FileWriter("words.txt");
        for (int i = 0; i < 100000; i++) {
            //1 - 10
           int length = (int)(Math.random() * (10 - 1 + 1) + 1);
            fw.write(getString(length) + "\n");
        }
        fw.close();
    }
    
    public static String getString(int length){
        String str = "";
        for (int i = 0; i < length; i++) {
            //65 - 90, 97-122
            int num = (int)(Math.random() * (90 - 65 + 1) + 65) + (int)(Math.random() * 2) * 32;
            str += (char)num;
        }
        return str;
    }
    

    接下来我们根据设置不同大小看看,将这10万个长度不超过10的字符串读取看看效率怎么样
    在这里插入图片描述

    花费时间:143ms
    

    在这里插入图片描述

    花费时间:47ms
    

    二、String的内存分配


    那么String结构主要放在哪呢?其实我们在前几篇有提到过这个放的位置

    那么我们再出于完整性的考虑并且例子来说明确实是这样的结构当中

    首先我们说在Java语言中有8种基本数据类型1种比较特殊的类型String

    那么这九种那个唯独可以放在一起呢?那就是常量池使得它们更快更节省内存等

    常量池就类似一个Java系统级别提供的缓存,8种基本数据类型的常量池都是系统协调的,String类型的常量池比较特殊。它的主要使用方法有两种

    • 直接使用双引号声明出来的String对象会直接存储在常量池中。
    • 如果不是用双引号声明的String对象,可以使用String提供的intern()方法。这

    Java 6 及以前,字符串常量池存放在永久代
    在这里插入图片描述

    Java 7中 Oracle的工程师对字符串池的逻辑做了很大的改变,即将字符串常量池的位置调整到Java堆内

    所有的字符串都保存在堆(Heap)中,和其他普通对象一样,这样可以让你在进行调优应用时仅需要调整堆大小就可以了

    字符串常量池概念原本使用得比较多,但是这个改动使得我们有足够的理由让我们重新考虑在Java 7中使用String.intern()。
    在这里插入图片描述

    Java8元空间,字符串常量在堆

    在这里插入图片描述

    我们可以使用示例来体会一下字符串常量池爆出OOM的不同情况

    public class StringTest3 {
        public static void main(String[] args) {
            //使用Set保持着常量池引用,避免full gc回收常量池行为
            Set<String> set = new HashSet<String>();
            //在short可以取值的范围内足以让6MB的PermSize或heap产生OOM了。
            short i = 0;
            while(true){
                set.add(String.valueOf(i++).intern());
            }
        }
    }
    
    JDK 6环境运行下在永久代中:

    在这里插入图片描述

    Exception in thread "main" java.lang.outOfMemoryError: PermGen space
    at java.lang.string.intern(Native Method)
    at com.atguigu.java.stringTest3.main(StringTest3. java:22)
    
    JDK 8环境运行下在堆中:

    在这里插入图片描述

    运行结果
    Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
        at java.util.HashMap.resize(HashMap.java:703)
        at java.util.HashMap.putVal(HashMap.java:662)
        at java.util.HashMap.put(HashMap.java:611)
        at java.util.HashSet.add(HashSet.java:219)
        at com.atguigu.java.StringTest3.main(StringTest3.java:22)
    

    那么为什么要调整字符串常量池的位置?

    ================================

    首先永久代的默认空间大小比较小并且垃圾回收频率低,大量的字符串无法及时回收,容易进行Full GC产生STW或者容易产生OOM:PermGen Space

    而堆中空间足够大,字符串可被及时回收

    在JDK 7中,interned字符串不再在Java堆的永久代中分配,而是在Java堆的主要部分(称为年轻代和年老代)中分配,与应用程序创建的其他对象一起分配

    此更改将导致驻留在主Java堆中的数据更多,驻留在永久生成中的数据更少,因此可能需要调整堆大小

    三、String的基本操作


    我们来看一个代码示例,按照我们之前说的特性,后面同样的字符串则不会再生成

     public static void main(String[] args) {
        System.out.println();
        System.out.println("1");
        System.out.println("2");
        System.out.println("3");
        System.out.println("4");
        System.out.println("5");
        System.out.println("6");
        System.out.println("7");
        System.out.println("8");
        System.out.println("9");
        System.out.println("10");
        //如下的字符串"1" 到 "10"不会再次加载
        System.out.println("1");
        System.out.println("2");
        System.out.println("3");
        System.out.println("4");
        System.out.println("5");
        System.out.println("6");
        System.out.println("7");
        System.out.println("8");
        System.out.println("9");
        System.out.println("10");
    }
    

    那么我们使用debug运行起来代码,看看真的是这样吗?
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    Java语言规范里要求完全相同的字符串字面量,应该包含同样的Unicode字符序列(包含同一份码点序列的常量),并且必须是指向同一个String类实例

    那么接下来我们再看一个列子,当我们运行起来一起分析main方法与foo方法各指向的位置

    //官方示例代码
    class Memory {
        public static void main(String[] args) {//line 1
            int i = 1;//line 2
            Object obj = new Object();//line 3
            Memory mem = new Memory();//line 4
            mem.foo(obj);//line 5
        }//line 9
    
        private void foo(Object param) {//line 6
            String str = param.toString();//line 7
            System.out.println(str);
        }//line 8
    }
    

    在这里插入图片描述

    四、字符串拼接操作


    我们先来看看以下代码的拼接操作,若进行匹配的话结果是什么呢?

    public void test1(){
        String s1 = "a" + "b" + "c";
        String s2 = "abc";
        System.out.println(s1 == s2);
        System.out.println(s1.equals(s2));
    }
    

    结果是输出两个true、为什么呢?我们先将它进行编译一起看看.class文件内容是什么

    public void test1(){
        String s1 = "abc";
        String s2 = "abc";
        System.out.println(s1 == s2);
        System.out.println(s1.equals(s2));
    }
    

    我们发现常量与常量的拼接的话,它们的结果在常量池原理是编译优化将s1等同于"abc"

    同时我们也可以看看该字节码是怎么回事
    在这里插入图片描述

    从字节码指令看出:编译器做了优化,将 “a” + “b” + “c” 优化成了 “abc”

    接下来在看看下一个示例代码

    public void test2(){
    
        String s1 = "javaEE";
        String s2 = "hadoop";
        String s3 = "javaEEhadoop";
        String s4 = "javaEE" + "hadoop";
        String s5 = s1 + "hadoop";
        String s6 = "javaEE" + s2;
        String s7 = s1 + s2;
    
        System.out.println(s3 == s4);
        System.out.println(s3 == s5);
        System.out.println(s3 == s6);
        System.out.println(s3 == s7);
        System.out.println(s5 == s6);
        System.out.println(s5 == s7);
        System.out.println(s6 == s7);
        
        String s8 = s6.intern();
        System.out.println(s3 == s8);
    }
    

    那么当代码运行起来后运行结果为:true、false、false、false、false、false、false、false、

    那么为什么会这样呢?我们和上面一样将它编译并且看看.class文件内容

    public void test2(){
    
        String s1 = "javaEE";
        String s2 = "hadoop";
        String s3 = "javaEEhadoop";
        String s4 = "javaEEhadoop";
        String s5 = s1 + "hadoop";
        String s6 = "javaEE" + s2;
        String s7 = s1 + s2;
    
        System.out.println(s3 == s4);
        System.out.println(s3 == s5);
        System.out.println(s3 == s6);
        System.out.println(s3 == s7);
        System.out.println(s5 == s6);
        System.out.println(s5 == s7);
        System.out.println(s6 == s7);
        
        String s8 = s6.intern();
        System.out.println(s3 == s8);
    }
    

    我们发现s4 常量与常量的拼接的话,会进行编译优化就连接成一起了

    如果拼接符号的前后出现了变量则相当于在堆空间中new String(),具体的内容为拼接的结果:javaEEhadoop(s5、s6、s7出现)所以此时再进行比较的时候,结果就会为false

    那么我们发现s8 = s6.intern(),那么它是什么情况呢?

    如果拼接的结果调用intern()方法,根据该字符串是否在常量池中存在,分为:

    • 如果存在,则返回字符串在常量池中的地址
    • 如果字符串常量池中不存在该字符串,则在常量池中创建一份,并返回此对象的地址

    而我们s6 拼接符号的前后面出现了变量则堆空间中new String(),所以字符串常量池不存在

    接下来在看看下一个示例代码

    public void test3(){
        String s1 = "a";
        String s2 = "b";
        String s3 = "ab";
        String s4 = s1 + s2;
        System.out.println(s3 == s4);
    }
    

    那么当代码运行起来后运行结果为:false

    那么为什么会这样呢?我们一起运行分析一下字节码看看
    在这里插入图片描述

    我们发现s4 = s1 + s2 的时候第九行操作指令创建StringBuilder并进行了实例化赋默认值

    将局部变量表索引为1的a、索引为2的ab进行append。我们可以使用临时代码展示这个操作

    public void test3(){
        String s1 = "a";
        String s2 = "b";
        String s3 = "ab";
        String s4 = s1 + s2;
        /*
        如下的s1 + s2 的执行细节:(变量s是我临时定义的)
        ① StringBuilder s = new StringBuilder();
        ② s.append("a")
        ③ s.append("b")
        ④ s.toString()  --> 约等于 new String("ab"),但不等价
        补充:在jdk5.0之后使用的是StringBuilder,在jdk5.0之前使用的是StringBuffer
         */
        System.out.println(s3 == s4);
    }
    

    结论:拼接前后只要其中有一个是变量结果就在堆中。变量拼接的原理是StringBuilder

    接下来在看看下一个示例代码

    public void test4(){
        final String s1 = "a";
        final String s2 = "b";
        String s3 = "ab";
        String s4 = s1 + s2;
        System.out.println(s3 == s4);
    }
    

    那么当代码运行起来后运行结果为:true

    那么为什么会这样呢?不是说相当于StringBuilder新建存储在堆中吗?让我们一起来分析分析
    在这里插入图片描述
    如果拼接符号左右两边都是字符串常量或常量引用,则仍然使用编译期优化,即非StringBuilder的方式

    针对于final修饰类、方法、基本数据类型、引用数据类型的量的结构时,能使用上final的时候建议使用上

    从字节码角度来看:为变量 s4 赋值时,直接使用 #16 符号引用,即字符串常量 “ab”

    接下来我们使用一个拼接操作与append 操作的效率进行对比

    public void test6(){
    
        long start = System.currentTimeMillis();
        method1(100000);//4014
        long end = System.currentTimeMillis();
        System.out.println("花费的时间为:" + (end - start));
    }
    
    public void method1(int highLevel){
        String src = "";
        for(int i = 0;i < highLevel;i++){
            src = src + "a";//每次循环都会创建一个StringBuilder、String
        }
    }
    //运行结果如下:
    花费的时间:4014
    public void test6(){
    
        long start = System.currentTimeMillis();
        method2(100000);
        long end = System.currentTimeMillis();
        System.out.println("花费的时间为:" + (end - start));
    }
    public void method2(int highLevel){
        //只需要创建一个StringBuilder
        StringBuilder src = new StringBuilder();
        for (int i = 0; i < highLevel; i++) {
            src.append("a");
        }
    }
    //运行结果如下:
    花费的时间:7
    

    经过输出花费的时间我们可以体会执行效率:通过StringBuilder的append()的方式添加字符串的效率要远高于使用String的字符串拼接方式!

    原因是因为StringBuilder的append()的方式:自始至终中只创建过一个StringBuilder的对象

    那么对于使用String的字符串拼接方式有不足呢:

    • 创建过多个StringBuilder和String(调的toString方法)的对象,内存占用更大;
    • 如果进行GC,需要花费额外的时间(在拼接的过程中产生的一些中间字符串可能永远也用不到,会产生大量垃圾字符串)。

    五、intern()的使用


    我们先看看intern在String类是什么怎么描述的呢?
    在这里插入图片描述
    我们看图就可以知道intern是一个native方法,调用的是底层C的方法

    字符串常量池池最初是空的,由String类私有地维护。在调用intern方法时,如果池中已经包含了由equals(object)方法确定的与该字符串内容相等的字符串,则返回池中的字符串地址。否则,该字符串对象将被添加到池中,并返回对该字符串对象的地址。(这是源码里的大概翻译)

    如果不是用双引号声明的String对象,可以使用String提供的intern方法:从字符串常量池中查询当前字符串是否存在,若不存在就会将当前字符串放入常量池中。比如:

    String myInfo = new string("I love you").intern();
    

    我们说new String()存放在堆空间,那么就会在堆空间创建"I love you “同时去常量池判断是否有这个"I love you”,若不存在则将当前字符串放入常量池中同时返回地址给myInfo

    这样的话也就说如果在任意字符串上调用String.intern方法,那么其返回结果所指向的那个类实例,必须和直接以常量形式出现的字符串实例完全相同。因此,下列表达式的值必定是true

    ("a"+"b"+"c").intern()=="abc"
    

    通俗点讲interned 对于String就是确保字符串在内存里只有一份拷贝,这样可以节约内存空间,加快字符串操作任务的执行速度。注意:这个值不存在会创建并存放在字符串内部池(String Intern Pool)

    关于 new String() 的说明

    ================================

    下面我们看看这个问题:new String(“ab”)会创建几个对象?

    根据我们前面的思路,我们还直接观看字节码吧,看看到底做了些什么事情

    • new #2 :在堆中创建了一个 String 对象
    • ldc #3 :在字符串常量池中放入 “ab”(如果之前字符串常量池中没有 “ab” 的话)
      在这里插入图片描述

    下面我们看看这个问题:new String(“a”) + new String(“b”) 会创建几个对象?

    根据我们前面的思路,我们还直接观看字节码吧,看看到底做了些什么事情

    • new #2 :拼接字符串会创建一个 StringBuilder 对象
    • new #4 :创建 String 对象,对应于 new String(“a”)
    • ldc #5 :在字符串常量池中放入 “a”(如果之前字符串常量池中没有 “a” 的话)
    • new #4 :创建 String 对象,对应于 new String(“b”)
    • ldc #8 :在字符串常量池中放入 “b”(如果之前字符串常量池中没有 “b” 的话)
    • invokevirtual #9 :调用 StringBuilder 的 toString() 方法,会生成一个 String 对象
      在这里插入图片描述
      在这里插入图片描述

    有了前面两道题做基础,我们接下来看看一道比较难的题目看看创建了几个对象

    public class StringIntern {
        public static void main(String[] args) {
            String s = new String("1");
            s.intern();
            String s2 = "1";
            System.out.println(s == s2);
            String s3 = new String("1") + new String("1");
            s3.intern();
            String s4 = "11";
            System.out.println(s3 == s4);
        }
    }
    

    我们在JDK6的环境下运行看看输出结果是什么
    在这里插入图片描述
    我们运行起来,发现结果是运行结果:false、false

    我们在JDK7的环境下运行看看输出结果是什么
    在这里插入图片描述
    我们运行起来,发现结果是运行结果:false、true

    目前我们的环境是JDk7,这时我们编译看看main方法的字节码是怎么样的
    在这里插入图片描述

    而刚刚我们前面的第二个问题铺垫时就说到过new String() + new String()的问题解析

    那么我们就分析一下s3.intern()是做了什么事情呢?

    在前面new String(“1”) + new String(“1”)的时候会调用 StringBuilder 的 toString() 方法,会生成一个 String 对象为"11",但我们前面提到过在字符串常量池中并没有生成

    所以当我们执行s3.intern()的时候

    字符串常量池没有s3的"11",所以创建一个指向堆空间new String(“11”)的地址

    执行s4 = “11"的时候常量池里有"11”,所以就会使用s3.intern()的那个指向地址,所以s3 == s4 为true

    那么我们一起看看这个解释的思路图吧(JDk7 环境)
    在这里插入图片描述

    那么在JDK6的思路图当中就不一样,我们一起来看看
    在这里插入图片描述
    在JDK6当中字符串常量池并没有在堆空间,所以它会在常量池生成一个新的对象"11"并且有新的地址

    所以当在JDk6中的s3 与 s4 ,它们的引用地址各不同所以s3 == s4 为false

    接下来我们根据这个特性再扩展一下,看下面的代码块输出结果是什么呢?

    public class StringIntern1 {
        public static void main(String[] args) {
            String s3 = new String("1") + new String("1");
            String s4 = "11";  
            String s5 = s3.intern();
            System.out.println(s3 == s4);
        }
    }
    

    我们运行起来,发现结果是运行结果:false

    那么根据前面的题目分析,我们知道执行new String(“1”) + new String(“1”)

    字符串常量池中并不会存储"11",当执行s4 = "11"才会在字符串常量池中存在

    而s5 = s3.intern()其实是在常量池寻找是否有"11",若有则返回指向地址给到s5

    此时s3的"11"是存储在堆空间当中的,但s4的"11"是存储在字符串常量池中,所以为false

    小结intern()

    ================================

    JDK 1.6中,将这个字符串对象尝试放入串池。
    • 如果串池中有,则并不会放入。返回已有的串池中的对象的地址
    • 如果没有,会把此对象复制一份,放入串池,并返回串池中的新对象地址
    Jdk1.7起,将这个字符串对象尝试放入串池。
    • 如果串池中有,则并不会放入。返回已有的串池中的对象的地址
    • 如果没有,则会把对象的引用地址复制一份放入串池,并返回串池中的引用地址

    使用intern()测试执行效率

    ================================

    接下来我们测试一下intern()进行执行一下效率

    public class StringIntern2 {
        static final int MAX_COUNT = 1000 * 10000;
        static final String[] arr = new String[MAX_COUNT];
    
        public static void main(String[] args) {
            Integer[] data = new Integer[]{1,2,3,4,5,6,7,8,9,10};
    
            long start = System.currentTimeMillis();
            for (int i = 0; i < MAX_COUNT; i++) {
               arr[i] = new String(String.valueOf(data[i % data.length]));
            }
            long end = System.currentTimeMillis();
            System.out.println("花费的时间为:" + (end - start));
    
            try {
                Thread.sleep(1000000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.gc();
        }
    }
    //运行结果如下:
    花费时间:7307
    

    并且我们通过Java VisualVm工具看看这段代码怎么样呢?
    在这里插入图片描述

    我们再看看使用intern()执行同样的需求,看看它的花费时间是多少呢?

    public class StringIntern2 {
        static final int MAX_COUNT = 1000 * 10000;
        static final String[] arr = new String[MAX_COUNT];
    
        public static void main(String[] args) {
            Integer[] data = new Integer[]{1,2,3,4,5,6,7,8,9,10};
    
            long start = System.currentTimeMillis();
            for (int i = 0; i < MAX_COUNT; i++) {
               arr[i] = new String(String.valueOf(data[i % data.length])).intern();
            }
            long end = System.currentTimeMillis();
            System.out.println("花费的时间为:" + (end - start));
    
            try {
                Thread.sleep(1000000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.gc();
        }
    }
    //运行结果如下:
    花费时间:1311
    

    并且我们通过Java VisualVm工具看看这段代码怎么样呢?
    在这里插入图片描述

    由此可见我们可以对比两个操作

    直接 new String :每个 String 对象都是 new 出来的,所以程序需要维护大量存放在堆空间中的 String 实例,程序内存占用也会变高

    使用 intern() 方法:由于数组中字符串的引用都指向字符串常量池中的字符串,所以程序需要维护的 String 对象更少,内存占用也更低

    结论:

    对于程序中大量使用存在的字符串时,尤其存在很多已经重复的字符串时,使用intern()方法能够节省很大的内存空间。

    大的网站平台,需要内存中存储大量的字符串。比如社交网站,很多人都存储:北京市、海淀区等信息。这时候如果字符串都调用intern() 方法,就会很明显降低内存的大小

    六、String Table的垃圾回收


    我们刚刚演示intern()的执行效率也证明了String 存在垃圾回收,所以试用intern()时更省

    接下来我们再通过下面的代码块来体会一下String的垃圾回收

    public class StringGCTest {
        public static void main(String[] args) {
            for (int j = 0; j < 100000; j++) {
                String.valueOf(j).intern();
            }
        }
    }
    

    使用命令:-Xma15m- -Xnx15m XX:+PrintStringTabIeStat1stIcs -XX:+PrintGCDetaiis查看字符串常量池的信息
    在这里插入图片描述

    我们将循环的操作先注释掉,看看未循环添加时的字符串常量池是怎么样的
    在这里插入图片描述

    这时我们再运行起来看看,进行循环后的字符串常量池是怎么样的
    在这里插入图片描述

    若我们将for循环的次数增加到十万的话,再运行起来是怎么样的呢?
    在这里插入图片描述

    由十万的输出结果我们就可以知道StringTable 区发生了垃圾回收

    • 在 PSYoungGen 区发生了垃圾回收
    • Number of entries 和 Number of literals 明显没有 100000

    七、G1的String去重操作


    对于G1中对于String有去除重复的操作,具体详细可查看官方文档:访问入口

    许多大规模的Java应用的瓶颈在于内存,测试表明在这些类型的应用里面,Java堆中存活的数据集合差不多25%是String对象。更进一步,这里面差不多一半String对象是重复的,重复的意思是说:str1.equals(str2)= true。

    堆上存在重复的String对象必然是一种内存的浪费。这个项目将在G1垃圾收集器中实现自动持续对重复的String对象进行去重,这样就能避免浪费内存

    观看官方文档可以知道在我们的应用中一般的堆空间里

    • 堆存活数据集合里面String对象占了25%
    • 堆存活数据集合里面重复的String对象有13.5%
    • String对象的平均长度是45

    比如说一下代码块,体会一下:

    Strnig str1 =new String("hello");    
    Strnig str2 =new String("hello");
    

    那么对于这种情况,我们G1是怎么操作的呢?

    当垃圾收集器工作的时候会访问堆上存活的对象。对每一个访问的对象都会检查是否它是候选的要去重的String对象如果是:把这个对象的一个引用插入到队列中等待后续的处理

    这时有一个去重的线程在后台运行处理这个队列。处理队列的时候把元素从队列删除这个元素,然后尝试去引用已有一样的String对象

    使用一个Hashtable来记录所有的被String对象使用的不重复的char数组。当去重的时候会查这个Hashtable,来看堆上是否已经存在一个一模一样的char数组

    如果存在String对象会被调整引用那个数组,释放对原来的数组的引用,最终会被垃圾收集器回收掉。如果查找失败,char数组会被插入到Hashtable,这样以后的时候就可以共享这个数组了

    提示:暂时了解一下,后面会详解垃圾回收器

    对于去重的命令选项如下:

    • UseStringDeduplication(bool):开启String去重,默认是不开启的,需要手动开启。
    • PrintStringDeduplicationStatistics(bool):打印详细的去重统计信息
    • stringDeduplicationAgeThreshold(uintx):达到这个年龄的String对象被认为是去重的候选对象

    参考资料


    尚硅谷:JVM虚拟机(宋红康老师)

    下面是本人的公众号:(有兴趣可以扫一下,文章会同步过去)
    在这里插入图片描述

    我是小白弟弟,一个在互联网行业的小白,立志成为一名架构师
    https://blog.csdn.net/zhouhengzhe?t=1

    展开全文
  • 字符串常量放在只读存储区

    千次阅读 2016-07-15 20:29:39
    给出以下定义,下列哪些操作是合法的? 1 2 const char *p1 = “hello”; char *const p2 = “world”; 正确答案: A 你的答案: C (错误) p1...
  • ( )是机器人的支持部分,语言中有固定式和移动式两种,该部件必须具有足够...字符下列程序的执行结果是___________Dim a(5) As StringDim b As IntegerDim I As IntegerFor I = 0 To 5a(I) = I + 1 Print a(I);Next ...
  • 字符串常量

    2017-05-31 16:41:25
    首先理解,如何在Java语言中如何创建字符串。在Java语言中有两种方式创建一个字符串对象 使用new运算符 String str = new String("hello"); StringBuilder str = new StringBuilder("Hello&...
  • c语言常量正确表示方法有哪些

    千次阅读 2021-05-19 11:25:00
    c语言常量正确表示方法有哪些C语言中的数值常量可以简单的分为如下两种整型常量和浮点数常量:一、整型常量 如6,27,-299在 C 语言中,整型常量分为十进制整型常量、八进制整型常量和十六进制整型常量三种表示...
  • 文章目录8_字符串常量池 StringTable8.1_String 的基本特性8.2_String 的内存分配8.3_String 的拼接操作拼接操作与 append 的效率对比8.4_intern() 的使用new String("ab")会创建几个对象?new String("a") + new ...
  • 字符串常量

    千次阅读 2016-12-12 20:19:51
    写出下列程序的输出结果 ...s是指针,指向这个字符串常量,所以声明s的时候就有问题。 cosnt char* s="AAA";然后又因为是常量,所以对是s[0]的赋值操作是不合法的。 编译能够通过,但是运行期间会发生
  • C++常量(C++数值常量、字符串常量、符号常量) 一、 数值常量 数值常量就是通常所说的常数。在C++中,数值常量是区分类型的,从字面形式即可识别其类型。 1. 整型常量(整数)的类型 在上一节中已知道: 整型数据...
  • C语言-字符与字符串常量及变量

    千次阅读 2021-05-19 08:25:08
    字符型数据字符常量:用单引号括起来的一个字符例如:'a' 'A''$''!'注:'a'与'A'是不同的字符常量转义字符:①以一个'\'开头的字符序列例如:'\n'...故要想在字符串中使用它们,须在其前面加上一个反斜线③若反斜线...
  • C语言字符常量常量定义

    千次阅读 2022-02-09 17:12:22
    字符常量可以是一个普通的字符(例如 'x')、一个转义序列(例如 '\t'),或一个通用的字符(例如 '\u02C0')。 在 C 中,有一些特定的字符,当它们前面有反斜杠时,它们就具有特殊的含义,被用来表示如换行符(\n...
  • 在C语言中下列合法的字符常量

    千次阅读 2021-05-25 08:33:16
    2019-10-09阅读(79)8-10...加鸡油法:做清蒸鱼时,除了放好作料外,再把成块鸡油放在鱼肉上面,这样鱼肉2019-10-09阅读(137)C语言中的常量是不接受程序修改的固定值,常量可为任意数据类型,如下例所示:数据类型。C...
  • 字符数组与字符串常量
  • 在宏定义中,当需要把一个宏的参数转换为字符串常量时,则使用字符串常量化运算符(#)。在宏中使用的该运算符有一个特定的参数或参数列表。例如: #include <stdio.h> #define message_for(a, b) \ ...
  • 关于C语言数值常量字符常量

    千次阅读 多人点赞 2019-08-12 16:22:36
    2)字符常量只能是单个字符,不能是字符串。  3)字符可以是字符集中任意字符。但数字被定义为字符型之后就不能参与数值运算。 下面四个选项中,均是正确的数值常量或字符常量的选项是()。 A.0.0 0f 8.9e ‘&’...
  • 一、字符串问题  字符串在我们平时的编码工作中其实用的非常多,并且用起来也比较简单,所以很少有人对其做特别深入的研究。倒是面试或者笔试的时候,往往会涉及比较深入和难度大一点的问题。我在招聘的时候也偶尔...
  • 思考下列表达式代表什么意思??? 1:“zyh_helen”+ 1 2: *“zyh_helen” ...当一个字符串常量出现在表达式中时,他的值是一个指针常量。编译器把这些指定字符的一份拷贝存储在内存中的某个位置,并存
  • s就是字符串常量,"abc"也是字符串常量。字符串里面的字符的值不能再改变了 主串和子串 char s[] = "abcdef";其中s全部的字符串"abcdef",就是主串,其中连续的一部分,例如"abc","bcde","ef"等都可...
  • 关于c语言的字符常量

    千次阅读 2021-01-04 16:40:57
    2.字符常量只能是单个字符,不能为字符串。 3.字符可以是字符集中的任意一个(ASCII)表中。 4.在运算时‘3’的值就变成了其在ASCII表中对应的数值51(其他数字同理)。 例:下列常量中合法的字符常量是(B) A.A B...
  • 当定义的变量为单个字符是,必须外面是单引号,字符用双引号引起。 附:转载OGNL表达式相关的知识点: OGNL的全称是Object Graph Navigation Language(对象图导航语言),它是一种强大的表达式语言,让你...
  • 从零学Java(6)之字面量和常量 从零学Java(5)之关键字和保留字 从零学Java(4)之编程规范 从零学Java(3)之第一个实例HelloWorld 从零学Java(2)之搭建开发环境 从零学Java(1)之面向对象 引言 ♀ 小AD: ♂...
  • C语言字符串长度

    千次阅读 2022-03-10 21:05:40
    在 C 语言中,字符串实际上是使用空字符 \0 结尾的一维字符数组。因此,\0 是用于标记字符串的结束。空字符(Null character)又称结束符,缩写 NUL,是一个数值为 0 的控制字符,\0 是转义字符,意思是告诉编译器,...
  • C++中如何对字符串进行switch

    千次阅读 2021-05-19 07:53:58
    C++中如何对字符串进行switch判断?C++的开关语句,形式为switch(expr1){caseno1:break;caseno2:break;...casenoN:break;default:break;}其功能是对expr1的值做判断,并执行与其相等的case分支,并一直执行到break或...
  • 字符串常量池中是不会存储相同内容的字符串的。 ➢String的String Pool是一个固定大小的Hashtable,默认值大小长度是1009。如果放进String Pool的String非常多, 就会造成Hash冲突严重,从而导致链表会很长,而...
  • [C]字符串数据在C中的存储方式:

    千次阅读 多人点赞 2020-11-27 11:57:19
    内存中的五大区域: 1)栈:是专门用来存储局部变量的,所有的局部变量都是声明在栈区域中 2)堆:允许程序员手动的从堆申请指定字节数的空间来使用 3)BSS段:是用来存储未初始化的全局变量...字符串数据在C语言中有两
  • 字符数组的初始化及字符串的使用

    千次阅读 2020-10-15 18:55:45
    文章目录字符数组的定义字符数组初始化字符串存放'\0'的作用 字符数组的定义 同整型数组一样: char c[10]; char ch[3][4]; 字符数组初始化 定义数组时初始化,如: char c[10]={'a','b','c','d'}; 如果输出格式为...
  • 1[单选题] 下面关于“EOF”的叙述,正确的是A.EOF的值等于0B.EOF是在库函数文件中定义的符号常量C.文本文件和二进制文件都可以用EOF作为文件结束标志D.对于文本文件,fgetc函数读入最后一个字符时,返回值是EOF参考...
  • 目录创建字符串方式1方式2方式3三种方式的内存图方式1 方式2方式3总结理解池的概念回忆引用字符串判断相等判断字符串引用是否相等代码1代码2代码3代码4总结判断字符串内容是否相等变量与变量进行比较字符串常量与...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 51,116
精华内容 20,446
热门标签
关键字:

下列正确的字符串常量