《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/
评论 (0)