Java - 反射(reflect)

本文详细介绍了Java中的反射机制,从什么是反射开始,讲解了Class类、Field类、Method类以及如何通过反射查看类的信息。同时,讨论了反射的安全性和可能带来的缺点,提醒开发者在使用时需谨慎。

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

一 什么是反射

反射是一种可以动态加载正在运行的类的信息的机制(信息包括属性、方法、注解等)。
如果有一个类,则可以通过类创建对象.而反射可以实现通过一个对象找到一个类.

二 Class类

Java
将所有的类都封装成一个Class,
将类中所有的属性封装成一个Field,
将所有的构造方法都封装成一个Constructor,
将所有的方法都封装成一个Method,
将所有的注解都封装成一个Annotation

Class类是java.lang包中的类.当程序使用某个类时,Java虚拟机会将该类加载到内存中,该类的class文件读入内存,并为该类创建一个java.lang.Class对象,它包含了与类有关的信息.每个类都有一个Class对象,即每当编写并编译了一个新类时,就会产生一个Class对象,被保存在同名的.class文件.在Java程序中获得Class对象通常有三种方式:

  1. 调用某个类的class属性来获取该类的Class对象
  2. 调用某个对象的getClass()方法.这个方法是java.lang.Object类中的一个方法.所有的Class对象都可以调用这个方法.
  3. Class.forName(String className) 使用Class类的静态方法,参数是某个类的权限定类名(包含完整的包名)

(这三种方式的区别是?)

public class TestClass {
    public static void main(String[] args) throws ClassNotFoundException {
        Class<?> tClass = String.class;
        System.out.println(tClass);

        String s = "Hello";
        Class<?> tclass1 = s.getClass();
        System.out.println(tclass1);

        Class<?> tclass2 = Class.forName("java.lang.String");
        System.out.println(tclass2);
    }
}

//输出
class java.lang.String
class java.lang.String
class java.lang.String

三 通过反射我们可以查看类的信息

可以借助java.lang.reflect包中的以下几个类实现:

  1. Constructor —— 构造方法
  2. Field —— 属性
  3. Method —— 方法
  4. Annotation —— 注解

获取java.lang.Math类的声明(包含类的名称,方法,类的继承关系),类的父类及实现的接口

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

public class Test {
    public static void main(String[] args) {
        Class <?> c1 = null;
        try {
            //这里用Class的forName()方法获得想要的类
            c1 = Class.forName("java.lang.Math");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        //获取所有声明的属性(不包含从父类继承的属性)
        Field[] fields = c1.getDeclaredFields();
        for (Field field: fields) {
            System.out.println(field);
        }

       //获取实现的全部接口
        Class<?> c[] = c1.getInterfaces();
        //打印所有接口
        for (int i = 0; i < c.length; i ++) {
            System.out.println("c[i] = " + c[i]);
        }

        //获取全部的构造方法
        Constructor<?> con[] = c1.getConstructors();
        for (int i = 0; i < con.length; i ++) {
            System.out.println("c[i] = " + con[i]);
        }

        //获取全部的方法
        Method m[] = c1.getMethods();
        for (int i = 0; i < m.length; i ++) {
            Class<?> r = m[i].getReturnType();
            Class<?> p[] = m[i].getParameterTypes();
            int xsf = m[i].getModifiers();
            System.out.println(Modifier.toString(xsf) + " ");
            System.out.println(r.getName() + " ");
            System.out.println(m[i].getName() + " ");
            System.out.println("(");
            for (int j = 0; j < p.length; j ++) {
                System.out.println(p[j].getName() + " " + "arg" + j);
                if (j < p.length - 1) System.out.println(",");
            }
        }
        
        //获取父类名
        Class <?> c2 = c1.getSuperclass();
        System.out.println("父类名为: " + c2.getName());
    }
}

这个例子讲解的不错:https://www.cnblogs.com/fzz9/p/7738381.html

四 Field类

表示类属性,也即类的成员变量,包含了注解、访问修饰符,属性类型,属性名称

  //获取属性(成员变量)的值,obj 指定的成员变量所属的对象
  Object		get(Object obj) 
  // 获取属性上指定的注解
  <T extends Annotation> T		getAnnotation(Class<T> annotationClass) 
   //获取属性上所有的注解   
  Annotation[]    	getDeclaredAnnotations() 
  // 获取属性名称    
  String     	getName() 
  // 获取属性类型     
  Class<?>    	getType()
  // 为属性赋值  Object obj  所属对象, Object value  值   
  void    		set(Object obj, Object value) 
public class FieldTest {
	
	@AnnotationDemo01(10)
	private Integer id;
	
	private String name;
	
	public static void main(String[] args) throws Exception {
		//1. 类对应的Class 对象
		Class cls = FieldTest.class;
		// 找到类中属性
		Field id = cls.getDeclaredField("id");
		Field name = cls.getDeclaredField("name");
		//获取属性名称
		System.out.println(id.getName());
		//获取属性的类型
		System.out.println(id.getType().getSimpleName());
		//获取属性上的注解
		AnnotationDemo01 annotation = id.getAnnotation(AnnotationDemo01.class);
		System.out.println(annotation.value());
		//获取属性上所有的注解
		Annotation[] annotations = id.getDeclaredAnnotations();
		for (Annotation annotation2 : annotations) {
			System.out.println(annotation2);
		}
		// 获取属性值
		FieldTest obj1 = new FieldTest();
		obj1.id = 100;
		obj1.name = "韩梅梅";
		FieldTest obj2 = new FieldTest();
		obj2.id = 101;
		obj2.name = "李磊";
		Object object1 = id.get(obj1);
		System.out.println(object1);
		
		Object object2 = id.get(obj2);
		System.out.println(object2);
		//设置属性值
		FieldTest obj3 = new FieldTest();
		id.set(obj3, 102);
		name.set(obj3, "Lucy");
		System.out.println(obj3.id+"   "+ obj3.name);
	}
}

五 Method类

表示类的方法,也即注解、访问修饰符、返回值、参数列表、【异常】

 //获取方法上指定的注解
<T extends Annotation> T	getAnnotation(Class<T> annotationClass) 
 //获取方法上所有的注解
 Annotation[]    			getDeclaredAnnotations() 
 // 获取方法名称
 String    		getName() 
 // 获取参数类型列表   
 Class<?>[]		getParameterTypes() 
 // 获取返回值类型   
 Class<?>    	getReturnType() 
 // 执行方法      Object obj 执行该方法的对象, 	Object... args  方法的参数
 invoke(Object obj, Object... args) 
 //设置访问权限   当私有时,在其他类中,使用反射去访问属性或者方法需要设置访问权限 设置为  true
 setAccessible(boolean b) 
 //使用反射创建对象    
 newInstance() 
public class MethodTest {
	
	@AnnotationDemo01(1)
	public void test1() {
		System.out.println(" 我是共有的的 无参  无返回值的  方法  test1  ");
	}
	
	public static void main(String[] args) throws Exception {
		//1.找到类对应Class 类对象
		Class cls = MethodTest.class;
		// 根据名称 和参数找到对应的方法
		Method m  = cls.getDeclaredMethod("test1");
		// 获取方法名称
		System.out.println(m.getName());
		// 参数个数
		System.out.println(m.getParameterCount());
		// 参数类型列表
		System.out.println(m.getParameterTypes().length);
		//获取方法上的注解
		AnnotationDemo01 annotation = m.getDeclaredAnnotation(AnnotationDemo01.class);
		System.out.println(annotation);
		//获取所有注解
		Annotation[] annotations = m.getDeclaredAnnotations();
		for (Annotation annotation2 : annotations) {
			System.out.println(annotation2);
		}
		//执行test1方法,在此之前要先声明执行方法的对象
		MethodTest obj = new MethodTest();
		m.invoke(obj);
	}

}

六 反射安全吗?

反射被广泛地用于那些需要在运行时检测或修改程序行为的程序中。这是一个相对高级的特性,只有那些语言基础非常扎实的开发者才应该使用它。如果能把这句警示时刻放在心里,那么反射机制就会成为一项强大的技术,可以让应用程序做一些几乎不可能做到的事情。

七 反射的缺点 Drawbacks of Reflection

尽管反射非常强大,但也不能滥用。如果一个功能可以不用反射完成,那么最好就不用。在我们使用反射技术时,下面几条内容应该牢记于心:

性能第一
反射包括了一些动态类型,所以JVM无法对这些代码进行优化。因此,反射操作的效率要比那些非反射操作低得多。我们应该避免在经常被 执行的代码或对性能要求很高的程序中使用反射。

安全限制
使用反射技术要求程序必须在一个没有安全限制的环境中运行。如果一个程序必须在有安全限制的环境中运行,如Applet,那么这就是个问题了。

内部暴露
由于反射允许代码执行一些在正常情况下不被允许的操作(比如访问私有的属性和方法),所以使用反射可能会导致意料之外的副作用--代码有功能上的错误,降低可移植性。反射代码破坏了抽象性,因此当平台发生改变的时候,代码的行为就有可能也随着变化

### Java 反射 Reflect 使用教程 #### 获取 `Class` 对象 在Java中,反射的核心是`Class`对象。可以通过三种方式获得: - **通过名**:使用`.class`语法。 ```java Class<?> clazz = String.class; ``` - **通过实例**:调用对象的`.getClass()`方法。 ```java Object obj = new Integer(0); Class<?> clazz = obj.getClass(); ``` - **通过全限定名**:利用`Class.forName(String className)`静态方法。 ```java try { Class<?> clazz = Class.forName("java.lang.String"); } catch (ClassNotFoundException e) { // Handle exception } ``` 上述每种方法都可用于获取指定型的`Class`对象[^1]。 #### 访问字段 (`Field`) 一旦有了`Class`对象,就可以访问其声明的成员变量(即字段)。这括私有字段在内的所有字段。 ```java // 假设有一个Person public class Person { private String name; public void setName(String n){ this.name=n; } @Override public String toString(){ return "Name:"+this.name; } } try { Class<Person> personClass = Person.class; // 获取名为"name"的私有字段 Field field = personClass.getDeclaredField("name"); // 设置可访问标志位为true以便能够读写该字段 field.setAccessible(true); Person p = new Person(); // 向p设置值 field.set(p,"John Doe"); System.out.println(p.toString()); // 输出 Name:John Doe } catch (NoSuchFieldException | IllegalAccessException e) { // 处理异常... } ``` 这段代码展示了如何创建一个新的人(`Person`)对象并为其内部不可见的名字(`name`)属性赋值。 #### 调用方法 (`Method`) 除了可以操作字段外,还可以通过反射来执行某个特定的方法。 ```java try { Class<Person> personClass = Person.class; Method method = personClass.getMethod("setName",String.class); Person p = new Person(); // 执行setName()方法并将参数传递给它 method.invoke(p, "Jane Doe"); System.out.println(p.toString()); } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { // 处理异常... } ``` 这里说明了怎样找到公共方法`setName`并通过传入的对象实例去调用这个方法。 #### 构造器 (`Constructor`) 最后,也可以借助于反射机制来进行对象的构建工作。 ```java try { Class<Person> personClass = Person.class; Constructor<Person> constructor = personClass.getConstructor(); Person p = constructor.newInstance(); System.out.println(p.toString()); } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { // 处理异常... } ``` 此片段显示了一个无参构造函数是如何被用来生成新的`Person`实体的。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值