文章目录
JAVA常用类:
API概述:
- 概念:
API (Application Programming Interface) 代指应用程序编程接口。 - JAVA API:
就是Java提供给我们使用的类,这些类将底层的实现封装了起来,
我们不需要关心这些类是如何实现的,只需要学习这些类如何使用。
Object 类:
- 概念:Object:顶层父类,所有的类,都是直接或间接继承自它。
- 构造方法:
回想面向对象中为什么说:public Object()
子类的构造方法默认访问的是父类的无参构造方法,所以JAVA中所有对象都会调用Object的构造方法,同时它们都是Object的子类(那么如果Object中有一些成员方法,则这些子类都会继承这些方法或者改写这些方法,以达到自己想要的效果!)
Object的hashCode()方法:
- 示例:
Object obj = new Object(); int hashCode = obj.hashCode(); --------------- 输出: 460141958
- 特点:
a. 返回该对象的哈希码值。默认情况下,该方法会根据对象的地址来计算。
b. 不同对象的,hashCode()一般来说不会相同。 但是,同一个对象的hashCode()值肯定相同。
c. 不是对象的实际地址值,可以理解为逻辑地址值。
Object类的getClass()方法:
- 概念:返回对象对应的类的字节码文件对象!
- 示例:
Object obj = new Object(); Class aClass = obj.getClass(); //Object.class---->JVM为这个文件创建的对象, //aClass就是获取出来的代表Object.class这个文件(类)的字节码对象! System.out.println(aClass.getName());//可以通过Class类中的一个方法,获取对象的真实类的全名称。 ------------------ 输出: java.lang.Object
- 特点:
a. 返回此 Object 的运行时类。
b. 可以通过返回的文件字节码对象中的一个方法,获取对象的真实类的全名称。
Object类的toString()方法:
- 概念:返回该对象的字符串表示。
- 示例:
- 源码追溯:
查看println源码可得到:System.out.println(obj); ------------------------- 输出: java.lang.Object@1b6d3586
查看String.valueOf(x)的源码可得到:(看出来其执行的实际是obj.toString()方法返回的字符串!)public void println(Object x) { String s = String.valueOf(x); synchronized (this) { print(s); newLine(); } }
继续追踪查看obj.toString()方法的源码可得到:public static String valueOf(Object obj) { return (obj == null) ? "null" : obj.toString(); }
public String toString() { return getClass().getName() + "@" + Integer.toHexString(hashCode()); }
- 结论:我们通过追踪System.out.println(obj)的源码发现,其打印的是obj.toString()方法返回的字符串!,由此可以通过prinln(obj)来得到一些有用的反馈,如果在改写了相应类的toString()方法后!
- 源码追溯:
- 问题:
由于默认情况下的数据对我们来说没有意义,一般建议重写该方法。
那么怎么重写呢? 一般是将该类的所有的成员变量组成返回即可
示例:public class MyTest extends Object{ public static void main(String[] args) { //常用类:Java给我们提供好的类,我们学习他里面封装的功能,灵活去用,完成我们的业务需求。 //Object :顶层父类,所有的类,都是直接或间接继承自他 Object obj = new Object(); System.out.println(obj); String string = obj.toString(); System.out.println(string); /* public String toString () { return getClass().getName() + "@" + Integer.toHexString(hashCode()); } */ //我们认为老是打印地址值,不是我想要的。我想打印一些我们认为有用的数据,那么子类可以重新toString() Student student = new Student(); System.out.println(student); } } class Student{ int a=200; @Override public String toString() { return "Student{" + "a=" + a + '}'; } } ---------------- 输出: java.lang.Object@1b6d3586 java.lang.Object@1b6d3586 Student{a=200}
Object类的equals()方法:
- 概念:比较两个对象的地址值是否相同
- 用法:
boolean b = obj.equals(obj2);
- 查看equals()方法的源码,发现与"=="一致。
示例:(这是Object源码中对equals方法的定义!)public boolean equals(Object obj) { return (this == obj); }
- == 和 equals()有什么区别?
a. == 是一个比较运算符,他可以比较基本数据类,也可以比较引用数据类型
b. == 比较基本数据类型,比较的是两个值是否相同,比较引用数据类型,比较的是地址值是否相同
c. equals() 是Object类中的方法,只能比较引用数据类型,比较的是两个对象的地址值。 - 问题:
默认equals()方法比较两个对象的地址值,是否相同,认为意义不大。
如果两个对象的成员变量值一模一样就认为两对象一样。
(可以通过子类可以重写父类的equals()方法来实现!) - 示例:
public class MyTest { public static void main(String[] args) { Student s1 = new Student("张三", 23); Student s2 = new Student("张三", 24); // System.out.println(s1==s2); //默认 equals(s2);方法比较两个对象的地址值,是否相同,我认为意义不是很大。 //我认为两个对象的成员变量的值,一模一样,就认为两个对象一样。 //子类就可以重新父类equals(s2)按照我定义的比较方式来比较 boolean b = s1.equals(s2);// System.out.println(b); } } class Student{ private String name; private int age; public Student() { } public Student(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override //重写部分! public boolean equals(Object obj) { // 我认为两个对象的成员变量的值,一模一样,就认为两个对象一样。 // 非空判断: if(obj == null){ return false; } // 从效率方面考虑: if (this == obj){ return true; } // 从健壮性考虑: // 判断他传过来的这个引用(对象)是不是该类的引用(对象) // instancedof()判断一个对象(引用)是不是该类型的一个引用 if (!(obj instanceof Student)){ //obj 是否是 Student 的实例对象? return false; } Student transform_obj = ((Student)(obj)); //向下转型! // String 类是引用类型,你用 == 来比较两个字符串,比较的是两个字符串的地址值是否一样,如果想要比较两个字符串的字面值内容是否一样,则调用equals()方法来比较! // String 类也是继承于Object方法的! // String 类也重写了equals()方法来比较两个字符串的内容是否相同(很多类都重写类Object的equals()方法!) //旧版: //if (transform_obj.age == this.age && transform_obj.name == this.name){ // return true; //} //改进版本 if (transform_obj.age == this.age && transform_obj.name.equals(this.name)){ return true; } return false; } } -------------------------------------------- 输出: true
- 改进版:
示例:public class MyTest { public static void main(String[] args) { Student s1 = new Student("张三", 23); Student s2 = new Student("张三", 23); Student s3 = new Student("张三",25); Dog d1 = new Dog("二哈",15); // System.out.println(s1==s2); //默认 equals(s2);方法比较两个对象的地址值,是否相同,我认为意义不是很大。 //我认为两个对象的成员变量的值,一模一样,就认为两个对象一样。 //子类就可以重新父类equals(s2)按照我定义的比较方式来比较 System.out.println(s1.equals(s2)); System.out.println(s1.equals(s3)); System.out.println(s1.equals(s1)); System.out.println(s1.equals(d1)); System.out.println(s1.equals(null)); } } class Student{ private String name; private int age; public Student() { } public Student(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public boolean equals(Object obj) { // 我认为两个对象的成员变量的值,一模一样,就认为两个对象一样。 // 非空判断; if(obj == null){ System.out.println("obj对象为空!,不能参与比较!"); return false; } //从效率方面考虑: if (this == obj){ System.out.println("两个对象是同一个对象!,无需比较!"); return true; } // 从健壮性方面考虑: // 判断他传过来的这个引用(对象)是不是该类的引用(对象) // instancedof()判断一个对象(引用)是不是该类型的一个引用 if (!(obj instanceof Student)){ System.out.println("obj不是Student类型的对象!,不能进行比较!"); return false; } Student transform_obj = ((Student)(obj)); //向下转型 if (transform_obj.age == this.age && transform_obj.name.equals(this.name)){ System.out.println("这两个对象同属于一个类,且成员属性的值都相同!"); return true; } System.out.println("这两个对象同属于一个类,但成员属性的值不相同!"); return false; } } class Dog{ private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public Dog() { } public Dog(String name, int age) { this.name = name; this.age = age; } } ----------------------------------------------- 输出: 这两个对象同属于一个类,且成员属性的值都相同! true 这两个对象同属于一个类,但成员属性的值不相同! false 两个对象是同一个对象!,无需比较! true obj不是Student类型的对象!,不能进行比较! false obj对象为空!,不能参与比较! false
- 注意:下面两者的效果是一样的!
原因:.java 文件被 javac 编译了以后会生成 .java 文件中所写的所有类的 .class 文件,然后,在主函数运行的过程中,如果用到哪个类的 .class 文件,就要将 .class 文件加载入内存从而生成它的文件字节码对象。(只加载一次!, 所以该对象在JVM的方法区中应该只存在一个)。 且 obj.getClass() 方法返回就是这个obj对应类的文件字节码对象。所以可以利用这个方法判断两个对象是否属于同一个类,进而再进行向下转型!if (!(obj.getClass() == this.getClass())){ System.out.println("obj不是Student类型的对象!,不能进行比较!"); return false; } ------------------------------------------------ if (!(obj instanceof Student)){ System.out.println("obj不是Student类型的对象!,不能进行比较!"); return false; }
Object类的clone() 方法(浅克隆):
- 概念:创建并返回此对象的副本(也是一个对象)。
- 使用方法:
示例:
public class MyTest {
public static void main(String[] args) throws CloneNotSupportedException {
// protected Object clone () 创建并返回此对象的副本。
//clone()方法是受保护的 你让子类重写一些,把方法的修饰符改为public 重写逻辑还是使用父类的
Dog dog = new Dog("旺财", 3);
System.out.println(dog.name);
System.out.println(dog.age);
Dog dog2 = (Dog) dog.clone();//这里需要进行向下转换!(dog.clone()返回的是java.lang.Object对象!)
dog2.name="小白";
System.out.println(dog2.name);
System.out.println(dog2.age);
// CloneNotSupportedException
}
}
//Cloneable 这个借口什么方法都没实现,只是一个空壳子!
//像这种接口中没有任何的抽象方法的接口,我们称之为标记接口
//标记接口,该该类打一个标记,是告诉JVM我要完成某种操作
class Dog extends Object implements Cloneable{
String name;
int age;
public Dog(String name, int age) {
this.name = name;
this.age = age;
}
@Override //这里重写的应该是超类(Object)的clone()方法!
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
----------------------
输出:
旺财
3
小白
3
-
对象浅克隆要注意的细节:
1. 如果一个对象需要调用clone的方法克隆,那么该对象所属的类必须要实现Cloneable接口。
2. Cloneable接口只不过是一个标识接口而已,没有任何方法。
3. 对象的浅克隆就是克隆一个对象的时候,如果被克隆的对象中维护了另外一个类的对象,这时候只是克隆另外一个对象的地址,而没有把另外一个对象也克隆一份。
4. 对象的浅克隆也不会调用到构造方法的。 -
问题:为啥protected的clone方法不能访问?(这个参考老师上课给的权限修饰符的作用范围是有歧义的,解释不通!)
深浅拷贝的问题:
- 从一个代码的示例来引出深浅拷贝的问题:
示例:
public class MyTest {
public static void main(String[] args) throws CloneNotSupportedException {
//创建一个狗粮类对象
DogFood dogFood = new DogFood("双汇王中王");
Dog dog = new Dog("小白", 23, dogFood);
dog.dogFood.name="金锣火腿肠";
//克隆狗对象
Dog dog2= (Dog) dog.clone();
dog2.name="旺财";
dog2.age=4;
dog2.dogFood.name = "泡面搭档火腿肠";
System.out.println("--------------------");
System.out.println(dog.name);
System.out.println(dog.age);
System.out.println(dog.dogFood.name); // 金锣火腿肠 or 泡面搭档火腿肠 ?
System.out.println("--------------------");
System.out.println(dog2.name);
System.out.println(dog2.age);
System.out.println(dog2.dogFood.name); // 泡面搭档火腿肠
}
}
class Dog implements Cloneable{
String name;
int age;
//在这个Dog类里面维护着另一个类的对象
DogFood dogFood;
public Dog(String name, int age, DogFood dogFood) {
this.name = name;
this.age = age;
this.dogFood = dogFood;
}
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
class DogFood{
String name;
public DogFood(String name) {
this.name = name;
}
}
------------------------
输出:
--------------------
小白
23
泡面搭档火腿肠
--------------------
旺财
4
泡面搭档火腿肠
- 结论:从上述的输出结果中可以看到,dog和dog2的dogFood指向的应该是同一个对象!,所以clone()是一个浅克隆!
- 上述代码执行过程中的内存图示:(可以更清楚地看到上面的结论!)
- 解决办法:
a. 那如果遇到这种问题,该如何解决呢?就得使用深克隆的办法。(因为浅克隆不会克隆嵌套的子对象!)
b. 对象的深克隆(后面讲):采用IO流来实现,使用 ObjectOutputStream 将对象写入文件中,然后再用ObjectInputStream读取回来便可得到另一个对象,这个对象相当于是克隆得到的一样(同时也会构造它的嵌套子对象,所以称为深克隆(拷贝))