JAVA面试100道必考题

1. JVM指令手册
摘要由CSDN通过智能技术生成

1.如下代码的执行结果: 4,1,11

 @Test
    void demo01(){
   
        int i=1;
        i=i++;
        int j=i++;// i=2  j=1
        int k=i+ ++i * i++;//2+3*3=11
        System.out.println("i="+i);//4
        System.out.println("j="+j);//1
        System.out.println("k="+k);//k=2+3*3=11
    }

=号右边为操作数栈,=号左边变量为局部变量表,
=号右边的从左到右加载值依次压入操作数栈中,实际运算哪个看运算符优先级
自增,自减操作都是直接修改变量的值,不经过操作数栈
最后的赋值之前,临时结果也是存储在操作数栈中.

2.什么是Singleton?
单例设计模式: 即某个类在整个系统中只能有一个实例对象,可被获取和使用的设计模式.

3.面向过程和面向对象的区别?什么是面向对象?
面向过程:是分析解决问题的步骤,然后用函数把这些步骤一一的实现,然后再使用的时候一一调用.
面向过程,更加注重事情的每一个步骤和顺序

面向对象:是把构成问题的事物分解成若干个对象,而建立对象的目的是为了描述整个事物在解决整个问题中所发生的行为,
面向对象更注重事情有哪些参与者(对象),及各自都需要作什么

可以看出: 面向过程直接高效, 而面向对象更易于扩展和维护.

面向对象: 封装,继承.,多态
封装: 封装对象的属性和细节,并对外提供统一的访问方式
在这里插入图片描述
继承: 继承基类的方法,并做出自己的改变或扩展

ORM 对象关系映射.实现数据模型与数据库的解耦;

子类共性的方法或者属性直接使用父类的,而不需要自己再定义,只需要自己扩展自己个性的

多态:方法重写,父类引用指向子类对象,无法调用子类特有的功能,调用的是父类的方法
如果调用子类方法,.那么调用的方法必须是子类重写的方法—看引用的类型
在这里插入图片描述

4.HashCode的作用?
作用为:获取哈希码,也叫散列码,实际上返回的是一个int整数,
这个哈希码的作用: 确定该对象在哈希表中的索引位置.

再向集合中添加元素时,先计算该元素的hashCode的值.就可以定位到该元素所在的物理位置. 如果这个位置上没有元素,则直接存储,如果该位置已经存放了元素,就调用它的equals()方法和新元素进行比较,相同的话就不存储,不相同的话,就散列到其他位置.
这样一来,实际调用equals()方法的次数就大大降低,

hashCode() 定义在JDK.Object.java中, java中的任何类都包含有hashCode()函数. 散列表存储的是键值对key-value,
特点是: 能够根据键快速的检索出对应的值.-----这其中就利用到了散列码----可以快速的找到所需要的对象

hashCode和equals的区别?
在这里插入图片描述
为什么要有hashCode?

hashCode的默认行为是堆上的对象产生的独特值,如果没有重写hashCode()方法,则该Class的两个对象一定不相同.即使两个对象有相同数据
在这里插入图片描述

5.JVM性能调优?
JVM调优目标:使用较小的内存占用来获得较高的吞吐量或者较低的延迟。
在这里插入图片描述
JVM虚拟机底层:
在这里插入图片描述
在target -classes 文件夹中,找到编译后的文件 ,右键 open in Terminal 打开终端
然后javap 命令,可以查看方法参数 -c 为 JVM的反汇编
javap -c Math.class >Math.txt 将Math.txt文件打到记事本文件下

FILO 栈 先进后出 ,方法嵌套调用,用完就销毁了,
程序计数器: 线程切换, 比如线程被抢占CPU时间片 ,线程被挂起,记录线程被挂起的位置

线程栈:compute()方法栈帧
局部变量表: 给局部变量分配内存空间.把数据放进去
操作数栈: 操作数在程序运行过程中,做操作的临时中转的运行空间
动态链接
方法出口: 传给main方法

栈里面有很多变量,其中一部分变量指向堆,指向对象在内存中的内存地址

方法区(元空间): 存放 常量+静态变量+类信息
方法区:在JDK1.8之前叫永久态,持久态, 在JDK1.8后叫元空间.使用的是操作系统的物理地址

本地方法栈 new Thread().start();
private native void start0();

注意: 线程栈,本地方法栈,程序计时器是线程私有的. 堆和方法区是所有线程共有的

如何判断对象是否可以被回收呢?
GC Roots的根结点; 线程栈的本地变量.静态变量,本地方法栈的变量等
在这里插入图片描述
若分代第15次还存活,则会分到老年代里面

年轻代满了.就会触发Minor GC(新生代GC),可能有1%少数进入Survivor幸存区,
默认的设置下,当对象的年龄达到15岁时,也就是躲过15次GC的时候,他就会转移到老年代里去
在这里插入图片描述
JVM调试工具
第一种: cmd---->jvisualvm
在这里插入图片描述
第二种: Arthas 阿里巴巴
网址: https://arthas.aliyun.com/doc/en/quick-start.html
java -jar arthas-boot.jar 输入数字 dashboard仪表盘

  1. JDK,JRE,JVM的区别?
    在这里插入图片描述
    JVM分代年龄里面为什么是15次进入老年代?
    JAVA对象存储在堆内存, MarkWord为对象头
    在这里插入图片描述
    其中对象的分代年龄占4位,也就是0000,最大值为1111也就是最大为15.而不可能为16或者17之类的。

  2. ==和equals比较?
    ==对比的是栈中的值,基本数据类型是变量值,引用类型是堆中内存对象的地址

equals; Object中默认也是采用==比较, String类中被重写了equals()方法,其实是比较两个字符串的内容

 /**
     * ==对于基本类型,
     */
    @Test
    public void test06(){
   
        Integer i6=128;
        Integer i7=128;
        int i8=128;
        System.out.println(i6==i7);//false
        System.out.println(i6==i8);//true 自动拆箱,数值
    }

String类重写了equals()方法.String类new的话,是在堆中new创建了一块内存区域,
String str1=“abc”;是在常量池中的一块区域.
字符串是不可变的对象,在字符串拼接时会新创建一块对象.

8.简述final的作用? 为什么局部内部类和匿名内部类只能访问局部final变量?
局部内部类和匿名内部类只能访问局部final变量

注意:
final修饰的是基本数据类型,这个值本身不能被修改.
修饰的是引用数据类型.引用的指向不能被修改.
在这里插入图片描述

  /***
     * final 修饰的是引用类型,引用的指向不能被改变
     */
    @Test
    public void test04(){
   
        final Person p1 = new Person("abc", 12);
        p1.setAge(15);
        System.out.println(p1);//Person{name='java', age=15, height=null}
        System.out.println(p1.getAge());//15
    }

因为匿名内部类线程可能还没有跑完.,当外部类结束后,内部类对象仍然有引用指向它,内部类访问一个不存在的变量.
就将局部变量复制了一份,作为内部类的成员变量,copy了一份,延长了局部变量的生命周期,
使用final修饰,保证了内部类的局部变量和外部类的成员变量一致性

!!!
内部类和外部类是同一级别的,内部类不会随着因为定义在方法中就会随着方法的执行完毕就被销毁
如果内部类修改成员变量,方法中的局部变量也会跟着改变,这实际上也是一种妥协
在这里插入图片描述

package test;

/**
 * @author guoxin
 * @date 2021/10/13
 */
public class Final {
   
  public static void main(String[] args) {
   
      new OutClass().outPrint(15);
  }

  public static void test(final int b) {
   
    // 局部final变量a和b
    final int a = 10;
    // 匿名内部类
    new Thread() {
   
      @Override
      public void run() {
   
        System.out.println(a);
        System.out.println(b);
      }
    }.start();
  }
}

/**
 * 内部类和外部类是同一级别,
 */
class OutClass {
   
  private int age = 12;

  public void outPrint(final int x) {
   
    class InClass {
   
      public void InPrint() {
   
        System.out.println(x);
        System.out.println(age);
      }
    }
    new InClass().InPrint();
  }
}

9.String,StringBuilder,StringBuffer的区别以及使用场景?
StringBuilder, StringBuffer继承了AbstractStringBuilder, 里面是一个可变的char类型的字符数组
String private final char value[]; 不可变对象
在这里插入图片描述
优先使用StringBuilder,单线程安全.

开发中你用StringBuilder解决什么问题?
多线程情况下,访问同一个资源的时候,

一个对象是线程安全的 ?
多线程环境下.对这个对象的访问,不需要额外的同步控制,操作的数据结果仍然正确 ///多线程使用共享变量StringBuffer

10.重载和重写的区别?
在这里插入图片描述
重写 : 2同2小1大

11.接口和抽象类的区别?
JDK1.8之前,抽象类可以存在普通成员方法,而接口只能为public abstract 方法
抽象类里面的成员变量可以是各种类型,而接口中的成员变量只能是public static final类型
抽象类只能继承一个,而接口可以实现多个

抽象类是对类本质的抽象 is a 关系,包含子类通用特性,先有子类,后有抽象类.抽象类的设计目的是行为的抽象
接口是对行为的抽象.like a 关系.接口的设计目的是: 约束行为的有无.但不对如何实现行为进行限制.

使用场景:当关注一个事物的本质的时候,使用抽象类, 当关注一个操作的时候,使用接口.

12.List和Set的区别?
在这里插入图片描述
13. ArrayList和LinkedList的区别?

ArrayList: 如何扩容?
基于动态数组.连续内存存储. 适合下标访问(随机访问). //因为数组长度固定.超出长度需要新建数组,然后拷贝原数组数据
初始容量为10, 创建一个新数组.新数组的长度是原数组的长度的1.5倍 ,位运算
int newCapacity = oldCapacity + (oldCapacity >> 1);

将原数组的数据再拷贝到新数组中
elementData = Arrays.copyOf(elementData, newCapacity);

假如有20个数据需要添加,那么会分别在第一次的时候,将ArrayList的容量变为10 ;之后扩容会按照1.5倍增长。也就是当添加第11个数据的时候,Arraylist继续扩容变为10*1.5=15;当添加第16个数据时,继续扩容变为15 * 1.5 =22个。

LinkedList jdk1.6是双向循环链表,jdk1.7之后就变成了双向链表
LinkedList 基于链表,可以存储在分散的内存中,适合做数据的插入和删除操作,不适合查询,
在这里插入图片描述
14.hashmap和hashtable的区别?
区别: hashmap没有synchronized方法,线程非安全.hashtable有synchronized方法,线程安全
hashmap允许key和value为null, 而hashtable不允许nul键和null值

hashmap是无序的
底层实现:
hashmap 数组+链表
JDK8开始,链表长度高于8,数组长度大于64, 链表转为红黑树, 元素内部以node节点存在
计算key的hash值,然后进行二次hash对数组长度取模,对应到数组下标(调用散列函数,得到记录存放的位置)

如果没有产生hash冲突(对应位置没有元素),则直接创建node存入数组
如果产生hash冲突, 先进行equals比较,相同则进行替换,不相同则判断链表高度插入链表,
当链表高度大于8,数组长度到64时,则链表转换为红黑树.
当红黑树元素个数小于6的时候,就会转换回链表。

如果key为null.,存在下标为0的位置
hashmap扩容,初始容量为16,加载因子0.75

15.ConcurrentHashMap原理,JDK1.7和JDK1.8版本的区别?
比hashtable效率高,hashtable是在所有方法上,添加synchronized方法,synchronized是全局锁,ConcurrentHashMap是分段锁

JDK7
数据结构:
ReentrantLock+Segment+HashEntry, 一个Segment中包含一个HashEntry数组,每个HashEntry又是一个链表结构
元素查询: 二次hash, 第一次hash定位到Segment, 第二次hash定位到元素所在的链表头部
锁: Segment分段锁,Segment继承了ReentrantLock,锁定操作的Segment,其他的Segment不受影响,并发度为Segment的个数,可以通过构造函数指定,数组扩容不会影响其他的Segment
get方法无法加锁.,Volatile保证

JDK8
数据结构
synchronized+CAS+Node+红黑树
Node的val和next都用 volatile修饰,保持可见性
CAS 乐观锁
查找.替换,赋值操作都使用CAS
锁: 锁链表的head结点,不影响其他元素的读写,锁粒度更细,效率更高. 扩容时,阻塞所有的读写操作,并发扩容

读操作无锁:
Node的val和next都使用volcatile修饰,读写线程对该变量可见
数组用volatile修饰,保证扩容时被线程通知,

16.如何实现一个IOC容器的思路?

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值