• Java学习笔记(二):String


    String

    在Java中String是作为引用对象存在的一种数据类型,用来保存字符串。

    实例化和赋值

    //直接声明
    String s1 = "Hello world!";
    //通过构造函数创建, 提供了 11 种不同参数创建的方法
    char[] c = { 'h', 'e', 'l', 'l', 'o', '.'};
    String s2 = new String(c);

    String类是不可改变的,所以你一旦创建了String对象,那它的值就无法改变了。如果需要对字符串做很多修改,那么应该选择使用StringBuffer或StringBuilder类。

    我们来看下面的另一种创建方法:

    使用这种方式来创建字符串,在堆内存中会创建两个字符串对象,因为当使用""符号是就表示创建了一个新的字符串,而str只指向了第二个对象,导致第一个对象没有引用,处于等待回收的状态,除了加大内存使用外,还增加了GC的负担,所以直接使用""来创建字符串是更好的方式。

    连接字符串

    String类提供了连接两个字符串的方法:

    string1.concat(string2);

    返回string2连接string1的新字符串。也可以对字符串常量使用concat()方法,如:

    "My name is ".concat("Zara");

    更常用的是使用'+'操作符来连接字符串,如:

    "Hello," + " world" + "!"

    结果如下:

    "Hello, world!"

    下面是一个例子:

    1 public class StringDemo {
    2    public static void main(String args[]) {     
    3    String string1 = "saw I was ";     
    4    System.out.println("Dot " + string1 + "Tod");  
    5 }
    6 }

    以上实例编译运行结果如下:

    Dot saw I was Tod

    比较字符串

    判断字符串内容是否相等,Java和大部分语言不一样,Java中==是比较两个字符串引用的地址是否相同,即是否指向同一个对象,而equals方法则比较字符串的内容是否相同。

    String a = "abc";
    String b = new String("abc");

    a == b返回false,a.equals(b)返回true。这时创建b时不管"abc"是否存在都会new一个新的"abc",从而a和b指向的字符创对象是不同的,因此返回false。

    我们来看下面的情况,容易混淆:

    String a = "abc";
    String b = "abc";

    a == b返回true,a.equals(b)同样返回true,这是为什么呢?

    原来程序在运行时有一个字符串池,创建字符串时会先查找池中是否有相应的字符串,如果已经存在的话只需把引用指向它即可,如果没有则新建一个。

    上例中创建a时,会在字符串池中首先创建一个"abc",然后a指向它;创建b时,由于"abc"已经存在,b直接指向它即可。

    格式化字符串

    String类的format()方法用于创建格式化的字符串以及连接多个字符串对象,

    format()方法有两种重载形式:

    • format(String format, Object... args) 新字符串使用本地语言环境,制定字符串格式和参数生成格式化的新字符串。
    • format(Locale locale, String format, Object... args) 使用指定的语言环境,制定字符串格式和参数生成格式化的字符串。

    显示不同转换符实现不同数据类型到字符串的转换,如图所示:

    转  换  符

    说    明 

    示    例

    %s

    字符串类型

    "mingrisoft"

    %c

    字符类型

    'm'

    %b

    布尔类型

    true

    %d

    整数类型(十进制)

    99

    %x

    整数类型(十六进制)

    FF

    %o

    整数类型(八进制)

    77

    %f

    浮点类型

    99.99

    %a

    十六进制浮点类型

    FF.35AE

    %e

    指数类型

    9.38e+5

    %g

    通用浮点类型(f和e类型中较短的)

     

    %h

    散列码

     

    %%

    百分比类型

    %n

    换行符

     

    %tx

    日期与时间类型(x代表不同的日期与时间转换符

     

    测试用例:

     1 public static void main(String[] args) {  
     2     String str=null;  
     3     str=String.format("Hi,%s", "王力");  
     4     System.out.println(str);  
     5     str=String.format("Hi,%s:%s.%s", "王南","王力","王张");            
     6     System.out.println(str);                           
     7     System.out.printf("字母a的大写是:%c %n", 'A');  
     8     System.out.printf("3>7的结果是:%b %n", 3>7);  
     9     System.out.printf("100的一半是:%d %n", 100/2);  
    10     System.out.printf("100的16进制数是:%x %n", 100);  
    11     System.out.printf("100的8进制数是:%o %n", 100);  
    12     System.out.printf("50元的书打8.5折扣是:%f 元%n", 50*0.85);  
    13     System.out.printf("上面价格的16进制数是:%a %n", 50*0.85);  
    14     System.out.printf("上面价格的指数表示:%e %n", 50*0.85);  
    15     System.out.printf("上面价格的指数和浮点数结果的长度较短的是:%g %n", 50*0.85);  
    16     System.out.printf("上面的折扣是%d%% %n", 85);  
    17     System.out.printf("字母A的散列码是:%h %n", 'A');  
    18 }  

    输出结果:

     1 Hi,王力  
     2 Hi,王南:王力.王张  
     3 字母a的大写是:A   
     4 3>7的结果是:false   
     5 100的一半是:50   
     6 100的16进制数是:64   
     7 100的8进制数是:144   
     8 50元的书打8.5折扣是:42.500000 9 上面价格的16进制数是:0x1.54p5   
    10 上面价格的指数表示:4.250000e+01   
    11 上面价格的指数和浮点数结果的长度较短的是:42.5000   
    12 上面的折扣是85%   
    13 字母A的散列码是:41   

    String、StringBuffer和StringBuider

    首先,通过阅读Java源码我们可以发现String类是被final标记为不能继承的类,其内部是使用一个char数组来记录整个字符串的值,而String类的大部分改变数据的方法(如substring、concat和replace等),都是直接创建一个新的String对象改变后返回,所以我们说String的数据是不能更改的,实际上Java也没有为String设计更改其内部char数组的方法。

    在这里要永远记住一点:“对String对象的任何改变都不影响到原对象,相关的任何change操作都会生成新的对象”。

    看个例子

     1 public class Main {
     2          
     3     public static void main(String[] args) {
     4         String str1 = "hello world";
     5         String str2 = new String("hello world");
     6         String str3 = "hello world";
     7         String str4 = new String("hello world");
     8          
     9         System.out.println(str1==str2);
    10         System.out.println(str1==str3);
    11         System.out.println(str2==str4);
    12     }
    13 }

    输出的结果是:

    1 false
    2 true
    3 false

    为什么会出现这样的结果?下面解释一下原因:

    对于JVM内存机制,在class文件中有一部分 来存储编译期间生成的 字面常量以及符号引用,这部分叫做class文件常量池,在运行期间对应着方法区的运行时常量池。

    因此在上述代码中,String str1 = "hello world";和String str3 = "hello world"; 都在编译期间生成了字面常量和符号引用,运行期间字面常量"hello world"被存储在运行时常量池(当然只保存了一份)。通过这种方式来将String对象跟引用绑定的话,JVM执行引擎会先在运行时常量池查找是否存在相同的字面常量,如果存在,则直接将引用指向已经存在的字面常量;否则在运行时常量池开辟一个空间来存储该字面常量,并将引用指向该字面常量。

    总所周知,通过new关键字来生成对象是在堆区进行的,而在堆区进行对象生成的过程是不会去检测该对象是否已经存在的。因此通过new来创建对象,创建出的一定是不同的对象,即使字符串的内容是相同的。

    StringBuilder

    我们看一下下面的例子:

    1 public class Main {
    2     public static void main(String[] args) {
    3         String string = "";
    4         for(int i=0;i<10000;i++){
    5             string += "hello";
    6         }
    7     }
    8 }

    我们知道,每次对String进行连接操作都是重新生成一个新的String,那么这段代码运行后会创建10001个String对象,而存在引用的仅1个String对象,其他对象会被当做垃圾回收,造成了内存和CPU的浪费。

    Java为了解决这种情况,设计了StringBuilder,StringBuilder可以直接修改其内部记录的char数组,在改变字符串时不会生成多余的对象,如下:

    1 public class Main {
    2     public static void main(String[] args) {
    3         StringBuilder stringBuilder = new StringBuilder();
    4         for(int i=0;i<10000;i++){
    5             stringBuilder.append("hello");
    6         }
    7     }
    8 }

    得到的结果和上面使用String的方法一致,但避免了内存和CPU资源的浪费。

    StringBuffer

    实际上StringBuffer提供的功能和StringBuilder是一样的,唯一的不同就是StringBuilder是线程不安全的,只能在单线程中使用,而StringBuffer是线程安全的,可以在多线程中使用。

  • 相关阅读:
    sort color (荷兰国旗)
    先序遍历和后序遍历构建二叉树
    二叉树的遍历
    排序
    内存相关内容
    chrome控制台console方法表
    记一次移动端CSS引发的小Bug
    JavaScript的事件
    浅谈webpack打包原理
    JS模块化进程
  • 原文地址:https://www.cnblogs.com/hammerc/p/5199256.html
Copyright © 2020-2023  润新知