关于java中的反射,我只能努力到这一步了

Java
278
0
0
2023-09-15
标签   Java反射

反射是 java 高级特性之一,常用的框架,例如: Spring 、 mybatis 等在实现的过程中都使用到了反射,所以还是非常有必要了解反射的。

反射是什么

Java反射提供了检查和修改应用程序运行时行为的能力。反射允许我们在 「运行时」 检查和操作类、接口、构造函数、方法和字段,即使类在编译时不可访问。我们还可以使用反射来实例化一个对象,调用它的方法,更改字段值。这种 「动态获取的信息以及动态调⽤对象的⽅法的功能」 称为java语⾔的反射机制。

反射的用途

可能有些人认为反射在工作中用的并不多,但其实并不是这样的,工作中处处都能见到反射的影子,比如工作中经常会通过对象 「.」 的时候编译器会列出该对象可以调用的方法,其实这个原理使用的也是反射。还有像Spring、Mybatis、Tomcat等通用框架或者容器底层都会大量的使用到反射。所以如果你想实现一个通用的功能,反射或许是最好的选择之一。

反射的缺点

反射的好处是允许我们在运行时对类、接口等进行一系列操作。但是它也有缺点:

  • 性能不佳: 由于 java 反射动态解析类型,它涉及扫描类路径以查找要加载的类等处理,导致性能下降。
  • 安全限制:反射需要运行时权限,这对于在安全管理器下运行的系统可能不可用。由于安全管理器,这可能会导致应用程序在运行时失败。
  • 安全问题: 使用反射我们可以访问我们不应该访问的部分代码,例如我们可以访问一个类的私有字段并更改它的值。这可能是一个严重的安全威胁,并导致应用程序行为异常。
  • 高维护: 反射代码难以理解和调试,并且在编译时无法发现代码的任何问题,因为类可能不可用,使其灵活性降低且难以维护。

反射的基本运用

以下类用于测试

BaseInterface.java

  Package  com.example.reflection;

public interface BaseInterface {
    public int interfaceInt=;

     void  method1();

    int method(String str);
} 

BaseClass.java

 package com.example.reflection;

public class BaseClass {
    public int baseInt;

     private   static  void method3(){
        System.out.println("Method");
    }

    public int method(){
        System.out.println("Method");
        return;
    }

    public static int method(){
        System.out.println("Method");
        return;
    }

    void method(){
        System.out.println("Method");
    }

    // inner public class
    public class BaseClassInnerClass{}

    //member public enum
    public enum BaseClassMemberEnum{}
}

ConcreteClass.java

 package com.example.reflection;

public class ConcreteClass  extends  BaseClass implements BaseInterface{

    public int publicInt;

    private String privateString="private string";

 public static String staticStr = "public static string";

    protected  Boolean  protectedBoolean;

    Object defaultObject;

    public ConcreteClass(int i){
        this.publicInt=i;
    }

 public ConcreteClass(){
    }

    private ConcreteClass(String privateString){
        this.privateString = privateString;
    }

    @Override
    public void method() {
        System.out.println("Method impl.");
    }

    @Override
    public int method(String str) {
        System.out.println("Method impl.");
        return;
    }

    @Override
    public int method(){
        System.out.println("Method overriden.");
        return;
    }

    public int method(int i){
        System.out.println("Method overriden.");
        return;
    }

    // inner classes
    public class ConcreteClassPublic Class {}
    private class ConcreteClassPrivateClass{}
    protected class ConcreteClassProtectedClass{}
    class ConcreteClassDefaultClass{}

    //member enum
    enum ConcreteClassDefaultEnum{}
    public enum ConcreteClassPublicEnum{}

    //member interface
    public interface ConcreteClassPublicInterface{}
} 

获取Class 类对象

java.lang .Class是所有反射操作的入口点。Class 类只有 java虚拟机 才能new出来,任何⼀个类都是 Class 类 的实例对象。要使用反射首先要获取到Class对象,Class 对象有三种方式可以获取到:

  1. 通过 .class
 Class< Integer > integerClass = Integer.class; 
  1. 使用对象的 getClass 方法
 Date date = new Date();
Class<? extends Date> dateClass = date.getClass(); 
  1. 通过 java.lang.Class.forName(String fullyClassifiedClassName) 方法
 Class<Integer> forName = Class.forName("java.lang.Integer");

对于基本类型和数组,Class对象的获取可以通过 .class 的方式获取。

 Class<?>  boolean Class = boolean.class;
System.out.println(booleanClass.getCanonicalName()); // prints boolean

Class<?> cDouble = Double.TYPE;
System.out.println(cDouble.getCanonicalName()); // prints double

Class<?> cDoubleArray = Class.forName("[D");
System.out.println(cDoubleArray.getCanonicalName()); //prints double[]

Class<?> twoDStringArray = String[][].class;
System.out.println(twoDStringArray.getCanonicalName()); // prints java.lang.String[][] 

对于包装类,也可以通过静态变量 Type 获取Class对象

 Class<?> doubleClass = Double.TYPE; 

类相关的反射

获取包名

getPackage() 方法返回这个类所在的包。该类的类加载器用于查找包。

 final Package aPackage = ConcreteClass.class.getPackage();
System.out.println(aPackage);
 System.out.println(aPackage.getName()); 

结果:

 package com.example.reflection
com.example.reflection 

获取supperClass

getSuperclass() 如果是类对象调用的话会返回类的父类,如果此类为Object类、接口、基本类型或void,则返回null。如果此对象为数组类,则返回表示Object类的类对象。

 final Class<? super Integer> superclass = Integer.class.getSuperclass();
System.out.println(superclass); // 结果:class java.lang.Number

System.out.println(Object.class.getSuperclass()); // 结果:null

System.out.println(String[][].class.getSuperclass()); // 结果:class java.lang.Object 

获取Public成员类

getClasses() 返回一个Class类型数组, 通过 getClasses() 可以获取到Class对象的所有Public类、接口和枚举类。这包括从父类继承的公共类和接口以及由类声明的公共类和接口。如果Class对象没有Public成员类、接口,又或者Class对象是个数组、基本类型、void, getClasses() 会返回一个空数组。

 final Class<?>[] publicClasses = ConcreteClass.class.getClasses();
Arrays.stream(publicClasses).forEach(System.out::println); 

结果:

获取声明的类

getDeclaredClasses() 方法返回一个 Class 对象数组,返回声明为该 Class 对象表示的类成员的所有类和接口。返回的数组不包括在继承的类和接口中声明的类。

 final Class<?>[] declaredClasses = ConcreteClass.class.getDeclaredClasses();
Arrays.stream(declaredClasses).forEach(System.out::println); 

结果:

获取所有Public构造方法

getConstructors() 方法返回Class对象的Public 构造方法 列表。

 final Constructor<?>[] constructors = ConcreteClass.class.getConstructors();
Arrays.stream(constructors).forEach(System.out::println); 

结果:

 public com.example.reflection.ConcreteClass()
public com.example.reflection.ConcreteClass(int) 

获取 泛型 参数

如果有与该类关联的任何泛型参数, getTypeParameters() 则返回 TypeVariable 数组。泛型参数的返回顺序与声明的顺序相同。

 TypeVariable<?>[] typeParameters = Class.forName("java.util. hashMap ").getTypeParameters();
for(TypeVariable<?> t : typeParameters){
    System.out.print(t.getName()+",");
} 

结果:

 K,V, 

获取实现的接口

getGenericInterfaces() 方法返回由具有泛型类型信息的类实现的接口数组。

getInterfaces() 方法返回类实现的所有接口

 final Class<?> hashMapClass = Class.forName("java.util. HashMap ");
System.out.println(Arrays.toString(hashMapClass.getGenericInterfaces()));
System.out.println(Arrays.toString(hashMapClass.getInterfaces())); 

结果:

 [java.util.Map<K, V>, interface java.lang.Cloneable, interface java.io.Serializable]
[interface java.util.Map, interface java.lang.Cloneable, interface java.io.Serializable] 

获取所有Public方法

getMethods()方法返回该类的公共方法数组,包括它的父类和实现的接口的公共方法。

 final Method[] allMethods = ConcreteClass.class.getMethods();
Arrays.stream(allMethods).forEach(System.out::println); 

结果:

获取所有Public字段

getFields() 方法返回该类的公共字段数组,包括它的父类和实现的接口的公共字段。

 public int com.example.reflection.ConcreteClass.publicInt
public static final int com.example.reflection.BaseInterface.interfaceInt
public int com.example.reflection.BaseClass.baseInt 

获取所有注释

getAnnotations() 方法返回元素的所有注释。

 final  Annotation [] annotations = ConcreteClass.class.getAnnotations();
System.out.println(Arrays.toString(annotations)); 

结果:

 [@java.lang.Deprecated()] 

获取权限修饰符

getModifiers() 方法返回类修饰符的int表示,可以使用java.lang.reflect.Modifier.toString()方法以源代码中使用的 字符串 格式来显示出来。

 final int modifiers = ConcreteClass.class.getModifiers();
final String modifier = Modifier.toString(modifiers);
System.out.println("modifiers="+modifiers +",modifier="+modifier); 

结果:

 modifiers=,modifier=public 

java.lang.reflect.Modifier 权限修饰符表示的常量

 public static final int PUBLIC           =x00000001;
/**
 * The {@code int} value representing the {@code private}
 * modifier.
 */public static final int PRIVATE          =x00000002;

/**
 * The {@code int} value representing the {@code protected}
 * modifier.
 */public static final int PROTECTED        =x00000004; 

字段相关反射

获取字段

getField() getDeclaredField() 方法可以获取到类的字段。不同的是, getField() 获取的必须是声明了public的字段,包括父类或者实现的接口中的public字段; getDeclaredField() 只能获取的本类中定义的字段。

 final Class<ConcreteClass> concreteClassClass = ConcreteClass.class;
//获取public字段
final Field interfaceInt = concreteClassClass.getField("interfaceInt");
System.out.println(interfaceInt.getName());

//获取private字段
final Field privateString = concreteClassClass.getDeclaredField("privateString");
// 这一步很重要
privateString.setAccessible(true);
System.out.println(privateString.getName()); 

对于private类型字段一定要用 setAccessible(true) 关闭访问检查,表示让字段可以被访问到,如果不关闭会报错。

字段赋值

 //public 字段设值
final Field publicInt = concreteClassClass.getField("publicInt");
ConcreteClass obj = new ConcreteClass();
System.out.println(publicInt.get(obj)); //prints
publicInt.setInt(obj,); //setting field value to 10 in object
System.out.println(publicInt.get(obj)); //prints

//private字段设值
Field privateField = concreteClassClass.getDeclaredField("privateString");
privateField.setAccessible(true);
ConcreteClass objTest = new ConcreteClass();
System.out.println(privateField.get(objTest)); // prints "private string"
privateField.set(objTest, "private string updated");
System.out.println(privateField.get(objTest)); //prints "private string updated"

//static字段
final Field staticStr = concreteClassClass.getField("staticStr");
staticStr.set(null , "public static string updated");
System.out.println(staticStr.get(null)); 

如果字段是静态的,可以在 get() 方法中将 第一个Object参数传NULL值来调用。

获取字段类型

getType() 方法返回声明的字段类型的 Class 对象,如果字段是原始类型,则返回包装类对象。

 final Class<ConcreteClass> concreteClassClass = ConcreteClass.class;
final Field publicInt = concreteClassClass.getField("publicInt");
final Class<?> type = publicInt.getType();
System.out.println(type.getCanonicalName()); //prints int 

获取字段声明类

可以使用 getDeclaringClass() 字段对象来获取声明字段的类。

 final Field interfaceInt = concreteClassClass.getField("interfaceInt");
final Class<?> declaringClass = interfaceInt.getDeclaringClass();
System.out.println(declaringClass); 

方法相关的反射

获取方法

  • public方法的获取:以HashMap 的 put() 方法为例。
  • 可以使用 getMethod() 来获取类的公共方法,我们需要传递该方法的方法名和参数类型。如果在类中找不到该方法,反射 API 会在超类中查找该方法。
 Method method = Class.forName("java.util.HashMap").getMethod("put", Object.class, Object.class);
//get method parameter types, prints "[class java.lang.Object, class java.lang.Object]"
System.out.println(Arrays.toString(method.getParameterTypes()));
//get method return type, return "class java.lang.Object", class reference for void
System.out.println(method.getReturnType());
//get method modifiers
System.out.println(Modifier.toString(method.getModifiers())); //prints "public 
  • private 方法获取 可以使用 getDeclaredMethod() 来获取私有方法,要使用 setAccessible(true) 关闭访问检查
 final Class<BaseClass> baseClassClass = BaseClass.class;
Method method = baseClassClass.getDeclaredMethod("method", null);
method.setAccessible(true); 

调用方法

  • public方法调用 可以使用 Method 对象的 invoke() 方法来调用方法,以HashMap 的 put() 方法为例
 Method method = Class.forName("java.util.HashMap").getMethod("put", Object.class, Object.class);
Map<String, String> hm = new HashMap<>();
method.invoke(hm, "key", "value");
System.out.println(hm); // prints {key=value} 
  • private方法调用 以调用静态且没有参数的 BaseClass 的 method3()为例。
 final Class<BaseClass> baseClassClass = BaseClass.class;
Method method = baseClassClass.getDeclaredMethod("method", null);
method.setAccessible(true);
method.invoke(null, null); //prints "Method" 
 和静态字段一样,invoke() 方法的第一个参数objectNULL值则是调用静态方法。 

构造方法的反射

获取构造方法

可以在对象的类表示上使用 getConstructor() 方法来获取特定的public构造函数。 可以在对象的类表示上使用 getDeclaredConstructor() 方法来获取特定的public构造函数。

 //获取有参构造方法
Constructor<?> constructor = concreteClassClass.getConstructor(int.class);
//获取构造方法参数
System.out.println(Arrays.toString(constructor.getParameterTypes())); // prints "[int]"

//获取无参构造方法
final Constructor<ConcreteClass> classClassConstructor = concreteClassClass.getConstructor();
System.out.println(classClassConstructor); // 结果:public com.example.reflection.ConcreteClass()

//获取私有构造方法
final Constructor<ConcreteClass> declaredConstructor =concreteClassClass.getDeclaredConstructor(String.class);
declaredConstructor.setAccessible(true);
System.out.println(declaredConstructor); //结果:private com.example.reflection.ConcreteClass(java.lang.String) 

使用构造函数实例化对象

可以在构造函数对象上使用 newInstance() 方法来实例化该类的新实例。

 //获取有参构造方法
Constructor<?> constructor = concreteClassClass.getConstructor(int.class);
final Object newInstance = constructor.newInstance();
//获取method 方法
final Method method = newInstance.getClass().getMethod("method1", null);
//调用method方法
method.invoke(newInstance , null); 

总结

从上面所有的测试中我们可以发现,在Class对象中的方法中只要是带有 「Declared」 字段的都是获取本类中声明的方法、字段或者构造方法等,反之则是调用public的方法;在调用私有方法时要注意一点要将访问检查关闭