1、面向对象三大特征
关键字:封装、继承、多态
面向对象的三大特征分别是封装、继承和多态。那么怎么理解呢?
我们知道java中万物皆对象,既然是对象,那么肯定有属性和行为。有的属性和行为我们不希望外部调用,比如一个电脑要用主机机箱包起来,因为内部的其他部分是使用者用不到的。所以Java提供了封装机制,可以把对象封装起来,我们可以控制访问权限来指定谁可以访问和操作。
继承相当于使用了对象的公共部分。如果每次都创建类似的新类,一来太麻烦,二来耦合度太高了。Java提供了继承机制,让我们可以创建新的类来继承以前的类、抽象类和接口。
多态相当于使用了对象的特点,不同于父类的地方。任何对象都不可能一模一样,肯定会有它的差异,Java提供了父类型引用指向子类型对象的多态机制,这让我们调用代码更加灵活。编译阶段JVM只会看这个调用到的父类型是否满足要求,执行的时候才会看子类型。这样耦合度就非常小了。
2、HashMap的加载因子
关键字:0.75、泊松分布
先看源码(截取):
public class HashMap<K,V> extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable {
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
static final float DEFAULT_LOAD_FACTOR = 0.75f;
}
这里面说明了HashMap的初始容量为16,而它的默认加载因子是0.75。
为什么 HashMap 的加载因子是0.75?_Java技术栈,分享最主流的Java技术-CSDN博客
HashMap是一个数组 + 链表的数据结构,但是数组如果设置的太大,那么可能分布的太分散,导致占用率不高;如果分数组设置的太小,那么最后占满了发现没地方了。
占满了没地方怎么解决呢?可以有很多种办法,线性探测法、平方探测法、伪随机探测法、再哈希法、链地址法。HashMap很明显使用的链地址法。
看源码中的注释:
/*Ideally, under random hashCodes, the frequency of
* nodes in bins follows a Poisson distribution
* (http://en.wikipedia.org/wiki/Poisson_distribution) with a
* parameter of about 0.5 on average for the default resizing
* threshold of 0.75, although with a large variance because of
* resizing granularity. Ignoring variance, the expected
* occurrences of list size k are (exp(-0.5) * pow(0.5, k) /
* factorial(k)). The first values are:
*
* 0: 0.60653066
* 1: 0.30326533
* 2: 0.07581633
* 3: 0.01263606
* 4: 0.00157952
* 5: 0.00015795
* 6: 0.00001316
* 7: 0.00000094
* 8: 0.00000006
* more: less than 1 in ten million
*/
这里很难看懂,大概意思就是根据泊松分布原理,加载因子为0.75的话,当链表长度达到8的时候,可能性为0.00000006,机会就是不可能的了。
补:加载因子是0.75,每次扩容都是2的倍数,因为HashMap底层中,扩容时会调用一个resize方法,这个方法中可以看出他会创建一个新的数组,并且将旧数组的元素经过e.hash&(newCap-1)的计算方法添加到新数组中,这个&是位运算,效率非常高,按位与的计算过程就是一假则假,当HashMap的容量为2的幂时,newCap-1的二进制就是全1的这种形式,这样与添加的元素的hash值进行位运算能够充分散列,让添加的元素均匀分布在HashMap的每个位置上,减少hash碰撞。
3、集合:List、Set、Map谁是有序的,谁是无序的,各自的方法有哪些,继承结构是怎样的,扩容、线程安全、初始数组
关键字:数据结构为Array和Queue有序,Linked、Set、Map的无序
扩容:数组相关为10,因子为1,扩容1倍;Hash相关的16,因子0.75,扩容1倍
3.1 各种集合的特性和继承结构
先看看各种集合类型的特点:
Set和数组差不多,但是无序。
List是个链表,与位置有关与数组相不相等无关,List有序。
Queue是队列,先进先出,后进后出。
Hash是根据key计算hash值,也就意味着存入是没有顺序的。
这里要先看看各自的数据结构才能更清晰的理解。
Collection(顶级接口):
|——Set:无序(存取顺序不对等)
|——HashSet:底层是HashMap,无序
|——LinkedHashSet:一看是链表必然有序,但是不可重复
|——TreeSet:无序,但是树会根据值进行排序
|——List:有序,可重复
|——ArrayList:数组,查询方便,存取麻烦
|——LinkedList:链表:存取方便,查找麻烦
|——Vector:线程安全的,底层和ArrayList类似
|——Queue:队列,有序,可重复
Map:(子类全部都是无序不可重复的)
方法
那直接看Collection接口和Map接口
public interface Collection<E> extends Iterable<E> {
int size();
boolean isEmpty();
boolean contains(Object o);
boolean containsAll(Collection<?> c);
Iterator<E> iterator();
Object[] toArray();
boolean add(E e);
boolean addAll(Collection<? extends E> c);
boolean remove(Object o);
boolean removeAll(Collection<?> c);
boolean retainAll(Collection<?> c);
void clear();
boolean equals(Object o);
int hashCode();
......
}
那么根据增删改查操作分析一下:
-
增:add、addAll
-
删:remove、removeAll、clear
-
改:就是直接直接赋值、还有变成数组toArray
-
查:查是否空isEmpty、查是否有contains、containsAll、retainAll、查是否等equals、查有多少size
Map接口:
public interface Map<K,V> {
int size();
boolean isEmpty();
boolean containsKey(Object key);
boolean containsValue(Object value);
V get(Object key);
V put(K key, V value);
V remove(Object key);
void putAll(Map<? extends K, ? extends V> m);
void clear();
......
和上面类似,只是有获取key和value的方法。
初始容量和扩容
Vector和ArrayList初始容量是10,加载因子是1,扩容1倍
HashSet初始容量16,加载因子0.75,扩容1倍
HashMap初始容量16,加载因子0.75,扩容1倍
4、Comparable和Comparator的区别
关键字:Comparable在java.lang;Comparator在java.util;Comparable不如Comparator灵活
首先二者所在的包不同:Comparable在java.lang包下,Comparator在java.util包下。
Comparable接口的比较方法:
public int compareTo(T o);
某个对象调用它的话,在我们写这个compareTo方法时系统是不知道的,所以我们就无法拿两者的属性进行比较,但是Comparator不一样。Comparator的方法是这样的。
int compare(T o1, T o2);
我们可以传两个对象,生成它们的比较策略。Comparator更像是设计模式中的策略模式。提供很多种计算方法、然后可以让它们相互之间替换。
5、IO流有哪几类,它们有哪些方法?
FileInputStream、FileOutPutStream、FileReader、FileWriter、BufferReader、BufferWriter、InputStreamReader、OutputStreamWriter、ObjectInputStream、ObjectOutputStream
reader、writer、flush、close方法;
6、String、StringBuffer和StringBuilder的区别?
String是不可变的类型,它的底层是用final修饰的,所以每次给String字符串追加的时候,都要生成新的字符串,很占内存。
StringBuffer和StringBuilder更适合对字符串进行操作,它们有append方法、insert方法、reverse方法等等。
StringBuffer是线程安全的,StringBuilder是非线程安全的,它里面没有synchroized方法。
insert方法不常用,记录一下。
public static void main(String[] args) {
StringBuilder sb = new StringBuilder();
sb.append("sss");
System.out.println(sb);
sb.insert(1,"sas");
System.out.println(sb);
}
-------------------------------------------------
sss
ssasss
7、Object类是所有类的父类,说说他的几个常用方法,并且介绍一下
Object类是所有类的父类,它里面有几个方法,equals、hashcode、clone、finalize、wait、notify、notifyAll、toString、getClass方法和native本地方法。
8、接口和抽象类的区别
关键字:构造方法、名称、继承方式、修饰符、变量常量
(1)接口没有构造方法,抽象类可以有
(2)抽象类是class、接口时interface
(3)接口是implements、抽象类是extends
(4)接口支持多继承、抽象类是单继承
(5)接口的方法一般就是public、抽象类则都可以(jdk8以后接口的方法可以有实现,抽象类也是可有可无的)
(6)接口为static修饰的、实现类的不一定
9、你对IO流了解?你说说BIO、NIO和AIO 有什么区别?
关键字:同步阻塞、同步非阻、异步阻塞
BIO(Blocking IO)是同步阻塞是IO,使用socket创建连接。
NIO(Non-Blocking IO)是同步非阻塞式IO,客户端和服务器端通过 Channel(通道)通讯,实现了多路复用。
AIO(Asynchronous IO)是异步非阻塞式IO,基于事件和回调机制。
10、HashMap中去重问题
因为hashMap的key不能相同,所以可以使用HashMap去重。