反射

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 对象的三种方式

  • 调用某个对象的 getClass 方法,不常用,已经有对象了,需要反射的意义不大
    • Class one = test.getClass();
  • 使用 Class 类的 forName 静态方法,常用,字符串可以传入也可写在配置文件中等多种方法
    • Class two = Class.forName("com.test.Test");
  • 直接获取某一个对象的 class,不常用,需要导入类的包,依赖太强,不导包就抛编译错误
    • Class three = Test.class;

1.5 使用反射创建对象

// 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.6 获取方法

1.6.1 获得方法数组

Method[] getDeclaredMethods() Method[] getMethods()
获取类或接口的 所有方法 获取类或接口的 所有公共方法
不包括继承的方法 包括继承的方法(公共方法)
public class Person { public void show() { System.out.println("show"); } public void say(String name) { System.out.println(name); } private void run() { System.out.println("run"); } }
public class Test { public static void main(String[] args) throws Exception { Class person = Class.forName("com.test.Person"); // 获取类或接口的所有方法,但不包括继承的方法 Method[] m1s = person.getDeclaredMethods(); for (Method method : m1s) { System.out.println(method); } // 获取类或接口的所有公共方法,包括继承的方法 Method[] m2s = person.getMethods(); for (Method method : m2s) { System.out.println(method); } } }
  • 输出了 Person 类的所有方法

private void com.test.Person.run()
public void com.test.Person.show()
public void com.test.Person.say(java.lang.String)

  • 只输出了 Person 类的公共方法,并且输出了隐含继承的 Object 类的公共方法

public void com.test.Person.show()
public void com.test.Person.say(java.lang.String)
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public java.lang.String java.lang.Object.toString()
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()

1.6.2 获取方法

Method getDeclaredMethod() Method getMethod()
获取类或接口中的方法 获取类或接口中的 公共方法
不包括继承的方法 包括继承的方法(公共方法)
public class Test { public static void main(String[] args) throws Exception { Class person = Class.forName("com.test.Person"); // 返回一个特定的方法,第一个参数为方法名称,后面的参数为方法的参数对应Class的对象 Method m1 = person.getDeclaredMethod("show"); Method m2 = person.getDeclaredMethod("say", String.class); Method m3 = person.getDeclaredMethod("run"); System.out.println(m1); System.out.println(m2); System.out.println(m3); // 返回一个特定的方法,第一个参数为方法名称,后面的参数为方法的参数对应Class的对象 // 只能获取公共的方法,否则回会报NoSuchMethodException Method m4 = person.getMethod("show"); Method m5 = person.getMethod("say", String.class); Method m6 = person.getMethod("hashCode"); System.out.println(m4); System.out.println(m5); System.out.println(m6); } }

public void com.test.Person.show()
public void com.test.Person.say(java.lang.String)
private void com.test.Person.run()

  • 可以输出隐含继承的 Object 类的 hashCode 方法

public void com.test.Person.show()
public void com.test.Person.say(java.lang.String)
public native int java.lang.Object.hashCode()

1.6.3 获取构造方法数组

public class Person { public Person() { } public Person(String name) { System.out.println(name); } }
public class Test { public static void main(String[] args) throws Exception { Class person = Class.forName("com.test.Person"); // 获取类中所有的构造方法 Constructor[] c1s = person.getDeclaredConstructors(); for (Constructor constructor : c1s) { System.out.println(constructor); } // 获取类中所有的公共的构造方法 Constructor[] c2s = person.getConstructors(); for (Constructor constructor : c2s) { System.out.println(constructor); } } }

public com.test.Person()
public com.test.Person(java.lang.String)
private com.test.Person(int)

public com.test.Person()
public com.test.Person(java.lang.String)

1.6.4 获取构造方法

public class Test { public static void main(String[] args) throws Exception { Class person = Class.forName("com.test.Person"); // 返回一个特定的构造方法,参数为方法的参数对应Class的对象 Constructor c1 = person.getDeclaredConstructor(); Constructor c2 = person.getDeclaredConstructor(String.class); Constructor c3 = person.getDeclaredConstructor(int.class); System.out.println(c1); System.out.println(c2); System.out.println(c3); // 返回一个特定的公共的构造方法,参数为方法的参数对应Class的对象 Constructor c4 = person.getConstructor(); Constructor c5 = person.getConstructor(String.class); System.out.println(c4); System.out.println(c5); } }

public com.test.Person()
public com.test.Person(java.lang.String)
private com.test.Person(int)

public com.test.Person()
public com.test.Person(java.lang.String)

1.6.5 使用反射调用类中的方法

Method say = person.getDeclaredMethod("say", String.class); Object obj = person.newInstance(); // Person obj = (Person) person.newInstance(); // 传入对象和参数 say.invoke(obj, "特朗普"); // 调用私有方法 Method run = person.getDeclaredMethod("run"); // 破坏封装 run.setAccessible(true); run.invoke(obj);
  • 可以使用 void setAccessible(true) 方法,之后调用私有方法或成员变量

1.7 获取成员变量

1.7.1 获取成员变量数组

Field[] getDeclaredMethod() Field[] getMethod()
获取类或接口的所有的成员变量 获取类或接口的所有的 公共成员变量
不包括继承的成员变量 包括继承的成员变量(公共成员变量)
public class Person extends Human { public int x; public String y = "name"; int z; } public class Human { public float f; double d; }
public class Test { public static void main(String[] args) throws Exception { Class person = Class.forName("com.test.Person"); // 获取类或接口的所有的成员变量,但不包括继承的成员变量 Field[] f1s = person.getDeclaredFields(); for (Field field : f1s) { System.out.println(field); } // 获取类或接口的所有的公共成员变量,包括继承的成员变量 Field[] f2s = person.getFields(); for (Field field : f2s) { System.out.println(field); } } }

public int com.test.Person.x
public java.lang.String com.test.Person.y
int com.test.Person.z

public int com.test.Person.x
public java.lang.String com.test.Person.y
public float com.test.Human.f

1.7.2 获取成员变量

public class Test { public static void main(String[] args) throws Exception { Class person = Class.forName("com.test.Person"); // 返回一个特定的成员变量,参数为成员变量名 Field f1 = person.getDeclaredField("x"); Field f2 = person.getDeclaredField("y"); Field f3 = person.getDeclaredField("z"); System.out.println(f1); System.out.println(f2); System.out.println(f3); // 返回一个特定的公共的成员变量,参数为成员变量名,包括继承的成员变量 Field f4 = person.getField("x"); Field f5 = person.getField("y"); Field f6 = person.getField("f"); System.out.println(f4); System.out.println(f5); System.out.println(f6); } }

public int com.test.Person.x
public java.lang.String com.test.Person.y
int com.test.Person.z

public int com.test.Person.x
public java.lang.String com.test.Person.y
public float com.test.Human.f

1.7.3 使用反射获取类中指定的属性并赋值

Object obj = person.newInstance(); Field x = person.getDeclaredField("x"); Field y = person.getDeclaredField("y"); Field z = person.getDeclaredField("z"); x.set(obj, 999); System.out.println(x.get(obj)); y.set(obj, "fuck"); System.out.println(y.get(obj)); // 从外部打破封装性 z.setAccessible(true); z.set(obj, 18); System.out.println(z.get(obj));

1.8 获取父类或父接口

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

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


__EOF__

本文作者holyholic704
本文链接https://www.cnblogs.com/holyholic/p/13669841.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   holyholic704  阅读(173)  评论(0编辑  收藏  举报
编辑推荐:
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)
点击右上角即可分享
微信分享提示