Thinking in java 读书笔记
运行时类型信息使得你可以在程序运行时发现和使用类型信息。
Java 通过两种方式在运行时识别对象和类信息的,一种是 传统的RTTI,(RunTime Type Identification 运行时类型定义),假定我们在编译时已经知道了所有的类型。第二种是 反射机制,它允许在运行时发现和使用类信息。
啥叫多态,父类方法在子类可能会被覆盖,但是由于方法是动态绑定的,所以即使是通过泛化的父类引用来调用方法,也是可以产生正确的行为,这就是多态。
// 父类
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";
}
}
//子类
class Triangle extends Shape {
public String toString() {
return "Triangle";
}
}
// 利用父类引用调用子类方法的荔枝
public class Shapes {
public static void main(String[] args) {
// 把 Shape子类数组转换为 泛型为Shape的List容器
List shapeList = Arrays.asList(new Circle(), new Square(), new Triangle());
for (Shape shape : shapeList)
shape.draw();
System.out.println();
for (Shape shape : shapeList)
System.out.println("does " + shape.getClass().getName() + " belong to Circle = " + rotate(shape));
}
static boolean rotate(Shape s) {
if(s instanceof Circle) {
return true;
} else {
return false;
}
}
}
/*
Circle.draw()
Square.draw()
Triangle.draw()
does chapter14.Circle belong to Circle = true
does chapter14.Square belong to Circle = false
does chapter14.Triangle belong to Circle = false
*/
RTTI的基本形式,当从数组中取出元素时, RTTI
会将 Object
转换成 Shape
类型, RTTI
类型转换并不彻底,因为把所有的 Object
转换成 Shape
父类,而不是转换到 Circle
或 Square
或 Triangle
。
.class
文件。class Name {
static {
System.out.println("name is loading");
}
}
class Sex {
static {
System.out.println("sex is loading");
}
}
class Age {
public static String desc = "desc";
static {
System.out.println("age is loading");
}
Age() {
System.out.println("age construct");
}
}
public class ClassTest {
public static void main(String[] args) {
new Name();
try {
if (Boolean.TYPE == boolean.class) {
System.out.println(true);
}
Class.forName("chapter14.Sex");
} catch (Exception e) {
e.printStackTrace();
}
new Age();
}
}
// output:
/**
name is loading
true
sex is loading
age is loading
age construct
*/
Class 仅在需要的时候才被加载, static 初始化是在类加载时就被加载。
Class.forname() 取得 Class 对象引用的方法。
方法名 | 介绍 |
---|---|
getName() | Class 类型信息所存储的全限定类名 |
isInterface() | 是否是接口 |
getSimpleName() | Class 类型信息存储的类名(不包含包名的类名) |
getCanonicalName() | Class 类型信息所有存储的全限定类名 |
getInterfaces() | 返回接口数组 |
getSuperClass() | 返回直接基类 |
newInstance() | 创建 class 运行时类型信息对象对应的实例 |
使用类字面常量生成对Class
对象的引用: 如 FancyToy.class
;这种做法不仅简单而且安全,因为它在编译时就会受到检查(不需要放在 try
块中),并且根除了对 forName()
的调用,所以高效。
类字面常量应用范围于 普通类,接口,数组,基本数据类型和包装器类, 以及一个标准字段TYPE等。TYPE字段:是一个引用,指向对应的基本数据类型的 Class对象。
boolean.class 等价于 Boolean.TYPE
char.class 等价于 Character.TYPE
byte.class 等价于 Byte.TYPE
short.class 等价于 Short.TYPE
int.class 等价于 Integer.TYPE
long.class 等价于 Long.TYPE
float.class 等价于 Float.TYPE
double.class 等价于 Double.TYPE
void.class 等价于 Void.TYPE
推荐使用 .class 的形式,以保持与普通类的一致性
当使用 .class
来创建Class
对象的引用时,不会自动初始化该 Class
对象。
为使用类而做的准备工作包含3个步骤。
class Initable {
static final int staticFinal = 47; // 编译期常量
// staticFinal2 是运行时常量,因为它需要调用其他类的方法进行赋值.
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");
System.out.println("staticNonFinal = " + staticNonFinal);
}
}
public class ClassInitialization {
public static Random rand = new Random(47);
public static void main(String[] args) throws Exception {
Class initable = Initable.class; // Initable.class 不会触发初始化
// 不会触发类初始化(调用常量)
System.out.println("Initable.staticFinal = " + Initable.staticFinal + "\n");
// 会触发初始化(调用常量, 但常量是有其他静态方法进行赋值的)
System.out.println("Initable.staticFinal2 = " + Initable.staticFinal2 + "\n");
// 会触发初始化(调用非final static 变量,非常数静态域)
System.out.println("Initable2.staticNonFinal = " + Initable2.staticNonFinal+"\n");
System.out.println("======");
Class initable3 = Class.forName("chapter14.Initable3"); // Class.forName() 触发类初始化
System.out.println("\nAfter creating Initable3 ref");
System.out.println("Initable3.staticNonFinal = " + Initable3.staticNonFinal); // 已经触发了Initable3初始化,不会再次触发了(仅初始化一次)
}
}
/*
Initable.staticFinal = 47
Initializing Initable
Initable.staticFinal2 = 258
Initializing Initable2
Initable2.staticNonFinal = 147
======
Initializing Initable3
staticNonFinal = 74
After creating Initable3 ref
Initable3.staticNonFinal = 74
*/
class Father { }
class Child extends Father { }
class Apple { }
// 荔枝-class对象的cast()方法,类型转换(特别是父类转子类的荔枝)
public class ClassCasts {
public static void main(String[] args) {
Father father = new Child();
Class childType = Child.class;
// classObj.cast(otherObj) 将 otherObj 转换为 classObj 对应的对象引用(父类对象引用转换为 子类对象引用)
Child child = childType.cast(father); //
System.out.println(child.getClass().getName()); // chapter14.Child
child = (Child)father; // 也是将 Father 对象转换为 Child 对象(同样的效果)
// 自定义测试
// child = childType.cast(new Apple()); // 编译期不报错,运行时报错
}
}
// chapter14.Child
传统的类型转换: 由RTTI 确保类型转换的正确性,若有错误抛出 ClassCastException 异常(类型转换异常);
封装对象的运行时类型信息的Class对象: 通过查询 Class 对象可以获取运行时类型信息;
RTTI还有第3中方式:关键字 instanceof;它返回一个 boolean值,表示对象是否属于某种类型;
如果程序中出现了过多的 instanceof,说明程序设计存在瑕疵。
class Part {
public String toString() {
return getClass().getSimpleName();
}
static List extends Part>> partFactories = new ArrayList extends Part>>();
static {
// Collections.addAll() gives an "unchecked generic
// array creation ... for varargs parameter" warning.
partFactories.add(new FuelFilter.Factory());
partFactories.add(new AirFilter.Factory());
partFactories.add(new CabinAirFilter.Factory());
partFactories.add(new OilFilter.Factory());
partFactories.add(new FanBelt.Factory());
partFactories.add(new PowerSteeringBelt.Factory());
partFactories.add(new GeneratorBelt.Factory());
}
private static Random rand = new Random(47);
public static Part createRandom() {
int n = rand.nextInt(partFactories.size()); // partFactories.size() = 7
return partFactories.get(n).create();
}
}
class Filter extends Part {}
class FuelFilter extends Filter {
// Create a Class Factory for each specific type:
public static class Factory implements typeinfo.factory.Factory<FuelFilter> { // 静态内部类作为工厂方法类
public FuelFilter create() { return new FuelFilter(); }
}
}
class AirFilter extends Filter {
public static class Factory implements typeinfo.factory.Factory<AirFilter> {// 静态内部类作为工厂方法类
public AirFilter create() { return new AirFilter(); }
}
}
class CabinAirFilter extends Filter {
public static class Factory implements typeinfo.factory.Factory<CabinAirFilter> {// 静态内部类作为工厂方法类
public CabinAirFilter create() { return new CabinAirFilter(); }
}
}
class OilFilter extends Filter {
public static class Factory implements typeinfo.factory.Factory<OilFilter> {// 静态内部类作为工厂方法类
public OilFilter create() { return new OilFilter(); }
}
}
class Belt extends Part {}
class FanBelt extends Belt {
public static class Factory implements typeinfo.factory.Factory<FanBelt> {// 静态内部类作为工厂方法类
public FanBelt create() { return new FanBelt(); }
}
}
class GeneratorBelt extends Belt {
public static class Factory implements typeinfo.factory.Factory<GeneratorBelt> {// 静态内部类作为工厂方法类
public GeneratorBelt create() {
return new GeneratorBelt();
}
}
}
class PowerSteeringBelt extends Belt {
public static class Factory implements typeinfo.factory.Factory<PowerSteeringBelt> {// 静态内部类作为工厂方法类
public PowerSteeringBelt create() {
return new PowerSteeringBelt();
}
}
}
public class RegisteredFactories {
public static void main(String[] args) {
for(int i = 0; i < 10; i++)
System.out.println(Part.createRandom());
}
}
/*
GeneratorBelt
CabinAirFilter
GeneratorBelt
AirFilter
PowerSteeringBelt
CabinAirFilter
FuelFilter
PowerSteeringBelt
PowerSteeringBelt
FuelFilter
*/
class Base {}
class Derived extends Base {}
// 荔枝-instanceof 与 直接比较Class对象的区别
public class FamilyVsExactType {
static void test(Object x) {
print("class Derived extends Base {}");
print("x.getClass() " + x.getClass());
print("(x instanceof Base) " + (x instanceof Base));
print("(x instanceof Derived) "+ (x instanceof Derived));
print("Base.class.isInstance(x) "+ Base.class.isInstance(x));
print("Derived.class.isInstance(x) " + Derived.class.isInstance(x));
print("(x.getClass() == Base.class) " + (x.getClass() == Base.class));
print("(x.getClass() == Derived.class) " + (x.getClass() == Derived.class));
print("(x.getClass().equals(Base.class)) "+ (x.getClass().equals(Base.class)));
print("(x.getClass().equals(Derived.class)) " + (x.getClass().equals(Derived.class)));
}
public static void main(String[] args) {
test(new Base());
System.out.println("===============================");
test(new Derived());
}
}
/*
============ Base 父类===================
class Derived extends Base {}
x.getClass() class typeinfo.Base
(x instanceof Base) true
(x instanceof Derived) false
Base.class.isInstance(x) true
Derived.class.isInstance(x) false
(x.getClass() == Base.class) true
(x.getClass() == Derived.class) false
(x.getClass().equals(Base.class)) true
(x.getClass().equals(Derived.class)) false
============ Derived 子类===================
class Derived extends Base {}
x.getClass() class typeinfo.Derived
(x instanceof Base) true // 子类是父类的实例
(x instanceof Derived) true
Base.class.isInstance(x) true // 子类是父类的实例
Derived.class.isInstance(x) true
(x.getClass() == Base.class) false
(x.getClass() == Derived.class) true
(x.getClass().equals(Base.class)) false
(x.getClass().equals(Derived.class)) true
*/
代理是基本的设计模式之一,代理用来代替实际对象的对象,代理充当着中间人的角色。
class ProxyTest {
private static void consumer(Interface anInterface) {
anInterface.doSomeThing();
anInterface.doSomeThingElse("biu~");
}
public static void main(String[] args) {
consumer(new RealObject());
System.out.println();
System.out.println();
consumer(new ProxyObject(new RealObject()));
}
}
interface Interface {
void doSomeThing();
void doSomeThingElse(String args);
}
class RealObject implements Interface {
@Override
public void doSomeThing() {
System.out.println("doSomeThing");
}
@Override
public void doSomeThingElse(String args) {
System.out.println("doSomeThingElse:" + args);
}
}
class ProxyObject implements Interface {
private Interface anInterface;
ProxyObject(Interface anInterface) {
this.anInterface = anInterface;
}
@Override
public void doSomeThing() {
System.out.println("ProxyObject doSomeThing");
anInterface.doSomeThing();
}
@Override
public void doSomeThingElse(String args) {
System.out.println("ProxyObject doSomeThingElse:" + args);
anInterface.doSomeThingElse(args);
}
}
代理的目的在于,任何时刻,只要你想要将额外的操作从实际对象中分离到不同地方,特别是当你希望能够做出修改时,从没有使用额外操作转为使用这些操作,或者反过来,代理就特别有用。
class DynamicProxyHandler implements InvocationHandler {
private Object proxied;
DynamicProxyHandler(Object proxied) {
this.proxied = proxied;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException {
System.out.println("DynamicProxy " + method.getName());
return method.invoke(proxied, args);
}
}
// output:
/**
doSomeThing
doSomeThingElse:biu~
ProxyObject doSomeThing
doSomeThing
ProxyObject doSomeThingElse:biu~
doSomeThingElse:biu~
DynamicProxy doSomeThing
doSomeThing
DynamicProxy doSomeThingElse
doSomeThingElse:biu~
*/
通过反射可以访问私有方法
public class HiddenImplementation {
public static void main(String[] args) throws Exception {
A a = HiddenC.makeA();
a.f();
System.out.println("a.getClass().getName() = " + a.getClass().getName());
/* 这里编译报错: 找不到类表示C2 (如果C2 与 HiddenImplementation 不在同一个包下)
* if(a instanceof C) { C c = (C)a; c.g(); }
*/
// 通过反射可以访问 私有方法,受保护或包可见性方法。
callHiddenMethod(a, "g");
// And even methods that are less accessible!
callHiddenMethod(a, "u");
callHiddenMethod(a, "v");
callHiddenMethod(a, "w");
}
static void callHiddenMethod(Object a, String methodName) throws Exception {
Method g = a.getClass().getDeclaredMethod(methodName); // 获取对象a的方法对象
g.setAccessible(true); // 设置访问权限为可访问
g.invoke(a); // 触发调用 对象a 的 g() 方法
}
}
/*
public C.f()
a.getClass().getName() = chapter14.C2
public C.g()
package C.u()
protected C.v()
private C.w()
*/
// C2默认为包可见性
class C2 implements A {
@Override
public void f() {
print("public C.f()");
}
public void g() {
print("public C.g()");
}
void u() {
print("package C.u()");
}
protected void v() {
print("protected C.v()");
}
private void w() {
print("private C.w()");
}
}
public class HiddenC {
public static A makeA() {
return new C2();
}
}
通过反射访问私有内部
class AnonymousTest {
static A makeA() {
return new A() {
@Override
public void a() {
System.out.println("aaaa");
}
@Override
public void b() {
System.out.println("bbbb");
}
@Override
public void c() {
System.out.println("cccc");
}
@Override
public void d() {
System.out.println("dddd");
}
};
}
private static void callHideMethod(String className, String methodName) {
try {
Class> aClass = Class.forName(className);
Method method = aClass.getMethod(methodName);
Object instance = aClass.newInstance();
method.invoke(instance);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
A a = AnonymousTest.makeA();
a.a();
System.out.println();
String name = a.getClass().getName();
callHideMethod(name, "b");
callHideMethod(name, "c");
callHideMethod(name, "d");
}
}
interface A {
void a();
void b();
void c();
void d();
}
通过反射修改私有变量
class WithPrivateFinalField {
private int i = 1;
private final String s = "I'm totally safe"; // 常量字符串
private String s2 = "Am I safe?";
public String toString() {
return "private int i = " + i + ", private final String s = " + s + ", private String s2 = " + s2;
}
}
// 荔枝-通过反射访问和修改私有变量,私有常量的荔枝
public class ModifyingPrivateFields {
public static void main(String[] args) throws Exception {
WithPrivateFinalField pf = new WithPrivateFinalField();
System.out.println(pf + "\n");
// 通过反射访问私有变量i
Field f = pf.getClass().getDeclaredField("i");
f.setAccessible(true); // 设置字段 i 的可访问权限为true
System.out.println("Field f = pf.getClass().getDeclaredField(\"i\"); f.setAccessible(true); f.getInt(pf) = " + f.getInt(pf));
// 通过反射更改私有变量i
f.setInt(pf, 47);
System.out.println("f.setInt(pf, 47); f.get(pf) = " + f.get(pf));
// 通过反射访问私有常量s
f = pf.getClass().getDeclaredField("s");
f.setAccessible(true); // 设置字段 s 的可访问权限为true
System.out.println("f = pf.getClass().getDeclaredField(\"s\"); f.setAccessible(true); f.get(pf) 静态常量修改前 = " + f.get(pf));
// 但通过反射甚至可以修改私有常量(final) s
f.set(pf, "No, you're not!");
System.out.println("f.set(pf, \"No, you're not!\");, f.get(pf) 静态常量修改后 = " + f.get(pf));
// 通过反射访问私有变量s2
f = pf.getClass().getDeclaredField("s2");
f.setAccessible(true);
System.out.println("f = pf.getClass().getDeclaredField(\"s2\"); f.setAccessible(true); f.get(pf) = " + f.get(pf));
// 通过反射修改私有变量s2
f.set(pf, "No, you're not!");
System.out.println("f.set(pf, \"No, you're not!\"); f.get(pf) = " + f.get(pf));
}
}
/*
private int i = 1, private final String s = I'm totally safe, private String s2 = Am I safe?
Field f = pf.getClass().getDeclaredField("i"); f.setAccessible(true); f.getInt(pf) = 1 // 通过反射机制【访问】私有变量
f.setInt(pf, 47); f.get(pf) = 47 // 通过反射机制 【修改】 私有变量
f = pf.getClass().getDeclaredField("s"); f.setAccessible(true); f.get(pf) = I'm totally safe // 通过反射机制【访问】私有常量
f.set(pf, "No, you're not!");, f.get(pf) = No, you're not! // 通过反射机制【修改】私有常量【(这个牛逼了)】
f = pf.getClass().getDeclaredField("s2"); f.setAccessible(true); f.get(pf) = Am I safe? // 通过反射机制【访问】私有变量
f.set(pf, "No, you're not!"); f.get(pf) = No, you're not! // 通过反射机制【修改】私有变量
*/
面向对象编程语言的目的:凡是可以在使用的地方都使用多态机制,只在必需的时候使用 RTTI 。
不要过早关注程序的效率问题。最好首先让程序运行起来,然后考虑他的速度。
thinking-in-java(14)类型信息