深拷贝和浅拷贝

浅拷贝(shallowCopy)只是增加了一个指针指向已存在的内存地址,
深拷贝(deepCopy)是增加了一个指针并且申请了一个新的内存,使这个增加的指针指向这个新的内存。

也就是说,浅拷贝是直指要害,而深拷贝只是表面的上的拷贝,因此造成的结果就是,浅拷贝发生变化时,会影响真正的值,(比如一些值传递,以int数据为例,相当于你改变了当前的int数据),而深拷贝就不会,你变你的,不会改变真正的值(比如引用传递,相当于你不指向当前的对象,指向了其他对象)。

Java字符串拼接方式

① 直接用+ ,这个会不断生成新的字符串,而不是在原来的基础上直接加,生成新的字符串就需要重新申请内存,当连续相加的字符串很多时(a+b+c+d+e+f+...),效率低下就是必然的了。底层是将String转成了StringBuilder后,使用其append方法进行处理的。那么也就是说,Java中的+对字符串的拼接,其实现原理是使用StringBuilder.append

② 使用Stringconcat方法,该方法中使用Arrays.copyOf 创建一个新的字符数组 buf 并将当前字符串 value数组的值拷贝到 buf中,buf 长度 = 当前字符串长度 + 拼接字符串长度。之后调用 getChars 方法使用System.arraycopy将拼接字符串的值也拷贝到 buf 数组,最后用 buf 作为构造参数 new 一个新的String对象返回。效率稍高于直接使用 +。

③ 使用StringBuilderStringBuffer,两者的 append方法都继承自 AbstractStringBuilder,该方法首先使用Arrays.copyOf确定新的字符数组容量,再调用 getChars 方法使用System.arraycopy将新的值追加到数组中。StringBuilder是 JDK5 引入的,效率高但线程不安全。StringBuffer 使用 synchronized 保证线程安全。

三大特性是封装,继承和多态

类之间的关系

从英文上理解最直观

  • 继承 is-a
  • 实现 can-do
  • 组合 contains-a (请注意组合与聚合区别,从中文上理解也是不同的,有一个女朋友就不能用包含)
  • 聚合 has-a
  • 依赖 depends-a
  • 关联 平等的使用关系:links-a

反射的优缺点

反射提高了Java程序的灵活性和扩展性,降低耦合性,提高自适应能力。它允许程序创建和控制任何类的对象,无需提前硬编码目标类;反射是其它一些常用语言,如C、C++、Fortran 或者Pascal等都不具备的。

缺点:
性能问题:使用反射基本上是一种解释操作,用于字段和方法接入时要远慢于直接代码。因此Java反射机制主要应用在对灵活性和扩展性要求很高的系统框架上,普通程序不建议使用。
安全问题:由于反射允许代码执行一些在正常情况下不被允许的操作(比如访问私有的属性和方法),绕过了源代码的技术,反射可以忽略权限检查,因此可能会破坏封装性而导致安全问题,也会带来维护问题。

值传递和引用传递

按值调用(call by value)表示方法接收的是调用者提供的值,而按引用调用(call by reference)表示方法接收的是调用者提供的变量地址。一个方法可以修改传递引用所对应的变量值,而不能修改传递值调用所对应的变量值。

Java 程序设计语言总是采用按值调用。也就是说,方法得到的是所有参数值的一个拷贝,也就是说,方法不能修改传递给它的任何参数变量的内容。

强调:Java是按值调用,但是大部分情况下,方法中修改对象的时候,可以修改成功看起来好像是引用传递一样,实际上并不是,这里的原理暂时不懂。(可能是传递拷贝的一份地址)

ArrayList、Vector、synchronizedList和CopyOnWriteArrayList

ArrayList是用于替代Vector的,Vector是线程安全的容器。因为它几乎在每个方法声明处都加了synchronized关键字来使容器安全。如果使用Collections.synchronizedList(new ArrayList())来使ArrayList变成是线程安全的话,也是几乎都是每个方法都加上synchronized关键字的,只不过它不是加在方法的声明处,而是方法的内部。

一般来说,我们会认为:CopyOnWriteArrayList是同步List的替代品,CopyOnWriteArraySet是同步Set的替代品。

无论是Hashtable-->ConcurrentHashMap,还是说Vector-->CopyOnWriteArrayList。JUC下支持并发的容器与老一代的线程安全类相比,总结起来就是加锁粒度的问题

对于CopyOnWriteArrayList

  • 在修改时,复制出一个新数组,修改的操作在新数组中完成,最后将新数组交由array变量指向。
  • 写加锁,读不加锁
 public boolean add(E e) {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        Object[] elements = getArray();
        int len = elements.length;
        Object[] newElements = Arrays.copyOf(elements, len + 1);
        newElements[len] = e;
        setArray(newElements);
        return true;
    } finally {
        lock.unlock();
    }
}

final void setArray(Object[] a) {
    array = a;
}

优点
CopyOnWriteArrayList 在写操作的同时允许读操作,大大提高了读操作的性能,因此很适合读多写少的应用场景

缺点:

  • 内存占用:如果CopyOnWriteArrayList经常要增删改里面的数据,经常要执行add()、set()、remove()的话,那是比较耗费内存的。因为我们知道每次add()、set()、remove()这些增删改操作都要复制一个数组出来。
  • 数据一致性:CopyOnWrite容器只能保证数据的最终一致性,不能保证数据的实时一致性。比如线程A在迭代CopyOnWriteArrayList容器的数据。线程B在线程A迭代的间隙中将CopyOnWriteArrayList部分的数据修改了(已经调用setArray()了)。但是线程A迭代出来的是原有的数据。

备注:与之呼应的是 Hashtable、HashMap、synchronizedmap和ConcurrentHashMap