java面试基础知识


"=="和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

throwthrows
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接口。存储数据特点相同,有序,不唯一的数据

不同点:

ArrayListLinkedListVector
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会提前扩容,让出现链表的情况尽可能少,有的位置可能永远会为空。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值