面试 Java 基础八股文十问十答第二期
面试 Java 基础八股文十问十答第二期
作者:程序员小白条
⭐点赞⭐收藏⭐不迷路!⭐
11.什么是反射?反射有哪些作用?反射在Sping中的体现
(1): 什么是反射?
- 反射可以在运行时获取到一个类的所有信息,包括(成员变量,成员方法,构造器等)
- 反射可以直接操作类的私有属性
- 反射就是把Java类中的各种成分映射成一个个的Java对象
(2): 反射有哪些作用?
- 获取类对应的字节码的对象 对象.getClass() Person.class Class.forName(“类的全路径”) 第三种是最安全,性能最好
- 反射可以获取一个类的所有信息,比如包名,类名,构造器,方法,成员变量 可以操作私有属性和私有方法
(3): 反射在Spring中的体现
- SprignIOC控制反转利用:工厂模式+反射+xml解析(配置文件)
- 利用Class.forName(“类的全路径”).newInstance
- @Bean注入组件就是利用反射+代理模式
- 从容器中拿取,一般会写getBean(“指定类名称”)或者是getBean(“类名称”,返回的Bean类型)
- 单元测试。JUnit等单元测试框架可以使用反射机制在运行时动态地获取类和方法的信息,实现自动化测试。
12.为什么要使用克隆? 怎么实现对象克隆? 克隆有几种方式和它们的区别
(1): 为什么要使用克隆?
- 业务逻辑中存在,想要对一个对象进行复制,但又不想更改原有的对象,比如DTO操作中,将POJO中的属性拷贝到DTO中
(2): 怎么实现对象克隆?
- 实现Cloneable接口,重写clone方法
- 实现Serializable接口,通过对象的序列化和反序列化实现克隆,属于深拷贝的克隆,对象转换字节流,然后写入对象流,然后从对象流中读取readObject获取对象
- Spring、Apache都提供了BeanUtils来拷贝对象,属于浅拷贝,推荐使用Spring自带的BeanUtils,效率更高
(3): 克隆有几种方式和区别
- 浅拷贝: 克隆基本数据类型,当克隆引用类型时,克隆对象的引用类型改变会导致原来的对象也发生改变,因此当克隆存在引用类型的属性时,推荐采用深拷贝。比如Dept部门中有Emp员工的情况
- 深拷贝: 克隆基本数据类型和引用类型,并且互相隔离,互不影响。
- 浅拷贝可以直接采用clone(),或者用BeanUtils
- 深拷贝,可以重写clone()方法,但当属性类型比较多,层级深的时候,不推荐。可以采用第二种方法,序列化和反序列化,当然类要实现Serializable接口。
13.常见的运行时异常有哪些?
- NumberFormatException 数字转换异常
- ArrayIndexOutofBoundsException 数组下标越界异常
- NullPointerException 空指针异常
- ArithmeticException 算术逻辑异常
- ClassCastException 类型转换异常
14.String,StringBuffer,StringBuilder之间的区别和联系
- 首先String类是final修饰,不可继承,底层维护了private final char value[] 属性,因此是常量,不能修改其引用地址,但是单个字符内容是可以发生改变,其实就是常量池和堆,String修改内容,会导致大量副本残留,因此效率会降低。
- StringBuffer是线程安全的,因为有synchronized修饰,底层用的和StringBuilder一样的AbstractStringBuilder的append。
- StringBuilder是线程不安全的,适合单线程情况下使用,但速度最快。
- 效率对比: StringBuilder>StringBuffer>String
15.重写和重载的区别和使用场景
- 重写: 是建立在继承关系上的,子类在继承父类的基础上,可以增加新的功能,使用场景:在不修改原方法的基础上对方法进行扩展和增加,例如:CGLIB实现动态代理(SpringBoot2.x),SpringBoot1.0和Spring5,AOP还是使用的是JDK动态代理,但JDK动态代理有局限性,必须要有接口。但CGLIB不存在这个问题,代理对象无论是赋值给接口还是实现类,这两者都是代理对象的父类。
- 重载:重载是多态的体现,一个类中处理不同类型的参数可以用重载,比如构造器重载,方法重载,
16.实例化对象有哪几种方式
- 直接new
- clone()克隆
- 反射机制 Class.forName(“类的全路径”).newInstance()
- 对象的序列化和反序列化,利用对象流
17.类什么时候会被加载
- 创建对象实例(new)
- 创建子类对象实例时,父类也会被加载
- 使用类的静态成员(属性的访问或赋值或方法的调用)
- 静态常量在访问时不会触发类的加载机制,常量在常量池,本质上没有直接引用到定义常量的类(静态常量存储在元数据区(JDK1.8)的静态常量池和运行时常量池并列)。
- 当子类引用父类的静态字段,不会触发子类的类加载
- 当通过数组来定义引用类,不会触发该类的类加载机制,数组在编译时不能确定元素类型,只有在运行时才能确定元素类型。
18.什么是双亲委派模型?
- 双亲委派模型,就是加载类的时候,先请求其父类加载器去加载,如果父类加载器无法加载类,再尝试自己去加载类。如果都没加载到,就抛出异常。
好处:
- 使得 Java 类随着它的类加载器一起具有一种带有优先级的层次关系,从而使得基础类得到统一
- 通过这种层级关可以避免类的重复加载,当父亲已经加载了该类时,就没有必要子ClassLoader再加载一次,避免了多份同样字节码的加载,。
- 安全,避免核心类被修改java核心api中定义类型不会被随意替换,假设通过网络传递一个名为java.lang.Integer的类,通过双亲委托模式传递到启动类加载器,而启动类加载器在核心Java API发现这个名字的类,发现该类已被加载,并不会重新加载网络传递的过来的java.lang.Integer,而直接返回已加载过的Integer.class,这样便可以防止核心API库被随意篡改。
19.HashMap和HashTable有什么区别?
- HashMap是JDK1.2引入的,线程不安全
- HashTable是JDK1.0引入的,线程安全
- HashMap中允许键和值为Null,而HashTable不允许
- HashTable直接使用对象的hashcode
- HashMap重新计算hash值(高低16位异或)
- HashMap没有HashTable的contains方法,改为containsKey和containsValue
- HashTable默认容量为11,HashMap为16
- HashTable扩容机制为11乘2+1,HashMap为16乘2
- HashTable继承Dictionary,HashMap继承AbstractMap
- 不建议使用HashTable,在多线程环境下,JDK1.5引入ConcurrentHashMap,在HashMap的基础上增加线程安全性保障
20.HashMap的实现原理
- 初始化大小默认16,2倍扩容机制
- 负载因子0.75
- HashMap在JDK1.7中存储结构采用数组+链表。HashMap采取Entry数组来存储key-value,每一个键值对组成了一个Entry实体,Entry类实际上是一个单向的链表结构,它具有next指针,指向下一个Entry实体,以此来解决Hash冲突的问题。
- HashMap在JDK1.8中采用数组+链表+红黑树,当链表长度大于等于8,并且数组长度大于等于64的时候进行树化,如果数组长度不大于64,仅进行正常的数组扩容