溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

java中object方法的案例分析

發(fā)布時(shí)間:2020-10-29 09:52:46 來(lái)源:億速云 閱讀:161 作者:小新 欄目:編程語(yǔ)言

java中object方法的案例分析?這個(gè)問(wèn)題可能是我們?nèi)粘W(xué)習(xí)或工作經(jīng)常見(jiàn)到的。希望通過(guò)這個(gè)問(wèn)題能讓你收獲頗深。下面是小編給大家?guī)?lái)的參考內(nèi)容,讓我們一起來(lái)看看吧!

java中object方法有:1、【getClass()】是一個(gè)public的方法;2、【hashCode()】是一個(gè)public的方法,可以直接通過(guò)對(duì)象調(diào)用;3、【equals()】用于比較當(dāng)前對(duì)象與目標(biāo)對(duì)象是否相等。

java中object方法有:

一、引言

Object是java所有類的基類,是整個(gè)類繼承結(jié)構(gòu)的頂端,也是最抽象的一個(gè)類。大家天天都在使用toString()、equals()、hashCode()、waite()、notify()、getClass()等方法,或許都沒(méi)有意識(shí)到是Object的方法,也沒(méi)有去看Object還有哪些方法以及思考為什么這些方法要放到Object中。本篇就每個(gè)方法具體功能、重寫(xiě)規(guī)則以及自己的一些理解。

二、Object方法詳解

Object中含有:registerNatives()、getClass()、hashCode()、equals()、clone()、toString()、notify()、notifyAll()、wait(long)、wait(long,int)、wait()、finalize()共十二個(gè)方法。這個(gè)順序是按照Object類中定義方法的順序列舉的,下面我也會(huì)按照這個(gè)順序依次進(jìn)行講解。

1.1、registerNatives()

public class Object {
    private static native void registerNatives();
    static {
        registerNatives();
    }
}

什么鬼?哈哈哈,我剛看到這方法,一臉懵逼。從名字上理解,這個(gè)方法是注冊(cè)native方法(本地方法,由JVM實(shí)現(xiàn),底層是C/C++實(shí)現(xiàn)的)向誰(shuí)注冊(cè)呢?當(dāng)然是向JVM,當(dāng)有程序調(diào)用到native方法時(shí),JVM才好去找到這些底層的方法進(jìn)行調(diào)用。

Object中的native方法,并使用registerNatives()向JVM進(jìn)行注冊(cè)。(這屬于JNI的范疇,9龍暫不了解,有興趣的可自行查閱。)

static JNINativeMethod methods[] = {
    {"hashCode",    "()I",                    (void *)&JVM_IHashCode},
    {"wait",        "(J)V",                   (void *)&JVM_MonitorWait},
    {"notify",      "()V",                    (void *)&JVM_MonitorNotify},
    {"notifyAll",   "()V",                    (void *)&JVM_MonitorNotifyAll},
    {"clone",       "()Ljava/lang/Object;",   (void *)&JVM_Clone},
};

為什么要使用靜態(tài)方法,還要放到靜態(tài)塊中呢?

我們知道在類初始化的時(shí)候,會(huì)依次從父類到本類的類變量及類初始化塊中的類變量及方法按照定義順序放到< clinit>方法中,這樣可以保證父類的類變量及方法的初始化一定先于子類。所以當(dāng)子類調(diào)用相應(yīng)native方法,比如計(jì)算hashCode時(shí),一定可以保證能夠調(diào)用到JVM的native方法。

1.2、getClass()

public final native Class getClass():這是一個(gè)public的方法,我們可以直接通過(guò)對(duì)象調(diào)用。

類加載的第一階段類的加載就是將.class文件加載到內(nèi)存,并生成一個(gè)java.lang.Class對(duì)象的過(guò)程。getClass()方法就是獲取這個(gè)對(duì)象,這是當(dāng)前類的對(duì)象在運(yùn)行時(shí)類的所有信息的集合。這個(gè)方法是反射三種方式之一。

1.2.1、反射三種方式:

對(duì)象的getClass();

類名.class;

Class.forName();
class extends ObjectTest {
    private void privateTest(String str) {
        System.out.println(str);
    }
    public void say(String str) {
        System.out.println(str);
    }
}
public class ObjectTest {
    public static void main(String[] args) throws Exception {
        ObjectTest  = new ();
        //獲取對(duì)象運(yùn)行的Class對(duì)象
        Class<? extends ObjectTest> aClass = .getClass();
        System.out.println(aClass);
        //getDeclaredMethod這個(gè)方法可以獲取所有的方法,包括私有方法
        Method privateTest = aClass.getDeclaredMethod("privateTest", String.class);
        //取消java訪問(wèn)修飾符限制。
        privateTest.setAccessible(true);
        privateTest.invoke(aClass.newInstance(), "private method test");
        //getMethod只能獲取public方法
        Method say = aClass.getMethod("say", String.class);
        say.invoke(aClass.newInstance(), "Hello World");
    }
}
//輸出結(jié)果:
//class test.
//private method test
//Hello World

反射主要用來(lái)獲取運(yùn)行時(shí)的信息,可以將java這種靜態(tài)語(yǔ)言動(dòng)態(tài)化,可以在編寫(xiě)代碼時(shí)將一個(gè)子對(duì)象賦值給父類的一個(gè)引用,在運(yùn)行時(shí)通過(guò)反射可以或許運(yùn)行時(shí)對(duì)象的所有信息,即多態(tài)的體現(xiàn)。對(duì)于反射知識(shí)還是很多的,這里就不展開(kāi)講了。

1.3、hashCode()

public native int hashCode(); 這是一個(gè)public的方法,所以子類可以重寫(xiě)它。這個(gè)方法返回當(dāng)前對(duì)象的hashCode值,這個(gè)值是一個(gè)整數(shù)范圍內(nèi)的(-2^31 ~ 2^31 - 1)數(shù)字。

對(duì)于hashCode有以下幾點(diǎn)約束

在 Java 應(yīng)用程序執(zhí)行期間,在對(duì)同一對(duì)象多次調(diào)用 hashCode 方法時(shí),必須一致地返回相同的整數(shù),前提是將對(duì)象進(jìn)行 equals 比較時(shí)所用的信息沒(méi)有被修改;

如果兩個(gè)對(duì)象 x.equals(y) 方法返回true,則x、y這兩個(gè)對(duì)象的hashCode必須相等。

如果兩個(gè)對(duì)象x.equals(y) 方法返回false,則x、y這兩個(gè)對(duì)象的hashCode可以相等也可以不等。但是,為不相等的對(duì)象生成不同整數(shù)結(jié)果可以提高哈希表的性能。

默認(rèn)的hashCode是將內(nèi)存地址轉(zhuǎn)換為的hash值,重寫(xiě)過(guò)后就是自定義的計(jì)算方式;也可以通過(guò)System.identityHashCode(Object)來(lái)返回原本的hashCode。

public class HashCodeTest {
    private int age;
    private String name;
    @Override
    public int hashCode() {
        Object[] a = Stream.of(age, name).toArray();
        int result = 1;
        for (Object element : a) {
            result = 31 * result + (element == null ? 0 : element.hashCode());
        }
        return result;
    }
}

推薦使用Objects.hash(Object… values)方法。相信看源碼的時(shí)候,都看到計(jì)算hashCode都使用了31作為基礎(chǔ)乘數(shù),為什么使用31呢?我比較贊同與理解result * 31 = (result<<5) - result。JVM底層可以自動(dòng)做優(yōu)化為位運(yùn)算,效率很高;還有因?yàn)?1計(jì)算的hashCode沖突較少,利于hash桶位的分布。

1.4、equals()

public boolean equals(Object obj);用于比較當(dāng)前對(duì)象與目標(biāo)對(duì)象是否相等,默認(rèn)是比較引用是否指向同一對(duì)象。為public方法,子類可重寫(xiě)。

public class Object{
    public boolean equals(Object obj) {
        return (this == obj);
    }
}

為什么需要重寫(xiě)equals方法?

因?yàn)槿绻恢貙?xiě)equals方法,當(dāng)將自定義對(duì)象放到map或者set中時(shí);如果這時(shí)兩個(gè)對(duì)象的hashCode相同,就會(huì)調(diào)用equals方法進(jìn)行比較,這個(gè)時(shí)候會(huì)調(diào)用Object中默認(rèn)的equals方法,而默認(rèn)的equals方法只是比較了兩個(gè)對(duì)象的引用是否指向了同一個(gè)對(duì)象,顯然大多數(shù)時(shí)候都不會(huì)指向,這樣就會(huì)將重復(fù)對(duì)象存入map或者set中。這就破壞了map與set不能存儲(chǔ)重復(fù)對(duì)象的特性,會(huì)造成內(nèi)存溢出。

重寫(xiě)equals方法的幾條約定:

自反性:即x.equals(x)返回true,x不為null;

對(duì)稱性:即x.equals(y)與y.equals(x)的結(jié)果相同,x與y不為null;

傳遞性:即x.equals(y)結(jié)果為true, y.equals(z)結(jié)果為true,則x.equals(z)結(jié)果也必須為true;

一致性:即x.equals(y)返回true或false,在未更改equals方法使用的參數(shù)條件下,多次調(diào)用返回的結(jié)果也必須一致。x與y不為null。

如果x不為null, x.equals(null)返回false。

我們根據(jù)上述規(guī)則來(lái)重寫(xiě)equals方法。

public class EqualsTest{
    private int age;
    private String name;
    //省略get、set、構(gòu)造函數(shù)等
     @Override
    public boolean equals(Object o) {
        //先判斷是否為同一對(duì)象
        if (this == o) {
            return true;
        }
        //再判斷目標(biāo)對(duì)象是否是當(dāng)前類及子類的實(shí)例對(duì)象
        //注意:instanceof包括了判斷為null的情況,如果o為null,則返回false
        if (!(o instanceof )) {
            return false;
        }
         that = () o;
        return age == that.age &&
                Objects.equals(name, that.name);
    }
     public static void main(String[] args) throws Exception {
         EqualsTest1 equalsTest1 = new EqualsTest1(23, "9龍");
        EqualsTest1 equalsTest12 = new EqualsTest1(23, "9龍");
        EqualsTest1 equalsTest13 = new EqualsTest1(23, "9龍");
        System.out.println("-----------自反性----------");
        System.out.println(equalsTest1.equals(equalsTest1));
        System.out.println("-----------對(duì)稱性----------");
        System.out.println(equalsTest12.equals(equalsTest1));
        System.out.println(equalsTest1.equals(equalsTest12));
        System.out.println("-----------傳遞性----------");
        System.out.println(equalsTest1.equals(equalsTest12));
        System.out.println(equalsTest12.equals(equalsTest13));
        System.out.println(equalsTest1.equals(equalsTest13));
        System.out.println("-----------一致性----------");
        System.out.println(equalsTest1.equals(equalsTest12));
        System.out.println(equalsTest1.equals(equalsTest12));
        System.out.println("-----目標(biāo)對(duì)象為null情況----");
        System.out.println(equalsTest1.equals(null));
    }
}
//輸出結(jié)果
//-----------自反性----------
//true
//-----------對(duì)稱性----------
//true
//true
//-----------傳遞性----------
//true
//true
//true
//-----------一致性----------
//true
//true
//-----目標(biāo)對(duì)象為null情況----
//false

從以上輸出結(jié)果驗(yàn)證了我們的重寫(xiě)規(guī)定是正確的。

注意:instanceof 關(guān)鍵字已經(jīng)幫我們做了目標(biāo)對(duì)象為null返回false,我們就不用再去顯示判斷了。

建議equals及hashCode兩個(gè)方法,需要重寫(xiě)時(shí),兩個(gè)都要重寫(xiě),一般都是將自定義對(duì)象放至Set中,或者M(jìn)ap中的key時(shí),需要重寫(xiě)這兩個(gè)方法。

1.4、clone()

protected native Object clone() throws CloneNotSupportedException;

此方法返回當(dāng)前對(duì)象的一個(gè)副本。

這是一個(gè)protected方法,提供給子類重寫(xiě)。但需要實(shí)現(xiàn)Cloneable接口,這是一個(gè)標(biāo)記接口,如果沒(méi)有實(shí)現(xiàn),當(dāng)調(diào)用object.clone()方法,會(huì)拋出CloneNotSupportedException。

public class CloneTest implements Cloneable {
    private int age;
    private String name;
    //省略get、set、構(gòu)造函數(shù)等
    @Override
    protected CloneTest clone() throws CloneNotSupportedException {
        return (CloneTest) super.clone();
    }
    public static void main(String[] args) throws CloneNotSupportedException {
        CloneTest cloneTest = new CloneTest(23, "9龍");
        CloneTest clone = cloneTest.clone();
        System.out.println(clone == cloneTest);
        System.out.println(cloneTest.getAge()==clone.getAge());
        System.out.println(cloneTest.getName()==clone.getName());
    }
}
//輸出結(jié)果
//false
//true
//true

從輸出我們看見(jiàn),clone的對(duì)象是一個(gè)新的對(duì)象;但原對(duì)象與clone對(duì)象的String類型的name卻是同一個(gè)引用,這表明,super.clone方法對(duì)成員變量如果是引用類型,進(jìn)行是淺拷貝。

那什么是淺拷貝?對(duì)應(yīng)的深拷貝?

淺拷貝:拷貝的是引用。

深拷貝:新開(kāi)辟內(nèi)存空間,進(jìn)行值拷貝。

那如果我們要進(jìn)行深拷貝怎么辦呢?看下面的例子。

class Person implements Cloneable{
    private int age;
    private String name;
     //省略get、set、構(gòu)造函數(shù)等
     @Override
    protected Person clone() throws CloneNotSupportedException {
        Person person = (Person) super.clone();
        //name通過(guò)new開(kāi)辟內(nèi)存空間
        person.name = new String(name);
        return person;
   }
}
public class CloneTest implements Cloneable {
    private int age;
    private String name;
    //增加了person成員變量
    private Person person;
    //省略get、set、構(gòu)造函數(shù)等
    @Override
    protected CloneTest clone() throws CloneNotSupportedException {
        CloneTest clone = (CloneTest) super.clone();
        clone.person = person.clone();
        return clone;
    }
    public static void main(String[] args) throws CloneNotSupportedException {
       CloneTest cloneTest = new CloneTest(23, "9龍");
        Person person = new Person(22, "路飛");
        cloneTest.setPerson(person);
        CloneTest clone = cloneTest.clone();
        System.out.println(clone == cloneTest);
        System.out.println(cloneTest.getAge() == clone.getAge());
        System.out.println(cloneTest.getName() == clone.getName());
        Person clonePerson = clone.getPerson();
        System.out.println(person == clonePerson);
        System.out.println(person.getName() == clonePerson.getName());
    }
}
//輸出結(jié)果
//false
//true
//true
//false
//false

可以看到,即使成員變量是引用類型,我們也實(shí)現(xiàn)了深拷貝。如果成員變量是引用類型,想實(shí)現(xiàn)深拷貝,則成員變量也要實(shí)現(xiàn)Cloneable接口,重寫(xiě)clone方法。

1.5、toString()

public String toString();這是一個(gè)public方法,子類可重寫(xiě),建議所有子類都重寫(xiě)toString方法,默認(rèn)的toString方法,只是將當(dāng)前類的全限定性類名+@+十六進(jìn)制的hashCode值。

public class Object{
    public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }
}

我們思考一下為什么需要toString方法?

我這么理解的,返回當(dāng)前對(duì)象的字符串表示,可以將其打印方便查看對(duì)象的信息,方便記錄日志信息提供調(diào)試。

我們可以選擇需要表示的重要信息重寫(xiě)到toString方法中。為什么Object的toString方法只記錄類名跟內(nèi)存地址呢?因?yàn)镺bject沒(méi)有其他信息了,哈哈哈。

1.6、wait()/ wait(long)/ waite(long,int)

這三個(gè)方法是用來(lái)線程間通信用的,作用是阻塞當(dāng)前線程,等待其他線程調(diào)用notify()/notifyAll()方法將其喚醒。這些方法都是public final的,不可被重寫(xiě)。

注意:

此方法只能在當(dāng)前線程獲取到對(duì)象的鎖監(jiān)視器之后才能調(diào)用,否則會(huì)拋出IllegalMonitorStateException異常。

調(diào)用wait方法,線程會(huì)將鎖監(jiān)視器進(jìn)行釋放;而Thread.sleep,Thread.yield()并不會(huì)釋放鎖。

wait方法會(huì)一直阻塞,直到其他線程調(diào)用當(dāng)前對(duì)象的notify()/notifyAll()方法將其喚醒;而wait(long)是等待給定超時(shí)時(shí)間內(nèi)(單位毫秒),如果還沒(méi)有調(diào)用notify()/nofiyAll()會(huì)自動(dòng)喚醒;waite(long,int)如果第二個(gè)參數(shù)大于0并且小于999999,則第一個(gè)參數(shù)+1作為超時(shí)時(shí)間;

 public final void wait() throws InterruptedException {
        wait(0);
    } 
public final native void wait(long timeout) throws InterruptedException;
public final void wait(long timeout, int nanos) throws InterruptedException {
        if (timeout < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }
        if (nanos < 0 || nanos > 999999) {
            throw new IllegalArgumentException(
                                "nanosecond timeout value out of range");
        }
        if (nanos > 0) {
            timeout++;
        }
        wait(timeout);
    }

1.7、notify()/notifyAll()

前面說(shuō)了,如果當(dāng)前線程獲得了當(dāng)前對(duì)象鎖,調(diào)用wait方法,將鎖釋放并阻塞;這時(shí)另一個(gè)線程獲取到了此對(duì)象鎖,并調(diào)用此對(duì)象的notify()/notifyAll()方法將之前的線程喚醒。這些方法都是public final的,不可被重寫(xiě)。

public final native void notify(); 隨機(jī)喚醒之前在當(dāng)前對(duì)象上調(diào)用wait方法的一個(gè)線程

public final native void notifyAll(); 喚醒所有之前在當(dāng)前對(duì)象上調(diào)用wait方法的線程

下面我們使用wait()、notify()展示線程間通信。假設(shè)9龍有一個(gè)賬戶,只要9龍一發(fā)工資,就被女朋友給取走了。

//賬戶
public class Account {
    private String accountNo;
    private double balance;
    private boolean flag = false;
    public Account() {
    }
    public Account(String accountNo, double balance) {
        this.accountNo = accountNo;
        this.balance = balance;
    }
    /**
     * 取錢方法
     *
     * @param drawAmount 取款金額
     */
    public synchronized void draw(double drawAmount) {
        try {
            if (!flag) {
                //如果flag為false,表明賬戶還沒(méi)有存入錢,取錢方法阻塞
                wait();
            } else {
                //執(zhí)行取錢操作
                System.out.println(Thread.currentThread().getName() + " 取錢" + drawAmount);
                balance -= drawAmount;
                //標(biāo)識(shí)賬戶已沒(méi)錢
                flag = false;
                //喚醒其他線程
                notify();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    public synchronized void deposit(double depositAmount) {
        try {
            if (flag) {
                //如果flag為true,表明賬戶已經(jīng)存入錢,取錢方法阻塞
                wait();
            } else {
                //存錢操作
                System.out.println(Thread.currentThread().getName() + " 存錢" + depositAmount);
                balance += depositAmount;
                //標(biāo)識(shí)賬戶已存入錢
                flag = true;
                //喚醒其他線程
                notify();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
//取錢者
public class DrawThread extends Thread {
    private Account account;
    private double drawAmount;
    public DrawThread(String name, Account account, double drawAmount) {
        super(name);
        this.account = account;
        this.drawAmount = drawAmount;
    }
    @Override
    public void run() {
        //循環(huán)6次取錢
        for (int i = 0; i < 6; i++) {
            account.draw(drawAmount);
        }
    }
}
//存錢者
public class DepositThread extends Thread {
    private Account account;
    private double depositAmount;
    public DepositThread(String name, Account account, double depositAmount) {
        super(name);
        this.account = account;
        this.depositAmount = depositAmount;
    }
    @Override
    public void run() {
        //循環(huán)6次存錢操作
        for (int i = 0; i < 6; i++) {
            account.deposit(depositAmount);
        }
    }
}
//測(cè)試
public class DrawTest {
    public static void main(String[] args) {
        Account brady = new Account("9龍", 0);
        new DrawThread("女票", brady, 10).start();
        new DepositThread("公司", brady, 10).start();
    }
}
//輸出結(jié)果
//公司 存錢10.0
//女票 取錢10.0
//公司 存錢10.0
//女票 取錢10.0
//公司 存錢10.0
//女票 取錢10.0

例子中我們通過(guò)一個(gè)boolean變量來(lái)判斷賬戶是否有錢,當(dāng)取錢線程來(lái)判斷如果賬戶沒(méi)錢,就會(huì)調(diào)用wait方法將此線程進(jìn)行阻塞;這時(shí)候存錢線程判斷到賬戶沒(méi)錢, 就會(huì)將錢存入賬戶,并且調(diào)用notify()方法通知被阻塞的線程,并更改標(biāo)志;取錢線程收到通知后,再次獲取到cpu的調(diào)度就可以進(jìn)行取錢。反復(fù)更改標(biāo)志,通過(guò)調(diào)用wait與notify()進(jìn)行線程間通信。實(shí)際中我們會(huì)時(shí)候生產(chǎn)者消費(fèi)者隊(duì)列會(huì)更簡(jiǎn)單。

注意:調(diào)用notify()后,阻塞線程被喚醒,可以參與鎖的競(jìng)爭(zhēng),但可能調(diào)用notify()方法的線程還要繼續(xù)做其他事,鎖并未釋放,所以我們看到的結(jié)果是,無(wú)論notify()是在方法一開(kāi)始調(diào)用,還是最后調(diào)用,阻塞線程都要等待當(dāng)前線程結(jié)束才能開(kāi)始。

為什么wait()/notify()方法要放到Object中呢?

因?yàn)槊總€(gè)對(duì)象都可以成為鎖監(jiān)視器對(duì)象,所以放到Object中,可以直接使用。

1.8、finalize()

protected void finalize() throws Throwable ;

此方法是在垃圾回收之前,JVM會(huì)調(diào)用此方法來(lái)清理資源。此方法可能會(huì)將對(duì)象重新置為可達(dá)狀態(tài),導(dǎo)致JVM無(wú)法進(jìn)行垃圾回收。

我們知道java相對(duì)于C++很大的優(yōu)勢(shì)是程序員不用手動(dòng)管理內(nèi)存,內(nèi)存由jvm管理;如果我們的引用對(duì)象在堆中沒(méi)有引用指向他們時(shí),當(dāng)內(nèi)存不足時(shí),JVM會(huì)自動(dòng)將這些對(duì)象進(jìn)行回收釋放內(nèi)存,這就是我們常說(shuō)的垃圾回收。但垃圾回收沒(méi)有講述的這么簡(jiǎn)單。

finalize()方法具有如下4個(gè)特點(diǎn):

永遠(yuǎn)不要主動(dòng)調(diào)用某個(gè)對(duì)象的finalize()方法,該方法由垃圾回收機(jī)制自己調(diào)用;

finalize()何時(shí)被調(diào)用,是否被調(diào)用具有不確定性;

當(dāng)JVM執(zhí)行可恢復(fù)對(duì)象的finalize()可能會(huì)將此對(duì)象重新變?yōu)榭蛇_(dá)狀態(tài);

當(dāng)JVM執(zhí)行finalize()方法時(shí)出現(xiàn)異常,垃圾回收機(jī)制不會(huì)報(bào)告異常,程序繼續(xù)執(zhí)行。

public class FinalizeTest {
    private static FinalizeTest ft = null;
    public void info(){
        System.out.println("測(cè)試資源清理得finalize方法");
    }
    public static void main(String[] args) {
        //創(chuàng)建FinalizeTest對(duì)象立即進(jìn)入可恢復(fù)狀態(tài)
        new FinalizeTest();
        //通知系統(tǒng)進(jìn)行垃圾回收
        System.gc();
        //強(qiáng)制回收機(jī)制調(diào)用可恢復(fù)對(duì)象的finalize()方法
//        Runtime.getRuntime().runFinalization();
        System.runFinalization();
        ft.info();
    }
    @Override
    public void finalize(){
        //讓ft引用到試圖回收的可恢復(fù)對(duì)象,即可恢復(fù)對(duì)象重新變成可達(dá)
        ft = this;
        throw new RuntimeException("出異常了,你管不管啊");
    }
}
//輸出結(jié)果
//測(cè)試資源清理得finalize方法

我們看到,finalize()方法將可恢復(fù)對(duì)象置為了可達(dá)對(duì)象,并且在finalize中拋出異常,都沒(méi)有任何信息,被忽略了。

1.8.1、對(duì)象在內(nèi)存中的狀態(tài)

對(duì)象在內(nèi)存中存在三種狀態(tài):

可達(dá)狀態(tài):有引用指向,這種對(duì)象為可達(dá)狀態(tài);

可恢復(fù)狀態(tài):失去引用,這種對(duì)象稱為可恢復(fù)狀態(tài);垃圾回收機(jī)制開(kāi)始回收時(shí),回調(diào)用可恢復(fù)狀態(tài)對(duì)象的finalize()方法(如果此方法讓此對(duì)象重新獲得引用,就會(huì)變?yōu)榭蛇_(dá)狀態(tài),否則,會(huì)變?yōu)椴豢纱鬆顟B(tài))。

不可達(dá)狀態(tài):徹底失去引用,這種狀態(tài)稱為不可達(dá)狀態(tài),如果垃圾回收機(jī)制這時(shí)開(kāi)始回收,就會(huì)將這種狀態(tài)的對(duì)象回收掉。

1.8.2、垃圾回收機(jī)制

垃圾回收機(jī)制只負(fù)責(zé)回收堆內(nèi)存種的對(duì)象,不會(huì)回收任何物理資源(例如數(shù)據(jù)庫(kù)連接、網(wǎng)絡(luò)IO等資源);

程序無(wú)法精確控制垃圾回收的運(yùn)行,垃圾回收只會(huì)在合適的時(shí)候進(jìn)行。當(dāng)對(duì)象為不可達(dá)狀態(tài)時(shí),系統(tǒng)會(huì)在合適的時(shí)候回收它的內(nèi)存。

在垃圾回收機(jī)制回收任何對(duì)象之前,總會(huì)先調(diào)用它的finalize()方法,該方法可能會(huì)將對(duì)象置為可達(dá)狀態(tài),導(dǎo)致垃圾回收機(jī)制取消回收。

1.8.3、強(qiáng)制垃圾回收

上面我們已經(jīng)說(shuō)了,當(dāng)對(duì)象失去引用時(shí),會(huì)變?yōu)榭苫謴?fù)狀態(tài),但垃圾回收機(jī)制什么時(shí)候運(yùn)行,什么時(shí)候調(diào)用finalize方法無(wú)法知道。雖然垃圾回收機(jī)制無(wú)法精準(zhǔn)控制,但java還是提供了方法可以建議JVM進(jìn)行垃圾回收,至于是否回收,這取決于虛擬機(jī)。但似乎可以看到一些效果。

public class GcTest {
    public static void main(String[] args){
        for(int i=0;i<4;i++){
            //沒(méi)有引用指向這些對(duì)象,所以為可恢復(fù)狀態(tài)
            new GcTest();
            //強(qiáng)制JVM進(jìn)行垃圾回收(這只是建議JVM)
            System.gc();
            //Runtime.getRuntime().gc();
        }
    }
    @Override
    public void finalize(){
        System.out.println("系統(tǒng)正在清理GcTest資源。。。。");
    }
}
//輸出結(jié)果
//系統(tǒng)正在清理GcTest資源。。。。
//系統(tǒng)正在清理GcTest資源。。。。

System.gc(),Runtime.getRuntime().gc()兩個(gè)方法作用一樣的,都是建議JVM垃圾回收,但不一定回收,多運(yùn)行幾次,結(jié)果可能都不一致。

感謝各位的閱讀!看完上述內(nèi)容,你們對(duì)java中object方法的案例分析大概了解了嗎?希望文章內(nèi)容對(duì)大家有所幫助。如果想了解更多相關(guān)文章內(nèi)容,歡迎關(guān)注億速云行業(yè)資訊頻道。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI