03. JAVA反射机制

本文深入解析Java反射机制,涵盖反射基础、动态编译的区别,以及反射的优点和缺点。通过实例演示如何获取Class对象、判断类型、创建实例、操作成员变量和方法,以及在Gson反序列化中的应用。最后,以自动注入注解为例,展示了反射在实际开发中的实用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.反射概述

一般情况下,我们使用某个类时,必定知道它是什么类,是用来做什么的,并能够获得此类的引用,于是我们直接对这个类进行实例化,之后使用这个类对象进行操作,而反射是一开始不知道我要初始化的对象是什么,自然无法使用new关键字来创建对象,这时使用JDK提供的反射API进行反射调用,反射就是在运行状态中,都能调用它的任意方法和属性,并能改变它的属性,这种动态获取的信息以及动态调用对象的方法的功能称为java的反射机制,这也是Java被视为动态语言的关键;

必须先得到代表的字节码的Class,Class类用于表示.class文件(字节码),对任意一个类或一个对象;从对象出发,通过该反射(Class类)可以取得类的完整信息:类名(Class类型),所在包,具有的所有方法Method[]类型,获取某个方法完整信息(包括修饰符,返回值类型,参数类型),所有属性Field,某个属性信息,构造方法(Constructors);

2.静态编译和动态编译

静态编译:在编译时确定类型,绑定对象

动态编译:运行时确定类型,绑定对象

java反射机制主要提供了以下功能

1) 在运行时构造任意一个类的对象;

2) 在运行时获取或修改任意一个类所具有的成员变量和方法;

3) 在运行时调用任意一个对象的方法(属性)

3.反射机制优缺点

1) 优点: 运行期类型的判断,动态加载类,提高代码灵活度

2) 缺点: 性能瓶颈,反射相当于一系列解释操作,通知JVM要做的事情,性能比直接的java代码要慢很多;

反射始于Class,Class是一个类,封装了当前对象对应的类的信息,一个类中有属性,方法,构造器,对每个类而言JRE都为其保留

一个不变的Class类型对象,一个Class对象包含了特定某个类的有关信息,对象只能由系统建立对象,一个类(而不是一个对象)

在JVM中只会有一个Class实例

4.反射的使用

1>获取Class对象的三种方法

*1.通过类名获取 类名.class;

*2.通过对象获取 对象名.getClass();

*3.通过全类名获取 Class.forName(全类名) classLoader.loadClass(全类名);

● 使用Class类的 forName 静态方法

   public static Class<?> forName(String className)
   public static void main(String[] args) {
        //直接获取对象的class
        Class<?> clazz = int.class;
        Class<?> Clazz1= Integer.TYPE;
        //调用每个对象getClass()方法
        StringBuilder sb = new StringBuilder("1,2,3");
        Class<?> klazz1=sb.getClass();
        /**
         * clazz=>int
         * Clazz1=>int
         * klazz1==>class java.lang.StringBuilder
         */
        System.out.println("clazz=>"+clazz+"\n"+"Clazz1=>"+Clazz1+"\n"+"klazz1==>"+klazz1);
   }

2>判断是否为某个类的实例

一般地,我们用 instanceof 关键字来判断是否为某个类的实例。同时我们也可以借助反射中 Class 对象的

isInstance() 方法来判断是否为某个类的实例,它是一个 native 方法:

public native boolean isInstance(Object obj);

判断是否为某个类的类型

public boolean isAssignableFrom(Class<?> cls)

3>创建实例

通过反射来生成对象主要有两种方式

●使用Class对象newInstance()方法来创建Class对象对应类的实例

Class<?> c = String.class;
Object o = c.newInstance();
或者:
Constructor<?> constructor = c.getConstructor(String.class);
Object obj = constructor.newInstance("2223");

4>得到构造器的方法

Class<String> c = String.class;
c.getConstructor(String.class);//获得使用特殊的参数类型的public构造方法(包括父类)
c.getConstructors();//获得类的公共构造方法
c.getDeclaredConstructor(String.class);//获得使用特点参数的构造函数(包括私有)
c.getDeclaredConstructors();// 获得类的所有构造方法

5>获取类的成员变量(字段)信息

Field[] fields = clazz.getFields();
Field[] declaredFields = clazz.getDeclaredFields();//获取自己所有成员属性(不包括父类的)
clazz.getSuperclass().getDeclaredFields();//获取父类的所有成员属性;
Field length = clazz.getField("length");//获取类的自己+父类的成员字段,不包括(private修饰的)
Field hash = clazz.getDeclaredField("hash");//获得类声明的命名的字段 (没有权限限制)

6>获取调用方法

Class<String> clazz = String.class;
String s = clazz.getConstructor(String.class).newInstance("222");
Method[] methods = clazz.getMethods();//自己+父类的成员方法,不包括(private修饰的)
Method[] declaredMethods = clazz.getDeclaredMethods();//自己所有成员方法(不包括父类的)
Method hashCode = clazz.getMethod("hashCode", (Class<?>) null);//指定公共方法
Method equals = clazz.getDeclaredMethod("equals", Object.class);//指定方法 名,参数类型
boolean isSame = (boolean) equals.invoke(s, "221");//使用反射的方法

7>利用反射创建数组

数组在Java里是比较特殊的一种类型,他可以赋值给一个Object Reference其中的Array类为java.lang,reflect,Array

类,通过 String[] arr = (String[]) Array.newInstance(String.class, 4);

8>反射获取泛型真实类型

当我们对一个泛型类型进行反射时,需要得到泛型中的真实数据类型,来完成如json反序列化的操作.此时需要通过Type体系

来完成,Type接口包含了一个实现类(Class)和四个接口,它们分别是:

1) TypeVariable

泛型类型变量,可获取泛型上下限等信息

Field fk = TypeTest.class.getDeclaredField("key");//获取字段的类型
TypeVariable keyType = (TypeVariable) fk.getGenericType();
System.out.println(keyType.getName()); //K
keyType.getGenericDeclaration(); //class com.xzh.study.annotation.TestType.
for (Type type : keyType.getBounds()) {
   System.out.println(type);//有两个 interface java.lang.comparable 和 java.io.Serializable
}

2) ParameterizedType

具体的泛型类型,可以获得元数据中泛型签名类型(泛型真实类型)

Map<String, String> map;
Field f = TypeTest.class.getDeclaredField("map");
System.out.println(f.getGenericType()); // java.util.Map<java.lang.String, java.lang.String>
ParameterizedType pType = (ParameterizedType) f.getGenericType();
System.out.println(pType.getRawType());
for(Type type :pType.getActualTypeArguments()){
    System.out.println(type);
} // 打印两遍: class java.lang.String

3) GenericArrayType

当需要描述的类型是泛型类的数组时,比如List[] Map[],此接口会最为Type的实现

public class TypeTest<T> {
    List<String>[] lists;
    
    public static void main(String[] args) throws Exception {
        Field f = TypeTest.class.getDeclaredField("lists");
        GenericArrayType genericType = (GenericArrayType) f.getGenericType();
        Type genericComponentType = genericType.getGenericComponentType();
        System.out.println("genericComponentType" + genericComponentType);
    }
}

4) WildcardType

通配符泛型,获得上下限信息;

private List<? extends Number> a; // 上限
private List<? super String> b; //下限
public static void main(String[] args) throws Exception {
   Field fieldA = TestType.class.getDeclaredField("a");
   Field fieldB = TestType.class.getDeclaredField("b");
   // 先拿到泛型类型
   ParameterizedType pTypeA = (ParameterizedType) fieldA.getGenericType();
   ParameterizedType pTypeB = (ParameterizedType) fieldB.getGenericType();
   // 再从泛型里拿到通配符类型
   WildcardType wTypeA = (WildcardType) pTypeA.getActualTypeArguments()[0];
   WildcardType wTypeB = (WildcardType) pTypeB.getActualTypeArguments()[0];
   // 方法测试
   System.out.println(wTypeA.getUpperBounds()[0]); // class java.lang.Number
   System.out.println(wTypeB.getLowerBounds()[0]); // class java.lang.String
   // 看看通配符类型到底是什么,
   System.out.println(wTypeA); //打印结果为: ? extends java.lang.Number
}

9>Gson反序列化

static class Response<T> {
    T data;
    int code;
    String message;
            
    @Override
    public String toString() {
        return "Response{" +
                "data=" + data +
                ", code=" + code +
                ", message='" + message + '\'' +
                '}';
    }
        
    public Response(T data, int code, String message) {
        this.data = data;
        this.code = code;
        this.message = message;
    }
}


static class Data {
    String result;
    
    public Data(String result) {
        this.result = result;
    }
    
    @Override
    public String toString() {
        return "Data{" + "result=" + result + '}';
    }
}


public static void main(String[] args) {
     Response<Data> dataResponse = new Response<>(new Data("data"), 1, "success");
     Gson gson = new Gson();
     String json = gson.toJson(dataResponse);
     System.out.println(json);//打印结果 {"data":{"result":"data"},"code":1,"message":"success"}
     
     //为什么TypeToken要定义为抽象类,因为抽象类或接口在使用时要创建对应的实现类({} 匿名内部类),
     //此时确定泛型类型,编译才能够将泛型signature信息记录到Class元数据中,编辑到匿名内部类中;
     Response<Data> resp=gson.fromJson(json,new TypeToken<Response<Data>>() {
     }.getType());
     System.out.println(resp.data.result);//打印结果 data
}

实战案例

注解+反射 自动完成findViewById

 //InjectView注解类
 @Target(ElementType.FIELD)
 @Retention(RetentionPolicy.CLASS)
 public @interface InjectView {
     @IdRes int value();
 }


 //注入工具类
 public class InjectUtils {
     public static void injectView(Activity activity) {
         Class<? extends Activity> cls = activity.getClass();
         //获得此类所有的成员
         Field[] declaredFields = cls.getDeclaredFields();
         for (Field field : declaredFields) {
             //判断属性是否被InjectView注解声明
             if (field.isAnnotationPresent(InjectView.class)) {
                 InjectView injectView = field.getAnnotation(InjectView.class);
                 //获得注解中设置的id
                 int id = injectView.value();
                 View view = activity.findViewById(id);
                 //反射设置属性的值
                 field.setAccessible(true);//允许访问权限,允许操作private的属性
                 try {
                     //反射赋值
                     field.set(activity,view);
                 } catch (IllegalAccessException e) {
                     e.printStackTrace();
                 }
             }
        }
     }
 }


 //使用InjectUtils完成注入
 public class MainActivity extends AppCompatActivity {
     @InjectView(R.id.text_id)
     TextView tv;


     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.activity_main);
         initData();
     }


     private void initData() {
         InjectUtils.injectView(this);
         tv.setText("这是测试的代码");
     }
 }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值