反射
- 一.反射(Java Reflection)概述
- 二.Class类
- 1.Class类概述
- 2.实例化Class类对象(四种方法)
- 三.通过反射调用类的完整结构
- 1.通过反射调用类构造方法(创建对象)
- 2.通过反射调用类方法
- 3.通过反射调用类属性
- 4.通过反射调用类中的指定方法
- 5.通过反射调用类中的指定属性
- 四.动态代理
一.反射(Java Reflection)概述
即通过类名信息, 可以取得类的内部信息
Reflection (反射) 是被视为动态语言的关键, 反射机制允许程序在执行期借助于 Reflection API 取得任何类的内部信息, 并能直接操作任意对象的内部属性和方法。
Java 反射机制提供的功能
- 在运行时判断任意一个对象所属的类
- 在运行时构造任意一个类的对象
- 在运行时判断任意一个类所具有的成员变量和方法
- 在运行时调用任意一个对象的成员变量和方法
- 生产动态代理
Java反射机制研究及应用
反射相关的主要API:
- java.lang.Class: 代表一个类
- java.lang.reflect.Method: 代表类的方法
- java.lang.reflect.Filed: 代表类的成员变量
- java.lang.reflect.Constructor: 代表类的构造方法
二.Class类
1.Class类概述
Class类是所有类的高度抽象, 是一个可以描述所有类的类
在Object类中定义了以下的方法, 此方法将被所有子类继承:
- public final Class getClass()
该方法的返回值类型是一个Class类,
Class类是Java反射的源头, 从程序的运行结果来看, 即通过对象反射求出类的名称。
反射可以得到的信息:
某个类的属性、方法和构造器、某个类到底实现了哪些接口。
对于每个类而言, JRE都为其保留一个不变的 Class 类型的对象。
一个Class对象包含了特定某个类的有关信息。
- Class本身也是个类
- Class对象只能由系统建立对象
- 一个类在JVM中只会有一个Class实例
- 一个Class对象对应的是一个加载到JVM中的一个.class文件
- 每个类的实例都会记得自己是由哪个Class实例所生成
- 通过Class可以完整地得到一个类中的完整结构
2.实例化Class类对象(四种方法)
1.通过类名.class创建
- Class cls = Person.class;
- 前提:已知具体的类, 通过类的class属性获取, 该方法最为安全可靠, 程序性能最高
2.通过类的实例对象.getClass() 方法获取
- Class cls = p.getClass();
- 前提: 已知某个类的实例对象
3.通过Class类的静态方法forName() 获取(常用方法)
- Class cls = Class.forName(“day10.Person”);
- 前提: 已知一个类的全路径(包名.类名), 可能抛出ClassNotFoundException
4.通过ClassLoader(了解)
- ClassLoader cl = this.getClass().getClassLoader();
- Class cls = cl.loadClass(“day10.Person”);
public class TestClass {
public static void main(String[] args) {
Person p = new Person();
p.getClass();
Class<? extends Person> cls = p.getClass();//cls对象就包含对象p所属Person类的所有信息
Class c1 = Person.class;//通过类名.class创建指定类的Class实例
Class c2 = p.getClass();//通过一个类的实例对象.getClass()方法,获取对应类的Class实例
//通过Class的静态方法forName(String className)来获取一个类的class实例
//参数String className是类的全路径(包名.类名)
//这是获取Clsss实例的常用方式
try {
Class c3 = Class.forName("day20210411.Person");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
三.通过反射调用类的完整结构
Field 属性
Method 方法
Constructor 结构
Superclas 父类
Interface 接口
Annotation 注解
使用反射可以取得:
类所在的包
- public Package getPackage()
返回此对象所在的包
1.实现的全部Interfaces(接口)
- public Class<?>[] getInterfaces()
返回此Class对象所表示的类或接口 所实现的所有接口
2.所继承的Superclass(父类)
- public Class<? Super T>getSuperclass()
返回表示此Class 所表示的实体(类、接口、基本类型)的父类的Class
3.全部的Constructors(构造器)
- public Constructor[] getConstructors()
返回此对象所表示的类的全部public构造方法 - public Constructor[] getDeclaredConstructors()
返回此对象所表示的类声明的全部构造方法
Constructor类中:
- 取得修饰符: public int getModifiers(); //返回值1代表public,2代表private
- 取得方法名称: public String getName();
- 取得参数的类型: public Class<?>[] getParameterTypes();
4.全部的Methods(方法)
- public Method[] getMethods()
返回此对象所表示的类或接口的全部public方法 - public Constructor[] getDeclaredMethods()
返回此对象所表示的类或接口的全部方法
Method类中:
- 取得修饰符: public int getModifiers();
- 取得方法名称: public String getName();
- 取得全部的参数类型: public Class<?>[] getParameterTypes();
- 取得全部的返回值类型: public Class<?>[] getReturnTypes();
5.全部的Fields(属性)
- public Field[] getFields()
返回此Class对象所表示的类或接口的public Field - public Field[] getDeclaredFields()
返回此Class对象所表示的类或接口的全部Field
Field类中:
- 取得修饰符: public int gerModifiers();
- 取得Field的名称: public String getName();
- 取得Field的属性类型: public Class<?> getType();
import java.lang.reflect.Constructor;
public class Test {
public static void main(String[] args) {
try {
Class<?> cls = Class.forName("day20210411.Student");//获取Student类的Class对象
Class<?> superCls = cls.getSuperclass();//获取当前类的父类
System.out.println("父类:"+ superCls.getName());
Class<?>[] interfaces = cls.getInterfaces();//获取当前类的所有接口(数组)
for(Class<?> c : interfaces ) {
System.out.println("接口:"+ c.getName());
}
// Constructor<?>[] cons = cls.getConstructors();//获得类的公有的构造方法
Constructor<?>[] cons = cls.getDeclaredConstructors();//获得类的所有的构造方法
for(Constructor<?> c : cons) {
System.out.println("构造方法:"+ c.getName());//获取方法名称
System.out.println("构造方法:"+ c.getModifiers());//获取方法修饰符
//方法修饰符: 返回数值 1代表public, 2代表private
Class<?>[] types = c.getParameterTypes();//获取参数类型
for(Class<?> c1 : types) {
System.out.println("参数类型:"+c1.getName());
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
1.通过反射调用类构造方法(创建对象)
首先要获取类的Class对象
- Class<?> cls = Class.forName(“day20210411.Student”);
1.调用类的public无参构造
- Object obj = cls.getConstructor().newInstance();
//返回的是Object类对象,需要向下转型 - Student stu = (Student)obj;
2.调用类的public有参构造(根据参数类型)
- Constructor<?> c = cls.getConstructor(String.class);
//调用有一个String类型参数的构造器 - Student stu1 = (Student)c.newInstance(“第一中学”);
//通过newInstance()实例化对象,Object类对象,需要向下转型
3.强制调用所有本类定义的有参构造
- Constructor<?> c2 = cls.getDeclaredConstructor(String.class,
int.class);
//调用有(String,int)类型参数的构造方法 - c2.setAccessible(true);
//解除私有化封装,下面就可以对这个私有方法强制调用 - Student stu2 = (Student)c2.newInstance(“李白”,16);
//传入实参,实例化对象 - c2.setAccessible(false);//重新对构造进行私有封装
- System.out.println(stu2.school);//私有封装后已实例化的对象仍然可以访问
import java.lang.reflect.Constructor;
public class Test {
public static void main(String[] args) {
try {
Class<?> cls = Class.forName("day20210411.Student");//获取Student类的Class对象
/**
* 如何用反射的构造方法来创建对象
*/
//相当于调用Student类的公有无参构造,需要抛出异常
Object obj = cls.getDeclaredConstructor().newInstance();//返回Object类对象,需要向下转型
Student stu = (Student)obj;
//相当于调用Student类的公有有参构造,
Constructor<?> c = cls.getConstructor(String.class);//有一个String类型参数
Student stu1 = (Student)c.newInstance("第一中学");//通过newInstance()实例化对象,Object类对象,需要向下转型
//通过反射机制可以强制调用私有的构造方法
Constructor<?> c2 = cls.getDeclaredConstructor(String.class, int.class);//调用有(String,int)类型参数的构造方法
c2.setAccessible(true);//解除私有的封装,下面就可以对这个私有方法强制调用
Student stu2 = (Student)c2.newInstance("李白",16);//传入实参,实例化对象
c2.setAccessible(false);//重新对构造进行私有封装
System.out.println(stu2.school);//私有封装后已实例化的对象仍然可以访问
} catch (Exception e) {
e.printStackTrace();
}
}
}
2.通过反射调用类方法
同上:
- punlic Method[] getMethods()
- 返回此对象所表示的类或接口的全部public方法, 包括从父类继承的public方法
- public Constructor[] getDeclaredMethods()
- 返回此对象所表示的类或接口的本类/接口中定义的所有方法, 不受权限修饰符的限制, 不包括从父类中继承的方法
Method类中:
- 取得修饰符: public int getModifiers();
- 取得方法名称: public String getName();
- 取得全部的参数类型: public Class<?>[] getParameterTypes();
- 取得全部的返回值类型: public Class<?>[] getReturnTypes();
import java.lang.reflect.Method;
public class TestMethods {
public static void main(String[] args) {
Class<Student> cls = Student.class;
// Method[] methods = cls.getMethods();//获取类的所有的公有方法
Method[] methods = cls.getDeclaredMethods();//获取类的所有方法
for(Method m : methods) {
System.out.print("方法:"+m.getName()+",返回值:"+m.getReturnType()+",修饰符:"+m.getModifiers());
Class<?>[] tp= m.getParameterTypes();
if(tp != null && tp.length > 0) {
for(Class<?> c : tp) {
System.out.print(",参数:"+c);
}
}
System.out.print('\n');
}
}
}
3.通过反射调用类属性
同上:
- public Field[] getFields()
返回此Class对象所表示的类或接口的public Field, 包括从父类中继承的public 属性 - public Field[] getDeclaredFields()
返回此Class对象所表示的类或接口的本类/接口中定义的全部Field, 不受权限修饰符限制, 但不包括从父类中继承的属性
Field类中:
- 取得修饰符: public int gerModifiers();
- 取得Field的名称: public String getName();
- 取得Field的属性类型: public Class<?> getType();
- 取得Field值, (需要先实例化对象) public Object get(obj);
import java.lang.reflect.Field;
public class TestFileds {
public static void main(String[] args) {
try {
Class<?> cls = Class.forName("day20210411.Student");
Student stu = new Student();//访问属性值需要先实例化对象
// Field[] fld = cls.getFields();//获取当前类的public属性(default(修饰符0)修饰的属性无法访问)
Field[] fld = cls.getDeclaredFields();//获取当前类的全部属性
for(Field f : fld) {
System.out.println("类型:"+f.getType()+",属性:"+f.getName()+",修饰符:"+f.getModifiers());
f.setAccessible(true);//解除私有属性的封装
try {
Object value = f.get(stu);//访问stu对象的属性值
System.out.println(value);
} catch (Exception e) {
e.printStackTrace();
}
}
Package pkg = cls.getPackage();//获取当前类所在的包
System.out.println(pkg.getName());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
4.通过反射调用类中的指定方法
通过反射, 调用类中的方法, 通过Method类完成。
步骤:
- 通过Class类的getMethod(String name, Class…ParaeterTypes)方法, 通过指定方法名称、参数类型、数量, 来取得指定方法的一个Method对象
- 使用Object invoke(Object obj, Object[] args)方法进行调用, 并向调用的方法中传递要设置的obj对象以及实参信息
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
public class TestMethods {
public static void main(String[] args) {
Class<Student> cls = Student.class;
/**
* 调用指定方法
* 注意:下面调用的都是obj对象的方法,obj对象实际上就是Student对象
*/
try {
Constructor<Student> con = cls.getConstructor();
Object obj = con.newInstance();//调用无参构造,实例化对象
// Person p = (Person)obj;
//调用公有方法
Method m = cls.getMethod("setInfo",String.class,String.class);//获取指定方法getMethod(方法名,参数类型)
m.invoke(obj,"李白","清华大学");//调用有参构造需要实例化对象。参数1是对象,后面的参数是实参
//调用私有方法
Method m1 = cls.getDeclaredMethod("test", String.class);
m1.setAccessible(true);//解除私有化封装
m1.invoke(obj,"李白");
//有返回值的方法
Method m3 = cls.getMethod("getSchool");//获取方法名为getSchool(),且没有参数的方法
String school = (String) m3.invoke(obj);//调用有返回值的方法,返回值为Object类,需要向下转型为String类型
System.out.println(school);
} catch (Exception e) {
e.printStackTrace();
}
}
}
5.通过反射调用类中的指定属性
在反射机制中, 可以直接通过Field类操作类中的属性, 通过Field类提供的set()和get()方法就可以完成设置和取得属性内容的操作。
- public Field getField(String name)
返回此Class对象所表示的类或接口的指定的public 的Field - public Field getDeclaredField(String name)
返回此Class对象所表示的类或接口的指定的Field
在Field类中:
- 取得指定对象obj上此Field属性内容: public Object get(Object obj);
- 设置指定对象obj上此Field属性内容: public void set(Object obj,Object value);
- 使私有属性解除私有化封装: public void setAccessible(true);
注: 在类中属性都设置为private的前提下, 在使用set()和get()方法时, 首先要使用Field类中的setAccessible(true)方法将要操作的属性设置为可以被外部访问。
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
public class TestFileds {
public static void main(String[] args) {
try {
Class<?> cls = Class.forName("day20210411.Student");
/**
* 调用指定属性
*/
//首先反射创建一个对象
Constructor<?> con = cls.getConstructor();
// Object obj = con.newInstance();
Student stu1 = (Student)con.newInstance();
Field fld1 = cls.getField("school");//获取名称为school的public的属性
fld1.set(stu1,"北京大学");//对stu1的school属性设置值
String school = (String) fld1.get(stu1);//获取stu1的school属性的值
System.out.println(school);
//调用私有的属性
Field fld2 = cls.getDeclaredField("secret");//获取名称为secret的private(或default)的属性
fld2.setAccessible(true);//解除fld2(即secret)属性的私有化封装
fld2.set(stu1, "密码");
String secret = (String) fld2.get(stu1);
System.out.println(secret);
} catch (Exception e) {
e.printStackTrace();
}
}
}
四.动态代理
JDK中的动态代理是通过反射类Proxy以及InvocationHandler回调接口实现的。
但是,JDK中所要进行动态代理的类必须要实现一个接口,也就是说只能对该类所实现接口中定义的方法进行代理,这在实际编程中具有一定的局限性,而且使用反射的效率也并不是很高。
Proxy类:
专门完成代理的操作类, 是所有动态代理的父类。
通过此类为一个或多个接口动态地生成实现类。
创建一个动态代理类所对应的Class对象
static Object newProxyInstance(
ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
* 参数1是代理对象的类加载器,handler.getClass().getClassLoader()
* 参数2是被代理对象的接口,test.getClass().getInterfaces()
* 参数3是代理对象,handler
*
* 返回值为Object类,就是成功被代理后的对象
* 根据当时情况进行类型转换
动态代理步骤:
- 创建一个实现接口InvocationHandler的类, 它必须实现invoke方法, 以完成代理的具体操作
- 创建被代理的类及接口
- 通过Proxy的静态方法Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)创建一个Subject接口代理
- 通过Subject代理调用RealSubject实现类的方法
- 注意:如果一个对象需要通过Proxy.newProxyInstance()方法被代理。那么这个对象的类必须有相应的接口, 就像本例中的ItestDemo接口和实现类TestDemoImpl
1.定义接口。(动态代理实现的, 是实现类继承自接口中定义的方法)
public interface Study {
//采用Proxy.newProxyInstance()方法进行动态代理
//是对一个类所实现接口中定义的方法进行代理
void study1();
void study2();
}
2.定义实现类。
public class Student implements Study{
@Override
public void study1() {
System.out.println("执行study1方法");
}
@Override
public void study2() {
System.out.println("执行study2方法");
}
}
3.创建动态代理类, 实现代理的具体操作
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* 动态代理类
* @author chenyao
*/
public class ProxyDemo implements InvocationHandler {//代理类必须实现InvocationHandler接口
Object obj;//定义被代理的对象
public ProxyDemo(Object obj) {//定义构造方法从外部传入对象
this.obj = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(method.getName()+"方法开始执行");
Object result = method.invoke(this.obj, args);//正常通过反射调用类方法的返回结果
System.out.println(method.getName()+"方法执行完毕");
return result;//返回invoke调用的方法执行结果
}
}
4.通过动态代理对象调用实现类重写的接口的方法
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class Test {
public static void main(String[] args) {
// TestDemoImpl test = new TestDemoImpl();
Study stu = new Student();//对象的多态
//直接调用方法
stu.study1();
stu.study2();
System.out.println("=======================");
/**
* 需求:
* 在执行study1和study2方法时加入相应动作
* 在执行方法前打印study1或study2开始执行
* 在执行方法后打印study1或study2执行完毕
*/
/**
* 注意:如果一个对象需要通过Proxy.newProxyInstance()方法被代理
* 这个对象的类必须有相应的接口,
* 就像本例中的Student接口和实现类Study
*/
//创建代理器, 使用代理对象调用方法
// ProxyDemo handler = new ProxyDemo(stu);
InvocationHandler handler = new ProxyDemo(stu);//传入被代理的对象
/**
* Proxy.newProxyInstance(ClassLoader loader,
* Class<?> interfaces,
* InvocationHandler h)
* 参数1是代理对象的类加载器,handler.getClass().getClassLoader()
* 参数2是被代理对象的接口,test.getClass().getInterfaces()
* 参数3是代理对象,handler
*
* 返回值为Object类,就是成功被代理后的对象
* 根据当时情况进行类型转换为实现的接口类型
*/
Study s = (Study) Proxy.newProxyInstance(
handler.getClass().getClassLoader(),
stu.getClass().getInterfaces(),
handler); //创建接口对象
s.study1();//通过代理对象代理实现接口的方法
s.study2();
}
}