反射(Reflection
)
所谓反射就是在Java
运行期间,程序可以拿到一个对象的所有信息。
反射是为了解决在对一个对象完全未知的情况下调用其方法还产生的。
通过Class实例获取class
信息的方法称为反射(Reflection)。
通过反射读写字段是一种非常规方法,它会破坏对象的封装。
class类
获取class实例
1、通过class
静态变量class
获取
Class cls = String.class;
2、实例变量的getClass()
方法获取
String s = "Hello";
Class cls = s.getClass();
3、Class.forName("class完整类名")
获取
Class cls = Class.forName("java.lang.String");
class实例获取基本信息
public class Main {
public static void main(String[] args) {
printClassInfo("".getClass());
printClassInfo(Runnable.class);
printClassInfo(java.time.Month.class);
printClassInfo(String[].class);
printClassInfo(int.class);
}
static void printClassInfo(Class cls) {
System.out.println("Class name: " + cls.getName());
System.out.println("Simple name: " + cls.getSimpleName());
if (cls.getPackage() != null) {
System.out.println("Package name: " + cls.getPackage().getName());
}
System.out.println("is interface: " + cls.isInterface());
System.out.println("is enum: " + cls.isEnum());
System.out.println("is array: " + cls.isArray());
System.out.println("is primitive: " + cls.isPrimitive());
}
}
创建class实例
1、可通过class
实例创建class
实例(实例-->实例)
// 获取String的Class实例:
Class cls = String.class;
// 创建一个String实例:
String s = (String) cls.newInstance();
通过newInstance
创建实例存在局限性:只能访问public
的无参构造方法,带参构造或者非public
构造方法都无法访问。
2、new创建实例
String s = new String()
3、动态加载
jvm
对于java
程序的执行并非是一次性加载完所有的class
类,而是只有首次使用class
时才会加载。
如下所示:在create
方法调用之前,Person.class
类不会加载
// Main.java
public class Main {
public static void main(String[] args) {
if (args.length > 0) {
create(args[0]);
}
}
static void create(String name) {
Person p = new Person(name);
}
}
访问字段(实例>class>class信息)
对于一个object
实例来说,只要获得它的class
,就能获取它的所有信息
获取class类字段
Field getField(name)
:根据字段名获取某个public
的field
(包括父类)Field getDeclaredField(name)
:根据字段名获取当前类的某个field
(不包括父类)Field[] getFields()
:获取所有public
的field
(包括父类)Field[] getDeclaredFields()
:获取当前类的所有field
(不包括父类)
注:Declared
不包含父类,即要获取父类的字段信息需要使用不含Declared
相关方法,而Declared
可以来获取非public
的字段信息,故无法获取父类中的非public
字段信息
public class Main {
public static void main(String[] args) throws Exception {
Class stdClass = Student.class;
// 获取public字段"score":
System.out.println(stdClass.getField("score")); //public int Student.score
// 获取继承的public字段"name":
System.out.println(stdClass.getField("name")); //public java.lang.String Person.name
// 获取private字段"grade":
System.out.println(stdClass.getDeclaredField("grade")); //private int Student.grade
}
}
class Student extends Person {
public int score;
private int grade;
}
class Person {
public String name;
}
Filed对象相关方法
-
get(Object obj)
:返回指定对象obj上此field表示的字段值,例如,"zhangsan"
;1、字段不是静态字段的话,要传入反射类的对象
Field field=A.class.getDeclaredField("fild"); // a表示A class上fild字段的字段值 int a= (Integer)field.get(new A());
2、字段是静态字段的话,传入任何对象都是可以的,包括null
Field static field=A.class.getDeclaredField("staticFild"); // staticFild字段是静态字段 static private staticFild int b= (Integer)staticfield.get("") ; int d= (Integer)staticfield.get(null) ;
-
set(Object obj, Object value)
:将指定对象变量上此field 对象表示的字段设置为指定的新值,第一个Object
参数是指定的实例,第二个Object
参数是待修改的值。 -
getName()
:返回字段名称,例如,"name"
; -
getType()
:返回字段类型,也是一个Class
实例,例如,String.class
; -
getModifiers()
:返回字段的修饰符,它是一个int
,不同的bit表示不同的含义。
public final class String {
private final byte[] value;
}
Field f = String.class.getDeclaredField("value");
f.getName(); // "value"
f.getType(); // class [B 表示byte[]类型
int m = f.getModifiers();
Modifier.isFinal(m); // true
Modifier.isPublic(m); // false
Modifier.isProtected(m); // false
Modifier.isPrivate(m); // true
Modifier.isStatic(m); // false
获取字段值
// reflection
// Filed描述的是类的属性信息,功能:获取当前对象的成员变量的类型,对成员变量重新设置值
import java.lang.reflect.Field;
public class Main {
public static void main(String[] args) throws Exception {
// new 一个person实例
Object p = new Person("Xiao Ming");
// getClass() 获取p的class实例
Class c = p.getClass();
// 设置为true允许访问private变量
Field.setAccessible(true)
// 根据class实例获取name的Filed实例,getDeclaredField获取指定字段Field
Field f = c.getDeclaredField("name");
// 获取
Object value = f.get(p);
System.out.println(value); // "Xiao Ming"
}
}
class Person {
private String name;
public Person(String name) {
this.name = name;
}
}
取值流程:获取Class
实例—>获取filed实例—>Field.get(Object)
获取实例指定字段的值。
为了保证JVM
核心库的安全,setAccessible(true)
在jvm
运行期存在SecurityManager
,可能会被检查规则检查导致不生效。
设置字段值
方法:Field.set(Object, Object)
,其中第一个Object
参数是class
实例,第二个Object
是需要更改的值
// reflection
import java.lang.reflect.Field;
public class Main {
public static void main(String[] args) throws Exception {
Person p = new Person("new a");
System.out.println(p.getName()); // "new a"
Class c = p.getClass();
Field f = c.getDeclaredField("name");
// 修改非public的值时需要调用setAccessible(true)
// 如下,name是private变量的值,需要调用setAccessible,在使用set赋值
f.setAccessible(true);
f.set(p, "new a");
System.out.println(p.getName()); // "new a"
}
}
class Person {
private String name;
public Person(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
}
调用方法
访问字段是通过class实例获取获取所有Field
对象,调用方法就是获取所有Method
信息。
使用反射调用方法时,仍然遵循多态原则:即总是调用实际类型的覆写方法(如果存在)。
有如下几种方式:
Method getMethod(name, Class...)
:获取某个public
的Method
(包括父类)Method getDeclaredMethod(name, Class...)
:获取当前类的某个Method
(不包括父类)Method[] getMethods()
:获取所有public
的Method
(包括父类)Method[] getDeclaredMethods()
:获取当前类的所有Method
(不包括父类)
一个Method
对象包含一个方法的所有信息:
getName()
:返回方法名称,例如:"getScore"
;getReturnType()
:返回方法返回值类型,也是一个Class实例,例如:String.class
;getParameterTypes()
:返回方法的参数类型,是一个Class数组,例如:{String.class, int.class}
;getModifiers()
:返回方法的修饰符,它是一个int
,不同的bit表示不同的含义。
public class Main {
public static void main(String[] args) throws Exception {
// String对象:
String s = "Hello world";
// 获取String substring(int)方法,参数为int:
Method m = String.class.getMethod("substring", int.class);
// 在s对象上调用该方法并获取结果
// 调用invake相当于调用方法substring,第一个参数表示对象实例,第二个参数是是方法substring的参数
// 调用静态方法无需第一个参数,第一个参数永远为null,即invoke(null, "12345")
String r = (String) m.invoke(s, 6);
// 打印调用结果:
System.out.println(r);
<----调用非public方法---->
// 1、非public方法使用getDeclaredMethod获取方法实例
// 2、Method.setAccessible(true)允许其调用
Method m = p.getClass().getDeclaredMethod("setName", String.class);
m.setAccessible(true);
m.invoke(p, "Bob");
}
}
调用构造方法
newInstance()
// newInstance()只能调用该类无参构造方法,对于有参构造和非public构造不能通过newInstance访问
Person p = Person.class.newInstance();
Constructor对象
Constructor
是当前定义类的构造函数,与父类无关,不存在多态问题
getConstructor(Class...)
:获取某个public
的Constructor
;getDeclaredConstructor(Class...)
:获取某个Constructor
;getConstructors()
:获取所有public
的Constructor
;getDeclaredConstructors()
:获取所有Constructor
调用非public
的Constructor
时,必须首先通过setAccessible(true)
设置允许访问,setAccessible(true)
可能失败
import java.lang.reflect.Constructor;
public class Main {
public static void main(String[] args) throws Exception {
// 获取构造方法Integer(int):
Constructor cons1 = Integer.class.getConstructor(int.class);
// 调用构造方法:
Integer n1 = (Integer) cons1.newInstance(123);
System.out.println(n1);
// 获取构造方法Integer(String)
Constructor cons2 = Integer.class.getConstructor(String.class);
Integer n2 = (Integer) cons2.newInstance("456");
System.out.println(n2);
}
}
获取继承关系
Class getSuperclass()
:获取父类类型Class[] getInterfaces()
:获取当前类实现的所有接口- 通过
Class
对象的isAssignableFrom()
方法可以判断一个向上转型是否可以实现
public class Main {
public static void main(String[] args) throws Exception {
Class i = Integer.class;
Class n = i.getSuperclass();
System.out.println(n);
Class o = n.getSuperclass();
System.out.println(o);
System.out.println(o.getSuperclass());
}
}
Integer
的父类类型是Number
,Number
的父类是Object
,Object
的父类是null
。除Object
外,其他任何非interface
的Class
都必定存在一个父类类型。
动态代理
Java标准库提供了一种动态代理(Dynamic Proxy)的机制:可以在运行期动态创建某个interface
的实例
静态代理创建
// 定义接口
public interface Hello {
void morning(String name);
}
// 实现类
public class HelloWorld implements Hello {
public void morning(String name) {
System.out.println("Good morning, " + name);
}
}
// 创建实例,转型为接口并调用
Hello hello = new HelloWorld();
hello.morning("Bob");
动态代理
拿上面的例子举例:先定义Hello接口,中间跳过实现类的编写,通过调用Proxy.newProxyInstance()
创建接口对象,这种没有实现类但是在运行期动 态创建了一个接口对象的方式,我们称为动态代码
JDK提供的动态创建接口对象的方式,就叫动态代理
动态代理在spring等框架中大量使用
运行期动态创建一个interface
实例的方法:
- 定义一个
InvocationHandler
实例,它负责实现接口的方法调用; - 通过
Proxy.newProxyInstance()
创建interface
实例,它需要3个参数:- 使用的
ClassLoader
,通常就是接口类的ClassLoader
; - 需要实现的接口数组,至少需要传入一个接口进去;
- 用来处理接口方法调用的
InvocationHandler
实例。
- 使用的
- 将返回的
Object
强制转型为接口。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class Main {
public static void main(String[] args) {
InvocationHandler handler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(method);
if (method.getName().equals("morning")) {
System.out.println("Good morning, " + args[0]);
}
return null;
}
};
Hello hello = (Hello) Proxy.newProxyInstance(
Hello.class.getClassLoader(), // 传入ClassLoader
new Class[] { Hello.class }, // 传入要实现的接口
handler); // 传入处理调用方法的InvocationHandler
hello.morning("Bob");
}
}
interface Hello {
void morning(String name);
}
标签:反射,Class,String,class,name,获取,public,Reflection
From: https://www.cnblogs.com/yangguanglei/p/16843969.html