导图社区 Java基础
从0开始学Java的小伙伴这张图必须收藏!Java基础知识都在图里了。
编辑于2020-09-26 19:22:07Java基础
面向对象的特征
封装
客观事物进行抽象,有选择性的隐藏信息
private、定义类与方法
继承
单继承,可实现多个接口。继承就是子类继承父类的特征和行为,使得子类对象具有父类的非private属性和方法。
多态
三个条件:继承,重写、父类引用指向子类的对象(Animal animal = new Cat();//向上转型)
子类继承父类,重写父类的方法,当子类对象调用重写的方法时,调用的是子类的方法,而不是父类的方法,当想要调用父类中被重写的方法时,则需使用关键字super。
面向对象(设计模式)的6大原则
单一职责Single-Resposibility
提高内聚性;如果一个类承担的职责过多,那么这些职责就会相互依赖,一个职责的变化可能会影响另一个职责的履行
开放封闭Open-Closed
用抽象构建框架,用实现扩展细节
围绕变化展开,变化来临时,如果不必改动软件实体裁的源代码,就能扩充它的行为;创建抽象类或接口
里氏替换Liskov-Substituion
子类型必须能够替换掉它们的基类型;使得使用基类型模块无需修改就可扩充
子类必须实现父类的抽象方法,但不得重写(覆盖)父类的非抽象(已实现)方法。
子类中可以增加自己特有的方法。
当子类覆盖或实现父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松。
当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格。
依赖倒置Dependecy-Inversion
抽象不应依赖于细节,细节应该依赖于抽象
高层模块不应该依赖底层模块,二者都应该依赖抽象。
依赖倒置的中心思想是面向接口编程。
依赖倒置原则是基于这样的设计理念:相对于细节的多变性,抽象的东西要稳定的多。以抽象为基础搭建的架构比以细节为基础搭建的架构要稳定的多。
使用接口或抽象类的目的是指定好规范,而不涉及任何具体的操作,把展现细节的任务交给他们的实现类来完成。
接口隔离Interface-Segregation
不要在一个接口里面放很多的方法,这样会显得这个类很臃肿。接口应该尽量细化,一个接口对应一个功能模块,同时接口里面的方法应该尽可能的少,使接口更加灵活轻便
良性依赖
"不会在实际中造成危害的依赖关系,都是良性依赖。"通过分析不难发现,本原则的核心思想是"务实",很好地揭示了极限编程(Extreme Programming)中"简单设计"各"重构"的理论基础。本原则可以帮助我们抵御"面向对象设计五大原则"以及设计模式的诱惑,以免陷入过度设计(Over-engineering)的尴尬境地,带来不必要的复杂性。
迪米特法则
在类的划分上,应该创建弱耦合的类。类与类之间的耦合越弱,就越有利于实现可复用的目标。
在类的结构设计上,尽量降低类成员的访问权限。
在类的设计上,优先考虑将一个类设置成不变类。
在对其他类的引用上,将引用其他对象的次数降到最低
不暴露类的属性成员,而应该提供相应的访问器(set 和 get 方法)
谨慎使用序列化(Serializable)功能。
容器
ArrayList
随机访问效率高;插入,删除效率较低
实现了AbstractList(数组操作的实现),Cloneable(可克隆),RandomAccess(随机访问),Serializable(以流的形式通过ObjectInputStream/ObjectOutputStream来写/读)
默认容量是10
在预先能够确认元素数量的情况下,才使用ArrayList,否则不建议使用
线程不安全
LinkedList
实现了Deque接口(double ended queue(双端队列))大多数Deque接口的实现都不会限制元素的数量,但是这个队列既支持有容量限制的实现,也支持没有容量限制的实现,比如LinkedList就是有容量限制的实现,其最大的容量为Integer.MAX_VALUE
双向链表,有指向前一个元素的指针,有指向后一个元素的指针
线程不安全
Vector
CopyOnWriteArrayList
比较: ArrayList查询性能优于LinkedList LinkedList新增,删除性能优于ArrayList 最佳回答:ArrayList和LinkedList有什么区别? 1.ArrayList查询快,写数据慢;LinkedList查询慢,写数据快。面试官只能算你勉强合格。 2.ArrayList查询快是因为底层是由数组实现,通过下标定位数据快。写数据慢是因为复制数组耗时。LinkedList底层是双向链表,查询数据依次遍历慢。写数据只需修改指针引用。 3.ArrayList和LinkedList都不是线程安全的,小并发量的情况下可以使用Vector,若并发量很多,且读多写少可以考虑使用CopyOnWriteArrayList。因为CopyOnWriteArrayList底层使用ReentrantLock锁,比使用synchronized关键字的Vector能更好的处理锁竞争的问题。
HashSet
HashSet不是key value结构,仅仅是存储不重复的元素,相当于简化版的HashMap,只是包含HashMap中的key而已
非线程安全的
HashTable
HashMap
什么时候使用: 是基于Map接口的实现,存储键值对时,它可以接收null的键值,是非同步的,HashMap存储着Entry(hash, key, value, next)对象。
equals()和hashCode()的都有什么作用? 通过对key的hashCode()进行hashing,并计算下标( n-1 & hash),从而获得buckets的位置。如果产生碰撞,则利用key.equals()方法去链表或树中去查找对应的节点
你知道hash的实现吗?为什么要这样实现? 在Java 1.8的实现中,是通过hashCode()的高16位异或低16位实现的:(h = k.hashCode()) ^ (h >>> 16),主要是从速度、功效、质量来考虑的,这么做可以在bucket的n比较小的时候,也能保证考虑到高低bit都参与到hash的计算中,同时不会有太大的开销。
如果HashMap的大小超过了负载因子(load factor)定义的容量,怎么办? 如果超过了负载因子(默认0.75),则会重新resize一个原来长度两倍的HashMap,并且重新调用hash方法。
非线程安全的
HashMap和Hashtable的底层实现都是数组+链表结构实现的,这点上完全一致 1. 添加、删除、获取元素时都是先计算hash,根据hash和table.length计算index也就是table数组的下标,然后进行相应操作 2. 两者最主要的区别在于Hashtable是线程安全,而HashMap则非线程安全;Hashtable的实现方法里面都添加了synchronized关键字来确保线程同步,因此相对而言HashMap性能会高一些,我们平时使用时若无特殊需求建议使用HashMap,在多线程环境下若使用HashMap需要使用Collections.synchronizedMap()方法来获取一个线程安全的集合 3. HashMap可以使用null作为key,而Hashtable则不允许null作为key 虽说HashMap支持null值作为key,不过建议还是尽量避免这样使用,因为一旦不小心使用了,若因此引发一些问题,排查起来很是费事:HashMap以null作为key时,总是存储在table数组的第一个节点上 4. HashMap是对Map接口的实现,HashTable实现了Map接口和Dictionary抽象类 5. HashMap的初始容量为16,Hashtable初始容量为11,两者的填充因子默认都是0.75 HashMap扩容时是当前容量翻倍即:capacity*2,Hashtable扩容时是容量翻倍+1即:capacity*2+1 6. 两者计算hash的方法不同 Hashtable计算hash是直接使用key的hashcode对table数组的长度直接进行取模 HashMap计算hash对key的hashcode进行了二次hash,以获得更好的散列值,然后对table数组长度取摸 7. HashMap和Hashtable的底层实现都是数组+链表结构实现
TreeMap
TreeMap实现SortMap接口,能够把它保存的记录根据键排序。 默认是按键值的升序排序,也可以指定排序的比较器,当用Iterator 遍历TreeMap时,得到的记录是排过序的。
与 HashMap 不同, TreeMap 的底层就是一颗红黑树,它的 containsKey , get , put and remove 方法的时间复杂度是 log(n) ,并且它是按照 key 的自然顺序(或者指定排序)排列,与 LinkedHashMap 不同, LinkedHashMap 保证了元素是按照插入的顺序排列。
LinkedHashMap
LinkedHashMap保存了记录的插入顺序,在用Iterator遍历LinkedHashMap时,先得到的记录肯定是先插入的。也可以在构造时带参数,按照应用次数排序。 在遍历的时候会比HashMap慢,不过有种情况例外:当HashMap容量很大,实际数据较少时,遍历起来可能会比LinkedHashMap慢。因为LinkedHashMap的遍历速度只和实际数据有关,和容量无关,而HashMap的遍历速度和他的容量有关。
LinkedHashMap 拥有 HashMap 的所有特性,它比 HashMap 多维护了一个双向链表,因此可以按照插入的顺序从头部或者从尾部迭代,是有序的,不过因为比 HashMap 多维护了一个双向链表,它的内存相比而言要比 HashMap 大,并且性能会差一些,但是如果需要考虑到元素插入的顺序的话, LinkedHashMap 不失为一种好的选择
1、一般情况下,我们用的最多的是HashMap。HashMap里面存入的键值对在取出的时候是随机的,它根据键的HashCode值存储数据,根据键可以直接获取它的值,具有很快的访问速度。在Map 中插入、删除和定位元素,HashMap 是最好的选择。 2、TreeMap取出来的是排序后的键值对。但如果您要按自然顺序或自定义顺序遍历键,那么TreeMap会更好。 3、LinkedHashMap 是HashMap的一个子类,如果需要输出的顺序和输入的相同,那么用LinkedHashMap可以实现,它还可以按读取顺序来排列,像连接池中可以应用。
BitSet位图
适用场景:整数,无重复;
常见的应用是那些需要对海量数据进行一些统计工作的时候,比如日志分析、用户数统计等等 如统计40亿个数据中没有出现的数据,将40亿个不同数据进行排序等。
反射
泛型
栈&堆
值类型被创建在栈上,引用类型被创建在堆上
栈是为执行线程留出的内存空间;堆(heap)是为动态分配预留的内存空间
每一个线程都有一个栈,但是每一个应用程序通常都只有一个堆(尽管为不同类型分配内存使用多个堆的情况也是有的)。
相比堆而言在栈上分配内存要快的多;堆的大小 可以高达 4G,栈的大小 一般是 1M ~10M 不等
当用栈过多时可导致栈溢出(无穷次(大量的)的递归调用,或者大量的内存分配)。堆是由程序员通过 调用系统库函数来管理内存,所以管理不力 就会出现常说的内存泄漏
堆是向高地址扩展 也就是常说的向上生长。是不连续的内存区域。 栈是向低地址扩展 也就是常说的向下生长。 是连续的内存区域。
在栈上创建变量的时候会扩展,并且会自动回收;在堆上的变量必须要手动释放,不存在作用域的问题。
栈的内存速率较快
线程基础
在java中要想实现多线程,有两种手段,一种是继续Thread类,另外一种是实现Runable接口.(其实准确来讲,应该有三种,还有一种是实现Callable接口,并与Future、线程池结合使用)
IO流
IO流主要分为两大类,字节流和字符流。字节流可以处理任何类型的数据,如图片,视频等,字符流只能处理字符类型的数据。
IO流的本质是数据传输,并且流是单向的。
对象的初始化过程
静态初始化->父类初始化->子类初始化
形参和实参
出现在函数定义中的参数,就叫形参 出现在函数调用中的参数,就叫实参
形参变量只有在被调用时才分配内存单元,在调用结束时,即刻释放所分配的内存单元。
实参可以是常量、变量、表达式、函数等,无论实参是何种类型的量,在进行函数调用时,它们都必须有确定的值,以便把这些值传送给形参
在一般传值调用的机制中只能把实参传送给形参,而不能把形参的值反向地传送给实参。因此在函数调用过程中,形参值发生改变,而实参中的值不会变化。而在引用调用的机制当中是将实参引用的地址传递给了形参,所以任何发生在形参上的改变实际上也发生在实参变量上。
引用类型
强引用
默认类型
GC Roots可达性分析:可达,仍被引用不回收;不可达,回收
所有程序的场景,基本对象,自定义对象等。
String str = "xxx";
软引用
比强引用稍弱
内存足,不会被回收;内存不足,才回收
一般用在对内存非常敏感的资源上,用作缓存的场景比较多:网页缓存、图片缓存等
SoftReference<String> softReference = new SoftReference<String>(new String("xxx")); System.out.println(softReference.get());
弱引用
比软引用生命周期更短,只能存活到下一次垃圾收集之前
生命周期很短的对象,例如ThreadLocal中的Key
WeakReference<String> weakReference = new WeakReference<String>(new String("Misout的博客")); System.gc(); if(weakReference.get() == null) { System.out.println("weakReference已经被GC回收"); } // 结果 weakReference已经被GC回收
虚引用
随时会被回收, 创建了可能很快就会被回收
必须和引用队列关联使用
业界暂无使用场景, 可能被JVM团队内部用来跟踪JVM的垃圾回收活动
PhantomReference<String> phantomReference = new PhantomReference<String>(new String("Misout的博客"), new ReferenceQueue<String>()); System.out.println(phantomReference.get()); //结果总是Null
作用:为了更好的管理对象的内存,更好的进行垃圾回收