版权声明:【分享也是一种提高】个人转载请在正文开头明显位置注明出处,未经作者同意禁止企业/组织转载,禁止私自更改原文,禁止用于商业目的。 https://blog.csdn.net/u010887744/article/details/57428913
java反
射在学习工作中时常使用,自己也利用反射做了一些工具(比如《
利用反射打造自定义注解,自动校验或处理数据
》),但一直对反射缺乏较为系统的了解。以下内容是慕课网教程《
反射——Java高级开发必须懂的
》的学习笔记,相关代码见
github工程github.com/zxiaofan/JDK-Study
,该项目主要用于
学习JDK相关源码以及基础知识
。当然,想要更为系统地直接地了解反射,还需要去研究源码,这也是以后必须要做的事情。
1、Class类的使用
1.1、面向对象世界里,万事外物皆对象。
起初,“面向对象”是专指在程序设计中采用
封装、继承、多态等设计方法。
类是对象,类是java.lang.Class类的实例对象。
There is a class named Class:有一个类,其名字是Class, 其实例是每一个对象。
1.2、类的实例对象如何表示
// 私有构造方法,仅java虚拟机能创建Class的实例对象
private Class(ClassLoader
loader) {
classLoader =
loader;
}
任何一个类都是Class的实例对象,这个实例对象有3种表示方式:
public class Class_Basic1 {
@SuppressWarnings("rawtypes")
public static void main(String[] args) {
// Fruit实例对象表示方式
Fruit fruit = new Fruit();
// 任何一个类都是Class的实例对象(成为该类的类类型),这个实例对象有3种表示方式:
// ① 任何一个类都有一个隐含的静态成员变量
Class c1 = Fruit.class;
// ② 类对象的getClass方法
Class c2 = fruit.getClass();
// c1/c2:Fruit类的类类型(class type)
System.out.println(c1 == c2); // true
// ③ forName
Class c3 = null;
try {
c3 = Class.forName("java1.lang.reflect.Fruit");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
System.out.println(c2 == c3); // true
// 通过类的类类型(c1、c2、c3)创建类的创建类的对象实例。
try {
Fruit fruit2 = (Fruit) c1.newInstance(); // 需要有无参的构造方法
fruit2.getColour();
} catch (Exception e) {
e.printStackTrace();
}
}
}
class Fruit {
void getColour() {
System.out.println("Hi colour");
}
}
2、java动态加载类
Class.forName("类全称"):表示了类的类类型;代表了动态加载类。
静态加载类:编译时加载的类
如:new创建对象
动态加载类:运行时加载的类
假设有一个方法
getColour(String className){
Class c=Class.forName("指定水果");
IFruit
fruit2 = (IFruit)
c1.newInstance();
fruit2.getColour();
}
如果我们想获得不同水果的颜色(或者说根据不同的类调用不同的执行方法getColour),只需传入指定水果类(需实现IFruit接口,重写getColour方法)的类全称即可。
3、java获取方法信息
/**
* 通过反射获取Class相关信息.
*
* 获取类信息,需先获取类的类类型 Class c = obj.getClass();
*
* @author xiaofan
*/
public class ClassInfoUtil extends TestCase {
/**
* 测试打印方法信息.
*
*/
public void testPrintClassMethodInfo() {
Class c1 = int.class;
Class c2 = String.class;
Class c3 = void.class;
System.out.println(c1.getName()); // 类的全称(int)
System.out.println(c2.getName()); // java.lang.String
System.out.println(c2.getSimpleName()); // 不包含包名的类名称(String)
System.out.println(c3.getName()); // void
// 基本数据类型、void关键字,都存在类类型
String str = "zxiaofan.com";
System.out.println("=====打印 " + str.getClass().getName() + " 的类信息=====");
printClassMethodInfo(str);
}
/**
* 测试打印成员变量信息.
*
*/
public void testFieldInfo() {
printFieldInfo(new Integer("1"));
}
/**
* 测试打印构造函数信息.
*
*/
public void testConstructorInfo() {
printConstructorInfo(new Integer("1"));
}
/**
* 打印任意类的信息(类的成员函数、成员变量).
*
*/
@SuppressWarnings("rawtypes")
public static void printClassMethodInfo(Object obj) {
// 获取类信息,需先获取类的类类型
Class c = obj.getClass();
// 获取类名称
System.out.println("类全称是:" + c.getName());
// Method类,方法对象,一个成员对象就是一个Method对象;
// getMethods()获取所有public的函数,包括父类继承而来的
// c.getDeclaredMethods()获取所有该类自己声明的方法(所有访问类型)
Method[] ms = c.getMethods(); // c.getDeclaredMethods()
System.out.println("类方法如下:");
for (int i = 0; i < ms.length; i++) {
Method method = ms[i];
// 方法返回值类型的类类型
Class returnType = method.getReturnType();
System.out.print("__" + returnType.getName() + " ");
// 方法名称
System.out.print(method.getName() + "(");
// 获取参数类型(参数列表的类型的类类型)
Class[] paramTypes = method.getParameterTypes();
for (Class class1 : paramTypes) {
System.out.print(class1.getName() + ",");
}
System.out.println(")");
}
}
/**
* 打印任意类的成员变量信息.
*
* 成员变量也是对象,java.lang.Field类封装了成员变量相关操作。
*
* getFields():所有public的成员变量;
*
* getDeclaredFields():该类自己声明的(即所有)成员变量信息。
*
*/
@SuppressWarnings("rawtypes")
public static void printFieldInfo(Object obj) {
// 获取类信息,需先获取类的类类型
Class c = obj.getClass();
// Field[] fields=c.getFields();
Field[] fields = c.getDeclaredFields();
for (Field field : fields) {
// 成员变量的类型的类类型
Class fieldType = field.getType();
String typeName = fieldType.getName();
// 成员变量的名称
String fieldName = field.getName();
System.out.println(typeName + " " + fieldName);
}
}
/**
* 打印任意类的构造函数信息.
*
* 构造函数也是对象。java.lang.Constructor封装了构造函数信息
*
* getConstructors():所有public的构造函数
*
* getDeclaredConstructors():所有构造函数
*
* getEnclosingConstructor():类A构造函数定义了内部类InnerA,则通过InnerA的Class对象调用getEnclosingConstructor可获取类A的构造函数(不是构造函数列表)
*
*/
@SuppressWarnings("rawtypes")
public static void printConstructorInfo(Object obj) {
// 获取类信息,需先获取类的类类型
Class c = obj.getClass();
// Constructor[] constructors = c.getConstructors();
// Constructor enclosingConstructor = c.getEnclosingConstructor();
Constructor[] constructors = c.getDeclaredConstructors();
for (Constructor constructor : constructors) {
System.out.print(constructor.getName() + " (");
Class[] paramTypes = constructor.getParameterTypes();
for (Class class1 : paramTypes) {
System.out.print(class1.getName() + ",");
}
System.out.println(")");
}
}
}
4、通过反射调用指定方法
方法名和参数列表唯一决定某个方法(method.invoke(对象,参数列表))
public class ClassMethod extends TestCase {
/**
* 使用Method.invoke调用方法.
*
*/
public void testInvokemethod() {
A a = new A();
Class c = a.getClass(); // 获取类的方法《--类信息《--类类型
// 方法:由名称和参数列表决定
// getMethod获取public方法
//
try {
// Method method = c.getDeclaredMethod("print", new Class[]{int.class, int.class});
Method method_int = c.getDeclaredMethod("print", int.class, int.class); // 等价于new Class[]{int.class, int.class}
// 方法method没有返回值则返回null,否则需要强转
Object invokeReturn = method_int.invoke(a, new Object[]{10, 20}); // 等价于 a.print(10,20);
System.out.println("强转:" + (int) invokeReturn);
System.out.println("==========");
Method method_String = c.getDeclaredMethod("print", String.class, String.class);
method_String.invoke(a, "hi", "zxiaofan"); // 即a.print("hi","zxiaofan")
System.out.println("==========");
// Method method_NoParam = c.getDeclaredMethod("print", new Class[]{});
Method method_NoParam = c.getDeclaredMethod("print"); // 等价于("print", new Class[]{})
method_NoParam.invoke(a, new Object[]{});
method_NoParam.invoke(a); // 等价于invoke(a, new Object[]{})
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 反射与泛型.
*
* 通过反射插入集合的非相同类型的数据,只能直接处理Object或对Object强转到实际类型。
*/
@SuppressWarnings("rawtypes")
public void testGeneric() {
List list0 = new ArrayList<>();
List<String> list = new ArrayList<>();
list.add("hi");
// list.add(123); // 编译报错
Class c0 = list0.getClass();
Class c = list.getClass();
assertEquals(c0, c); // true
// c0==c1说明:编译之后集合的泛型是去泛型化的;java中的泛型是防止错误输入的,只在编译阶段有效,编译后无效
// 通过方法的反射绕过编译
Method method;
try {
method = c.getMethod("add", Object.class);
method.invoke(list, 456); // 绕过编译操作就绕过了泛型
} catch (Exception e) {
e.printStackTrace();
}
for (Object obj : list) { // 这样遍历是可以的
System.out.println(obj);
}
System.out.println("======");
for (String str : list) {
System.out.println(str); // 不能这样遍历,Iterator迭代也不行
// str为int时抛异常:java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
}
}
}
class A {
public void print() { // 无参无返回值
System.out.println("hi zxiaofan.com");
}
public String print(String a, String b) { // 参数String返回String
String c = a + b;
System.out.println(c);
return c;
}
public int print(int a, int b) { // 参数int返回int
int c = a + b;
System.out.println(c);
return c;
}
}
欢迎个人转载,但须在文章页面明显位置给出原文连接;
未经作者同意必须保留此段声明、不得随意修改原文、不得用于商业用途,否则保留追究法律责任的权利。
【 CSDN 】:csdn.zxiaofan.com
【GitHub】:github.zxiaofan.com
如有任何问题,欢迎留言。祝君好运!
Life is all about choices!
将来的你一定会感激现在拼命的自己!