Java基础面试
* 如有错误,请指正。
-
八大基本类型
- 整型:byte、short、int、long
- 浮点型:double、float
- 字符型:char
- 布尔型:boolean
-
装箱和拆箱
- 装箱:从基本类型自动提升到对应的引用类型,为自动装箱
- 拆箱:从引用类型自动转为对应的基本类型,为自动拆箱。
-
面向对象的三大特性
- 继承:指一个类派生出新的类,即子类,有子类继承父类,并拥有父类所包含的属性和方法(使用private修饰的除外),并且子类可以拥有自己的属性和方法。
- 多态:指父类引用指向子类对象,指向不同的子类,拥有不同的方法或属性,如动物类、猫类、狗类,由猫类和狗类继承动物类。当用动物类接收狗类或猫类的对象时,动物类就拥有了猫或狗类的功能,
- 封装:如一个类中定义了对应的属性,将该属性进行私有化,通过提供的get/set来进行调用,这就是封装。
-
String、StringBuffer、StringBuilder的区别
- String:被final所修饰,长度不可变,初始化后不能变化。在定义变量赋值后,重新给该变量赋值,打印时该变量值发生变化。这是因为重新new了一个对象,并将该变量指向了新new出来的对象的地址 。
- StringBuffer:长度可变,线程安全的,被synchronized修饰,性能低。
- StringBuilder:长度可变,线程不安全的,性能高。
- 当经常改变字符串长度时,建议使用StringBuffer或StringBuilder,当有线程安全问题时,使用StringBuffer,线程安全。
-
equals和==的区别
- ==:通常用来比较值类型
- equals:当比较对象时,原则上和==一样,比较地址,一般一样内容的,返回的结果为false。重写后,比较对象的内容,返回结果为true
-
final、finally、finalize的区别
- final:是修饰符关键字,如果一个类被final修饰,那么该类则不能被继承,被修饰的方法不能够重写,修饰的变量不能够被改变。
- finally:try-catch异常块的后面部分,当catch后面有finaly的时候,该finaly块总会被执行。
- finalize:是一个方法名。finalize()是Object中的方法,当垃圾回收器将要回收对象所占内存之前被调用,即当一个对象被虚拟机宣告死亡时会先调用它finalize()方法。
-
抽象类和接口的区别
- 抽象类被关键字abstract修饰,接口关键字为interface。
- 抽象类中可以有非抽象方法,接口中只能是抽象方法(jdk8可以有一个默认的非抽象方法),并且接口中的变量通常为常量,被final修饰。
- 抽象类只能被一个类单继承,关键字为extends,接口可以被一个类多实现,关键字为implements。
-
sleep和wait的区别
- sleep是使线程进入休眠,当休眠时间结束后,自动会进入就绪状态,然后继续执行该线程,不具备改变锁的状态功能。
- wait是是使线程进入等待状态,同时释放锁,必须要去进行唤醒,通常是由notify进行唤醒,或者由notifyAll进行唤醒。notifyAll为唤醒所有线程。
-
多线程
- 创建线程方法一:
- 创建类,继承Thread类,重写run方法。实例化该类,调用start()启动线程。
1 public class ThreadLesson extends Thread{ 2 3 @Override 4 public void run() { 5 //执行代码 6 } 7 8 public static void main(String[] args) { 9 new ThreadLesson().start(); 10 } 11 }
- 创建类,继承Thread类,重写run方法。实例化该类,调用start()启动线程。
- 创建线程方法二:
- 创建一个类,实现RUnable接口,重写run方法,实例化Thread对象,将创建的类实例化,以参数的形式传入,调用start()方法。
1 public class Thread02 implements Runnable { 2 @Override 3 public void run() { 4 //执行代码 5 } 6 7 public static void main(String[] args) { 8 new Thread(new Thread02()).start(); 9 } 10 }
- 创建一个类,实现RUnable接口,重写run方法,实例化Thread对象,将创建的类实例化,以参数的形式传入,调用start()方法。
- 创建线程方法一:
-
集合
- 集合分为Collection和Map,其中Collection分为List和Set。
- List集合
- List集合下分为LinkedList和ArrayList,其中LinkedList底层为双向链表,ArrayList底层为动态数组,并且二者为有序可重复的。
- LinkedList为链表结构,所以不存在扩容情况。
- ArrayList默认大小为10.当检测到容量不够需要扩容时,是按照1.5倍进行扩容,然后之前的数组放入到新数组里面。
- 从头到尾查询时,两者性能差距不大。随机访问时。ArrayList的性能要优于LinkedList,因为LinkedList底层是双向链表需要前后移动,而ArrayList底层是数组,直接通过下标获取。
- 添加数据在头部或者中间部位的时候,LinkedList的性能要优于ArrayList,因为LinkedList只需要断开指针,然后将插入的数据前后连接起来即可。而ArrayList需要找到位置后,将数据加入进去,后面的其他数据都需要往后移动。
- 删除数据的时候LinkedList的性能要优于ArrayList,因为当删除某个数据的时候,LinkedList只需要断开前后指针,然后连接即可,而ArrayList删除后,需要移动后面的其他数据往前移动。
- 注意:具体情况具体分析,当数据量过大时,可能LinkedList的性能将低于ArrayList,因为在LinkedList在移动指针时可能会耗费大量的时间。
- Set集合
- Set集合分为HashSet和TreeSet,HashSet的底层为哈希表,TreeSet的底层为二叉树。
- HashSet为无序且不可重复的,TreeSet默认是排好序的,且不可重复的。
- HashSet源码中,构造函数对HashMap进行实例化的。底层原理:当存入一个key-value的时候,会先对key进行哈希值获取,根据哈希值将数据放到对应的位置中,如果发现该位置已经有数据了,则会用链表的形式将数据链接起来;没有数据直接放到该位置。
- TreeSet源码中,构造函数实例化了TreeMap。底层原理:当存入一个数据时,会按照指定的顺序进行排序,每增加一条数据,进行对比排序一次。
- Map集合
- HashMap:看HashSet,HashSet的底层跟HashMap一样,HashSet由HashMap进行了包装,允许键值对为null,线程是不安全的。
- TreeMap:看TreeSet,TreeSet底层同TreeMap一样,TreeSet由TreeMap进行了包装。
- HashTable:线程安全的,不允许键值对为null。
- HashMap和HashTable的区别
- HashTable是线程安全的,HashMap是线程不安全的。
- HashTable的key和value不允许为null,HashMap的key和value可以为null。
- HashTable继承Dictionary,HashMap继承Map。
- HashMap 的迭代器(Iterator)是 fail-fast 迭代器,HashTable的 enumerator 迭代器不是 fail-fast 的。
- HashTable是直接使用了对象的hashCode,而HashMap是重新计算了hash值。
- HashTable在不指定容量的情况下,默认容量为11,而HashMap的默认容量是16。
- Hashtable不要求底层数组的容量一定要为2的整数次幂,而HashMap则要求一定为2的整数次幂。
- Hashtable扩容时,将容量变为原来的2倍加1,而HashMap扩容时,将容量变为原来的2倍。
- ConCurrentHashMap:线程安全的,在多线程情况下,使用ConCurrentHashMap而不使用HashTable,ConCurrentHashMap不允许键值对为null。ConcurrentHashMap 对整个桶数组进行了分割分段(Segment),然后在每一个分段上都用 lock 锁进行保护,相对于Hashtable 的 syn 关键字锁的粒度更精细了一些,并发性能更好。因为ConCurrentHashMap采取分段加锁,所以当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问。Hashtable的线程都必须竞争同一把锁,同一时间点只能有一个线程持有。
- List集合