文章目录
1. 什么是反射机制
答案:
所谓的反射机制就是 java 语言在运行时拥有一项自观的能力。通过这种能力可以彻底的了解自身的情况为下一步的动作做准备。 Java 的反射机制的实现要借助于 4 个类:Class,Constructor,Field,Method; 其中 Class - 类对 象,Constructor-类的构造器对象,Field-类的属性对象,Method-类的方法对象。通过这四个对象我们可以粗略的看到一个 类的各个组成部分。
1.1 反射原理
Java反射机制(Java Reflection)是Java语言中一种动态(运行时)访问、检测和修改它本身的能力,主要作用是动态(运行时)获取类的完整结构信息和调用对象的方法。
2. Java反射的作用
答案:
在 Java 运行时环境中,对于任意一个类,可以知道这个类有哪些属性和方法。对于任意一个对象,可以调用它的任意一个方法。这种动态获取类的信息 以及动态调用对象的方法的功能来自于 Java 语言的反射(Reflection)机制。
3. Java反射机制提供功能
- 在运行时判断任意一个对象所属的类;
- 在运行时构造任意一个类的对象;
- 在运行时判断任意一个类所具有的成员变量和方法;
- 在运行时调用任意一个对象的方法;
- 生成动态代理
静态编译和动态编译
静态编译:在编译时确定类型,绑定对象
动态编译:运行时确定类型,绑定对象
4. Java反射的优缺点
优点:
1.增加程序的灵活性,可以在运行的过程中动态对类进行修改和操作
2.提高代码的复用率,比如动态代理、spring管理bean,就是用到反射来实现的
3.可以在运行时轻松获取任意一个类的方法、属性,并且还能通过反射进行动态调用
缺点:
1.反射会涉及到动态类型的解析,所以jvm无法对这些代码进行优化,导致性能要比非反射调用更低
2.使用反射以后,代码的可读性会下降
3.反射可以绕过一些限制访问的属性或者方法,可能会导致破坏代码本身的抽象性
5. 应用场景
- 逆向代码 ,例如反编译
- 与注解相结合的框架,例如 Retrofit
- 单纯的反射机制应用框架,例如 EventBus
- 动态生成类框架 例如Gson
6. Class的几种获取方式
(1)获取类的java.lang.Class实例对象,常见的三种方式分别为:
- 通过MyClass.class获取,这里的MyClass指具体类
- 通过Class.forName(“类的全局定名”)获取,全局定名为包名+类名
- 通过new MyClass().getClass()获取,这里的MyClass指具体类
//第一种方式 通过Class类的静态方法——forName()来实现
class1 = Class.forName("com.tsp.reflection.Person");
//第二种方式 通过类的class属性
class1 = Person.class;
//第三种方式 通过对象getClass方法
Person person = new Person();
Class<?> class1 = person.getClass();
(2)通过MyClass.class获取,JVM会使用ClassLoader类加载器将类加载到内存中,但并不会做任何类的初始化工作,返回java.lang.Class对象
(3)通过Class.forName(“类的全局定名”)获取,同样,类会被JVM加载到内存中,并且会进行类的静态初始化工作,返回java.lang.Class对象
(4)通过new MyClass().getClass()获取,这种方式使用了new进行实例化操作,因此静态初始化和非静态初始化工作都会进行,getClass方法属于顶级Object类中的方法,任何子类对象都可以调用,哪个子类调用,就返回那个子类的java.lang.Class对象
这3种方式,最终在JVM堆区对应类的java.lang.Class对象都属于同一个,也就是内存地址相同,进行==双等号比较结果为true,原因是JVM类加载过程中使用的是同一个ClassLoader类加载器加载某个类,不论加载多少次,生成到堆区的java.lang.Class对象始终只有一个,除非自定义类加载器,破坏JVM的双亲委派机制,使得同一个类被不同类加载器加载,JVM才会把它当做两个不同的java.lang.Class对象
7. 实例代码
7.1 常用方法测试
package reflect;
import java.lang.reflect.Method;
import java.util.Scanner;
/**
* JAVA反射机制
* 反射是JAVA的动态机制,可以在程序【运行期间】再确定如:对象实例化,方法调用
* 属性操作等。
* 反射机制可以大大的提高代码的灵活度和可扩展性,但是随之带来的是较慢的运行
* 效率和更多的系统开销。因此不能过度依赖反射机制。
*/
public class ReflectDemo1 {
public static void main(String[] args) throws ClassNotFoundException {
/*
Class类
Class类的实例被称为"类对象"。
JVM在加载一个类的字节码文件到内部时就会实例化一个Class实例
与加载的类对应,用这个Class实例记录加载的类的相关信息。并且
在JVM内部每个被加载的类【都有且只有一个】类对象与之对应。
获取一个类的类对象方式有:
1:类名.class
Class cls = String.class;
Class cls = int.class;
2.Class.forName(String className)
参数:类的完全限定名(包名.类名)
Class cls = Class.forName("java.lang.String")
3:使用ClassLoader加载
4:对象.getClass()
*/
//获取String的类对象
// Class cls = String.class;
// Class cls = Person.class;
// Class cls = Class.forName("java.lang.String");
// Class cls = Class.forName("reflect.Person");
/*
java.lang.String
java.util.HashMap
java.util.ArrayList
java.io.FileInputStream
reflect.Person
*/
Scanner scanner = new Scanner(System.in);
System.out.println("请输入一个类名:");
String className = scanner.nextLine();
Class cls = Class.forName(className);
String name = cls.getName();//获取类的完全限定名
System.out.println(name);
name = cls.getSimpleName();//仅获取类名
System.out.println(name);
/*
Method类
Method类的每一个实例称为"方法对象"。
该类的每个实例表示某个类中的一个方法。通过方法对象我们可以得知
其表示的方法的相关信息,如:方法名,返回值类型,参数个数,参数类型
等等。
*/
/*
Class中的方法:
Method[] getMethods()
获取当前类对象所表示的类的所有公开方法
*/
Method[] methods = cls.getMethods();
System.out.println(name+"一共有"+methods.length+"个公开方法");
for(Method method : methods){
System.out.println(method.getName());
}
}
}
package reflect;
import java.util.Scanner;
/**
* 使用类对象实例化
*/
public class ReflectDemo2 {
public static void main(String[] args) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
Person p = new Person();
System.out.println(p);
/*
反射机制进行实例化的步骤:
1:加载要实例化对象的类的类对象
2:直接通过类对象的方法newInstance()实例化
*/
// Class cls = Class.forName("reflect.Person");
/*
java.util.ArrayList java.util.HashMap
reflect.Person java.util.Date
*/
Scanner scanner = new Scanner(System.in);
System.out.println("请输入一个类名:");
String className= scanner.nextLine();
Class cls = Class.forName(className);
//newInstance()方法会调用类对象所表示的类的【公开的无参构造器】实例化
Object obj = cls.newInstance();//new Person();
System.out.println(obj);
}
}
7.2 使用自定义类测试
package reflect.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 在注解上我们可以使用java预定义的注解来规范某些操作,常用的有:
* @Target 用于指定当前注解可以被应用的位置。
* 不指定该注解时,我们定义的注解可以被应用于任何可以使用注解的位置,
* 比如:类上,方法上,构造器上,属性上,参数上等
* 可以通过为Target注解传入参数来指定可以使用的特定位置,这些位置
* 都被ElementType规定。
* @Target(ElementType.TYPE)
* 只能在类上使用该注解
*
* @Target({ElementType.TYPE,ElementType.FIELD})
* 可以在类上或属性上使用该注解(多个位置时要以数组形式表达)
*
* @Retention 用于指定当前注解的保留级别,有三个可选值
* RetentionPolicy.SOURCE 注解仅保留在源代码中
* RetentionPolicy.CLASS 注解保留在字节码中,但是不可被反射机制访问(默认保留级别)
* RetentionPolicy.RUNTIME 注解保留在字节码中,可被反射机制访问
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface AutoRunClass {
}
package reflect.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface AutoRunMethod {
/*
注解中可以定义参数
格式为:
类型 参数名() [default 默认值]
默认值是可选的,不是必须指定的。如果指定了默认值,那么使用注解时
可以不传入该参数,此时参数使用这个默认值。
如果参数没有指定默认值,那么使用注解时必须为这个参数赋值。
如果注解中只有一个参数时,参数名建议选取"value"。这样外界在使用
该注解时,传递参数则不需要指定参数名,可以直接写作:
@AutoRunMethod(5) 此时value的值就是5
如果该参数名不叫value,比如叫:
int num() default 1;
则使用注解时为该参数传递值时必须写作:
@AutoRunMethod(num=5)
*/
int value() default 1;
/*
一个注解中可以定义多个参数,当使用该注解时传参格式为:name=value
@AutoRunMethod(num=1,name="张三")
或
@AutoRunMethod(name="张三",num=1) (传参顺序不所谓)
*/
// int num() default 1;
// String name();
/*
并且两个以上参数(含两个)时,其中某一个参数名指定为value,传参时也
必须指定该参数名
例如:
@AutoRunMethod(value=1,name="张三")
或
@AutoRunMethod(name="张三",value=1)
*/
// int value() default 1;
// String name();
}
package reflect;
import reflect.annotations.AutoRunClass;
import reflect.annotations.AutoRunMethod;
/**
* 使用当前类测试反射操作
*/
@AutoRunClass
public class Person {
private String name = "张三";
private int age = 20;
public Person(){}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@AutoRunMethod
public void sayHello(){
System.out.println(name+":hello!");
}
@AutoRunMethod(5)
public void sayHi(){
System.out.println(name+":hi!");
}
public void say(String info){
System.out.println(name+":"+info);
}
public void say(String info,int count){
for(int i=0;i<count;i++) {
System.out.println(name + ":" + info);
}
}
@AutoRunMethod(8)
public void watchTV(){
System.out.println(name+"看电视");
}
public void playGame(){
System.out.println(name+"打游戏");
}
@AutoRunMethod(3)
public void justDoIt(){
System.out.println(name+"只管干!");
}
public void eat(){
System.out.println(name+"干饭!");
}
public void sleep(){
System.out.println(name+"睡觉");
}
private void hehe(){
System.out.println("我是Person的私有方法!");
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
package reflect;
import java.lang.reflect.Constructor;
/**
* 使用有参构造器实例化对象
*/
public class ReflectDemo3 {
public static void main(String[] args) throws Exception {
Person p = new Person("李四",22);
System.out.println(p);
//加载类对象
Class cls = Class.forName("reflect.Person");
/*
Constructor 构造器对象
该类的每一个实例用于表示一个类中的某个构造器
*/
//通过类对象获取无参构造器
// Constructor constructor = cls.getConstructor();//Person()
//Person(String,int)
Constructor con = cls.getConstructor(String.class,int.class);
//new Person("王五",33)
Object obj = con.newInstance("王五",33);
System.out.println(obj);
}
}
package reflect;
import java.lang.reflect.Method;
import java.util.Scanner;
/**
* 使用反射机制调用方法
*/
public class ReflectDemo4 {
public static void main(String[] args) throws Exception {
Person p = new Person();
p.eat();
//实例化
// Class cls = Class.forName("reflect.Person");
// Object o = cls.newInstance();//Person p = new Person();
// //调用方法
// Method method = cls.getMethod("eat");//获取名为"eat"的无参方法
// method.invoke(o);//执行eat方法 p.eat()
Scanner scanner = new Scanner(System.in);
System.out.println("请输入类名:");
String className = scanner.nextLine();
System.out.println("请输入方法名:");
String methodName = scanner.nextLine();
Class cls = Class.forName(className);
Object o = cls.newInstance();
Method method = cls.getMethod(methodName);
method.invoke(o);
}
}
package reflect;
import java.lang.reflect.Method;
/**
* 调用有参方法
*/
public class ReflectDemo5 {
public static void main(String[] args) throws Exception {
Person p = new Person();
p.say("你好!");
//实例化
Class cls = Class.forName("reflect.Person");
Object o = cls.newInstance();
//调用方法
Method m = cls.getMethod("say1",String.class);//say(String)
//invoke方法第二个参数开始为调用方法时传递的实际参数
m.invoke(o,"大家好!");
Method m2 = cls.getMethod("say",String.class,int.class);
m2.invoke(o,"嘿嘿",5);
}
}
package reflect;
import java.lang.reflect.Method;
/**
* 反射机制访问私有成员(暴力反射)
*/
public class ReflectDemo6 {
public static void main(String[] args) throws Exception {
// Person p = new Person();
// p.hehe();//编译不通过,私有方法不可以被类的外部调用
Class cls = Class.forName("reflect.Person");
Object o = cls.newInstance();
/*
Class的方法:
Method getMethod(String name,Class... cls)
Method[] getMethods()
上述两个方法仅能获取该类的公开方法(包括从超类继承的方法)
getDeclaredMethod()
getDeclaredMethods()
上述两个方法可以获取本类定义的方法(含有私有的,不含有继承的)
*/
// Method m = cls.getMethod("hehe");
// Method[] methods = cls.getDeclaredMethods();
// for(Method method:methods){
// System.out.println(method.getName());
// }
Method m = cls.getDeclaredMethod("hehe");
m.setAccessible(true);//强行打开访问权限
m.invoke(o);
m.setAccessible(false);//对于私有成员,访问后关闭(必须)
}
}
package reflect;
//单例模式
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return instance;
}
}
package reflect;
import java.lang.reflect.Constructor;
/**
* 反射机制可破坏单例模式
*/
public class ReflectDemo7 {
public static void main(String[] args) throws Exception {
Singleton s1 = Singleton.getInstance();
Singleton s2 = Singleton.getInstance();
System.out.println(s1);
System.out.println(s2);
Class cls = Class.forName("reflect.Singleton");
//获取私有的无参构造器
Constructor c = cls.getDeclaredConstructor();
c.setAccessible(true);
Object o = c.newInstance();
System.out.println(o);
}
}
package reflect;
import reflect.annotations.AutoRunClass;
/**
* 反射机制访问注解
*/
public class ReflectDemo8 {
public static void main(String[] args) throws Exception {
/*
常用的反射对象:
Class,Method,Constructor,Field,Annotation
都提供了一个方法:
boolean isAnnotationPresent(Class cls)
判断当前反射对象表示的内容是否被cls所表示的注解标注了
*/
Class cls = Class.forName("reflect.Person");
//判断当前cls所表示的类Person是否被注解AutoRunClass标注了
if(cls.isAnnotationPresent(AutoRunClass.class)){
System.out.println("被标注了!");
}else{
System.out.println("没有被标注!");
}
}
}
package reflect;
import reflect.annotations.AutoRunMethod;
import java.lang.reflect.Method;
public class ReflectDemo9 {
public static void main(String[] args) throws Exception {
Class cls = Class.forName("reflect.Person");
// Method method = cls.getMethod("sayHi");
Method method = cls.getMethod("eat");
if(method.isAnnotationPresent(AutoRunMethod.class)){
System.out.println("被标注了!");
}else{
System.out.println("没有被标注!");
}
}
}
package reflect;
import reflect.annotations.AutoRunClass;
import reflect.annotations.AutoRunMethod;
import java.lang.reflect.Method;
/**
* 访问注解参数
*/
public class ReflectDemo10 {
public static void main(String[] args) throws Exception {
Class cls = Class.forName("reflect.Person");
if(cls.isAnnotationPresent(AutoRunClass.class)){
Method method = cls.getDeclaredMethod("watchTV");
if(method.isAnnotationPresent(AutoRunMethod.class)){
/*
所有反射对象都支持方法:
Annotation getAnnotation(Class cls)
获取cls类对象表示的注解
*/
//返回当前method对象表示的方法"watchTV"上的注解@AutoRunMethod
AutoRunMethod arm = method.getAnnotation(AutoRunMethod.class);
int value = arm.value();
System.out.println("参数值:"+value);
}
}
}
}