文章目录
"=="和equals()的区别
“==” | equals() |
---|---|
如果比较的值是基本数据类型,则比较的是具体值; 如果比较的是引用数据类型,则比较的是地址值。 | Object类中的一个方法,只适用于引用数据类型, equals()本质上与“==”一样,我们所调用的 equals()方法一般都是String类重写过的equals()方法。 |
方法重载和方法重写
方法重写 | 方法重载 |
---|---|
发生在子类 | 在同一个类中 |
方法名相同 | 方法名相同 |
参数的类型、个数、顺序相同 | 参数的类型、个数、顺序不同 |
子类重写父类的方法,返回值类型必须与父类相同或是与父类的子类相同 | 与方法的权限修饰符,返回值类型无关 |
在多态情况下,在方法调用的那一刻编译器才能确定所要调用的具体方法,称为“晚绑定”或“动态绑定” | 重载方法的调用地址在编译期就绑定了,在方法调用前,编译器就确定了要调用的方法,称为“早绑定”或“静态绑定” |
子类重写父类方法,权限修饰符不小于父类声明的权限修饰符 | |
子类抛出异常不大于父类抛出异常 |
抽象类和接口的异同
接口 | 抽象类 |
---|---|
用interface声明,子类用implements实现,是特殊的引用数据类型,不是类。 | 用abstract声明 |
不可以有构造器,接口时多继承的 | 抽象类不能创建对象,可以有构造方法,用于创建子类对象。抽象类时单继承 |
属性都是public static final;方法都是public abstract | 不能用static修饰,static修饰的结构随着类的加载而执行,方法归类所有,不能被继承。 |
JDK1.7及以前都是静态常量和抽象方法; JDK1.8及之后可有静态常量、抽象方法、非抽象方法(用default修饰)(静态方法、默认方法) | 不能用final修饰,抽象类中可以有0个或者多个抽象方法,也可以有实例方法 |
相同点:抽象方法没有方法体,以分号结尾
子类必须实现父类中所有抽象方法
实现类必须实现接口中的抽象方法
final、finally、finalize
final、finally是关键字,finalize是Object类中的一个方法
①final关键字可用来修饰类、方法、变量。
修饰的属性 | 作用 |
---|---|
修饰类 | 被修饰的类不可以被继承,不能有子类 |
修饰方法 | 被修饰的方法不可以被重写 |
变量 | 修饰基本数据类型时,值不可以被更改; 修饰引用数据类型时,地址值不可以被更改 |
final可以与static一起使用,一起使用时只可以用来修饰属性和方法
②finally可以与try-catch-finally组合使用处理异常。
finally中的代码时程序无论是否产生异常都一定会执行的代码
③
throw、throws
throw | throws |
---|---|
throws关键字声明一个方法可能抛出的异常,写在方法名后, 可以写多个Exception子类,每个异常类型之间用英文逗号隔开。 | throw是手动产生一个异常对象,并将其抛出,写在方法里。 |
throws关键字把捕获的异常想上一级调用处声明抛出,是异常处理的一种方式 |
synchronized、lock的异同
同 | 异 |
---|---|
都是解决线程安全问题 | ①lock需要手动启动同步(lock()),结束同步(unlock()) ②synchronized机制在执行完成相应的同步代码后,自动释放同步监视器 |
解决线程安全问题有几种方式?
三种。
①同步代码块
②同步方法
③lock锁
创建多线程有几种方式
四种。
①继承Thread类
②实现Runnable接口
③实现Callable接口
④使用线程池
String 、StringBuffer、StringBuilder
类 | 说明 |
---|---|
String | 不可变字符序列,底层结构char[]存储 |
StringBuffer | 可变字符序列,线程安全,效率低 ,底层结构char[]存储,默认char[16]. |
StringBuilder | 可变字符序列,jdk5.0新增,线程不安全,效率高,底层结构char[]存储,默认char[16]. |
源码分析
源码 | 解析 |
---|---|
String s=new String(); | char[ ] value=new char[0] |
String s=new String(“ab”); | char[ ] value=new char[ ]{‘a’,‘b’} |
StringBuffer sb=new StringBuffer(); | char[ ] value=new char[16],sb.length()=0 |
sb.append(‘a’); | value[0]=‘a’; |
sb.append(‘b’); | value[1]=‘b’; |
StringBuffer sb=new StringBuffer(‘abc’); | char[ ] value=new char[“abc”.length()+16]. 在原有基础上额外加16位,sb.length()=3; |
扩容问题
如果要添加的数据,底层数组放不下,则需要扩容底层的数组
默认情况下,扩容为原来容量的2倍+2,同时将原有数组中的元素复制到新的数组中。
基本、引用数据类型,char[],byte[]与String类的转换
转换类型 | 调用方法 |
---|---|
基本数据类型—>引用数据类型 | 调用包装类的构造器 |
引用数据类型—>基本数据类型 | 调用包装类的×××Value()方法 |
引用数据类型—>String | 调用String重载的ValueOf()方法 |
String—>引用数据类型 | 调用包装类的parse×××()方法 |
String—>char[] | 调用String的toCharArray()方法 |
char[]—>String | 调用String构造器 |
String—>byte[] | 调用String的getBytes()方法 |
byte[]—>String | 调用String构造器 |
collection、collections的区别
collection是存储单列数据的集合接口,包含两个子接口List接口和Set接口。
List接口存储有序、不唯一的元素对象,有序指的是元素的添加顺序
Set接口存储无序、唯一的元素对象。Set接口没有对Collection接口中的方法进行扩展,Collection接口中的方法Set接口都能使用。
ArrayList、LinkedList、Vector的区别
同:三个类都实现了List接口。存储数据特点相同,有序,不唯一的数据
不同点:
ArrayList | LinkedList | Vector |
---|---|---|
ArrayList是List接口的主要实现类,线程不安全,效率高 ;底层使用Object[ ] elementData存储 | 对于频繁插入、删除等操作,使用此类效率比ArrayList高;底层使用双向链表存储 | Vector是List接口的古老实现类,线程安全,效率低;底层使用Object[ ] elementData存储 |
ArrayList、LinkedList、Vector源码分析:
ArrayList:
jdk1.7:
ArrayList list=new ArreyList( );—>底层创建长度为10的Object[ ] elementData数组
list.add(123);—>elementData[0]=new Integer(123);
当容量不够时,则考虑扩容。默认情况下,扩容为原来的1.5倍,同时需要将原有数组中的数据复制到新数组中。建议使用带参的构造器:ArrayList list=new ArrayList(int capacity)—>指定容量
jdk1.8中ArrayList的变化:
ArrayList list=new ArrayList( );—>底层Object[ ] elementData初始化为{ },并没有创建长度为10的数组。
list.add(123);—>第一次调用add时,底层才创建长度为10的数组,并将数据123添加到elementData[0]
扩容操作和jdk1.7一样
jdk1.7中的ArrayList的对象创建类似单例模式的饿汉式;jdk1.8类似懒汉式,延迟了数组的创建,节省内存。
LinkedList:
LinkedList list =new LinkedList( );—>内部声明Node类型的first、last属性,默认值为null,first用于记录首元素,last用于记录末尾元素
Vector:
jdk1.7、jdk1.8中通过Vector( )构造器创建对象时,底层都创建长度为10的数组。
默认扩容为原来数组长度的2倍。
Map接口结构的理解
map接口:存储双列数据,存储key-value对的数据。
HashMap:Map接口的主要实现类,线程不安全,效率高,存储null的key和value
LinkedHashMap:保证遍历Map元素时,可以按照添加的顺序实现遍历。
在原有的HashMap底层结构基础上,添加了一对指针,指向前一个元素和后一个元素,对于高频率的遍历操作,此类执行效率高于HashMap。
TreeMap:保证按照添加到key-value对进行排序,实现排序遍历,此时考虑key的自然排序或定制排序,底层使用红黑树。
Hashtable:Map接口的古老实现类,线程安全,效率低,不能存储null的key和value。
Properties:常用来处理配置文件,key和value都是String类型。
Map中的key:无序、唯一,使用Set存储所有的key—>key所在的类要重写equals()、hashCode() (以HashMap为例)
Map中的value:无序、不唯一,使用collection存储所有的value—>所在类重写equals()
一个键值对key-value构成一个Entry对象
Map中的Entry:无序、唯一,使用set存储所有的Entry。
HashMap的底层实现原理
jdk1.7
HashMap map=new HashMap( )---->实例化后,底层创建长度为16的一维数组Entry[ ] table.
map.put(key1,value1);
调用key1所在类的hashCode( )计算key1的哈希值,此时的哈希值通过某种算法计算出在Entry数组中的存放位置。
①、若此位置上的数据为空,key1-value1—>添加成功Ⅰ
②、若此位置上不为空,已经存在一个或者多个数据(以链表形式存在),则比较key1和已经存在的一个或者多个数据的哈希值。
①、若key1的哈希值与已经存在的数据的哈希值不相同,key1-value1—>添加成功Ⅱ
②、若key1的哈希值和已经存在的某一个数据(key2-value2)的哈希值相同,则继续比较,调用key1所在类的equals(key2)。
①、若equals( )方法返回false,key1-value1—>添加成功Ⅲ
②、若equals( )方法返回true,使用value1替换value2
添加成功Ⅱ、添加成功Ⅲ,key1-value1和原来的数据以链表的形式存储。
扩容情况:默认扩容方式,扩容为原来容量的2倍,并将原有数据复制到扩容后的数组中。当超出临界值且要存放的位置非空时,扩容。
jdk1.8相较于jdk1.7在底层实现方面的不同
- new HashMap( ):底层没有创建长度为16d的数组
- jdk1.8底层数组是Node[ ],而不是Entry [ ].
- 首次调用put( )方法时,底层创建长度为16的数组
- jdk1.7 底层结构:数组+链表
jdk1.8的层结构:数组+链表+红黑树
当数组的某一个索引位置上的元素以链表形式存在的数据个数>8时,且当前数组长度>64时,此索引位置上的所有数据改为使用红黑树存储。
DEFAULT_INITIAL_CAPACITY: HashMap的默认容量---->16
DEFAULT_LOAD_FACTOR: HashMap的默认加载因子---->0.75
threshold: 扩容的临界值=容量*填充因子---->==16×0.75---->12
TREEIFY_THRESHOLD: Bucket中链表长度大于该默认值时,转为红黑树---->8
MIN_TREEIFY_CAPACITY: Node被树化时最小的hash表容量---->64
HashMap会提前扩容,让出现链表的情况尽可能少,有的位置可能永远会为空。