反射基础知识
1.概念:Class类实例表示正在运行的Java应用程序中类和接口2.无公共构造方法Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的每一个在运行的类都会有一个Class实例的存在,该实例由虚拟机获取3.获取XXXXX获取某类的Class实例方法一:反射 Class.forName()方法二:对象名.getClass()方法三:类名.c
·
类的加载步骤
1.加载
- 通过全路径名获取二进制字节流文件
二进制流的来源
1.从压缩包(jar war ear)中获取
2.从网络中获取(Apple技术)
3.**从运行过程中动态生成(动态代理) -- Proxy-->$Proxy (AVRO,Tlwift等格式可以在运行时将某个schema文件生成对应的若干class再加载)
4.从其他文件生成(JSP文件生成class类)
5.从数据库中读取
- 将字节流文件静态数据结构转化为方法区中运行的数据结构
- 在内存中生成代表这个类的Class对象,作为方法区中该类数据结构的访问入口
- 说明
1.任何类被使用时,系统都会创建一个Class对象
2.**加载通过程序能控制
2.连接
- 将已读入到内存的类的二进制数据合并到虚拟机运行时的环境中去
- 验证
确保类的正确性+保证字节流中包含的文件符号当前虚拟机的要求,并且不会危害虚拟机的安全
1.为啥要验证字节码?
Class文件的产生不强制要求必须是Java代码编译而来,甚至可以自己用十六进制编辑器直接编写
2.验证什么?
类文件结构+语义检查+字节码验证+二进制兼容性验证
- 准备
为静态成员在方法区分配内存,并设置默认值
- 解析
将类的二进制数据中的符号引用替换为直接引用
对象名.方法名()替换成指针,该指针指向类的方法在方法区中内存的位置
3.初始化
- 真正执行Java类中代码
- 根据程序员写的代码去初始化变量和资源
- 类的主动使用与被动使用
1.JVM启动后,对类的初始化是一个延迟机制,即JVM虚拟机实现必须在每个类或接口被Java程序首次主动使用时初始化他们(使用此类时才会才会加载)
2.六种主动使用的场景
创建类的实例+访问或对某个类或接口的静态变量赋值+调用类的静态方法+反射+初始化类的子类(直接通过子类访问父类的静态变量会导致父类初始化,子类不会)+启动类sun.misc.Launcher
3.除以上六种情况都叫被动使用,不会导致类的加载和初始化(构造某个类的数组不会引起该类初始化+引用类的静态常量不会导致类的初始化)
4.使用
- new
5.unloading从jvm中移除次实例
类加载器
1.作用
- 将Class文件读入内存,并位置创建一个Class对象
2.种类
- 根类(启动类)加载器 Bootstrap ClassLoader
1.负责Java核心类库的加载(jre/lib),只将能被虚拟机识别的类库加载到内存中
2.可通过-Xbootclasspath指定,也可通过系统属性sun.boot.class.path获取当前根类加载器加载到资源
3.C++编写
- 扩展类加载器 Extension ClassLoader
1.负责jre扩展目录jre/lib/ext中jar包的加载
2.通过系统属性java.ext.dirs获取加载情况
3.Java编写,类名:sun.misc.Launcher$ExtClassLoader
- 应用程序类加载器 Application ClassLoader
1.也称系统类加载器
2.负责用户路径classpath下指定类库的加载
3.可通过-classpath指定,也可通过系统属性java.class.path获取
3.双亲委派模型
- 双亲委派模型:三种类加载器的加载顺序
Application在缓存中找该字节码,找到就直接返回,找不在就去Extention中找
Extention在缓存中找该字节码,找到就直接返回,找不在就去Bootstrap中找
Bootstrap在缓存中找该字节码,找到就直接返回
找不在就去JAVA_HOME/jre/lib路径中找
找不到就去jre/lib/ext找
找不到就去Application类路径下找
还找不到就抛出异常ClassNotFound异常
- 统一整个系统的结构
反射
1.概述
- Java语言的反射机制
在运行状态中
对任意一个类,都可知道这个类所有的属性和方法
对任意一个对象,都能调用它的属性和方法
===>动态获取信息
- Class类实例表示正在运行的Java应用程序中类和接口
2.获取Class类实例的方法
- Class类静态方法: Class.forName(“全路径”)
开发中常用Class类静态方法获取class实例
因为全路径名是一个字符串,而不是一个具体的类,可以将该字符串配置到配置文件中
- Object方法: 对象名.getClass()
- 数据类型的静态属性: 类名.class
- 注意
不管new几个对象,同一个类的Class类实例地址值是相同的
3.通过反射获取构造方法
- 获取单个(public+private)构造方法(无参+有参)
1.获取字节码文件对象
Class c = Class.forName("全路径");
2.获取单个公共无参构造方法
//public Constructor<T> getConstructor(Class<?>... parameterTypes)
//参数:要获取的构造方法的参数数据类型的class字节码文件对象
Constructor con = c.getConstructor();// 返回的是构造方法对象
//public T newInstance(Object... initargs)
//使用此Constructor对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例
Object obj = con.newInstance();
System.out.println(obj);//全路径对象
3.获取单个公共有参构造方法
//public Constructor<T> getConstructor(Class<?>... parameterTypes)
Constructor con = c.getConstructor(String.class, int.class,String.class);
//public T newInstance(Object... initargs)
Object obj = con.newInstance("name1", 20, "北京");
System.out.println(obj);
4.获取单个私有构造方法
//IllegalAccessException:非法的访问异常
Constructor con = c.getDeclaredConstructor(String.class);
//暴力访问
con.setAccessible(true);//值为true则指示反射的对象在使用时应该取消Java语言访问检查
Object obj = con.newInstance("name1");
System.out.println(obj);
- 获取所有public构造方法
1.获取字节码文件对象
Class c = Class.forName("全路径");
2.获取public构造方法
//public Constructor[] getConstructors():所有公共构造方法
Constructor[] cons = c.getConstructors();
for (Constructor con : cons) {
System.out.println(con);
}
- 获取所有(public+private)构造方法
1.获取字节码文件对象
Class c = Class.forName("全路径");
2.获取public+private构造方法
//public Constructor[] getDeclaredConstructors():所有构造方法
Constructor[] cons = c.getDeclaredConstructors();
for (Constructor con : cons) {
System.out.println(con);
}
4.通过反射获取成员变量
- 获取单个(public+private)成员变量并赋值
1.获取字节码文件对象
Class c = Class.forName("全路径");
2.通过无参构造方法创建对象
Constructor con = c.getConstructor();
Object obj = con.newInstance();
2.获取单个公共成员变量并赋值
Field field = c.getField("变量名");
//public void set(Object obj,Object value)
field.set(obj, "北京");//给obj对象的field字段设置值为"北京"
System.out.println(obj);
3.获取单个私有成员变量并赋值
Field field = c.getDeclaredField("变量名");
field.setAccessible(true);
field.set(obj, "北京");
System.out.println(obj);
- 获取所有public成员变量
1.获取字节码文件对象
Class c = Class.forName("全路径");
2.获取public成员变量
Field[] fields = c.getFields();
for (Field field : fields) {
System.out.println(field);
}
- 获取所有(public+private)成员变量
1.获取字节码文件对象
Class c = Class.forName("全路径");
2.获取public+private成员变量
Field[] fields = c.getDeclaredFields();
for (Field field : fields) {
System.out.println(field);
}
5.通过反射获取成员方法
- 获取单个(public+private)成员方法并使用
1.获取字节码文件对象
Class c = Class.forName("全路径");
2.通过无参构造方法创建对象
Constructor con = c.getConstructor();
Object obj = con.newInstance();
3.获取单个公共无参成员方法并使用
//public Method getMethod(String name,Class<?>... parameterTypes)
//第一个参数表示的方法名,第二个参数表示的是方法参数的class类型
Method m1 = c.getMethod("方法名");
//public Object invoke(Object obj,Object... args)
//返回值是Object接收,第一个参数表示激活方法的对象,第二参数表示给方法传入的实参
m1.invoke(obj); //调用obj对象的m1方法,不传入参数
4.获取单个公共有参成员方法并使用
//public Method getMethod(String name,Class<?>... parameterTypes)
Method m2 = c.getMethod("method", String.class);
m2.invoke(obj, "hello");
5.获取单个私有成员方法并使用
Method m4 = c.getDeclaredMethod("方法名");
//暴力访问
m4.setAccessible(true);
m4.invoke(obj);
- 获取所有public成员方法,包括父类的
1.获取字节码文件对象
Class c = Class.forName("全路径");
2.获取public成员方法,包括父类的
Method[] methods = c.getMethods(); // 获取自己的包括父亲的公共方法
for (Method method : methods) {
System.out.println(method);
}
- 获取所有(public+private)成员方法
1.获取字节码文件对象
Class c = Class.forName("全路径");
2.获取public+private成员方法
Method[] methods = c.getDeclaredMethods();
for (Method method : methods) {
System.out.println(method);
}
6.通过反射运行配置文件内容
- class.txt(配置文件)
className=类的全路径名
methodName=方法名
- 使用反射加载
1.加载数据
Properties p = new Properties();
FileRead fr = new FileRead("class.text");
p.load(fr);
fr.close();
2.获取数据
String className = p.getProperty("className");
String methodName = p.getProperty("methodName");
3.反射创建类实例
Class c = Class.forName(className);
Constructor con = c.getConstructor();
Object obj = con.newInstance();
4.调用方法
Method method = c.getMethod(methodName);
method.invoke(obj);
7.通过反射越过泛型检查
- 向ArrayList中添加一个字符串数据
ArrayList<Integer> array = new ArrayList<Integer>();
Class c = array.getClass();
Method method = c.getMethod("add",Object.class);
m.invoke(array,"java");
8.通过反射知道某对象的某属性为指定的值
- 将Object value的值设置给obj对象的属性String name
Class c = obj.getClass();
Field field = c.getDeclaredField("name");
field.setAccessible(true);
field.set(obj,value);
动态代理
1.概述
- 代理
本来自己要做的事情,却请了别人来做,被请的人就是代理对象
- 动态代理
在程序运行过程中产生这个对象,而反射正是在程序运行过程中产生这个对象
动态代理其实就是利用反射生成代理
2.生成动态代理对象
- 概述
Proxy类
InvocationHandler接口
注意:JDK提供的代理只能争对接口做代理
- 实现
public class MyInvocationHandler implements InvocationHandler {
private Object target; // 目标对象obj
public MyInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {
//proxy代理实例
//method对应代理实例接口上的方法
//args接口上方法的参数
Object result = method.invoke(target, args);
return result; // 返回的是代理对象
}
}
===============================================
public class Test {
public static void main(String[] args) {
UserDao ud = new UserDaoImpl();
// 准备对ud对象做一个代理对象
MyInvocationHandler handler = new MyInvocationHandler(ud);
// Proxy类中有一个方法可以创建动态代理对象
// public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
UserDao proxy = (UserDao) Proxy.newProxyInstance(ud.getClass().getClassLoader(), ud.getClass().getInterfaces(), handler);
proxy.add();
proxy.delete();
proxy.update();
proxy.find();
}
}
更多推荐
所有评论(0)