Java 之反射
反射
概念:通过Classs实例获取class信息的方法称为反射
一、通过反射获取 class 的四种方法
/**
* 通过反射获取class实例的方法有四种:
*/
public static void f() {
// 方法一:直接通过一个class的静态变量class获取:
// 1. 知道具体类的情况下可以使用:
Class cls = String.class;
// 方法二:如果我们有一个实例变量,可以通过该实例变量提供getClass()方法获取:
String s = "hello";
Class clss = s.getClass();
// 方法三: 如果知道一个class 的完整类型,可以通过静态方法class.forName()获取
try {
Class cls1 = Class.forName("java.lang.String");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
// 方式四:通过类加载器直接获取
Object tmp = ClassLoader.getSystemClassLoader().loadClass("com.liaoxuefeng.eReflection.ReflectionDemo");
System.out.println("方式四: " + tmp);
}
/**
* class 中常用的方法有:
*
* @param cls
*/
public static void printClassInfo(Class cls) {
System.out.println("cls.getName() = " + cls.getName());
System.out.println("cls.getSimpleName() = " + cls.getSimpleName());
if (cls.getPackage() != null) {
System.out.println("cls.getPackage().getName() = " + cls.getPackage().getName());
}
System.out.println("cls.isInterface() = " + cls.isInterface());
System.out.println("cls.isEnum() = " + cls.isEnum());
System.out.println("cls.isArray() = " + cls.isArray());
System.out.println("cls.isPrimitive() = " + cls.isPrimitive());
}
public static void main(String[] args) {
printClassInfo("".getClass());
printClassInfo(Runnable.class);
printClassInfo(java.time.Month.class);
printClassInfo(String[].class);
printClassInfo(int.class);
}
二、反射中访问字段
/**
* CLass 类提供了以下几种方法来获取字段:
* 1、Field getField(name):根据字段名获取某个public的field(包括父类)
* 2、Field getDeclaredField(name):根据字段名获取当前类的某个field(不包括父类)
* 3、Field getFields(): 获取所有public的field(包括父类)
* 4、Field[] getDeclaredFields():获取当签类的所有field
*/
public static void getFields() throws NoSuchFieldException {
Class stdClass = Student.class;
// 获取public 字段 "score"
System.out.println("stdClass.getField(\"score\") = " + stdClass.getField("score"));
// 获取student 中的"grade"
System.out.println("stdClass.getDeclaredField(\"grade\") = " + stdClass.getDeclaredField("grade")); // class [B 表示byte[]类型
System.out.println("stdClass.getFields() = " + Arrays.toString(stdClass.getFields()));
System.out.println("stdClass.getDeclaredFields() = " + Arrays.toString(stdClass.getDeclaredFields()));
}
/**
* 一个Field对象包含一个字段的所有信息:
* 1、getName():返回字段名称,例如,"name"
* 2、getType():返回字段类型,也是一个Class实例,例如:String.class;
* 3、getModifiers():返回字段的修饰符,它是一个int,不同的bit表示不同的含义
*/
public static void fieldClass() throws NoSuchFieldException {
Field field = String.class.getDeclaredField("value");
System.out.println("field.getName() = " + field.getName());
System.out.println("field.getType() " + field.getType());
int m = field.getModifiers();
System.out.println("m = " + m);
System.out.println("Modifier.isFinal(m) = " + Modifier.isFinal(m));
System.out.println("Modifier.isPublic(m) = " + Modifier.isPublic(m));
System.out.println("Modifier.isProtected(m) = " + Modifier.isProtected(m));
System.out.println("Modifier.isPrivate(m) = " + Modifier.isPrivate(m));
System.out.println("Modifier.isStatic(m) = " + Modifier.isStatic(m));
}
/**
* 获取具体值
* 通过Field对象的get(Object)方法可以获取字段的当前值:
*/
public static void getFieldValue() throws NoSuchFieldException, IllegalAccessException {
Person1 p = new Person1("xiaoming");
Class c = p.getClass();
Field field = c.getDeclaredField("name");
System.out.println("field = " + field);
field.setAccessible(true); // Field.setAccessible(true)的意思是,别管这个字段是不是public,一律允许访问。
Object value = field.get(p); // 根据实例获取字段值
System.out.println("value = " + value);
}
/**
* 设置字段值 <br/>
* 通过Field实例既然可以获取到指定实例的字段值,自然也可以设置字段的值。<br/>
* 设置字段值是通过Field.set(Object, Object)实现的,其中第一个Object参数是指定的实例,第二个Object参数是待修改的值。示例代码如下
*
* @param args
* @throws NoSuchFieldException
* @throws IllegalAccessException
*/
public static void setFieldValue() throws NoSuchFieldException, IllegalAccessException {
Person3 person3 = new Person3("xiao ming");
System.out.println("person3.getName() = " + person3.getName());
Class c = person3.getClass();
Field field = c.getDeclaredField("name");
field.setAccessible(true);
field.set(person3, "xiao bai");
System.out.println("Person3 = " + person3.getName());
}
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
getFields();
fieldClass();
getFieldValue();
setFieldValue();
}
三、获取反射的方法
/**
* 获取Method的几种方法:
* Class类提供以下几种方法获取Method:
* 1、Method getMethod(name,Class ,,,): 获取某个public的method(包括父类)
* 2、Method getMethods() : 获取所有public 的Menthod(包括父类)
* 2、Method getDeclareMethods() : 获取当前类的所有Menthod,包含括private方法 (不包括父类)
* 3、Method getDeclaredMethod(name.Class ...):获取当前类的某个method,包含括private方法(不包括父类)
**/
public static void getMethodWays() throws NoSuchMethodException {
Class stdClass = Student4.class;
// 获取private方法getScore,参数为String
System.out.println("stdClass.getMethod(\"getScore\", String.class) = " + stdClass.getMethod("getScore", String.class));
// 获取继承的public getName 方法,无参数
System.out.println("stdClass.getMethod(\"getName\") = " + stdClass.getMethod("getName"));
// 获取private方法getGrade,参数为int:
System.out.println("stdClass.getDeclaredMethod(\"getGrade\", int.class) = " + stdClass.getDeclaredMethod("getGrade", int.class));
// 获取所有public 的Menthod(包括父类)
System.out.println("stdClass.getMethods() = " + Arrays.toString(stdClass.getMethods()));
// 获取当前类中所有方法,包括private方法
System.out.println("stdClass.getDeclaredMethods() = " + Arrays.toString(stdClass.getDeclaredMethods()));
}
/**
* 反射 Methond 里面常用的一些方法
* * 一个Menthod 对象可以包含一个方法的所有信息:
* * 1、getName() 返回方法,例如:"getScore"
* * 2、getReturnType(): 返回方法返回值类型,也是一个Class类型,例如:String.class
* * 3、getParameterType():返回方法的参数类型,是一个Class数据,例如:{String.class,int,class}
* * 4、getModifiers():返回方法的修饰符,它是一个int,不同的bit有不同的含义
*
* @throws NoSuchMethodException
*/
public static void methodClass() throws NoSuchMethodException {
// String对象:
String s = "Hello world";
// 获取String substring(int)方法,参数为int:
Method m = String.class.getMethod("substring", int.class);
System.out.println("m.getName() = " + m.getName());
System.out.println("m.getReturnType() = " + m.getReturnType());
System.out.println("m.getParameterTypes() = " + Arrays.toString(m.getParameterTypes()));
System.out.println("m.getModifiers() = " + m.getModifiers());
}
/**
* 使用反射调用方法的实际应用
* 核心就是invoke() 方法调用
* invoke 需要传入一个实例对象,对Method实例调用invoke就相当于调用该方法,invoke的第一个参数是对象实例,即在哪个实例上调用该方法,后面的可变参数要与方法参数一致,否则将报错。
*/
public static void methodsScenarios() throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
// 原来实现逻辑
String s = "Hello world";
String r = s.substring(6); // "world"
System.out.println("r = " + r);
// 使用反射实现逻辑
// 方法一:反射传一个参数
Method m = String.class.getMethod("substring", int.class);
// 在s对象上调用该方法并获取结果:
// invoke 需要传入一个实例对象
// 对Method实例调用invoke就相当于调用该方法,invoke的第一个参数是对象实例,即在哪个实例上调用该方法,后面的可变参数要与方法参数一致,否则将报错。
String r1 = (String) m.invoke(s, 6);
System.out.println("反射调用一个参数:" + r1);
// 方法二:反射传多个参数
// 获取String substring(int,int)方法,参数为int,int:
Object o1 = String.class.getMethod("substring", int.class, int.class).invoke(s, 0, 6);
System.out.println("反射调用多个参数:" + o1);
// 调用public方法
// 如果获取的方法是静态方法,则invoke方法传入的第一个参数是null,后面的可变参数要与方法参数一致
Method method = Integer.class.getMethod("parseInt", String.class);
Object o = method.invoke(null, "123");
System.out.println("o = " + o);
// 调用非public方法
// 先使用 Class.getDeclaredMethod()获取该方法实例,然后,用Method.setAccessible(true)允许其调用
Student4 s4 = new Student4();
Method m1 = s4.getClass().getDeclaredMethod("getGrade", int.class);
m1.setAccessible(true);
m1.invoke(s4, 1);
}
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
getMethodWays();
methodClass();
methodsScenarios();
}
四、反射调用构造方法
public class TestConstructionMethod {
public static void f1() throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
// 正常创建实例
Person p = new Person();
// 反射创建实例
Person person = Person.class.newInstance();
}
/**
* 使用Constuctor对象,调用,返回实例
* <p>
* 通过Class实例获取Constructor的方法如下:
* 1、getConstructor(Class...):获取某个public的Constructor;
* 2、getDeclaredConstructor(Class...):获取某个Constructor;
* 3、getConstructors():获取所有public的Constructor;
* 4、getDeclaredConstructors():获取所有Constructor。
* 注意Constructor总是当前类定义的构造方法,和父类无关,因此不存在多态的问题。
* <p>
* <p>
* Constructor对象封装了构造方法的所有信息;
* <p>
* 通过Class实例的方法可以获取Constructor实例:getConstructor(),getConstructors(),getDeclaredConstructor(),getDeclaredConstructors();
* <p>
* 通过Constructor实例可以创建一个实例对象:newInstance(Object... parameters); 通过设置setAccessible(true)来访问非public构造方法。
* <p>
* 调用非public的Constructor时,必须首先通过setAccessible(true)设置允许访问。setAccessible(true)可能会失败
*/
public static void f2() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
// 获取构造方法 Interger(int)
Constructor<Integer> constructor = Integer.class.getConstructor(int.class);
// 调用构造方法
Integer i = constructor.newInstance(123);
System.out.println("i = " + i);
}
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
f1();
f2();
}
}
五、反射获取继承关系
public class TestExtends {
/**
* 使用反射获取实class的三种方式
*/
public static void f1() throws ClassNotFoundException {
// 方式一: 使用.class
Class<String> stringClass = String.class;
// 方式二: 使用getCLass()
String s = "Hello";
Class cls = s.getClass();
// 方式三:class.forName(""),传入完整的Class 类名
Class<?> aClass = Class.forName("java.lang.String");
}
/**
* 获取父类类型
* 1、使用getSuperclass()方法
* 2、getInterfaces()只返回当前类直接实现的接口类型,并不包括其父类实现的接口类型
* 过Class对象可以获取继承关系:
* <p>
* Class getSuperclass():获取父类类型;
* Class[] getInterfaces():获取当前类实现的所有接口。
* 通过Class对象的isAssignableFrom()方法可以判断一个向上转型是否可以实现。
*/
// 获取父类
public static void f2() {
Class<Integer> i = Integer.class;
Class<? super Integer> s = i.getSuperclass();
System.out.println("s = " + s);
Class<? super Integer> s1 = s.getSuperclass();
System.out.println("s1 = " + s1);
System.out.println("s1.getSuperclass() = " + s1.getSuperclass());
}
// 获取Interface
// getInterfaces()只返回当前类直接实现的接口类型,并不包括其父类实现的接口类型:
// Class[] getInterfaces():获取当前类实现的所有接口。
public static void f3() {
Class<Integer> s = Integer.class;
Class<?>[] interfaces = s.getInterfaces();
for (Class<?> anInterface : interfaces) {
System.out.println("anInterface = " + anInterface);
}
System.out.println(java.io.DataInputStream.class.getSuperclass());// java.io.FilterInputStream,因为DataInputStream继承自FilterInputStream
System.out.println(java.io.Closeable.class.getSuperclass()); // null,对接口调用getSuperclass()总是返回null,获取接口的父接口要用getInterfaces()
System.out.println(java.io.Closeable.class.getInterfaces()); // 返回继承的接口
}
// 继承关系
// 通过Class对象的isAssignableFrom()方法可以判断一个向上转型是否可以实现。
public static void f4() {
Class n = Integer.class;
/* boolean isDouble = n instanceof Double; // false
boolean isInteger = n instanceof Integer; // true
boolean isNumber = n instanceof Number; // true
boolean isSerializable = n instanceof java.io.Serializable; // true*/
// 如果是两个Class实例,要判断一个向上转型是否成立,可以调用isAssignableFrom():
// Integer i = ?
Integer.class.isAssignableFrom(Integer.class); // true,因为Integer可以赋值给Integer
// Number n = ?
Number.class.isAssignableFrom(Integer.class); // true,因为Integer可以赋值给Number
// Object o = ?
Object.class.isAssignableFrom(Integer.class); // true,因为Integer可以赋值给Object
// Integer i = ?
Integer.class.isAssignableFrom(Number.class); // false,因为Number不能赋值给Integer
}
public static void main(String[] args) throws ClassNotFoundException {
f1();
f2();
f3();
f4();
}
}
本文来自博客园,作者:快乐小王子帅气哥哥,转载请注明原文链接:https://www.cnblogs.com/alex-oos/p/18373963