1、面向对象和面向过程
面向过程:强调的是功能,强调功能的过程、动作;
面向对象:将功能封装进对象,强调具备了某功能的对象。(强调对象、事物);
面向对象的三大特征:
封装、继承、多态
1.1封装
封装:
指隐藏对象的属性和实现细节,仅对外提供公共访问的方式。
好处:提高安全性,便于使用。
封装原则:将不需要对外提供的内容都隐藏起来;
1.public:public表明该数据成员、成员函数是对所有用户开放的,所有用户都可以直接进行调用
2.private:private表示私有,私有的意思就是除了class自己之外,任何人都不可以调用
3.protected:protected对于子女、朋友来说,就是public的,可以自由使用,没有任何限制,而对于其他的外部class,protected就变成private
1.2 继承
Java的类可以分为三类:
- 类:使用class定义,没有抽象方法
- 抽象类:使用abstract class定义,可以有也可以没有抽象方法
- 接口:使用interface定义,只能有抽象方法
在这三个类型之间存在如下关系:
-
类可以extends:类、抽象类(必须实现所有抽象方法),但只能extends一个,可以implements多个接口(必须实现所有接口方法)
-
抽象类可以extends:类,抽象类(可全部、部分、或者完全不实现父类抽象方法),可以implements多个接口(可全部、部分、或者完全不实现接口方法)
-
接口可以extends多个接口
继承以后子类可以得到什么:
子类拥有父类非private的属性和方法
子类可以添加自己的方法和属性,即对父类进行扩展
子类可以重新定义父类的方法,即多态里面的覆盖,后面会详述
关于构造函数:构造函数不能被继承,子类可以通过super()显示调用父类的构造函数
创建子类时,编译器会自动调用父类的 无参构造函数
如果父类没有定义无参构造函数,子类必须在构造函数的第一行代码使用super()显示调用
类默认拥有无参构造函数,如果定义了其他有参构造函数,则无参函数失效,所以父类没有定义无参构造函数,不是指父类没有写无参构造函数。
1.3 多态
在了解多态之前,首先需要知道方法的唯一性标识即什么是相同/不同的方法:
-
一个方法可以由:修饰符如public、static+返回值+方法名+参数+throw的异常 5部分构成
-
其中只有方法名和参数是唯一性标识,意即只要方法名和参数相同那他们就是相同的方法
-
所谓参数相同,是指参数的个数,类型,顺序一致,其中任何一项不同都是不同的方法
何谓重载:
- 重载是指一个类里面(包括父类的方法)存在方法名相同,但是参数不一样的方法,参数不一样可以是不同的参数个数、类型或顺序
- 如果仅仅是修饰符、返回值、throw的异常 不同,那这是2个相同的方法,编译都通不过,更不要说重载了
何谓覆盖/重写:
- 覆盖描述存在继承关系时子类的一种行为
- 子类中存在和父类相同的方法即为覆盖,何谓相同方法请牢记前面的描述,方法名和参数相同,包括参数个数、类型、顺序
覆盖/重写的规则:
子类不能覆盖父类private的方法,private对子类不可见,如果子类定义了一个和父类private方法相同的方法,实为新增方法
重写方法的修饰符一定要大于或等于被重写方法的修饰符(public > protected > default > private)
重写抛出的异常需与父类相同或是父类异常的子类,或者重写方法干脆不写throws
重写方法的返回值必须与被重写方法一致,否则编译报错
静态方法不能被重写为非静态方法,否则编译出错 -
多态可以说是“一个接口,多种实现”或者说是父类的引用变量可以指向子类的实例,被引用对象的类型决定调用谁的方法,但这个方法必须在父类中定义
-
多态可以分为两种类型:编译时多态(方法的重载)和运行时多态(继承时方法的重写),编译时多态很好理解,后述内容针对运行时多态
-
运行时多态依赖于继承、重写和向上转型
2、Java集合框架和常用的数据结构及原理
参考资料:
https://www.cnblogs.com/bingyimeiling/p/10255037.html
集合可以看作是一种容器,用来存储对象信息。
所有集合类都位于java.util包下,但支持多线程的集合类位于java.util.concurrent包下。
数组与集合的区别如下:
1)数组长度不可变化而且无法保存具有映射关系的数据;集合类用于保存数量不确定的数据,以及保存具有映射关系的数据。
2)数组元素既可以是基本类型的值,也可以是对象;集合只能保存对象。
Java集合主要有两个跟接口:Collection和Map
Collection又有三个子接口:List、Set、Queue。
List:代表有序可重复集合,可以根据元素的索引来访问。
Set:代表无序不可重复集合,根据元素本身来访问。
Queue:队列集合。
Map:存储key-value对的集合,可以根据元素的key来访问元素的value。
2.1 Collection接口
常用方法:
2.2 Set集合
主要特征:无序,不可重复。
以下几个实现类都是线性不安全
常用实现类:
2.2.1 HashSet
HashSet是基于HashMap实现的。按照hash算法来存储元素,有很好的存取和查找性能。
不能保证元素的顺序。
允许有null值。
不是线性安全的。
设是一个length为16的数组
存储原理:
第一步:首先调用对象的hashcode()方法,也就是用哈希散列0-15中的随机一个数%16来得到一个hashcode值,这个值就是这个对象在数组中存储的位置。
如果这个位置没有对象,则保存在该位置,存储完成。反之进入第二步.
第二步:
让需要存储的对象和该位置的对象使用equals进行对比。
如果对比结果为true,则需要存储的对象被舍弃,报一个对象已存在的错误。
如果对比结果为false,则需要存储的对象以链式结构将两个对象放在同一个位置,这将导致性能下降,因此应当避免这种情况出现。
查找原理:
通过使用hashcode方法计算出元素的hashcode值,然后去这个值对应的位置取出元素即可。
重写hashCode()方法的基本原则如下:
♦ 在程序运行过程中,同一个对象的hashCode()方法返回值应相同。
♦ 当两个对象通过equals()方法比较返回true时,这两个对象的hashCode()方法返回值应该相等。
♦ 对象中用作equals()方法比较标准的实例变量,都应该用于计算hashCode值。
2.2.2 LinkedHashSet
LinkedHashSet是HashSet的一个子类,具有HashSet的特性,也是根据元素的hashCode值来决定元素的存储位置。
但它使用链表维护元素的次序,元素的顺序与添加顺序一致。
由于LinkedHashSet需要维护元素的插入顺序,因此性能略低于HashSet,
但在迭代访问Set里的全部元素时由很好的性能。
2.2.3 TreeSet
TreeSet时SortedSet接口的实现类,TreeSet可以保证元素处于排序状态,它采用红黑树的数据结构来存储集合元素。
支持两种排序:
自然排序:
通过调用compareTo(Object obj)方法来比较元素的大小关系,然后将元素按升序排列。
定制排序:想要实现定制排序,需要在创建TreeSet集合对象时,提供一个Comparator对象与该TreeSet集合关联,由Comparator对象负责集合元素的排序逻辑。
自然排序实现的是Comparable接口,定制排序实现的是Comparator接口。
2.2.4 EnumSet类
EnumSet是一个专为枚举类设计的集合类,不允许添加null值。EnumSet的集合元素也是有序的,它以枚举值在Enum类内的定义顺序来决定集合元素的顺序。
2.2.5 各个set实现类性能分析
HashSet的性能比TreeSet的性能好(特别是添加,查询元素时),因为TreeSet需要额外的红黑树算法维护元素的次序,如果需要一个保持排序的Set时才用TreeSet,否则应该使用HashSet。
LinkedHashSet是HashSet的子类,由于需要链表维护元素的顺序,所以插入和删除操作比HashSet要慢,但遍历比HashSet快。
EnumSet是所有Set实现类中性能最好的,但它只能 保存同一个枚举类的枚举值作为集合元素。
2.3、List
特征:有序,可重复。
实现接口:ArrayList、LinkedList、Vector、Stack。
2.3.1 ArrayList
动态数组。
允许插入null。
初始容量:10
果我们明确所插入元素的多少,最好指定一个初始容量值,避免过多的进行扩容操作而浪费时间、效率。
ArrayList擅长于随机访问。同时ArrayList是非同步的。
2.3.2 LinkedList
LinkedList是List接口的另一个实现,除了可以根据索引访问集合元素外,LinkedList还实现了Deque接口,可以当作双端队列来使用,
也就是说,既可以当作“栈”使用,又可以当作队列使用。
LinkedList的实现机制与ArrayList的实现机制完全不同,ArrayLiat内部以数组的形式保存集合的元素,所以随机访问集合元素有较好的性能;LinkedList内部以链表的形式保存集合中的元素,所以随机访问集合中的元素性能较差,但在插入删除元素时有较好的性能。
2.3.3 Vector
与ArrayList相似,但是Vector是线程同步的。所以说Vector是线程安全的动态数组。它的操作与ArrayList几乎一样。
2.3.4 Stack
Stack继承自Vector,实现一个后进先出的堆栈。
Stack提供5个额外的方法使得Vector得以被当作堆栈使用。
基本的push和pop 方法,还有peek方法得到栈顶的元素,empty方法测试堆栈是否为空,search方法检测一个元素在堆栈中的位置。Stack刚创建后是空栈。
2.3.5 Iterator和ListIterator
Iterator是一个接口,它是集合的迭代器。集合可以通过Iterator去遍历集合中的元素。Iterator提供的API接口如下:
♦ boolean hasNext():判断集合里是否存在下一个元素。如果有,hasNext()方法返回 true。
♦ Object next():返回集合里下一个元素。
♦ void remove():删除集合里上一次next方法返回的元素。
ListIterator接口继承Iterator接口,提供了专门操作List的方法。ListIterator接口在Iterator接口的基础上增加了以下几个方法:
♦ boolean hasPrevious():判断集合里是否存在上一个元素。如果有,该方法返回 true。
♦ Object previous():返回集合里上一个元素。
♦ void add(Object o):在指定位置插入一个元素。
比较:ListIterator增加了向前迭代的功能(Iterator只能向后迭代),ListIterator还可以通过add()方法向List集合中添加元素(Iterator只能删除元素)。
2.4 Map
Map接口采用键值对Map<K,V>的存储方式,保存具有映射关系的数据
key值不允许重复,可以为null。如果添加key-value对时Map中已经有重复的key,则新添加的value会覆盖该key原来对应的value。
常见的实现类:HashMap、LinkedHashMap、TreeMap、Hashtable。
Map常见方法:
2.4.1 HashMap和Hashtable
HashMap与Hashtable是Map接口的两个典型实现,它们之间的关系完全类似于ArrayList与Vertor。HashTable是一个古老的Map实现类,它提供的方法比较繁琐,目前基本不用了
HashMap与Hashtable主要存在以下两个典型区别:
♦ HashMap是线程不安全,HashTable是线程安全的。
♦ HashMap可以使用null值最为key或value;Hashtable不允许使用null值作为key和value,如果把null放进HashTable中,将会发生空指针异常。
HashMap基于hashing原理,通过put()和get()方法存储和获取对象。当我们将键值对传递给put()方法时,它调用建对象的hashCode()方法来计算hashCode值,然后找到bucket位置来储存值对象。
当获取对象时,通过建对象的equals()方法找到正确的键值对,然后返回对象。HashMap使用链表来解决碰撞问题,当发生碰撞了,对象将会存储在链表的下一个节点中。
2.4.2 LinkedHashMap
LinkedHashMap使用双向链表来维护key-value对的次序(其实只需要考虑key的次序即可),该链表负责维护Map的迭代顺序,与插入顺序一致,因此性能比HashMap低,但在迭代访问Map里的全部元素时有较好的性能。
2.4.3 Properties
Properties类时Hashtable类的子类,它相当于一个key、value都是String类型的Map,主要用于读取配置文件。
2.4.4 TreeMap
TreeMap是SortedMap的实现类,是一个红黑树的数据结构,每个key-value对作为红黑树的一个节点。
TreeMap存储key-value对时,需要根据key对节点进行排序。TreeMap也有两种排序方式:
♦ 自然排序:TreeMap的所有key必须实现Comparable接口,而且所有的key应该是同一个类的对象,否则会抛出ClassCastException。
♦ 定制排序:创建TreeMap时,传入一个Comparator对象,该对象负责对TreeMap中的所有key进行排序。
2.4.5 各个Map实现类的性能分析
♦ HashMap通常比Hashtable(古老的线程安全的集合)要快
♦ TreeMap通常比HashMap、Hashtable要慢,因为TreeMap底层采用红黑树来管理key-value。
♦ LinkedHashMap比HashMap慢一点,因为它需要维护链表来爆出key-value的插入顺序。
2.4.6 几个线程安全的实现类
1.Hashtable是线程安全的哈希表,它是通过synchronized来保证线程安全的;即,多线程通过同一个“对象的同步锁”来实现并发控制。Hashtable在线程竞争激烈时,效率比较低(此时建议使用ConcurrentHashMap)。当一个线程访问Hashtable的同步方法时,其它线程如果也在访问Hashtable的同步方法时,可能会进入阻塞状态。
2.Collections.synchronizedMap()使用了synchronized同步关键字来保证对Map的操作是线程安全的。
3.ConcurrentHashMap是线程安全的哈希表。在JDK1.7中它是通过“锁分段”来保证线程安全的,本质上也是一个“可重入的互斥锁”(ReentrantLock)。多线程对同一个片段的访问,是互斥的;但是,对于不同片段的访问,却是可以同步进行的。在JDK1.8中是通过使用CAS原子更新、volatile关键字、synchronized可重入锁实现的。
2.5 Queue
Java中实现Queue有三种方式:
- 阻塞队列
- 非阻塞队列
- 双端队列
2.5.1 阻塞队列(BlockingQueue)
常用的实现类:
实现类 简单描述
ArrayBlockingQueue 基于数组的有界阻塞队列,必须指定大
LinkedBlockingQueue 基于单链表的无界阻塞队列,不需要指定大小
PriorityBlockingQueue 基于最小二叉堆的无界、优先级阻塞队列
DelayQueue 基于延迟、优先级、无界阻塞队列
SynchronousQueue 基于CAS的阻塞队列
常用方法:
add():新增一个元索,假如队列已满,则抛异常。
offer():新增一个元素,假如队列没满则返回 true,假如队列已满,则返回 false。
put():新增一个元素,假如队列满,则阻塞。
element():获取队列头部一个元素,假如队列为空,则抛异常。
peek():获取队列头部一个元素,假如队列为空,则返回 null。
remove():执行删除操作,返回队列头部的元素,假如队列为空,则抛异常。
poll():执行删除操作,返回队列头部的元素,假如队列为空,则返回 null。
take():执行删除操作,返回队列头部的元素,假如队列为空,则阻塞。
补充:CAS: compare and set 先比较再放置更新
三个基本操作数:内存地址V、旧的预期值A,要修改的新值B
假如要修改v里的值10 ,改为11,
当线程进行更新操作时,只有v的值等于10才会更新为11。
synchronized属于悲观锁,而cas属于乐观锁
CAS的缺点:
1) CPU开销过大
在并发量比较高的情况下,如果许多线程反复尝试更新某一个变量,却又一直更新不成功,循环往复,会给CPU带来很到的压力。
2) 不能保证代码块的原子性
CAS机制所保证的只是一个变量的原子性操作,而不能保证整个代码块的原子性。比如需要保证3个变量共同进行原子性的更新,就不得不使用synchronized了。
3) ABA问题
当一个值从A变成B,又更新回A,普通CAS机制会误判通过检测。
利用版本号比较可以有效解决ABA问题。
假设有一个遵循CAS原理的提款机,小灰有100元存款,要用这个提款机来提款50元。由于提款机硬件出了点问题,小灰的提款操作被同时提交了两次,开启了两个线程,两个线程都是获取当前值100元,要更新成50元。
线程1首先执行成功,把余额从100改成50.线程2因为某种原因阻塞。这时,小灰的妈妈刚好给小灰汇款50元。
线程2仍然是阻塞状态,线程3执行成功,把余额从50改成了100。
线程2恢复运行,由于阻塞之前获得了“当前值”100,并且经过compare检测,此时存款实际值也是100,所以会成功把变量值100更新成50。
导致结果出错!
2.5.2 非阻塞队列
非阻塞队列是使用CAS机制实现的。
常见的非阻塞队列:
PriorityQueue 基于优先级的无界优先级队列
ConcurrentLinkedDeque 基于双向链表的无界并发队列
2.5.3双端队列(Deque)
Deque是一个可以在头部和尾部操作元素的队列,俗称双端队列。
常用的双端队列实现类:
LinkedList 基于单链表的无界双端队列,允许元素为null,线性不安全
ArrayDeque 基于数组的有界双端队列,不允许null,不是线性安全,当作栈使用时,性能比Stack好,当作队列使用时,性能比LinkedList好。
3、Java中常用包解析
第一个包:java.lang包。
该包提供了Java语言进行程序设计的基础类,它是默认导入的包。该包里面的Runnable接口和Object、Math、String、StringBuffer、System、Thread以及Throwable类需要重点掌握,因为它们应用很广。
第二个包:java.util包。
该包提供了包含集合框架、遗留的集合类、事件模型、日期和时间实施、国际化和各种实用工具类(字符串标记生成器、随机数生成器和位数组)。
第三个包:java.io包。
该包通过文件系统、数据流和序列化提供系统的输入与输出。
第四个包:java.net包。
该包提供实现网络应用与开发的类。
第五个包:java.sql包。
该包提供了使用Java语言访问并处理存储在数据源(通常是一个关系型数据库)中的数据API。
第六个包:java.awt包
该包包含了构成抽象窗口工具集(abstract window toolkits)的多个类,这些类被用来构建和管理应用程序的图形用户界面(GUI)。
第七个包:javax.swing包。
这两个包提供了GUI设计与开发的类。java.awt包提供了创建界面和绘制图形图像的所有类,而javax.swing包提供了一组“轻量级”的组件,尽量让这些组件在所有平台上的工作方式相同。
java.lang包的思维导图:
4、异常处理机制
参考资料:https://www.cnblogs.com/lulipro/p/7504267.html
Java中的异常思维导图:
错误:Error类以及他的子类的实例,代表了JVM本身的错误。错误不能被程序员通过代码处理,Error很少出现
异常:Exception以及他的子类,代表程序运行时发送的各种不期望发生的事件。可以被Java异常处理机制使用,是异常处理的核心。
异常分为两大类:
非检查异常
:error和runtimeException及其子类。
javac编译时不会提升和发现的异常,我们可以通过try…catch来不处理这种异常。
异常原因:多半是代码写的有问题。
NullPointerException - 空指针引用异常
ClassCastException - 类型强制转换异常。
IllegalArgumentException - 传递非法参数异常。
ArithmeticException - 算术运算异常,/0
ArrayStoreException - 向数组中存放与声明类型不兼容对象异常
IndexOutOfBoundsException - 下标越界异常
NegativeArraySizeException - 创建一个大小为负数的数组错误异常
NumberFormatException - 数字格式异常
SecurityException - 安全异常
UnsupportedOperationException - 不支持的操作异常
检查异常
:除了Error 和 RuntimeException的其它异常。
javac强制要求程序员为这样的异常做预备处理工作(使用try…catch…finally或者throws)。在方法中要么用try-catch语句捕获它并处理,要么用throws子句声明抛出它,否则编译不会通过。这样的异常一般是由程序的运行环境导致的。因为程序可能被运行在各种未知的环境下,而程序员无法干预用户如何使用他编写的程序,于是程序员就应该为这样的异常时刻准备着。如SQLException , IOException,ClassNotFoundException 等。
IOException:输入输出异常。
https://www.codenong.com/13216148/
使用场景:
- 读取网络文件并断开连接。
- 读取不再可用的本地文件。
- 使用某些流来读取数据,其他一些进程关闭了流。
- 尝试读取/写入文件但没有权限。
- 尝试写入文件但磁盘空间不再可用。
通常,I / O表示输入或输出。只要输入或输出操作失败或解释,这些方法就会抛出IOException。请注意,读取或写入内存时不会抛出此内容,因为Java会自动处理它。