类class是java中已经定义好的一些模板,用户可以按照他们的样子建立object,这些object直接具有class内的各种功能,用户就可以直接使用。
-
内部类(之前已经讲过,见Java学习23–内部类)
-
String类
-
Object Class
-
包装类wrapper
-
Math
-
StringBuffer
-
StringBuilder
-
File
-
Data
-
Random
String类
-
不可变性final: 字符串是一个不可变的类型(final类),几乎所有的字符串操作都会返回一个新字符串而不是在原有基础上进行修改。
-
操作量较少
修饰符和返回值的类型 | 方法名 | 解释 |
---|---|---|
char | charAt() | 获取某个位置的字符 |
String | concat() | 字符串的拼接。一般字符串拼接直接相加就好了 |
boolean | contains() | 判断原字符串是否含有xxx字符串,常用于子串的判断 |
boolean | endsWith() | 判断原字符串是否以xxx字符串结尾 |
boolean | startsWith() | 判断原字符串是否以xxx字符串开头 |
boolean | equals() | 判断两边字符串内容是否相同;==判断地址是否相同 |
boolean | equalsIgnoreCase() | 忽略大小写判断两边字符串的内容是否一样 |
int | indexOf() | 计算给出字符串第一个出现的位置 |
int | lastIndexOf() | 计算给出字符串最后一个出现的位置 |
int | length() | 计算字符串的长度 |
String | replace() | 字符串内容的替换 |
String[] | split() | 字符串切割,最终结果是一个字符串数组 |
String | substring() | 字符串截取,左闭右开:[ ) |
String | trim() | 去掉字符串左右两边的空格,中间的不行 |
static | String.valueOf() | 官方:其他数据类型转为字符串的方法;也可以直接任意变量 + “” |
其中equals是一个常用的判断两个object_A和object_B是否相等的方法。使用方法如下:
object_A.equals(object_B)
分析:java中的equals方法和==
通常来讲==
判断的是地址。(主要判斷兩個對象的引用是否相等,也就是看是不是指向内存中的同一个地址)
equals
判断的是内容。(主要判斷兩個對象的值是否相等。广义上Object类默认实现是比较Object大类的引用。
所以如果没有重写equals方法,对于Object总类下面的一般object对象,equals()
方法一般和==
没有区别。(下面介绍在String类里面的equals使用,它和==
有区别!!!)
特殊的String.equals()
String类的equals()方法被String类重写了。
即判断地址是否相同,还判断字符串是否相同,只要有相同的,就为true。
从技术上来说,当使用==
运算符比较两个String对象时候,我们已经知道,==
比较的是地址。由于String对象定以后是final类,是不会变的,java为了省空间,String会store在字符串常量池里,当用户创建字符串时,如String1=“hello”
java会先检查常量池中是否存在该String字符,如果已经存在,直接返回该字符串的引用;如果不存在,会创建一个新的字符串对象。上面讲的也叫做String类的不可变性(immutability)和字符串常量池(String Constant Pool)的使用原理。
package Stringtest;
public class test2 {
private String name;
private int age;
public test2(String name, int age) {
this.name = name;
this.age = age;
}
// 同时存在age和name等变量,用户重写equals方法
@Override
public boolean equals(Object myobj) {
if (this == myobj) {return true;}
if (myobj == null || getClass() != myobj.getClass()) {return false;}
test2 myperson = (test2) myobj;
return (age == myperson.age) && (name.equals(myperson.name));
}
public static void main(String[] args) {
test2 p1 = new test2("Alice", 25);
test2 p2 = new test2("Alice", 25);
// 使用==
System.out.println(p1 == p2); // 输出 false,因为p1和p2引用的是两个不同的对象
// 使用equals
System.out.println(p1.equals(p2)); // 输出 true,因为我们重写了equals方法,使其比较两个Person对象的name和age属性,字符串或者地址,只要相同就为true
}
}
运行结果
false
true
Process finished with exit code 0
- String class常用method(见上面总表)应用代码举例:
package Stringtest;
public class test {
private String defname;
private int defage;
private int defid;
public test(String qname, int qage, int qid) {
this.defname = qname;
this.defage = qage;
this.defid = qid;
}
public static void main(String[] args) {
String str1="hello this is me.";
String str2=" my name is Dee";
String str3="hello you and me.";
//int[] x= new int[5];
test p1=new test(str1,22,567);
test p2 = p1;
test p3=new test(str1,22,567);
System.out.println(str1.charAt(3));//str1[3]==l;
System.out.println(str1.concat(str2)); //string1 + strinif the string ends with "me" g2==hello this is me. my name is Dee
System.out.println(str1.contains("Dee")); //if the keyword is inside the String str1 False
System.out.println(str2.contains("Dee")); //if the keyword is inside the String str2 True
System.out.println("=======1111=========>");
System.out.println(str1.endsWith("me.")); //if the string ends with "me." True
System.out.println(str2.endsWith("me.")); //if the string ends with "me." False
System.out.println("=======2222=========>");
System.out.println(str1.startsWith("hello")); //if the string starts with "hello" True
System.out.println(str2.startsWith("hello")); //if the string starts with "hello" False
System.out.println("=======3333=========>");
System.out.println(p1.equals(p2)); //if p1 object same as p2, p2 was made from p1, definately true
System.out.println(p1.equals(p3)); //if p1 object same as p3, they are different object, so address are different, false
System.out.println("========44444========>");
System.out.println(p1==p2); //if p1 object address same as p2, true
System.out.println(p1==p3); //if p1 object address same as p3, false
System.out.println("========55555========>");
String str4=str3.replace("hello","HeLLo");
System.out.println("str3 is "+str3);
System.out.println("str4 is "+str4);
System.out.println(str3.equalsIgnoreCase(str4));
System.out.println("========66666========>");
System.out.println(str3.indexOf("ll"));//"hello you and me."; ll第一次出现是角标2位置
System.out.println(str3.lastIndexOf("lo")); //ll第一次出现是角标3位置
System.out.println(str2.length()); //" my name is Dee" total length is 15
System.out.println("========77777========>");
String yoyo="荔枝!龙眼!芒果!西瓜!波罗蜜!水蜜桃!哈密瓜!好多好吃的呀!";
String [] str5=yoyo.split("!");//搜寻字符串中的“!”作为分隔符,分隔各个字符片段
for (String s : str5) {
System.out.println(s.trim()); // 使用trim()去除可能的前后空格
}
System.out.println(yoyo.substring(0,8));//取左不取右,按角标取string片段,0位取,8位已知是个!号,但是8位不取
System.out.println("========88888========>");
String yoyo2 = " apple with space ";
System.out.println(yoyo2.trim());
System.out.println("========99999========>");
long dodo1 = 123;
long dodo2 = 678;
System.out.println(String.valueOf(dodo1)+String.valueOf(dodo2));
}
}
运行结果:
l
hello this is me. my name is Dee
false
true
=======1111=========>
true
false
=======2222=========>
true
false
=======3333=========>
true
false
========44444========>
true
false
========55555========>
str3 is hello you and me.
str4 is HeLLo you and me.
true
========66666========>
2
3
15
========77777========>
荔枝
龙眼
芒果
西瓜
波罗蜜
水蜜桃
哈密瓜
好多好吃的呀
荔枝!龙眼!芒果
========88888========>
apple with space
========99999========>
123678
Process finished with exit code 0
StringBuffer(更安全)
因为String 是一个不可变的数据类型,每每拼接都会产生一个新的字符串,那么内存迟早会被这些拼接的字符串塞满。StringBuilder和StringBuffer用可变的字符串,不产生新对象,比较省内存,StringBuilder类速度快用的最多,但当考虑线程安全时必须用StringBuffer类。
-
(StringBuffer或StringBuilder类的) object.append()方法用于字符串拼接
-
(StringBuffer或StringBuilder类的) object.insert()方法用于角标位置插入字符串
-
多线程数据量较大 线程安全 效率高
StringBuilder(更快,用的多)
- 可变长
-
单线程数据量较大 线程不安全 效率低
简单看使用程序代码,可以互换。
举例
package Stringtest;
public class test3 {
public static void main(String[] args) {
StringBuilder buil1 = new StringBuilder("01234");
StringBuilder buil2 = new StringBuilder("5678");
System.out.println(buil1.append(buil2) ); //buil1=buil1+buil2
System.out.println(buil1.insert(2,"插入的"));
StringBuffer buf1 = new StringBuffer("01234");
StringBuffer buf2 = new StringBuffer("5678");
System.out.println(buf1.append(buf2) ); //buf1=buf1+buf2
System.out.println(buf1.insert(2,"插入的"));
}
}
012345678
01插入的2345678
012345678
01插入的2345678
DecimalFormat
对小数位进行格式化,保留几位小数
. 表示小数点
0和# 表示数字位,保留几位就几个0或者#
package Stringtest;
import java.text.DecimalFormat;
public class test4 {
public static void main(String[] args) {
double my_num = Math.sqrt(2d); //squrd(double number 2.0)=1.414...
//.表示小数点
// 0或者#表示数字
DecimalFormat df = new DecimalFormat(".0000####");//保留小数点后8位数
System.out.println(df.format(my_num));
}
}
1.41421356
Process finished with exit code 0
Object类(注意区分O大o小写)
大写的Object表示的是总类/基类。(小写的object表示一个实例。)
基类是所有object-对象-的父类。Object类可以存储任何对象。作为参数可以接受任何对象。作为返回值,可返回任何对象。
常用方法
重点:toString()/equals()
简单了解:getClass() / hashCode() /clone() / finalize()
多线程等后面再讲:notify() /notifyAll() / wait() /wait(xx) /wait(xx,yy)
- toString()
返回对象的字符串表现形式,默认会展示hashcode等细节信息。常常被用户重写override。
package Class;
public class Test{
public static void main(String[] args) {
Animal cat = new Animal();
Animal dog = new Animal();
cat.age=3;
cat.name="catty";
dog.age=2;
dog.name="doggy";
Animal dog2 =dog;
//hashcode method
System.out.println(cat.hashCode());
System.out.println(dog.hashCode());
System.out.println(dog2.hashCode());//注意这里的dog2和dog指向的是同一个地址,所以最后获得的hashcode也是一样的
//toString method
System.out.println(cat.toString());
System.out.println(dog.toString());
}
}
注意下面的8efb846(16进制数)就是10进制的149928006。
传统的toString默认输出结构为:class父类型(如果有package等还会写的更详细).object所在的子类型@16进制的hashcode(hashcode等下立刻讲,就是一串独有的代码)
比如可能输出com.qf.chapter11.Student@15db9742,在应用中,用户经常overridetoString方法,这样可以自定义输出的细节和内容。
运行结果:
149928006
713338599
713338599
Class.Animal@8efb846
Class.Animal@2a84aee7
Process finished with exit code 0
下面对toString内容进行改写(因为结构不一样,每个自定义的class里面都得重写一遍toString method)。
比如想要根据object不同,输出Animal里面的不同动物的name或者age。先更改Animal.class文件(IDEA alt+insert+(再加Fn-华硕电脑)-快速生成有惊喜):
package Class;
public class Animal {
private int age =0;
private String name;
public void eat(){
System.out.println("animal is eating");
};
public Animal(){
//auto generated constractor
}
public Animal(String con_name, int con_age){
this.name=con_name;
this.age=con_age;
}
public void setAge(int age) {
this.age = age;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public String getName() {
return name;
}
//public String toString() {
// return this.name+":"+this.age;
//}
//自己写的toString,输出比较简单
@Override
public String toString() {
return "Animal{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
//这是alt+insert(+Fn)自动生成的toString method
}
package Class;
public class Test{
public static void main(String[] args) {
Animal cat = new Animal();
Animal dog = new Animal();
cat.setAge(3);
cat.setName("catty");
dog.setAge(2);
dog.setName("doggy");
//toString method
System.out.println(cat.toString());
System.out.println(dog.toString());
}
}
运行结果:
Animal{age=3, name='catty'}
Animal{age=2, name='doggy'}
- equals()
刚才在String class已经介绍过这个方法,现在我们针对Object大类中的equals方法做更多说明。
作用:返回一个boolean,判断两个对象x,y是否相等。主要比较的其实是他们指向的地址。
用法: x.equals(y)
默认实现为(this==obj),比较两个对象地址是否相同。
对于输入细节(name,age)完全相同两个object,也会比较出false,因为比较的是objects们指向的地址。
如果用户希望同样字符内容的object被认为是相同的,程序员需要自己对equals()method进行override。
事实上,equals也是经常被程序员重写的一个method。
先使用默认的equals进行比较:
package Class;
public class Test{
public static void main(String[] args) {
Animal cat = new Animal();
Animal dog = new Animal();
cat.setAge(3);
cat.setName("catty");
dog.setAge(2);
dog.setName("doggy");
Animal dog2 =dog;
//equals() method
System.out.println(cat.equals(dog));
System.out.println(dog2.equals(dog));
System.out.println(dog3.equals(dog2));//dog3和dog2都是名叫doggy,2岁,但是比较的结果为false,因为比较的是地址
}
}
运行结果:
false
true
false
Process finished with exit code 0
小tips:不同的class里面对equals重写的内容是不一样的
例如系统默认Object class里面仅仅比较地址
而String class系统默认除了比较地址之外,也会比较字符串的内容,只要有一相符合都算true
IDEA用ctrl+n可以快速搜索不同class文件的位置并打开,从而可以查看method实现的具体细节,观察内置代码的不同。
现在对本例中equals method进行改写
改写逻辑
1.先比较两个引用是否指向同一个对象?判断地址是否一样,地址一样肯定是true;
2.判断obj是否是null,如果是,得false
3.判断两个引用指向的实际对象类型是否一致,如果不是,直接false
4.判断他们是不是有父子类关系,如果是,直接强制类型转换,然后依次比较各个属性值是否相同
package Class;
public class Animal {
private int age =0;
private String name;
public void eat(){
System.out.println("animal is eating");
};
public Animal(){
//auto generated constructor
}
public Animal(String con_name, int con_age){
this.name=con_name;
this.age=con_age;
}
public void setAge(int age) {
this.age = age;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public String getName() {
return name;
}
//public String toString() {
// return this.name+":"+this.age;
//}
@Override
public String toString() {
return "Animal{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
public boolean equals(Object obj) {
if(this==obj){return true;}
if(obj==null){return false;}
//if(this.getClass()!=obj.getClass()){return false;}
//上面这种方法应用了下面要介绍的方法getClass method,我们还没学过,
//下面自建代码完成可类似getClass的功能
if(obj instanceof Animal){//如果obj是Animal的子类
Animal x = (Animal)obj; //进行强制类型转换
if(this.name.equals(x.getName()) && this.age==x.getAge()){
//如果当前名字等于obj的名字,之前介绍过,String类系统默认重写了equals
//String类的equals不光比较地址还比较字符串,有一个为true都算。
//剩下int比较,就可以直接用==比较了。当前age等于obj的age
return true;
}
}
return false;
}
}
主程序
package Class;
public class Test{
public static void main(String[] args) {
Animal cat = new Animal();
Animal dog = new Animal();
cat.setAge(3);
cat.setName("catty");
dog.setAge(2);
dog.setName("doggy");
Animal dog2 =dog;
Animal dog3 = new Animal();
dog3.setAge(2);
dog3.setName("doggy");
//equals() method
System.out.println(cat.equals(dog));
System.out.println(dog2.equals(dog));
System.out.println(dog3.equals(dog));
}
}
false
true
true
- getClass()
用法object.getClass()
返回此objects所属的类名,常用于两个objects判断比较。
举例
package Class;
public class Test{
public static void main(String[] args) {
Animal cat = new Animal();
Animal dog = new Animal();
cat.age=3;
cat.name="catty";
dog.age=2;
dog.name="doggy";
Class class1=cat.getClass();
Class class2=dog.getClass();
if(class1==class2){
System.out.println("they are the same class");
}
else{
System.out.println("they are not the same class");
}
}
}
package Class;
public class Animal {
int age =0;
String name;
public void eat(){
System.out.println("animal is eating");
};
}
运行结果
they are the same class
Process finished with exit code 0
- hashCode()
用法:object.hashCode()
作用:返回该对象的hashcode哈希码值
哈希码值是根据对象的地址,或字符串,或数字使用hash算法,计算出来的int类型的数字。它通常用于在数据结构(比如hash表中)快速查找对象。
扩展应用:当用到hashMap和hashSet等集合类,向这些集合中添加对象时候,会使用hashcode确定对象在hash表中的位置,并且可以实现快速查找或快速删除对象的功能。
一般情况下,相同的对象返回的hashcode是一样的。
package Class;
public class Test{
public static void main(String[] args) {
Animal cat = new Animal();
Animal dog = new Animal();
cat.age=3;
cat.name="catty";
dog.age=2;
dog.name="doggy";
Animal dog2 =dog;
//hashcode method
System.out.println(cat.hashCode());
System.out.println(dog.hashCode());
System.out.println(dog2.hashCode());
//注意这里的dog2和dog指向的是同一个地址,所以最后获得的hashcode也是一样的
}
}
149928006
713338599
713338599
- clone()
这个方法就是在别的地址,新做一个和模板objectA一模一样的objectB复制品。
所以objectA和objectB的内容是一样的但是地址不一样。
使用须知:(1&2需要同时达到要求)
- clone()方法要求类实现Cloneable接口,没有接口会报错CloneNotSupportedException异常。
- 同时clone方法会被默认标记为protected,仅允许字段或方法能在子类内部使用,但是不允许通过子类的实例在子类外部使用。
使用方法(1&2步需要同时满足):
- 在MyObject类中override重写clone()方法,覆盖掉继承自父类的clone()方法,则会编译通过,不再有因为protected引起的不可见问题。(注意覆盖方法时,要将权限改成public)
也就是说,想要使用时候一般需要重写clone方法,并且第一步你的子类需要包含这样一段代码:
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
- 在Object.clone()的源码中要求,被克隆的类必须实现Cloneable接口,Cloneable是一个标记接口,它不包含任何方法,唯一的作用是用于在类型检查中通过instanceof检查。如果没有实现该接口,在克隆时会出CloneNotSupportedException异常。
结合1&2,你的子类至少应该包含下面的1&2模块,才可以成功调用clone() method
class MyObject implements Cloneable{ // 定义一个MyObject类,是Object类的子类,同时连接着Cloneable接口
// 注意要将权限改成public,否则不在同个包下且非子类的类中是无法调用protected方法的
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
举例,新建一个学生类,在里面建立学生s1 s2 s3 用clone复制学生s4,模板取自s1,比较s1 s4两者的区别。
package ClassDemo;
public class Test2 {
public static void main(String[] args) throws CloneNotSupportedException{
Student s1 = new Student(13,"F","Rose");
Student s2 = new Student(14,"M","Juli");
Student s3 = new Student(15,"F","Cosa");
try {
Student s4 = (Student) s1.clone();
System.out.println(s4.getAge()+s4.gender+s4.getName());
System.out.println(s4==s1);//==比较的是地址,这里s4和s1地址不可能相同,所以得到false
s4.setAge(12);
s4.setGender("M");
s4.setName("Dodo");
System.out.println(s4.getAge()+s4.gender+s4.getName());
System.out.println("s4 has been set");
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
} finally {
}
}
}
package ClassDemo;
public class Student implements Cloneable{
public int age = 0;
public String gender;
public String name;
public Student() {
}
public Student(int age, String gender, String name) {
this.age = age;
this.gender = gender;
this.name = name;
}
public int getAge() {
return age;
}
public String getGender() {
return gender;
}
public String getName() {
return name;
}
public void setAge(int age) {
this.age = age;
}
public void setGender(String gender) {
this.gender = gender;
}
public void setName(String name) {
this.name = name;
}
public Object clone() throws CloneNotSupportedException{
return super.clone();
}
}
运行结果:
13FRose
false
12MDodo
s4 has been set
Process finished with exit code 0
- finalize()
标注为@Deprecated也就是过期了过时了,不建议再使用了,since9就是JDK9以后就不再使用了,传统上一般用做“清场”工作,释放内存。由于有可能导致无限循环导致无法"清场",JDK9后 finalize()不建议使用。
@Deprecated(since="9", forRemoval=true)
protected void finalize() throws Throwable { }
一般是程序自动调用的,不是程序员调用的。
当对象被判定为垃圾对象(没有有效引用指向此对象)时,由JVM自动调用此方法,用以标记垃圾对象,进入回收队列。
垃圾回收:由GC销毁对象,释放数据存储空间。
自动回收机制:JVM内存耗尽,一次性回收所有垃圾对象。
手动回收机制:使用System.gc();通知JVM执行垃圾回收。
- notify() 多线程那再讲,略
- wait() 多线程那再讲,略