HashMap缺点:
线程不安全,多线程情况下,同时进行resize操作可能引起死循环,还有,两个线程同时进行 put操作时,也可能导致put失败。
HashMap的key和value都允许放入null值。
HashTable缺点:
HashTable采用sychronized来保证线程安全,但是在线程激烈竞争情况下,效率非常低。一个线程访问HashTable的同步方法时,其他线程接着访问的话,可能会进入阻塞或者轮询状态。比如线程1使用put方法添加元素,那么线程2既不能使用put添加元素,也不能使用get获取元素,导致效率降低。
HashTable不允许null值。
ConcurrentHashMap
ConcurrentHashMap采用 “分段锁”,在容器中有多把锁,每个锁锁住一段数据,这样在多线程访问不同段的数据时,就不会存在锁竞争了,提高并发效率。
原理:
ConcurrentHashMap维护一个Segment数组,一个Segment维护一个HashEntry数组。
对于ConcurrentHashMap的数据插入,这里需要进行两次Hash计算去定位数据的存储位置。
Segment实现了ReenTrantLock,也就带有锁的功能,当执行put操作时,会进行第一次key的hash操作,来定位到segment的位置,如果该segment还没有初始化则直接放入,
否则进行第二次hash值的计算,找到对应的HashEntry位置。在将数据插入到HashEntry链表时(链表的尾端),会通过继承的ReenTrantLock的tryLock()方法去尝试获取锁,
如果获取成功就直接插入到该位置,如果已经有线程获取该位置的锁了,那当前线程就会以自旋的方式去继续的调用tryLock()方法去获取锁,超过指定次数就挂起,等待被唤醒。
1个或者2个,java中有String常量池,每当创建一个新对象时,就先去常量池中找是否有这个字符串,有的话就不在常量池中创建新对象,没有的话就创建一个对象,另外一个对象是在堆上创建。
3.1 Java序列化是指将java对象转化为字节序列的过程,而java反序列化是把字节序列恢复为对象的过程。
序列化的作用:
在传递和保存对象的时候,保证对象的完整性。序列化是把对象转化为有序字节流,以便在网络上传输或者保存在本地文件中。
反序列化:
客户端从文件或者网络上获得序列化后的对象字节流后,根据字节流中所保存的对象状态以及描述信息,通过反序列化重建对象。
3.2 序列化与反序列化的作用以及好处:
适用于当两个java进程进行通信时
- (1)实现了数据的持久化,通过序列化可以把数据持久的放在硬盘上(通常存放在文件里)
- (2)利用序列化进行远程通信,即通过网络传输传送对象。
3.3 与序列化相关的API:
(1)ObjectOutputStream:表示对象输出流
它的writeObject可以对指定参数的obj对象进行序列化,把得到的字节序列写到一个目标输出流中。
(2)ObjectInputStream:表示对象输入流
它的readObject方法可以源输入流中读取字节序列,再把它们反序列化成为一个对象,并将其返回。
浅拷贝 是指在拷贝对象时,对于基本数据类型的变量会重新复制一份,因此是两份不同的数据,修改其中一份,另一份不会被影响。
而对于引用类型的变量只是对引用进行拷贝,这两个引用都指向同一个地址,因此修改其中一个另一个也会改变。
深拷贝 则是对对象以及该对象关联的内容,都进行拷贝,修改其中一个,另一个不会改变。
举例:假如B复制了A,当修改了A之后,看B是否变化,如果B变化了,则说明是浅拷贝,如果B没变,则说明是深拷贝。
深拷贝可以采用的方法:
1.采用 JSON.stringify 和 JSON.parse
function deepClone(obj){
let _obj = JSON.stringify(obj),
objClone = JSON.parse(_obj);
return objClone
}
2.对对象进行深拷贝
(1)每一个对象都实现Cloneable接口,并重写clone()方法,最后在最顶层的类重写的clone()方法中调用所有的clone()方法即可实现深拷贝。
(2)通过序列化实现深拷贝,每个对象都要实现Serializable接口。
Age a=new Age(20);
Student stu1=new Student("张三",a,175);
//通过序列化方法实现深拷贝
ByteArrayOutputStream bos=new ByteArrayOutputStream();
ObjectOutputStream oos=new ObjectOutputStream(bos);
oos.writeObject(stu1);
oos.flush();
ObjectInputStream ois=new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
Student stu2=(Student)ois.readObject();
5.1 反射的原理:
Java语言编译后会形成一个.Class文件,反射就是通过字节码文件找到某一个类,类中的方法以及属性等。
5.2 反射的实现主要借助四个类:
Class:类的对象
Constructor:类的构造方法
Field:类中的属性对象
Method:类中的方法对象
5.3 使用反射机制的步骤:
1.导入java.lang.reflect包
2.遵循三个步骤:
5.4 获取Class对象的方法:
(1)根据对象来获取
Class c = 对象名.getClass();
(2)直接写类路径全称
Class c = java.awt.Button.class;
(3)包装类:Class c = Integer.TYPE;
(4)Class.forName
// 要求JVM查找并加载指定的类,这时候,
// 会把静态属性,方法以及静态代码块都加载到内存中
Class t = Class.forName("com.TestOne");
// 产生这个Class对象的一个实例,调用该类的无参构造方法,作用等同于new TestOne();
t.newInstance();
5.5 Class对象可以调用的方法:
方法名 | 介绍 |
---|---|
getName() | 获得类的完整名字。 |
getFields() | 获得类的public类型的属性。 |
getDeclaredFields() | 获得类的所有属性。包括private 声明的和继承类 |
getMethods() | 获得类的public类型的方法。 |
getDeclaredMethods() | 获得类的所有方法。包括private 声明的和继承类 |
getMethod(String name, Class[] parameterTypes) | 获得类的特定方法,name参数指定方法的名字,parameterTypes 参数指定方法的参数类型。 |
getConstructors() | 获得类的public类型的构造方法。 |
getConstructor(Class[] parameterTypes) | 获得类的特定构造方法,parameterTypes 参数指定构造方法的参数类型。 |
newInstance() | 通过类的不带参数的构造方法创建这个类的一个对象。 |
5.6 Class能实现的功能:
1.判断对象属于哪个类
2.获取类信息
3.构建对象
4.动态执行方法,通过invoke调用
Class class1 = Person.class;
Method work = class1.getDeclaredMethod("work");
Person person = new Person();
work.invoke(person);
5.动态操作属性
Class class1 = Person.class;
Person person = new Person();
Field field = class1.getDeclaredField("age");
//age默认值是18
field.set(person,22);
System.out.println(person.age);
6.动态代理
5.7 newInstance 和 new的区别:
前者是使用类加载机制,后者是创建一个新类。
newInstance的使用更加局限,只能调用无参构造,但是使用new可以调用任何public构造。
从JVM角度看,使用new关键字的时候,这个类可以没有被加载,但是使用newInstance的时候,就必须保证这个类已经加载了,这个类已经连接了。