java 反射机制一:获取Class 类实例、理解ClassLoader读取配置文件操作
文章目录
- 前文回顾
- 前言
- 一、创建运行时类的对象
- 二、获取运行时类的完整结构
- 1.实现的全部接口
- 2.所继承的父类
- 3.全部的构造器
- 4.全部的方法
- 5.全部的Field
- 6.Annotation相关
- 7.泛型相关
- 8.类所在的包
- 三、调用运行时类的指定结构
- 1.调用指定方法
- 2.调用指定属性
- 3.调用指定构造器
- 四、反射的应用:动态代理
- 1.动态代理步骤
- 2.动态代理与AOP
前言
上期我们对 java 的反射机制有了一定的了解,主要是能够掌握 Class 类实例的获取,同时要能够对 ClassLoader 要有一些理解,尤其是读取配置文件的操作要能够掌握。详细的信息可以点击上方的链接查看”☝️
一、创建运行时类的对象
有了Class对象,能做什么?
❤️ 创建类的对象: 调用Class对象的 newInstance() 方法
- 要 求:
1)类必须有一个无参数的构造器。
2)类的构造器的访问权限需要足够。
难道没有无参的构造器就不能创建对象了吗?
不是!只要在操作的时候明确的调用类中的构造器, 并将参数传递进去之后,才可以实例化操作。
- 步骤如下:
1)通过Class类的 getDeclaredConstructor(Class … parameterTypes) 取得本类的指定形参类型的构造器。
2)向构造器的形参中传递一个对象数组进去,里面包含了构造器中所需的各个参数。
3)通过Constructor实例化对象。
public class NewInstanceTest {
@Test
public void test1() throws InstantiationException, IllegalAccessException {
Class clazz = Person.class;
Person obj = clazz.newInstance();
System.out.println(obj); // Person{name='null', age=0}
}
// 体会反射的动态性
@Test
public void test2() {
for (int i = 0; i < 100; i++) {
int num = new Random().nextInt(3);
String classPath = "";
switch (num) {
case 0:
classPath = "java.util.Date";
break;
case 1:
classPath = "java.lang.Object";
break;
case 2:
classPath = "com.deve.java.Person";
break;
}
try {
Object obj = getInstance(classPath);
System.out.println(obj);
} catch (Exception e) {
e.printStackTrace();
}
}
}
public Object getInstance(String classPath) throws Exception {
Class> clazz = Class.forName(classPath);
return clazz.newInstance();
}
}
二、获取运行时类的完整结构
1.实现的全部接口
❤️ public Class>[] getInterfaces() 确定此对象所表示的类或接口实现的接口。
public class OtherTest {
@Test
public void test5() {
Class clazz = Person.class;
Class[] interfaces = clazz.getInterfaces();
for (Class c : interfaces) {
System.out.println(c);
}
// 获取当前运行时类的父类实现的接口
Class[] interfaces1 = clazz.getSuperclass().getInterfaces();
for (Class c : interfaces1) {
System.out.println(c);
}
}
}
2.所继承的父类
❤️ public Class Super T> getSuperclass() 返回表示此 Class 所表示的实体(类、接口、基本类型)的父类的Class。
public class OtherTest {
@Test
public void test2() {
Class clazz = Person.class;
Class superclass = clazz.getSuperclass();
System.out.println(superclass); // class com.atguigu.java1.Creature
}
@Test
public void test3() {
Class clazz = Person.class;
Type genericSuperclass = clazz.getGenericSuperclass();
System.out.println(genericSuperclass); // com.atguigu.java1.Creature
}
}
3.全部的构造器
❤️ public Constructor[] getConstructors() 返回此 Class 对象所表示的类的所有public构造方法。
❤️ public Constructor[] getDeclaredConstructors() 返回此 Class 对象表示的类声明的所有构造方法。
❤️ Constructor类中:
- 取得修饰符: public int getModifiers();
- 取得方法名称: public String getName();
- 取得参数的类型 public Class>[] getParameterTypes();
public class OtherTest {
@Test
public void test1() {
Class clazz = Person.class;
// getConstructors():获取当前运行时类中声明为public的构造器
Constructor[] constructors = clazz.getConstructors();
for (Constructor c : constructors) {
System.out.println(c);
}
System.out.println();
// getDeclaredConstructors():获取当前运行时类中声明的所有的构造器
Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
for (Constructor c : declaredConstructors) {
System.out.println(c);
}
}
}
4.全部的方法
❤️ public Method[] getDeclaredMethods() 返回此Class对象所表示的类或接口的全部方法
❤️ public Method[] getMethods() 返回此Class对象所表示的类或接口的public的方法
❤️ Method类中:
- public Class> getReturnType() 取得全部的返回值
- public Class>[] getParameterTypes() 取得全部的参数
- public int getModifiers() 取得修饰符
- public Class>[] getExceptionTypes() 取得异常信息
public class MethodTest {
@Test
public void test1() {
Class clazz = Person.class;
// getMethods(): 获取当前运行时类及其所有父类中声明为public权限的方法。
Method[] methods = clazz.getMethods();
for (Method m : methods) {
System.out.println(m);
}
System.out.println();
// getDeclaredMethods():获取当前运行时类中声明的所有方法。(不包含父类中声明的方法)
Method[] declaredMethods = clazz.getDeclaredMethods();
for (Method m : declaredMethods) {
System.out.println(m);
}
}
@Test
public void test2() {
Class clazz = Person.class;
Method[] declaredMethods = clazz.getDeclaredMethods();
for (Method m : declaredMethods) {
// 1. 获取方法声明的注解
Annotation[] annotations = m.getAnnotations();
for (Annotation a : annotations) {
System.out.println(a); // @com.atguigu.java1.MyAnnotation(value=hello)
}
// 2. 获取方法的权限修饰符
System.out.print(Modifier.toString(m.getModifiers()) + "t");
// 3. 获取方法的返回值类型
System.out.print(m.getReturnType().getName() + "t");
// 4. 获取方法的方法名
System.out.print(m.getName());
System.out.print("(");
// 5. 获取方法的形参列表
Class>[] parameterTypes = m.getParameterTypes();
if ( ! (parameterTypes == null && parameterTypes.length == 0)) {
for (int i = 0; i < parameterTypes.length; i++) {
if (i == parameterTypes.length - 1) {
System.out.print(parameterTypes[i].getName() + " args_" + i);
break;
}
System.out.print(parameterTypes[i].getName() + " args_" + i + ", ");
}
}
System.out.print(") ");
// 6. 获取方法抛出的异常
Class>[] exceptionTypes = m.getExceptionTypes();
if ( exceptionTypes.length != 0) {
System.out.print("throws ");
for (int i = 0; i < exceptionTypes.length; i++) {
if (i == exceptionTypes.length - 1) {
System.out.print(exceptionTypes[i].getName());
break;
}
System.out.print(exceptionTypes[i].getName() + ", ");
}
}
System.out.println();
}
}
}
5.全部的Field
❤️ public Field[] getFields() 返回此Class对象所表示的类或接口的public的Field。
❤️ public Field[] getDeclaredFields() 返回此Class对象所表示的类或接口的全部Field。
❤️ Field方法中:
- public int getModifiers() 以整数形式返回此Field的修饰符
- public Class> getType() 得到Field的属性类型
- public String getName() 返回Field的名称。
public class FieldTest {
@Test
public void test1() {
Class clazz = Person.class;
// 获取属性结构
// getFields():获取当前运行时类及其所有父类中声明为public访问权限的属性
Field[] fields = clazz.getFields();
for (Field f : fields) {
System.out.println(f);
}
// getDeclaredFields():获取当前运行时类当中声明的所有属性。(不包含父类中声明的属性)
Field[] fields1 = clazz.getDeclaredFields();
for (Field f1 : fields1) {
System.out.println(f1);
}
}
// 权限修饰符 数据类型 变量名
@Test
public void test2() {
Class clazz = Person.class;
Field[] declaredFields = clazz.getDeclaredFields();
for (Field f : declaredFields) {
// 1. 权限修饰符
int modifiers = f.getModifiers();
System.out.print(Modifier.toString(modifiers) + "t");
// 2. 数据类型
Class> type = f.getType();
System.out.print(type.getName() + "t");
// 3. 变量名
String fName = f.getName();
System.out.println(fName);
}
}
}
6.Annotation相关
❤️ get Annotation(Class annotationClass)
❤️ getDeclaredAnnotations()
public class OtherTest {
@Test
public void test7() {
Class clazz = Person.class;
Annotation[] annotations = clazz.getAnnotations();
for (Annotation annos : annotations) {
System.out.println(annos);
}
}
}
7.泛型相关
❤️ 获取父类泛型类型: Type getGenericSuperclass()
❤️ 泛型类型: ParameterizedType
❤️ 获取实际的泛型类型参数数组: getActualTypeArguments()
public class OtherTest {
@Test
public void test4() {
Class clazz = Person.class;
Type genericSuperclass = clazz.getGenericSuperclass();
ParameterizedType paramType = (ParameterizedType) genericSuperclass;
Type[] actualTypeArguments = paramType.getActualTypeArguments();
// System.out.println(actualTypeArguments[0].getTypeName()); // java.lang.String
System.out.println(((Class) actualTypeArguments[0]).getName()); // java.lang.String
}
}
8.类所在的包
❤️ Package getPackage()
public class OtherTest {
@Test
public void test6() {
Class clazz = Person.class;
Package aPackage = clazz.getPackage();
System.out.println(aPackage);
}
}
1.在实际的操作中,取得类的信息的操作代码,并不会经常开发。
2.一定要熟悉java.lang.reflect包的作用,反射机制。
3.如何取得属性、方法、构造器的名称,修饰符等。
❤️ 通过反射,调用类中的方法,通过Method类完成。步骤:
1.通过Class类的 getMethod(String name,Class…parameterTypes) 方法取得一个Method对象,并设置此方法操作时所需要的参数类型。
2.之后使用 Object invoke(Object obj, Object[] args) 进行调用,并向方法中传递要设置的obj对象的参数信息。
❤️ Object invoke(Object obj, Object … args)
说明:
1.Object 对应原方法的返回值,若原方法无返回值,此时返回null。
2.若原方法若为静态方法,此时形参Object obj可为null。
3.若原方法形参列表为空,则Object[] args为null。
4.若原方法声明为private,则需要在调用此invoke()方法前,显式调用方法对象的setAccessible(true)方法,将可访问private的方法。
❤️ 关于setAccessible方法的使用
- Method和Field、Constructor对象都有setAccessible()方法。
- setAccessible启动和禁用访问安全检查的开关。
- 参数值为true则指示反射的对象在使用时应该取消Java语言访问检查。
- 提高反射的效率。 如果代码中必须用反射, 而该句代码需要频繁的被调用, 那么请设置为true。
- 使得原本无法访问的私有成员也可以访问
- 参数值为false则指示反射的对象应该实施Java语言访问检查
public class ReflectionTest {
@Test
public void testMethod() throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
Class clazz = Person.class;
// 创建运行时类的对象
Person p = (Person) clazz.newInstance();
// 1. 获取指定的某个方法 getDeclaredMethod(): 参数1:指明获取的方法的名称 参数2:指明获取的方法的形参列表
Method show = clazz.getDeclaredMethod("show", String.class);
// 2. 保证当前的方法是可访问的
show.setAccessible(true);
// 3. 调用方法的invoke(): 参数1:方法的调用者 参数2:给方法形参赋值的实参
// invoke()的返回值即为对应类中调用的方法的返回值。
Object returnValue = show.invoke(p, "CHN");
System.out.println(returnValue);
System.out.println("===============如何调用静态的方法==================");
// private static void showDesc()
Method showDesc = clazz.getDeclaredMethod("showDesc");
showDesc.setAccessible(true);
// 如果调用的运行时类中的方法没有返回值,则此invoke()返回null
Object returnVal = showDesc.invoke(Person.class);
// 或
// Object returnVal = showDesc.invoke(null);
System.out.println(returnVal); // null
}
}
2.调用指定属性
❤️ 在反射机制中,可以直接通过Field类操作类中的属性,通过Field类提供的 set() 和 get() 方法就可以完成设置和取得属性内容的操作。
- public Field getField(String name) 返回此Class对象表示的类或接口的指定的public的Field。
- public Field getDeclaredField(String name)返回此Class对象表示的类或接口的指定的Field。
❤️ 在Field中:
- public Object get(Object obj) 取得指定对象obj上此Field的属性内容
- public void set(Object obj,Object value) 设置指定对象obj上此Field的属性内容
public class ReflectionTest {
@Test
public void testField() throws Exception {
Class clazz = Person.class;
// 创建运行时类的对象
Person p = (Person) clazz.newInstance();
// 获取指定的属性:要求此运行时类中的属性是public的
// 通常不采用此方法
Field id = clazz.getField("id");
id.set(p, 1001);
int pId = (int) id.get(p);
System.out.println(pId);
}
@Test
public void testField1() throws InstantiationException, IllegalAccessException, NoSuchFieldException {
Class clazz = Person.class;
// 创建运行时类的对象
Person p = (Person) clazz.newInstance();
// 1、getDeclaredField(String fieldName):获取运行时类中指定变量名的属性
Field name = clazz.getDeclaredField("name");
// 2、保证当前属性是可访问的
name.setAccessible(true);
// 3、获取、设置指定对象的属性值
name.set(p, "Tom");
// 获取属性的值
System.out.println(name.get(p));
}
}
3.调用指定构造器
public class ReflectionTest {
@Test
public void testConstructor() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
Class clazz = Person.class;
// private Person(String name)
// 1. 获取指定的构造器 getDeclaredConstructor(): 参数:指明构造器的参数列表
Constructor declaredConstructor = clazz.getDeclaredConstructor(String.class);
// 2. 保证此构造器是可访问的
declaredConstructor.setAccessible(true);
// 3. 调用此构造器创建运行时类的对象
Person per = (Person) declaredConstructor.newInstance("Tom");
System.out.println(per); // Person{name='Tom', age=0, id=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) {
// 创建被代理类的对象
NikeClothFactory nike = new NikeClothFactory();
// 创建代理类的对象
ProxyClothFactory proxyClothFactory = new ProxyClothFactory(nike);
//
proxyClothFactory.produceCloth();
}
}
1.动态代理步骤
❤️ 使用Proxy生成一个动态代理时,往往并不会凭空产生一个动态代理,这样没有太大的意义。通常都是为指定的目标对象生成动态代理。
❤️ 这种动态代理在AOP中被称为AOP代理, AOP代理可代替目标对象, AOP代理包含了目标对象的全部方法。但AOP代理中的方法与目标对象的方法存在差异。
❤️ AOP代理里的方法可以在执行目标方法之前、之后插入一些通用处理。
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 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 {
// method:即为代理类对象调用的方法,此方法也就作为了被代理类对象要调用的方法
// obj:被代理类的对象
Object returnValue = method.invoke(obj, args);
// 上述方法的返回值就作为当前类中的invoke()的返回值
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();
}
}



