JavaSE 基础语法 - 认识String类
基本数据类型可以表示整数、小数、字符以及真假等基本数据但在实际的开发中我们处理最多的数据之一其实是字符串。例如学生姓名 用户名 密码 地址 电话号码 电子邮箱 商品名称 文件路径 提示信息这些数据都有一个共同的特点它们本质上都是由多个字符组成的字符串。例如Stringname张三;Stringusernameadmin;Stringaddress北京市;这里我们都使用到了String。那么问题来了String 到底是什么它是数据类型吗它和我们之前学习的 int、double 有什么区别为什么我们可以直接写Stringname张三;而不需要StringnamenewString(张三);还有一个非常经典的问题Strings1Java;Strings2Java;System.out.println(s1s2);为什么有时候两个字符串使用 比较会得到 true而下面的代码却得到 false 。Strings1newString(Java);Strings2newString(Java);System.out.println(s1s2);1、什么是 String1.1 String 是一个类首先需要明确String 不是 Java 的基本数据类型而是一个类。我们之前学习过intdoublecharboolean这些属于 Java 的基本数据类型。而String是一个类。因此Stringname张三;中的 String 本质上是一个引用类型。这和我们前面学习的类和对象有直接关系。例如之前我们自己定义过classStudent{Stringname;intage;}然后StudentstudentnewStudent();这里 Student 是类。而 student 是引用变量。String 也是一样。所以我们可以把字符串理解为String 类的对象。1.2 String 类在哪里String 类属于java.lang包。java.lang.String由于 java.lang 包中的类会被 Java 自动导入所以我们平时不需要写直接使用String即可。2、字符串是如何创建的既然 String 是一个类那么我们应该可以通过 new 创建对象。例如StringnamenewString(张三);这里确实创建了一个 String 对象。但是我们平时更常见的写法却是Stringname张三;为什么这是因为 Java 对 String 提供了特殊的语法支持。2.1 使用字符串字面量创建例如Stringname张三;张三就是一个字符串字面量。Java 会自动将它处理成 String 对象。所以我们平时创建字符串时一般直接写Stringname张三;这种方式更加简单也是实际开发中最常用的方式。2.2 使用 new 创建当然我们也可以StringnamenewString(张三);这种方式会显式创建一个新的 String 对象。但是也有可能是创建两个对象如果常量池中还没有 “张三”会先在常量池中创建一个然后 new 关键字会在堆内存中再创建一个新的 String 对象并复制其内容。因此Strings1Java;和Strings2newString(Java);虽然最终都表示 Java 。但是它们的对象创建方式并不完全相同。这就涉及 String 一个非常重要的知识字符串常量池。3、字符串常量池字符串常量池是学习 String 时非常重要的一个概念。先看下面代码Strings1Java;Strings2Java;如果按照普通对象的思维来理解可能会认为s1是一个 Java 对象s2又是另一个 Java 对象但是 Java 对字符串做了特殊处理。对于字符串字面量JavaJava 会使用字符串常量池对字符串对象进行管理和复用。可以简单理解为因此Strings1Java;Strings2Java;两个变量可能指向常量池中的同一个字符串对象。所以System.out.println(s1s2);//结果为true4、为什么 Java 要设计字符串常量池为什么 Java 要这么设计假设一个程序中有大量相同的字符串JavaJavaJavaJavaJava如果每出现一次都创建一个新的 String 对象就会产生大量重复对象。而 String 有一个非常重要的特点String 对象是不可变的。既然字符串对象创建之后不能被修改那么相同内容的字符串对象就可以安全地进行复用。例如“Java”这个对象不会因为 s1 使用它而被修改也不会因为 s2 使用它而被修改。所以可以多个引用共同指向同一个字符串对象。这样可以减少重复对象提高内存使用效率。5、String 的不可变性String 最重要的特性之一就是String 对象一旦创建其内容就不能改变。例如StringstrHello;现在执行strJava;很多会认为好像是把原来的字符串 “Hello” 修改成了 “Java”。实际上并不是。而是原来的对象 “Hello” 并没有被修改。变量 str 原来指向 “Hello”现在重新指向 “Java”。可以理解为第一次str 指向 “Hello”。重新赋值后str 指向 “Java”。所以改变的是引用变量的指向而不是 String 对象本身。6、字符串拼接为什么会产生新对象继续看一个非常典型的例子StringstrHello;strstr Java;表面上看好像是 Hello Java 实际上 String 并没有直接修改 “Hello”。更准确地理解“Hello” Java 后产生新的字符串 “Hello Java”然后 str重新指向 “Hello Java” 。因此这个代码并不是修改原来的 String 对象而是产生新的字符串。这正是 String 不可变性的体现。7、String 的不可变性有什么好处String 为什么要设计成不可变主要有几个重要原因。第一字符串可以安全共享因为 String 不可变所以Strings1Java;Strings2Java;可以让两个引用共享同一个对象。如果 String 可以被随意修改那么一个变量修改字符串内容之后可能会影响其他引用这就会产生严重的问题。第二可以使用字符串常量池字符串常量池能够大量复用字符串对象很大程度上也是建立在 String 不可变的基础之上的。第三安全性更好字符串经常用于文件路径网络地址类名数据库连接信息用户名权限相关信息如果 String 可以被随意修改就可能造成很多不可预测的问题。所以 Java 将 String 设计成不可变类。8、String 与 现在我们再来看一个非常经典的问题。Strings1Java;Strings2Java;System.out.println(s1s2);//结果为true因为两个字符串字面量可以复用字符串常量池中的同一个对象。所以 s1 s2比较的是两个引用是否指向同一个对象。9、使用 new 创建 String再来看Strings1newString(Java);Strings2newString(Java);System.out.println(s1s2);//结果为false因为newString(Java)会创建新的 String 对象。因此可以理解为s1 指向 String 对象1内容是Javas2 指向 String 对象2内容是Java两个对象的内容相同都为Java但是对象不同。因此 s1 s2 的结果为 false 。10、 和 equals() 的区别这里是 String 学习中必须掌握的重点。我们前面学习过对于引用类型来说比较的是两个引用是否指向同一个对象。而equals()通常用于判断对象的内容是否相等。String 重写了 equals() 方法使它可以按照字符串内容进行比较。例如Strings1newString(Java);Strings2newString(Java);System.out.println(s1s2);//输出falseSystem.out.println(s1.equals(s2));//输出true第一句s1s2比较的是对象由于对象1 不等于 对象2所以结果为false第二句s1.equals(s2)比较字符串内容由于 Java Java 所以结果为true因此一定要记住String 比较内容是否相同要使用 equals()而不是 。11、为什么不能使用 比较字符串内容例如StringusernamenewString(admin);if(usernameadmin){System.out.println(登录成功);}这里的代码存在问题。因为username指向的是通过 new 创建的 String 对象。而admin是字符串常量池中的字符串对象。它们不一定是同一个对象。即使内容都是admin也不能说明usernameadmin成立。正确的方式if(username.equals(admin)){System.out.println(登录成功);}或者更加安全地写if(admin.equals(username)){System.out.println(登录成功);}这样即使usernamenull;也不会因为调用username.equals()而产生空指针异常。12、String 中的 null前面学习对象的时候我们已经知道引用变量可以保存 null。String 当然也不例外。例如Stringnamenull;此时 name 它并没有指向任何 String 对象。12.1 null 和空字符串的区别下面两个代码完全不同Strings1null;Strings2;null表示没有指向任何对象。而表示一个长度为 0 的字符串对象。所以s1 null的结果为 true 。而 s2.length() 的结果为 0 。13、null 调用 String 方法的问题如果Stringnamenull;然后System.out.println(name.length());程序会产生NullPointerException原因很简单name 没有对象自然无法调用对象的方法。这和前面学习引用类型时的知识是一致的。因此开发中要特别注意 String str null;之后不能直接str.length();str.equals(...);str.substring(...);除非已经确认 str 不为 null。14、String 的常用方法理解了 String 的本质以后我们再来学习 String 常用的方法。14.1 length()获取字符串长度。StringstrHello;System.out.println(str.length());//输出514.2 charAt()根据下标获取字符。StringstrJava;System.out.println(str.charAt(0));//输出JSystem.out.println(str.charAt(2));//输出v字符串下标和数组类似也是从 0 开始。Ja v a012314.3 遍历 String有了length()和charAt()就可以遍历字符串。StringstrJava;for(inti0;istr.length();i){System.out.println(str.charAt(i));}/* 输出 J a v a */这和之前学习数组的遍历非常类似。15、String 的字符串拼接Java 中最常用的字符串拼接方式是例如Stringname张三;intage20;Stringinfo姓名name年龄age;System.out.println(info);//输出姓名张三年龄20当运算中出现 String 时其他数据会参与字符串拼接。例如System.out.println(年龄20);//输出年龄2016、字符串拼接中的运算顺序看下面代码System.out.println(12Java);//结果为3Java因为计算顺序是从左到右先计算 1 2然后再将结果与字符串拼接。但是System.out.println(Java12);//结果为Java12因为Java1-Java1Java12-Java12所以字符串拼接中的 运算要特别注意从左到右的计算顺序。17、String 的常用判断方法在实际开发中我们经常需要判断一个字符串。例如StringstrHello Java;判断是否包含str.contains(Java);判断是否以某个内容开头str.startsWith(Hello);判断是否以某个内容结尾str.endsWith(Java);这些方法都会返回boolean类型。例如if(str.contains(Java)){System.out.println(包含 Java);}18、String 的截取String 提供substring()用于截取字符串。例如StringstrHelloJava;Stringresultstr.substring(5);System.out.println(result);//输出Java还可以Stringresultstr.substring(0,5);//结果为Hello这里需要注意开始下标包含结束下标不包含。也就是[beginIndex, endIndex)这和数组下标的很多处理规则是一致的。19、String 的查找与替换可以使用indexOf()查找字符串第一次出现的位置。例如StringstrHello Java;System.out.println(str.indexOf(Java));//输出 6如果没有找到System.out.println(str.indexOf(Python));//输出 - 1还可以使用replace()进行替换StringstrHello Java;Stringresultstr.replace(Java,World);System.out.println(result);// 输出Hello World需要注意replace() 并没有修改原来的 String 对象而是返回了一个新的字符串。这仍然是 String 不可变性的体现。20、String 的大小写转换String 提供toUpperCase()转换为大写Stringstrjava;System.out.println(str.toUpperCase());// 结果为JAVA也可以使用toLowerCase()转换为小写StringstrJAVA;System.out.println(str.toLowerCase());//结果为java同样这些方法不会修改原来的字符串而是返回新的字符串。21、String 与基本数据类型之间的转换实际开发中经常需要在 String 和基本数据类型之间进行转换。例如intage20;转换成 StringStringstrString.valueOf(age);此时age-20int str-20String21.1 String 转 int如果Stringstr20;可以这样intageInteger.parseInt(str);最终得到的是 int 类型的数据。类似地还有Double.parseDouble()Boolean.parseBoolean()Long.parseLong()等方法。22、String 与 char[] 之间的转换String 还可以和字符数组进行转换。例如StringstrJava;char[]charsstr.toCharArray();得到Ja v a也可以把字符数组转换成 Stringchar[]chars{J,a,v,a};StringstrnewString(chars);这在字符串处理过程中非常常见。23、综合案例用户登录现在完成一个简单的登录案例。假设系统中保存了Stringusernameadmin;Stringpassword123456;用户输入StringinputUsernameadmin;StringinputPassword123456;判断用户名和密码是否正确publicclassLoginDemo{publicstaticvoidmain(String[]args){Stringusernameadmin;Stringpassword123456;StringinputUsernameadmin;StringinputPassword123456;if(username.equals(inputUsername)password.equals(inputPassword)){System.out.println(登录成功);}else{System.out.println(用户名或密码错误);}}}运行结果登录成功这里最重要的不是登录功能本身而是 equals() 的使用。因为我们需要判断的是用户名的内容是否相同。而不是两个 String 是否为同一个对象。所以这里应该使用 equals() 而不是 。24、综合案例统计字符串中的字符再来看一个字符串处理案例。要求统计字符串中字符 a 出现的次数。例如StringstrJava is a programming language;可以使用publicclassStringDemo{publicstaticvoidmain(String[]args){StringstrJava is a programming language;intcount0;for(inti0;istr.length();i){if(str.charAt(i)a){count;}}System.out.println(a 出现的次数count);}}25、String 的内存理解现在把本篇最重要的几个知识点串起来。例如Strings1Java;Strings2Java;Strings3newString(Java);Strings4newString(Java);可以使用简化的内存模型理解所以 s1 s2 的结果为true而 s3 s4 的结果为false但是 s3.equals(s4) 的结果为true因为s3 的内容是 Javas4 的内容也是 Java 。