Java基础面试题整理(一)

讲一下Java的几个特性

  • 抽象: 就是将现实生活中的某一类东西提取出来,通常称为类或方法。抽象出来的代码包括两方面:一个是数据抽象,一个是过程抽象。数据抽象也就是对象的属性,过程抽象是对象的行为特征。
  • 封装: 通过代码将抽象事物封装成抽象的类,并且类可以把自己的数据和方法只让其他可信的类或者对象操作,对不可信的进行封装隐藏。封装可以分为属性的封装和方法的封装。
  • 继承: 是对有着共同特性的多类事物,再抽象成一个类。这个类就是多类事物的父类,父类的意义在于抽取多类事物的共性;
  • 多态: 允许不同类的对象对统一消息做出响应。方法的重载、类的覆盖正体现了多态。

重载和重写的区别

  • 重载: 重载发生在同一个类中,方法名必须相同,参数类型不同、个数不同、顺序不同,方法返回值和访问修饰符可以不同;
  • 重写: 发生在父子类中、方法名、参数列表必须相同,返回值类型,抛出的异常小于等于父类,访问修饰符大于等于父类,如果父类方法访问修饰符为private则子类中就不是重写;

父类的静态方法可以被子类重写?静态属性和静态方法是否可以被继承?

  • 父类的静态方法和属性不能被子类重写,但子类可以继承父类静态方法和属性,如父类和子类都有同名同参,
    如:Father son = new Son(); //Son extends Father,会调用Father对象的静态方法,静态是指在编译时就会分配内存且一直存在,跟对象实例无关;

构造方法可以被重写么?

构造器不能被重写,不能用static修饰构造器,只能用public,private、protected这三个权限,且不能有返回语句

String和StringBuffer、StringBuilder的区别

  1. 可变性:String类中使用字符数组保存字符串,private final char value[],String对象是不可变的,StringBuilder和StringBuffer都继承自AbstractStringBuilder,在AbstractStringBuilder中也是使用字符数组保存字符串,char[] value,这两种对象都是可变的。
  2. 线程安全性: String对象是不可变的,也就可以理解为常量,线程安全。AbstractStringBuilder是StringBuilder与StringBuffer的公共父类,定义了一些字符串的基本操作,如expandCapacity、append、inser、indexOf等公共方法。StingBuffer对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。StringBuilder并没有对方法进行加同步锁,所以是非线程安全的。
  3. 性能:
    • 每次对String类型改变的时候,都会生成一个新的String对象,然后将指针指向新的String对象。
    • StringBuffer每次都会对StringBuffer对象本身进行操作,而不是生成新的对象并改变对象引用;
    • 相同情况下,StirngBuilder相比使用StringBuffer 仅能获得10%~15% 左右的性能提升,但却要冒多线程不安全的风险。

hashCode方法、euqals方法 、==的关系

  1. hashcode()方法跟equals()方法都是判断两个对象是否相等;
  2. 两个对象相同,则hashcode一定要相同,即对象相同–>成员变量相同–>hashcode值一定相同;
  3. 两个对象hashcode值相同,对象不一定相同。总结:equals相等则hashcode一定相等,hashcode相等,equals不一定相等。
  4. ==比较的是两个引用在内存中指向的是不是同一对象(即统一内存空间),==若是基本类型比较,是比较值,若是引用类型,则比较,则比较的是他们在内存中的存放地址。对象存放在堆中,栈中存放的对象的引用,所以 == 是对栈中的进行比较,若返回true表示变量的内存地址相同;

Error、Exception区别

  • Error类和Exception类的父类都是Throwable类,区别是
    • Error类一般是指与虚拟机相关的问题,如系统崩溃、虚拟机错误、内存空间、方法调用栈溢出等。对于这类错误的导致的应用程序中断,仅靠程序本身无法恢复和预防,遇到这样的错误;
    • Exception类表示程序可以处理的异常,可以捕获且可能恢复,遇到这类异常应该尽可能处理异常,使程序恢复运行,而不应该随意终止异常;

简述下进程、程序、线程的基本概念以及他们之间的关系

  • 程序是含有指令和数据的文件,存在磁盘或其他数据存储设备商,也就是说程序就是静态的代码;
  • 进程是程序的一次执行过程,是系统程序运行的基本单位。因此进程是动态的,系统运行一个程序即是一个进程从创建、运行到消亡的过程。简单来说,一个进程就是一个执行中的程序,它在计算机中一个指令接着一个指令去执行着。同时每个进程还占有某些系统资源如CPU时间、内存空间、文件、输入输出设备的使用权。
  • 线程是进程划分成的更小的基本单位。进程和线程最大的不同在于进程之间是独立的,但是线程则不一定,同一个进程之内可能包含多个线程,进程属于操作系统的范畴;

线程的基本状态(六个)

  • NEW: 初始状态,线程被构建,但还是没有调用start()方法;
  • RUNNABLE:运行状态;
  • BLOCKED:阻塞状态,表示线程阻塞于锁;
  • WAITING:等待,表示线程进入等待状态
  • TIME_WAITING: 超时等待状态;
  • TERMINATED: 终止状态;
    在这里插入图片描述

从这张图可以看出:线程创建之后它将处于NEW(新建状态),调用start()方法后开始运行,线程现在处于READY(可运行)状态,可运行状态获得了cpu资源就处于RUNNING(运行)状态;
当线程执行wait()方法之后,线程进入WAITINE(等待)状态,进入等待状态的线程需要依靠其他线程的通知才能够返回到运行状态,而TIME_WAITING(超时等待)状态相当于在等待状态的基础上增加了超时限制,比如通过sleep(long mills)方法或者wait(long mills)方法将Java线程置于TIME_WAITING状态。当超时时间达到之后Java线程会返回RUNNABLE状态,当线程调用同步方法时候,在没有获取到锁的情况下,线程会进入BLOCKED状态。线程在执行Runnable的run()方法之后进入到终止状态

简单说下深拷贝vs浅拷贝

  • 浅拷贝: 对基本数据类型进行值传递,对引用数据类型进行引用传递般的拷贝;
  • 深拷贝: 对基本数据类型进行值传递,对引用数据类型,创建一个新的对象,并复制其内容;

Unchecked异常(非受检异常)

  • Unchecked Exception(非运行时异常):
    • 运行时异常指的是程序的瑕疵或逻辑错误,并且在运行时无法恢复
    • 包括Error以及RuntimeException及其子类,如OutOfMemoryException,UndeclaredThrowableException, IllegalArgumentException,
      IllegalMonitorStateException, NullPointerException, IllegalStateException等;
    • 语法上不需要抛出异常;

Checked Exception(受检异常)

  • 代表程序不能直接控制的无效外界情况,需要手动处理的异常(try catch或throws);
  • 除了Error和RuntimeException及其子类外,如:ClassNotFoundException、NamingException, ServletException, SQLException, IOException等;

讲一下Java中的集合类及关系图

  • List和Set都继承自Collection接口;
  • Set无序且不允许元素重复,HashSet与TreeSet是两个主要的实现类;
  • List有序且允许元素重复,ArrayList和LinkedList和Vector是三个主要实现类;
  • Map也属于集合,与Collection接口没关系,Map是key对value的映射集合,其中key列就是一个结合,key不能重复,value可重复,HashMap、TreeMap和HashTable 是三个主要实现类
  • SortedSet和SortedMap接口对元素按指定规则排序,SortedMap是对key列进行排序

ArrayList与LinkedList的存储性能和特性;

  • 是否保证线程安全: ArrayListLinkedList都是不同步的,也就是不保证线程安全;
  • 底层数据结构:ArrayList底层使用的Object数组;LinkedList底层使用的是双向链表
  • 插⼊和删除是否受元素位置的影响:
    • ArrayList 采⽤数组存储,所以插⼊和删除元素的
      时间复杂度受元素位置的影响;
    • LinkedList 采⽤链表存储,所以对于 add(E e) ⽅法的插⼊,删除元素时间复杂度不受元素
      位置的影响,近似 O(1),如果是要在指定位置 i 插⼊和删除元素的话( (add(int index, E
      element) ) 时间复杂度近似为 o(n)) 因为需要先移动到指定位置再插⼊。
  • 是否支持快速随机访问ArrayList支持;
  • 内存空间占用: ArrayList的空间浪费体现在list列表的结尾要预留一定的容量空间;LinkedList的空间花费体现在它的每一个元素存储都要消耗比ArrayList更多的空间;

HashMap和HashTable的区别

  1. 线程是否安全: HashMap是⾮线程安全的, HashTable 是线程安全的,因为 HashTable 内部的⽅法基本都经过 synchronized 修饰。

  2. 效率: 因为线程安全的问题, HashMap 要⽐ HashTable 效率⾼⼀点。

  3. 对Null key和Null value的支持: HashMap可以存储null的key和value,HashTable不允许有null键和null值;

  4. 初始容量大小和每次扩容容量大小不同: ① 创建时如果不指定容量初始值, Hashtable默认的初始⼤⼩为 11,之后每次扩充,容量变为原来的 2n+1。 HashMap 默认的初始化⼤⼩为 16。之后每次扩充,容量变为原来的2 倍。② 创建时如果给定了容量初始值,那么
    Hashtable 会直接使⽤你给定的⼤⼩,⽽ HashMap 会将其扩充为 2 的幂次⽅⼤⼩( HashMap 中的 tableSizeFor() ⽅法保证,下⾯给出了源代码)。也就是说 HashMap 总是使⽤ 2 的幂作为哈希表的⼤⼩;
    下图表示保证了HashMap总是使用2的幂次方作为哈希表大小:
    在这里插入图片描述

  5. 底层数据结构: JDK1.8 以后的 HashMap 在解决哈希冲突时有了较⼤的变化,当链表⻓度⼤于阈值(默认为 8)(将链表转换成红⿊树前会判断,如果当前数组的⻓度⼩于 64,那么会选择先进⾏数组扩容,⽽不是转换为红⿊树)时,将链表转化为红⿊树,以减少搜索时间。Hashtable 没有这样的机制。

HashMap和HashSet的区别

HashSet底层就是基于HashMap实现的。
在这里插入图片描述

HashSet如何检查数据重复

当有一个对象加入HashSet时候,HashSet 会先计算对象的 hashcode 值来判断对象加⼊的位置,同时也会与其他加⼊的对象hashcode 值作⽐较,如果没有相符的 hashcode , HashSet 会假设对象没有重复出现。但是如果发现有相同 hashcode 值的对象,这时会调⽤ equals() ⽅法来检查hashcode 相等的对象是否真的相同。如果两者相同, HashSet 就不会让加⼊操作成功。

介绍一下HashMap的底层实现

DK1.8 之后在解决哈希冲突时有了较⼤的变化,当链表⻓度⼤于阈值(默认为 8)(将链表转换成红⿊树前会判断,如果当前数组的⻓度⼩于 64,那么会选择先进⾏数组扩容,⽽不是转换为红⿊树)时,将链表转化为红⿊树,以减少搜索时间。

HashMap 的⻓度为什么是2的幂次⽅

为了能让 HashMap 存取⾼效,尽量较少碰撞,也就是要尽量把数据分配均匀。我们都知道为了找到 KEY 的位置在哈希表的哪个槽里面,需要计算 hash(KEY) % 数组长度,但是 % !的计算速度效率是远低于&,所以,为了保证 & 的计算结果等于 % 的结果需要把 length 减 1
取余(%)操作中如果除数是2的幂次则等价于与其除数减⼀的与(&)操作(也就是说 hash%length==hash&(length-1)的前提
是 length 是2的 n 次⽅;)。” 并且 采⽤⼆进制位操作 &,相对于%能够提⾼运算效率,这就解释了 HashMap 的⻓度为什么是2的幂次⽅。

ConcurrentHashMap 和 Hashtable 的区别

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值