本文主要是介绍吃透Java基础五:Class类和Object类,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
在Java的世界里,一切皆是对象,所有的对象都是继承于Object类,而记录对象的类型的信息是由Class类来完成的,下面就让我们来具体了解一下Class类和Object类。
一:Class类
每个类的运行时的类型信息就是用Class对象表示的,它包含了与类有关的信息,其实我们的实例对象就通过Class对象来创建的。Java使用Class对象执行其RTTI(运行时类型识别,Run-Time Type Identification),多态是基于RTTI实现的。
每一个类都有Class对象,基本类型 ( byte, char, short, int, long, float, double and boolean)有Class对象,数组有Class对象,就连关键字void也有Class对象(void.class),Class对象对应着java.lang.Class类,如果说类是对象抽象和集合的话,那么Class类就是对类的抽象和集合。
Class类没有公共的构造方法,Class对象是在类加载的时候由Java虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的,因此不能显式地声明一个Class对象。一个类被加载到内存并供我们使用需要经历如下三个阶段:
-
加载:“加载”是“类加载”过程的第一个阶段,在加载阶段,虚拟机主要完成以下3件事情。
1、通过一个类的全限定名来获取定义此类的二进制字节流。
2、将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
3、在内存中生成一个代表类的 java.lang.Class对象,作为方法区这个类的各种数据的访问入口。 -
链接:链接可以分为 验证、准备、解析 三个阶段
验证:为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全,包括 文件格式验证、元数据验证、字节码验证、符号引用验证。
准备:准备阶段是正式为类变量(静态变量)分配内存并设置类变量初始值的阶段,这些变量所使用的内存都将在方法区中进行分配。
解析:解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程,包括 类或接口的解析、字段解析、类方法解析、接口方法解析。 -
初始化:初始化阶段是执行类构造器()方法的过程,()方法是由编译器自动收集类中的所有类变量的赋值动作和静态语句块中的语句合并产生的,编译器收集的顺序是由语句在源文件中出现的顺序所决定的。静态语句块中只能访问到定义在静态语句块之前的变量,定义在它之后的变量,在前面的静态语句块可以赋值,但是不能访问。
什么时候触发类的加载,请参考上一篇文章:吃透Java基础三:触发类初始化的五种方式
获取Class对象的三种方式
- Class.forName(“类的全限定名”)
- 实例对象.getClass()
- 类名.class (类字面常量)
Class类重要方法
类信息
私有构造函数,表示外部不能创建,只能由虚拟机默认去创建。
public final class Class<T> implements java.io.Serializable,GenericDeclaration,Type,AnnotatedElement {/** 私有构造函数,只能由虚拟机去创建*/private Class(ClassLoader loader) {classLoader = loader;}public String toString() {return (isInterface() ? "interface " : (isPrimitive() ? "" : "class "))+ getName();}}
通过forName()方法获取一个类的Class信息
/*** 返回与给定字符串名称的类或接口相关联的类对象*/
public static Class<?> forName(String className)throws ClassNotFoundException {Class<?> caller = Reflection.getCallerClass();return forName0(className, true, ClassLoader.getClassLoader(caller), caller);}/*** 使用给定的类加载器返回与给定字符串名称的类或接口相关联的类对象*/
public static Class<?> forName(String name, boolean initialize,ClassLoader loader)throws ClassNotFoundException{Class<?> caller = null;SecurityManager sm = System.getSecurityManager();if (sm != null) {// Reflective call to get caller class is only needed if a security manager// is present. Avoid the overhead of making this call otherwise.caller = Reflection.getCallerClass();if (sun.misc.VM.isSystemDomainLoader(loader)) {ClassLoader ccl = ClassLoader.getClassLoader(caller);if (!sun.misc.VM.isSystemDomainLoader(ccl)) {sm.checkPermission(SecurityConstants.GET_CLASSLOADER_PERMISSION);}}}return forName0(name, initialize, loader, caller);}/****/
private static native Class<?> forName0(String name, boolean initialize,ClassLoader loader,Class<?> caller)throws ClassNotFoundException;
通过newInstance可以创建一个该类的新的实例化对象
public T newInstance()throws InstantiationException, IllegalAccessException{if (System.getSecurityManager() != null) {checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), false);}// NOTE: the following code may not be strictly correct under// the current Java memory model.// Constructor lookupif (cachedConstructor == null) {if (this == Class.class) {throw new IllegalAccessException("Can not call newInstance() on the Class for java.lang.Class");}try {Class<?>[] empty = {};final Constructor<T> c = getConstructor0(empty, Member.DECLARED);// Disable accessibility checks on the constructor// since we have to do the security check here anyway// (the stack depth is wrong for the Constructor's// security check to work)java.security.AccessController.doPrivileged(new java.security.PrivilegedAction<Void>() {public Void run() {c.setAccessible(true);return null;}});cachedConstructor = c;} catch (NoSuchMethodException e) {throw (InstantiationException)new InstantiationException(getName()).initCause(e);}}Constructor<T> tmpConstructor = cachedConstructor;// Security check (same as in java.lang.reflect.Constructor)int modifiers = tmpConstructor.getModifiers();if (!Reflection.quickCheckMemberAccess(this, modifiers)) {Class<?> caller = Reflection.getCallerClass();if (newInstanceCallerCache != caller) {Reflection.ensureMemberAccess(caller, this, null, modifiers);newInstanceCallerCache = caller;}}// Run constructortry {return tmpConstructor.newInstance((Object[])null);} catch (InvocationTargetException e) {Unsafe.getUnsafe().throwException(e.getTargetException());// Not reachedreturn null;}}
二:Object类
Java中所有对象默认继承Object类,可以说,Object类是所有对象的祖先。Object类中一共定义12个方法,其中7个native方法,5个普通方法。
public class Object {private static native void registerNatives();static {registerNatives();}public final native Class<?> getClass();public native int hashCode();protected native Object clone() throws CloneNotSupportedException;public final native void wait(long timeout) throws InterruptedException;public final native void notify();public final native void notifyAll();public boolean equals(Object obj) {return (this == obj);}public String toString() {return getClass().getName() + "@" + Integer.toHexString(hashCode());}protected void finalize() throws Throwable { }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);}public final void wait() throws InterruptedException {wait(0);}
}
registerNatives()
registerNatives函数前面有native关键字修饰,Java中,用native关键字修饰的函数表明该方法的实现并不是在Java中去完成,而是由C/C++去完成,并被编译成了.dll,由Java去调用。
getClass()
getClass()也是一个native方法,返回的是此Object对象的类对象/运行时类对象Class<?>。效果与Object.class相同
hashCode()
返回对象的哈希码值。 支持这种方法是为了散列表,如HashMap提供的那样 。
- 只要在执行Java应用程序时多次在同一个对象上调用该方法, hashCode方法必须始终返回相同的整数
- 如果根据equals(Object)方法两个对象相等,则在两个对象中的每个对象上调用hashCode方法必须产生相同的整数结果。
- 不要求如果两个对象根据equals(java.lang.Object)方法不相等,那么在两个对象中的每个对象上调用hashCode方法必须产生不同的整数结果。 但是,程序员应该意识到,为不等对象生成不同的整数结果可能会提高哈希表的性能。
clone()
clone方法实现的是浅拷贝,只拷贝当前对象,并且在堆中分配新的空间,放这个复制的对象。但是对象如果里面有其他类的子对象,那么就不会拷贝到新的对象中。
深拷贝与浅拷贝
浅拷贝
浅拷贝是按位拷贝对象,它会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。
如果属性是基本类型,拷贝的就是基本类型的值;如果属性是内存地址(引用类型),
拷贝的就是内存地址 ,因此如果其中一个对象改变了这个地址,就会影响到另一个对象。深拷贝
深拷贝会拷贝所有的属性,并拷贝属性指向的动态分配的内存。当对象和它所引用的对象一起拷贝时即发生深拷贝。
深拷贝相比于浅拷贝速度较慢并且花销较大。
现在为了要在clone对象时进行深拷贝, 那么就要Clonable接口,覆盖并实现clone方法,
除了调用父类中的clone方法得到新的对象, 还要将该类中的引用变量也clone出来。
如果只是用Object中默认的clone方法,是浅拷贝的。
wait() notify() notifAll()
wait():调用此方法所在的当前线程等待,直到在其他线程上调用此对象的notify()/notifyAll()方法。
wait(long timeout)/wait(long timeout, int nanos):调用此方法所在的当前线程等待,直到在其他线程上调用此对象的notify()/notifyAll()方法,或超过指定的超时时间量。
notify()/notifyAll():唤醒在此对象监视器上等待的单个线程/所有线程。
equals(Object obj)
判断两个对象是否相等,equals方法在非空对象引用上实现等价关系:
- 自反性 :对于任何非空的参考值x , x.equals(x)应该返回true 。
- 对称性 :对于任何非空引用值x和y , x.equals(y)应该返回true当且仅当y.equals(x)回报true 。
- 传递性 :对于任何非空引用值x , y和z ,如果x.equals(y)回报true个y.equals(z)回报true ,然后x.equals(z)应该返回true 。
- 一致性 :对于任何非空引用值x和y ,多次调用x.equals(y)始终返回true或始终返回false ,没有设置中使用的信息equals比较上的对象被修改。
- 对于任何非空的参考值x , x.equals(null)应该返回false 。
toString()
返回对象的字符串表示形式,getClass()返回对象的类对象,getClassName()以String形式返回类对象的名称(含包名)。Integer.toHexString(hashCode())则是以对象的哈希码为实参,以16进制无符号整数形式返回此哈希码的字符串表示形式。
finalize()
一个子类覆盖了finalize方法,当垃圾回收此对象之前会调用此方法,但是只会调用一次,加入这次在finalize()方法中让此对象有引用指向他,那么下次再回收此对象时就不会调用此方法。
这篇关于吃透Java基础五:Class类和Object类的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!