@(簡書)
引言
工作中,有時感覺對Java的理解不夠深雳攘,想補補。以后汁尺,不定期更新《重新看編程思想》《嗦桑《Java編程思想》這本書從大一開始伴隨著我痴突,再看這本書感覺全是回憶。
RTTI(Run-Time Type Identification))
- 理解泛型狼荞,首先得知道RTTI是什么辽装?
運行時類型信息可以在程序運行時發(fā)現(xiàn)和使用類型信息。
下面看一個例子相味,來看看RTTI的作用拾积。
定義一個抽象類,聲明
abstract
方法丰涉。
public abstract class Shape {
void draw() {
System.out.println(this + ".draw()");
}
abstract public String printStr();
}
- 繼承抽象類拓巧,重寫基類方法。
public class Square extends Shape {
@Override
public String printStr() {
return "Square";
}
}
public class Triangle extends Shape {
@Override
public String printStr() {
return "Triangle";
}
}
public class Circle extends Shape {
@Override
public String printStr() {
return "Circle";
}
}
- 測試運行類
public class Shapes {
public static void main(String[] args) {
List<Shape> shapes = new ArrayList<Shape>() {{
add(new Circle());
add(new Triangle());
add(new Square());
}};
for (Shape shape : shapes) {
System.out.println(shape.printStr());
shape.draw();
}
}
}
- 運行結果
Circle
fanxing.Circle@54bedef2.draw()
Triangle
fanxing.Triangle@5caf905d.draw()
Square
fanxing.Square@27716f4.draw()
分析
對于
Shape
列表來說一死,把Circle
肛度、Triangle
、Square
放入時投慈,都會被向上轉型承耿,即對列表而言,都是Shape
對象伪煤。當遍歷元素時加袋,容器會將所有事物當做
Object
持有,并自動轉型回Shape
抱既,這就是RTTI的基本使用形式职烧,在運行時,識別一個對象的類型蝙砌。可以看出阳堕,RTTI的轉型并不徹底,只是從
Object
轉型為Shape
择克,沒有轉型為具體的Circle
:
Object -> Shape ? -> Circle
- 那么,為什么執(zhí)行了
Circle
的代碼呢前普?
多態(tài)使得
Shape
對象執(zhí)行引用所指向的具體對象的代碼肚邢。
Class對象
理解RTTI的工作原理,首先要知道類型信息在運行時是如何表示的,即Class
對象骡湖。
每個類都有一個
Class
對象(每當編寫并且編譯一個類)贱纠,對象被載入內(nèi)存,就會被用來創(chuàng)建這個類的對象响蕴。
Class
對象還可以使用另外一種方法生成谆焊,即Gum.class
,即類字面常量浦夷。
try {
// forName方式
Class<?> gum = Class.forName("fanxing.studyclass.Gum");
System.out.println(gum.getCanonicalName());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
// 類字面常量
System.out.println(Gum.class.getCanonicalName());
- 為
.class
創(chuàng)建Class
對象引用的“三步準備”辖试。
加載:類加載器執(zhí)行,查找字節(jié)碼劈狐,并從字節(jié)碼中創(chuàng)建一個Class對象罐孝。
鏈接:驗證類的字節(jié)碼,為靜態(tài)域分配存儲空間肥缔,解析這個類創(chuàng)建的對其他類的引用莲兢。
初始化:如果該類具有超類,對其初始化续膳,執(zhí)行靜態(tài)初始化器和靜態(tài)初始化塊改艇。
public class Initable {
static final int a = 47;
static final int b = new Random().nextInt(1000);
static {
System.out.println("初始化 Initable");
}
}
public class Initable2 {
static int a = 147;
static {
System.out.println("初始化 Initable2");
}
}
public class Initable3 {
public static int a = 74;
static {
System.out.println("初始化 Initable3");
}
}
public static void main(String[] args) throws ClassNotFoundException {
// 沒有引發(fā)Initable初始化
Class initable = Initable.class;
System.out.println("創(chuàng)建Initable class對象引用");
// 沒有引發(fā)Initable初始化
System.out.println(Initable.a);
// 引發(fā)初始化
System.out.println(Initable.b);
// Initable2 初始化
System.out.println(Initable2.a);
Class initable3 = Class.forName("fanxing.leizimianchangliang.Initable3");
System.out.println("創(chuàng)建Initable3 class對象引用");
System.out.println(Initable3.a);
}
分析
初始化為“惰性”,static final值如果為編譯期常量坟岔,不會引發(fā)初始化遣耍。
.class獲得對類的引用,不會引發(fā)初始化炮车,但是forname產(chǎn)生class引用舵变,立即引發(fā)初始化。
反射:運行時的類信息
- 使用反射瘦穆,進行類方法提取
try {
Class<?> c = Class.forName(args[0]);
Method[] methods = c.getMethods();
Constructor[] constructors = c.getConstructors();
if (args.length == 1) {
for (Method method : methods) {
System.out.println(p.matcher(method.toString()).replaceAll(""));
}
for (Constructor constructor: constructors) {
System.out.println(p.matcher(constructor.toString()).replaceAll(""));
}
lines = methods.length + constructors.length;
} else {
for (Method method: methods) {
if (method.toString().contains(args[1])) {
System.out.println(p.matcher(method.toString()).replaceAll(""));
lines++;
}
for (Constructor constructor:constructors) {
if (constructor.toString().contains(args[1])) {
System.out.println(p.matcher(constructor.toString()).replaceAll(""));
lines ++;
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
動態(tài)代理
public class DynamicProxyHandler implements InvocationHandler {
private Object proxied;
public DynamicProxyHandler(Object proxied) {
this.proxied = proxied;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("proxy: " + proxy.getClass() + " method: " + method + " args: " + args);
if (args != null) {
for (Object s : args) {
System.out.println(" arg");
}
}
return method.invoke(proxied, args);
}
}
public class SimpleDynamicProxy {
public static void consumer(Interface in) {
in.doSomething();
in.somethingElse("bo");
}
public static void main(String[] args) {
RealObject object = new RealObject();
consumer(object);
// 動態(tài)創(chuàng)建代理
Interface proxy = (Interface) Proxy.newProxyInstance(
Interface.class.getClassLoader(),
new Class[]{Interface.class},
new DynamicProxyHandler(object));
consumer(proxy);
}
結語
- 理解上述概念有助于后續(xù)對一些框架源碼的閱讀纪隙,例如反射、動態(tài)代理會在很多框架源碼中看到扛或。
- 后續(xù)自己對一些業(yè)務功能绵咱,進行封裝也可以考慮動態(tài)代理。
參考文獻
- Java編程思想