首先,知道一种区别:
编译时可操作类型信息;
运行时识别类型信息{1-RTTI;2-反射机制};
RTTI:在运行时,识别对象的类型。
1-运行时类型识别的必要性:
多态中,向上转型,动态绑定的场景,方法和方法体在运行时才关联起来。
接口类引用=具体对象;
接口类引用.function();运行时识别具体对象类型,方法动态绑定。
从数组中取出数据时,数组将所有事物当成Object类型,会自动转型成对应类型,这是RTTI最基本的使用形式。
所有的类型转换,都是在运行时进行正确性检查。
抛出问题:怎样知道泛化引用的确切类型?
使用RTTI可以知道。
2-Class对象
类型信息在运行时如何表示?--->由Class对象来表示--包含类信息的对象。
Class对象作用:1.用来创建类的所有常规对象。
2.RTTI.
每个类都有一个Class对象,编写并且编译了一个新类,就会产生一个Class对象,保存在.class文件中。
jvm使用-类加载器-生成-Class对象。
所有类的加载,是在其第一次被使用时,动态加载到jvm.
创建第一个对类的静态成员的引用时,就会加载这个类。因此可以知道:构造器也是类的静态方法,即使没有static关键字。因此,使用构造方法也是对类的静态成员的引用。
java程序在运行前并非完全加载完毕,而是“按需加载”。
当把Class对象加载到内存,便可用它来创建类的所有对象。
Class.forName()-----获取Class对象的引用,若没被加载则加载。
必须先获取Class对象的引用,才能在运行时获取类型信息。
获取Class对象的引用3种方法:
(1) Class.forName()----不需要具体对象就可以获取Class对象的引用;
(2) 具体对象.getClass()-----也可以获取Class对象的引用;
(3) 类字面常量:ClassName.class;简单安全高效,注意:不会自动初始化该类。 也就是说,static final 常量 不会初始化类,也即 不用初始化类就可以获取其值。static final 非常量则会初始化类。
Class对象有一些有用的方法:getName();getSimpleName();getSuperclass();
加载:类的加载是指把.class文件中的二进制数据读入到内存中,把他放在运行时的数据区的方法区内,后在堆区创建一个Class的对象。
java虚拟机可以从多种来源加载类的二进制数据,
a) 从本地文件系统中加载类的.class文件,常用的方式
b) 通过网络下载.class文件
c) 从ZIP、JAR或其他类型提取.class文件
d) 从一个专有数据库中提取.class文件
e) 把一个Java源文件动态编译为.class文件
初始化:为类的静态变量赋予正确的初始值,执行静态块。若有超类则先对其进行初始化。
易混点:1.对类的静态变量赋予正确的初始值,执行静态块,是在初始化时进行的,并非是在类加载时执行的。
2.初始化!=实例化
3-泛化的Class引用
对Class对象的类型进行限定,提供编译期类型检查---用到泛型。--通过泛型可以让编译器强制执行额外的类型检查。--若想放松该限制,可用通配符“?”。
java泛型的通配符“?”表示任何事物。
Class<?>优于普通的Class,前者表明是我明确选择了非具体类型,不是碰巧和疏忽。
Class<? extends SuperClass> ---SuperClass的任何子类。
Class<? super sonClass> ---sonClass的任何超类。
可以使用Class引用来创建类的实例。方法:newInstance().
易错点:该类必须与它一同工作的任何类都有一个无参构造器。否则会运行时异常。
RTTI在Java中的三种存在形式:
1,经典造型,首字母大写的类型名,如“Shape”,它用RTTI确保造型的正确性。
2,代表对象类型的Class对象。可查询Class对象,获取有用的运行期资料。
3.,instanceof 告诉我们对象是不是一个特定类型的实例。
类型转换前先做检查---instanceof : 只可与类型名称进行比较,不可与Class对象做比较。
动态的instance----isInstance(obj) : 动态体现在哪里???
instanceof与Class的等价性:
区别:
a)直接判断Class对象; ---比较实际的Class对象,未考虑继承关系。
b)使用instanceof或isInstance()判断对象。--考虑了继承关系。
例子:
x.getClass()==y.getClass();
x instanceof y
class Xx{}
class Yy extends Xx{}
public class Bj {
public static void main(String[] args) {
System.out.println(new Xx().getClass()==new Yy().getClass());
System.out.println(new Xx().getClass().equals(new Yy().getClass()));
System.out.println(new Yy() instanceof Xx);
System.out.println(Xx.class.isInstance(new Yy()));
}
}
4-反射
---使用反射机制,可以在运行时通过类名获得类信息;通过对象获得对象信息。
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;
对于任意一个对象,都能够调用它的任意方法和属性;
这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。
Class类与java.lang.reflect类库共同对反射概念进行了支持。
该类库包含了Field,Method,Constructor类。
这些类型的对象由jvm在运行时创建,用以表示未知类里对应的成员。
Constructor---创建新对象,
get()与set()--获取和修改与Field对象关联的字段,
invoke()------调用与Method对象关联的方法。
反射与RTTI区别: 反射比RTTI更懒。
RTTI:在编译时打开和检查.class文件;
反射:在运行时打开和检查.class文件。
{动态语言,静态语言}---划分---:在运行时是(动态)否(静态)可以改变程序的结构和变量类型。
用点菜的例子比喻反射和RTTI:
反射:菜单(.class文件),点菜记录表。我在点菜记录表上手写的菜名,在菜单上可以找到,在记录表内我可以动态得写下菜名;
RTTI:直接在菜单(.class文件)上勾出你想点的菜。
Class.forName()-----生成的结果在编译时是未知的,所有信息实在执行时被提取出来。
通过反射机制,可以创建编译时完全未知的对象,并调用此对象的方法。
5-动态代理
代理:中间人。不直接操作实现对象,通过代理对象间接操作实现对象。
实现对象--也称被代理对象。
动态代理:动态体现在{1.动态创建代理对象;2.动态调用方法}
Proxy.newProxyInstance(); 创建动态代理对象
实现InvocationHandler调用处理器接口,重写invoke(),将请求发送给被代理对象,并传入必要参数。