《Java编程思想》读书笔记(七)

Author Avatar
wshunli 12月 04, 2017
  • 在其它设备中阅读本文章

《Java编程思想》读书笔记 —— 类型信息及泛型。

第14章 类型信息

RTTI (Run-Time Type Identification, 运行时类型识别) 是 Java 中非常有用的机制。

abstract class Shape {
  void draw() { System.out.println(this + ".draw()"); }
  abstract public String toString();
}
class Circle extends Shape {
  public String toString() { return "Circle"; }
}
class Square extends Shape {
  public String toString() { return "Square"; }
}
public class Shapes {
  public static void main(String[] args) {
    List<Shape> shapeList = Arrays.asList(
      new Circle(), new Square()
    );
    for(Shape shape : shapeList) shape.draw();
  }
}
/* Output:
Circle.draw()
Square.draw()
*/

在Java运行时,RTTI 维护类的相关信息,识别一个对象的类型。

1.Class 对象

类是程序的一部分,每个类都有一个Class对象。

interface HasBatteries {}
interface Waterproof {}
interface Shoots {}
class Toy {
  // Comment out the following default constructor
  // to see NoSuchMethodError from (*1*)
  Toy() {}
  Toy(int i) {}
}
class FancyToy extends Toy
implements HasBatteries, Waterproof, Shoots {
  FancyToy() { super(1); }
}
public class ToyTest {
  static void printInfo(Class cc) {
    print("Class name: " + cc.getName() + // 全限定的类型
      " is interface? [" + cc.isInterface() + "]"); // Class 对象是否表示某接口
    print("Simple name: " + cc.getSimpleName()); // 不含包名的类名
    print("Canonical name : " + cc.getCanonicalName()); // 含包名的类名
  }
  public static void main(String[] args) {
    Class c = null;
    try {
      c = Class.forName("typeinfo.toys.FancyToy"); // 获取 Class 对象的引用
    } catch(ClassNotFoundException e) {
      print("Can't find FancyToy");
      System.exit(1);
    }
    printInfo(c);
    for(Class face : c.getInterfaces()) // 获取 Class 对象中包含的接口
      printInfo(face);
    Class up = c.getSuperclass(); // 获取 Class 对象的直接基类
    Object obj = null;
    try {
      // Requires default constructor:
      obj = up.newInstance(); // 实现虚拟构造器
    } catch(InstantiationException e) {
      print("Cannot instantiate");
      System.exit(1);
    } catch(IllegalAccessException e) {
      print("Cannot access");
      System.exit(1);
    }
    printInfo(obj.getClass());
  }
} /* Output:
Class name: typeinfo.toys.FancyToy is interface? [false]
Simple name: FancyToy
Canonical name : typeinfo.toys.FancyToy
Class name: typeinfo.toys.HasBatteries is interface? [true]
Simple name: HasBatteries
Canonical name : typeinfo.toys.HasBatteries
Class name: typeinfo.toys.Waterproof is interface? [true]
Simple name: Waterproof
Canonical name : typeinfo.toys.Waterproof
Class name: typeinfo.toys.Shoots is interface? [true]
Simple name: Shoots
Canonical name : typeinfo.toys.Shoots
Class name: typeinfo.toys.Toy is interface? [false]
Simple name: Toy
Canonical name : typeinfo.toys.Toy
*/

2.类加载器是通过判断这个类包含的Class对象是否已经加载来判断它是否已经加载过这个类。为使用类而做的准备工作实际上包括三个步骤:

1 加载。这个由类加载器去完成。查找相关的字节码,并从这些字节码中创建一个Class对象。
2 链接。在链接阶段将验证类中的字节码,为静态域分配存储空间,并且必须的话将解析这个类创建的对其他类的引用。
3 初始化。如果该类具有超类,则对其初始化,执行静态初始化器和静态初始化块。初始化被延迟到了静态方法或者静态域进行首次引用时才执行。

class Initable {
  static final int staticFinal = 47;
  static final int staticFinal2 =
    ClassInitialization.rand.nextInt(1000);
  static {
    System.out.println("Initializing Initable");
  }
}
class Initable2 {
  static int staticNonFinal = 147;
  static {
    System.out.println("Initializing Initable2");
  }
}
class Initable3 {
  static int staticNonFinal = 74;
  static {
    System.out.println("Initializing Initable3");
  }
}
public class ClassInitialization {
  public static Random rand = new Random(47);
  public static void main(String[] args) throws Exception {
    Class initable = Initable.class;
    System.out.println("After creating Initable ref");
    // Does not trigger initialization:
    System.out.println(Initable.staticFinal);
    // Does trigger initialization:
    System.out.println(Initable.staticFinal2);
    // Does trigger initialization:
    System.out.println(Initable2.staticNonFinal);
    Class initable3 = Class.forName("Initable3");
    System.out.println("After creating Initable3 ref");
    System.out.println(Initable3.staticNonFinal);
  }
} /* Output:
After creating Initable ref
47
Initializing Initable
258
Initializing Initable2
147
Initializing Initable3
After creating Initable3 ref
74
*/

3.instanceof

返回一个布尔值,判断对象是不是某个特定类型的实例。

x instanceof Boy

4.反射:运行时的类信息

反射机制是在运行状态中,
对于任意一个类,都能够知道这个类的所有属性和方法;
对于任意一个对象,都能够调用它的任意一个方法和属性;
这种动态获取的信息以及动态调用对象的方法的功能称为反射。

5.动态代理

代理是一本基本的设计模式;代理通常充当着中间人的角色。

第15章 泛型

Java SE5 泛型实现了参数化类型的概念,使代码可以应用于多种类型。

泛型的出现,最引人注目的原因是为了创造容器类

一个只能持有单个对象的类:

public class Holder {
  private Automobile a;
  public Holder(Automobile a) { this.a = a; }
  Automobile get() { return a; }
}

显然,Holder 类的重用性很差,只能持有 Automobile 类对象。

泛型类

public class Holder3<T> {
  private T a;
  public Holder3(T a) { this.a = a; }
  public void set(T a) { this.a = a; }
  public T get() { return a; }
  public static void main(String[] args) {
    Holder3<Automobile> h3 =
      new Holder3<Automobile>(new Automobile());
    Automobile a = h3.get(); // No cast needed
    // h3.set("Not an Automobile"); // Error
    // h3.set(1); // Error
  }
}

告诉编译器使用什么类型,然后编译器帮你处理一切细节。

泛型接口

泛型也可以应用于接口;例如生成器,这是一种专门负责创建对象的类。

生成器接口定义如下:

public interface Generator<T> { T next(); }

实现生成器接口

public class CoffeeGenerator
implements Generator<Coffee>, Iterable<Coffee> {
  private Class[] types = { Latte.class, Mocha.class,
    Cappuccino.class, Americano.class, Breve.class, };
  private static Random rand = new Random(47);
  public CoffeeGenerator() {}
  // For iteration:
  private int size = 0;
  public CoffeeGenerator(int sz) { size = sz; }
  public Coffee next() {
    try {
      return (Coffee)
        types[rand.nextInt(types.length)].newInstance();
      // Report programmer errors at run time:
    } catch(Exception e) {
      throw new RuntimeException(e);
    }
  }
  class CoffeeIterator implements Iterator<Coffee> {
    int count = size;
    public boolean hasNext() { return count > 0; }
    public Coffee next() {
      count--;
      return CoffeeGenerator.this.next();
    }
    public void remove() { // Not implemented
      throw new UnsupportedOperationException();
    }
  };
  public Iterator<Coffee> iterator() {
    return new CoffeeIterator();
  }
  public static void main(String[] args) {
    CoffeeGenerator gen = new CoffeeGenerator();
    for(int i = 0; i < 5; i++)
      System.out.println(gen.next());
    for(Coffee c : new CoffeeGenerator(5))
      System.out.println(c);
  }
} /* Output:
Americano 0
Latte 1
Americano 2
Mocha 3
Mocha 4
Breve 5
Americano 6
Latte 7
Cappuccino 8
Cappuccino 9
*/

泛型方法

一个基本指导原则:无论何时,只要你能做到,你就应该使用泛型方法。

public class GenericMethods {
  public <T> void f(T x) {
    System.out.println(x.getClass().getName());
  }
  public static void main(String[] args) {
    GenericMethods gm = new GenericMethods();
    gm.f("");
    gm.f(1);
    gm.f(1.0);
    gm.f(1.0F);
    gm.f('c');
    gm.f(gm);
  }
} /* Output:
java.lang.String
java.lang.Integer
java.lang.Double
java.lang.Float
java.lang.Character
GenericMethods
*/

泛型方法与可变参数列表能够很好地共存:

public class GenericVarargs {
  public static <T> List<T> makeList(T... args) {
    List<T> result = new ArrayList<T>();
    for(T item : args)
      result.add(item);
    return result;
  }
  public static void main(String[] args) {
    List<String> ls = makeList("A");
    System.out.println(ls);
    ls = makeList("A", "B", "C");
    System.out.println(ls);
    ls = makeList("ABCDEFFHIJKLMNOPQRSTUVWXYZ".split(""));
    System.out.println(ls);
  }
} /* Output:
[A]
[A, B, C]
[, A, B, C, D, E, F, F, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z]
*/

参考资料
1、《Java编程思想》读书笔记 第十四章 01 类型信息
https://zhuanlan.zhihu.com/p/26081790
2、《java编程思想》14章类型信息 读书笔记 - CSDN博客
http://blog.csdn.net/L2HL2H/article/details/51120480
3、《Java编程思想》读书笔记 第十四章 02 反射
https://zhuanlan.zhihu.com/p/26190300
4、Java泛型详解 | ZiWenXie
https://www.ziwenxie.site/2017/03/01/java-generic/

如果本文对您有所帮助,且您手头还很宽裕,欢迎打赏赞助我,以支付网站服务器和域名费用。 https://paypal.me/wshunli 您的鼓励与支持是我更新的最大动力,我会铭记于心,倾于博客。
本文链接:https://www.wshunli.com/posts/1fc6c3f.html