JAVA
深拷贝
序列化与反序列化
fastjson从Object转成json,然后转回object,本质上是反射
头插法代码
e.next = newTable[i];
newTable[i] = e;
头插法。
这个是hashmao1.7的源码,扩容时候的
把e的next指向newTable[I]也就是链表的首节点,然后在把e作为链表的首节点
1.8后采用尾插法
要使用Java SPI(Service Provider Interface 为了服务提供者提供的接口),需要遵循如下约定:
- 1、当服务提供者提供了接口的一种具体实现后,在jar包的META-INF/services目录下创建一个以“接口全限定名”为命名的文件,内容为实现类的全限定名;
- 2、接口实现类所在的jar包放在主程序的classpath中;
- 3、主程序通过java.util.ServiceLoder动态装载实现模块,它通过扫描META-INF/services目录下的配置文件找到实现类的全限定名,把类加载到JVM;
- 4、SPI的实现类必须携带一个不带参数的构造方法;
public class SPIMain {
public static void main(String[] args) {
ServiceLoader shouts = ServiceLoader.load(IShout.class);
for (IShout s : shouts) {
s.shout();
}
}
}
- 数据库驱动加载接口实现类的加载
JDBC加载不同类型数据库的驱动 - 日志 log4j,其是一个接口
优点:
使用Java SPI机制的优势是实现解耦,使得第三方服务模块的装配控制的逻辑与调用者的业务代码分离,而不是耦合在一起。应用程序可以根据实际业务情况启用框架扩展或替换框架组件。
缺点:
- 虽然ServiceLoader也算是使用的延迟加载,但是基本只能通过遍历全部获取,也就是接口的实现类全部加载并实例化一遍。如果你并不想用某些实现类,它也被加载并实例化了,这就造成了浪费。获取某个实现类的方式不够灵活,只能通过Iterator形式获取,不能根据某个参数来获取对应的实现类。
- 多个并发多线程使用ServiceLoader类的实例是不安全的。
直接new 接口(父类) 重写 方法 ,即可实例化,
即Invoker是一个接口或者一个抽象类或者类,里面重写destroy方法
Invoker last = new Invoker() {
@Override
public void destroy() {
invoker.destroy();
}
};
最典型的就是
Thread t = new Thread()
{
public void run()
{
System.out.println(“Child Thread”);
}
};
不允许外部类使用static ,因为定义类,就是要访问,类里的方法,
访问类里的方法,就要加载类,类是静态怎么加载
TreeSet和HashSet不重复的,hashset 通过hashcode和equal
final 定义的引用类型,里面的属性可以改,但是指向不能改。即不可变性
外部类名.this.属性
引用外部类对象的属性
Hashset 底层容器是hashmap
Treeset:加入treeset容器的元素必须实现compare接口,
Treeset底层使用的容器就是treemap
// 默认构造函数。使用该构造函数,TreeMap中的元素按照自然排序进行排列。排序指的是按照key来排序
TreeMap()
// 创建的TreeMap包含Map
TreeMap(Map<? extends K, ? extends V> copyFrom)
// 指定Tree的比较器
TreeMap(Comparator<? super K> comparator)
Map<String,String> map = new TreeMap<>(new Comparator(){
public int compare(String o1,String o2){
return o2.compareTo(o1); //用正负表示大小值
}
});
// 创建的TreeSet包含copyFrom
TreeMap(SortedMap<K, ? extends V> copyFrom)
Tree底层都是红黑树
类加final,私有化构造函数,在构造函数中抛出异常 ,这个类就禁止实例化了
class ClassLoader {
// 加载入口,定义了双亲委派规则
Class loadClass(String name) {
// 是否已经加载了
Class t = this.findFromLoaded(name);
if(t == null) {
// 交给双亲
t = this.parent.loadClass(name)
}
if(t == null) {
// 双亲都不行,只能靠自己了
t = this.findClass(name);
}
return t;
}
// 交给子类自己去实现
Class findClass(String name) {
throw ClassNotFoundException();
}
// 组装Class对象
Class defineClass(byte[] code, String name) {
return buildClassFromCode(code, name);
}
}
class CustomClassLoader extends ClassLoader {
Class findClass(String name) {
// 寻找字节码
byte[] code = findCodeFromSomewhere(name);
// 组装Class对象
return this.defineClass(code, name);
}
}
自定义类加载器不易破坏双亲委派规则,不要轻易覆盖loadClass方法,通常使用defineClass方法。否则可能会导致自定义加载器无法加载内置的核心类库。在使用自定义加载器时,要明确好它的父加载器是谁,将父加载器通过子类的构造器传入。如果父类加载器是 null,那就表示父加载器是「根加载器」。