Java基础_字符串
来源:互联网 发布:linux运维简历 编辑:程序博客网 时间:2024/06/06 09:00
Java基础_字符串
标签(空格分隔): java基础
Java核心&Java编程思想读书笔记
Java字符串就是Unicode字符序列。Java没有内置的字符串类型,而是在标准库中提供了一个类 java.lang.String,我们看到的所有被双引号括起来的字符串都是String类的一个实例
创建字符串
创建一个字符串有两种方式
String str = "this is a string"; String s = new String("this is a string too");
最后得到的str以及s都是字符串,但是两者稍微有一些不同
对于前者而言
String str1 = "this is a string"; String str2 = "this is a string"; System.out.println(str1==str2);//true System.out.println(str1.equals(str2));//true
对于后者而言
String str1 = new String("this is a string"); String str2 = new String("this is a string"); System.out.println(str1==str2);//false System.out.println(str1.equals(str2));//true
这是因为前者的创建方式中,JVM会去字符串池(String Pool)查找”this is a string”,这个对象,不存在则创建,存在则直接将地址给予str,这也就是为什么第一种方式即使使用==比较地址也会得到相同的结果,因为它们本身就是指同一个地址
后者的创建方式中使用了new构造器的方式创建字符串,会在堆中创建出”this is a string”对象,再根据string pool是否存在选择性创建,显而易见的是,最后str指向的是堆内存的地址,因此使用==对比时是不相等的。
字符串不可变
String的对象不可变,我们所进行的修改操作其实并没有改变String的对象,而是创建了一个新的String对象。不可变性对于拼接字符串确实带来效率的下降,但是也带来了好处,还是刚才提到的string pool共享池,字符串变量指向池中的对象,大家共享它,这样如果进行复制或者判断之类的操作效率就要高很多。事实证明,对于字符串的修改操作要小于字符串的比较操作。
ps:GC不会回收共享池中的字符串对象
重载+与StringBuilder
String对象不可变,因此我们可以为String对象起任意多的名字(句柄),他们不会影响到String对象本身。
那么回到最开始的问题,假如我们使用+=拼接大量字符串,由于字符串的不可变性,那么就会产生大量的中间对象,而且这些对象始终不会被我们用到,这样一定会大大降低程序的性能,事实真的就是如此么?
创建以下代码
public class StringTest{ public static void main(String args[]){ String str = "my"; String result = str+" name"+" is"+" cjmust."; System.out.println(result);//my name is cjmust. } }
ok,根据我们的分析,上述代码至少创建了两个无用的中间对象,接下来我们看一看它到底是怎么运行的。
首先使用javac 命令将.java文件编译为.class 文件
javac StringTest.java
然后使用javap -c 命令生成JVM字节码
javap -c StringTest
获得字节码文件如下
public class StringTest { public StringTest(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return public static void main(java.lang.String[]); Code: 0: ldc #2 // String my 2: astore_1 3: new #3 // class java/lang/StringBuilder 6: dup 7: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V 10: aload_1 11: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 14: ldc #6 // String name is cjmust. 16: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 19: invokevirtual #7 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 22: astore_2 23: getstatic #8 // Field java/lang/System.out:Ljava/io/PrintStream; 26: aload_2 27: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 30: return}
即使不能完全看懂我们也可以猜一猜发生了什么,其实注释写得很清楚了:new指令只出现了一次,也就是说它可能只创建了一个对象,再根据后面的注释 java.lang.StringBuilder,可以认为JVM创建了一个StringBuilder的对象,然后又调用了该对象的append方法,然后又使用toString()方法转换为String类型之后,输出~
以上例子说明,当我们使用String直接拼接的时候,其实JVM会替我们进行某种程度上的优化。但是不要因为JVM会优化就可以随意使用String类型,尤其是在使用循环拼接字符串的时候,因为每次拼接JVM都会新创建出一个StringBuilder对象,而假如我们自己一开始就使用StringBuilder的append方法的话,那么StringBuilder对象就只有一个
补充:还有一个和StringBuilder类似的类,StringBuffer,该类线程安全,不过性能开销会稍微大些。
无意的递归
留个坑:容器类,集合类
补充:一个常识,所有Java类都继承自Object类,容器类当然也源于Object,又因为容器类中都有重写后的toString()方法,因此直接输出容器类对象而得到的是容器被重写后的结果,一般来说是容器中的内容物,比如我们使用ArrayList的toString()方法,其实就是遍历了ArrayList中的所有内容并且调用它们的toString()方法。
其实我们使用Sysout输出对象时,就是调用了它的toString()方法,那么我们创建一个对象,重写toString()方法时使其输出它的地址。
public class Test { @Override public String toString() { // 重写改类的toString方法,使其输出自己的内存地址 return ("address"+this); }}
当我们创建好对象并输出时,会出现一个Exception Exception in thread "main" java.lang.StackOverflowError
这是因为在遇到一个字符串使用+ this时发生类型转换,编译器要把this转换为String类型,为了转换,又调用了它的toString方法,而再次调用的话又会出现一个+this,又会调用这个this的toString方法…很显然这样会发生递归。
如果真正想要安全地输出地址的话,使用eclipse自动创建出的形式即可,也就是调用Object的toString方法
@Override public String toString() { // TODO Auto-generated method stub return super.toString(); }
空串与Null串
空串是长度为0的字符串
String str = "";
长度为0
if(str.length()==0) //true
无内容
if(str.equals("")) //true
还有Null,内容为空,此时调用调用该字符串的方法会产生空指针异常
String str = null;if(str==null) //true
最为常用的用法,判断一个字符串既不是”“,也不是Null
if(str!=null && str.length()!=0)
注意两个判断不能换位置,将str本身是否为Null放在前面,因为&&运算符短路的原因可以避免空指针异常,许多对象的判断都应该采用这种思路
码点与代码单元
字符串的本质是char值序列,而char数据类型是一个采用UTF-16编码表示Unicode码点的代码单元。
当我们调用length()方法时,返回的数字是UTF-16编码表示给定字符串所需要的代码单元数量,想要获得真正的长度,即码点数量,需要调用codePointCount(int x,int y)方法
String str = "Hello";int trueLength = str.codePointCount(0,str.length());
格式化输出
Java的格式化输出从JDK5开始得到大家的喜爱
System.out.printf();
可以使用多个参数,格式化输出的内容
以下为坑,更新后修正
String API
正则表达式
十六进制转储工具
- Java基础_字符串
- Java基础<八>_字符串
- MySQL_005_基础_字符串
- 数据结构基础_插入字符串
- Java基础_函数
- Java基础_多线程
- Java基础_序
- Java基础_环境变量
- Java基础_标识符
- Java基础_数据类型
- Java基础_变量
- Java基础_多态
- Java基础_接口
- Java基础_异常
- Java基础_数组
- Java基础_容器
- Java基础_泛型
- Java基础_线程
- 入门:登录与退出Mysql服务器
- 玩具电路搭建纪实--为什么国外中学生这么牛逼
- Java wait() notify()方法使用实例讲解
- Android标题栏上菜单的创建
- Java 中的Servlet(四)——页面跳转
- Java基础_字符串
- 数据库设计 基础概念:概念模型、逻辑模型、物理模型、实体、联系等
- Ubuntu下安装php的gd,mysql扩展库
- java 中的内省 introspector
- Object类,String类
- sdnu1105.椭圆
- html5基础学习日记1_超链接
- Junit 测试之 Spring Test
- 9. explain