欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

对象类Object

程序员文章站 2022-04-04 23:29:31
...

所有的对象都继承自Object类,它没有成员变量,只有成员方法,而且很多都是native的。

public class Object {

    private static native void registerNatives();    //对native方法进行注册,在类加载时就注册
    static {
        registerNatives();
    }
1、 //Returns the runtime class of this Object. The returned Class object is the object that is locked by  static synchronized methods of the represented class.
    public final native Class<?> getClass();    //获取对象的运行时类
2、
    //Returns a hash code value for the object. This method is supported for the benefit of hash tables such as those provided by java.util.HashMap.
    public native int hashCode();    //返回对象的hash值
3、 //Indicates whether some other object is "equal to" this one.
    public boolean equals(Object obj) {    //比较两个对象是否是同一个对象,==表示比较的是对象在系统hash table 的位置
        return (this == obj);
    }
4、 //Creates and returns a copy of this object.  The precise meaning of "copy" may depend on the class of the object. 
    protected native Object clone() throws CloneNotSupportedException;    //创建一个新的对象,该对象是对象的拷贝,即x.clone() != x 为true
5、 //Returns a string representation of the object. In general, the toString method returns a string that "textually represents" this object.
    public String toString() {        //返回类名+hashCode的十六进制
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }
6、 //Wakes up a single thread that is waiting on this object's monitor. If any threads are waiting on this object, one of them is chosen to be awakened.
    public final native void notify();    //唤醒在wait池中这个对象的监视器上等待的一个线程,进入就绪状态

    public final native void notifyAll();    //唤醒在wait池中这个对象的监视器上等待的全部线程,进入就绪状态
7、 //Causes the current thread to wait until another thread invokes the notify() method or the notifyAll() method for this object.In other words, this method behaves exactly as if it simply performs the call wait(0).
    public final void wait() throws InterruptedException {   //使正在运行状态的线程立即进入wait池,这种只能被notify和notifyAll唤醒
        wait(0);
    }
    //Causes the current thread to wait until either another thread invokes thenotify() method or the notifyAll() method for this object, or a specified amount of time has elapsed.
    public final native void wait(long timeout) throws InterruptedException;     //使正在运行状态的线程立即进入wait池,等待timeout毫秒后,自动进入就绪状态或者被notify或notifyAll进入就绪状态
8、 //Called by the garbage collector on an object when garbage collection determines that there are no more references to the object. A subclass overrides the finalize method to dispose of system resources or to perform other cleanup.
    protected void finalize() throws Throwable { }//当垃圾回收机制确定没有对该对象的引用时,垃圾回收器对该对象调用。

}

(1)getClass():

返回该对象的运行时类。返回的类对象是由表示类的静态同步方法锁定的对象。泛型不要管,他会在编译时进行类型的验证,在运行期间消除。

(2)hashCode():

返回对象的哈希代码值(这种可能是系统的hash table)。这种方法的好处是支持哈希表,如java.util.HashMap提供的表。

很多是时候hashCode都是被重写了,但是重写要遵循这个规则:

如果两个对象按照equals(object)方法相等,那么调用两个对象中的每一个对象的hashCode()方法必须产生相同的整数结果。这就需要重写的equals()和hashCode()要保持一致。比如:String的equals(object)方法重写成比较两个字符串是否一样。而hashCode生成的整数为s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1],同样两者的hashCode是一样的。

(3)equals(object):

比较两个对象是否是同一个对象,而equals的实质是==,而==表示比较的是对象在系统的hash table的hashCode是否一样。所有在没有重写equals时,调用的时候要当心。重写equals的规则跟hashCode一样。

(4)clone():

创建并返回此对象的副本(这个副本只是这个对象的属性,方法是没有的,其实你生成的所有对象只有属性,而对象调用的方法只是对属性赋值等)。“复制”的确切含义可能取决于对象的类别。一般的意图是,对于任何对象X,表达式:X.clone!=X是true,而另一个表达式X.clone().getClass() == X.getClass()为true。

重点:而且还要保证X.clone().equals(X)为true,按我们理解的肯定是false,因为equals默认条件是比较变量中引用地址的值,那么API上的意思是如果我们需要使用Clone()方法,那么一般情况下也应该再重写equals()方法。重写equals()方法的目的就是判断两个对象不需通过比较对象的地址,只要需要值一样就可以了(对象里面的属性)。

重写clone():返回的对象应该通过调用super.clone()获得。而super.clone()但是这只复制了表面的对象属性(又称为浅复制),而最重要是属性里面可能有其他对象的引用而浅复制只有表象,没有分配引用对象的空间,克隆对象的内部“深层结构”(可能引用类里面还有引用),并将这些对象的引用替换为对副本的引用(又称深复制)。而达到深复制只能重写clone(),或者用序列化(序列化技术可以序列化对象的引用,并通过反序列化生成对象的复制)

(5)toString():

返回对象的字符串表示形式。通常,ToString方法返回一个“文本表示”这个对象的字符串。建议所有子类重写此方法。Integer.toHexString(hashCode()这个是将hashCode整数值转化为十六进制,然后在变为字符串类型。

(6)notify():

唤醒在这个对象的监视器上等待的单个线程。如果任何线程在这个对象上等待,其中一个线程被选择唤醒。而唤醒后会进入锁池状态。在锁池中拿到对象的锁,才能进入就绪状态,等待CUP使用。

对于notifyAll()唤醒在这个对象的监视器上等待的所有线程,全部进入锁池状态。 

(7)wait():

使当前线程等待直到另一个线程调用该对象的notify()方法或notifyAll()方法,进入锁池状态,获得锁进而进入就绪状态。还有该方法的行为完全类似于它简单地执行调用wait(0)。调用wait()会使锁对象进入wait池,并释放到锁对象身上的锁。

而wait(int):使当前线程等待,直到另一个线程调用该对象的notify()方法或notifyAll()方法,或者指定的时间量已经过去,这样才能被唤醒进入锁池状态,获得锁后进而进入就绪状态。

(8)finalize():

当垃圾回收确定没有对对象的引用时,垃圾回收器对对象调用。子类重写finalize()方法来处理系统资源或执行其他清理。

补充知识:

在java中,每个对象都有两个池,锁(monitor)池和等待池

对象类Object

 wait() ,notifyAll(),notify() 三个方法都是Object类中的方法.

 锁池:假设线程A已经拥有了某个对象(注意:不是类)的锁,而其它的线程想要调用这个对象的某个synchronized方法(或者synchronized块),由于这些线程在进入对象的synchronized方法之前必须先获得该对象的锁的拥有权,但是该对象的锁目前正被线程A拥有,所以这些线程就进入了该对象的锁池中,当获得对象锁之后在进入就绪状态。

 等待池:假设一个线程A调用了某个对象的wait()方法,线程A就会释放该对象的锁(因为wait()方法必须出现在synchronized中,这样自然在执行wait()方法之前线程A就已经拥有了该对象的锁),同时线程A就进入到了该对象的等待池中。如果另外的一个线程调用了相同对象的notifyAll()方法,那么处于该对象的等待池中的线程就会全部进入该对象的锁池中,准备争夺锁的拥有权。如果另外的一个线程调用了相同对象的notify()方法,那么仅仅有一个处于该对象的等待池中的线程(随机)会进入该对象的锁池.

 下面通过一个例子来说明:

 要求写两个线程,一个线程将某个对象的某个成员变量的值加1,而另外一个线程将这个成员变量的值减1.使得该变量的值始终处于[0,2].初始值为0.

class Target
{
	private int count;
	
	public synchronized void increase()
	{
		if(count == 2)
		{
			try
			{
				wait();
			} 
			catch (InterruptedException e)
			{
				e.printStackTrace();
			}
		}
		count++;
		System.out.println(Thread.currentThread().getName() + ":" + count);
		notify();
	}
	
	public synchronized void decrease()
	{
		if(count == 0)
		{
			try
			{
				//等待,由于Decrease线程调用的该方法,
				//所以Decrease线程进入对象t(main函数中实例化的)的等待池,并且释放对象t的锁
				wait();//Object类的方法
			}
			catch (InterruptedException e)
			{
				e.printStackTrace();
			}
		}
		count--;
		System.out.println(Thread.currentThread().getName() + ":" + count);
		
		//唤醒线程Increase,Increase线程从等待池到锁池
		notify();
	}
}
class Increase extends Thread
{
	private Target t;
	
	public Increase(Target t)
	{
		this.t = t;
	}
	@Override
	public void run()
	{	
		for(int i = 0 ;i < 30; i++)
		{
			try
			{
				Thread.sleep((long)(Math.random()*500));
			}
			catch (InterruptedException e)
			{
				e.printStackTrace();
			}
			
			t.increase();
		}
		
	}
	
}
class Decrease extends Thread
{
	
	private Target t;
	public Decrease(Target t)
	{
		this.t = t;
	}
	
	@Override
	public void run()
	{
		for(int i = 0 ; i < 30 ; i++)
		{
			try
			{
				//随机睡眠0~500毫秒
				//sleep方法的调用,不会释放对象t的锁
				Thread.sleep((long)(Math.random()*500));
			}
			catch (InterruptedException e)
			{
				e.printStackTrace();
			}
			
			t.decrease();
			
		}
		
	}
	
}
 
public class Test
{
	public static void main(String[] args)
	{
		Target t = new Target();
		
		Thread t1 = new Increase(t);
		t1.setName("Increase");
		Thread t2 = new Decrease(t);
		t2.setName("Decrease");
		
		t1.start();
		t2.start();
	}
}

对上图的详解:

1)Thread t = new Thread(),初始化一个线程,实际上就是一个普通对象,此时他的状态为New

2)t.start(); 线程处于就绪状态(可运行状态),也就是随时等待着运行, 不要小看这个start,这个start决定了他是否是一个真正的线程实例,因为start为其准备了线程环境,你若只是普通调用run方法,那么这就是 一个普通的方法。处在这个时候的线程,都会去竞争CPU资源,所以谁被竞争到了CPU资源,也就是被调度Scheduler,那么他就可以从可运行状态到 真正运行状态。

3)当线程获取到了CPU资源时,线程就从可运行状态到真正运行状态,也就是Running,不用怀疑,他现在正在运行。

4)如果这个线程正在等待客户输入学习,也就是IO异常,等各种阻塞事件,也有可能是自己调用了sleep等阻塞事件,线程就会从运行状态转为阻塞状态,这个状态是不会发生任何事情的!

5)一旦阻塞事件被清除,比如用户已经输入完成,IO流已经关闭,sleep也已经超时等,线程从阻塞状态变为就绪状态,又一次回到了可运行状态,随时与别的线程竞争资源,等待运行!

6)处于运行状态的线程可能会在运行当中遇到了同步方法或同步块,也就是synchronized标记的方法或块,这个时候该线程获到了对象的锁, 其他线程就无法进入该同步方法,那么这些无法执行的线程怎么办呢?他们就都阻塞在这里,等待锁的释放,从新去竞争锁资源,因为只有拥有锁的线程才有资格继 续往下运行,那么这里这些线程就阻塞在锁池(Lock Pool)。

7)一旦被阻塞在锁池的线程竞争到了锁(之前的线程运行完了或之前的线程在内部跑出来异常,或者调用了wait等,都会释放线程的锁),那么这个线 程就会从阻塞状态转为就绪状态,不要以为这个线程会立刻执行,这是不可能的,你要想到线程执行都是要获取到CPU资源的,如果没有操作系统的调度,他们都 没有资格运行!

8)处于运行状态的线程可能会在运行当中进入了同步方法或同步块,这个时候他拥有了对象的锁,至高无上,可是由于当前环境可能导致他没必要继续执 行,所以他会自己让出锁资源让别的线程也有机会继续执行,所以这个线程可能在synchronized内部调用所对象的wait方法,一旦调用,当前线程 让出锁资源,同时自己进入等待池(wait pool)中,直到被别的线程唤醒!如果没有被唤醒就一直会处在等待池当中,受到线程的阻塞,所以这个时候他们一心想要的是被唤醒,因为只有唤醒才有可能 继续运行!

9)一旦被阻塞在等待池的线程被唤醒(可能是某个synchronized的线程调用了notify或notifyAll,也可能是外部调用 interrupt导致内部抛出异常,也会获取到锁),那么这个线程就会从等待池转为锁池当中,继续阻塞,所以不要以为线程被唤醒就会继续运行,这是不可 能的,他们同样需要竞争锁资源。

10)线程运行过程中抛出异常,或者线程实在运行完了,那么线程就结束了,也就是消亡期。运行完了是不可以继续start的,必须从新new 一个线程才能start。那么将是有一个生命周期。

相关标签: Object