1. 程式人生 > >Java基礎12——深入理解Class類和Object類

Java基礎12——深入理解Class類和Object類

深入理解Class類和Object類

一、所有類的型別資訊的記錄員——Class類

先來簡單瞭解一下Java虛擬機器中類的載入過程:
「載入」階段是「類載入」過程的第一個階段,虛擬機器需要完成以下三件事情:
1. 通過一個類的全限定名來獲取定義此類的二進位制位元組流(.class檔案即儲存 著類的二進位制資料)。
2. 將該位元組流所代表的靜態儲存結構轉化為方法區的執行時資料結構。
3. 在Java堆中生成一個代表該類的java.lang.Class物件,作為方法區中該資料結構的訪問入口。

也就是說,Class是一個儲存著執行時類所有資訊的類,即在程式執行時跟蹤類且掌握著類的全部資訊,故其也被稱為反射的源頭(有點兒小抽象)。

  Java程式在執行時,Java執行時系統一直對所有的物件進行所謂的執行時型別標識,即所謂的RTTI。

這項資訊紀錄了每個物件所屬的類。虛擬機器通常使用執行時型別資訊選準正確方法去執行,用來儲存這些型別資訊的類是Class類。Class類封裝一個物件和介面執行時的狀態,當裝載類時,Class型別的物件自動建立。

簡單點說就是:

  1. Class類也是類的一種,只是名字和class關鍵字高度相似。Java是大小寫敏感的語言。
  2. Class類的物件內容是你建立的類的型別資訊,比如你建立一個shapes類,那麼,Java會生成一個內容是shapes的Class類的物件。
  3. Class類的物件不能像普通類一樣,以 new shapes() 的方式建立,它的物件只能由JVM建立,因為這個類沒有public建構函式。
   /*
     * Private constructor. Only the Java Virtual Machine creates Class objects.
     * This constructor is not used and prevents the default constructor being
     * generated.
     */
     //私有構造方法,只能由jvm進行例項化
    private
Class(ClassLoader loader) { // Initialize final field for classLoader. The initialization value of non-null // prevents future JIT optimizations from assuming this final field is null. classLoader = loader; }

Class類的作用是執行時提供或獲得某個物件的型別資訊,和C++中的typeid()函式類似。這些資訊也可用於反射。

原始碼:

//Class類中封裝了型別的各種資訊。在jvm中就是通過Class類的例項來獲取每個Java類的所有資訊的。

public class Class{
    Class aClass = null;

//    private EnclosingMethodInfo getEnclosingMethodInfo() {
//        Object[] enclosingInfo = getEnclosingMethod0();
//        if (enclosingInfo == null)
//            return null;
//        else {
//            return new EnclosingMethodInfo(enclosingInfo);
//        }
//    }

    /**提供原子類操作
     * Atomic operations support.
     */
//    private static class Atomic {
//        // initialize Unsafe machinery here, since we need to call Class.class instance method
//        // and have to avoid calling it in the static initializer of the Class class...
//        private static final Unsafe unsafe = Unsafe.getUnsafe();
//        // offset of Class.reflectionData instance field
//        private static final long reflectionDataOffset;
//        // offset of Class.annotationType instance field
//        private static final long annotationTypeOffset;
//        // offset of Class.annotationData instance field
//        private static final long annotationDataOffset;
//
//        static {
//            Field[] fields = Class.class.getDeclaredFields0(false); // bypass caches
//            reflectionDataOffset = objectFieldOffset(fields, "reflectionData");
//            annotationTypeOffset = objectFieldOffset(fields, "annotationType");
//            annotationDataOffset = objectFieldOffset(fields, "annotationData");
//        }

        //提供反射資訊
    // reflection data that might get invalidated when JVM TI RedefineClasses() is called
//    private static class ReflectionData<T> {
//        volatile Field[] declaredFields;
//        volatile Field[] publicFields;
//        volatile Method[] declaredMethods;
//        volatile Method[] publicMethods;
//        volatile Constructor<T>[] declaredConstructors;
//        volatile Constructor<T>[] publicConstructors;
//        // Intermediate results for getFields and getMethods
//        volatile Field[] declaredPublicFields;
//        volatile Method[] declaredPublicMethods;
//        volatile Class<?>[] interfaces;
//
//        // Value of classRedefinedCount when we created this ReflectionData instance
//        final int redefinedCount;
//
//        ReflectionData(int redefinedCount) {
//            this.redefinedCount = redefinedCount;
//        }
//    }
        //方法陣列
//    static class MethodArray {
//        // Don't add or remove methods except by add() or remove() calls.
//        private Method[] methods;
//        private int length;
//        private int defaults;
//
//        MethodArray() {
//            this(20);
//        }
//
//        MethodArray(int initialSize) {
//            if (initialSize < 2)
//                throw new IllegalArgumentException("Size should be 2 or more");
//
//            methods = new Method[initialSize];
//            length = 0;
//            defaults = 0;
//        }

    //註解資訊
    // annotation data that might get invalidated when JVM TI RedefineClasses() is called
//    private static class AnnotationData {
//        final Map<Class<? extends Annotation>, Annotation> annotations;
//        final Map<Class<? extends Annotation>, Annotation> declaredAnnotations;
//
//        // Value of classRedefinedCount when we created this AnnotationData instance
//        final int redefinedCount;
//
//        AnnotationData(Map<Class<? extends Annotation>, Annotation> annotations,
//                       Map<Class<? extends Annotation>, Annotation> declaredAnnotations,
//                       int redefinedCount) {
//            this.annotations = annotations;
//            this.declaredAnnotations = declaredAnnotations;
//            this.redefinedCount = redefinedCount;
//        }
//    }
}

我們都知道所有的java類都是繼承了object這個類,在object這個類中有一個方法:getclass().這個方法是用來取得該類已經被例項化了的物件的該類的引用,這個引用指向的是Class類的物件。

我們自己無法生成一個Class物件(建構函式為private),而 這個Class類的物件是在當各類被調入時,由 Java 虛擬機器自動建立 Class 物件,或通過類裝載器中的 defineClass 方法生成。

//通過該方法可以動態地將位元組碼轉為一個Class類物件
protected final Class<?> defineClass(String name, byte[] b, int off, int len)
    throws ClassFormatError
{
    return defineClass(name, b, off, len, null);
}

  我們生成的物件都會有個欄位記錄該物件所屬類在CLass類的物件的所在位置。如下圖所示:
在這裡插入圖片描述

如何獲得一個Class類物件(How?)

  請注意,以下這些方法都是指某個類對應的Class物件已經在堆中生成以後,我們通過不同方式獲取對這個Class物件的引用。而上面說的DefineClass才是真正將位元組碼載入到虛擬機器的方法,會在堆中生成新的一個Class物件。

第一種辦法,Class類的forName函式
public class shapes{}
Class obj= Class.forName(“shapes”); // 只要通過給定的類的名稱就可以獲取這個類,更為擴充套件

第二種辦法,使用物件的getClass()函式
public class shapes{}
shapes s1=newshapes(); //需要明確具體的類,並建立物件
Class obj=s1.getClass();
Class obj1=s1.getSuperclass();//這個函式作用是獲取shapes類的父類的型別

第三種辦法,使用類字面常量
Class obj=String.class;// 相對簡單,但是還是要明確用到類中的靜態成員
Class obj1=int.class;
注意,使用這種辦法生成Class類物件時,不會使JVM自動載入該類(如String類)。而其他辦法會使得JVM初始化該類。

第四種方法,通過類的載入器獲取
ClassLoader cl = this.getClass().getClassLoader();
Class clazz4 = cl.loadClass(className);
System.out.println(clazz4.getName());

Class類的例項可以做什麼?

獲取到執行時類的Class例項後,通過Class類的例項可以:

  • 通過newInstance()方法建立對應執行類的物件。
  • 獲取其對應類的完整結構,如構造器、屬性、方法、內部類、父類、所在的 包、異常和註解等。
  • 呼叫對應的執行時類的指定結構,如屬性、方法和構造器。
  • 反射的應用,即動態代理(又挖一坑,下一章重點說明)。
    注意事項:
      請注意,一個Class物件實際上表示的是一個型別,而這個型別未必一定是一種類。例如,int不是類,但int.class是一個Class型別的物件。
      另外,呼叫Class類例項的 newInstance()方法動態建立類物件時,需要對應的 執行時類中有空參的構造器。通過Class類的例項獲取執行時類的所有描述資訊的程式碼較長,在此僅給出對應的方法描述,如下:
    在這裡插入圖片描述

二、所有類的祖宗——Object類

  Object類是Java中其他所有類的祖先,沒有Object類Java面向物件無從談起。作為其他所有類的基類,Object具有哪些屬性和行為,是Java語言設計背後的思維體現。
  Object類位於java.lang包中,java.lang包包含著Java最基礎和核心的類,在編譯時會自動匯入。Object類沒有定義屬性,一共有13個方法,13個方法之中並不是所有方法都是子類可訪問的,一共有9個方法是所有子類都繼承了的。
在這裡插入圖片描述

1.clone方法
保護方法,實現物件的淺複製,只有實現了Cloneable接口才可以呼叫該方法,否則丟擲CloneNotSupportedException異常。
2.getClass方法
final方法,獲得執行時型別。
3.toString方法
該方法用得比較多,一般子類都有覆蓋。
4.finalize方法
該方法用於釋放資源。因為無法確定該方法什麼時候被呼叫,很少使用。
5.equals方法
該方法是非常重要的一個方法。一般equals和==是不一樣的,但是在Object中兩者是一樣的。子類一般都要重寫這個方法。
6.hashCode方法
該方法用於雜湊查詢,重寫了equals方法一般都要重寫hashCode方法。這個方法在一些具有雜湊功能的Collection中用到。
一般必須滿足obj1.equals(obj2)==true。可以推出obj1.hash- Code()==obj2.hashCode(),但是hashCode相等不一定就滿足equals。不過為了提高效率,應該儘量使上面兩個條件接近等價。
7.wait方法
wait方法就是使當前執行緒等待該物件的鎖,當前執行緒必須是該物件的擁有者,也就是具有該物件的鎖。wait()方法一直等待,直到獲得鎖或者被中斷。wait(long timeout)設定一個超時間隔,如果在規定時間內沒有獲得鎖就返回。
呼叫該方法後當前執行緒進入睡眠狀態,直到以下事件發生。
(1)其他執行緒呼叫了該物件的notify方法。
(2)其他執行緒呼叫了該物件的notifyAll方法。
(3)其他執行緒呼叫了interrupt中斷該執行緒。
(4)時間間隔到了。
此時該執行緒就可以被排程了,如果是被中斷的話就丟擲一個InterruptedException異常。
8.notify方法
該方法喚醒在該物件上等待的某個執行緒。
9.notifyAll方法
該方法喚醒在該物件上等待的所有執行緒。

原始碼閱讀:

package java.lang;
public class Object {
/** 
* 私有的,在靜態程式碼塊中執行,且僅在Object類首次被載入時執行一
* 次。 
*/ 
private static native void registerNatives(); 
static { 
	registerNatives(); 
}
/** 
* final方法:不能被子類所重寫 
*/ 
public final native Class<?> getClass();
/** 
* 在子類中重寫該方法時,可直接呼叫Objects.hash(Object...)來獲取
* 物件的雜湊值 
*/ 
public native int hashCode();
/** 
* 預設實現中,僅比較兩個物件是否引用的同一個物件 
* 實際開發中,需要重寫該方法來比較兩個物件的具體內容是否相等 
*/ 
public boolean equals(Object obj) { 
	return (this == obj); 
}
/** 
* 僅對本包下的所有類和當前類的子類可見。 
* 只有實現Cloneable介面的類的物件才能呼叫該方法,否則會丟擲異常 */ 
protected native Object clone() throws CloneNotSupportedException;
/** 
* 返回:執行時類名@十六進位制雜湊值字串 
*/ 
public String toString() { return getClass().getName() + "@" + Integer.toHexString(hashCode()); }
/** 
* 喚醒單個執行緒,但不確定具體喚醒的是哪個執行緒 
*/ 
public final native void notify();
/** 
* 喚醒所有執行緒,但並不是所有執行緒都可以拿到物件鎖而進入就緒狀態 
*/ 
public final native void notifyAll();
/** 
* 使當前執行緒釋放物件鎖,即執行緒暫停執行,直到其他執行緒呼叫 notify()/notifyAll() 
*/ 
public final native void wait(long timeout) throws InterruptedException
/** 
* 大等待時間:1000000*timeout+nanos 
*/ 
public final void wait(long timeout, int nanos) throws InterruptedExcept 
	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);
}
/** 
* 未設定大等待時間,即只能被notify()/notifyAll()喚醒 
*/ public final void wait() throws InterruptedException {
	 wait(0); 
	}
/** 
* 子類可通過重寫該方法,以在垃圾回收前整理系統資源或執行其他清理操作 
* 在該方法中,若將該物件重新與引用鏈建立關聯關係,則會逃離本次垃圾回收 
*/ 
protected void finalize() throws Throwable { }
}

1、registerNatives()方法

  registerNatives函式前面有native關鍵字修飾,Java中,用native關鍵字修飾的函式表明該方法的實現並不是在Java中去完成,而是由C/C++去完成,並被編譯成了.dll,由Java去呼叫。
  方法的具體實現體在dll檔案中,對於不同平臺,其具體實現應該有所不同。用native修飾,即表示作業系統,需要提供此方法,Java本身需要使用。
  具體到registerNatives()方法本身,其主要作用是將C/C++中的方法對映到Java中的native方法,實現方法命名的解耦。
  既然如此,可能有人會問,registerNatives()修飾符為private,且並沒有執行,作用何以達到?其實,在Java原始碼中,此方法的聲明後有緊接著一段靜態程式碼塊。

2、深拷貝和淺拷貝的區別和具體實現

淺拷貝和深拷貝有什麼區別?
先來了解一下兩個概念:「引用拷貝」「物件拷貝」
「引用拷貝」是指建立一個指向物件的引用變數的拷貝,例如:

Employee emp1 = new Employee("Taylor", 26); 
Employee emp2 = emp1; 
System.out.println(emp1); // [email protected] 
System.out.println(emp2); // [email protected]

而「物件拷貝」是指建立物件本身的一個副本,例如:

Employee emp1 = new Employee("Swift", 26); 
Employee emp2 = (Employee) emp1.clone(); 
System.out.println(emp1); // [email protected] 
System.out.println(emp2); // [email protected]

  即emp1和emp2分別指向堆空間中的不同物件,這就叫「物件拷貝」,但需要注意的是,使用clone()方法進行物件拷貝時,必須要求Employee類實現 Cloneable介面並重寫clone()方法,且上述程式碼段所在的方法還需要處理 CloneNotSupportedException異常。

其中,「淺拷貝」和「深拷貝」都屬於「物件拷貝」。
  對於基本資料型別的成員變數,無論「淺拷貝」還是「深拷貝」都會直接進行值傳遞,原物件和新物件的該屬性值為兩份資料,相互獨立,且互不影響。
  而對於引用型別的成員變數,「淺拷貝」僅複製該屬性的引用而不復制引用所指向的物件,即原物件和新物件的該屬性指向的是同一個物件;「深拷貝」則會直接複製該屬性所指向的物件,即原物件和新物件的該屬性指向的是堆空間中記憶體地址不同的物件。

如何實現「淺拷貝」?

要求待拷貝物件所屬類:

  • 實現Cloneable介面;
  • 重寫clone()方法,並指定public訪問修飾符。

要求在呼叫待拷貝物件的clone()方法時:

  • 處理編譯時異常:CloneNotSupportedException。

另外,也可以手動採用賦值的方式將原物件的各個屬性值拷貝到新的物件。

程式碼報錯:

package com.corn.objectsummary;  
  
import com.corn.Person;  
  
public class ObjectTest {  
  
    public static void main(String[] args) {  
  
        Object o1 = new Object();  
        Object clone = o1.clone(); // The method clone() from the type Object is not visible  
    }  
  
}  

原因:
  根據提示,第一反應是ObjectTest類中定義的Oject物件無法訪問其clone()方法。回到Object類中clone()方法的定義,可以看到其被宣告為protected,估計問題就在這上面了, protected修飾的屬性或方法表示:在同一個包內或者不同包的子類可以訪問。顯然,Object類與ObjectTest類在不同的包中,但是ObjectTest繼承自Object,是Object類的子類,於是,現在卻出現子類中通過Object引用不能訪問protected方法,原因在於對"不同包中的子類可以訪問"沒有正確理解。
  "不同包中的子類可以訪問",是指當兩個類不在同一個包中的時候,繼承自父類的子類內部且主調(呼叫者)為子類的引用時才能訪問父類用protected修飾的成員(屬性/方法)。 在子類內部,主調為父類的引用時並不能訪問此protected修飾的成員。!(super關鍵字除外)

程式碼報錯:

package com.corn.objectsummary;  
  	public class ObjectTest {  
  
    public static void main(String[] args) {  
        ObjectTest ot1 = new ObjectTest();  
  
        try {  
            ObjectTest ot2 = (ObjectTest) ot1.clone();  
        } catch (CloneNotSupportedException e) {  
            // TODO Auto-generated catch block  
            e.printStackTrace();  
        }  
    }  
  
}  

正常執行:

//clone()的正確呼叫是需要實現Cloneable介面,如果沒有實現Cloneable介面,並且子類直接呼叫Object類的clone()方法,則會丟擲CloneNotSupportedException異常。
package com.corn.objectsummary;  
  
public class ObjectTest implements Cloneable {  
  
    public static void main(String[] args) {  
  
        ObjectTest ot1 = new ObjectTest();  
  
        try {  
            ObjectTest ot2 = (ObjectTest) ot1.clone();  
            System.out.println("ot2:" + ot2);  
        } catch (CloneNotSupportedException e) {  
            // TODO Auto-generated catch block  
            e.printStackTrace();  
        }  
    }  
  
}  

如何實現「深拷貝」?

方式1: 通過實現Cloneable介面並重寫clone()方法來實現,即將原物件及其所引用的所有物件所屬的類均實現Cloneable介面並重寫clone()方法。
方式2: 通過序列化方式來實現。

3、完整的栗子說明深拷貝和淺拷貝

淺拷貝:

package csdn.cn.copytest;

public class ShallowCopy {

	public static void main(String[] args) throws CloneNotSupportedException {
		Teacher teacher = new Teacher();
        teacher.setName("Delacey");
        teacher.setAge(29);

        Student student1 = new Student();
        student1.setName("Dream");
        student1.setAge(18);
        student1.setTeacher(teacher);

        Student student2 = (Student) student1.clone();
        System.out.println("拷貝後");
        System.out.println(student2.getName());
        System.out.println(student2.getAge());
        System.out.println(student2.getTeacher().getName());
        System.out.println(student2.getTeacher().getAge());
        System.out.println("修改老師的資訊後-------------");
        // 修改老師的資訊
        teacher.setName("Jam");
        System.out.println(student1.getTeacher().getName());
        System.out.println(student2.getTeacher().getName());
			
	}
}
// Teacher類	
package csdn.cn.copytest;

public class Teacher implements Cloneable {
	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;
	}
	@Override
	public String toString() {
		return "Teacher [name=" + name + ", age=" + age + "]";
	}
	
}
//Student類
package csdn.cn.copytest;

public class Student implements Cloneable {
	private String name;
	private int age;
	private Teacher teacher;
	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 Teacher getTeacher() {
		return teacher;
	}
	public void setTeacher(Teacher teacher) {
		this.teacher = teacher;
	}

	@Override
	public String toString() {
		return "Student [name=" + name + ", age=" + age + ", teacher_name=" + teacher.getName() +", teacher_age=" + teacher.getAge()+"]";
	}
	
	@Override
	protected Object clone() throws CloneNotSupportedException {
		
		return super.clone();
	}
	
}
//執行結果:
拷貝後
Dream
18
Delacey
29
修改老師的資訊後-------------
Jam
Jam

結果分析: 兩個引用student1和student2指向不同的兩個物件,但是兩個引用student1和student2中的兩個teacher引用指向的是同一個物件,所以說明是淺拷貝。
在這裡插入圖片描述
淺拷貝的特點:
  被複制物件的所有變數都含有與原來的物件相同的值,而所有的對其他物件的引用仍然指向原來的物件。即物件的淺拷貝會對“主”物件進行拷貝,但不會複製主物件裡面的物件。”裡面的物件“會在原來的物件和它的副本之間共享。

深拷貝:

package csdn.cn.copytest;

public class DeepCopy {
    public static void main(String[] args) throws Exception
    {
        Teacher2 teacher = new Teacher2();
        teacher.setName("Delacey");
        teacher.setAge(29);

        Student3 student1 = new Student3();
        student1.setName("Dream");
        student1.setAge(18);
        student1.setTeacher(teacher);

        Student3 student2 = (Student3) student1.clone();
        System.out.println("拷貝後");
        System.out.println(student2.getName());
        System.out
            
           

相關推薦

Java基礎12——深入理解ClassObject

深入理解Class類和Object類 一、所有類的型別資訊的記錄員——Class類 先來簡單瞭解一下Java虛擬機器中類的載入過程: 「載入」階段是「類載入」過程的第一個階段,虛擬機器需要完成以下三件事情: 1. 通過一個類的全限定名來獲取定義此類的二進位制位元組流(.class檔案

Java基礎深入理解Class物件與反射機制

深入理解Class物件 RRIT及Class物件的概念 RRIT(Run-Time Type Identification)執行時型別識別。在《Thinking in Java》一書第十四章中有提到,它的功能是在執行時識別物件的型別和類資訊。有兩種主要方式:“傳統的”RTTI(它假定我們在編譯時

Java基礎4——深入理解final關鍵字static關鍵字以及初始化順序

深入理解final關鍵字和static關鍵字以及初始化順序 final關鍵字(基礎1中提到) final關鍵字可以修飾類、方法和引用。 修飾類,該類不能被繼承。並且這個類的物件在堆中分配記憶體後地址不可變。 修飾方法,方法不能被子類重寫。 修飾引用,引用無法改變,對於基本型別,無法修

Java基礎15-final、static關鍵字Object

不同 擁有 bsp 指數 col main hashcode 技術分享 fin 一、final關鍵字 1.什麽是final關鍵字 繼承的出現提高了代碼的復用性,並方便開發。但隨之也有問題,有些類在描述完之後,不想被繼承,或者有些類中的部分方法功能是固定的,不想讓子類重寫。可

夯實Java基礎系列9:深入理解ClassObject

目錄 Java中Class類及用法 Class類原理 如何獲得一個Class類物件 使用Class類的物件來生成目標類的例項 Object類 類構造器public Object(); registerNatives()方法; Clone()方法實現淺拷貝 getClass()方法 equals()方法

Java基礎5——深入理解抽象介面

抽象類和介面 抽象方法和抽象類   抽象方法是一種特殊的方法:它只有宣告,而沒有具體的實現。抽象方法的宣告格式為: abstract void f();   《Java程式設計思想》一書中將抽象類定義為包含抽象方法的類,但準確來說,包含抽象方法的類一定是抽象類,但抽象類不

Java基礎深入理解Java的介面抽象

     對於面向物件程式設計來說,抽象是它的一大特徵之一。在Java中,可以通過兩種形式來體現OOP的抽象:介面和抽象類。這兩者有太多相似的地方,又有太多不同的地方。很多人在初學的時候會以為它們可以隨意互換使用,但是實際則不然。今天我們就一起來學習一下Java中的介面和

Java基礎深入理解java異常處理機制的原理開發應用【轉載】

Java異常處理機制在日常開發中應用頻繁,本篇文章主要在基礎的使用方法上,更進一步的,如何更加合理的使用異常機制,希望可以對各位朋友能有所幫助。   Java異常處理機制其最主要的幾個關鍵字:try、catch、finally、throw、throws,以及各種各樣

Java基礎7——深入理解Java與包

java類與包 (文章部分內容轉載於How 2 Play Life ) *.Java檔案 問題:一個”.java”原始檔中是否可以包括多個類(不是內部類)?有什麼限制? 答案:可以有多個類,但只能有一個public的類,並且public的類名必須與檔名相一致。一個檔案

Java基礎3——深入理解String

深入理解String類 String類的不變性    java.lang.String類使用了final修飾,不能被繼承。Java程式中的所有字面值,即雙引號括起的字串,如"abc",都是作為String類的例項實現的。String是常量,其物件一旦構造就不能再被改變。換句話說,St

Java基礎系列-深入理解==equals的區別(一)

一、前言 說到==和equals的問題,面試的時候可能經常被問題到,有時候如果你真的沒有搞清楚裡邊的原因,被面試官一頓繞就懵了,所以今天我們也來徹底瞭解一下這個知識點。 二、==和equals的作用 2.1 ==的作用 在java中我們用==來判斷兩個變數是否相等,但是會根據資料型別有所區別: 1.對於8種基

深入理解接口抽象的區別以及使用

靜態方法 因此 解決辦法 stat 繼承 功能 fun 抽象方法 調用 感覺這篇文章對於接口和抽象類的理解非常有幫助!故抄襲下來,留存。 對於面向對象編程來說,抽象是它的一大特征之一。在Java中,可以通過兩種形式來體現OOP的抽象:接口和抽象類。這兩者有太多相似的地方,又

java基礎深入理解JDK動態代理

其他更多java基礎文章: java基礎學習(目錄) 經過上一節我們講了Class物件和反射機制,這節就來講一下反射機制在java中的主要應用——動態代理。在講動態代理之前,會先講一下代理模式和靜態代理。 一、代理模式 代理模式是常用的java設計模式,他的特徵是代理類與委託類有同樣的介面,代理類主

Java基礎9——深入理解java回撥機制

Java回撥機制 回撥的引入故事 轉載自Bro_超 // 同步回撥 1 public interface doJob 2 { 3 public void fillBlank(int a, int b, int result); 4 } 1 public class Su

Java基礎13——深入理解java中的反射機制

java中的反射機制 什麼是反射?   反射(Reflection)是Java 程式開發語言的特徵之一,它允許執行中的 Java 程式獲取自身的資訊,並且可以操作類或物件的內部屬性。   簡而言之,通過反射,我們可以在執行時獲得程式或程式集中每一個型別的成員和成員的資訊。    程式

Java基礎11———深入理解Java泛型

Java泛型 why need 泛型? 首先,我們看下下面這段簡短的程式碼: public class GenericTest { public static void main(String[] args) { List list = new Arra

Java基礎10——深入理解Java異常

深入理解Java異常 Java中的異常和處理詳解 為什麼使用異常?   使用異常機制它能夠降低錯誤處理程式碼的複雜度,如果不使用異常,那麼就必須檢查特定的錯誤,並在程式中的許多地方去處理它,而如果使用異常,那就不必在方法呼叫處進行檢查,因為異常機制將保證能夠捕獲這個錯誤,並且,只

Java基礎8——深入理解java的內部類

深入理解內部類 內部類基礎   在Java中,可以將一個類定義在另一個類裡面或者一個方法裡面,這樣的類稱為內部類。廣泛意義上的內部類一般來說包括這四種:成員內部類、區域性內部類、匿名內部類和靜態內部類。 問題:為什麼要使用內部類?   在《Think in java》中有這樣一句

Java基礎6——深入理解五種型別的程式碼塊與程式碼載入順序

程式碼塊與程式碼載入順序 程式碼塊 public class Test { { //// } }   這種形式的程式段我們將其稱之為程式碼塊,所謂程式碼塊就是用大括號({ })將多行程式碼封裝在一起,形成一個獨立的資料體,用於實現特定的演算法。一

Java基礎2——深入理解基本資料型別與常量池

基本資料型別與常量池 基本資料型別   Java中的基本資料型別只有8個:byte(1位元組=8位)、short(2位元組)、int(4字 節)、long(8位元組)、float(4位元組)、double(8位元組)、char(1字 節)、boolean(1位)。   除了以上8種基