【面试题】1.JavaSE基础常见面试题

1.1 基础部分

1.1.1 String是最基本的数据类型吗?

  • 基本数据类型包括byte(1)、short(2)、char(2)、int(4)、float(4)、long(8)、double(8)、boolean。取值范围是 -2^(字节数 * 8 - 1) ~ 2^(字节数 * 8 - 1) - 1
  • double不能精确表示为0.2(任何有限长度的二进制),在内存中存储格式为:符号位(1bit) +指数(11 bit) +尾数(52 bit),因为从小数点转换到二进制会丢失精度,小数计算一般使用BigDecimal(String val),它是不可变的,可以用来表示任意精度的带符号十进制数。原理:把十进制小数扩大N倍让它在整数上进行计算,并保留相应的精度信息。
  • Boolean是4个字节,而boolean数组1个字节,这是因为虚拟机用int来代替boolean,保证高效存取。
  • java.lang.String类是final类型的,因此不能继承也不能修改String类。String还重写了hashCode和equals方法。String的hashCode方法中使用了质数31,因为31是一个不大不小的质数,减少了哈希冲突,关键是它可以被JVM优化,31 * i = (i << 5) - i

1.1.2 new一个字符串“xyz”创建了几个对象?

  • 两个或一个,new一个字符串会产生两个对象,一个在堆上(每new一次产生一个新的对象),一个在常量池中;
    • 创建了一个对象的情况:常量池中有需要的对象,就不会创建新的对象,直接拿来用;
    • 创建了两个对象的情况:常量池中没有要用的对象,则会创建一个,再加堆上的对象,一共两个对象。

1.1.3 int 和 Integer 有什么区别?

  • int是java的原始数据类型,Integer是java为int提供的封装类。
  • Integer实例变量的缺省值为 null,而int实例变量的缺省值与它们的类型有关。
  • int与Integer之间可以进行自动拆装箱的转换,但Integer在[-128,127]范围内会从IntegerCache.cache缓存中取值以复用,若超过这个范围,就new一个新的对象返回(Valueof方法),所以比较Integer类型应该使用equals()方法。127是默认值,使用-XX:AutoBoxCacheMax 参数可以调整

1.1.4 String 、StringBuffer和StringBuilder的区别?

  • String提供了数值不可改变的字符串。而StringBuffer和StringBuilder可以对字符串进行修改。但StringBuffer是线程安全的,而StringBuilder是非线程安全的,相比StringBuffer来说,StringBuilder的效率要高,
  • StringBuilder内部维护了一个char[]类型的value(默认长度是16),不断的append()就是不断的往char[]里填东西的过程,用System.arraycopy成倍复制扩容!
  • 可以重用StringBuilder:通过 sb.setLength(0) 方法可以把char数组的内存区域设置为0,为了避免并发访问,可以在ThreadLocal中使用固定的StringBuilderHolder,该StringBuilder实例的内存空间一直不会被GC回收造成内存浪费。

注:SimpleDateFormat类是非线程安全的,JDK8推荐使用线程安全的DateTimeFormatter类

1.1.5 ==和equals方法的区别?

  • ==用来比较两个变量的值是否相等,也就是用于比较变量所对应的内存中所存储的数值是否相同,即比较两个基本类型的数据或两个引用变量是否相等
  • equals方法是用于比较两个独立对象的内容是否相同,字符串的比较常用equals方法。
  • 如果一个类没有自己定义equals方法,那么它将继承Object类的equals方法(使用==操作符)

1.1.6 public,private,protected及default的区别?

修饰符 同类 同包 子类 其它
public
protected ×
default × ×
private × × ×

1.1.7 JDK7、JDK8、JDK9的新特性

  • JDK7的Switch语句支持string类型 ,在一个语句块中捕获多种异常;支持动态语言;
    支持try-with-resources;引入Java NIO.2开发包;null值的自动处理。
  • JDK8支持lambda表达式(替代匿名内部类),用Metaspace代替PermGen,Stream函数式编程、使用Optional类解决NPE、接口中可定义默认方法。
  • JDK9提供了JShell工具、模块化、多版本兼容、钻石操作符的升级{}、支持HTTP2.0和websocket协议、String底层存储结构char[]更换为byte[]。
  • JDK10支持局部变量var类型,JDK11支持ZGC。

1.2 封装、继承与多态

1.2.1 重写和重载的区别

  • 方法重写:相同方法名,相同返回值,相同参数列表,子类写了一个和父类相同的方法。重写方法的修饰符不能比父类方法严格,也不能是private。重写是运行时活动
  • 方法重载:方法名相同、参数不同(类型不同或者顺序不同)、与返回值无关。 重载是编译时活动

1.2.2 抽象类与接口的区别?

  • 抽象类表示的是一种继承关系,类是单继承。但可以实现多个接口。
  • 抽象类中可以有自己的数据成员,也可以有非抽象的成员方法,而在接口中,属性只能是public static final的,而方法只能是public abstract的。
  • 抽象类表示的是"is-a"关系,接口表示的是"like-a"关系。
  • 继承抽象类就必须实现该类的所有抽象方法,抽象类中可以有非抽象方法。而实现接口的类必须实现其中的所有方法。
  • 抽象类可以有构造方法、普通成员变量、静态方法,但接口中不能有构造方法、没有普通成员变量、不能包含静态方法
  • 抽象的方法不能同时是静态的,因为抽象方法需要重写,而静态方法不能被重写,不能实现抽象方法的同步锁,而静态方法可以。
  • 抽象类适合使用模板设计模式,如造汽车类,实现抽象方法glass,egineer,frame,非抽象方法make,在make中一次调用抽象方法,再利用多态,就实现了造多种车的场景。
  • 而接口适合使用简单工厂设计模式,如动物接口,定义吃、叫,速度方法,猫来了就实现动物接口中的方法,而不用自己写了。狗来了也是一样。

1.2.3 对static关键字的理解?

  • static关键字可以修饰属性、方法、代码块,可以静态导包。也可以实现静态内部类
  • 成员变量与静态变量的区别:成员变量的生命周期随对象的创建,而静态变量随类的加载;成员变量的存储位置在堆内存中,而静态变量在方法区中;它们的别名也不同。
  • 内部类又分为普通、匿名、方法、静态内部类,而静态内部类不需要依赖外部类。
  • 匿名内部类不能定义构造函数,也不能存在任何的静态成员变量和静态方法。
  • static方法中不能调用非static的方法和属性,在静态方法中不能使用this和super关键字
  • 当先new子类,后new父类时,初始化时的顺序为:父类的静态代码块 → 子类的静态代码块 → 父类的匿名代码块 → 父类的构造方法 → 子类的匿名代码块 → 子类的构造方法 → 。。。→ 父类的匿名代码块 → 父类的构造方法
  • 静态代码块只能被实例化一次。
  • 执行顺序问题
    • 静态/非静态属性值+静态/匿名代码块:哪个在最后,哪个就是最终值
    • 静态属性值+静态代码块+匿名代码块:无论顺序如何,最终值都是匿名代码块中的值

1.2.4 值传递与引用传递

  • 值传递:在方法调用的时候,实参将自己的一份拷贝赋给形参,在方法内,对该参数值的修改不影响原来实参。java中都是值传递
  • 引用传递:在方法调用的时候,实参将自己的地址传递的形参,此时方法内对该参数值的改变,就是对该实参的实际操作。(基本类型的数组是引用传递)

1.2.5 hashCode()和equals方法的联系?

  • equals方法的特性:自反性、传递性、对称性、非空性、一致性(多次比较结果相同)。
  • 只要重写equals,就必须重写hashCode。若没有重写会违反JavaSE6的Object规范第二条:两个相等的对象必须有相同的哈希值。 若只重写了equals方法,而没有重写hashcode方法,存入两个相同的对象,但取出来可能是不同的。
  • 因为Set存储的是不重复的对象,依据hashCode和equals进行判断,所以Set存储的对象必须重写这两个方法。
  • 如果自定义对象做为Map的键,那么必须重写hashCode和equals。由于String重写了hashCode和equals方法,可作为key来使用。

1.2.6 多态是如何实现的?

  • 运行时多态:java程序运行时,类的相关信息放在方法区,而方法表包含有该类型所定义的所有方法的信息和指向这些方法实际代码的指针。
  • 在调用方法时,虚拟机通过对象引用得到方法区中类型信息的入口,查询类的方法表,找到实际代码的指针。
  • 多态是父类的引用指向子类对象,执行时通过子类对象,得到子类的方法表。

1.3 Java深入

1.3.1 常见的异常

  • java中的异常是Throwable,而它又派生了两个子类:Error和Exception,
  • Error表示应用程序本身无法克服和恢复的一种严重问题,例如内存溢出和线程死锁等系统问题。
  • Exception表示程序还能够克服和恢复的问题,其中又分为IOException和RuntimeException
  • IOException表示已受检异常,需要使用try/catch块捕获
  • RuntimeException表示运行时异常,指程序存在bug
  • 常见的运行时异常有NullPointerException、IllegalStateException、ArrayIndexOutOfBoundsException 、ClassCastException、ClassNotFoundException

1.3.2 final, finally, finalize的区别?

  • final 用于声明属性,方法和类,分别表示属性不可变,方法不可覆盖,类不可继承。内部类要访问局部变量,局部变量必须定义成final类型。java中的final类有java.lang.String、java.lang.Math、java.util.Scanner、java.net.URL、java.lang.reflect.Parameter、java.time.Year等。
  • finally是异常处理语句结构的一部分,表示总是执行。
    • finally中的return会覆盖try和catch中的return值。
    • finally语句在return语句执行之后return返回之前执行的。
    • 如果finally语句中没有return语句覆盖返回值,那么原来的返回值可能因为finally里的修改而改变(map)也可能不变(int)。
    • try或catch代码块中(return/throw)的返回值保留,再来执行finally代码块中的语句,等到finally代码块执行完毕之后,在把之前保留的返回值给返回出去。
    • finally代码块执行不到的情况:
      • 在执行到try catch finally前就抛出了异常
      • try中有 System.exit(0) 代码,会退出JVM
      • 线程在执行 try 语句块或者 catch 语句块时被打断(interrupted)或者被终止(killed),或宕机
  • finalize是Object类的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法,可以覆盖此方法提供垃圾收集时的其他资源回收,例如关闭文件等。该方法最多只被调用一次,但JVM不保证此方法总被调用,

1.3.3 对于反射的理解?

  • JAVA反射机制指在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性。即可动态获取信息和动态调用对象方法。
  • 反射原理:Java在编译之后会生成一个class文件,反射通过字节码文件找到其类中的方法和属性等
  • 使用Class.forName(“类名”)方法获取类。
  • 方法
    • getDeclaredConstructors(),获取类声明的所有构造方法,
    • getFields():获得类的所有公共字段
    • getDeclaredMethods():获得类声明的所有方法
  • 直接对 private 的属性赋值:field.setAccessible(true),然后调用set方法设置值。
  • 反射的使用场景:
    • 开发通用框架:例如:Spring的配置化(通过XML文件配置JavaBean、Filter等)需要根据配置文件运行时动态加载不同的对象或类,调用不同的方法。
    • 动态代理类:为其他对象提供一种代理以控制对这个对象的访问,它不会替你作实质性的工作,在生成它的实例时你必须继承InvocationHandler类并重写invoke方法,由它来完成代理的具体操作。
    • 自定义注解:注解仅仅起到标记作用,需要利用反射机制根据注解标记去调用注解解释器,执行具体行为。
  • 反射性能低的原因:编译器没法对反射相关的代码做优化。需要检查方法可见性、校验参数。

1.3.4 深拷贝与浅拷贝

  • 深拷贝和浅拷贝都是对象拷贝
  • 浅拷贝仅仅复制所考虑的对象,而不复制它所引用的对象。浅拷贝在重写的clone()中,只是拷贝了主对象。
  • 深拷贝把要复制的对象所引用的对象都复制了一遍。
    而深拷贝在重写的clone()中,不仅拷贝了主对象,还拷贝了主对象引用的所有对象。

1.3.5 创建对象有几种方式?

创建对象的5种方式:

  • 用new语句创建对象,这是最常见的创建对象的方法。
  • 调用java.lang.Class类的newInstance()实例方法。
  • 调用java.lang.reflect.Constructor类的newInstance()实例方法。
  • 调用对象的clone()方法。
  • 通过I/O流(包括反序列化)

注:前三种会调用构造函数,而后两种不会调用构造函数

1.3.6 对于RESTful API 的理解?

  • 资源:URI
  • 使用标准的 HTTP 方法:查询 GET,新增 POST,修改 PUT,部分修改 PATCH,删除 DELETE
  • 安全性和幂等性:调用接口不对资源产生修改,调用方法1次或N次对资源产生的影响结果都是相同的。
  • 状态码和错误处理
  • 返回结果:API接口要求返回的格式是 application/json

1.3.7 其它

  • 泛型中extends和super的区别:extends用于返回类型限定,super用于参数类型限定。PECS ( Producer Extends,Consumer Super ),即读取extends,插入super
版权声明:程序员胖胖胖虎阿 发表于 2022年10月4日 上午12:08。
转载请注明:【面试题】1.JavaSE基础常见面试题 | 胖虎的工具箱-编程导航

相关文章

暂无评论

暂无评论...