类型信息
class 对象
类是程序的一部分,每个类都有一个 Class
对象。为了生成这个类的对象,Java 虚拟机 (JVM) 先会调用"类加载器"子系统把这个类加载到内存中。类加载器首先会检查这个类的 Class
对象是否已经加载,如果尚未加载,默认的类加载器就会根据类名查找 .class
文件。
- 所有
Class
对象都属于Class
类,
Class.forName("Gum");
我们可以使用 forName()
根据目标类的类名(String
)得到该类的 Class
对象.如果找不到要加载的类,它就 会抛出异常 ClassNotFoundException
。使用该方法你不需要先持有这个类型 的对象。
- 如果你已经拥有了目标类的对象,那就可以调用
对象名.getClass()
方法来获取Class
引用。
类字面量
类名.class
来获取类对象的使用。效率更高。不仅可以应用于普通类,也可以应用于接口、数组以及基本数据类型。
加载
,这是由类加载器执行的。该步骤将查找字节码(通常在classpath
所指定的路径中查找,但这并非是必须的),并从这些字节码中创建一个Class
对象。链接
。在链接阶段将验证类中的字节码,为static
字段分配存储空间,并且如果需要的话,将解析这个类创建的对其他类的所有引用。初始化
。如果该类具有超类,则先初始化超类,执行static
初始化器和static
初始化块。
-
当使用
.class
来创建对Class
对象的引用时,不会自动地初始化该Class
对象。直到第一次引用一个static
方法(构造器隐式地是static
)或者非常量的static
字段,才会进行类初始化 -
如果一个
static final
值是“编译期常量”,那么这个值不需要对 类进行初始化就可以被读取。但是,如果只是将一个字段设置成为static
或final
,还不足以确保这种行为。 -
如果一个
static
字段不是final
的,那么在对它访问时,总是要求在它被读取之前,要先进行链接(为这个字段分配存储空间)和初始化(初始化该存储空间)
import java.util.*;
class Initable {
static final int STATIC_FINAL = 47;
static final int STATIC_FINAL2 =
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.STATIC_FINAL);
引起初始化
System.out.println(Initable.STATIC_FINAL2);
引起初始化
System.out.println(Initable2.staticNonFinal);
Class initable3 = Class.forName("Initable3");
System.out.println("After creating Initable3 ref");
System.out.println(Initable3.staticNonFinal);
}
}
输出结果:
After creating Initable ref
47
Initializing Initable
258
Initializing Initable2
147
Initializing Initable3
After creating Initable3 ref
74
- 可以使用泛型对
Class
引用所指向的Class
对象的类型进行限定。
Class<Number> geenericNumberClass = int.class;
Integer
继承自 Number
。但事实却是不行,因为 Integer
的 Class
对象并不是 Number
的 Class
对象的子类
- 将通配符与
extends
关键字配合使用,创建一个范围限定。
public class BoundedClassReferences {
public static void main(String[] args) {
Class<? extends Number> bounded = int.class;
bounded = double.class;
bounded = Number.class;
}
}
- 使用
泛型
产生确切类型
Class<? super FancyToy> up = ftClass.getSuperclass();
Object obj = up.newInstance(); 返回object对象
- 关键字
instanceof
。它返回一个布尔值,告诉我们对象是不是某个特定类型的实例,
if(x instanceof Dog)
((Dog)x).bark();向下转型
-
有一个带一个参数的构造器,编译器不会自动地为我们加上无参构造器。
-
类
Class
支持反射的概念,java.lang.reflect
库中包含类Field
、Method
和Constructor
(每一个都实现了Member
接口)。
动态代理
可以通过调用静态方法 Proxy.newProxyInstance()
来创建动态代理,该方法需要一个类加载器(通常可以从已加载的对象中获取),希望代理实现的接口列表(不是类或抽象类),以及接口 InvocationHandler
的一个实现。
import java.lang.reflect.*;
interface SomeMethods {
void boring1();
void boring2();
void interesting(String arg);
void boring3();
}
class MethodSelector implements InvocationHandler {
private Object proxied;
MethodSelector(Object proxied) {
this.proxied = proxied;
}
@Override
public Object
invoke(Object proxy, Method method, Object[] args)
throws Throwable {
if (method.getName().equals("interesting"))
System.out.println(
"Proxy detected the interesting method");
return method.invoke(proxied, args);
}
}
class Implementation implements SomeMethods {
@Override
public void boring1() {
System.out.println("boring1");
}
@Override
public void boring2() {
System.out.println("boring2");
}
@Override
public void interesting(String arg) {
System.out.println("interesting " + arg);
}
@Override
public void boring3() {
System.out.println("boring3");
}
}
class SelectingMethods {
public static void main(String[] args) {
SomeMethods proxy =
(SomeMethods) Proxy.newProxyInstance(
SomeMethods.class.getClassLoader(),
new Class[]{ SomeMethods.class },
new MethodSelector(new Implementation()));
proxy.boring1();
proxy.boring2();
proxy.interesting("bonobo");
proxy.boring3();
}
}
输出结果:
boring1
boring2
Proxy detected the interesting method
interesting bonobo
boring3