文章目录
Java-14反射
一、Java反射机制概述
- Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期 借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内 部属性及方法。
- 加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个 类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可 以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看 到类的结构,所以,我们形象的称之为:反射。
1.反射提供的功能
在运行时判断任意一个对象所属的类
* 在运行时构造任意一个类的对象
* 在运行时判断任意一个类所具有的成员变量和方法
* 在运行时获取泛型信息
* 在运行时调用任意一个对象的成员变量和方法
* 在运行时处理注解
* 生成动态代理
二、理解Class类并获取Class实例
public final Class getClass()
1.Class类的常用方法
方法名 | 功能 |
---|---|
static Class forName(String name) | 返回指定类名 name 的 Class 对象 |
Object newInstance() | 调用缺省构造函数,返回该Class对象的一个实例 |
getName() | 返回此Class对象所表示的实体(类、接口、数组类、基本类型 或void)名称 |
Class getSuperClass() | 返回当前Class对象的父类的Class对象 |
Class [] getInterfaces() | 获取当前Class对象的接口 |
ClassLoader getClassLoader() | 返回该类的类加载器 |
Class getSuperclass() | 返回表示此Class所表示的实体的超类的Class |
Constructor[] getConstructors() | 返回一个包含某些Constructor对象的数组 |
Field[] getDeclaredFields() | 返回Field对象的一个数组 |
Method getMethod(String name,Class … paramTypes | 返回一个Method对象,此对象的形参类型为paramType |
public void test2() throws Exception{
Class clazz = Person.class;
//1.通过反射,创建Person类的对象
Constructor constructor = clazz.getConstructor(String.class, int.class);
Object obj = constructor.newInstance("Tom", 12);
Person p = (Person) obj;
System.out.println(obj.toString());
//2.通过反射,调用对象指定的属性、方法
//调用属性
Field age = clazz.getDeclaredField("age");
age.set(p,10);
System.out.println(p);
//调用方法
Method show = clazz.getDeclaredMethod("show");
show.invoke(p);
//通过反射,可以调用Person类的私有结构的。比如:私有的构造器、方法、属性
Constructor cons1 = clazz.getDeclaredConstructor(String.class);
cons1.setAccessible(true);
Person p1 = (Person)cons1.newInstance("Jerry");
System.out.println(p1);
//调用私有属性
Field name = clazz.getDeclaredField("name");
name.setAccessible(true);
name.set(p1,"Tom");
System.out.println(p1);
//调用私有的方法
Method showNation = clazz.getDeclaredMethod("showNation", String.class);
showNation.setAccessible(true);
showNation.invoke(p1,"中国");
}
三、类的加载与ClassLoader的理解
- 加载:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时 数据结构,然后生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问 入口(即引用地址)。所有需要访问和使用类数据只能通过这个Class对象。这个加载的 过程需要类加载器参与。
- 链接:将Java类的二进制代码合并到JVM的运行状态之中的过程。
- 验证:确保加载的类信息符合JVM规范,例如:以cafe开头,没有安全方面的问题
- 准备:正式为类变量(static)分配内存并设置类变量默认初始值的阶段,这些内存 都将在方法区中进行分配。
- 解析:虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程。
- 初始化:
- 执行类构造器()方法的过程。类构造器()方法是由编译期自动收集类中 所有类变量的赋值动作和静态代码块中的语句合并产生的。(类构造器是构造类信 息的,不是构造该类对象的构造器)。
- 当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类 的初始化。
- 虚拟机会保证一个类的()方法在多线程环境中被正确加锁和同步。
//1.获取一个系统类加载器
ClassLoader classloader = ClassLoader.getSystemClassLoader();
System.out.println(classloader);
//2.获取系统类加载器的父类加载器,即扩展类加载器
classloader = classloader.getParent();
System.out.println(classloader);
3.获取扩展类加载器的父类加载器,即引导类加载器
classloader = classloader.getParent();
System.out.println(classloader);
//4.测试当前类由哪个类加载器进行加载
classloader = Class.forName("exer2.ClassloaderDemo").getClassLoader();
System.out.println(classloader);
//5.测试JDK提供的Object类由哪个类加载器加载
classloader = Class.forName("java.lang.Object").getClassLoader();
System.out.println(classloader);
//*6.关于类加载器的一个主要方法:getResourceAsStream(String str):获取类路径下的指定文件的输入流
InputStream in = null;
in = this.getClass().getClassLoader().getResourceAsStream("exer2\\test.properties");
System.out.println(in);
四、创建运行时类的对象
//1.根据全类名获取对应的Class对象
String name = “atguigu.java.Person";
Class clazz = null;
clazz = Class.forName(name);
//2.调用指定参数结构的构造器,生成Constructor的实例
Constructor con = clazz.getConstructor(String.class,Integer.class);
//3.通过Constructor的实例创建对应类的对象,并初始化类属性
Person p2 = (Person) con.newInstance("Peter",20);
System.out.println(p2);
五、获取运行时类的完整结构
方法 | 功能 |
---|---|
public Class[] getInterfaces() | 确定此对象所表示的类或接口实现的接口。 |
public Class getSuperclass() | 返回表示此 Class 所表示的实体(类、接口、基本类型)的父类的 Class。 |
public Constructor[] getConstructors() | 返回此 Class 对象所表示的类的所有public构造方法。 |
public Constructor[] getDeclaredConstructors() | 返回此 Class 对象表示的类声明的所有构造方法。 |
public Method[] getDeclaredMethods() | 返回此Class对象所表示的类或接口的全部方法 |
public Method[] getMethods() | 返回此Class对象所表示的类或接口的public的方法 |
public Field[] getFields() | 返回此Class对象所表示的类或接口的public的Field。 |
public Field[] getDeclaredFields() | 返回此Class对象所表示的类或接口的全部Field。 |
get Annotation(Class annotationClass) | 注解 |
getDeclaredAnnotations() | 注解 |
Constructor类中:
- 取得修饰符: public int getModifiers();
- 取得方法名称: public String getName();
- 取得参数的类型:public Class[] getParameterTypes();
Method类中:
- public Class getReturnType()取得全部的返回值
- public Class[] getParameterTypes()取得全部的参数
- public int getModifiers()取得修饰符
- public Class[] getExceptionTypes()取得异常信息
Field方法中:
- public int getModifiers() 以整数形式返回此Field的修饰符
- public Class getType() 得到Field的属性类型
- public String getName() 返回Field的名称。
六、调用运行时类的指定结构
1.调用指定方法
1.1 步骤
- 通过Class类的getMethod(String name,Class…parameterTypes)方法取得 一个Method对象,并设置此方法操作时所需要的参数类型。
- 之后使用Object invoke(Object obj, Object[] args)进行调用,并向方法中 传递要设置的obj对象的参数信息。
- 1.Object 对应原方法的返回值,若原方法无返回值,此时返回null
- 2.若原方法若为静态方法,此时形参Object obj可为null
- 3.若原方法形参列表为空,则Object[] args为null
- 4.若原方法声明为private,则需要在调用此invoke()方法前,显式调用 方法对象的setAccessible(true)方法,将可访问private的方法。
2.调用指定属性
- public Field getField(String name) 返回此Class对象表示的类或接口的指定的 public的Field。
- public Field getDeclaredField(String name)返回此Class对象表示的类或接口的 指定的Field。
3.setAccessible
- Method和Field、Constructor对象都有setAccessible()方法。
- setAccessible启动和禁用访问安全检查的开关。
- 参数值为true则指示反射的对象在使用时应该取消Java语言访问检查。
- 提高反射的效率。如果代码中必须用反射,而该句代码需要频繁的被 调用,那么请设置为true。
- 使得原本无法访问的私有成员也可以访问
- 参数值为false则指示反射的对象应该实施Java语言访问检查
七、反射的应用:动态代理
1.原理
使用一个代理将对象包装起来, 然后用该代理对象取代原始对象。任何对原 始对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原 始对象上。
public Object invoke(Object theProxy, Method method, Object[] params)
throws Throwable{
try{
Object retval = method.invoke(targetObj, params);
// Print out the result
System.out.println(retval);
return retval;
}catch (Exception exc){}
}
2.创建被代理的类以及接口
3.通过Proxy的静态方法
newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) 创建
一个Subject接口代理
RealSubject target = new RealSubject();
// Create a proxy to wrap the original implementation
DebugProxy proxy = new DebugProxy(target);
// Get a reference to the proxy through the Subject interface
Subject sub = (Subject) Proxy.newProxyInstance(
Subject.class.getClassLoader(),new Class[] { Subject.class }, proxy);
4.通过 Subject代理调用RealSubject实现类的方法
String info = sub.say(“Peter", 24);
System.out.println(info);
2.步骤
1.创建一个实现接口InvocationHandler的类,它必须实现invoke方 法,以完成代理的具体操作。
public Object invoke(Object theProxy, Method method, Object[] params)
throws Throwable{
try{
Object retval = method.invoke(targetObj, params);
// Print out the result
System.out.println(retval);
return retval;
}catch (Exception exc){}
}
package 反射07反射的应用0动态代理;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 动态代理的举例
* 不要自己写下来 知道这个过程就行 理解动态代理就可以
*/
interface Human{
String getBelief();
void eat(String food);
}
//被代理类
class SuperMan implements Human{
@Override
public String getBelief() {
return "I believe I can fly!";
}
@Override
public void eat(String food) {
System.out.println("我喜欢吃" + food);
}
}
class HumanUtil{
void method1(){
System.out.println("==========通用方法一==========");
}
void method2(){
System.out.println("==========通用方法二==========");
}
}
/*
要想实现动态代理,需要解决的问题?
问题一:如何根据加载到内存中的被代理类,动态的创建一个代理类及其对象
问题二:当通过代理类的对象调用方法时,如何动态的区调用被代理类中的同名方法
*/
class ProxyFactory{
//调用此方法,返回一个代理类的对象,解决问题一
public static Object getProxyInstance(Object obj){ //obj:被代理类的对象
MyInvocationHandler handler = new MyInvocationHandler();
handler.bind(obj);
return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),handler);
}
}
class MyInvocationHandler implements InvocationHandler{
private Object obj;//赋值时,也需要使用被代理类对象进行赋值
public void bind(Object obj){
this.obj = obj;
}
//当我们通过代理类的对象,调用方法A时,就会自动的调用如下的方法:invoke()
//将被代理类要执行的方法A的功能酒声明在invoke()中
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
HumanUtil util = new HumanUtil();
util.method1();
//method:即为代理类对象调用的方法,此方法也就作为了被代理类对象要调用的方法
//obj:被代理类的对象
Object returnValue = method.invoke(obj, args);
//上述方法的返回值就作为当前类中的invoke()的返回值
util.method2();
return returnValue;
}
}
public class ProxyTest {
public static void main(String[] args) {
SuperMan superMan = new SuperMan();
//proxyInstance:代理类的对象
Human proxyInstance = (Human) ProxyFactory.getProxyInstance(superMan);
//当通过代理类对象调用方法时,会自动的调用被代理类中同名的方法
String belief = proxyInstance.getBelief();
System.out.println(belief);
proxyInstance.eat("四川麻辣烫");
System.out.println("***********动态的体现************");
NikeClothFactory nikeClothFactory = new NikeClothFactory();
ClothFactory proxyClothFactory = (ClothFactory) ProxyFactory.getProxyInstance(nikeClothFactory);
proxyClothFactory.produceCloth();
}
}
package 反射07反射的应用0动态代理;
/**
* 静态代理举例
*
* 特点:代理类和被代理类在编译期间,酒确定下来了
*/
interface ClothFactory{
void produceCloth();
}
class ProxyClothFactory implements ClothFactory{
private ClothFactory factory;//用被代理类对象进行实例化
public ProxyClothFactory(ClothFactory factory){
this.factory = factory;
}
@Override
public void produceCloth() {
System.out.println("代理工厂做一些准备工作");
factory.produceCloth();
System.out.println("代理工厂做一些后续的收尾工作");
}
}
//被代理类
class NikeClothFactory implements ClothFactory{
@Override
public void produceCloth() {
System.out.println("Nike工厂生产一批运动服");
}
}
public class StaticProxyTest {
public static void main(String[] args) {
//创建被代理类的对象
ClothFactory nike = new NikeClothFactory();
//创建代理类的对象
ClothFactory proxyClothFactory = new ProxyClothFactory(nike);
proxyClothFactory.produceCloth();
}
}
@Override
public void produceCloth() {
System.out.println("Nike工厂生产一批运动服");
}
}
public class StaticProxyTest {
public static void main(String[] args) {
//创建被代理类的对象
ClothFactory nike = new NikeClothFactory();
//创建代理类的对象
ClothFactory proxyClothFactory = new ProxyClothFactory(nike);
proxyClothFactory.produceCloth();
}
}