零基础学习java------day11------常用API---Object、Scanner、String、StringBufer/StringBuilder
API概述
API(application Programming Interface, 应用程序编程接口),是一些预先定义的函数。目的是提供应用程序与开发人员基于某软件或硬件得以访问一组例程的能力,而又无需访问源码,或理解内部工作机制的细节
比如我需要在一个程序里面嵌入语音识别交互功能,那么这个功能如果让我们自己从零开始实现,肯定是不现实的,所以市场上面就出现了提供这个语音识别功能的公司,这些公司会提供给应用程序编程的接口,大家把这些类称为Xxx Voice API. 我们只需要调用相应的方法就可以实现我们想做的事情,底层实现不需要我们关注。本章涉及的java API指的就是JDK中提供的各种功能的java类
一 Object
1. 构造方法:
public Object()
2. 成员方法
2.1 方法介绍
public int hashCode() 返回对象的hashCode值(默认地址值根据特定算法运算得到的一个整数值),可重写(源码不可见,此方法是用native修饰,用native修饰的方法一般都是用其他语言实现(C),效率高)
public final Class getClass() 返回字节码对象(Class对象),里面包含了类中的所有信息,不能被重写(此方法使用final修饰)
public String toString(): 返回对象的字符串形式(默认是全类名+地址值),可重写
public boolean equals(Object obj): 判断两个对象是否是同一个对象,默认用==实现,可重写
1. hashCode:
一般用hashCode判断两个对象是不是同一个对象,如果hashCode不相等,则肯定不是一个对象,如果hashCode相等,则一定是同一个对象
public class ObjectClass { public static void main(String[] args) { Person p = new Person(); Person p1 = new Person(); System.out.println(p.hashCode()); //804564176 System.out.println(p1.hashCode()); // 1421795058 } } class Person{ String name; }
可见对象p和对象p1不是同一个对象(注释为得到的hashCode值)
2. getClass:
其余代码同上
Class clazz = p.getClass(); // 获取p的字节码对象 Person类的字节码对象 System.out.println(clazz); // class com._51doit.javase.day11.Person System.out.println(clazz.getSimpleName()); // Person System.out.println(clazz.getName()); // com._51doit.javase.day11.Person System.out.println(clazz.getPackage()); // package com._51doit.javase.day11
3. toString:
System.out.println(p.toString()); //com._51doit.javase.day11.Person@2ff4acd0 全类名+@+hashcode值的16进制(hashcode值是10进制) System.out.println(p); //com._51doit.javase.day11.Person@2ff4acd0
println()内部实现是toString,toString源码如下,由此可理解打印的结果
4. equals
格式:对象1.equals(对象2)
(1)equals和 ==
equals:判断两个对象是否是同一个对象(默认是==实现的,可重写)
==:连接基本数据类型(比较的是值)
连接引用数据类型(比较的是地址值)
(2)equals和hashCode的用法
一般我们在比较对象是否是同一个对象的时候,是同时使用hashCode和equals
a. 先用hashCode 作比较:
如果hashCode 不同: 一定不是同一个对象
如果hashCode 相同: 不一定,继续比较equals:( 这样比较的原因:hashCode方法的实现是C语言,效率高)
equals:
true 说明是同一个对象
false: 不是同一个对象
b. 为了保证equals 和hashCode 结果的一致性
我们在重写equals 方法的同时,也要重写hashCode,约定俗称(即当equals定义相等的条件改变时,hashCode也要改变)
自动重写:右键-->source-->generate hashCode and equals
练习
定义一个Teacher类,并创建2个对象,如果两个对象的属性值相同,就认定为同一个对象,打印对象:System.out.println(t),打印的格式为(姓名:张三,年龄:18,工资:20000)
public class Exer1 { public static void main(String[] args) { Teacher t1 = new Teacher(); Teacher t2 = new Teacher(); System.out.println(t1); if(t1.hashCode() == t2.hashCode()) { if(t1.equals(t2)) { System.out.println("true"); }else { System.out.println("false"); } }else { System.out.println("false"); } } } class Teacher{ String name="张三"; int age = 18; double salary = 20000; public String toString() { return "姓名:"+name+",年龄:"+age+",工资:"+salary; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + age; result = prime * result + ((name == null) ? 0 : name.hashCode()); long temp; temp = Double.doubleToLongBits(salary); result = prime * result + (int) (temp ^ (temp >>> 32)); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Teacher other = (Teacher) obj; if (age != other.age) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; if (Double.doubleToLongBits(salary) != Double.doubleToLongBits(other.salary)) return false; return true; } }
二 Scanner
scanner表示键盘录入,但若输入的数据类型与表达式不同则会报错,以int为例
Scanner sc = new Scanner(System.in); int a = sc.nextInt(); // 此处a若输入一个非int值时就会报错
解决方法
//加个条件判断 Scanner sc= new Scanner(System.in); if(sc.hasNextInt()){ int a = sc.nextInt(); }
三. String
1. String类概述:
字符串是由多个字符组成的一串数据(字符序列,字符串可以看成是字符数组)
2. 构造方法
(1)public String()
(2)public String(byte[ ] bytes)
(3)public String(byte[ ] bytes, int offset, int length)
(4)public String(byte[ ] value)
(5)public String(char[ ] value, int offset, int count)
(6)public String(String original)
案例
public class StringDemo { public static void main(String[] args) { } }
方法体中添内容
a 构造方法1
String str = new String(); System.out.println(str);
// 打印结果为空,所以第一个构造方法对应空字符串
其构造方法源码如下
b 构造方法2(byte数组转为字符串)
byte[] bs = new byte[] {97,98,99}; String str1 = new String(bs); System.out.println(str1); // 运行的结果为abc
此处构建字符串是用数组中编码所代表的字符来构建字符串,编码97在ascii码中代表a
c 构造方法3(byte类型转换的字符串进行切片)
String str2 = new String(bs,1,2);//offset=1,length=2表示从数组索引为1的编码开始取2位 System.out.println(str2); // 运行结果:bc
d 构造方法4(char类型数组转为字符串)
char[] chs = new char[] {'哈','哈', '大','笑','哭','笑','不','得'};//右边可以省略new char String str3 = new String(chs);
System.out.println(str3); // 运行结果:哈哈大笑哭笑不得
e 构造方法5(带偏移量以及count的char数组转为字符串)
System.out.println(new String(chs,3,3)); // 运行结果:笑哭笑
f 构造方法6(字符串转字符串,即赋值)
String str4 = new String("你好,世界"); System.out.println(str4);
相当于将“你好,世界”赋值给了str4
3 此种赋值方式(str4)与直接赋值(str=“你好,世界”)有什么区别?
存放位置不一样,直接赋值的str存放在常量池,而str4的形式是创建了一个新对象,其存放位置为堆。
案例
public class StringDemo2 { public static void main(String[] args) { String str = "你好,世界"; // 存放在常量池(jdk1.6位置为方法区,1.7为堆内存中,1.8以后放在一个元空间) String str1 = new String("你好,世界"); System.out.println(str == str1); } } // 运行结果:false
若另外创建一个内容相同的常量字符串str2,其地址值是否和ste一样?答案:一样
String str2 = "你好,世界"; System.out.println(str == str2); //运行结果:true
注意字符串中的equals比较的是字符串内容是否相等
System.out.println(str.equals(str1)); 运行结果是true
同理,equals重写了,相应的hashCode方法也被重写了,str和str1的hashCode值也相等
System.out.println(str.hashCode() == str1.hashCode()); // 运行结果:true
4. String内存分配
(1)直接赋值:存放在常量池中,先在常量池中找该字符是否存在,如果存在,直接用这个字符串,如果没有,则创建一个新的,相同的内容,只有一份
(2)new:存放在堆内存中,new几个就开辟几块空间,也就是几个地址值
面试题
第4行代码中的s存在那一块?
1 public class StringDemo3 { 2 public static void main(String[] args) { 3 String s = "hello"; // 常量池 4 s += "world"; // s = s+ world 堆内存 5 } 6 }
为了弄清第4行代码是怎么运行的,找到这个文件的.class,进行反编译(将计算机能看懂的文件转换成人能看懂的内容),用XJAD软件打开即可
在eclipse上找.java文件的.class文件的流程
特定.java文件上右键--->show In----->System Explorer,即可得到.java文件所在的目录,如下
在地址栏那一块点击src上一层的javase,即可出现如下界面
这个时候点击bin文件夹,即可找到相应的.class文件
利用xjad进行反编译可得:
由19行可知,s存在堆内存中
5. String的判断功能
String
(1)boolean equals(Object obj):比较字符串的内容是否一样
(2)boolean equalsIgnoreCase(String str):忽略大小写比较字符串的内容
(3)boolean contains(String str):某个字符串是否包含另一内容
(4)boolean startsWith(String str):是否以。。。开头
(5)boolean endsWith(String str):是否以。。。结尾
(6)boolean isEmpty():判断字符串是否为空
案例
public class StringDemo4 { public static void main(String[] args) { String str = "abc"; String str1 = "abc"; System.out.println(str.equals(str1)); // true,equals判断的是字符串的内容是否相同 System.out.println("abc".equalsIgnoreCase("ABc")); // true System.out.println("abcdefgh".contains("def")); //true System.out.println("Abcsd".startsWith("Ab")); //true System.out.println("Abcsd".endsWith("sd")); // true System.out.println("abc".isEmpty()); // false } }
练习
模拟登陆,给三次机会,并提示还有几次机会
public class Login { public static void main(String[] args) { String name = "老王"; String pwd = "123456"; Scanner sc = new Scanner(System.in); for(int i=0;i<3;i++) { // 请输入您的姓名 System.out.println("请输入您的姓名"); String userName = sc.nextLine(); // 请输入您的密码 System.out.println("请输入您的密码"); String userPwd = sc.nextLine(); if(userName.equals(name) && userPwd.equals(pwd)) { System.out.println("登陆成功"); break; }else { if(i<2) { System.out.println("用户名或密码错误,您还有"+(2-i)+"次机会"); }else { System.out.println("今日机会已用完"); } } } } }
6. 字符串的获取功能
(1)int length(): 获取字符串的长度
(2)char charAt(int index) : 返回index对应的字符
(3)int indexOf(int ch): 返回ch对应的字符,第一次出现的索引值
(4)int intdexOf(String str): 返回str第一次出现的索引值
(5)int indexOf(int ch, int fromIndex)
(6)int indexOf(String str, int fromIndex)
(7)String substring(int start): 字符串的截取从start到最后
(8)String substring(int start, int end): 包含start,不包含end
public class StringDemo5 { public static void main(String[] args) { String str = "好好学习,天天向上"; System.out.println(str.length());//9 System.out.println(str.charAt(2));//学,此可以用来遍历一个字符串的每个字符 System.out.println('好'+0);//22909,得到好字在编码中对应的编号为22909,此处通过加个整数是字符类型强转为int类型 System.out.println(str.indexOf(22909));// 0 System.out.println(str.indexOf("好")); //0 System.out.println(str.indexOf(22909,1));//1 System.out.println(str.indexOf("天",6));//6 System.out.println(str.substring(5)); System.out.println(str.substring(2,8));//学习,天天向 } }
练习
遍历获取字符串中的每一个 字符,并统计一个字符串中的大写字母,小写字母以及数字字符出现的次数
public class Exer2 { public static void main(String[] args) { String str = "adAAD2312sdfaSSD"; int n = str.length(); int count1 = 0; int count2 = 0; int count3 = 0; for(int i=0;i<n;i++) { char ch = str.charAt(i); if(ch>='A' && ch<='Z') { count1++; } if(ch>='a' && ch<='z') { count2++; } if(ch>='1' && ch<='9') { count3++; } } System.out.println("大写字母的个数为:"+count1); System.out.println("小写字母的个数为:"+count2); System.out.println("数字的个数为:"+count3); } }
7. 字符串的转换功能
(1)byte[ ] getBytes(): 将字符串转成bytes数组
(2)char[ ] toCharArray() : 将字符串转成char数组
(3)static String valueOf(char[ ] chs):把char数组转成字符串(用构造方法转也可以)
(4)static String valueOf(int i):把int转成字符串
(5)String toLowerCase():转成小写
(6)String toUpperCase():转成大写
(7)String concat(String str):字符串的拼接
案例
package com._51doit.javase.day11; import com._51doit.javase.util.ArrayUtil; public class StringDemo6 { public static void main(String[] args) { String str = "abcdABC"; //待补代码 } }
(1)byte[ ] getBytes(): 将字符串转成bytes数组
byte[] bs = str.getBytes();
System.out.println(bs.length)// 长度为7 ArrayUtil.printArr(bs); // 运行结果:[97,98,99,100,65,66,67]
可见字符串转为byte数组时,是将每个字符的编码作为转换对象
当str变为中文时,要注意字符串的长度
String str = "大家好"; byte[] bs1 = str.getBytes(); System.out.println(bs1.length); //长度为9(不是3)
// 将前三个字节中存的编号取出 byte b1 = bs1[0]; byte b2 = bs1[1]; byte b3 = bs1[2];
// 将编号赋值给byte数组,并将之转为字符串(此处是将编码中编号对应的结果转换成字符串) System.out.println(new String(new byte[] {b1,b2,b3}));// 此处得到的结果是“好”字,说明"好"占3个字节(b1,b2,b3)
注意:在utf-8中,一个中文占3个字节内存,但其在gbk中占两个字节内存;一个英文字母在utf-8中占一个字节
(2)char[ ] toCharArray() : 将字符串转成char数组
char[] ch = str.toCharArray(); ArrayUtil.printArr(ch);
// 运行结果:[a,b,c,d,A,B,C]
(3)static String valueOf(char[ ] chs):把char数组转成字符串(用构造方法转也可以)
char[] ch = str.toCharArray(); ArrayUtil.printArr(ch);//打印ch数组 String str2 = String.valueOf(ch); System.out.println(str2); // 运行结果: [a,b,c,d,A,B,C] //打印字符时,不是用它的编码 abcdABC
构造方法new String(char[] ch)或者直接将char+字符串,也能达到将char数组转换成String的目标
(4)static String valueOf(int i):把int转成字符串
System.out.println(12); //得到类型为String的12
(5)String toLowerCase():转成小写
System.out.println(str.toLowerCase());
// 运行结果:abcdabc
(6)String toUpperCase():转成大写
System.out.println(str.toUpperCase()); //运行结果:ABCDABC
(7)String concat(String str):字符串的拼接
System.out.println(str.concat("哈哈大笑")); // 运行结果:abcdABC哈哈大笑
这种字符串的拼接模式和“+”的区别
contact:只能拼接字符串类型
+ : 可以拼接任何类型
练习
把一个字符串的首字母转成大写,其余为小写(只考虑英文大小写字母字符)
public class Exer3 { public static void main(String[] args) { String str = "saadfD"; String str1 = str.substring(0,1).toUpperCase(); String str2 = str.substring(1); System.out.println(str1.concat(str2)); } } // 运行结果:SaadfD
8.字符串的其他功能
(1)替换功能
String replace(char old, char new); 将字符串中某个字符全部换成新的字符
String replace(String old, String new); 将字符串替换成另外一个字符串
String str = "我爱你,你却爱着他"; System.out.println(str.replace('你', '他')); String str1 = "是他,是他,就是他"; System.out.println(str.replace(str, str1));
(2)去除字符串头尾空格
String trim()
String str2 = " 哈哈, 一直往南方开 "; System.out.println(str2.trim()); // 运行结果:哈哈, 一直往南方开
(3)按字典顺序比较两个字符串(比较的是首字符,若首字符相同时,就比较第二个字符,依此类推。。。)
int compareTo(String str)
int compareToIgnoreCase(String str)
当比较结果为正时,表示前面的字符串的顺序比后面的字符串大,否则就小,0表示两者顺序一样
System.out.println("a".compareTo("b")); System.out.println("好".compareTo("你"));
System.out.println("a".compareToIgnoreCase("A")); // 运行结果: -1 2589
0
8 clone
Object:
protected Object clone() 克隆对象,返回一个副本
克隆的步骤:
(1)在子类中重写clone方法,写成public修饰的,目的是可以在其他类中使用
return super.clone
(2)上一步骤中,有异常需要处理,直接抛出
光标放到红线上,选择一个throws
(3)在main方法中使用对象调用clone方法,发现也有红线,继续向上抛
返回Object
(4)需要让被克隆的类支持克隆,实现一个借口:Cloneable
(5)要想调用副本中的方法,记得向下转型
案例
克隆一个student副本
public class CloneDemo { public static void main(String[] args) throws CloneNotSupportedException { Student s = new Student(); s.name = "老王"; s.age = 18; Object o = s.clone(); System.out.println(o instanceof Student); // System.out.println(o.name)用对象o调用会报错,其是object类型,为父类对象,需要向下转型 Student s1 = (Student)o;// 向下转型 System.out.println(s1.name); System.out.println(s1.age); } } class Student implements Cloneable{ String name; int age; public Object clone() throws CloneNotSupportedException { return super.clone(); } }
运行结果:
true
老王
18
四. StringBuffer(StringBuilder,以下例子都是用StringBuffer演示,StringBuilder结果一样)
StringBuffer和StringBuilder用法一样,StringBuffer是线程安全的,而StringBuilder是线程不安全的(效率高)
(1)StringBuffer类的概述:
我们如果对字符串进行拼接操作,每次拼接,都会构建一个新的String对象,既耗时,又浪费空间(如“a”+“b”会在常量池创建3个空间),而StringBuffer就可以解决这个问题,其是线程安全的可变字符序列。
(2)StringBuffer(StringBuilder)和String的区别?
String类型和StringBuffer类型的主要性能区别其实在于String是不可变的对象,因此在每次对String类型进行改变的时候其实都等同于生成了一个新的String对象,然后将指针指向新的String对象,所以经常改变内容的字符串最好不要用String,因为每次生成对象都会对性能产生影响,特别当内存中引用对象多了以后。JVM的GC就会开始工作,那速度就会相应变慢
(3)String和StringBuffer(StringBuilder)之间的相互转换
a. String转StringBuffer:构造方法
//为了方便就没写出main方法等其他代码
StringBuffer sb = new StringBuffer("大家好"); System.out.println(sb instanceof StringBuffer);
// 运行结果:true(这说明String被转成StringBuffer)
b. StringBuffer转String:toString方法
String str = sb3.toString(); System.out.println(str instanceof String); // 运行结果:true
(4)构造方法
public StringBuffer():无参构造方法
StringBuffer sb1 = new StringBuffer();
public StringBuffer(int capacity):容量:就是char数组的初始化长度,默认值是16(真正测试str的长度时,显示的是字符串真实的长度)
StringBuffer sb2 = new StringBuffer(20); System.out.println(sb2.length()); //运行结果:0
public StringBuffer(String str):将String类型的str转换成StringBuffer类型的str
如(3)中所示
(5)StringBuffer类的成员方法
添加功能
public StringBuffer append(String str) 追加
public StringBuffer insert(int offset,String str) 插入
删除功能
public StringBuffer deleteCharAt(int index) 删除执行索引对应的字符
public StringBuffer delete(int start,int end) 删除从[start-end)
替换功能
public StringBuffer replace(int start,int end,String str)
反转功能
public StringBuffer reverse() 反转
截取功能(此方法得到destr类型为String)
public String substring(int start) 字符串的截取
public String substring(int start,int end)
public class StringBuilderDemo { public static void main(String[] args) { StringBuilder str = new StringBuilder(32); // System.out.println(str.length());//0: length:被使用的长度 StringBuilder str1 = new StringBuilder("大家下午好"); str1.append("凑合吧").append("也就那样"); System.out.println(str1); System.out.println(str1.insert(1, "哈哈")); System.out.println(str1.deleteCharAt(0)); System.out.println(str1.delete(0, 2)); System.out.println(str1.replace(0, 2, "就这吧")); System.out.println(str1.reverse()); StringBuilder str3 = new StringBuilder("abcde"); String re = str3.substring(3); System.out.println(str3);//原内容不变, abcde System.out.println(re);//de String re2 = str3.substring(0, 1); System.out.println(re2); System.out.println(str3);//abcde String s = str3.toString(); System.out.println(s); } }