Java 后台开发面试题分享五

线程的 start 方法跟 run 方法的区别?

  1. start() 方法来启动线程,真正实现了多线程运行。这时无需等待 run() 方法体代码执行完毕,可以直接继续执行下面的代码。
  2. 通过调用 Thread 类的 start() 方法来启动一个线程, 这时此线程是处于就绪状态, 并没有运行。
  3. 方法 run() 称为线程体,它包含了要执行的这个线程的内容;当线程进入了运行状态,就开始运行 run() 函数当中的代码;当 run() 方法运行结束,此线程终止,然后 CPU 再调度其它线程。

list 和 map 的数据结构

List 属于单列集合;存储的是有序,可重复的元素。ArrayList、Vector、LinkedList 都实现了 list。ArrayList 和 Vector 底层使用了数组,查询快,增删慢;LinkedList 底层使用的是双向链表,查询慢,增删快。ArrayList 和 LinkedList 线程不安全,效率高;Vector 线程安全,效率低。

Map 是双列集合;存储的是键值对形式的元素,键唯一,值可重复;其数据结构仅仅对键有效,与值无关。HashMap、LinkedHashMap、Hashtable、TreeMap 都实现了 Map。HashMap 和 Hashtable 底层是哈希表;LinkedHashMap 底层是基于哈希表增加了双向链表记录迭代顺序;TreeMap 底层是红黑树。


同步机制 Sychronized 怎么实现同步的

Java 中每一个对象都可以作为锁,这是 synchronized 实现同步的基础。当一个线程访问同步代码块时,它首先是需要得到锁才能执行同步代码,当退出或者抛出异常时必须要释放锁。

Java 中同步代码块是使用 monitorenter 和 monitorexit 指令实现。同步方法依靠的是方法修饰符上的 ACC_SYNCHRONIZED 实现。

同步代码块:使用 MonitorEnter 和 MoniterExit 指令实现的,在编译时,monitorenter 指令插入到同步代码块的开始位置,monitorexit 指令插入到同步代码块的结束位置。JVM 需要保证每一个 monitorenter 都有一个 monitorexit 与之相对应。任何对象都有一个 monitor 与之相关联,当且一个 monitor 被持有之后,它将处于锁定状态。线程执行到 monitorenter 指令时,将会尝试获取对象所对应的 monitor 所有权,即尝试获取对象的锁。

同步方法:依赖 flags 标志 ACC_SYNCHRONIZED 实现,字节码中没有具体的逻辑,需要查看 JVM 的底层实现(同步方法也可以通过 Monitor 指令实现)。ACC_SYNCHRONIZED 标志表示方法为同步方法,如果为非静态方法(没有 ACC_STATIC 标志),使用调用该方法的对象作为锁对象;如果为静态方法(有 ACC_STATIC 标志),则使用该方法所属的 Class 类在 JVM 的内部对象表示 Klass 作为锁对象。


Java 内存垃圾回收前,如何判定某一对象是垃圾?

  1. 引用计数法:在 Java 中,引用和对象是有关联的。如果要操作对象则必须用引用进行。因此,可以通过引用计数来判断一个对象是否可以回收。简单说,即一个对象如果没有任何与之关联的引用,则说明对象不太可能再被用到,那么这个对象就是可回收对象。
  2. 可达性分析:为了解决引用计数法的循环引用问题,Java 使用了可达性分析的方法。通过一系列的 “GC roots” 对象作为起点搜索。如果在 “GC roots” 和一个对象之间没有可达路径,则称该对象是不可达的。要注意的是,不可达对象不等价于可回收对象,不可达对象变为可回收对象至少要经过两次标记过程。两次标记后仍然是可回收对象,则将面临回收。

HashMap 在 Java 7 跟 Java 8 中实现的不同点有哪些?

JAVA 7 实现:

大方向上,HashMap 里面是一个数组,然后数组中每个元素是一个单向链表。如图,每个绿色的实体是嵌套类 Entry 的实例,Entry 包含四个属性:key, value, hash 值和用于单向链表的 next。

  1. capacity:当前数组容量,始终保持 2^n,可以扩容,扩容后数组大小为当前的 2 倍。
  2. loadFactor:负载因子,默认为 0.75。
  3. threshold:扩容的阈值,等于 capacity * loadFactor。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6PdTfVVD-1603709839432)(img\ms-35-1.png)]

JAVA 8 实现:

  • 对 HashMap 进行了一些修改,最大的不同就是利用了红黑树,所以其由“数组+链表+红黑树”组成,如下图所示。
  • 在 Java 7 HashMap 查找的时候,根据 hash 值能够快速定位到数组的具体下标,但是之后的话,需要顺着链表一个个比较下去才能找到需要的,时间复杂度为 O(n) 取决于链表的长度。为了降低这部分的开销,在 Java 8 中,当链表中的元素超过了 8 个以后,会将链表转换为红黑树,在这些位置进行查找的时候可以降低时间复杂度为 O(logn)。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-H9mxLh5j-1603709839436)(img\ms-35-2.png)]


Java 中的检查型异常和非检查型异常有什么区别?

Error 错误:虚拟机内部的错误,通过代码无法解决。

Exception 异常:通过代码可以解决。

  1. 运行时异常, 又叫非检查型异常,指的是 RuntimeException 和 RuntimeException 的子类;运行的时候出现的问题,可以处理,也可以不处理。
  2. 非运行时异常,又叫检查型异常,指的是 Exception 和 Exception 的子类(除了 RuntimeException);在编译期间就必须处理。

检查型异常和非检查型异常的主要区别在于其处理方式。检查型异常都需要使用 try,catch 和 finally 关键字在编译器进行处理,否则会出现编译器报错;而非检查型异常则不需要这样做。


获取 class 对象的常用方式有哪些?

  1. 调用某个对象的 getClass() 方法,如 Person p = new Person(); Class c = p.getClass();
  2. 调用某个类的 class 属性来获取该类对应的 Class 对象,如 Class c = Person.class;
  3. 使用 Class 类中的 forName() 静态方法,这是最安全且性能最好的,也是最常用的,如 Class c = Class.forName("类的全路径");

利用反射创建对象的两种方式是什么?

  1. 使用 Class 对象 newInstance() 方法来创建该 Class 对象对应类的实例,但是这种方法要求该 Class 对象对应的类有默认的空构造器,若是没有,则会报异常。

  2. 调用 Constructor 对象的 newInstance()。先使用 Class 对象的 getConstructor() 方法来获取指定的 Constructor 对象,再调用 Constructor 对象的 newInstance() 方法来创建 Class 对象对应类的实例,通过这种方法可以选定构造方法来创建实例。


下面的代码输出结果是什么?

public static synchronized void main(String[] a){
    Thread t = new Thread(){
        public void run(){
            SoGood();
        }
    };
    t.run();
    System.out.print("Hello");
}
static synchronized void SoGood(){
    System.out.print("SoGood");
}

输出为 SoGoodHello

因为这里没有开启一个线程,run 只是调用 Thread 类的方法,而 start 才是开启一个线程。


多态常用的应用场景有哪些

  • 通过方法的参数传递形成多态。
public static void draw(Shape s){
    s.show();
}
draw(new Rect(1, 2, 3, 4));
  • 在方法体中使用抽象类的引用指向子类对象时形成多态。
Account acc = new FixedAccount();
  • 通过返回值类型形成多态。
Calender getInstance(){
    return new GregorianCalendar(zone, aLocale);
}

想了解更多,欢迎关注我的微信公众号:Renda_Zhang

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值