深拷贝和浅拷贝
浅拷贝(shallowCopy)只是增加了一个指针指向已存在的内存地址,
深拷贝(deepCopy)是增加了一个指针并且申请了一个新的内存,使这个增加的指针指向这个新的内存。
也就是说,浅拷贝是直指要害,而深拷贝只是表面的上的拷贝,因此造成的结果就是,浅拷贝发生变化时,会影响真正的值,(比如一些值传递,以int数据为例,相当于你改变了当前的int数据),而深拷贝就不会,你变你的,不会改变真正的值(比如引用传递,相当于你不指向当前的对象,指向了其他对象)。
Java字符串拼接方式
① 直接用+
,这个会不断生成新的字符串,而不是在原来的基础上直接加,生成新的字符串就需要重新申请内存,当连续相加的字符串很多时(a+b+c+d+e+f+...)
,效率低下就是必然的了。底层是将String
转成了StringBuilder
后,使用其append
方法进行处理的。那么也就是说,Java
中的+
对字符串的拼接,其实现原理是使用StringBuilder.append
。
② 使用String
的concat
方法,该方法中使用Arrays.copyOf
创建一个新的字符数组 buf
并将当前字符串 value
数组的值拷贝到 buf
中,buf 长度 = 当前字符串长度 + 拼接字符串长度
。之后调用 getChars
方法使用System.arraycopy
将拼接字符串的值也拷贝到 buf
数组,最后用 buf
作为构造参数 new
一个新的String
对象返回。效率稍高于直接使用 +。
③ 使用StringBuilder
或StringBuffer
,两者的 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迭代出来的是原有的数据。