什么是API
API (Application Programming Interface) :应用程序编程接口
java中的API
指的就是 JDK 中提供的各种功能的 Java类,这些类将底层的实现封装了起来,我们不需要关心这些类是如何实现的,只需要学习这些类如何使用即可,我们可以通过帮助文档来学习这些API如何使用。
其实之前我们已经学过两个API了,只是不知道叫什么而已,比如Scanner和Random这两个类就是java中比较常用的API。
打开帮助文档
最左边那个,没有的同学自行下载。
找到索引选项卡中的输入框
在输入框中输入Random
看类在哪个包下,可知这个类在java.util这个包下。
看类的描述
看构造方法
看成员方法,最左边的那一列表示方法的返回值。
知识点回顾:调用方法的时候,如果方法有明确的返回值,我们用变量接受,可以手动完成,也可以用快捷方式完成,快捷键为:Ctrl+Alt+V,这个快捷键前面讲过了,这里是温习。
String 类代表字符串,Java 程序中的所有字符串文字(例如“abc”)都被实现为此类的实例。也就是说,Java 程序中所有的双引号字符串,都是 String 类的对象。String 类在 java.lang 包下,所以使用的时候不需要导包!
字符串不可变,它们的值在创建后不能被更改
虽然 String 的值是不可变的,但是它们可以被共享
字符串效果上相当于字符数组( char[] ),但是底层原理是字节数组( byte[] )
常用的构造方法
方法名 | 说明 |
---|---|
public String() | 创建一个空白字符串对象,不含有任何内容 |
public String(char[] chs) | 根据字符数组的内容,来创建字符串对象 |
public String(byte[] bys) | 根据字节数组的内容,来创建字符串对象 |
String s = “abc”; | 直接赋值的方式创建字符串对象,内容就是abc |
示例代码
public class StringDemo01 {
public static void main(String[] args) {
//public String():创建一个空白字符串对象,不含有任何内容
String s1 = new String();
System.out.println("s1:" + s1);
//public String(char[] chs):根据字符数组的内容,来创建字符串对象
char[] chs = {'a', 'b', 'c'};
String s2 = new String(chs);
System.out.println("s2:" + s2);
//public String(byte[] bys):根据字节数组的内容,来创建字符串对象
byte[] bys = {97, 98, 99};
String s3 = new String(bys);
System.out.println("s3:" + s3);//要先转换为对应的ASCII码对应的字符在赋值给该String对象
//String s = “abc”; 直接赋值的方式创建字符串对象,内容就是abc
String s4 = "abc";
System.out.println("s4:" + s4);
}
}
运行结果:
尤其要注意第三行的结果,它是先把字节数组里的十进制数字先转换为对应的ASCII码对应的字符,然后再赋值给String变量。其次是注意第一行,我们之前在自己设计定义类的时候,打印这个类创建的对象名的时候打印的是一个地址,而在String类里则不是,它打印的是这个对象名所指的内容。如下:
这是为什么呢?这是因为String类定义的变量可以用于指向字符串对象,然后操作该字符串,它并不是一个地址。这个是和自己设计的类最大的区别之一。
通过构造方法创建
通过 new 创建的字符串对象,每一次 new 都会申请一个内存空间,虽然内容相同,但是地址值不同,前面熟悉内存分配就知道怎么理解了。
直接赋值方式创建
以" "方式给出的字符串,只要字符序列相同(顺序和大小写),无论在程序代码中出现几次,JVM 都只会建立一个 String 对象,并在字符串池中维护。
上面亮点是什么意思可能有人不是很明白,我举一个例子:
在内存中,虽然a和b创建的字符序列相同,但是在内存中是属于不同的空间,而c和d赋值的字符串序列相同,而且又是通过直接赋值的,因此在内存中只会创建“456”这一个对象。而不会创建两个对象。因此c和d这俩变量他们的地址相同。文章最后会将它的底层原理。
常用的字符串方法:
不用刻意去记这些方法,要用的时候我们直接用API文档查阅即可。
2.5.1==号的作用
比较基本数据类型:比较的是具体的值
比较引用数据类型:比较的是对象地址值,注意是比较地址!!
2.5.2equals方法的作用
方法介绍
public boolean equals(String s) 比较两个字符串内容是否相同、区分大小写
示例代码
public class StringDemo02 {
public static void main(String[] args) {
//构造方法的方式得到对象
char[] chs = {'a', 'b', 'c'};
String s1 = new String(chs);
String s2 = new String(chs);
//直接赋值的方式得到对象
String s3 = "abc";
String s4 = "abc";
//比较字符串对象地址是否相同
System.out.println(s1 == s2);
System.out.println(s1 == s3);
System.out.println(s3 == s4);
System.out.println("--------");
//比较字符串内容是否相同
System.out.println(s1.equals(s2));
System.out.println(s1.equals(s3));
System.out.println(s3.equals(s4));
}
}
运行结果:
我想把前面内存搞懂的话,这里这个结果是完全没啥问题的。通过查询API文档,我们额外还知道一个字符串的比较方法:equalsIgnoreCase(),将此字符串与指定对象进行比较,忽略大小写比较字符串。只关心字符内将是否一致!
2.6.1案例需求
已知用户名和密码,请用程序实现模拟用户登录。总共给三次机会,登录之后,给出相应的提示
2.6.2代码实现
/*
思路:
1:已知用户名和密码,定义两个字符串表示即可
2:键盘录入要登录的用户名和密码,用 Scanner 实现
3:拿键盘录入的用户名、密码和已知的用户名、密码进行比较,给出相应的提示。字符串的内容比较,用equals() 方法实现
4:用循环实现多次机会,这里的次数明确,采用for循环实现,并在登录成功的时候,使用break结束循环
*/
public class StringTest01 {
public static void main(String[] args) {
//已知用户名和密码,定义两个字符串表示即可
String username = "itheima";
String password = "czbk";
//用循环实现多次机会,这里的次数明确,采用for循环实现,并在登录成功的时候,使用break结束循环
for(int i&#61;0; i<3; i&#43;&#43;) {
//键盘录入要登录的用户名和密码&#xff0c;用 Scanner 实现
Scanner sc &#61; new Scanner(System.in);
System.out.println("请输入用户名&#xff1a;");
String name &#61; sc.nextLine();
System.out.println("请输入密码&#xff1a;");
String pwd &#61; sc.nextLine();
//拿键盘录入的用户名、密码和已知的用户名、密码进行比较&#xff0c;给出相应的提示。字符串的内容比较&#xff0c;用equals() 方法实现
if (name.equals(username) && pwd.equals(password)) {
System.out.println("登录成功");
break;
} else {
if(2-i &#61;&#61; 0) {
System.out.println("你的账户被锁定&#xff0c;请与管理员联系");
} else {
//2,1,0
//i,0,1,2
System.out.println("登录失败&#xff0c;你还有" &#43; (2 - i) &#43; "次机会");
}
}
}
}
}
这个是基础题&#xff0c;大家慢慢练习。
2.7.1案例需求
键盘录入一个字符串&#xff0c;使用程序实现在控制台遍历该字符串
2.7.2代码实现
/*
思路&#xff1a;
1:键盘录入一个字符串&#xff0c;用 Scanner 实现
2:遍历字符串&#xff0c;首先要能够获取到字符串中的每一个字符
public char charAt(int index)&#xff1a;返回指定索引处的char值&#xff0c;字符串的索引也是从0开始的
3:遍历字符串&#xff0c;其次要能够获取到字符串的长度
public int length()&#xff1a;返回此字符串的长度
数组的长度&#xff1a;数组名.length
字符串的长度&#xff1a;字符串对象.length()
4:遍历字符串的通用格式
*/
public class StringTest02 {
public static void main(String[] args) {
//键盘录入一个字符串&#xff0c;用 Scanner 实现
Scanner sc &#61; new Scanner(System.in);
System.out.println("请输入一个字符串&#xff1a;");
String line &#61; sc.nextLine();
for(int i&#61;0; i
}
}
}
注意一下的是String类型输入是nextLine()&#xff0c;而不是nextString()。
2.8.1案例需求
键盘录入一个字符串&#xff0c;统计该字符串中大写字母字符&#xff0c;小写字母字符&#xff0c;数字字符出现的次数(不考虑其他字符)
2.8.2代码实现
/*
思路&#xff1a;
1:键盘录入一个字符串&#xff0c;用 Scanner 实现
2:要统计三种类型的字符个数&#xff0c;需定义三个统计变量&#xff0c;初始值都为0
3:遍历字符串&#xff0c;得到每一个字符
4:判断该字符属于哪种类型&#xff0c;然后对应类型的统计变量&#43;1
假如ch是一个字符&#xff0c;我要判断它属于大写字母&#xff0c;小写字母&#xff0c;还是数字&#xff0c;直接判断该字符是否在对应的范围即可
大写字母&#xff1a;ch>&#61;&#39;A&#39; && ch<&#61;&#39;Z&#39;
小写字母&#xff1a; ch>&#61;&#39;a&#39; && ch<&#61;&#39;z&#39;
数字&#xff1a; ch>&#61;&#39;0&#39; && ch<&#61;&#39;9&#39;
5:输出三种类型的字符个数
*/
public class StringTest03 {
public static void main(String[] args) {
//键盘录入一个字符串&#xff0c;用 Scanner 实现
Scanner sc &#61; new Scanner(System.in);
System.out.println("请输入一个字符串&#xff1a;");
String line &#61; sc.nextLine();
//要统计三种类型的字符个数&#xff0c;需定义三个统计变量&#xff0c;初始值都为0
int bigCount &#61; 0;
int smallCount &#61; 0;
int numberCount &#61; 0;
//遍历字符串&#xff0c;得到每一个字符
for(int i&#61;0; i
//判断该字符属于哪种类型&#xff0c;然后对应类型的统计变量&#43;1
if(ch>&#61;&#39;A&#39; && ch<&#61;&#39;Z&#39;) {
bigCount&#43;&#43;;
} else if(ch>&#61;&#39;a&#39; && ch<&#61;&#39;z&#39;) {
smallCount&#43;&#43;;
} else if(ch>&#61;&#39;0&#39; && ch<&#61;&#39;9&#39;) {
numberCount&#43;&#43;;
}
}
//输出三种类型的字符个数
System.out.println("大写字母&#xff1a;" &#43; bigCount &#43; "个");
System.out.println("小写字母&#xff1a;" &#43; smallCount &#43; "个");
System.out.println("数字&#xff1a;" &#43; numberCount &#43; "个");
}
}
本题易错点在那个判断数字的if语句&#xff0c;有的同学可能会写成else if(ch>&#61;0 && ch<&#61;9)&#xff0c;这是不对的&#xff0c;数字0和字符0是不一样的。字符0在ASCII的码值为48.理解下面那个代码后就没啥问题了。
2.9.1案例需求
定义一个方法&#xff0c;把 int 数组中的数据按照指定的格式拼接成一个字符串返回&#xff0c;调用该方法&#xff0c;
并在控制台输出结果。例如&#xff0c;数组为 int[] arr &#61; {1,2,3}; &#xff0c;执行方法后的输出结果为&#xff1a;[1, 2, 3]
2.9.2代码实现
/*
思路&#xff1a;
1:定义一个 int 类型的数组&#xff0c;用静态初始化完成数组元素的初始化
2:定义一个方法&#xff0c;用于把 int 数组中的数据按照指定格式拼接成一个字符串返回。
返回值类型 String&#xff0c;参数列表 int[] arr
3:在方法中遍历数组&#xff0c;按照要求进行拼接
4:调用方法&#xff0c;用一个变量接收结果
5:输出结果
*/
public class StringTest04 {
public static void main(String[] args) {
//定义一个 int 类型的数组&#xff0c;用静态初始化完成数组元素的初始化
int[] arr &#61; {1, 2, 3};
//调用方法&#xff0c;用一个变量接收结果
String s &#61; arrayToString(arr);
//输出结果
System.out.println("s:" &#43; s);
}
//定义一个方法&#xff0c;用于把 int 数组中的数据按照指定格式拼接成一个字符串返回
/*
两个明确&#xff1a;
返回值类型&#xff1a;String
参数&#xff1a;int[] arr
*/
public static String arrayToString(int[] arr) {
//在方法中遍历数组&#xff0c;按照要求进行拼接
String s &#61; "";
s &#43;&#61; "[";
for(int i&#61;0; i
s &#43;&#61; arr[i];
} else {
s &#43;&#61; arr[i];
s &#43;&#61; ", ";
}
}
s &#43;&#61; "]";
return s;
}
}
注意&#xff1a;在测试类中写方法时方法要加上static关键字修饰&#xff0c;这里前面说过&#xff0c;可能有些忘了&#xff0c;这个知识点后面会系统讲解&#xff0c;反正在测试类中写方法时要加上static这个关键字。而在javabean类&#xff08;除了测试类就叫javabean类&#xff09;中&#xff0c;写方法一般是不加static关键字修饰的。不理解没关系&#xff0c;先这样记着。
2.10.1案例需求
定义一个方法&#xff0c;实现字符串反转。键盘录入一个字符串&#xff0c;调用该方法后&#xff0c;在控制台输出结果
例如&#xff0c;键盘录入 abc&#xff0c;输出结果 cba
2.10.2代码实现
/*
思路&#xff1a;
1:键盘录入一个字符串&#xff0c;用 Scanner 实现
2:定义一个方法&#xff0c;实现字符串反转。返回值类型 String&#xff0c;参数 String s
3:在方法中把字符串倒着遍历&#xff0c;然后把每一个得到的字符拼接成一个字符串并返回
4:调用方法&#xff0c;用一个变量接收结果
5:输出结果
*/
public class StringTest05 {
public static void main(String[] args) {
//键盘录入一个字符串&#xff0c;用 Scanner 实现
Scanner sc &#61; new Scanner(System.in);
System.out.println("请输入一个字符串&#xff1a;");
String line &#61; sc.nextLine();
//调用方法&#xff0c;用一个变量接收结果
String s &#61; reverse(line);
//输出结果
System.out.println("s:" &#43; s);
}
//定义一个方法&#xff0c;实现字符串反转
/*
两个明确&#xff1a;
返回值类型&#xff1a;String
参数&#xff1a;String s
*/
public static String reverse(String s) {
//在方法中把字符串倒着遍历&#xff0c;然后把每一个得到的字符拼接成一个字符串并返回
String ss &#61; "";
for(int i&#61;s.length()-1; i>&#61;0; i--) {
ss &#43;&#61; s.charAt(i);
}
return ss;
}
}
这是一个很经典的题目&#xff0c;这种编程思维很漂亮&#xff0c;我们要仔细吸收&#xff0c;手动敲出来。
要求&#xff1a;控制台输入一个字符串&#xff0c;若字符串含有敏感词的话&#xff0c;将其替换为“***”&#xff0c;可以建立一个敏感词库。敏感词库包含&#xff1a; JB、SB、垃圾
代码如下&#xff1a;
public class demo05 {
public static void main(String[] args) {
Scanner sc&#61;new Scanner(System.in);
System.out.println("输入你的发言");
String s&#61;sc.next();
s &#61; filter(s);
System.out.println(s);
}
public static String filter(String s){
String []a&#61;{"JB","SB","垃圾"};//定义敏感词库
for (int i &#61; 0; is&#61;s.replace(a[i],"***");
}
return s;
}
}
运行结果&#xff1a;
敏感词库可以自己定义&#xff0c;这个代码实现不难&#xff0c;但是在今后开发的论坛项目中&#xff0c;基本都会涉及到敏感词的屏蔽&#xff0c;大家从这里就知道替换的原理是怎么实现的了。
先看一个代码&#xff1a;
发现代码执行得很缓慢&#xff0c;我大概用手机计时了一下&#xff0c;发现在我的电脑上执行力3分钟20秒&#xff0c;时间是相当缓慢的。这是为什么呢&#xff1f;不着急&#xff0c;我们再看一个&#xff1a;
发现执行非常快&#xff0c;几乎是立马执行。这是为什么&#xff1f;这也是我们为什么要使用StringBuilder这个类的原因。
StringBuilder可以看作是一个容器&#xff0c;创建之后里面的内容是可变的。而String创建后里面的内容是不可变的。因此用StringBuilder这个类操作能提高效率。因此我们知道&#xff0c;String变量每次的修改其实都是产生并指向了新的字符串对象。 原来的字符串对象都是没有改变的&#xff0c;所以称不可变字符串。
这几个方法是StringBuilder类常用的几个方法。
字符串存储的内存原理
这几种赋值方法原理是一样的吗&#xff1f;
字节码文件加载到方法区&#xff0c;这是之前我们就知道的。
忘记内存的可以复习一下前面的内容。
可知 String s &#61; “abc”&#xff1b;直接赋值有如下特点&#xff1a;
此时字符串abc是存在字符串常量池中的。
先检查字符串常量池中有没有字符串abc&#xff0c;如果有&#xff0c;不会创建新的&#xff0c;而是直接复用。如果没有abc&#xff0c;才会创建一个新的。
所以&#xff0c;直接赋值的方式&#xff0c;代码简单&#xff0c;而且节约内存。文章后面会细讲。
new出来的字符串
看到new关键字&#xff0c;一定是在堆里面开辟了一个小空间&#xff01;&#xff01;
String s1 &#61; new String&#xff08;“abc”&#xff09;&#xff1b;
String s2 &#61; “abc”&#xff1b;
s1记录的是new出来的&#xff0c;在堆里面的地址值。
s2是直接赋值的&#xff0c;所以记录的是字符串常量池中的地址值。
&#61;&#61;号比较的到底是什么&#xff1f;
如果比较的是基本数据类型&#xff1a;比的是具体的数值是否相等。
如果比较的是引用数据类型&#xff1a;比的是地址值是否相等。
结论&#xff1a;&#61;&#61;只能用于比较基本数据类型。不能比较引用数据类型。
再看一张图&#xff1a;
字符串拼接的时候&#xff0c;没有变量参与&#xff08;重点重点再重点&#xff09;
String s &#61; “a” &#43; "b" &#43; "c"&#xff1b;
此时没有变量参与&#xff0c;那么在编译的时候&#xff0c;就已经变成最终的结果了“abc”&#xff0c;这是字符串的常亮优化机制!!!
字符串拼接的时候&#xff0c;有变量参与
String s1 &#61; “a”&#xff1b;
String s2 &#61; s1 &#43; “b”&#xff1b;
在底层&#xff0c;会创建一个StringBuilder对象&#xff0c;再利用append方法&#xff0c;把s1的内容和字符串a都拼接到StringBuilder容器当中&#xff0c;最后再调用toString方法变回一个字符串。在这个过程中&#xff0c;效率比较低&#xff0c;而且内存也有点浪费。
继续看&#xff1a;
继续&#xff1a;
StringBuilder提高效率原理图
StringBuilder sb &#61; new StringBuilder(); sb.append("a"); sb.append("b"); sb.append("c"); System.out.println(sb); 我们在代码中&#xff0c;只创建了一个StringBuilder容器对象&#xff0c;把所有的字符串都添加到同一个StringBuilder容器对象当中。而StringBuilder容器对象&#xff0c;里面的内容是可以发生改变的。从头到尾操作的都是同一个&#xff0c;所以效率较高。看下面的面试题&#xff1a;
有人会问打印结果为什么会是false?我们找到API文档找到toString()方法。如下&#xff1a;
转到方法定于定义&#xff08;快捷键&#xff1a;ctrl&#43;B&#xff09;
发现toString()方法返回的是一个返回newString。是一个新开辟的内存空间&#xff0c;因此地址是不同的。
接着&#xff1a;
由于没有变量拼接&#xff0c;此时没有变量参与&#xff0c;那么在编译的时候&#xff0c;就已经变成最终的结果了“abc”&#xff0c;这是字符串的常亮优化机制!!!因此地址是一样的。
好了基础阶段就会进入尾声了&#xff0c;通过这段时间的学习&#xff0c;大家对面向对象是不是有了更深的理解呢&#xff1f;