接口
接口中不能定义构造器
方法全部都是抽象方法,JDK8提供方法默认实现
接口中的成员都是public的
接口中定义的成员变量实际上都是常量
一个类可以实现多个接口
抽象类
抽象类中可以定义构造器
可以有抽象方法和具体方法
抽象类中的成员可以是private、默认、protected、public
抽象类中可以定义成员变量
有抽象方法的类必须声明为抽象类,而抽象类未必有抽象方法
抽象类中可以包含静态方法
一个类只能继承一个抽象类
抽象:把现实世界的某一类物质抽象出来,抽象分为数据抽象,过程抽象(数据抽象就是比如鸟的翅膀,过程抽象就是鸟用翅膀飞。)
继承:存在层级关系,子类复用父类的代码块,继承关系存在方法重写。
封装:将一部分代码封装出来,实现代码的复用
多态:子类的对象指向父类的引用,不同的子类对同一消息做出的响应。
接口的默认实现,default方法。给程序员留的一个后悔药,可以直接在接口的默认方法里写业务的代码逻辑。
LocalDateTime、LocalDateAPI,jdk8新加的时间Api,或方法LocalDate.now()可以通过提供的API获取年月日信息,格式化简便。
lambda表达式,使java语言拥有类似python语言的函数式编程方式,代码简洁。jdk8提供的函数式接口有Function、BiFunction、Consumer、Supplier、Predicate。
Stream集合API,提供流式操作集合,对集合的分类,规整更方便,常见的API有map()转换函数,filter()过滤函数,groupingBy()分组函数,sort()排序函数等配合Collectors收集器使用。
Optional类,解决空指针异常的类,支持获取对象,判断对象是否存在,对象为null返回兜底数据等。
&:按位与操作,只有对应的两个二进制数为1时,结果才为1。
|:按位或操作,只要有一个二进制数为1时,结果就为1。
&&:短路与操作,判断短路与左侧是否正确,如果不正确则不执行短路与后面的判断。
||:短路或操作,判断短路或左侧是否正确,如果正确则不执行短路或后面的判断。
采用位运算:2<<3
将一个数左移n位,就相当于乘以2的n次方,位运算是CPU直接支持的,所以效率高。
JDK源码中HashMap的默认容量是16,采用位运算。
int DEFAULT_INITIAL_CAPACITY = 1 << 4; //16
public static void main(String[] args) {int num = 2 << 3;System.out.println(num);}
定义一个中间变量c,但是这种往往损耗性能。
private static void swap1(int a, int b) {System.out.println("a = "+a+",b = "+b);//现在a 相当于a和b的和a = a+b;//b就相当于a的值了b = a-b;//a就相当于b的值了a = a-b;System.out.println("a = "+a+",b = "+b);}
private static void swap2(int a, int b) {System.out.println("a = "+a+",b = "+b);//a = a^ba = a^b;//b = b^a^bb = b^a;//a = a^b^aa = a^b;System.out.println("a = "+a+",b = "+b);}
==在比较基本数据类型的时候,比较的是数值,比较引用数据类型的时候,比较的是内存地址,Object类下的equals比较的是对象的地址,String类中重写了equals方法,比较的是字符串的数值。
public boolean test(){try {return false;}finally {return true;}}
在执行try、catch中的return之前⼀定会执行finally中的代码(如果finally存在),如果finally中有return语句,就会直接执行finally中的return方法,所以finally中的return语句⼀定会被执行的。
JDK1.7之后处理异常块,可实现自动关闭资源操作,try()里面定义多个资源,执行完进行关闭。
如果常量池中存在,则直接new一个对象。如果常量池不存在,则在常量池中创建一个对象,也在堆中创建一个对象。
三者都是被final修饰的类。
本质上都是char[]字符数组的实现。
String、StringBuffer、StringBuilder中,String是不可变的对象,其他两个是可变的。
使用场景
操作少量的数据用String,但是常改动内容且操作数据多情况下最好不要用String,因为每次生成中间对象性能会降低。
单线程下操作大量的字符串用StringBuilder,虽然线程不安全但是不影响。
多线程下操作大量字符串,且需要保证线程安全,则用StringBuffer
CopyOnWriteArrayList是JUC下提供的一个类,在执行修改操作时,会拷贝一份新的数组进行操作(add、set、remove),代价十分昂贵,在执行完修改后会将原来的集合的引用指向新的集合来完成修改的操作,源码里用ReentrantLock可重入锁来保证不会有多个线程同时拷贝一份数组。
CopyOnWriteArrayList的设计思想读写分离+最终一致。
缺点:内存占用问题,写时复制机制,内存里面会同时驻扎两个对象的内存,旧的对象和新写入的对象,如果对象大则容易发生Yong GC和Full GC。
注意:JDK1.7之前ArrayList默认大小是10,JDK1.7之后是0。
未指定集合容器大小,默认是0,若已经指定大小则集合大小为指定的,当集合第一次添加元素的时候,集合大小扩容为10,ArrayList的元素个数大于其容量,扩容的大小=原始大小+原始大小/2。
HashMap:底层是哈希表实现,非线程安全的,默认容量是16、允许有空的键和值,实现的是Map接口。
HashTable:基于哈希表实现,线程安全的(加了synchronized),默认容量是11,不允许有null的键和值,继承自 Dictionary 类。
hashcode():顶级类Object里面的方法,所有的类都是继承自Object,返回一个int类型的数,根据一定的hash规则,映射成一个数组,即散列值。
equals():顶级类Object类的方法,返回一个boolean类型,根据自定义的匹配规则,用于匹配两个对象是否一样。
判断一个对象是否相等,先判断hashcode,当hashcode不同时,返回两个对象不同,当hashcode相同时,就进行equals比较。
HashMap:底层实现是散列桶(数组+链表)jdk1.8之后,当链表长度大于8的时候转换成红黑树,可以实现快速的存储和检索,但是确实包含无序的元素,适用于map中插入删除和定位元素。
TreeMap:使用存储结构是一个平衡二叉树->红黑树,可以自定义排序规则,要实现Comparator接口,能便捷的实现内部元素的各种排序,但是一般性能比HashMap差,适用于按照自然排序或自定义规则的场景。
Set的核心就是不保存重复的元素,存储一组唯一的对象。
Set的每一种实现都是对应Map里的一种封装,HashSet对应的就是HashMap,TreeSet对应的就是TreeMap。
Set的集合对象作为Map的Key,在使用一个Object常量作为value。
HashMap底层是数组+链表+红黑树,红黑树是JDK1.8之后才有的,当链表长度大于8的时候,链表会转换成红黑树。
数组中的每一项都是一个链表,即数组和链表的结合体。
jdk1.8之后是一个Node
hash碰撞的意思就是不同的key计算得到相同的Hash值。需要放到同个数组的桶内。
常见的解决办法:链表法、开发地址法、再哈希法等。
HashMap采用的就是链表法。
数组Node
链表的作用是为了解决Hash冲突,将Hash值一样的对象存放在一个链表中对应的槽位上。
红黑树是JDK8替换节点超过8个的时候的链表用的,主要是提升查询性能。
通过hash碰撞,让HashMap不断产生碰撞,那么相同的key的位置的链表就会不断的增长,当对这个HashMap的响应位置进行查询的时候,就会遍历这个超大的链表,性能会下降,所以改用红黑树。
使用红黑树只要是为了提升查询的速度,红黑树是平衡二叉树的一种,插入数据后会通过左旋、右旋、变色等操作来保持平衡,解决单链表查询深度的问题。
二叉树在查找的时候会变成一个线性结构,和原来的链表存在一样深度遍历的问题,所以不是用二叉树。
在数据量少的时候操作数据,遍历线性表比红黑树所消耗的资源少,前期采用线性表,等到一定数量之后才会变成红黑树。
JDK8之前,ConcurrentHashMap使用锁分段技术,将数据分成⼀段段存储,每个数据段配置⼀把锁,即segment类,这个类继承ReentrantLock来保证线程安全。
JKD8的版本取消Segment这个分段锁数据结构,底层也是使⽤Node数组+链表+红⿊树,从而实现对每⼀段数据就行加锁,也减少了并发冲突的概率,CAS(读)+Synchronized(写)。
先通过map.keySet()获取到键,然后根据键获取到值。
通过Map.Entry(String,String)获取,然后使用entry.getKey()获取到键,然后通过entry.getValue()获取到值。
使用Iterator迭代器遍历Map。
Map.values()获取所有的value,进行遍历。