Java中的反射机制(Reflect)
来源:互联网 发布:windows密钥 编辑:程序博客网 时间:2024/05/20 22:26
一、Class类的使用
1、在面向对象的世界里,万事万物皆对象。
不过在java语言中,有两类事物不是面向对象的。它们分别是 java 中的基本数据类型(例如:int a = 5)和 java 中的静态成员(包括静态成员变量、静态成员方法)。其中,虽然普通数据类型不是对象,但是与之对应的包装类却是面向对象的,例如:int - Integer,boolean - Boolean 等。
2、类是谁的对象呢?
类是对象,类是 java.lang.Class 类的实例对象。(There is a class named Class)
假如有一个类 Person,那么可以用 new Person()来表示 Person 类的一个实例对象。
其实在 Java 中,Person 类本身也是一个类的实例对象,这个类就是 Class (java.lang.Class)。
也就是说,在 Java 中任何一个类都是 Class 的实例对象,只不过这些实例对象只能由 JVM 创建,可以理解为就是编译之后产生的一个个 .class 文件。
java.lang.Class的部分源码如下:
public final class Class<T> implements java.io.Serializable, java.lang.reflect.GenericDeclaration, java.lang.reflect.Type, java.lang.reflect.AnnotatedElement { ··· /* * Constructor. Only the Java Virtual Machine creates Class * 私有的构造函数,只有Java虚拟机可以创建Class * objects. */ private Class() {} ···}
Person 的实例对象可以用 new Person( )来表示,那么 Class 的实例对象应该如何表示呢?
以 Person 类为例,表示 Class 的实例对象一般有以下三种方式:
a、类名.class
Class c1 = Person.class;
实际在告诉我们任何一个类都有一个隐含的静态成员变量 class
b、该类的已知对象.getClass()
Person p = new Person();Class c2 = p.getClass();
小结:
这里 p 代表 Person 类的一个实例对象, c1 、c2 则分别代表 Class 类的实例对象。
官网描述为:c1、c2 表示了 Person 类的类类型(class type)。
万事万物皆对象,类也是对象,是Class类的实例对象,这个对象我们称为该类的类类型。
c1、c2都代表了 Person 类的类类型,c1 == c2 为true说明一个类只可能是 Class 类的一个实例对象。
c、Class.forName(“类的完整包名路径”)
try { Class<?> c3 = Class.forName("com.example.Person"); System.out.println(c3);} catch (ClassNotFoundException e) { e.printStackTrace();}
我们完全可以通过类的类类型来创建该类的对象实例。
try { Person person = c1.newInstance(); // 【注意】需要有无参数的构造方法 person.print();} catch (Exception e) { e.printStackTrace();}
完整的代码:
public class MainClass { public static void main(String[] args) { // Person是一个类,它的实例对象如何表示? Person p = new Person(); // p 就代表了Person类的一个实例对象 // 其实,Person类也是一个类的实例对象,这个类就是 Class (java.lang.Class) // 在Java中,任何一个类都是Class的实例对象,只不过这些实例对象只能由JVM创建,也就是编译后产生的一个个 .class 文件 // 那么Person作为Class的一个实例对象,应该如何来表示呢? // 第一种方式 Class<? extends Person> c1 = p.getClass(); System.out.println(c1); System.out.println(c1.getSuperclass()); // 这里印证了Class的父类其实还是Object // 第二种方式 Class<? extends Person> c2 = Person.class; // 任何一个类都有一个隐含的静态成员变量class System.out.println(c2); System.out.println(c1 == c2); // 第三种方式 try { Class<?> c3 = Class.forName("com.example.Person"); System.out.println(c3); } catch (ClassNotFoundException e) { e.printStackTrace(); } // 通过类的类类型来创建该类的对象实例 try { Person person = c1.newInstance(); // 【注意】需要有无参数的构造方法 person.print(); } catch (Exception e) { e.printStackTrace(); } }}class Person { void print() { System.out.print("person ----------------"); }}
二、基本数据类型
java中的基本数据类型(boolean、byte、char、short、int、long、float、double)以及void关键字等都存在类类型。
public class TypeClass { public static void main(String[] args) { Class c1 = int.class; Class c2 = Integer.class; Class c3 = double.class; Class c4 = Double.class; Class c5 = void.class; Class c6 = Void.class; System.out.println("int.class = " + c1.getName()); System.out.println("Integer.class = " + c2.getName()); System.out.println("double.class = " + c3.getName()); System.out.println("Double.class = " + c4.getName()); System.out.println("void.class = " + c5.getName()); System.out.println("Void.class = " + c6.getName()); }}
执行后的打印日志:
int.class = intInteger.class = java.lang.Integerdouble.class = doubleDouble.class = java.lang.Doublevoid.class = voidVoid.class = java.lang.Void
getName( )
获取类类型的名称
getSimpleName( )
获取不带包名的类类型名称
Class c = Integer.class;System.out.println("c.getName() = " + c.getName()); // c.getName() = java.lang.IntegerSystem.out.println("c.getSimpleName() = " + c.getSimpleName()); // c.getSimpleName() = Integer
三、动态加载类
Class.forName(“类的完整包名路径”)
不仅表示了类的类类型,还代表了动态加载类。
编译时刻加载类是静态加载类,运行时刻加载类是动态加载类。
静态加载示例:
public class Office { public static void main(String[] args) { start("Word"); } /** * new 创建对象是静态加载类,在编译时刻就需要加载所有可能使用到的类。 */ private static void start(String arg) { if ("Word".equals(arg)) { Word w = new Word(); w.start(); } if ("Excel".equals(arg)) { Excel e = new Excel(); e.start(); } }}
上边示例代码其实只使用到了 Word 类,但是因为是静态加载,在编译时期就需要加载所有可能使用到的类。所以,当只提供了 Word 类的情况下,因为没有 Excel 类,在编译时期就会检测到 Excel 类不存在的异常,导致程序其实根本用不到 Excel 类的情况下,也会因为编译时期报错而导致异常。
动态加载示例:
public class OfficeBetter { public static void main(String[] args) { run("Word"); run("Excel"); } /** * 根据文件类型动态加载对应的类,并执行其中的 start() 方法 * @param name 代表文件的类型 */ private static void run(String name) { try { Class<?> c1 = Class.forName("com.example.office." + name); // 这里因为动态加载,无法确定加载的类是Word还是Excel,所以抽出一个接口类 OfficeAble // 让 Word 和 Excel 都实现 OfficeAble 并实现 start() 方法即可 OfficeAble o = (OfficeAble) c1.newInstance(); o.start(); } catch (Exception e) { e.printStackTrace(); } }}
这里即使我们只使用Word类,也不会出现静态加载所产生的问题。同时如果后期需要添加新的功能,比如加入一个 PPT 的类并要执行一定的逻辑,这时不需要改动 OfficeAble 中的代码逻辑,只需要创建出 PPT 类并实现 OfficeAble 接口,将相应的逻辑功能在 start( )方法中实现即可。这就是动态加载相对的一些优势。
四、获取方法信息
方法也是对象,是 java.lang.reflect.Method 的实例对象, Method 类封装了关于类方法的操作。
先通过上面三种获取类类型的任意一种方式获取到类类型,例如:
String s = "abc";Class c = s.getClass();
Class.getMethods()
Method[] methods = c.getMethods(); // 【注意】获取的是所有public的方法,包括从父类继承而来的
Class.getDeclaredMethods()
Method[] methods = c.getDeclaredMethods(); // 【注意】获取的是所有该类自己声明的方法,不问访问权限(不包含父类继承来的方法)
Method常用方法
method.getReturnType().getSimpleName(); // getReturnType() 得到方法返回值类型的类类型method.getName(); // getName() 得到方法的名称Class<?>[] paramTypes = method.getParameterTypes(); // getParameterTypes() 得到方法参数列表类型的类类型
五、获取成员变量信息
成员变量也是对象,是 java.lang.reflect.Field 的实例对象, Field 类封装了关于成员变量的操作。
先通过上面三种获取类类型的任意一种方式获取到类类型,例如:
String s = "abc";Class c = s.getClass();
Class.getFields()
Field[] fields = c.getFields(); // 【注意】获取的是所有public的成员变量信息,包括从父类继承而来的
Class.getDeclaredFields()
Field[] fields = c.getDeclaredFields(); // 【注意】获取的是所有自己声明的成员变量信息
Field常用方法
Field[] fields = c.getFields();for (int i = 0; i < fields.length; i++) { // 可以直接打印成员变量的信息 // System.out.println(fields[i].toString()); Class<?> type = fields[i].getType(); // getType() 获得成员变量类型的类类型 System.out.println(type.getSimpleName() + " " + fields[i].getName());}
六、获取构造函数信息
构造函数也是对象,是 java.lang.reflect.Constructor 的实例对象, Constructor 类封装了关于构造函数的操作。
先通过上面三种获取类类型的任意一种方式获取到类类型,例如:
String s = "abc";Class c = s.getClass();
Class.getConstructors()
Constructor[] constructors = c.getConstructors(); // 【注意】获取的是所有public的构造函数
Class.getDeclaredConstructors()
Constructor[] declaredConstructors = c.getDeclaredConstructors(); // 【注意】获取所有的构造函数(不问访问权限,构造函数都是自己声明的)
Constructor常用方法
Constructor[] declaredConstructors = c.getDeclaredConstructors();for (int i = 0; i < constructors.length; i++) { Constructor constructor = constructors[i]; String name = constructor.getName(); // getName() 获取构造函数名称 Class[] paramTypes = constructor.getParameterTypes(); // getParameterTypes() 获取构造函数参数列表类型的类类型}
七、方法反射的基本操作
八、通过反射了解泛型
本人能力有限,如果此博文中有哪里讲得让人难以理解,欢迎留言交流,若有讲解错的地方欢迎指出,大家互相学期,共同进步!
- Java中的反射机制(Reflect)
- Java反射机制(Reflect)
- java 反射机制 reflect
- java反射机制reflect
- Java反射机制(reflect)
- Reflect Java反射机制
- Java-反射机制reflect
- java reflect:反射机制
- Java 反射机制- reflect
- Java反射机制(reflect)
- JAVA的反射机制(reflect)
- Java反射(Reflect)的机制
- 反射机制(Reflect)
- JAVA 反射机制实现 reflect
- Java之reflect 反射机制
- 反射机制:java.lang.reflect
- [Reflect]Java反射机制详解
- Java Reflect(反射)机制详解
- BZOJ 1067: [SCOI2007]降雨量
- PHP语法基础(与Java、C++等作比较)
- hadoop1和hadoop2的比较
- SpringMVC上传文件配置
- matlab 基础应用之二元插值
- Java中的反射机制(Reflect)
- PCA降维
- POJ-1723 中位数
- Python3之for循环、元组、字典
- Android Studio中Grpc的配置
- 【内功篇】指针&数组&字符串(四)
- ubuntu系统下codeblocks编译时提示/bin/sh: 1: g++: not found
- RecyclerView+SnapHelper实现无限循环筛选控件
- Hadoop数据完整性