文章目录

  • 如何理解根父类
  • Object类的方法
  • equals()
  • toString()
  • getClass()
  • hashCode()
  • clone()
  • finalize()
  • native关键字的理解


如何理解根父类

java.lang.Object是类层次结构的根类,即所有其它类的父类。每个类都使用Object作为超类。

java 不知道object类型 java中object_java

  • Object类型的变量与除Object以外的任意引用数据类型的对象都存在多态引用
method(Object obj){…} //可以接收任何类作为其参数
Person o = new Person();  
method(o);
  • 所有对象(包括数组)都实现这个类的方法
  • 一个类没有特别指定父类,那么默认则继承自Object类
public class Person {
	...
}
//上面和下面是等价的
public class Person extends Object {
	...
}

Object类的方法

根据JDK源代码及Object类的API文档,Object类中包含的方法有11个,但是今天只看其中主要的5个。

equals()

所有类都继承了Object,那么也就获得了equals()方法,且还可以重写方法。

equals():

  • 只能比较引用类型,比较是否指向同一个对象
  • 格式:obj1.equals(obj2)
  • 特例:当用equals()方法进行比较时,对类File、String、Date及包装类来说,是比较类型及内容而不考虑引用的是否是同一个对象,因为在这些类中重写了Object类的equals()方法
  • 当自定义equals()方法时,可以重写方法,用来比较两个对象的内容是否一样

重写equals()方法的原则:

  • 对称性:如果x.equals(y)返回是true,那么y.equals(x)也应该返回是true
  • 自反性:x.equals(x)必须返回是true
  • 传递性:如果x.equals(y)返回是true,且y.equals(z)返回是true,那么z.equals(x)也应该返回true
  • 一致性:如果x.equals(y)返回是true,只要x和y内容不变,无论重复x.equals(y)多少次,都是返回true
  • 任何情况下,x.equals(null)都是返回false,x.equals(和x是不同类型的对象)都是返回false

重写举例:

class User{
	private String host;
	private String username;
	private String password;
	public User(String host, String username, String password) {
		super();
		this.host = host;
		this.username = username;
		this.password = password;
	}
	public User() {
		super();
	}
	public String getHost() {
		return host;
	}
	public void setHost(String host) {
		this.host = host;
	}
	public String getUsername() {
		return username;
	}
	public void setUsername(String username) {
		this.username = username;
	}
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
	@Override
	public String toString() {
		return "User [host=" + host + ", username=" + username + ", password=" + password + "]";
	}
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		User other = (User) obj;
		if (host == null) {
			if (other.host != null)
				return false;
		} else if (!host.equals(other.host))
			return false;
		if (password == null) {
			if (other.password != null)
				return false;
		} else if (!password.equals(other.password))
			return false;
		if (username == null) {
			if (other.username != null)
				return false;
		} else if (!username.equals(other.username))
			return false;
		return true;
	}
}

= =:

  • 基本类型比较的是值:只要两个变量的值相等,即为true
  • 引用类型比较引用(是否指向同一个对象):只有指向同一个对象时,才返回true

注意:
用来进行比较时,符号两边的数据类型必须兼容(可自动转换的基本数据类型除外),否则编译报错。

= =和equals的区别:

  • = =即可以比较基本数据类型也可以比较引用类型,对于基本类型就是比较数值,对于引用类型就是比较内存地址
  • equals是属于java.lang.Object类里面的方法,如果该方法没有被重写,默认也是= =,String等类的equals方法是被重写过的,而且String类在日常开发中用的较多,形成了equals是比较值的错误观点,这点要注意
  • 具体要看自定义类型里有没有重写Object的equals方法来判断
  • 通常情况下,重写equals方法,会比较类中的相应属性是否都相等

toString()

public String toString():默认情况下toString()返回的是对象的运行时类型@对象的hashCode值的十六进制形式。

在进行String与其他类型数据的连接操作时,自动调用toString方法,比如:

Date now=new Date();
System.out.println("now="+now); //相当于"now="+now.toString()

如果直接打印对象,默认会调用该对象的toString()方法(Java的引用数据类型的变量中存储的实际上是对象的内存地址,但是Java对外隐藏了内存地址信息,所以不能直接将内存地址显示出来,所以当打印对象时,JVM会调用对象的toString()方法)。

可以根据需要在用户自定义类型中重写toString()方法。

getClass()

public final Class<?> getClass():获取对象的运行时类型。

由于Java有多态现象,所以一个引用数据类型的变量编译时类型与运行时类型可能不一致,因此如果需要查看这个变量实际指向的对象的类型,就需要用getClass()方法。

public static void main(String[] args) {
	Object obj = new Person();
	System.out.println(obj.getClass()); //获取运行时类型
}

hashCode()

public int hashCode():返回每个对象的hash值。

如果重写equals,那么通常会一起重写hashCode()方法,hashCode()方法主要是为了当对象存储到哈希表等容器中时提高存储和查询性能用的,这是因为关于hashCode()有两个常规协定:

  • 如果两个对象的hash值不同,那么这两个对象一定不相等
  • 如果两个对象的hash值是相同的,那么这两个对象不一定相等

重写equals()和hashCode()方法时,要保证满足如下要求:

  • 如果两个对象调用equals返回true,那么要求这两个对象的hashCode值一定是相等的
  • 如果两个对象的hashCode值不同,那么要求这两个对象调用equals方法一定是false
  • 如果两个对象的hashCode值相同,那么这两个对象调用equals可能是true,也可能是false
public static void main(String[] args) {
	System.out.println("Aa".hashCode()); //2112
	System.out.println("BB".hashCode()); //2112
}

clone()

clone()方法将对象复制了一份并返回给调用者,clone()的作用在于复制对象,在复制对象的过程中,首先要分配一个和源对象同样大小的空间,在这个空间中创建一个新的对象。

示例:

public class CloneTest {
	public static void main(String[] args) {
		Animal a1 = new Animal("小黑");
		try {
			Animal a2 = (Animal) a1.clone();
			System.out.println("原始对象:" + a1);
			a2.setName("小黄");
			System.out.println("clone的对象:" + a2);
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
	}
}

class Animal implements Cloneable{
	private String name;
	public Animal() {
		super();
	}
	public Animal(String name) {
		super();
		this.name = name;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	@Override
	public String toString() {
		return "Animal [name=" + name + "]";
	}
	@Override
	protected Object clone() throws CloneNotSupportedException {
		return super.clone();
	}
}

finalize()

当对象被回收时,系统自动调用该对象的finalize()方法,子类可以重写该方法,做一些释放资源的操作。如果重写该方法,让一个新的引用变量重新引用该对象,则会重新激活对象。

永远不要主动调用某个对象的finalize方法,应该交给垃圾回收机制调用。

什么时候被回收:
当某个对象没有任何引用时,JVM就认为这个对象是垃圾对象,就会在之后不确定的时间使用垃圾回收机制来销毁该对象,在销毁该对象前,会先调用finalize()方法(垃圾回收发生具有不可预知性,程序无法精确控制垃圾回收机制的执行)。

垃圾回收机制的调用是由系统来决定的,也可以通过System.gc()或者Runtime.getRuntime().gc()来通知系统进行垃圾回收,会有一些效果,但是系统是否进行垃圾回收依然是不确定的。

示例:

public class FinalizeTest {
	public static void main(String[] args) {
		Person p = new Person("Peter", 12);
		System.out.println(p);
		p = null; //此时对象实体就是垃圾对象,等待被回收,但时间不确定
		System.gc(); //强制性释放空间
	}
}

class Person{
	private String name;
	private int age;
	public Person(String name, int age) {
		super();
		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
	protected void finalize() throws Throwable {
		System.out.println("对象被释放--->" + this);
	}
	@Override
	public String toString() {
		return "Person [name=" + name + ", age=" + age + "]";
	}
}

native关键字的理解

使用native关键字说明这个方法是原生函数,也就是这个方法是用c/c++等非Java语言实现的,并且被编译成了dll,由Java去调用,在定义一个native方法时,并不提供实现体。

为什么要用native方法

虽然Java使用起来非常方便,然而有些层次的任务用Java实现起来不易,或对程序的效率很在意时就会考虑native方法。

例如:有时Java应用需要与Java外面的环境交互,这是本地方法存在的主要原因,当Java需要与一些底层系统如操作系统或某些硬件交换信息时的情况,本地方法正是这样的一种交流机制,它提供了一个非常简洁的接口,而且无需我们去了解Java应用之外的繁琐细节。

native声明的方法,对于调用者可以当做其他Java方法一样使用

一个native方法可以返回任何Java类型,包括非基本类型,而且同样可以进行异常控制。

native方法的存在并不会对其他类调用这些本地方法产生任何的影响,实际上调用这些方法的其他类甚至不知道它调用的是一个本地方法,JVM将控制调用本地方法的所有细节。

如果一个含有本地方法的类被继承,子类会继承这个本地方法并且可以用Java在需要的时候重写该方法。

今天的内容就到这里,喜欢的话点个关注吧,下篇见!