001_Object源码解析

Object类实例方法

Object 类是所有类的超类,可见这个类是有多么基础,Object类内部可以考察的知识点也是可以非常深入的。我们先从Object实例方法入手,所有的方法都很基础,需要熟记于心。

public class Object {
  //将Java层的方法名和本地函数对应起来
  private static native void registerNatives();

  static {
    registerNatives();
  }
  //得到当前Object的类类型
  public final native Class<?> getClass();

  public native int hashCode();

  //直接比较地址值
  public boolean equals(Object obj) {
    return (this == obj);
  }

  //克隆当前实例
  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();
  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);
  }

  public final void wait() throws InterruptedException {
    wait(0);
  }
    
  //与回收机制相关
  protected void finalize() throws Throwable {}
}

registerNatives方法

private static native void registerNatives();

static {
  registerNatives();
}

从上面的代码中看到Object类定义了一个静态初始化块,我们知道当创建Java对象时,系统总是先调用静态初始化块。而静态初始化块调用的是registerNatives方法,它的修饰的关键字使用了native。Java中,用native关键字修饰的函数表明该方法的实现并不是在Java中去完成,而是由C/C++去完成,并被编译成了.dll文件,由Java去调用。方法的具体实现体在dll文件中,对于不同平台,其具体实现可能有所不同。而registerNatives()方法本身,其主要作用是将C/C++中的方法映射到Java中的native方法,实现方法命名的解耦。registerNatives()对应的本地方法为Java_java_lang_Object_registerNatives

JNIEXPORT void JNICALL
Java_java_lang_Object_registerNatives(JNIEnv *env, jclass cls){
    (*env)->RegisterNatives(env, cls, methods, sizeof(methods)/sizeof(methods[0]));
}

可以看到它间接调用了JNINativeInterface_结构体的方法,简单可以看成是这样:它干的事大概就是将Java层的方法名和本地函数对应起来,方便执行引擎在执行字节码时根据这些对应关系表来调用C/C++函数,如下面,将这些方法进行注册,执行引擎执行到hashCode方法时就可以通过关系表来查找到JVM的JVM_IHashCode函数,其中()I还可以得知Java层上的类型应该转为int类型。这个映射其实就可以看成将字符串映射到函数指针。

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},
};

getClass方法

getClass方法也是个本地方法,对应的本地方法为Java_java_lang_Object_getClass

JNIEXPORT jclass JNICALL
Java_java_lang_Object_getClass(JNIEnv *env, jobject this){
    if (this == NULL) {
        JNU_ThrowNullPointerException(env, NULL);
        return 0;
    } else {
        return (*env)->GetObjectClass(env, this);
    }
}

所以这里主要就是看GetObjectClass函数了,Java层的Class在C++层与之对应的则是klassOop,所以关于类的元数据和方法信息可以通过它获得。

JNI_ENTRY(jclass, jni_GetObjectClass(JNIEnv *env, jobject obj))
  JNIWrapper("GetObjectClass");
  DTRACE_PROBE2(hotspot_jni, GetObjectClass__entry, env, obj);
  klassOop k = JNIHandles::resolve_non_null(obj)->klass();
  jclass ret =
    (jclass) JNIHandles::make_local(env, Klass::cast(k)->java_mirror());
  DTRACE_PROBE1(hotspot_jni, GetObjectClass__return, ret);
  return ret;
JNI_END

hashCode方法

由前面registerNatives方法将几个本地方法注册可知,hashCode方法对应的函数为JVM_IHashCode,即

JVM_ENTRY(jint, JVM_IHashCode(JNIEnv* env, jobject handle))
  JVMWrapper("JVM_IHashCode");
  // as implemented in the classic virtual machine; return 0 if object is NULL
  return handle == NULL ? 0 : ObjectSynchronizer::FastHashCode (THREAD, JNIHandles::resolve_non_null(handle)) ;
JVM_END

对于hashcode生成的逻辑由synchronizer.cpp的get_next_hash函数决定,实现比较复杂,根据hashcode的不同值有不同的生成策略,最后使用一个hash掩码处理。

static inline intptr_t get_next_hash(Thread * Self, oop obj) {
  intptr_t value = 0 ;
  if (hashCode == 0) {
     value = os::random() ;
  } else
  if (hashCode == 1) {
     intptr_t addrBits = intptr_t(obj) >> 3 ;
     value = addrBits ^ (addrBits >> 5) ^ GVars.stwRandom ;
  } else
  if (hashCode == 2) {
     value = 1 ;            // for sensitivity testing
  } else
  if (hashCode == 3) {
     value = ++GVars.hcSequence ;
  } else
  if (hashCode == 4) {
     value = intptr_t(obj) ;
  } else {
     unsigned t = Self->_hashStateX ;
     t ^= (t << 11) ;
     Self->_hashStateX = Self->_hashStateY ;
     Self->_hashStateY = Self->_hashStateZ ;
     Self->_hashStateZ = Self->_hashStateW ;
     unsigned v = Self->_hashStateW ;
     v = (v ^ (v >> 19)) ^ (t ^ (t >> 8)) ;
     Self->_hashStateW = v ;
     value = v ;
  }

  value &= markOopDesc::hash_mask;
  if (value == 0) value = 0xBAD ;
  assert (value != markOopDesc::no_hash, "invariant") ;
  TEVENT (hashCode: GENERATE) ;
  return value;
}

equals方法

== 符号是来判断左右值的直接引用地址是否相同。而equals则表达逻辑上左右值是否相等。hashCode方法与equals的方法是需要配合的。规则如下:

  1. 如果 A.equals(B),则 A.hashCode == B.hashCode
  2. 如果 A == B,则 A.equals(B) => true
  3. 如果 A.hashCode == B.hashCode,不能推出 A == B

综上,equals ⽅法被覆盖过,则 hashCode ⽅法也必须被覆盖。hashCode()的默认⾏为是对堆上的对象产⽣独特值。如果没有重写 hashCode(),则该 class 的两个对象⽆论如何都不会相等。

clone方法

后面我们详细描述。

toString方法

逻辑是获取class名称加上@再加上十六进制的hashCode。

notify()、notifyAll()、wait()

我们先来看看这几个方法是怎么用的:


public class NotifyTest {
    @Test
    public void test1() {
        Object lock = new Object();
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("线程A等待获取lock锁");
                synchronized (lock) {
                    try {
                        System.out.println("线程A获取了lock锁");
                        Thread.sleep(1000);
                        System.out.println("线程A将要运行lock.wait()方法进行等待");
                        lock.wait();
                        System.out.println("线程A等待结束");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("线程B等待获取lock锁");
                synchronized (lock) {
                    System.out.println("线程B获取了lock锁");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("线程B将要运行lock.notify()方法进行通知");
                    lock.notify();
                    while (true) {
                    }
                }
            }
        }).start();

        try {
            Thread.sleep(1000000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

执行之后获得的输出是:

线程A等待获取lock锁
线程A获取了lock锁
线程B等待获取lock锁
线程A将要运行lock.wait()方法进行等待
线程B获取了lock锁
线程B将要运行lock.notify()方法进行通知

notify等方法需要和synchronized关键字配合使用。notify方法用来唤醒线程,final修饰说明不可重写。与之对应的本地方法为JVM_MonitorNotify,ObjectSynchronizer::notify最终会调用ObjectMonitor::notify(TRAPS),这个过程是ObjectSynchronizer会尝试当前线程获取free ObjectMonitor对象,不成功则尝试从全局中获取。

JVM_ENTRY(void, JVM_MonitorNotify(JNIEnv* env, jobject handle))
  JVMWrapper("JVM_MonitorNotify");
  Handle obj(THREAD, JNIHandles::resolve_non_null(handle));
  assert(obj->is_instance() || obj->is_array(), "JVM_MonitorNotify must apply to an object");
  ObjectSynchronizer::notify(obj, CHECK);
JVM_END

ObjectMonitor对象包含一个WaitSet队列对象,此对象保存着所有处于wait状态的线程,用ObjectWaiter对象表示。notify要做的事是先获取WaitSet队列锁,再取出WaitSet队列中第一个ObjectWaiter对象,再根据不同策略处理该对象,比如把它加入到EntryList队列中。然后再释放WaitSet队列锁。它并没有释放synchronized对应的锁,所以锁只能等到synchronized同步块结束时才释放。

void ObjectMonitor::notify(TRAPS) {
  CHECK_OWNER();
  if (_WaitSet == NULL) {
     TEVENT (Empty-Notify) ;
     return ;
  }
  DTRACE_MONITOR_PROBE(notify, this, object(), THREAD);

  int Policy = Knob_MoveNotifyee ;

  Thread::SpinAcquire (&_WaitSetLock, "WaitSet - notify") ;
  ObjectWaiter * iterator = DequeueWaiter() ;
  if (iterator != NULL) {
     TEVENT (Notify1 - Transfer) ;
     guarantee (iterator->TState == ObjectWaiter::TS_WAIT, "invariant") ;
     guarantee (iterator->_notified == 0, "invariant") ;
     if (Policy != 4) {
        iterator->TState = ObjectWaiter::TS_ENTER ;
     }
     iterator->_notified = 1 ;

     ObjectWaiter * List = _EntryList ;
     if (List != NULL) {
        assert (List->_prev == NULL, "invariant") ;
        assert (List->TState == ObjectWaiter::TS_ENTER, "invariant") ;
        assert (List != iterator, "invariant") ;
     }

     if (Policy == 0) {       // prepend to EntryList
         if (List == NULL) {
             iterator->_next = iterator->_prev = NULL ;
             _EntryList = iterator ;
         } else {
             List->_prev = iterator ;
             iterator->_next = List ;
             iterator->_prev = NULL ;
             _EntryList = iterator ;
        }
     } else
     if (Policy == 1) {      // append to EntryList
         if (List == NULL) {
             iterator->_next = iterator->_prev = NULL ;
             _EntryList = iterator ;
         } else {
            // CONSIDER:  finding the tail currently requires a linear-time walk of
            // the EntryList.  We can make tail access constant-time by converting to
            // a CDLL instead of using our current DLL.
            ObjectWaiter * Tail ;
            for (Tail = List ; Tail->_next != NULL ; Tail = Tail->_next) ;
            assert (Tail != NULL && Tail->_next == NULL, "invariant") ;
            Tail->_next = iterator ;
            iterator->_prev = Tail ;
            iterator->_next = NULL ;
        }
     } else
     if (Policy == 2) {      // prepend to cxq
         // prepend to cxq
         if (List == NULL) {
             iterator->_next = iterator->_prev = NULL ;
             _EntryList = iterator ;
         } else {
            iterator->TState = ObjectWaiter::TS_CXQ ;
            for (;;) {
                ObjectWaiter * Front = _cxq ;
                iterator->_next = Front ;
                if (Atomic::cmpxchg_ptr (iterator, &_cxq, Front) == Front) {
                    break ;
                }
            }
         }
     } else
     if (Policy == 3) {      // append to cxq
        iterator->TState = ObjectWaiter::TS_CXQ ;
        for (;;) {
            ObjectWaiter * Tail ;
            Tail = _cxq ;
            if (Tail == NULL) {
                iterator->_next = NULL ;
                if (Atomic::cmpxchg_ptr (iterator, &_cxq, NULL) == NULL) {
                   break ;
                }
            } else {
                while (Tail->_next != NULL) Tail = Tail->_next ;
                Tail->_next = iterator ;
                iterator->_prev = Tail ;
                iterator->_next = NULL ;
                break ;
            }
        }
     } else {
        ParkEvent * ev = iterator->_event ;
        iterator->TState = ObjectWaiter::TS_RUN ;
        OrderAccess::fence() ;
        ev->unpark() ;
     }

     if (Policy < 4) {
       iterator->wait_reenter_begin(this);
     }

     // _WaitSetLock protects the wait queue, not the EntryList.  We could
     // move the add-to-EntryList operation, above, outside the critical section
     // protected by _WaitSetLock.  In practice that's not useful.  With the
     // exception of  wait() timeouts and interrupts the monitor owner
     // is the only thread that grabs _WaitSetLock.  There's almost no contention
     // on _WaitSetLock so it's not profitable to reduce the length of the
     // critical section.
  }

  Thread::SpinRelease (&_WaitSetLock) ;

  if (iterator != NULL && ObjectMonitor::_sync_Notifications != NULL) {
     ObjectMonitor::_sync_Notifications->inc() ;
  }
}

notifyAll方法 与notify方法类似,只是在取_WaitSet队列时不是取第一个而是取所有。
wait方法是让线程等待,它对应的本地方法是JVM_MonitorWait,间接调用了ObjectSynchronizer::wait,与notify对应,它也是对应调用ObjectMonitor对象的wait方法。这个方法大概就是创建一个ObjectWaiter对象,接着获取_WaitSet队列锁将ObjectWaiter对象添加到该队列中,再释放队列锁。另外,它还会释放synchronized对应的锁,所以锁没有等到synchronized同步块结束时才释放。

JVM_ENTRY(void, JVM_MonitorWait(JNIEnv* env, jobject handle, jlong ms))
  JVMWrapper("JVM_MonitorWait");
  Handle obj(THREAD, JNIHandles::resolve_non_null(handle));
  assert(obj->is_instance() || obj->is_array(), "JVM_MonitorWait must apply to an object");
  JavaThreadInObjectWaitState jtiows(thread, ms != 0);
  if (JvmtiExport::should_post_monitor_wait()) {
    JvmtiExport::post_monitor_wait((JavaThread *)THREAD, (oop)obj(), ms);
  }
  ObjectSynchronizer::wait(obj, ms, CHECK);
JVM_END

finalize方法

这个方法用于当对象被回收时调用,这个由JVM支持,Object的finalize方法默认是什么都没有做,如果子类需要在对象被回收时执行一些逻辑处理,则可以重写finalize方法。一般不需要关心这个方法。

Object对象的创建策略

创建一个对象并不是只能通过new出来。明面上,创建object的方式有很多很多,至少可以列出以下6点

  1. 通过构造方法实例化一个类;
  2. 通过Class实例化一个类;
  3. 通过反射实例化一个类;
  4. 通过克隆实例化一个类;
  5. 通过反序列化实例化一个类;
  6. 通过Unsafe实例化一个类;
public class InstantialTest {

    private static Unsafe unsafe;
    static {
        try {
            Field f = Unsafe.class.getDeclaredField("theUnsafe");
            f.setAccessible(true);
            unsafe = (Unsafe) f.get(null);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
    
    public static void main(String[] args) throws Exception {
        // 1. 构造方法
        User user1 = new User();
        // 2. Class,里面实际也是反射
        User user2 = User.class.newInstance();
        // 3. 通过构造函数反射
        User user3 = User.class.getConstructor().newInstance();
        // 4. 克隆
        User user4 = (User) user1.clone();
        // 5. 反序列化
        User user5 = unserialize(user1);
        // 6. Unsafe
        User user6 = (User) unsafe.allocateInstance(User.class);

        System.out.println(user1.age);
        System.out.println(user2.age);
        System.out.println(user3.age);
        System.out.println(user4.age);
        System.out.println(user5.age);

        //输出为0
        //Unsafe的allocateInstance()方法只会给对象分配内存,并不会初始化对象中的属性,所以int类型的默认值就是0
        System.out.println(user6.age);
    }

    private static User unserialize(User user1) throws Exception {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D://object.txt"));
        oos.writeObject(user1);
        oos.close();

        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D://object.txt"));
        // 反序列化
        User user5 = (User) ois.readObject();
        ois.close();
        return user5;
    }

    static class User implements Cloneable, Serializable {
        private int age = 20;

        public User() {
            this.age = 10;
        }

        @Override
        protected Object clone() throws CloneNotSupportedException {
            return super.clone();
        }
    }
}

Object对象初始化顺序

一般来说,对象初始化都是使用new 关键字进行的。而一个类的成分很多,并且还涉及到父类。我们走一个测试用例就可以将执行顺序表达出来。

class Field {

    static{
        System.out.println("Field-initial-static");
    }

    {
        System.out.println("Field-not-initial-static");
    }

    public Field(String str){
        System.out.println(str+"-Field-construct");
    }
}
class Father{

    private Field field = new Field("father");

    static{
        System.out.println("father-initial-static");
    }

    {
        System.out.println("father-not-initial-static");
    }

    public Father(){
        System.out.println("father-construct");
    }

}

public class Son extends Father{

    private Field field = new Field("son");

    static{
        System.out.println("Son-initial-static");
    }

    {
        System.out.println("Son-not-initial-static");
    }

    public Son(){
        System.out.println("Son-construct");
    }

    public static void main(String[] args) {
        Son son = new Son();
    }
}

获得到输出是:

father-initial-static
Son-initial-static
Field-initial-static
Field-not-initial-static
father-Field-construct
father-not-initial-static
father-construct
Field-not-initial-static
son-Field-construct
Son-not-initial-static
Son-construct

从输出可以看出满足以下规律:

  1. 碰到new关键字,先进行类加载,类加载会优先递归父类进行类加载,类加载会导致static块儿执行
  2. 进一步开始递归父类进行字段初始化,字段初始化同样会导致类加载
  3. 在执行构造函数之前,将判定是否有父类,存在则优先执行父类构造函数,不存在则执行非static块

Object对象克隆机制

我们知道所有的类都是Object的子类,而Object的类内天生就会有一个clone方法:

protected native Object clone() throws CloneNotSupportedException;

clone方法,顾名思义,就是将实例在内存内拷贝一份。这个Clone方法是native的,因此拷贝的能力是jvm提供的。但是JVM并不是针对所有的类都赋予可以拷贝的能力,如何区分呢?这就需要涉及到Cloneable接口的作用。Cloneable接口是一个空接口,起的是一个标识作用,而这个标识是给JVM看的,表达当前类是可以被拷贝的。如果一个类没有实现这个接口,那么当你在操作clone方法的时候,虚拟机会抛出CloneNotSupportedException异常。为了更加深刻理解,我们写一下测试用例:

package com.zifang.ex.bust.ex;

public class CloneableTest{
    public String name;

    public static void main(String[] args) throws CloneNotSupportedException {
        CloneableTest cloneableTest = new CloneableTest();
        cloneableTest.name = "吃饭";

        CloneableTest cloneableTest1 = (CloneableTest)cloneableTest.clone();
        System.out.println(cloneableTest1.name);
    }
}

这样执行的结果是抛出异常:

Exception in thread "main" java.lang.CloneNotSupportedException: com.zifang.ex.bust.ex.CloneableTest
at java.lang.Object.clone(Native Method)
at com.zifang.ex.bust.ex.CloneableTest.main(CloneableTest.java:10)

Process finished with exit code 1

那么我改成这样:

package com.zifang.ex.bust.ex;

public class CloneableTest implements Cloneable{
    public String name;

    public static void main(String[] args) throws CloneNotSupportedException {
        CloneableTest cloneableTest = new CloneableTest();
        cloneableTest.name = "吃饭";

        CloneableTest cloneableTest1 = (CloneableTest)cloneableTest.clone();
        System.out.println(cloneableTest1.name);
    }
}

这样一来,可以正常执行。这里需要注意,clone方法是protected标记的,因此在其他类内调用是调用不到的,因此在这个类内需要进行覆盖,将clone方法暴露为public方法,这样才能在其他类内被访问到。

package com.zifang.ex.bust.ex;

public class CloneableTest implements Cloneable{
    public String name;

    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    public static void main(String[] args) throws CloneNotSupportedException {
        CloneableTest cloneableTest = new CloneableTest();
        cloneableTest.name = "吃饭";

        CloneableTest cloneableTest1 = (CloneableTest)cloneableTest.clone();
        System.out.println(cloneableTest1.name);
    }
}

在覆盖了的类内调用父类的clone方法,委托jvm进行数据拷贝,然后clone方法暴露为public类型,在其他类内可以访问到。
我们把虚拟机的C++代码扒出来,clone方法对应的本地函数为JVM_Clone,其中if (!klass->is_cloneable())这里会校验是否有实现Cloneable接口,如果不是则抛出异常。

JVM_ENTRY(jobject, JVM_Clone(JNIEnv* env, jobject handle))
  JVMWrapper("JVM_Clone");
  Handle obj(THREAD, JNIHandles::resolve_non_null(handle));
  const KlassHandle klass (THREAD, obj->klass());
  JvmtiVMObjectAllocEventCollector oam;

  if (!klass->is_cloneable()) {
    ResourceMark rm(THREAD);
    THROW_MSG_0(vmSymbols::java_lang_CloneNotSupportedException(), klass->external_name());
  }

  const int size = obj->size();
  oop new_obj = NULL;
  // 判断是否是数组分两种情况分配内存空间
  if (obj->is_javaArray()) {
    const int length = ((arrayOop)obj())->length();
    // 新对象为new_obj
    new_obj = CollectedHeap::array_allocate(klass, size, length, CHECK_NULL);
  } else {
    new_obj = CollectedHeap::obj_allocate(klass, size, CHECK_NULL);
  }
  // 对new_obj进行copy
  Copy::conjoint_jlongs_atomic((jlong*)obj(), (jlong*)new_obj,
                               (size_t)align_object_size(size) / HeapWordsPerLong);
  // 进行数据结构的设置
  new_obj->init_mark();

  BarrierSet* bs = Universe::heap()->barrier_set();
  assert(bs->has_write_region_opt(), "Barrier set does not have write_region");
  bs->write_region(MemRegion((HeapWord*)new_obj, size));

  if (klass->has_finalizer()) {
    assert(obj->is_instance(), "should be instanceOop");
    new_obj = instanceKlass::register_finalizer(instanceOop(new_obj), CHECK_NULL);
  }
  // 转成jobject类型方便转成Java层的Object类型
  return JNIHandles::make_local(env, oop(new_obj));
JVM_END

Object#clone的行为是,将当前的Object数据在内存里整个重新复制一遍,生成另外一个相同的Obejct,但是这两个Object的内存地址是一样的,如果在拷贝出来的对象上进行数据变更,是有可能影响到拷贝之前的数据的。这个我们称为浅拷贝与深拷贝。

class CloneField implements Cloneable {

    private Long l = System.currentTimeMillis();

}
public class CloneableTest implements Cloneable {

    public String name;

    public CloneField field = new CloneField();

    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    public static void main(String[] args) throws CloneNotSupportedException {
        CloneableTest cloneableTest = new CloneableTest();
        cloneableTest.name = "吃饭";

        CloneableTest cloneableTest1 = (CloneableTest) cloneableTest.clone();
        System.out.println(cloneableTest1.name);
        System.out.println(cloneableTest.field == cloneableTest1.field);
    }
}

image.png
如果希望执行clone是深拷贝的结果,可以有两种思路:

  1. 使用序列化,将Object存为字节数组然后读入jvm生成一个新的Obejct;
  2. 在Object子类内覆盖Clone方式,手动实现深拷贝
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ArrayList是Java集合框架中的一个类,它实现了List接口,可以用来存储一组对象,这些对象可以是任意类型。 下面是ArrayList的源码解析: 1. 成员变量 ```java /** * Default initial capacity. */ private static final int DEFAULT_CAPACITY = 10; /** * Shared empty array instance used for empty instances. */ private static final Object[] EMPTY_ELEMENTDATA = {}; /** * Shared empty array instance used for default sized empty instances. We * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when * first element is added. */ private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; /** * The array buffer into which the elements of the ArrayList are stored. * The capacity of the ArrayList is the length of this array buffer. Any * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA * will be expanded to DEFAULT_CAPACITY when the first element is added. */ transient Object[] elementData; // non-private to simplify nested class access /** * The size of the ArrayList (the number of elements it contains). * * @serial */ private int size; ``` ArrayList有三个成员变量,分别是DEFAULT_CAPACITY、EMPTY_ELEMENTDATA和DEFAULTCAPACITY_EMPTY_ELEMENTDATA。DEFAULT_CAPACITY表示默认的容量大小,EMPTY_ELEMENTDATA是一个空数组,DEFAULTCAPACITY_EMPTY_ELEMENTDATA也是一个空数组,但它会在第一次添加元素时扩容为DEFAULT_CAPACITY大小。elementData是一个Object类型的数组,用于存储ArrayList中的元素,size表示ArrayList中元素的数量。 2. 构造方法 ```java /** * Constructs an empty list with an initial capacity of ten. */ public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; } /** * Constructs a list containing the elements of the specified * collection, in the order they are returned by the collection's * iterator. * * @param c the collection whose elements are to be placed into this list * @throws NullPointerException if the specified collection is null */ public ArrayList(Collection<? extends E> c) { elementData = c.toArray(); if ((size = elementData.length) != 0) { // defend against c.toArray (incorrectly) not returning Object[] // (see e.g. https://bugs.openjdk.java.net/browse/JDK-6260652) if (elementData.getClass() != Object[].class) elementData = Arrays.copyOf(elementData, size, Object[].class); } else { // replace with empty array. this.elementData = EMPTY_ELEMENTDATA; } } /** * Constructs an empty list with the specified initial capacity. * * @param initialCapacity the initial capacity of the list * @throws IllegalArgumentException if the specified initial capacity * is negative */ public ArrayList(int initialCapacity) { if (initialCapacity > 0) { this.elementData = new Object[initialCapacity]; } else if (initialCapacity == 0) { this.elementData = EMPTY_ELEMENTDATA; } else { throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); } } ``` ArrayList提供了三个构造方法。第一个构造方法是无参的构造方法,它将elementData赋值为DEFAULTCAPACITY_EMPTY_ELEMENTDATA。第二个构造方法接收一个Collection类型的参数c,它将参数c中的元素转为数组并将其赋值给elementData。第三个构造方法接收一个int类型的参数initialCapacity,它根据参数initialCapacity的值创建一个Object类型的数组并将其赋值给elementData。 3. 常用方法 常用方法包括add()、get()、set()、remove()、size()等。 add()方法用于在ArrayList中添加一个元素,如果elementData的容量不足,就需要进行扩容。扩容的方式是将elementData数组的大小增加50%。 get()方法用于获取ArrayList中指定位置的元素。 set()方法用于将ArrayList中指定位置的元素替换为指定的元素。 remove()方法用于删除ArrayList中指定位置的元素。 size()方法用于获取ArrayList中元素的数量。 4. 总结 ArrayList是Java集合框架中的一个类,它实现了List接口,可以用来存储一组对象。ArrayList的源码解析包括成员变量、构造方法和常用方法。掌握ArrayList的源码可以帮助我们更好地理解它的实现原理,从而更加灵活地应用它。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值