一 什么是反射
反射是一种可以动态加载正在运行的类的信息的机制(信息包括属性、方法、注解等)。
如果有一个类,则可以通过类创建对象.而反射可以实现通过一个对象找到一个类.
二 Class类
Java
将所有的类都封装成一个Class,
将类中所有的属性封装成一个Field,
将所有的构造方法都封装成一个Constructor,
将所有的方法都封装成一个Method,
将所有的注解都封装成一个Annotation
Class类是java.lang包中的类.当程序使用某个类时,Java虚拟机会将该类加载到内存中,该类的class文件读入内存,并为该类创建一个java.lang.Class对象,它包含了与类有关的信息.每个类都有一个Class对象,即每当编写并编译了一个新类时,就会产生一个Class对象,被保存在同名的.class文件.在Java程序中获得Class对象通常有三种方式:
- 调用某个类的class属性来获取该类的Class对象
- 调用某个对象的getClass()方法.这个方法是java.lang.Object类中的一个方法.所有的Class对象都可以调用这个方法.
- 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包中的以下几个类实现:
- Constructor —— 构造方法
- Field —— 属性
- Method —— 方法
- 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,那么这就是个问题了。
内部暴露
由于反射允许代码执行一些在正常情况下不被允许的操作(比如访问私有的属性和方法),所以使用反射可能会导致意料之外的副作用--代码有功能上的错误,降低可移植性。反射代码破坏了抽象性,因此当平台发生改变的时候,代码的行为就有可能也随着变化