java高级程序员面试笔试宝典

第1章 重视基础知识

1.1 不可变类

  1. 定义:对象一旦创建,成员变量不可被修改
  2. 例子:所有基本类型的包装类(Integer、Float),String类
  3. 创建不可变类:
  • private修饰成员变量:防止其他类直接访问
  • final修饰类:确保所有方法不会被子类覆盖
  • setXXX()
  • clone()赋值:如果某个成员变量不是不可变量,如Date,则成员初始化或者使用get方法获取该成员变量需要通过clone方法解除引用关系,确保类的不可变性(this.d = (Date)d.clone();
  • 必要时重写equals()hashCode()

1.2 equals()hashCode()

  1. hashCode()
  • Object类中hashCode()方法返回对象在内存中地址转换成的int值
  • 如果没有重写hashCode(),任何对象的hashCode()方法都是不相等的
  1. equals()hashCode()关系
  • equals相等,hashCode相等
  • hashCode不相等,equals不相等
  • hashCode相等,equals不一定相等
  1. 场景: HashMap中判断key是否重复

1.3 Java关键字

  1. static
  • 作用:
    • 对象分配单一存储空间;
    • 属性或方法和类而不是对象绑定;
  • 修饰:
    • 变量
    • 方法
    • 代码块:类加载时执行
    • 内部类
    • 导入包
  1. final
  • 修饰:
    • 属性:属性不可变(引用不可变,但对象的内容可变)
    • 方法:方法不可被覆盖
    • 类:类不可被继承
  • 匿名内部类为什么可以使用final修饰的局部变量
    • final修饰的局部变量在匿名内部类中有一个引用的副本
    • 匿名内部类生存期比一半的局部变量更久
  • final var
    • var仍是和对象绑定,所以在声明、构造方法中初始化,除非用static修饰,否则不能在静态代码块中初始化
  1. transient
  • 作用:修饰不想被序列化(serialization)的属性
  • 原理:transient修饰的属性,生命周期仅在内存中,表示对象的临时数据,不会被写到磁盘
  • 序列化
    • 实现方式
      • 实现Serializable接口(内部无任何方法,用于标记该类的对象支持序列化,可以用ObjectOutputSream对象的writeObject(Object obj)写出)
      • 实现Exteranlizable接口,包含readExternal()writeExternal()方法,可以决定哪些属性需要序列化,包括被transient修饰的属性也可以被序列化
    • 不可以被序列化的属性
      • static修饰的属性:static修饰的属性属于类,序列化是对象属性的序列化
      • transient修饰的属性:生命周期仅在内存,代表对象的临时数据
  1. volitale
  • 作用:修饰被多线程访问的属性,保证修改对所有线程可见
  • 原理:Memory Barrier(内存栅栏)
    • 修改:JVM先执行Write-Barrier指令,将CPU中的数据写回内存,并将CPU核心里引用了该内存地址的数据变成脏数据
    • 读取:JVM先执行Read-Barrier指令,如果数据已经变脏,则从主存中重新获取数据

第2章 再论面向对象

2.1 继承

  1. 实现多重继承
  • 方法1:实现多个接口
  • 方法2:定义多个内部类,分别继承不同的父类
  1. 多态的表现形式
  • 方法1:重载,编译时多态
  • 方法2:覆盖,运行时多态

注:成员变量无法实现多态,成员变量的值取父类还是子类并不取决于创建对象的类型,而是取决于定义变量的类型,在编译时确定,如Base b = new Derived();获取b.i得到的是Base类的成员变量不是Derived

2.2 反射

  1. 反射的作用
  • 获取类的访问修饰符、方法、属性以及父类信息
  • 运行时创建对象
  • 运行时判断对象属于哪个类
  • 生成动态代理
  1. 获取Class对象
  • Class<?> c = A.class;不执行静态块和动态构造块
  • Class<?> c = Class.forName("A");执行静态块,不执行动态构造块
  • Class<?> c = new A().getClass();即通过Object.getClass()获取,因为要创建对象所以执行静态块和动态构造块
  1. Class类的常用方法
  • 获取类的成员变量
    • public Field[] getFields()
    • public Field getField(String name)
    • public Field[] getDeclaredFields()
    • public Field getDeclaredField(String name)
  • 获取类的构造方法
    • public Constructor<?>[] getConstructors()
    • public Constructor<T> getConstructor(Class<?>...parameterTypes)
    • public Constructor<?>[] getDeclaredConstructors()
    • public Constructor<T> getDeclaredConstructor(Class<?>...parameterTypes)
  • 获取类的方法
    • public Method[] getMethods()
    • public Method getMethod(String name, Class<?>...parameterTypes)
    • public Method[] getDeclaredMethods()
    • public Method getDeclaredMethod(String name, Class<?>...parameterTypes)
  • 获取内部类
    • public Class<?>[] getDeclaredClasses();

注:无declared方法获取public的...,有declared的方法获取所有...

  1. 反射修改private成员变量

    Class<?> clazz = ReadOnlyClass.class;
    Field field = clazz.getDeclaredField("age");  // 获取private成员变量
    field.setAccessible(true);
    
    ReadOnlyClass rc = new ReadOnlyClass();  // 具体对象
    field.set(rc, 30);  // 修改具体对象的private成员变量值
    

2.3 嵌套类

  • 静态内部类
    • 不依赖实例
    • 不能与外部类有相同的名字
    • 只能访问外部类的静态成员和静态方法
  • 成员内部类
    • 依赖于实例,所以不能定义静态的属性和方法
  • 局部内部类
    • 不能被publicprotectedprivatestatic修饰
    • 局部静态内部类:定义在外部类静态方法或静态初始化代码段种的类
  • 匿名内部类
    • 属于局部内部类
    • 必须继承一个类或实现一个接口
    • 不能有构造函数(没用)
    • 不能定义静态成员和方法

第3章 泛型

  1. 泛型:类型参数化
  2. 泛型的好处
    • 代码重用
    • 编译时类型检查(无泛型时需要运行时检查类型)
    • 容器种存储类型声明时确定,省去类型转换

3.1 基本概念

  1. 泛型分类
  • 泛型接口:接口名后跟<T1, T2...>
  • 泛型类:类名后跟<T1, T2, ...>
  • 泛型方法:返回值前跟<T1, T2, ...>

泛型方法泛型参数的确定:根据方法参数确定;无参方法通过obj.<xxx>methodName();指定

  1. 有界泛型
  • ?
    • 指代某一个任意类型,但并不是Object
  • extends
    • 定义泛型的上界,T extends UpperBoundClass
  • super
    • 定义泛型的下界,T super LowerBoundClass

备注:

  • ArrayList<Parent> alist = new ArrayList<Sub>();编译时报错
  • T extends A,反射当成A类来处理,当用反射获取的时候也是通过A类
  1. 复杂泛型
  • 多个泛型参数使用逗号隔开:<T, K>
  • &连接同一个泛型参数的多个上界
  • 多个上界类型种最多只能有一个类,其它必须为接口,如果上界里有类,那么必须放置在第一位
  1. 数组和泛型容器
  • 协变性,逆变性,无关性
  • 数组具有协变性(容易造成运行时类型不匹配异常),泛型具有无关性(类型不匹配直接编译异常)
  • 数组和泛型不能混合使用,List<String>[] list = new ArrayList<String>[2];编译时异常

3.2 泛型擦除

  1. 编译器对泛型的两种实现方式
  • Code Specialization:C++
    • 每个泛型类实例都生成一份字节码
  • Code sharing:Java
    • 泛型类的不同实例使用同一份字节码,需要时再进行类型检查(原理:泛型擦除)
  1. 泛型擦除

    1. 泛型擦除(类型擦除)

      • 由于编译器会在字节码指令集中抹去全部泛型类型信息,所以Java泛型不存在于运行时
      • 必要的时候添加类型检查和类型转换的方法
      • 字节码中仅保留泛型的原始类型
    2. 原始类型:抹去泛型信息后的类型

      • <T>对应的原始类型Object
      • <T extends String>对应的原始类型String
    3. 反编译工具jd-gui:可以查看泛型擦除后的Java代码,可以看到做类型转换的地方

全部评论

相关推荐

机械打工仔:第一位颇有孟德之志
点赞 评论 收藏
分享
Twilight_mu:经典我朋友XXXX起手,这是那种经典的不知道目前行情搁那儿胡编乱造瞎指导的中年人,不用理这种**
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务