正所谓学而时习之,温故而知新,最近在回顾Java基础知识的过程中有了和以前不一样的理解,所以在此记录一下,注意这仅供参考哦!如果有什么理解有误的地方希望大家可以纠正,共同探讨,一起进步。
抽象类和接口
- 抽象类和接口都不能直接实例化。
- 接口只能包含方法声明和常量(从Java8开始接口可以包含static类型有着具体实现和返回类型的方法,这是Java8新特性),抽象类中可以包含方法声明和有着具体实现的方法。
- 接口里定义的变量只能是公共的静态的常量,抽象类中的变量是普通变量。
- 抽象类里可以没有抽象方法。
- 如果一个类里有抽象方法,那么这个类只能是抽象类。
- 抽象方法要被实现,所以不能是静态的,也不能是私有的。
- 接口可继承接口,并可多继承接口,但类只能单继承。
我认为Java中抽象类主要承担着封装的角色,而接口主要承担着多态的角色,接口是一种约定,实现接口的类要遵循这个约定,举个栗子吧,usb3.0就是一种规范,插口尺寸就是接口中的常量,传输数据、充电就是对应的方法,抽象类是一个类,主要是对一些拥有共同属性或方法的对象的一种封装。
HashMap、TreeMap、ConcurrentHashMap对比
- HashMap:使用数组和链表实现,它是线程不安全的Map,在JDK1.8中,如果链表中存储的Entry超过了8个则就会自动转换链表为红黑树,提高查询效率。
- TreeMap:基于红黑树实现,会对Key进行排序,使用了TreeMap存储键值对,再使用iterator进行输出时,会发现其默认采用key由小到大的顺序输出键值对,如果想要按照其他的方式来排序,需要重写它的compartor接口。
- ConcurrentHashMap:和HashMap相似,是线程安全的。
使用最多的通常是HashMap,如果需要有顺序的存储则使用TreeMap,并且可以自己定义排序规则,ConcurrentHashMap用在要求线程安全的情况下。
equals()和hashCode()的联系
equals()比较时有时可能会很复杂,这样效率就比较低,而利用hashCode()进行对比,则只要生成一个hash值进行比较就可以了,效率很高,那么hashCode()既然效率这么高为什么还要equals()呢?因为hashCode()并不是完全可靠,有时候不同的对象他们生成的hashCode也会一样,所以hashCode()只能说是大部分时候可靠,并不是绝对可靠,所以我们可以得出如下结论:
- equals()相等的两个对象他们的hashCode()肯定相等,也就是用equals()对比是绝对可靠的。
- hashCode()相等的两个对象他们的equals()不一定相等。
回到实际应用中,通常我们在重写一个类的equals方法时会同时重写它的hashCode方法,这主要是因为每当需要对比的时候,首先用hashCode()去对比,如果hashCode()不一样,则表示这两个对象肯定不相等(也就是不必再用equals()去再对比了),如果hashCode()相同,此时再对比他们的equals(),如果equals()也相同,则表示这两个对象是真的相同了,这样既能大大提高了效率也保证了对比的绝对正确性!
finally{}里的代码一定会执行吗?
- 在try和catch块分别加入System.exit(0),finally是不会执行的。(注意:一定是try块和catch块分别加上System.exit(0);少一个都不行,否则finally仍然会被执行,System.exit(0)实际上是JVM已经被终止了)
- 如果在try块或catch块中加入return,此时finally还是会执行,而且是在方法返回调用者前执行。(在finally中改变返回值的做法是不好的,因为如果存在finally代码块,try中的return语句不会立马返回调用者,而是记录下返回值待finally代码块执行完毕之后再向调用者返回其值,然后如果在finally中修改了返回值,就会返回修改后的值)
总结下来就是:与finally相对应的try语句得到执行的情况下,finally才有可能执行。finally执行前,程序或线程终止,finally不会执行。
sleep()方法和wait()方法对比
- sleep()方法是线程类(Thread)的静态方法,wait()是Object类的方法。
- sleep()会让当前线程暂停执行指定的时间,将执行机会(CPU)让给其他线程,但是对象的锁依然保持,因此休眠时间结束后会自动恢复。
- 调用对象的wait()方法导致当前线程放弃对象的锁(线程暂停执行),进入对象的等待池(wait pool),只有调用对象的notify()方法(或notifyAll()方法)时才能唤醒等待池中的线程进入等锁池(lock pool),如果线程重新获得对象的锁就可以进入就绪状态。
- sleep()方法必须要捕获异常,而wait()方法不需要捕获异常,sleep的过程中过程中有可能被其他对象调用它的interrupt(),产生InterruptedException异常,如果程序不捕获这个异常,线程就会异常终止。
其实这两个方法完全没有可比性,因为是两个不同类的方法,只是因为都有使线程休眠的功能,所以面试时可能会经常被面试官拿来作比较。
常用的设计模式
- 工厂模式:工厂类可以根据条件生成不同的子类实例,这些子类有一个公共的抽象父类并且实现了相同的方法,但是这些方法针对不同的数据进行了不同的操作(多态方法)。当得到子类的实例后,开发人员可以调用基类中的方法而不必考虑到底返回的是哪一个子类的实例。
- 代理模式:给一个对象提供一个代理对象,并由代理对象控制原对象的引用。
- 适配器模式:把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起使用的类能够一起工作。
- 单例模式:一个类只有一个实例,即一个类只有一个对象实例。
所谓设计模式,就是一套被反复使用的代码设计经验的总结,使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。
List、Set、Map的区别
在Java容器中一共定义了2种集合,顶层接口分别是Collection和Map。简单来看,Collection代表的是单个元素对象的序列,(可以有序/无序,可重复/不可重复等,具体依据具体的子接口Set,List,Queue等);Map代表的是“键值对”对象的集合(同样可以有序/无序等依据具体实现)。具体实现类如下图所示:
- List与Set都继承于Collection,Map是键值对映射容器,与List和Set有明显的区别
- List(列表):可以允许重复的对象,可以插入多个null元素,是一个有序容器,保持了每个元素的插入顺序,输出的顺序就是插入的顺序。
- Set(集):不允许重复对象,无序容器,无法保证每个元素的存储顺序,只允许一个null元素。
- Map(映射):元素是成对存在的,每个元素由键与值两部分组成,通过键可以找对所对应的值。