- 抽象,封装,继承,多态
- 深拷贝:对象A中有对象B的引用,浅拷贝A对象,会将对象A的值赋给对象C,且还是B的引用;深拷贝A对象,会将A对象的值赋给对象C,且将对象B的值赋值给对象D,此时C中就是D的引用而不是B的引用。实现方法:
- 构造函数
- 重载clone()方法
- 序列化
- 强引用,软引用,弱引用,虚引用
- 强引用:new出来的对象,只要引用存在就不会被垃圾回收
- 软引用:内存不足时回收,基于SoftReference创建:
SoftReference<String> softReference = new SoftReference<String>("abc");
- 弱引用:每次执行垃圾回收算法,发现只存在软引用的对象就清除,基于WeakReference创建:
WeakReference<String> weakReference = new WeakReference<String>("abc");
- 虚引用:虚引用不会决定对象的生命周期,如果一个对象仅持有需虚引用,则他任何时候都可能被垃圾回收,且不能通过虚引用去获得对象。虚引用的唯一目的就是,执行gc时能收到一个系统通知。基于PhantomReference实现,且必须和引用队列ReferenceQueue联用。
ReferenceQueue<String> queue = new ReferenceQueue<String>(); PhantomReference<String> pr = new PhantomReference<String>("abc", queue);
- 设计模式
- 六大原则:
- 开闭原则:对扩展开发,对修改关闭
- 里氏替换原则:子类可以替换父类
- 依赖倒装原则:多使用接口
- 接口隔离原则:使用多个隔离的接口,比使用单个接口要好
- 迪米特法则,最少知道原则:一个实体应当尽量少地与其他实体之间发生相互作用,模块化
- 合成复用原则:尽量使用合成/聚合的方式,而不是使用继承
- 单例模式(Singleton):最简单的设计模式之一,一个类只有唯一一个类供其他对象使用。基本方式为,其构造函数私有化,定义这个类的私有静态对象,通过方法调用这个静态对象
- 懒汉模式:对象延迟加载,申明对象后不实例化,调用方法的时候再实例化,线程不安全
public class Single { private static Single single = null;//静态私用成员,没有初始化 private Single(){} public static synchronized Single getsingle(){ if(single == null){ single = new Single(); } return single; } }
- 饿汉模式:对象预先加载,申明对象的同时就实例化,线程安全
public class Single { private static Single single = new Single(); private Single(){} public static Single getsingle(){ return single; } }
- 懒汉模式:对象延迟加载,申明对象后不实例化,调用方法的时候再实例化,线程不安全
- 工厂模式(简单工厂模式)(Factory):一种创建对象的模式,使用一个专门的类(工厂类)来实现实例化的过程
- 基本方式是:
- 创建一个接口
- 定义多个此接口的实现类
- 创捷工厂类,此工厂类会根据用户的需求创建对应的实现类的对象。
- 抽象工厂模式:使用一个专门的类(抽象工厂类)来实现工厂实例化的过程,此工厂能实现多个产品的实例化,每个产品也是一个工厂。
- 实现过程:
- 创建多个工厂
- 创建包含多个工厂实例化的抽象类
- 定义多个抽象类的子类,实现具体工厂的实现方法
- 创建一个工厂类,使用户更加需求,选择不同的工厂
- 实现过程:
- 基本方式是:
- 适配器模式(Adapter):为两个不相融的接口提供桥梁,一种结构形模式。实现过程:在一个接口A的实现类A1中定义另一个接口B实现类的对象,再使用这个A的另一个实现类A2调用类实现类A1。用户只需调用A2就可以使用两个接口的内容。
- 装饰器模式(Decorator):向一个现有的对象添加功能,一种结构形模式。实现过程:创建抽象类,调用需要扩展功能的类的
- 代理模式(Proxy):用一个类代表另外一个类的功能,一种构造形模式。实现过程:创建一个接口,创建多个他的实现类,调用的时候根据情况调用他的实现类
- 观察者模式(Observer):一对多关系,当一个对象被修改会通知其他依赖他的对象
- 策略模式(Strategy):一个类的行为或其算法可以在运行时更改
- 六大原则:
- 集合(Collection, Map):
- Collection:
- List(有序,有索引,元素可重复):
- ArrayList:线程不安全,支持序列化,底层是数组,能够动态扩容(将数组复制到一个1.5倍长的新数组中,初始默认长为10)
- LinkedList:底层是双向链表,线程不安全,支持栈和队列
- Vector:线程安全(synchronized),动态扩容的长度是2倍。
- CopyOnWriteArrayList:线程安全(synchronized修饰代码块,读取不加锁),修改添加,将原数组拷贝,再拷贝后的数组进行修改,修改后替换原数组,这个阶段加锁。
- Set(无序,无索引,元素不可重复):
- HashSet:底层是HashMap,将需要存放的元素作为HashMap里面的键,所有HashMap的值是一个静态的Object类的对象。
- LinkedHashSet:HashSet的之类,底层是LinkedHashMap,与HashSet的最大区别就是记录了插入顺序(不能根据下标获取,能保证迭代输出的顺序)
- TreeSet:底层是TreeMap,存储与HashSet类似
- HashSet:底层是HashMap,将需要存放的元素作为HashMap里面的键,所有HashMap的值是一个静态的Object类的对象。
- List(有序,有索引,元素可重复):
- Map:
- HashMap:
- 键可以是null,值也可以是null,如果键是null,则他对应的hashCode为0,计算hashCode要用native关键字,调用本地方法接口。
- JDK1.8之前,HashMap采用位桶+链表存储数据,而JDK1.8时,HashMap采用数组+链表+红黑树实现,当链表长度超过阈值8时,将链表转换为红黑树。少于6时转换为链表。
- HashMap默认初始容量为16,负载因子为0.75(负载因子越大,发生碰撞的概率就越大,时间复杂度就越高),当元素数量大小 > 初始容量 * 负载因子,HashMap就会进行扩容。容量上限为2^30。
- 扩容时,创建一个新的对象数组,大小为原来的两倍,将之前元素放到新的数组中去,不要重新计算hash值,只需通过hash值响应最高位(16 -> 32, 最高位为第5位)的是0,还是1去判断,元素是在原位置,还是在原位置加之前的数组的长度。
- 线程不安全
- 多线程同时进行put,若hash冲突发生在莫一个没有值得下标下。多个线程会认为此下标没有数据,直接赋值,就会发生覆盖的情况,产生数据冲突
- JDK1.7的时候,扩容可能会照成死循环,采用头插法。
- LinkedHashMap:在HashMap的基础上使用双向链表保存数据的插入删除顺序。
- HashTable:与HashMap基本类似
- 值不能是null,键也不能为空,因为Hashtable直接使用key值计算hash值,调用本地方法接口,所有key不能为空。
- 线程安全,使用synchronized修饰
- 初始容器大小为11
- 扩容长度为2倍 + 1,计算下标的方式跟hashmap不一样,采用取模(数组长)运算,以此减少hash冲突,所以数组长度最好为素数或奇数。所以扩容时2倍加1。
- TreeMap:
- 红黑树,
- 三个指针(父节点,左节点,右节点)
- 键不能为null,值可以为null。
- ConcurrentMap:
- ConcurrentHashMap
- 线程安全;JDK1.7的时候采用segment分段锁,每个分段都是一个数组。JDK1.8,存储结构为:数组+链表+红黑树,存在hash冲突就使用synchronized同步代码块加对象锁,锁住数组对应下标。
- 键值均不能为空
- ConcurrentHashMap
- HashMap:
- Collection:
- String,StringBuffer,StringBuilder
- String:由final修饰的类,不可继承,不可改变。当我们对String对象进行修改时,不是对原本的对象进行修改,而是新建一个修改后的对象
- StringBuilder:由final修饰,线程不安全。由字节数组存储,默认初始字节容器大小为16,扩容时,新长度 = 旧长度 * 2 + 2(+2原因,可能是因为初始长度可以为0)
- StringBuffer:与StringBuilder基本类似,但线程安全,由synchronized关键字修饰方法。
- java8特性
- lambda表达式(闭包),将函数当成方法参数,(参数列表(变量类型可省略)) -> 表达式;(参数列表) -> {表达式}
- 方法引用,对象名::方法名。类名::方法名
- 函数式接口,仅仅只有一个抽象方法的接口
- 默认方法,接口中的方法,可以通过default实现自己的默认方法。
- 日期时间API
- Stream流
- Base64编码
- equals和==的区别:==比较的是两个对象的地址是否是一样的,而equals继承自Object类,如果不对其进行重写,也是比较对象的地址,和==没有实际区别。String类就重写了equals方法,所以比较的是对象中的内容,而不是对象的地址。
- 重写equals方法还需重新hashcode方法:为了提高效率和节省空间,如果两个对象的地址相同,就不需要比较对象中的内容了,而相同对象通常情况下也不需要开辟额外的空间存放。
- 快速失败,安全失败
- 快速失败:使用迭代器遍历集合时,对集合中的内容进行增删改,就会抛出异常。原理:遍历时,使用modCount变量,集合改变,modCount就会改变,下一次遍历时,modCount改变直接终止遍历,抛出异常。
- 安全失败:采用安全失败机制的集合容器,在遍历时不是直接在集合内容上进行访问,而是先复制原有集合内容,在拷贝的集合上进行遍历。Concurrent下的容器都是安全失败。
- 内存泄漏和内存溢出:
- 内存泄漏:程序申请内存后,无法释放已申请的内存空间,例:强引用
- 内存溢出:执行多此GC后,内存还是不能满足需求
- 类锁和对象锁
- 对象锁:防止在同一个时刻多个线程访问同一个对象的synchronized块。类中非静态方法上的锁;同步代码块,以对象做锁
- 类锁:类的所有对象用同一把锁。类中静态方法;同步代码块,以类做锁。
- 泛型:参数化类型,将类型由原来的具体类型参数化,包括泛型类,泛型接口,泛型方法。ArrayList等集合类就是用的泛型,这样才可以存储不同类型的变量。