《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(); } }
|
在Java运行时,RTTI 维护类的相关信息,识别一个对象的类型。
1.Class 对象
类是程序的一部分,每个类都有一个Class对象。
interface HasBatteries {} interface Waterproof {} interface Shoots {} class Toy { 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() + "]"); 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"); } catch(ClassNotFoundException e) { print("Can't find FancyToy"); System.exit(1); } printInfo(c); for(Class face : c.getInterfaces()) printInfo(face); Class up = c.getSuperclass(); Object obj = null; try { obj = up.newInstance(); } catch(InstantiationException e) { print("Cannot instantiate"); System.exit(1); } catch(IllegalAccessException e) { print("Cannot access"); System.exit(1); } printInfo(obj.getClass()); } }
|
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"); System.out.println(Initable.staticFinal); System.out.println(Initable.staticFinal2); System.out.println(Initable2.staticNonFinal); Class initable3 = Class.forName("Initable3"); System.out.println("After creating Initable3 ref"); System.out.println(Initable3.staticNonFinal); } }
|
3.instanceof
返回一个布尔值,判断对象是不是某个特定类型的实例。
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(); } }
|
告诉编译器使用什么类型,然后编译器帮你处理一切细节。
泛型接口
泛型也可以应用于接口;例如生成器,这是一种专门负责创建对象的类。
生成器接口定义如下:
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() {} private int size = 0; public CoffeeGenerator(int sz) { size = sz; } public Coffee next() { try { return (Coffee) types[rand.nextInt(types.length)].newInstance(); } 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() { 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); } }
|
泛型方法
一个基本指导原则:无论何时,只要你能做到,你就应该使用泛型方法。
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); } }
|
泛型方法与可变参数列表能够很好地共存:
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); } }
|
参考资料
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/