类型信息
RTTI
RTTI-runtime type information运行时类型信息
运行时识别一个对象的类型:当一个指向对象基类的引用时,RTTI机制可以找到对象的确切类型
-
两种方法
- 传统RTTI,假定编译时已经知道了所有的类型
- “反射”机制,允许运行时发现和使用类的信息
-
RTTI的优点
- 向上转型,丢失具体的类型信息,但总是安全的
- 向下转型,必须检查
-
三种形式
-
传统类型转换:
(Apple)Fruit
,如果转换类型错误,抛出ClassCastException
异常 -
通过Class对象获取对象的类型
Class c=Class.forName("Apple"); Object o=c.newInstance();
关键字instanceof、方法Class.isInstance()
确定对象是否属于特定类或其基类-
每个类都有Class对象,当编写并编译了一个新类就会产生一个Class对象,被保存在同名.class文件中
-
当类的class对象载入到内存,就被用来创建这个类的所有对象
-
Java程序运行前不是全部被加载,在各部分必须的时候才加载
-
三种获得Class对象的引用方法
Class.forName("xx.class")
- 参数:目标类的文本名
- 目的:返回目标类的引用并操作它
- 类字面量
Gum.class
- 更简单更安全,应用于类、接口、数组、基本类型
- 对于基本类型的包装类,还有标准字段TYPE,指向基本类型的class引用
obj.getClass()
获得Class引用(已知某个对象)
class Candy{static{System.out.println("loading candy")};} class Gum{static{System.out.println("loading gum")};} class Cookie{static{System.out.println("loading cookie")};} public class SweetShop{ public static void main(String[]args){ new Candy(); try{ Class.forName("Gum"); }catch(ClassNotFoundException e){ System.out.println("not found") } new Cookie(); } }
//: typeinfo/toys/ToyTest.java pacakge typeinfo.toys; import static net.mindview.util.Print.*; interface HasBatteries{} interface Waterproof{} interface Shoots{} class Toy{Toy(int i){}} Class FancyToy extends Toy implements HasBatteries,Waterproff,Shoots{ FancyToy(int i){super(i);} } public class ToyTest{ static void printInfo(Class cc){ print("class name: "+cc.getName()); print("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("not found FancyToy"); } printInfo(c); //FancyToy False FancyToy typeinfo.toys.FancyToy for(Class face:c.getInterfaces()){ printInfo(face); //typeinfo.toys.HasBatteries true HasBatteries //typeinfo.toys.HasBatteries } class up=c.getSuperclass(); Object obj=null; try{ obj=up.newInstance(); }catch(InstantiationException e){ print("cannot instantiate"); }catch(IllegalAccessException e){ print("cannot access"); } printInfo(obj.getClass); //typeinfo.toys.Toy false Toy typeinfo.toys.Toy } }
-
-
Class对象:可以查出它的祖宗十八代
getInterfaces(): Class[]、newInstance(): Object
static Class.forName("Gum"): Class
isInterface(): bool、getName(): string、getSuperclass()
-
-
类字面常量
-
类字面常量不仅可以用于普通的类,也可以用应用于接口、数组、基本数据类型
-
对于基本数据类型的包装器类,该有一个标准字段TYPE
boolean.class==Boolean.TYPE、char.class==Character.TYPE
-
TPYE字段可以是一个引用,指向对应的基本数据类型Class对象
-
语法:
类名.class
-
过程:
- 加载:类加载器执行,查找字节码(通过classpath在指定的路径查找),从字节码中创建Class对象
- 链接:验证类中的字节码,为静态域分配存储空间,可能会解释这个类创建的对其他类的所有引用
- 初始化:先初始化超类,执行静态初始化器、静态初始化模块
//: typeinfo/ClassInitialization.java import java.util.*; class Initable{ //有static+final的编译常量,不需要初始化类 static final int staticFinal=47; //有static+final的非编译常量,需要初始化类 static final int staticFianl2= ClassInitializaition.rand.nextInt(1000); static{System.out.println("initializaiton initable");} } class Initable2{ //只有static,先链接和初始化 static int staticNonFinal=147; static{System.out.println("initialization initable2");} } class Initable3{ static int staticNonFinal=74; static{System.out.println("initialization 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(Initable.staticFianl);//47 System.out.println(Initable.staticFinal2);//initializing initable 258 System.out.println(initable2.staticNonFinal);//initializing initable2 147 Class initable3=Class.forName("Initable3");//要初始化类 initializing initable3 System.out.println(Initable3.staticNonFinal);//74 } }
-
x instanceof Dog
:判断对象是不是某些类的实例,返回一个布尔值 注意:Dog是一个命名类型,而不是Class对象动态的instanceof——isInstance
-
语法:
类.isInstance(对象)
,动态测试对象的路径导出类.isInstance(基类对象)
:基类对象是否导出类的对象,基类对象 instanceof 导出类
基类.isInstance(导出类对象)
:导出类对象是否基类的对象,导出类对象 instanceof 基类
//: generics/ClassTypeCapture.java package generics; /* Added by Eclipse.py */ class Building {} class House extends Building {} public class ClassTypeCapture<T> { Class<T> kind; public ClassTypeCapture(Class<T> kind) { this.kind = kind; } public boolean f(Object arg) { return kind.isInstance(arg); } public static void main(String[] args) { ClassTypeCapture<Building> ctt1 = new ClassTypeCapture<Building>(Building.class); System.out.println(ctt1.f(new Building()));//true System.out.println(ctt1.f(new House()));//ture ClassTypeCapture<House> ctt2 = new ClassTypeCapture<House>(House.class); System.out.println(ctt2.f(new Building()));//false System.out.println(ctt2.f(new House()));//true }
-
-
反射
Java被视为动态(准动态)语言的关键性质
- 动态语言:程序运行时,允许改变程序的结构或变量类型,比如Perl、python、ruby
-
程序运行后才知道要调用哪个对象,根据客户端的String参数判断执行的方法
- 在运行时加载、探知、使用编译期间完全未知的classes
反射的方式
- 类的Class对象
- 传统的RTTI方式:在编译时打开和检查.class文件
- 反射机制:在运行时打开和检查.class文件
反射
- java.lang.Class:类对象,对构造器、属性、方法提供了四种独立的反射调用,以不同的方式来获得信息
- java.lang.reflect:
- Constructor:类的构造器对象
Constructor getConstructor(Class[] params)
获得使用特殊的参数类型的公共构造函数。Constructor[] getConstructors()
获得类的所有公共构造函数。Constructor getDeclaredConstructor(Class[] params)
获得使用特定参数类型的构造函数(与接入级别无关)Constructor[] getDeclaredConstructors()
获得类的所有构造函数(与接入级别无关)
- Field:类的属性对象
Field getField(**String name**)
获得命名的公共字段Field[] getFields()
获得类的所有公共字段Field getDeclaredField(**String name**)
获得类声明的命名的字段Field[] getDeclaredFields()
获得类声明的所有字段
- Method:类的方法对象
Method getMethod(String name, Class[] params)
使用特定的参数类型,获得命名的公共方法Method[] getMethods()
获得类的所有公共方法Method getDeclaredMethod(String name, Class[] params)
使用特写的参数类型,获得类声明的命名的方法Method[] getDeclaredMethods()
获得类声明的所有方法
- Constructor:类的构造器对象
使用反射的原则
-
获得操作的类的java.lang.Class对象
Class c=Class.forName("java.lang.Class"); Class c=init.getClass(); Class c=Integer.TYPE;
-
调用诸如
getDeclaredMethods
的方法,获得类中定义所有方法的列表Method m[]=c.getDeclaredMethods();
-
使用reflection API操作这些信息
System.out.println(m[0].toString());
import java.lang.reflect.*;
import java.awt.*;
class SampleGet {
public static void main(String[] args) {
Rectangle r = new Rectangle(100, 325);
printHeight(r);
}
static void printHeight(Rectangle r) {
Field heightField;
Integer heightValue;
Class c = r.getClass();//创建一个class对象
try {
heightField = c.getField("height");//通过getField创建一个Field对象
heightValue = (Integer) heightField.get(r);
System.out.println("Height: " + heightValue.toString());
} catch (NoSuchFieldException e) {
System.out.println(e);
} catch (SecurityException e) {
System.out.println(e);
} catch (IllegalAccessException e) {
System.out.println(e);
}
}
}
反射的功能:运行时
- 判断对象属于的类
- 构造任意一个类的对象
- 判断任意一个类具有的成员变量和方法
- 调用任意一个对象的方法
//得到某个对象的属性
public Object getProperty(Object owner, String fieldName) throws exception
{
Class ownerClass = owner.getClass();
Field field = ownerClass.getField(fieldName);
Object property = field.get(owner);
return property;
}
//得到某个类的静态属性
public Object getStaticProperty(String className, String fieldName) throws Exception
{
Class ownerClass = Class.forName(className);
Field field = ownerClass.getField(fieldName);
Object property = field.get(ownerClass);
return property;
}
//执行某对象的方法
public Object invokeMethod(Object owner, String methodName, Object[] args) throws Exception
{
Class ownerClass = owner.getClass();
Class[] argsClass = new Class[args.length];
for (int i = 0, j = args.length; i < j; i++) {
argsClass[i] = args[i].getClass();
}
Method method = ownerClass.getMethod(methodName, argsClass);
return method.invoke(owner, args);
}
//执行某个类的静态方法
public Object invokeStaticMethod(String className, String methodName,Object[] args) throws Exception
{
Class ownerClass = Class.forName(className);
Class[] argsClass = new Class[args.length];
for (int i = 0, j = args.length; i < j; i++) {
argsClass[i] = args[i].getClass();
}
Method method = ownerClass.getMethod(methodName, argsClass);
return method.invoke(null, args);
}
//新建实例
public Object newInstance(String className, Object[] args) throws Exception
{
Class newoneClass = Class.forName(className);
Class[] argsClass = new Class[args.length];
for (int i = 0, j = args.length; i < j; i++) {
argsClass[i] = args[i].getClass();
}
Constructor cons = newoneClass.getConstructor(argsClass);
return cons.newInstance(args);
}
//判断是否为某个类的实例
public boolean isInstance(Object obj, Class cls) {
return cls.isInstance(obj); }
//得到数组钟的某个元素
public Object getByArray(Object array, int index) {
return Array.get(array,index); }
反射与安全性:对反射实施应用与源代码接入相同的限制
- 从任意位置导类公共组件的接入
- 类自身外部无任何到私有组件的接入
- 受保护和打包(缺省接入)组件的有限接入
反射与性能:反射是一种解释操作,告诉JVM我们希望做什么,总是更慢
null:伴随着空指针异常
空对象:可以假设所有对象是有效的,避免检查null异常
接口和类型信息
接口:允许隔离构建,降低耦合性
//: typeinfo/InterfaceViolation.java
// Sneaking around an interface.
import typeinfo.interfacea.*;
class B implements A {
public void f() {}
public void g() {}
}
public class InterfaceViolation {
public static void main(String[] args) {
A a = new B();
a.f();
// a.g(); // 编译错误
System.out.println(a.getClass().getName());
if(a instanceof B) { // true
B b = (B)a;
b.g();
}
}
} /* Output: