Java面试题笔记1

finalize:java中重写finalize方法中如果写了很多复杂的逻辑,当程序运行时,新的对象不断生成,而老的对象销毁的很慢,就会造成OOM(内存溢出):Out of Memory

栈溢出(StackOverflowError):栈溢出就是方法执行时创建的栈帧超过了栈的深度。那么最有可能的就是方法递归调用产生这种结果。(方法递归调用不停地产生栈帧)

堆溢出(OutOfMemoryError):heap space表示堆空间,堆中主要存储的是对象。如果不断的new对象则会导致堆中的空间溢出

直接new对象就是强引用

SoftReference:软引用
当内存不够用了,软引用就会被干掉。软引用非常适合缓存使用

WeakReference:弱引用
垃圾回收器看到了,就会被干掉 //System.gc();

PhantomReference:虚引用
直接get不到。 一般和队列结合使用,一般不用

面向对象:将方法封装在对象中,明确什么对象做什么事。
面向过程:每个功能都写个方法,更注重算法。

继承
封装
多态:体现:方法的重载和重写。一个父类引用指向子类的对象。

JDK:Java Development Kit,java开发工具包,提供Java的开发环境和运行环境
JRE:Java Runtime Environment。Java运行环境,包括java虚拟机和一些基础类库
JVM:Java Virtual Machine,java虚拟机。提供执行字节码文件的能力。
Java源文件——>编译(javac.exe)——>class字节码文件(jvm文件)——>运行——>JVM——>翻译(java.exe)——>操作系统

==:比较基本数据类型比较的是数值;比较引用类型比较的是地址。
equals:默认比较也是地址,Object类中的equals方法默认比较的是地址。

public boolean equals(Object obj) {
        return (this == obj);
    }

如果需要比较内容,就要重写equals方法。

final:修饰类,类不可被继承;修饰方法,方法不可被重写,但是可以被重载!
修饰变量,变量就是常量(变量是对象的话,那么该对象的地址不可变,对象内容可以变,也就是堆内存中的数据可以变)。
重载(Overload):方法名相同,参数列表不同,返回值类型及修饰符可以相同,也可以不同。
重写(Override):两个方法的返回值、方法名、参数的类型和个数相同(子类重写父类的方法)。关于权限:子类的权限必须大于等于父类方法的权限,且父类private方法不能被重写。
★重载必须是参数列表不同,跟返回类型没关系:
以下不构成重载:(会报错)
public double add(int a,int b)
public int add(int a,int b)

String,StringBuffer,StringBuilder区别:
String:

final修饰的类,String类是final的,不过跟值能否改变没有任何关系,一个类被定义为final类是说明该这个类不能被继承
String对象每次修改内容都需要创建新的对象:
private final char value[];底层使用的char value[]数组,构造函数中都是给这个用final修饰的value数组赋一个数组的地址值,如果再修改String对象就相当于修改value的地址值,但final修饰的value的地址值是不可被改变的,所以需要创建新的对象。(String类中很好的保护了value数组的不可变性,类中完全没有设置value[0]='x’的赋值方式)

String类中的方法:equals() toString() charAt() isEmpty() length() compareTo() hashcode() toLowerCase() toUpperCase() format() valueOf()…

StringBuffer:

public final class StringBuffer extends AbstractStringBuilder;继承的AbstractStringBuilder类,这个类中定义的value数组是char[] value;
构造函数赋值:
AbstractStringBuilder(int capacity) {
value = new char[capacity];
}
所以StringBuffer是可变字符串,默认分配数组长度为16;public StringBuffer(int capacity),也可在构造函数中自定义长度;如果构造函数中添加字符串则数组长度为字符串长度+16.

基本增加删除操作方法:append(),delete();
StringBuffer是线程安全的(对数组的操作用了同步方法),效率会较低。

StringBuilder:

基本原理跟StringBuffer相同,但是是线程不安全的,效率较高。

什么时候考虑线程安全的问题?

当多线程访问同一个资源的时候。

接口和抽象类的区别:
抽象类(abstract):

修饰类,类不可以实例化;修饰方法,方法没有方法体;不可修饰属性;
包括抽象方法的类,一定是抽象类;
抽象类中可以有普通方法;
抽象类中可以有构造函数,但是只能被子类调用。
abstract不可与static、private、final同时用,因为抽象方法要被重写。(synchronized、native也不能与abstract同时用)

接口(interface):

接口中的方法默认修饰符为 public abstract;即默认为抽象方法;
接口中的变量默认修饰符为 static final;即默认为全局常量;
jdk8中也可以在接口中定义静态方法和default 修饰的默认方法;
接口中不能定义构造器,意味着接口不可以实例化;
Java类可以实现多个接口,弥补了Java单继承的局限性;
接口与接口之间可以继承,而且可以多继承.

递归求N的阶层:

//递归求n的阶层,输入不为负数时
    public static int fun(int n){
        if(n==1||n==0)
            return n;
        else {
            return n*fun(n-1);
        }
    }

求解斐波那契数列的第N个数是几?

 //规律:1,1,2,3,5,8,13,21......
    public static int fun1(int n){
        if(n==1||n==2)
            return 1;
        else {
            return fun1(n-2)+fun1(n-1);
        }
    }

Integer:

 public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }

i值在-128-127的区间内,把变量i当做一个变量,放到内存中;但如果不在这个范围内,就会去new一个Integer对象。

		Integer i1 = new Integer(12);
        Integer i2 = new Integer(12);
        System.out.println(i1 == i2);//false
        
        Integer i3 = 126;//Integer.valueOf(126),自动装箱
        Integer i4 = 126;
        int i5 = 126;
        System.out.println(i3 == i4);//true
        System.out.println(i3 == i5);//true 自动拆箱,转化为int数值比较

        Integer i6 = 128;
        Integer i7 = 128;
        int i8 = 128;
        System.out.println(i6 == i7);//false  new Integer(i)
        System.out.println(i6 == i8);//数值

List和Set的区别:

都是继承了Collection接口,但List存储有序、可重复的数,Set存储无序、不可重复的数。

集合框架
|----Collection接口:单列集合,用来存储一个一个的对象
  |----List接口:存储有序的、可重复的数据。–>“动态”数组
    |----(类)ArrayList:作为list接口的主要实现类,线程不安全的,效率高;底层使用Object[] elementData存储
    |----LinkedList:对于频繁的插入、删除操作,使用此类效率比ArrayList高;底层使用双向链表存储
    |----Vector:作为list接口的古老实现类,线程安全的,效率低;底层使用Object[] elementData存储
  |----Set接口:存储无序的、不可重复的数据。
    |----HashSet:作为Set接口的主要实现类;线程不安全的,可以存储null值
      |----(HashSet子类)LinkedHashSet:作为HashSet的子类;遍历其内部数据时,可以按照添加的顺序遍历
    |----TreeSet:可以按照添加对象指定属性,进行排序。

|----Map接口:双列集合,用来存储一对(key - value)一对的数据。 -->y = f(x)—(x对应key,y对应value)
  |----HashMap:Map的主要实现类;线程不安全的,效率高;可以存储null的key和value
    |----LinkedHashMap:保证在遍历map元素时,可以按照添加的顺序实现遍历。
原因:在原有的HashMap底层结构基础上,添加了一对指针,指向前一个和后一个元素。
对于频繁的遍历操作,此类执行效率高于HashMap。
  |----TreeMap:保证按照添加的key-value对进行排序,实现排序遍历。此时考虑key的自然排序或定制排序
底层使用的是红黑树
  |----Hashtable:Map的古老实现类;线程安全的,效率低;不能存储null的key和value
    |----Properties:常用来处理配置文件。key和value都是String类型

throw:生成一个异常对象,并抛出。使用在方法内部<->自动抛出异常对象
throws:处理异常的方式。使用在方法声明处的末尾<->try-catch-finally

向Collection接口的实现类的对象中添加数据obj时,要求obj所在类重写equals()。

一、List接口
ArrayList、LinkedList、Vector三者的异同?
相同:三个类都实现了List接口,存储数据的特点相同:存储有序的、可重复的数据。
不同:见上。

1.ArrayList的源码分析:jdk 7情况下

ArrayList list = new ArrayList();//底层创建了长度是10的Object[]数组elementData
List.add(123);//elementData[0] = new Integer(123);

List.add(11);//如果此次的添加导致底层elementData数组容量不够,则扩容。
默认情况下,扩容为原来的容量的1.5倍,同时需要将原有数组中的数据复制到新的数组中。
结论:建议开发中使用带参的构造器:ArrayList list = new ArrayList(int capacity)

2.jdk 8中ArrayList的变化:

ArrayList list = new ArrayList();//底层Object[] elementData初始化为{},并没有创建长度为10的数组
List.add(123);//第一次调用add()时,底层才创建了长度10的数组,并将数据123添加到elementData[0]

后续的添加扩容操作与jdk 7无异。

小结:jdk7中的ArrayList的创建类似于单例的饿汉式,而jdk8中的ArrayList的对象的创建类似于单例的懒汉式,延迟了数组的创建,节省内存。

LinkedList的源码分析:

LinkedList list = new LinkedList();//内部声明了Node类型的first和last属性,默认值为null
Link.add(123);//将123封装到Node中,创建了Node对象。

注意区分List中remove(int index)和remove(Object obj)

二、Set接口
1.Set接口中没有额外定义新的方法,使用的都是Collection中声明过的方法。

以HashSet为例说明:

无序性:不等于随机性。存储的数据在底层数组中并非按照数组索引的顺序添加,而是根据数据的哈希值决定的。(添加的时候存储的位置不是一个挨着一个放的)
不可重复性:保证添加的元素按照equals方法判断时,不能返回true。即,相同的元素只能添加一个。

添加元素的过程:以HashSet为例:

我们向HashSet中添加元素a,首先调用这个元素a所在类的hashCode方法,计算元素a的哈希值,此哈希值通过某种算法计算出在HashSet底层数组中的存放位置(即为:索引位置),判断数组此位置上是否已经有元素,
如果此位置上没有其他元素,则元素a添加成功。----情况1
如果此位置上有其他元素b(或以链表形式存在的多个元素),则比较元素a与元素b的hash值:如果hash值不相同,则元素a添加成功。----情况2
如果hash值相同,进而需要调用元素a所在类的equals()方法,equals()返回true,元素a添加失败;equals()返回false,则元素a添加成功。----情况3
对于添加成功的情况2和情况3而言,元素a与已经存在指定索引位置上数据以链表的方式存储。jdk8中,原来的元素在数组中,指向元素a。

要求:向Set中添加的数据,其所在的类一定要重写hashCode()和equals()
要求:重写的hashCode()和equals()尽可能保持一致性。

HashSet底层:数组+链表的结构

LinkedHashSet:作为HashSet的子类,在添加数据的同时,每个数据还维护了两个变量,记录此数据前一个数据和后一个数据。
优点:对于频繁的遍历操作,LinkedHashSet效率高于HashSet

TreeSet:
1.向TreeSet中添加的数据,要求是相同类的对象。
2.两种排序方式:自然排序(实现Comparable接口)和定制排序(Comparator)
3.自然排序中,比较两个对象是否相同的标准为:compareTo()返回0,不再是equals().
4.定制排序中,比较两个对象是否相同的标准为:compare()返回0,不再是equals().

三、HashMap:
1.底层:
数组+链表(jdk 7)
数组+链表+红黑树(jdk 8)

2.HashMap的底层实现原理?以jdk7为例说明:
HashMap map = new HashMap();
在实例化以后,底层创建了长度是16的一维数组Entry[] table。
…可能已经执行过多次put…
map.put(key1,value1):
首先,调用key1所在类的hashCode()计算key1的哈希值,此哈希值经过某种算法计算以后,得到在Entry数组中的存放位置。
如果此位置上的数据为空,此时的key1-value1添加成功。----情况1
如果此位置上的数据不为空,意味着此位置上存在一个或多个数据(以链表形式存在),比较key1和已经存在的一个或多个数据的哈希值:
  如果key1的哈希值与已经存在的数据的哈希值都不相同,此时key1-value1添加成功。----情况2
  如果key1的哈希值和已经存在的某一个数据(key2-value2)的哈希值相同,继续比较:调用key1所在类的equals(key2)方法,比较:
    如果equals()返回fales:此时key1-value1添加成功。----情况3
    如果equals()返回true:使用value1替换value2。
补充:关于情况2和情况3:此时key1-value1和原来的数据以链表的方式存储。

在不断的添加过程中会涉及到扩容问题,默认的扩容方式:扩容为原来容量的2倍,并将原来的数据复制过来。

jdk8 相较于jdk7在底层实现方面的不同:
1.new HashMap():底层没有创建一个长度为16的数组
2.jdk8底层的数组是:Node[],而非Entry[]
3.首次调用put()方法时,底层创建长度为16的数组
4.jdk7底层结构只有:数组+链表。jdk8中底层结构:数组+链表+红黑树。
当数组的某一个索引位置上的元素以链表形式存在的数据个数 > 8 且当前数组的长度 > 64时,
此时此索引位置上的所有数据改为使用红黑树存储。

DEFAULT_INITIAL_CAPACITY:HashMap的默认容量,16
DEFAULT_LOAD_FACTOR:HashMap的加载因子,0.75
threshold:扩容的临界值,= 容量填充因子:160.75 =>12
TREEIFY_THRESHOLD:Bucket中链表长度大于该默认值,转化为红黑树:8
MIN_TREEIFY_CAPACITY:桶中的Node被树化时最小的hash表容量:64

3.Map结构的理解
Map中的key:无序的、不可重复的,使用Set存储所有的key。---->key所在的类要重写equals()和hashCode()(以HashMap为例)
Map中的value:无序的、可重复的,使用Collection存储所有的value。---->value所在的类要重写equals()
一个键值对:ket-value构成了一个Entry对象。
Map中的entry:无序的、不可重复的,使用Set存储所有的entry。

Map接口不能使用迭代器进行元素的遍历。

4.TreeMap:
向TreeMap中添加key-value,要求key必须是由同一个类创建的对象
因为要按照key进行排序:自然排序、定制排序

Map接口常用方法:
添加:put(Object key,Object value)
删除:remove(Object key)
修改:put(Object key,Object value)
查询:get(Object key)
长度:size()
遍历:keySet()/values()/entrySet()

四、Collections工具类
Collections:操作Collection、Map工具类

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值