//hotspot的oop.hpp文件中class oopDesc class oopDesc { friend class VMStructs; private: volatile markOop _mark; //对象部分 union _metadata { // klassOop 类元数据指针 Klass* _klass; narrowKlass _compressed_klass; } _metadata;
哪些信息会被压缩
public static void main(String[] args){ Object a = new Object(); // 16B 关闭压缩还是16B,需要是8B倍数;12B+填充的4B int[] arr = new int[10]; // 24B 关闭压缩则是16B } public class ObjectNum { //8B mark word //4B Klass Pointer 如果关闭压缩则占用8B //-XX:-UseCompressedClassPointers或-XX:-UseCompressedOops, int id; //4B String name; //4B 如果关闭压缩则占用8B byte b; //1B 实际内存可能会填充到4B Object o; //4B 如果关闭压缩则占用8B }
<br/>jvm要求对象起始位置对齐8字节的倍数,可以利用这点提升选址范围,理论上可以提升到`2^11 * 4G`。不过jvm将只是指针左移三位,因此`2^3 * 4G = 32G`。如果**大于32G**,指针压缩会失效。如果GC堆大小在 **4G以下**,直接砍掉高32位,避免了编码解码过程 - 启用指针压缩`-XX:+UseCompressedOops`(**默认开启**),禁止指针压缩:`-XX:-UseCompressedOops`
本地方法
private static native void registerNatives()
将Object定义的本地方法和java程序链接起来。对JNI方面了解不多,就不多解释了 Object类中的registerNatives public final native Class<?> getClass()
获取java的Class元数据 public native int hashCode()
获取对象的哈希Code protected native Object clone() throws CloneNotSupportedException
获得对象的克隆对象,浅复制 public final native void notify()
唤醒等待对象锁waitSet队列中的一个线程 public final native void notifyAll()
类似notify(),唤醒等待对象锁waitSet队列中的全部线程 public final native void wait(long timeout)
释放对象锁,进入对象锁的waitSet队列 普通方法
public String toString() { return getClass().getName() + "@" + Integer.toHexString(hashCode());} public boolean equals(Object obj) { return (this == obj);} public final void wait(long timeout, int nanos) throws InterruptedException; //都是基于native void wait(long timeout)实现的 public final void wait() throws InterruptedException; wait(long timeout, int nanos)、wait() //jvm回收对象前,会特意调用此方法 protected void finalize() throws Throwable;
如不指定排序顺序,java里的默认排序顺序是升序的,从小到大
public boolean equals(Object obj) { return (this == obj);}
是使用 == 去比较的。equals方法的好处是我们可以重写该方法 Comparable.compareTo 是接口Comparable里的抽象方法;如果对象实现该接口,可使用Collections.sort(List< T> col)进行排序。接下来看看源码怎么实现的
Collections.java //Collections.sort(List<T> list),调用的是List的sort方法 public static <T extends Comparable<? super T>> void sort(List<T> list) { list.sort(null); }
List的sort 则调用了Arrays.sort
List.java default void sort(Comparator<? super E> c) { Object[] a = this.toArray(); Arrays.sort(a, (Comparator) c); ListIterator<E> i = this.listIterator(); for (Object e : a) { i.next(); i.set((E) e); } }
如果Comparator c 为null,则是调用 Arrays.sort(Object[] a) ;最终调用LegacyMergeSort(归并排序)方法处理
Arrays.java public static <T> void sort(T[] a, Comparator<? super T> c) { if (c == null) { sort(a); } else { if (LegacyMergeSort.userRequested) legacyMergeSort(a, c); else TimSort.sort(a, 0, a.length, c, null, 0, 0); } }
LegacyMergeSort方法里的一段代码;最终底层是使用 归并排序 和compareTo来排序
Arrays.java ...... if (length < INSERTIONSORT_THRESHOLD) { for (int i=low; i<high; i++) for (int j=i; j>low && ((Comparable) dest[j-1]).compareTo(dest[j])>0; j--) swap(dest, j, j-1); return; }
Comparator也是一个接口,不过提供了更丰富的操作,需要实现 int compare(T o1, T o2)
方法
<br/>Comparator提供了常用的几个静态方法thenComparing、reversed、reverseOrder(操作对象需要实现Comparator或者Comparable);可配合List.sort、Stream.sorted、Collections.sort使用。
@Data @AllArgsConstructor static class Pair implements Comparator<Pair>, Comparable<Pair> { Integer one; Integer two; @Override public String toString() { return one + "-" + two; } @Override public int compareTo(Pair o) { return one.compareTo(o.one); } @Override public int compare(Pair o1, Pair o2) {return o1.compareTo(o2);} } public static void main(String[] args) { List<Pair> col = Arrays.asList( new Pair(4, 6), new Pair(4, 2),new Pair(1, 3)); col.sort(Comparator.reverseOrder()); col.stream().sorted(Comparator.comparing(Pair::getOne).thenComparing(Pair::getTwo)) .forEach(item -> System.out.println(item.toString()) ); }
Collections.sort默认是升序排序的,可以看到reverseOrder将顺序反过来了; 用了thenComparing的col则是先判断Pair::getOne的大小,如果相等则判断Pair::getTwo大小来排序
result: 4-6 4-2 1-3 ---------------- 1-3 4-2 4-6
构造方法是每一个类独有的,并不能被子类继承,因为构造方法没有返回值,子类定义不了和父类的构造方法一样的方法。但是在同一个类中,构造方法可以重载
public class TestEquals { int i; public TestEquals() { i = 0; } //构造方法重载 public TestEquals(int i) { this.i = i } }
equals是用来比较两个对象是否相等的,可以重写该方法来实现自定义的比较方法;而hashCode则是用来获取对象的哈希值,也可以重写该方法。当对象存储在Map时,是首先利用Object.hashCode判断是否映射在同一位置,若在同一映射位,则再使用equals比较两个对象是否相同。
如果重写equals导致对象比较相同而hashCode不一样,是违反JDK规范的;而且当用HashMap存储时,可能会存在多个我们自定义认为相同的对象,这样会为我们代码逻辑埋下坑。
Object.wait是需要在synchronized修饰的代码内使用,会让出CPU,并放弃对对象锁的持有状态。而Thread.sleep则简单的挂起,让出CPU,没有释放任何锁资源
finally和finalize区别
相似的关键字final
ObjectInputStream.readObject()
String one = new String("Hello");
<br/> 两个对象 和一个 栈变量 :一个栈变量one和一个new String()实例对象、一个"hello"字符串对象
<br/>如果是先执行 String s = "hello" ;
相当于执行了intern();先在常量池创建"hello",并且将引用A存入常量池,返回给s。此时String("hello").intern()会返回常量池的引用A返回
String one = "hello"; String two = new String("hello"); String three = one.intern(); System.out.println(two == one); System.out.println(three == one); result: false // one虽然不等于two;但是它们具体的char[] value 还是指向同一块内存的 true // one 和 three 引用相同
A a = new A(); A b = a;
此时a和b指向同一块内存的对象 使用Object.clone()方法,如果字段是值类型(基本类型)则是复制该值,如果是引用类型则复制对象的引用而并非对象
@Getter static class A implements Cloneable{ private B b; private int index; public A(){ b = new B(); index = 1000; } public A clone()throws CloneNotSupportedException{ return (A)super.clone(); } } static class B{ } public static void main(String[] args) throws Exception{ A a = new A(); A copyA = a.clone(); System.out.println( a.getIndex() == copyA.getIndex() ); System.out.println( a.getB() == copyA.getB() ); }
//返回结果都是true,引用类型只是复制了引用值 true true
深复制:重写clone方法时使用序列化复制,(注意需要实现Cloneable,Serializable)
public A clone() throws CloneNotSupportedException { try { ByteArrayOutputStream byteOut = new ByteArrayOutputStream(); ObjectOutputStream out = new ObjectOutputStream(byteOut); out.writeObject(this); ByteArrayInputStream byteIn = new ByteArrayInputStream(byteOut.toByteArray()); ObjectInputStream inputStream = new ObjectInputStream(byteIn); return (A) inputStream.readObject(); } catch (Exception e) { e.printStackTrace(); throw new CloneNotSupportedException(e.getLocalizedMessage()); } }