API (Application Programming Interface) :应用程序编程接口,就是别人写好的一些类,给咱们程序员直接拿去调用即可解决问题的
我们前面已经学过的 API 有 Scanner、Random。
但是 Java 中还有很多我们没有学习的类,下图仅一小部分:
但要我们要记住这些类是比较困难的。
这里我们可以使用 Java API 帮助文档来帮助我们使用这些类。
API 帮助文档使用流程:
在索引位置搜索自己要查看的类
看包
目的:是 java.lang包(核心包),不需要导包代码(import);不是java.lang包,都需要编写导包代码。
看这个类的介绍
目的:搞清楚这个类的作用
看这个类的构造方法
目的:为了将该类的对象,创建出来
看这个类的成员方法(方法摘要)
字符串在开发中的应用场景:
比如,我们打开浏览器,登录 b站,需要输入账号名、密码、验证码等,这些就使用到了字符串。
在游戏中的语言屏蔽机制
玩家在输入时文字时语言不文明:
可以对特定的字符串进行替换:
package cn.edu.hgu.string;public class StringDemo1 {public static void main(String[] args) {String s = "abc";System.out.println(s.toUpperCase());System.out.println("helloworld".toUpperCase());}
}
输出结果为:
如果想要更改,只能使用新的对象,做替换。
package cn.edu.hgu.string;public class StringDemo1 {public static void main(String[] args) {String s = "abc";System.out.println(s.hashCode());//输出对象的哈希码s = "def";System.out.println(s.hashCode());}
}
输出结果为:
我们可以看到,两个数的值是不相同的,说明对象进行了替换。
下面我们来引入一个概念:字符串常量池
字符串常量池:当我们使用双引号创建字符串对象时,会检查常量池中是否存在该数据
- 不存在:创建
- 存在:复用
我们通过代码来看一下:
package cn.edu.hgu.string;public class StringDemo1 {public static void main(String[] args) {String s1 = "abc";System.out.println(s1.hashCode());//输出对象的哈希码String s2 = "abc";System.out.println(s2.hashCode());}
}
构造方法 | 说明 |
---|---|
public String() | 创建空白字符串,不含任何内容 |
public String(String original) | 根据传入的字符串,创建字符串对象 |
public String(char[] chs) | 根据字符数组,创建字符串对象 |
下面我们来通过代码来使用一下这三个构造方法:
package cn.edu.hgu.string;public class StringDemo2 {public static void main(String[] args) {// public String():创建空白字符串,不含任何内容String s1 = new String();System.out.println(s1);// public String(char[] chs):根据传入的字符数组,创建字符串对象char[] chs = {'a', 'b', 'c'};String s2 = new String(chs);System.out.println(s2);// public String(String original):根据传入的字符串,来创建字符对象String s3 = new String("qwer");System.out.println(s3);}
}
输出结果为:
注意事项:
这三个构造方法,创建字符串对象,都没有双引号直接创建来的方便。
String s = “abc”;
打印对象名,会看到对象的内存地址,这里打印字符串对象,为什么没有看到地址值?
面试题1:下面代码结果为:
public class StringTest1 {public static void main(String[] args) {String s1 = "abc";String s2 = "abc";System.out.println(s1 == s2);//true}
}
因为两个变量使用的都是在字符变量池中的同一个变量地址。
面试题2:下面代码结果为:
public class StringTest1 {public static void main(String[] args) {String s1 = "abc";String s2 = new String("abc");System.out.println(s1 == s2);//false}
}
s1使用的是字符串中的地址,而s2会在堆内存中新开辟一块内存地址,两个地址是不一样的。
面试题3:下面代码结果为:
public class StringTest1 {public static void main(String[] args) {String s1 = "abc";String s2 = "ab";String s3 = s2 + "c";System.out.println(s1 == s3);//false}
}
s1使用的是在字符串常量池中的地址,字符串进行拼接时会使用 StringBuilder 类,拼接完成后会使用 toString 方法再堆内存中开辟一块地址,两个地址是不相同的。
面试题4:下面代码结果为:
public class StringTest1 {public static void main(String[] args) {String s1 = "abc";String s2 = "a" + "b" + "c";System.out.println(s1 == s2);//true}
}
前面讲了字符串在拼接时会调用 StringBuilder 类进行拼接,那为什么结果不对呢?
String s2 = “a” + “b”+“c”;编译器将这个"a" + “b”+“c"作为常量表达式,在编译时进行优化,直接取表达式结果"abc”,这里没有创建新的对象,而是从JVM字符串常量池中获取之前已经存在的"abc"对象。因此a,b具有对同一个string对象的引用,两个引用相等,结果true。
public boolean equals方法(要比较的字符串) 完全一样结果才是true,否则为false public boolean equalsIgnoreCase(要比较的字符串) 忽略大小写的比较
下面我们通过案例来学习这两个用于比较的方法:
package cn.edu.hgu.string.method;public class StringMethodDemo1 {public static void main(String[] args) {String s1 = "abc";String s2 = new String("abc");System.out.println(s1 == s2);//falseSystem.out.println(s1.equals(s2));//trueSystem.out.println("--------------");String ss1 = "abc";String ss2 = "ABC";System.out.println(ss1.equals(ss2));//falseSystem.out.println(ss1.equalsIgnoreCase(ss2));//true}
}
输出结果为:
案例:用户登录
需求:已知正确的用户名和密码,请用程序实现模拟用户登录。总共给三次机会,登录之后,给出相应的提示。
分析:
代码实现:
package cn.edu.hgu.test;import java.util.Scanner;public class StringTest1 {public static void main(String[] args) {Scanner sc = new Scanner(System.in);// 1.定义两个字符串类型变量,模拟已经存在的用户名和密码String username = "admin";String password = "123456";for (int i = 1; i <= 3; i++) {// 2. 键盘录入用户输入的用户名,密码System.out.println("请输入用户名:");String inputUsername = sc.nextLine();System.out.println("请输入密码:");String inputPassword = sc.nextLine();// 3.比对if (inputUsername.equals(username) && inputPassword.equals(password)) {System.out.println("登录成功!");break;} else {if (i == 3) {System.out.println("明儿再来吧~");} else {System.out.println("登陆失败,您还剩余" + (3 - i) + "次机会");}}}}
}
输出结果为:
public char[] toCharArray () 将字符串转换为字符数组
public char chatAt (int index) 根据索引找字符
public int length() : 返回字符串的长度
下面我们来简单了解一下这几个方法的使用:
package cn.edu.hgu.string.method;public class StringMethodDemo2 {public static void main(String[] args) {String s = "itheima";char[] chars = s.toCharArray();for (int i = 0; i < chars.length; i++) {System.out.println(chars[i]);}System.out.println("-----------------");for (int i = 0; i < s.length(); i++) {char c = s.charAt(i);System.out.println(c);}}
}
输出结果为:
案例:统计字符次数
需求 : 键盘录入一个字符串,统计该字符串中大写字母字符,小写字母字符,数字字符出现的次数(不考虑其他字符)
例如 : aAb3&c2B*4CD1
小写字母 : 3个
大写字母 : 4个
数字字母 : 4个
代码实现:
package cn.edu.hgu.test;import java.util.Scanner;public class StringTest2 {public static void main(String[] args) {// 1. 键盘录入一个字符串Scanner sc = new Scanner(System.in);System.out.println("请输入:");String content = sc.nextLine();// 2. 定义三份计数器变量,用于统计操作int smallCount = 0;int bigCount = 0;int numCount = 0;// 3.遍历字符串,获取到每一个字符char[] chars = content.toCharArray();for (int i = 0; i < chars.length; i++) {char c = chars[i];// 4.在遍历的过程中,加入 if 判断,看字符属于哪一种类别if (c >= 'a' && c <= 'z') {// 5.对应的计数器变量自增smallCount++;} else if (c >= 'A' && c <= 'Z') {bigCount++;} else if (c >= '0' && c <= '9') {numCount++;}}// 6.在遍历结束后,将统计好的计数器变量,打印在控制台System.out.println("小写字母:" + smallCount + "个");System.out.println("大写字母:" + bigCount + "个");System.out.println("数字字符:" + numCount + "个");}
}
输出结果为:
public String substring(int beginIndex, int endIndex) 截取注意点:包头不包尾,包左不包右只有返回值才是截取的小串
public String substring(int beginIndex) 截取到末尾
下面我们来了解一下这两个方法:
package cn.edu.hgu.string.method;public class StringMethodDemo3 {public static void main(String[] args) {String s = "itheima";String result1 = s.substring(2);System.out.println(result1);System.out.println("----------");String result2 = s.substring(0, 2);System.out.println(result2);}
}
输出结果为:
注意:截取出来的内容,是作为新的字符串返回,别忘记找变量接收
案例:手机号屏蔽
需求:以字符串的形式从键盘接受一个手机号,将中间四位号码屏蔽,最终效果为:156****1234
分析:
代码实现:
package cn.edu.hgu.test;import java.util.Scanner;public class StringTest3 {public static void main(String[] args) {// 1.键盘录入字符串Scanner sc = new Scanner(System.in);System.out.println("请输入手机号:");String tel = sc.nextLine();// 2. 截取前三位String start = tel.substring(0, 3);// 3. 截取后四位String end = tel.substring(7);// 4. 前三位 + "****" +后四位System.out.println(start + "****" + end);}
}
输出结果为:
public String replace(旧值,新值) 替换注意点:返回值才是替换之后的结果
下面我们来学习一下这个方法:
package cn.edu.hgu.string.method;public class StringMethodDemo4 {public static void main(String[] args) {String s = "itheima";System.out.println("原字符串为:" + s);String result = s.replace("heima", "baima");System.out.println("替换后的字符串为:" + result);}
}
输出结果为:
案例:敏感词替换
需求:键盘录入一个 字符串,如果字符串中包含(TMD),则使用 *** 替换
示例:
辅助你 TMD 是不是有病, 你出无尽干什么 ?
辅助你 *** 是不是有病, 你出无尽干什么 ?
代码实现:
package cn.edu.hgu.test;import java.util.Scanner;public class StringTest4 {/*需求:键盘录入一个字符串,如果字符串中包含(TMD),则使用 *** 替换*/public static void main(String[] args) {Scanner sc = new Scanner(System.in);System.out.println("请输入:");String content = sc.nextLine();content = content.replace("TMD", "***");System.out.println(content);}
}
输出结果为:
public String[] split(String regex) :根据传入的字符串作为规则进行切割将切割后的内容存入字符串数组中,并将字符串数组返回
下面我们来学习一下字符串的切割方法:
package cn.edu.hgu.string.method;public class StringMethodDemo5 {public static void main(String[] args) {String s1 = "192,168,1,1";String[] sArr1 = s1.split(",");for (int i = 0; i < sArr1.length; i++) {System.out.println(sArr1[i]);}String s2 = "192.168.1.1";String[] sArr2 = s2.split("\\.");for (int i = 0; i < sArr2.length; i++) {System.out.println(sArr2[i]);}}
}
输出结果为:
建议:先正常指定切割规则,后来发现没有得到自己要的效果,就可以尝试在规则前面,加入 \
public boolean equals方法 (要比较的字符串) : 比较内容
public boolean equalsIgnoreCase (要比较的字符串) : 比较内容, 忽略大小写
public char[] toCharArray () 将字符串转换为字符数组
public char chatAt (int index) 根据索引找字符
public int length() : 返回字符串的长度
public String substring(int beginIndex) 截取到末尾
public String substring(int beginIndex, int endIndex) 根据开始和结束索引做截取, 包含头不包含尾
public String replace(旧值,新值) 替换
public String[] split(String regex) :切割
提高字符串操作效率
我们通过一段代码来看一下是否能提高操作效率:
package cn.edu.hgu.stringbuilder;public class StringBuilderDemo1 {public static void main(String[] args) {long time1 = getTime1();long time2 = getTime2();System.out.println("第一次用时:" + time1 + "毫秒");System.out.println("第二次用时:" + time2 + "毫秒");}private static long getTime2() {long start = System.currentTimeMillis();StringBuilder sb = new StringBuilder();for (int i = 1; i <= 10000; i++) {sb.append(i);}System.out.println(sb);long end = System.currentTimeMillis();long time = end - start;return time;}private static long getTime1() {// 获取1970年1月1日0分0秒到现在所经历的毫秒值(1秒 = 1000毫秒)long start = System.currentTimeMillis();String s = "";for (int i = 1; i <= 10000; i++) {s += i;}System.out.println(s);long end = System.currentTimeMillis();long time = end - start;return time;}
}
输出结果为:
可以看到,使用 StringBuilder 类用时明显少于 String 类。
StringBuilder是一种可变的字符序列
package cn.edu.hgu.stringbuilder;public class StringBuilderDemo2 {public static void main(String[] args) {StringBuilder sb = new StringBuilder();System.out.println(sb);sb.append("红色");System.out.println(sb);sb.append("绿色");System.out.println(sb);sb.append("蓝色");System.out.println(sb);}
}
输出结果为:
StringBuilder是字符串的缓冲区, 我们可以将其理解为是一种容器,这个容器可以存储任意数据类型,但只要进入到这个容器,全部变成字符串。
package cn.edu.hgu.stringbuilder;public class StringBuilderDemo2 {public static void main(String[] args) {StringBuilder sb = new StringBuilder();sb.append(100);sb.append(45.6);sb.append(false);sb.append('中');sb.append("黑马程序员");System.out.println(sb);}
}
输出结果为:
构造方法 | 说明 |
---|---|
public StringBuilder() | 创建一个空的字符串缓冲区(容器),其初始容量为16个字符 |
public StringBuilder(String str) | 创建一个字符串缓冲区, 并初始化好指定的参数内容 |
方法名 | 说明 |
---|---|
public StringBuilder append (任意类型) | 添加数据,并返回对象本身 |
public StringBuilder reverse() | 反转容器中的内容 |
public int length() | 返回长度 ( 字符出现的个数) |
public String toString() | 通过toString()就可以实现把StringBuilder转换为String |
添加数据,并返回对象本身
package cn.edu.hgu.stringbuilder;public class StringBuilderDemo3 {public static void main(String[] args) {StringBuilder sb = new StringBuilder();StringBuilder sb2 = sb.append("绿色");StringBuilder sb3 = sb.append("红色");StringBuilder sb4 = sb.append("蓝色");System.out.println(sb == sb2);System.out.println(sb2 == sb3);System.out.println(sb3 == sb4);System.out.println(sb4 == sb);}
}
输出结果为:
因为该方法返回对象本身,那么我们可以使用链式编程来简化代码:
package cn.edu.hgu.stringbuilder;public class StringBuilderDemo3 {public static void main(String[] args) {StringBuilder sb = new StringBuilder();// 链式编程:调用的方法,返回的结果是对象,就可以继续向下调用方法sb.append("绿色").append("红色").append("蓝色");System.out.println(sb);}
}
反转容器中的内容
package cn.edu.hgu.stringbuilder;public class StringBuilderDemo3 {public static void main(String[] args) {StringBuilder sb = new StringBuilder();// 链式编程:调用的方法,返回的结果是对象,就可以继续向下调用方法sb.append("绿色").append("红色").append("蓝色");System.out.println(sb);sb.reverse().append("你好");System.out.println(sb);}
}
输出结果为:
返回长度
package cn.edu.hgu.stringbuilder;public class StringBuilderDemo3 {public static void main(String[] args) {StringBuilder sb = new StringBuilder();// 链式编程:调用的方法,返回的结果是对象,就可以继续向下调用方法sb.append("绿色").append("红色").append("蓝色");System.out.println(sb);System.out.println(sb.length());//6}
}
通过toString()就可以实现把StringBuilder转换为String
package cn.edu.hgu.stringbuilder;public class StringBuilderDemo3 {public static void main(String[] args) {StringBuilder sb = new StringBuilder();// 链式编程:调用的方法,返回的结果是对象,就可以继续向下调用方法sb.append("绿色").append("红色").append("蓝色");System.out.println(sb);// 情况:我数据在StringBuilder当中,我要调用的方法,StringBuilder没有,但String有// 解决:转换为String类,再调用String[] sArr = sb.toString().split("色");for (int i = 0; i < sArr.length; i++) {System.out.println(sArr[i]);}}
}
需求:键盘接受一个字符串,程序判断出该字符串是否是对称字符串(回文字符串),并在控制台打印是或不是
对称字符串:123321、111
非对称字符串:123123
思路:对拿到的字符串进行反转,反转后的内容,根原数据相同,判定为回文字符串
代码实现:
package cn.edu.hgu.test;import java.util.Scanner;public class StringBuilderTest1 {/*需求:键盘接受一个字符串,程序判断出该字符串是否是对称字符串(回文字符串),并在控制台打印是或不是*/public static void main(String[] args) {Scanner sc = new Scanner(System.in);System.out.println("请输入一个字符串:");String content = sc.nextLine();// 将String类型转换为StringBuilder类型,为了调用内部反转的方法StringBuilder sb = new StringBuilder(content);String s = sb.reverse().toString();// 判断反转后的内容,和原数据是否相同if (content.equals(s)) {System.out.println("是对称字符串");} else {System.out.println("不是对称字符串");}}
}
输出结果为:
String类型和StringBuilder类型互相转换:
String ---> StringBUilderString s = "abc";StringBuilder sb = new Stringbuilder(sb); StringBuilder ---> StringString s = sb.toString();
需求:定义一个方法,把 int 数组中的数据按照指定的格式拼接成一个字符串返回。
调用该方法,并在控制台输出结果。
例如:数组为int[] arr = {1,2,3};
执行方法后的输出结果为:[1, 2, 3]
代码实现:
package cn.edu.hgu.test;public class StringBuilderTest2 {public static void main(String[] args) {int[] arr = {1, 2, 3};StringBuilder result = arrayToString(arr);System.out.println(result);}private static StringBuilder arrayToString(int[] arr) {// 1.创建StringBuilder,准备进行拼接StringBuilder sb = new StringBuilder("[");// 2.遍历数组,获取内部元素for (int i = 0; i < arr.length - 1; i++) {// 3.将获取到的元素,拼接到字符串缓冲区sb.append(arr[i]).append(", ");}// 4.特殊处理最后一个元素sb.append(arr[arr.length - 1]).append("]");return sb;}}
输出结果为:
参考文章:
Java中的常量优化机制
c";
StringBuilder sb = new Stringbuilder(sb);
StringBuilder —> String
String s = sb.toString();
需求:定义一个方法,把 int 数组中的数据按照指定的格式拼接成一个字符串返回。
调用该方法,并在控制台输出结果。
例如:数组为int[] arr = {1,2,3};
执行方法后的输出结果为:[1, 2, 3]
代码实现:
package cn.edu.hgu.test;public class StringBuilderTest2 {public static void main(String[] args) {int[] arr = {1, 2, 3};StringBuilder result = arrayToString(arr);System.out.println(result);}private static StringBuilder arrayToString(int[] arr) {// 1.创建StringBuilder,准备进行拼接StringBuilder sb = new StringBuilder("[");// 2.遍历数组,获取内部元素for (int i = 0; i < arr.length - 1; i++) {// 3.将获取到的元素,拼接到字符串缓冲区sb.append(arr[i]).append(", ");}// 4.特殊处理最后一个元素sb.append(arr[arr.length - 1]).append("]");return sb;}}
输出结果为:
参考文章:
Java中的常量优化机制