温故而知新-Java 提升篇

2017-08-15  本文已影响0人  HansenGuan

Overriding(重写) & Overloading(重载)


强引用、弱引用,[链接1链接2]

finalize()函数是在JVM回收内存时执行的,但JVM并不保证在回收内存时一定会调用finalize()。


final, finally, finalize


深拷贝(deep clone),浅拷贝(shallow clone)

结论:

  1. 浅克隆:基本类型是可以被克隆的,但引用类型只是copy地址,并没有copy这个地址指向的对象的值,这使得两个地址指向同一值,修改其中一个,当然另一个也就变了.浅克隆只适合克隆基本类型,对于引用类型就不能实现克隆了.
  • 通过implements Cloneable 重写 clone方法实现

  1. 可以用序列化与反序列化实现深克隆(deep copy)
  • 通过 implements Serializable 实现

当克隆的对象只有基本类型,不含引用类型时,可以用浅克隆实现.
当克隆的对象含有引用类型时,必须使用深克隆实现.

- java提供一种叫浅拷贝(shallow copy)的默认方式实现clone,创建好对象的副本后然后通过赋值拷贝内容,意味着如果你的类包含引用类型,那么原始对象和克隆都将指向相同的引用内容。发生在可变的字段上任何改变将反应到他们所引用的共同内容上。为了避免这种情况,需要对引用的内容进行深度克隆。

comparable接口与comparator

结论:
“集合框架” 中有两种比较接口: Comparable 接口和 Comparator 接口:

  • Comparable 是通用的接口,用户可以实现它来完成自己特定的比较
  • Comparator 可以看成一种算法的实现,在需要容器集合实现比较功能的时候,来指定这个比较器,这可以看成一种设计模式,将算法和数据分离。

hashcode & equals [链接]


可变类与不可变类的区别


字符串常量池 ,深入解析String#intern

Q: String s = new String("abc")这个语句创建了几个对象的题目?(考察字符串对象的常量池)
A: 上述的语句中是创建了2个对象,第一个对象是"abc"字符串存储在常量池中,第二个对象在JAVA Heap中的 String 对象。


Java 泛型 [泛型详解]


String, StringBuffer, StringBuilder的区别

1. 如果要操作少量的数据,用String ;单线程操作大量数据,用StringBuilder ;多线程操作大量数据,用StringBuffer。
2.StringBuffer 或 StringBuilder 时应尽可能指定它们的容量


类的实例化顺序,比如父类静态数据,构造函数,字段,子类静态数据;构造函数,字段的执行顺序 [链接]


本地方法栈和虚拟机栈 、堆

注意事项

1. 虚拟机栈、本地方法栈JVM规范所规定的概念上的东西,并不是说具体的JVM实现真的要给每个Java线程开两个独立的栈。可能只使用一个栈,融合以上两个栈的概念
2. 堆不是数据结构意义上的堆(Heap,一种有序的树),而是动态分配意义上的堆---用于管理动态生命周期的内存区域
3. JVM堆被同一个JVM实例中的所有线程共享,通常由自动内存管理机制管理(“垃圾回收”,GC,garbage collection)


Java内存模型链接


为什么函数调用要用栈实现?【链接


反射和动态代理

> Proxy类的静态方法`newProxyInstance`对上面具体步骤的后三步做了封装,简化了动态代理对象的获取过程
  - 动态代理的优点与不足
    - 优点:动态代理与静态代理相比较,最大的好处是接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理(InvocationHandler.invoke)
    - 缺点:仅支持 interface 代理,__无法实现对 class 的动态代理__(原因是多继承在 Java 中本质上就行不通)

总结:
1. 为了解决使用静态代理会造成系统结构臃肿的问题,在运行状态中,需要代理的地方,根据Subject 和RealSubject,动态地创建一个Proxy,用完之后,就会销毁,这样就可以避免了Proxy 角色的class在系统中冗杂的问题了。(动态代理的优势)


2.为了构造出具有通用性和简单性的代理类,可以将所有的触发真实角色动作交给一个触发的管理器,让这个管理器统一地管理触发。这种管理器就是Invocation Handler。(Invocation Handler角色的由来)


3.动态代理工作的基本模式就是将自己的方法功能的实现交给 InvocationHandler角色,外界对Proxy角色中的每一个方法的调用,Proxy角色都会交给InvocationHandler来处理,而InvocationHandler则调用具体对象角色的方法。




4.约定Proxy 和RealSubject可以实现相同的功能,有两种方式:
  • 定义一个功能接口,然后让Proxy 和RealSubject来实现这个接口。(JDK创建动态代理用的这种思路)
  • 通过继承。因为如果Proxy 继承自RealSubject,这样Proxy则拥有了RealSubject的功能,Proxy还可以通过重写RealSubject中的方法,来实现多态。(cglib使用这种思路)

5.两种方式的比较:

  • JDK:接口特点可代理多个接口,无法代理未实现接口的类
  • Cglib:继承特点可直接代理类无需实现接口,无法一次代理多个类

反射中Class.forName 和 ClassLoader的区别

数据库链接为什么使用Class.forName(className)?
JDBC Driver源码

static{
 try{
   java.sql.DriverManager.registerDriver(new Driver());
 } catch(SQLException e){
   throw new RuntimeException("Can't register driver!");
 }
}

因此使用Class.forName(classname)才能在反射回去类的时候执行static块


Iterator设计思想

如何实现两种容器的可替换性(底层容器的实现随意改变不影响用户使用)?

统一接口、面向接口编程

以ArrayList和LinkedList为例

ArrayList的继承图:



LinkedList的继承图:


ArrayList和LinkedList都实现了Collection接口、Iterator接口

 //通过实现统一Collection接口,面向接口编程,实现底层容器切换不影响使用,达到对用户透明的效果
 Collection<String> collection = new ArrayList<String>();
 //Collection<String> collection = new LinkedList<String>();
 collection.add("hello");
 collection.add("java");
 collection.remove("hello");

Iterator 基于以上思想实现 Collection 所有实现类的统一遍历方式,不需关心具体实现数据结构(数组,链表),具体的遍历方式由容器自己根据自身特点实现

1. 实现Iterable接口的类都可以使用“foreach”操作

/** Implementing this interface allows an object to be the target of
*  the "foreach" statement.
* @since 1.5
*/
public interface Iterable<T> {
  /**
   * Returns an iterator over a set of elements of type T.
   *
   * @return an Iterator.
   */
  Iterator<T> iterator();

  ····
}

2. 统一 Iterator 接口

public interface Iterator<E> {
  // 是否还有元素
  boolean hasNext();
  // 下一个元素
  E next();
  // 将迭代器返回的元素删除
  void remove();

  ···
}

3. ArrayList的Iterator的具体实现


通过继承 Iterable 接口,定义 Iterator 规范 迭代方法,Collection 的所有实现类实现自己的 Iterator 接口,实现统一的、对用户透明的迭代方法

使用示例:

Iterator<String> iterator = collection.iterator();
while (iterator.hasNext()) {
     System. out.println(iterator.next());
}

AbstractList 抽象类中 modCount 变量分析

总结:

这个数统计list 发生结构性改变的次数
这个变量被 iterator 和 listIterator 方法返回的 iterator 实现使用,如果这个变量发生非预期的改变,iterator 将在 next , remove , previous , set 或者 add操作时抛出 ConcurrentModificationException 异常,提供了一个快速响应失败的机制(fail-fast)而不是在遍历时进行不确定的行为。

private class Itr implements Iterator<E>{
    int expectedModCount = modCount;  //遍历前获取当前list 的 修改数
    ···
    //实现Iterator接口定义的方法
    ···
    final void checkForComodification() {
        //当Iterator中的存储的 修改数 与 list 中的修改数不一致时,说明遍历时,list结构发生了改变
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
    }

}

当多线程操作同一容器的时候,一个线程通过Iterator遍历容器的同时,另一个线程修改了改容器的内容(add、remove等操作),Iterator及时抛出异常,防止发生 不确定性 的 行为。


上一篇 下一篇

猜你喜欢

热点阅读