什么是JDK API
JDK中包含大量的API类库,所谓API(Application Programming Interface,应用程序接口)就是一些已经写好的、可供直接调用的功能(在java语言中,这些功能以类的形式封装)
例如:字符串操作、集合操作、文件操作、输入输出操作、网络操作、多线程等等。
String字符串:极其重要的API类,一切文字的显示都用到它
1、java中最重要的基础API
2、java.lang.String使用了final修饰,不能修改
3、字符串底层封装了字符数组(数据类型一样)及针对字符串数组的操作算法
4、字符串一旦创建,对象永远无法改变,但字符引用可以重新赋值
5、Java字符串在内存中采用Unicode编码方式,任何一个字符对应两个字节的定长编码
Unicode就是为每一个对象和符号分一个数,因为计算机只能处理数字
6、字符串的长度:是字符的个数
字符串内存的占用:字节数,每个字符2个字节
“Tom你好”:字符串长度为5,在内存中占用10个字节
String常量池:
1、Java为了提高性能,静态字符串(字面量/常量/常量连接的结果)在常量池中创建,并尽量使用同一个对象,重用静态字符串;
2、对于重复出现的字符串直接量,JVM会首先在常量池中查找,如果存在即返回该对象
//测试静态字符串缓冲池现象,减少创建对象,优化性能
//静态字符串包括:字面量(直接量)、“字面量”的运算结果、常量
// "杯子1" "杯子"+1
//@Test
public void testString1() {
String tom="杯子1";
String jerry="杯子1";
String andy="杯子"+1;//编译以后是”杯子1“,运行期间,与tom引用同一个对象,这是字面量的运行结果
System.out.println(andy);
int n=1;
String john="杯子"+n;
//计算变量,编译以后没有值,运行之后n为1,运行期间连接并且创建新字符串,不在静态池中分配
System.out.println(john);
System.out.println(tom==andy);//true
System.out.println(tom==jerry);//true
System.out.println(tom==john);//false
}
public class StringConstantPool {
public static void main(String[] args) {
String str1="Hello";
String str2="Hello";//不会创建新的String对象,而是使用常量池中已有的"Hello"
System.out.println(str1==str2);//"=="比较的是常量的值,是str1的地址和str2的地址
String str3=new String("Hello");//使用new关键字创建新的对象
System.out.println(str1==str3);
}
}
使用indexOf实现检索:
indexOf方法用于实现在一个字符串中查找另一个字符串
String提供了几个重载的indexOf方法
int indexOf(String str):在字符串中检索str,返回第一次出现的位置,若没找到则返回负数
int indexOf(String str,int fromIndex):从字符串的fromIndex位置开始检索
int lastIndex(String str,int from):str在字符串中多次出现时,将返回最后一个出现的位置
public void testIndexOf() {
String url="http://www.nhgjfh.com.cn/index.html";
// 01234567890123456789012345678901234
int index=url.indexOf("/");
System.out.println(index);//5
index=url.indexOf("/", 7);//从第7个位置开始查找
System.out.println(index);//24
index=url.lastIndexOf("/");//从后往前找
System.out.println(index);//24
String host="nhgjfh.com.cn";
if(url.indexOf(host)>=0) {
System.out.println(url.indexOf(host));//"nhgjfh.com.cn"在字符串中11的位置
System.out.println("访问nhgjfh的URL");
}
}
使用substring获取字符串:
1、substring方法用于返回一个字符串的子字符串
2、substring常用重载方法定义如下:
A、String substring(intbeginIndex,int endIndex):返回字符串从beginIndex开始(包括)到endIndex(不包括)结束的字符串,包含开始,不包含结束
B、String substring(intbeginIndex):返回字符串中从下标beginIndex(包括)开始到字符串结尾的子字符串,从开始的位置到最后全要
public void testSubstring() {
//使用API可以简化编程,不用API使用for循环也可以处理同样的问题。
String email="tengxun@qq.com" ;
// 01234567890123
int index=email.indexOf('@');
String name=email.substring(0,index);//包括0,不包括7,tengxun
System.out.println(index+1);//qq.com
String host=email.substring(8);
System.out.println(host);
}
trim()去掉空字符:
空字符:编码小于空格的字符,空格也是字符!不仅仅是空格!
包括:空格 tab \n \r等
关于String API:如果返回值与原字符不同,就返回新字符串对象,原字符串对象不变;如果与原字符串对象相同,经常返回原字符串对象本身。
public void testTrim() {//把空字符都去掉
String name=" \n\t\r Tom\n \t\r";
name=name.trim();
System.out.println(name);
}
public void testTrim2() {
String name=" Tom ";
name.trim();
System.out.println(name);// Tom 引用name没有被改变,原对象不变
name=name.trim();//name引用被改变了
System.out.println(name);
}
public void testTrim3() {
String name="Tom";
String n=name.trim();
System.out.println(name==n);//true
}
charAt:遍历字符串
charcharAt(int index):用于返回字符串指定位置的字符
经常用来进行遍历字符串
当使用String API不能达到业务目的,可以采用charAt迭代(遍历)处理每个字符,可以实现复杂的字符处理。
测试用例:
将字符串中每个空格后(每个单词的首字母)的小写字母转换成大写字母:
public void testcharTest() {
String str="What is Java";
char ch=str.charAt(0);//W
System.out.println(ch);
String name="我爱大笨蛋";
//打印字符串中每个字符的编码,遍历每个字符
for(int i=0;i<name.length();i++) {
char c=name.charAt(i);
System.out.println((int)c);//强制类型转换,将字符都变成数了
}
}
public void testcharTest2() {
//将字符串中每个空格后的字母转换为大写字母
//当遇到前一个字符是空格,后一个字符是小写字母时候,需要转换,而且转换结果是新的
//设计pre 代表前一个字符,ch 代表当前的字符
String str="what is java!";
char pre=' ';
char[] chs=new char[str.length()];//输出的字符缓冲区,因为原数组不能被改变
for(int i=0;i<str.length();i++) {
char ch=str.charAt(i);
//System.out.println(pre+","+ch);//pre和ch是每两个相邻的字符,输出紧挨着的两个字符
if(pre==' '&&(ch>='a'&&ch<='z')) {//如果前一个是空格,后一个是小写字母的时候
System.out.println(pre+","+ch);
ch=(char)(ch+('A'-'a'));//将小写字母转换成大写字母
}
chs[i]=ch;//将每个字符复制到数组中
pre=ch;
}
String s=new String(chs);//字符数组转成字符串
System.out.println(s);
}
startsWith和endsWith:
检查一个字符串是否以指定字符串开头或结尾
public void testEndWith() {
String file="tetris.png";
if(file.endsWith(".png")) {//以.png结尾
System.out.println("这是一个png图片文件");
}
//startsWith()的用途:检查命令
String cmd="get day01.zip";
if(cmd.startsWith("get")){//下载文件命令只看开头是get就行
System.out.println("这是下载文件命令");
}
}
大小写变换:
转换字符串中英文字母的大小写形式
public void testToLowerCase() {
String file="tetris.PNG";
// file=file.toLowerCase();
// if(file.endsWith("png")) {
// System.out.println("这是一个图片文件");
// }
if(file.toLowerCase().endsWith("png")) {
System.out.println("这是一个图片文件");
}
}
valueOf:
将其他类型转换为字符串类型
String.valueOf()可以将任何类型转换为String用于最终的输出(在界面上显示结果)
public void testValueOf() {
int i=65;
//i=00000000 00000000 00000000 01000001
//计算机内部没有十进制,只有二进制,需要利用算法将二进制的数转换为字符串“65”,最终输出十进制的“65”
System.out.println(i);
String s=String.valueOf(i);//将其他类型转换为字符串类型
//valueOf的底层调用了Integer.toString(i)
System.out.println(s);
char[] chs= {'A','B','C'};
s=String.valueOf(chs);
System.out.println(s);
}
字符串API的特点:
1、是final类型
2、String对象是不可改变的(叫做不变模式)
3、String API如果改变了内容,就返回新对象,原对象永远不变
4、String API方法是对char数组的操作算法
注意:没有API,或者不用API可以利用for循环处理字符数组实现相应的功能,如果优化的好的话,性能优于String API。
public class StringDemo {
public static void main(String[] args) {
String name="Tom";//name是对象的引用,Tom是对象
String other=name;//other和name引用了同一个对象Tom
name=name+" and Jerry";//java的动态字符串连接运算产生了新的字符串对象:"Tom and Jerry"
//name被赋值引用了新字符串对象,原字符串对象不变,字符串引用变量name被改变了!
System.out.println(name);
System.out.println(other);//输出结果是:"Tom",说明对象没变
}
}
StringBuilder:
StringBuilderappend(String str);追加字符
StringBuilderinsert(int dstOffset,String s);插入字符串
StringBuilderdelete(int start,int end);删除字符串
StringBuilderreplace(int start,int end,String str);替换字符串
StringBuilderreverse();字符串反转
StringBuilder的很多方法的返回值均为StringBuilder类型,这些方法的返回语句均为:return this,返回的均为本身。一直在修改StringBuilder中的内容。这是跟String的不同。
public void testStringBuilder() {
StringBuilder buf=new StringBuilder();
//buf中的内容一直在被操作修改
buf.append("马伊利说:");//返回buf本身
buf.append("且行且珍惜");
buf.insert(3, "对文章");
buf.replace(1, 3, "司令");
// buf.append("马伊利说:")//可以在本身上面进行操作,因为返回值就是buf本身,方便镰蟹
// .append("且行且珍惜")
// .insert(3, "对文章")
// .replace(1, 3, "司令");
//
String str=buf.toString();
System.out.println(str);
}
StringBuilder总结:
StringBuilder是可变字符串,字符串的内容计算,建议采用StringBuilder实现,这样性能会好一些。Java的字符串String连接的过程是利用StringBuilder实现的。
如:
Strings=“杯子”;
Stringss=s+1;
//ss=newStringBuilder(s).append(1).toString();
Stringss=s+1+”3”;
//ss=newStringBuilder(s).append(1) .append(“3”).toString();
很多的连接要用ss=new StringBuilder(s).append(1).toString();这种形式
单个的或两个的可以使用累加+
StringBuilder和String:
StringBuilder:内部的字符数组内容和长度可变,是可变字符串
String:内部字符数组是不可变的,是不变字符串
那么java为什么要弄两个:
StringBuilder的字符操作性能好于String,StringBuilder不需要复制,而String改变的就是复制后的内容,原对象不变
StringBuffer和StringBuilder方法都一样,区别是:
StringBuffer:是线程安全的,同步处理的,性能稍慢
StringBuilder :是非线程安全的,并发处理的,性能稍快API现成的类(程序):
1、java API是java(oracle)提供的
系统标准API:System String Scanner Integer Math
2、第3方API
如:Junit.jar Spring MyBatis Apache提供的
3、可以自己开发一些API
如:开发方法:将空格前每个字母大写
4、一般情况下,任何技术都有现成的API;跟业务有关的都需要自己写。
Java系统API:
任何程序(API)=数据+算法
1、String类=字符数组(数据)+操作算法(方法)
一旦创建对象,数据不可改变
2、StringBuilder=字符数组(数据)+操作算法(方法)
可以利用方法改变字符数组的数据,最后需要toString
3、字符数组(数据)也是字符串,但是没有算法
字符数组(数据)+优化的算法是性能最优的字符串解决方案,但是开发测试量很大。
建议:输入输出和简单操作时候使用String
复杂的更改使用StringBuilder
极致性能要求时候:使用字符数组+算法
String类的静态缓冲池优化现象:
静态字符串:编译器能够确定内容的字符串
如:字符串面量、字符串常量、字面量常量的运算结果
相同静态字符串在运行期间引用同一个对象
Publicstatic final int ROWS=20;
ROWS:称为常量,常量是一个名字
20:称为字面量
字面量和常量是不同的
经典面试题:字符串是为了“性能”进行优化
1、考察字符串的静态连接过程
Strings=new String(“123”+“ABC”);
这个代码在“运行期间”会出现几个String对象?
答案:2个字符串对象,静态字符串连接一个,又new出来一个
2、考察字符串的静态连接过程
A、String sql=”select *from emp where id=?”
B、String sql=”select *“+
“from emp“+
”where id=?”;
C、StringBuilder buf=newStringBuilder();
Buf.append(”select *“)
.append(“from emp“)
.append(”where id=?”);
问题:A、B、C运行性能有差别吗?
答案:A、B性能没有差别,C运行期间执行,性能差。因为StringBuilder适合用来处理动态字符串,对于这种不变的字符串,性能差。
3、考察字符串的动态连接过程
String s=”123”;
String ss=s+”abc”;
1)、运行期间有几个字符串对象?
答案:3个
“123”是一个,“abc”是一个,连接起来是一个“123abc”
2)、运行期间至少有几个对象?
答案:4个,因为连接符+实质上是调用的StringBuilder对象,为什么说至少呢,因为StringBuilder里面还调用了别的对象。
“123”、“abc”、“123abc”、newStringBuilder()