《Java核心技术 卷1》读书笔记(1)

|第5章 继承

5.1 类、超类和子类

  • p156
    • 名称对应: 超类 - 子类; 基类 - 派生类(derived class); 父类 - 孩子类
    • “超”和“子”来源于超集、子集的概念
  • p158
    • superthis并不类似。super不是一个对象的引用,不可赋值给对象变量,只是一个指示编译器调用超类方法的特殊关键字
    • 使用super调用超类构造器的语句必须是子类构造器的第一条语句
    • this的两个含义:1.隐式参数的引用 2.调用该类的其他构造器
      super的两个含义:1.调用超类方法 2.调用超类的构造器
  • p159
    • 数组保持类型相同的元素,但可以保持超/子类混合的元素。 比如`<超类>[]{<超类实例>,<子类实例>...}
      注意:往子类数组里存储超类元素将引发ArrayStoreException异常。
      遍历时可以:
      1
      
        for (<超类> e : 数组) {e.方法;}
      

      当e为超类实例时,调用超类方法;e为子类实例时,调用子类方法。一个对象变量可以指示多种实际类型的现象即为多态在运行时能够判断对象的实际类型、自动选择适当方法称为动态绑定(dynamic binding)

  • p162
    • 继承层次(inheritance hierarchy):由一个公共超类派生出来的所有类的集合
    • is-a原则=替换原则(substitution principle):超类任何时候都可以用子类替换
  • p163
    • 重载解析(overriding resolution):方法调用时编译器从若干候补方法中选定签名完全匹配的方法的过程。由于过程耗时,实际会在预先计算好的方法表中搜索
    • 静态绑定(static binding):编译器处理调用private/static/final方法时使用的绑定
  • p165
    • 覆盖方法时子类方法的可见性不得低于超类方法
  • p167
    • 在继承链由上至下强制转换时,应使用instanceof检查可行性。但应尽量少进行强制转换,而应考虑重新设计超类

5.2 Object: 所有类的超类

  • p176
    • equals使用规范:1.自反性 2. 对称性 3.传递性 4.一致性(如果x,y引用的对象不变,反复比较x,y结果也应不变)5.对于任意非空引用x,x.equals(null)为false
  • p178
    • equals必须正确覆盖Object类的equals方法, 应该用@Override检测
  • p179
    • 散列码(哈希值):字符串的散列码由内容导出(容易相同),Object默认的散列码由对象的储存地址导出(基本不会相同)。
    • Objects.hashCode方法是null安全的。
  • p181
    • toString大多遵循类名[字段1=字段值,字段2=字段值...]的格式
    • 使用“+”连接字符串与对象会自动调用对象的toString方法

5.3 泛型数组列表

  • p188
    • 数组列表ArrayList可以自动调节容量,也可以手动调用ensureCapacity(int capacity)提前确保容量,还可以在初始化时指定容量
  • p190
    • 快速访问数组列表元素技巧:
      1
      2
      
      var a = new X[list.size]; //list is an ArrayList
      list.toArray(a);//copy list elements to the Array
      

5.4 对象包装器与自动装箱

  • p192-193
    • 包装器(wrapper): Integer,Boolean... 皆为不可变,不可继承(final),可为null
    • 自动装箱/拆箱:(编译器)
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    
    //boxing
    var list = new ArrayList<Integer>();
    list.add(3);
    // the same as list.add(Integer.valueOf(3));
    
    //unboxing
    int n = list.get(i);
    // the same as int n = list.get(i).intValue;
    
    //unboxing then boxing
    Integer n = 3;
    n++
    

    注意:Integer数组列表的效率远低于int[]数组,慎用。

    • 同一性: 用==比较包装器对象通常会失败。注意:介于-128~127的short或int会被包装到固定的对象中。

5.5 参数数量可变的方法

5.6 枚举类

  • p196
    • 枚举类定义的是一个类,每个项是一个实例,不能构造新对象。
    • 比较枚举类用==不用equals
  • p197
    • 枚举可以拥有私有构造器,枚举类是Enum类的子类
    • 实用方法toString(Enum to string), Enum.valueOf(EnumClass, String)(String to Enum), values()(Enum to array);

5.7 反射

  • p198
    • 反射:能够分析类能力的程序
  • p199
    • 运行时类型标识:java运行时为所有对象维护的标识信息,通过“Class”类访问
    • Class(类)对象:描述一个特定类的属性,有3种获取方法:实例.getClass()Class.forName(className)java类型.class
  • p200
    • 每个类型仅有唯一的Class对象,可用“==”比较同一性
  • p201-p204
    • 利用类对象构造实例:getConstructor( ).newInstance(params)
    • 利用类对象加载资源:URL getResource(String name)
    • 检查类结构:
    1
    2
    3
    4
    5
    6
    7
    
      Class cl = Class.forName("className");
      Class supercl = cl.getSuperclass();
      String modifiers = Modifier.toString(cl.getModifiers());
      Constructor[] constructors = cl.getDeclaredConstructors();
      Method[] methods = cl.getDeclaredMethods();
      Field[] fields = cl.getDeclaredFields();
      ...
    
  • p208-p210
    • 在运行时分析对象:
    1
    2
    3
    4
    5
    
      var obj = new SomeClass();
      Class cl = obj.getClass();
      Field f = cl.getDaclaredField("fieldName");
      f.setAccessible(true); //若无权限则抛出异常
      Object v = f.get(obj); //实例当前"fieldName"字段的值
    
  • p210
    • 将来可能用 可变句柄(variable handles) 而非反射来读写字段
  • p214
    • 利用反射编写泛型数组:
      Object newArray = Arrray.newInstance(cl.getComponentType(), newLength);
  • p216-p219
    • 调用任意方法和构造器
    • 可以完成C语言函数指针的所有操作
    1
    2
    3
    4
    5
    6
    
    Method m = cl.getMethod("methodName",  double.class); //p2: 参数型
    double n = (Double) m.invoke(obj, args);// 返回原返回类型的包装器类型
    //静态方法第一个参数为null
    
    Constructor c = cl.getConstructor(double.class); // p1: 参数型
    Object obj2 = c.newInstance(1.5);