反射

JAVA学习网 2020-09-15 17:59:02

1、反射

Java 反射机制是在 运行时 动态的获取信息和调用对象的方法,对于任意一个类,都能够获取这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性

程序中一般的对象的类型都是在编译期就确定下来的,而 Java 反射机制可以动态地创建对象并调用其属性,这样的对象的类型在编译期是未知的。所以可以通过反射机制直接创建对象,即使这个对象的类型在编译期是未知的

1.1 反射的作用

  • 可以实现简单的反编译,获取类中的属性和方法等基本信息

  • 获取类的属性、方法等,甚至可以调用 private 方法

  • 在使用 IDE 时,当输入一个对象或类并想调用它的属性或方法时,输入点号,编译器就会自动列出它的属性或方法,这里就会用到反射

  • 最重要的用途就是开发各种通用框架。很多框架都是配置化的,比如通过 XML 文件配置 Bean,为了保证框架的通用性,可能需要根据配置文件加载不同的对象或类,调用不同的方法,这个时候就必须用到反射,运行时动态的加载需要加载的对象

1.2 反射的优点

  • 提高程序的灵活性和扩展性,降低耦合性
  • 允许程序创建和控制任何类的对象,无需提前硬编码目标类

1.3 反射的缺点

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

  • 安全限制:使用反射技术要求程序必须在一个没有安全限制的环境中运行

  • 破坏封装性:由于反射允许代码执行一些在正常情况下不被允许的操作,如访问私有的属性和方法,降低可移植性。反射代码破坏了抽象性,因此当平台发生改变的时候,代码的行为就有可能也随着变化

1.4 Class 对象

在类加载器将 .class 文件读取到内存中的时候,JVM 会创建这个 .class 文件的对象,并且只创建一个存放到 JVM 的方法区内存中,即 在运行期间,一个类,只有一个 Class 对象产生。在 java.lang 包下有个 Class 类,这个类就是 .class 文件的对象类型,任何类在被使用时,都会创建这个类的 Class 对象。反射相关的类一般都在 java.lang.relfect 包里

Class 和 java.lang.reflect 一起对反射提供了支持,java.lang.reflect 类库主要包含了以下三个类

  • Field :可以使用 get 和 set 方法读取和修改 Field 对象关联的字段
  • Method :可以使用 invoke 方法调用与 Method 对象关联的方法
  • Constructor :可以用 Constructor 创建新的对象

1.4.1 获取 Class 对象的三种方式

// 1.调用某个对象的getClass方法,不常用,已经有对象了,需要反射的意义不大
Person person = new Person();
Class one = person.getClass();

// 2.使用Class类的forName静态方法,常用,字符串可以传入也可写在配置文件中等多种方法
Class two = Class.forName("com.test.Person");

// 3.直接获取某一个对象的class,不常用,需要导入类的包,依赖太强,不导包就抛编译错误
Class three = Person.class;

// 因为Person这个类在JVM中只有一个,所以内存地址应该都是相同的,指向堆中唯一的Class对象
System.out.println(one == two);		// true
System.out.println(two == three);	// true

// 也可以使用isInstance方法来判断是否为某个类的实例
System.out.println(one.isInstance(person));		// true
System.out.println(two.isInstance(person));		// true
System.out.println(three.isInstance(person));	// true

1.4.2 使用反射创建对象

// 1.通过Class对象的newInstance()方法
Test test = (Test) Class.forName("com.test.Test").newInstance();
// 或
Test test = Test.class.newInstance();

// 2.通过Constructor对象的newInstance()方法
Constructor<Test> constructor = Test.class.getConstructor();
Test test = constructor.newInstance();

1.4.3 获取方法

public class Person {
    public String name;

    public Person() {}
    public Person(String name) {this.name = name;}

    public void show() {System.out.println(this.name);}

    public void say(String name){System.out.println(name);}
}

// 获取类或接口的所有方法,但不包括继承的方法
Method[] methods = person.getDeclaredMethods();
// 输出:访问修饰符 返回类型 包名.类名.方法名
// public java.lang.String com.test.Test.Show()
for (Method method : methods) {
	System.out.println(method);
}

// 获取类或接口的所有公共方法,包括继承的方法
Method[] methods = person.getMethods();

// 返回一个特定的方法,第一个参数为方法名称,后面的参数为方法的参数对应Class的对象
Method method = person.getDeclaredMethod("show");
Method method = person.getDeclaredMethod("say", String.class);

// 返回一个特定的方法,第一个参数为方法名称,后面的参数为方法的参数对应Class的对象
// 只能获取共有的方法,否则回会报NoSuchMethodException
Method method = person.getMethod("say", String.class);

// 获取类中所有的构造方法
Constructor[] constructors = person.getDeclaredConstructors();
for (Constructor constructor : constructors) {
    System.out.println(constructor);
}

// 获取类中所有的公共的构造方法
Constructor[] constructors = person.getConstructors();

// 返回一个特定的构造方法,参数为方法的参数对应Class的对象
Constructor constructor = person.getDeclaredConstructor(String.class);

// 返回一个特定的公共的构造方法,参数为方法的参数对应Class的对象
Constructor constructor = person.getConstructor(String.class);

// 使用反射调用类中的方法
Method method = person.getDeclaredMethod("say", String.class);
Object obj = person.newInstance();
method.invoke(obj, "特朗普");

1.4.4 获取成员变量

// 获取类或接口的所有的成员变量,但不包括继承的成员变量
Field[] fields = person.getDeclaredFields();
// 输出:访问修饰符 数据类型 包名.类名.变量名
// public java.lang.String first.Person.name
for (Field field : fields) {
    System.out.println(field);
}

// 获取类或接口的所有的公共成员变量,包括继承的成员变量
Field[] fields = person.getFields();

// 返回一个特定的成员变量,参数为成员变量名
Field field = person.getDeclaredField("name");

// 返回一个特定的共有的成员变量,参数为成员变量名
Field field = person.getField("name");

// 使用反射获取类中指定的属性并赋值
Field field = person.getDeclaredField("name");
Object obj = person.newInstance();
// 从外部打破封装性
field.setAccessible(true);
field.set(obj, "特朗普");
System.out.println(field.get(obj));
  • 可以使用 void setAccessible(true) 方法,之后调用私有方法或成员变量,

1.4.5 获取父类或父接口

// 获取父类
Class sup = person.getSuperclass();

// 获取父接口
Class[] inter = person.getInterfaces();

更多:深入解析 Java反射()深入解析 Java反射(2)

阅读(2655) 评论(0)