RTTI:运行时类型识别,主要有两种方式:
- 传统的RTTI,在编译的时候就已知所有类型,但是不是所有的class都能在编译时明确;
- 反射机制,允许在运行市发现和使用类型的信息;
java用Class类来表示运行时的类型信息,
程序创建第一个对类的静态成员的引用时,JVM的类加载器子系统会将类对应的Class对象加载到JVM中,说明构造器也是类的静态方法,尽管构造器前面并没有static关键字修饰,如果使用new操作符操作一个类的实例对象时,会被当做静态成员的引用;
1、加载:类加载器根据类名找到此类的.Class文件,将该文件包含的字节码加载到内存中,生成class对象;
2、链接:
验证:确保class文件的字节流中包含的信息符合当前虚拟机的要求,不会危害虚拟机自身的安全;
准备:正式为类变量(static 成员变量)分配内存并设置初始值(零值)的阶段,这些变量所使用的内存都将在方法区中分配;
解析:虚拟机将常量池中的符号引用替换为直接引用的过程;
3、初始化:类在静态属性和初始化赋值,以及静态快的执行;
如下图所示:

try {
Class<?> user = Class.forName("com.test.User");//中间的字符串为类的路径
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
User user = new User();
Class<? extends com.test.User> aClass = user.getClass();
Class<User> userClass = User.class;
数据类型转换:
基本的转换思想:只有同类可转换、低级转高级可自动转换、高级转低级需强制转换;
instanceof:保持了类型的概念,而==或者equals比较的是实际的class对象,没有考虑继承
将类的各个组成部分封装成其他对象;
同一个字节码文件在一次程序运行过程中,只会加载一次,不论通过哪一种方式获取的Class对象都是同一个;
写好一个类的代码后,通过javac编译成一个class文件;通过类加载器进入第二阶段
有一个Class类对象,里面封装了成员变量类(数组)、构造方法类(数组)、成员方法类(数组)
直接创建对象
getFields():获取该类对象中所有public修饰的成员变量,用一个Field数组来存储;
getFields(String name):获取指定名称的成员变量;
Field[] getDeclaredFields():获取所有成员变量,不考虑修饰符;
field.setAccessible(true):忽略权限修饰符的安全检查;
@Data
class User {
public String name;
Integer no;
private String szf;
}
public class ClassTest {
public static void main(String[] args) {
public class ClassTest {
public static void main(String[] args) {
Class<User> userClass = User.class;
Field[] fields = userClass.getFields();
for (Field field : fields) {
System.out.println("getFields 方法 --->"+field);
}
try {
//Field szf = userClass.getField("szf");//会抛出异常
//System.out.println("getField(\"szf\") 获取 private 修饰的字段 --->"+szf);
Field szf = userClass.getDeclaredField("szf");
szf.setAccessible(false);
System.out.println("getDeclaredField(\"szf\") 获取 private 修饰的字段 忽略检查 --->"+szf);
Field name = userClass.getField("name");
System.out.println("getField(\"name\") 获取 public 修饰的字段 --->"+name);
//Field no = userClass.getField("no");//会抛出异常
//System.out.println("getField(\"no\") 获取 缺省 的字段 --->"+no);
Field no = userClass.getDeclaredField("no");
no.setAccessible(false);
System.out.println("getField(\"no\") 获取 缺省 的字段 忽略检查 --->"+no);
} catch (NoSuchFieldException e) {
throw new RuntimeException(e);
}
Field[] declaredFields = userClass.getDeclaredFields();//强制获取所有字段,无视修饰符
for (Field declaredField : declaredFields) {
System.out.println("getDeclaredFields 方法 --->"+declaredField);
}
}
}
}
}
输出结果:
getFields 方法 --->public java.lang.String com.test1.User.name
getDeclaredField("szf") 获取 private 修饰的字段 忽略检查 --->private java.lang.String com.test1.User.szf
getField("name") 获取 public 修饰的字段 --->public java.lang.String com.test1.User.name
getField("no") 获取 缺省 的字段 忽略检查 --->java.lang.Integer com.test1.User.no
getDeclaredFields 方法 --->public java.lang.String com.test1.User.name
getDeclaredFields 方法 --->java.lang.Integer com.test1.User.no
getDeclaredFields 方法 --->private java.lang.String com.test1.User.szf
获取到成员变量类之后,需要让它和实例化原类绑定,才能回去值和修改值:
Field a = class.getField("b"); Person p = new Person();//得到实例化对象 Object value = a.get(p);//a得到实例化对象,将实例化的p传进去,用Object接受 a.set(b,"eeee");
- 1
- 2
- 3
- 4
Constructor constructor1 = personClass.getConstructor();//有参数构造方法需要获取是添加具体的类对象至参数上,如:personClass.getConstructor(String.class);
System.out.println(constructor1);
//创建对象
Object person1 = constructor1.newInstance();
System.out.println(person1);
Object o = personClass.newInstance();
System.out.println(o);
//执行方法,需要传递一个实例化类:
Method amethod = class.getMethod("a");
User u = new User();
amethod.invoke(u);//有参方法添加相关的参数类型
一个人或者一个机构代表另一个人或者另一个机构采取行动,在一些情况下,一个客户不想或者不能直接引用一个对象,而代理对象可以在客户端与目标对象之间起到中介的作用。代理就是为其它对象提供一个代理以控制对某个对象的访问;
代理模式:代理类和被代理类实现共同的接口,代理类中存在指向被代理类的索引,实际执行中通过调用代理类的方法,实际执行的是被代理类的方法;
基于接口的代理( JDK代理 )和基于继承的代理(CGlib代理 );
一般的动态代理示例:
//接口
public interface FontProvider {
Font getFont(String name);
}
//真正提供的类
public class FontProviderFromDisk implements FontProvider{
@Override
public Font getFont(String name) {
System.out.println("磁盘上的文字");
return null;
}
}
//代理类
public class FontProviderProxy implements FontProvider{
private FontProviderFromDisk fontProviderFromDisk;
FontProviderProxy(FontProviderFromDisk frontProviderFromDisk){
this.fontProviderFromDisk = frontProviderFromDisk;
}
@Override
public Font getFont(String name) {
System.out.println("调用前");
Font font = fontProviderFromDisk.getFont(name);
System.out.println("调用后");
return null;
}
}
//测试类
public class MyFont {
public static void main(String[] args) {
FontProvider provider = new FontProviderProxy(new FontProviderFromDisk());
provider.getFont("字体");
}
}
测试结果:
调用前
磁盘上的文字
调用后
JDK动态代理主要涉及java.lang.reflect包下的Proxy类和InvocationHandler接口:
1、通过java.lang.reflect.Proxy类来动态生成代理类;
2、代理类要实现InvocationHandler接口;
3、JDK代理只能基于接口进行动态代理;
//Proxy类的作用就是用来创建代理对象的类,提供了很多方法,常用的就是newProxyInstance方法,这个方法是用来得到动态的代理对象,并接受三个参数:
public static Object newProxyInstance(ClassLoader loader,Class interfaces,InvocationHandler h) throws IllegalArgumentException;
loader:一个ClassLoader对象,定义了由哪个ClassLoader对象来生成代理对象进行加载;
interfaces:一个interface对象的数组,表示将要给我需要代理的对象提供一组什么接口,如果我提供一组接口给他,就会调用这组接口中的方法;
h:一个InvocationHandler对象,表示的是动态代理在调用方法的时候,会关联哪一个InvocationHandler对象上;
示例:
//接口,动态代理的前提
public interface JDKSubject {
void request();
void hello();
}
//目标对象
public class JDKSubjectImpl implements JDKSubject{
@Override
public void request() {
System.out.println("JDKSubjectImpl -------> request");
}
@Override
public void hello() {
System.out.println("JDKSubjectImpl -------> hello");
}
}
//代理类
public class ProxyJDKSubject implements InvocationHandler {
private Object subject;
ProxyJDKSubject(Object subject){
this.subject = subject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("start");
Object result = null;
try{
result = method.invoke(subject, args);
}catch (Exception e){
System.out.println(e.getMessage());
e.printStackTrace();
} finally {
System.out.println("end");
}
return result;
}
}
//测试类
public class Client {
public static void main(String[] args) {
JDKSubjectImpl impl = new JDKSubjectImpl();
InvocationHandler handler = new ProxyJDKSubject(impl);
JDKSubject o = (JDKSubject) Proxy.newProxyInstance(Client.class.getClassLoader(), new Class[]{JDKSubject.class}, handler);
o.hello();
o.request();
}
}
测试结果:
start
JDKSubjectImpl -------> hello
end
start
JDKSubjectImpl -------> request
end
ASM字节码生成框架,使用字节码技术生成代理类,为一个类创建子类,并在子类中采用方法拦截的技术拦截所有对父类方法的调用,并顺势加入横切逻辑。CGlib是针对类来实现代理的,原理是对指定的业务类生成一个子类,覆盖其中的业务方法实现代理,**因为采用的是继承,所以不能对final修饰的类进行代理 **,也是通过方法反射调用目标对象的方法;示例:
//目标类
public class RealSubject {
public void request(){
System.out.println("RealSubject is ----> request");
}
public void hello(){
System.out.println("RealSubject is ----> hello");
}
}
//代理类
public class CGlibProxy implements MethodInterceptor {
private Object target;
public Object getInstance(Object target){
this.target = target;
Enhancer enhancer = new Enhancer();//创建加强器,用来创建动态代理类
enhancer.setSuperclass(this.target.getClass());
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("cglib start--");
Object result = null;
try {
result = methodProxy.invokeSuper(o,objects);
}catch (Exception e){
System.out.println(e);
throw e;
}finally {
System.out.println("cglib end--");
}
return result;
}
}
//测试类
public class Client {
public static void main(String[] args) {
RealSubject instance = (RealSubject) new CGlibProxy().getInstance(new RealSubject());
instance.request();
instance.hello();
}
}
测试结果:
cglib start--
RealSubject is ----> request
cglib end--
cglib start--
RealSubject is ----> hello
cglib end--
通过简单学习,学会简单使用反射和代理,通常情况下,使用代理的效率会高于使用反射的效率,所以适合使用代理的情况就使用代理会更好!!!