常见对象(API概述以及Object类的概述)
API(Application Programming Interface)
应用程序编程接口
Java API
就是Java提供给我们使用的类,这些类将底层的实现封装了起来,
我们不需要关心这些类是如何实现的,只需要学习这些类如何使用。
Object类概述
类层次结构的根类,即所有类的顶层父类
所有类都直接或者间接的继承自该类
构造方法
public Object(){
}
回想面向对象中为什么说:
子类的构造方法默认访问的是父类的无参构造方法
常见对象(Object类的hashCode()方法)
代码示例:
package org.westos.demo2;
/**
* @Author: Administrator
* @CreateTime: 2019-04-20 09:21
*/
public class MyTest {
public static void main(String[] args) {
Object obj = new Object(); //创建Object类对象
//看下Object中的成员方法,长按ctrl后鼠标点击Object类便可查看
//int hashCode () 返回该对象的哈希码值。不同对象的哈希码值,是不一样的。
//System.out.println(obj); 如果执行这条语句,打印的是局部变量obj里存放的地址值
int i = obj.hashCode();
System.out.println(i);
Object obj2 = new Object();
int i1 = obj2.hashCode();
System.out.println(i1);
}
}
Object类在 java.lang 包下, java.lang包下的类,使用时可以不用导包
查看到Object类中的hashCode()方法为:
public native int hashCode();
方法特点:
-
返回该对象的哈希码值。默认情况下,该方法会根据对象的地址来计算。
-
不同对象的,hashCode()一般来说不会相同。但是,同一个对象的hashCode()值肯定相同。
-
不是对象的实际地址值,可以理解为逻辑地址值。
上述代码执行结果为:
356573597
1735600054
常见对象(Object类的getClass()方法)
package org.westos.demo2;
/**
* @Author: Administrator
* @CreateTime: 2019-04-20 09:28
*/
public class MyTest2 {
public static void main(String[] args) {
// Class<?> getClass () 返回该类的字节码文件对象
//万物皆对象
//Object.class 字节码文件加载进内存-----JVM就会为Object.class文件创建一个对象。
Object obj = new Object();
Class clazz = obj.getClass(); //Object.class---->字节码文件对象。
Object obj2 = new Object();
Class aClass = obj2.getClass();
Object obj3 = new Object();
Class aClass1 = obj3.getClass();
System.out.println(obj==obj2); //false //局部变量中存放的是地址值,对象不一样,所以地址值肯定不一样
System.out.println(clazz==aClass); //true
System.out.println(aClass==aClass1);//true 因为类只有一个字节码文件,
}
}
在Java语言中遵循万物皆对象的特点,所以在类的字节码文件被加载进内存后,JVM也会为该文件创建一个对象
查看到的Object类中的getClass方法为:
public final native Class<?> getClass();
方法特点:
-
返回此 Object 的运行时类,即类的字节码文件对象。
-
可以通过Class类中的一个方法,获取对象的真实类的全名称。
上述代码运行结果为:
false
true
true
原因不再详述。
常见对象(Object类的toString()方法)
代码示例1:
package org.westos.demo3;
/**
* @Author: Administrator
* @CreateTime: 2019-04-20 09:36
*/
public class MyTest {
public static void main(String[] args) {
Object obj = new Object();
String s = obj.toString(); //获取该对象的地址值的字符串表现形式
System.out.println(s);
Object o = new Object();
System.out.println(o.toString());
System.out.println(o);
}
}
}
在上述代码中调用了Object类的toString方法,该方法的具体实现如下
public String toString () {
return this.getClass().getName() + "@" + Integer.toHexString(this.hashCode());
该方法可以返回对象的地址值的字符串表现形式,所以上述代码的运行结果为:
java.lang.Object@1540e19d
java.lang.Object@677327b6
java.lang.Object@677327b6
需要说明的一点,在我们之前的程序中为了区分两个对象是否一样,通常会采用System.out.println(o);
语句来直接打印对象的地址来进行判断,实际上系统会默认在我们打印的对象后面加上.toString()
来调用toString()
方法获取对象的地址值的字符串表现形式。
但是也会存在下面的某种情况,当我打印对象时是想获得该对象的成员变量,那么我就可以在自定义类中对toString()
进行重写。可以查看以下代码。
代码示例2:
package org.westos.demo3;
import java.net.SocketTimeoutException;
public class MyTest2 {
public static void main(String[] args) {
Student student = new Student();
int i = student.hashCode();
System.out.println(i);
Class<? extends Student> aClass = student.getClass();
System.out.println(aClass.getName());
System.out.println(student.toString());
Student student1 = new Student("张三", 23);
//System.out.println(student1.getName());
//System.out.println(student1.getAge());
student1.show();
//一般情况下,我们自定义的类,都喜欢重写父类toString()方法,让他打印成员变量的值,当你根据你实际情况,可以打印其他任何内容
System.out.println(student1.toString());
//子类如果对父类的方法实现不满意,子类可以重写
}
}
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;
}
public void show(){
System.out.println(name+"==="+age);
}
@Override
public String toString() {
return "姓名:"+this.name+"==="+"年龄"+this.age;
}
}
在上述代码中我定义了一个测试类和自定义类Student,在自定义类中对toString()方法进行了重写,之后再测试类中就可以通过直接打印对象名来获得对象的成员变量,所以上述代码最终运行结果为:
356573597
org.westos.test.Student
姓名:null===年龄0
张三===23
姓名:张三===年龄23
在该代码中还调用了获取到的类的字节码文件对象的方法,这个方法可以返回该对象的名字
源代码:
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(this.hashCode());
}
它的值等于:
getClass().getName() + '@' + Integer.toHexString(对象名.hashCode())
由于默认情况下的数据对我们来说没有意义,一般建议重写该方法。 怎么重写?一般是将该类的所有的成员变量组成返回即可,也可以使用软件的快捷键来快速重写
利用软件重写toString()方法 alt+insert 选toStirng()方法,就会重写
常见对象(Object类的equals()方法)
A:案例演示
a:指示其他某个对象是否与此对象“相等”。
源代码:
public boolean equals(Object obj) {
return (this == obj);
}
b:默认情况下比较的是对象的引用是否相同。
c:由于比较对象的引用没有意义,一般建议重写该方法。一般用于比较成员变量的值是否相等
d:==和equals()的区别。(面试题)
常见对象(Object类的equals()方法代码优化)
equals()方法在Object类的定义中源码为:
public boolean equals(Object obj) {
return (this == obj);
}
从代码中可以看出该方法比较的是两的对象的地址值,通过下面的代码引入equals()方法
代码示例:
package org.westos.demo5;
/**
* @Author: Administrator
* @CreateTime: 2019-04-20 10:03
*/
public class MyTest {
public static void main(String[] args) {
Object obj = new Object();
Object obj2 = new Object();
// boolean equals (Object obj) 判断两个对象的地址值是否相同
System.out.println(obj == obj2);
boolean b = obj.equals(obj2);
System.out.println(b);
System.out.println("---------------------");
Student s1 = new Student("张三", 23);
Student s2 = new Student("张三", 23);
System.out.println(s1 == s2);//false
//System.out.println(s1.equals(s2)); //true
/*我觉得,s1.equals(s2) 比较两个对象的地址值,是否相同,对我来说意义不大,
我认为的是,只要两个对象的成员变量的值一样,我就认为这两个对象一样。
那么我就得重写父类的equals()方法,让他去比较两个对象的成员变量的值是否相同 */
boolean b1 = s1.equals(new Teacher());//ClassCastException 类型转换异常
System.out.println(b1);
System.out.println("---------------");
boolean b2 = s1.equals(s1);
System.out.println(b2);
}
}
class Student {
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Student)) {
return false;
}
//向下转型
Student stu = (Student) obj;
//字符串类,也认为父类的equals方法不满意,字符串这个类想要比较,两个字符串的内容是否相同
// 所以字符串也重写了父类的equals方法 去比较两个字符串的内容是否相同
return this.name.equals(stu.name) && this.age == stu.age;
}
}
class Teacher {
}
在该代码中,可以看到equals()方法在Studnet类中被重写,重写需要实现比较成员变量的值的逻辑,实现描述如下:
- 如果是对象自己跟自己比,直接返回true
- 若不满足上述条件,则需要比较两个对象的成员变量的值,但在比较值前需要判断要和我比较的对象和我本身是不是同一类,如果不是,两个对象的成员变量的值肯定不相等,直接返回false,而在执行刚刚提到的判断时使用了一个关系运算符
instanceof
,作用是判断一个对象,是否是,该类型的一个对象,具体代码实现如下:
if (!(obj instanceof Student)) {
return false;
}
- 在判断完成后,如果是同一类或者有继承关系,就会执行下一阶段:判断成员变量的值,但是由于Object类是所以类的直接或间接父类,所以要想比较obj对象和student对象的成员变量值就必须让obj对象向下转型,转型完成后进行判断并且返回true或者false
通过重写equal()方法可以实现:
-
提高效率
-
提高健壮性(instanceof)
常见对象(Object类的clone() 方法)
clone()的权限修饰符是受保护的,在用的时候,让该类重写该方法,并把该方法的权限修饰符改为public
对象的克隆有两种:浅克隆和深克隆
使用clone()方法采用的是浅克隆的方式
对象浅克隆要注意的细节:
-
如果一个对象需要调用
clone
的方法克隆,那么该对象所属的类必须要实现Cloneable接口。 -
Cloneable接口只不过是一个标识接口而已,没有任何方法。
-
对象的浅克隆就是克隆一个对象的时候,如果被克隆的对象中维护了另外一个类的对象,这时候只是克隆另外一个对象的地址,而没有把另外一个对象也克隆一份。
-
对象的浅克隆也不会调用到构造方法的。
对象的深克隆(后面讲):采用IO流来实现 使用 ObjectOutputStream 将对象写入文件中,然后再用ObjectInputStream读取回来
代码示例
public class MyTest {
public static void main(String[] args) throws CloneNotSupportedException {
Person p1 = new Person("狗娃", 23);
Person p2 = (Person) p1.clone();
p2.name="张三";
p2.age=30;
System.out.println(p1);
System.out.println(p2);
System.out.println("---------------------------");
Address address = new Address("北京");
Person p3 = new Person("王五", 25, address);
Person p4 = (Person) p3.clone();
p4.name="赵六";
p4.age=36;
p4.address.city="上海";
System.out.println(p3);
System.out.println(p4);
}
}
class Person implements Cloneable{ //主方法中要对p1、p2对象进行浅克隆,所以person类必须要实现Cloneable接口
public String name;
public int age;
public Address address;
public Person(String name, int age) { //有参构造
this.name = name;
this.age = age;
}
public Person(String name, int age, Address address) { //有参构造的重载
this.name = name;
this.age = age;
this.address = address;
}
@Override
public Object clone() throws CloneNotSupportedException { //重写,要抛出异常,将权限修饰符修改为
return super.clone();
}
@Override
public String toString() { //重写Tostring方法
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", address=" + address +
'}';
}
}
class Address{
public String city;
public Address(String city) {
this.city = city;
}
@Override
public String toString() {
return "Address{" +
"city='" + city + '\'' +
'}';
}
}
- 如果在没有实现 Cloneable 接口的实例上调用 Object 的
clone
方法,则会导致抛出
CloneNotSupportedException
异常。所以在主方法后面会出现throws CloneNotSupportedException
- 在重写clone方法时要将权限修饰符修改为public,而且方法内部还是使用clone方法在父类当中的逻辑