Java面试~基础

一、Java基础

1、基础知识
1.1重载和重写的区别
重载:发生在同一个类中,方法名必须相同,参数类型不同,个数不同,顺序不同时,方法返回值和访问修饰符可以不同,发生在编译时。
重写:发生在父子类中,方法名,参数列表必须相同,返回值范围小于等于父类,抛出的异常范围小于等于父类。访问修饰符范围大于等于父类,如果父类方法访问修饰符为private则子类就不能重写该方法。
1.2 String和StringBuffer,StringBuilder的区别是什么tring为什么是不可变的/strong>
可变性:
简单的来说:String类中使用final关键字字符数组保存字符串,private final char value[] ,所以String对象时不可变的。而StringBuilder和StringBuffer都继承自AbstractStringBuilder类,在AbstractStringBuilder中也是使用字符数组保存字符串 char value[] 但是没有用fianl修饰,所以这两种对象都是可变的。
StringBuilder 与 StringBuffer 的构造方法都是调用父类构造方法也就是
AbstractStringBuilder 实现的.

线程安全性
String 中的对象是不可变的,也就可以理解为常量,线程安全。
AbstractStringBuilder 是 StringBuilder 与 StringBuffer 的公共父类,定义
了一些字符串的基本操作,如 expandCapacity.append.insert.indexOf 等公
共 方法。StringBuffer 对方法加了同步锁或者对调用的方法加了同步锁,所以
是线程安全的。StringBuilder 并没有对
方法进行加同步锁,所以是非线程安全的。
性能

每次对 String 类型进行改变的时候,都会生成一个新的 String 对象,然
后将指针指向新的 String 对象。
StringBuffer 每次都会对 StringBuffer 对象本身进行操作,而不是生成新
的对象并改变对象引用。相同情况下使用 StirngBuilder 相比使用
StringBuffer 仅能获得 10%~15% 左右的性能提升,但却要冒多线程不安全的
风险。
对于三者使用的总结:

  1. 操作少量的数据 = String
  2. 单线程操作字符串缓冲区下操作大量数据 = StringBuilder
  3. 多线程操作字符串缓冲区下操作大量数据 = StringBuffer
    1.3 自动装箱与拆箱
    装箱:将基本类型用它们对应的引用类型包装起来;
    拆箱:将包装类型转换为基本数据类型;
    1.4 == 与 equals
    == : 它的作用是判断两个对象的地址是不是相等。即,判断两个对象是不
    是同一个对象。(基本数据类型比较的是值,引用数据类型比较的是内存
    地址)
    equals() : 它的作用也是判断两个对象是否相等。但它一般有两种使用情
    况:
    情况 1:类没有覆盖 equals() 方法。则通过 equals() 比较该类的两个对
    象时,等价于通过“==”比较这两个对象。
    情况 2:类覆盖了 equals() 方法。一般,我们都覆盖 equals() 方法来两
    个对象的内容相等;若它们的内容相等,则返回 true (即,认为这两个对象相等)。

说明:
String 中的 equals 方法是被重写过的,因为 object 的 equals 方法是
比较的对象的内存地址,而 String 的 equals 方法比较的是对象的值。
当创建 String 类型的对象时,虚拟机会在常量池中查找有没有已经存在的
值和要创建的值相同的对象,如果有就把它赋给当前引用。如果没有就在常量池
中重新创建一个 String 对象。
1.5 关于 final 关键字的一些总结
final 关键字主要用在三个地方:变量、方法、类。

  1. 对于一个 final 变量,如果是基本数据类型的变量,则其数值一旦在初始
    化之后便不能更改;如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象。
  2. 当用 final 修饰一个类时,表明这个类不能被继承。final 类中的所有成
    员方法都会被隐式地指定为 final 方法。

可查看堆空间大小分配(年轻代.年老代.持久代分配)
提供即时的垃圾回收功能
垃圾监控(长时间监控回收情况)

对象引用情况查看
有了堆信息查看方面的功能,我们一般可以顺利解决以下问题:

  • 年老代年轻代大小划分是否合理
  • 内存泄漏
  • Dump 线程详细信息:查看线程内部运行情况
    死锁检查
    热点分析

    这是最典型的内存泄漏方式,简单说就是所有堆空间都被无法回收的垃圾对
    象占满,虚拟机无法再在分配新空间。
    如上图所示,这是非常典型的内存泄漏的垃圾回收情况图。所有峰值部分都
    是一次垃圾回收点,所有谷底部分表示是一次垃圾回收后剩余的内存。连接所有
    谷底的点,可以发现一条由底到高的线,这说明,随时间的推移,系统的堆空间
    被不断占满,最终会占满整个堆空间。因此可以初步认为系统内部可能有内存泄
    漏。(上面的图仅供示例,在实际情况下收集数据的时间需要更长,比如几个小
    时或者几天)
    解决:这种方式解决起来也比较容易,一般就是根据垃圾回收前后情况对比,同时根据对象引用情况(常见的集合对象引用)分析,基本都可以找到泄漏点。
    持久代被占满
    异常:java.lang.OutOfMemoryError: PermGen space
    说明:Perm 空间被占满。无法为新的 class 分配存储空间而引发的异常。
    这个异常以前是没有的,但是在 Java 反射大量使用的今天这个异常比较常见了。
    主要原因就是大量动态反射生成的类不断被加载,最终导致 Perm 区被占满。
    更可怕的是,不同的 classLoader 即便使用了相同的类,但是都会对其
    进行加载,相当于同一个东西,如果有 N 个 classLoader 那么他将会被加载 N
    次。因此,某些情况下,这个问题基本视为无解。当然,存在大量 classLoader
    和大量反射类的情况其实也不多。
    解决

    1. -XX:MaxPermSize=16m
    2. 换用 JDK。比如 JRocket
      堆栈溢出
      异常:java.lang.StackOverflowError
      说明:这个就不多说了,一般就是递归没返回,或者循环调用造成
      线程堆栈满
      异常:Fatal: Stack size too small
      说明:java 中一个线程的空间大小是有限制的。JDK5.0 以后这个值是 1M。
      与这个线程相关的数据将会保存在其中。但是当线程空间满了以后,将会出现上
      面异常。
      解决:增加线程栈大小。-Xss2m。但这个配置无法解决根本问题,还要看
      代码部分是否有造成泄漏的部分。
      系统内存被占满
      异常:java.lang.OutOfMemoryError: unable to create new native thread
      说明:这个异常是由于操作系统没有足够的资源来产生这个线程造成的。系
      统创建线程时,除了要在 Java 堆中分配内存外,操作系统本身也需要分配资源
      来创建线程。因此,当线程数量大到一定程度以后,堆中或许还有空间,但是操
      作系统分配不出资源来了,就出现这个异常了。
      分配给 Java 虚拟机的内存愈多,系统剩余的资源就越少,因此,当系统内
      存固定时,分配给 Java 虚拟机的内存越多,那么,系统总共能够产生的线程也
      就越少,两者成反比的关系。同时,可以通过修改-Xss 来减少分配给单个线程
      的空间,也可以增加系统总共内生产的线程数。
      解决
    3. 重新设计系统减少线程数量。

    2. 什么时候回收/strong>
    引用计数法
    可达性分析
    2.1 引用计数法
    给对象添加一个引用计数器,每当有一个地方引用它时,计数器加一。反
    之每当一个引用失效时,计数器减一。当计数器为 0 时,则表示对象不被引用。
    举个例子:

    但是,引用计数法不能解决对象之间的循环引用,见下例

    2.2 可达性分析
    设立若干根对象(GC Root),每个对象都是一个子节点,当一个对象找
    不到根时,就认为该对象不可达。

    3.2 复制算法
    将内存分为两块,每次只使用一块。当这一块内存满了,就将还存活的对象
    复制到另一块上,并且严格按照内存地址排列,然后把已使用的那块内存统一回
    收。
    优点是:能够得到连续的内存空间
    缺点是:浪费了一半内存

    Java面试~基础
    补充:java finalize()方法:
    在被 GC 回收前,可以做一些操作,比如释放资源。有点像析构函数,但是
    一个对象只能调用一次 finalize()方法。
    2. Java 集合框架
    2.1 ArrayList 与 LinkedList 异同
    1. 是否保证线程安全: ArrayList 和 LinkedList 都是不同步的,也就是不
    保证线程安全;
    2. 底层数据结构: ArrayList 底层使用的是 Object 数组;LinkedList 底
    层使用的是双向链表数据结构(JDK1.6 之 前为循环链表,JDK1.7 取消了循环。
    注意双向链表和双向循环链表的区别:);
    3. 插入和删除是否受元素位置的影响① ArrayList 采用数组存储,所
    以插入和删除元素的时间复杂度受元素位置的影响。
    比如:执行 add(E e) 方法的时候, ArrayList 会默认在将指定的元素追加到此列表的末尾,这种情况时间复杂度就是 O(1)。但是如果要在指定位置 i 插入和删除元素的话( add(int index, E element) )时间复杂度就为 O(n-i)。因为在进行上述操作的时候集合中第 i 和第 i 个元素之后的(n-i)个元素都要执行向后位/向前移一位的操作。 ② LinkedList 采用链表存储,所以插入,删除元素时间复杂度不受元素位置的影响,都是近似 O(1)而数组为近似 O(n)。
    4. 是否支持快速随机访问: LinkedList 不支持高效的随机元素访问,而
    ArrayList 支持。快速随机访问就是通过元素的序 快速获取元素对象(对应于
    get(int index) 方法)。
    5. 内存空间占用: ArrayList 的空 间浪费主要体现在在 list 列表的结尾会
    预留一定的容量空间,而 LinkedList 的空间花费则体现在它的每一个元素都需
    要消耗比 ArrayList 更多的空间(因为要存放直接后继和直接前驱以及数据)。
    补充内容:RandomAccess 接口

    查看源码我们发现实际上 RandomAccess 接口中什么都没有定义。所以,
    在我看来 RandomAccess 接口不过是一个标识罢了。标识什么标识实现这
    个接口的类具有随机访问功能。
    在 binarySearch()方法中,它要判断传入的 list 是否 RamdomAccess
    的实例,如果是,调用
    indexedBinarySearch()方法,如果不是,那么调用 iteratorBinarySearch()方法

    public static T

    声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!

上一篇 2022年9月21日
下一篇 2022年9月21日

相关推荐