Java基础——反射 reflect
什么是反射
(1)Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为Java语言的反射机制。
(2)反射(Reflection)是Java程序开发语言的特征之一,它允许运行中的Java程序对自身进行检查, 也称自审,并能直接操作程序的内部属性。例如,使用它能获得Java类中各成员的名称并显示出来。
(3)Java的这一能力在实际应用中应用得很多,在其它的程序语言中根本就不存在这一特性。例如,Pascal、C或者C++中就没有办法在程序中获得函数定义相关的信息。
(4)JavaBean是类反射的实际应用之一,它能让一些工具可视化的操作软件组件。这些工具通过类反射动态的载入并取得Java组件(类)的属性。后面学习的各种框架,基本上都会有反射的使用。
使用反射的基本步骤
用于反射的类,如Method,可以在java.lang.reflect包中找到。使用这些类的时候必须要遵循三个步骤:
第一步:获得你想操作的类的java.lang.Class对象。在运行中的Java程序中,用java.lang.Class类来描述类和接口等。
第二步:调用诸如getDeclaredMethods的方法,取得该类中定义的所有方法的列表。
第三步:使用反射的API来操作这些信息。
认识 java.lang.Class 类
在面向对象的世界里,万事万物皆对象。
那么类(java语言中,静态的成员、普通数据类型除外)是不是对象呢?
类是(哪个类的对象呢?)谁的对象呢?
There is a class named Class .
定义的每一个类都是对象,类是java.lang.Class类的实例对象。
定义一个Foo类
- package com.demo.reflect;
class Foo{- public void print(){
- System.out.println("Foo");
- }
- }
Foo 类的实例对象表示方式:
Foo f = new Foo();
//f 就可以表示了 Foo 类的一个对象,这个表示很清晰明了。
定义的 Foo 类也是一个对象,是 java.lang.Class 的对象。
查看源码发现,java.lang.Class类没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的,因此不能显式地声明一个Class对象。
那么问题来了:怎么表示这个对象呢?
在 java 语言中,任何一个类都是Class的实例对象,这个实例对象有三种表示方式。
第一种表示方式: 类.class 告诉我们任何一个类都有一个隐含的静态成员变量class
Class c1 = Foo.class;
第二中表达方式 : 通过该类的对象调用 getClass() 方法
Class c2 = foo1.getClass();
第三种表达方式 : 使用Class类的中静态forName('类的全称')方法获得与字符串对应的Class对象
Class c3 = Class.forName("com.demo.reflect.Foo");
在java官网中说明:c1 、c2、c3 表示了 Foo类的类类型(class type)。
代码测试c1\c2\c3是否代表同一个对象?
System.out.println(c1==c2);//true
System.out.println(c1==c3);//true
根据输出结果,表示一个类只可能是Class类的一个实例对象。
实际情况下:虚拟机为每种类型管理一个独一无二的Class对象。也就是说,每个类(型)都有一个Class对象。运行程序时,Java虚拟机(JVM)首先检查是否 所要加载的类对应的Class对象是否已经加载。如果没有加载,JVM就会根据类名查找.class文件,并将其Class对象载入。
小结:万事万物皆对象,类也是对象,是Class类的实例对象,这个对象我们称为该类的类类型(不仅仅代表是类或者接口)。
备注:基本的 Java 类型(boolean、byte、char、short、int、long、float 和 double)和关键字 void 也都对应一个 Class 对象。 每个数组属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 Class 对象。
细说:Class.forName(String className)
Class.forName() 不仅表示了类的类类型,还代表了动态加载类。
那什么是静态加载和动态加载?
动态加载:程序在运行时调用相应方法,即使其他方法是错误的,程序依旧会执行。通过动态加载可以让程序的可延长性大大提升,对以后的维护和扩展有重要意义。
静态加载:程序在编译时执行。在执行过程中加载所有可能执行到的程序。在这种加载方式下,只要加载中一个方法出错,程序就不能运行。我们一般写程序默认的是静态加载。
通过下面案例进行分析:
- public class Test{
- public static void main(String[] args) {
- // 静态加载。编译时加载,编译时就需要加载全部可能使用到的的类,一个类出错,其他类的功能都不能得到执行
- if ("Word".equals(args[0])) {
- Word Word = new Word();
- System.out.println("Word");
- }
- if ("Excel".equals(args[0])) {
- Excel excel = new Excel();
- System.out.println("Excel");
- }
- }
- }
如果你不定义Word类和Excel类,则会出现编译错误。
若你想程序能运行,必须定义Word类和Excel类。
假想如下场景,Test中有100个功能,你只想使用A功能,如果你使用的是静态加载的机制,你必须定义其余的99种功能才能使用A功能,如果使用动态加载机制,不仅不用定义99中功能,通过实现某种标准(继承某个接口),将大大方便了代码的编写。
创建一个接口:
public interface MyStandard{
public void print();
}
新建一个程序入口:
public static void main(String[] args) {
//运行时加载,动态加载类
Class c1 = Class.forName(args[1]);
//通过类类型加载对象。
//返回的是包含N个class的接口,以免程序编译错乱
RunBetter rb = (RunBetter)c1.newInstance();
rb.print();
}
实现接口:
class Word implements MyStandard{
public void print(){
System.out.println("Word ");
}
}class Excel implements MyStandard{
public void print(){
System.out.println("A");
}
}
想要使用那个类就传入该类。这使得程序更加的多样,就算程序已经运行了,也可以通过这个方式进行加载新的类,实现某种升级。Class.forName()加载得到类类型。
得到类的类类型,我们可以干什么呢?
Class 类里面提供了很多解析、操作类的方法,详情可以查看API 文档。最重要的方法:Class.forName(String className)
得到类的构造方法
- /**
- * 打印对象的构造函数的信息
- * @param obj
- */
- public static void printConMessage(Object obj){
- Class c = obj.getClass();
- /*
- * 构造函数也是对象
- * java.lang. Constructor中封装了构造函数的信息
- * getConstructors获取所有的public的构造函数
- * getDeclaredConstructors得到所有的构造函数
- */
- //Constructor[] cs = c.getConstructors();
- Constructor[] cs = c.getDeclaredConstructors();
- for (Constructor constructor : cs) {
- System.out.print(constructor.getName()+"(");
- //获取构造函数的参数列表--->得到的是参数列表的类类型
- Class[] paramTypes = constructor.getParameterTypes();
- for (Class class1 : paramTypes) {
- System.out.print(class1.getName()+",");
- }
- System.out.println(")");
- }
- }
得到类的属性
- /**
- * 获取成员变量的信息
- * @param obj
- */
- public static void printFieldMessage(Object obj) {
- Class c = obj.getClass();
- /*
- * 成员变量也是对象
- * java.lang.reflect.Field
- * Field类封装了关于成员变量的操作
- * getFields()方法获取的是所有的public的成员变量的信息
- * getDeclaredFields获取的是该类自己声明的成员变量的信息
- * field.setAccessible(true); 破除封装,属性强制可见
- */
- //Field[] fs = c.getFields();
- Field[] fs = c.getDeclaredFields();
- for (Field field : fs) {
- //得到成员变量的类型的类类型
- Class fieldType = field.getType();
- String typeName = fieldType.getName();
- //得到成员变量的名称
- String fieldName = field.getName();
- System.out.println(typeName+" "+fieldName);
- }
- }
得到类的方法
- /*
- * Method类,方法对象
- * 一个成员方法就是一个Method对象
- * getMethods()方法获取的是所有的public的函数,包括父类继承而来的
- * getDeclaredMethods()获取的是所有该类自己声明的方法,不问访问权限
- */
- Method[] ms = c.getMethods();//c.getDeclaredMethods()
- for(int i = 0; i < ms.length;i++){
- //得到方法的返回值类型的类类型
- Class returnType = ms[i].getReturnType();
- System.out.print(returnType.getName()+" ");
- //得到方法的名称
- System.out.print(ms[i].getName()+"(");
- //获取参数类型--->得到的是参数列表的类型的类类型
- Class[] paramTypes = ms[i].getParameterTypes();
- for (Class class1 : paramTypes) {
- System.out.print(class1.getName()+",");
- }
- System.out.println(")");
- }
- }
反射的基本操作
- public class Book {
- private String name;
- private double price;
- public void setPrice(double price) {
- this.price = price;
- }
- public void setName(String name) {
- this.name = name;
- }
- public Book(){
- System.out.println("****无参数构造");
- }
- @Override
- public String toString() {
- return "Book [name=" + name + ", price=" + price + "]";
- }
- public Book(String name, double price) {
- super();
- this.name = name;
- this.price = price;
- }
- }
Book 类
构造方法反射实例化对象
getConstructor(Class[])
返回当前 Class 对象表示的类的指定的公有构造子对象。
getConstructors()
返回当前 Class 对象表示的类的所有公有构造子对象数组。
getDeclaredConstructor(Class[])
返回当前 Class 对象表示的类的指定已说明的一个构造子对象。
getDeclaredConstructors()
返回当前 Class 对象表示的类的所有已说明的构造子对象数组。
- @Test
- public void testBookConstructor() throws Exception{
- Class<?> cs = Class.forName("com.jcy.po.Book");
- //调用类的无参构造器
- Object obj = cs.newInstance();
- Book b = (Book) obj;
- b.setName("无参构造器");
- System.out.println(b);
- //调用类的有参构造器
- Constructor constructor =cs.getConstructor( new Class [] { String.class,double.class});
- //也可以 --> cs.getConstructor( String.class,double.class);
- Book b2 = (Book)constructor.newInstance("有参构造器",100.00);
- System.out.println(b2);
- //输出结果:
- //Book [name=无参构造器, price=0.0]
- //Book [name=有参构造器, price=100.0]
- }
反射操作属性
getDeclaredField(String)
返回当前 Class 对象表示的类或接口的指定已说明的一个域对象。
getDeclaredFields()
返回当前 Class 对象表示的类或接口的所有已说明的域对象数组。
getField(String)
返回当前 Class 对象表示的类或接口的指定的公有成员域对象。
getFields()
返回当前 Class 对象表示的类或接口的所有可访问的公有域对象数组。
- @Test
- public void testBookField() throws Exception{
- Book book = new Book("反射书",10.00);
- Class cs = book.getClass();
- System.out.println(book);
- Field [] fields = cs.getDeclaredFields();
- for (Field field : fields) {
- System.out.println(field.getName());
- }
- Field field = cs.getDeclaredField("name");
- //此处报错java.lang.IllegalAccessException
- //私有属性不可以直接访问
- //field.set("name", "强行赋值");
- field.setAccessible(true);//设置该属性为可使用
- field.set(book, "强行赋值");//不报错
- System.out.println(book);
- }
//输出结果:- Book [name=反射书, price=10.0]
name
price
Book [name=强行赋值, price=10.0]
方法反射的调用
getMethod(String, Class[])
返回当前 Class 对象表示的类或接口的指定的公有成员方法对象。
getMethods()
返回当前 Class 对象表示的类或接口的所有公有成员方法对象数组,包括已声明的和从父类继承的方法。getDeclaredMethod(String, Class[])
返回当前 Class 对象表示的类或接口的指定已说明的一个方法对象。
getDeclaredMethods()
返回 Class 对象表示的类或接口的所有已说明的方法数组。
- @Test
- public void testBookMethod() throws Exception{
- //获得类中的方法
- //getMethod获取的是public的方法,包括继承的方法
- //getDelcaredMethod自己声明的方法
- //getMethod 根据方法名和方法的参数类类型得到对应方法
- Class<?> cs = Class.forName("com.jcy.po.Book");
- Book b = (Book) cs.newInstance();
- //方法的反射操作
- //m.invoke(b,"格林童话");方法的反射操作是用m对象来进行方法调用
- //和b.setName(格林童话);调用的效果完全相同
- //方法如果没有返回值返回null,有返回值返回具体的返回值
- Method m =cs.getMethod("setName", String.class);
- System.out.println(b);
- m.invoke(b,"格林童话");
- System.out.println(b);
- Method[] ms = cs.getMethods();
- for (Method method : ms) {
- System.out.println(method.getName());
- }
- //输出结果:
- /*
- * Book [name=null, price=0.0]
- Book [name=格林童话, price=0.0]
- toString
- setName
- setPrice
- wait
- wait
- wait
- equals
- hashCode
- getClass
- notify
- notifyAll
- *
- */
- }
类反射调用图
Java基础——反射 reflect的更多相关文章
- Java基础-反射(reflect)技术详解
Java基础-反射(reflect)技术详解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.类加载器 1>.JVM 类加载机制 如下图所示,JVM类加载机制分为五个部分 ...
- Java基础——反射
今天学到Java基础中的反反射.依照我学习后的个人理解呢,反射就是一套获取类.属性.方法等的工具吧.(其实,感觉学完反射后,有点像喝凉水,解渴但确实我也没体会出它有什么味道,我可能没有学到精髓吧.自己 ...
- Java基础反射(二)
原文地址http://blog.csdn.net/sinat_38259539/article/details/71799078 反射是框架设计的灵魂 (使用的前提条件:必须先得到代表的字节码的Cla ...
- Java 基础-反射
反射-Reflect 测试用到的代码 1.接口 Person.java public interface Person { Boolean isMale = true; void say(); voi ...
- [java 基础]反射入门
原文 概况 使用java的反射,可以让我们检查(或者修改)类,接口,字段,方法的特性.当你在编译期不知道他们的名字的时候非常有用. 除此之外,可以使用反射来创建实例,调用方法或者get/set 字段值 ...
- JAVA基础-反射机制
什么是JAVA的反射机制 Java反射是Java被视为动态(或准动态)语言的一个关键性质.这个机制允许程序在运行时透过Reflection APIs取得任何一个已知名称的class的内部信息,包括其 ...
- java基础(反射,注解,多线程,juc)
JAVA基础 java反射 class对象 三种方式获取class加载时对象 1.class.forName("全类名"):将字节码文件加载进内存,返回class对象 2.类名.c ...
- Java框架基础——反射(reflect)
一.Class类的使用 1)在面向对象(oop)的世界里,万事万物皆对象. 在Java中,包括基本的数据类型,都是对象. Class c = int.class;//int 的类类型 那就是说: 类是 ...
- java基础-反射(细节)
java面试题--java反射机制? Java反射机制的作用:1)在运行时判断任意一个对象所属的类.2)在运行时判断任意一个类所具有的成员变量和方法.3)在运行时任意调用一个对象的方法4)在运行时构造 ...
随机推荐
- thinkphp->add方法错误
$group_id=$model->add($add); 以上这句代码如果执行成功,返回它存储的id,但是,会有一种情况一直返回1. 代码完全没有问题,检查数据库发现有两个主键id,删除一个就O ...
- 【转】 Git——如何将本地项目提交至远程仓库
1.(先进入项目文件夹)通过命令 git init 把这个目录变成git可以管理的仓库 git init 2.把文件添加到版本库中,使用命令 git add .添加到暂存区里面去,不要忘记后面的小数点 ...
- css 负边距
负边距 可以改变 他在文档流中的尺寸 当块级元素设置 margin: -10px; 这个快 的大小没变但是他的定位的位置向上串了,压住了上面的文字 而且在他后面的文字 会爬到他身上 而前面的文 ...
- Jeecg踩坑不完全指南
公司用了这个叫做jeecg的快速开发框架,我不知道有多少公司在用这个框架,园子里有的可以吱一声.个人觉得这框架唯一优势就是可以让不会ssh的人也能进行开发,只要你会J2SE,有web后台发开经验即可. ...
- HUST 1588 辗转数对
1588 - 辗转数对 时间限制:1秒 内存限制:128兆 155 次提交 27 次通过 题目描述 假设当前有一个数对(a, b),我们可以通过一步将这个数对变为一个新数对(a + b, b)或者是( ...
- bzoj 1415: [Noi2005]聪聪和可可
直接上记忆化搜索 #include<queue> #include<cstdio> #include<algorithm> using namespace std; ...
- 51 Nod 1057 N的阶乘【Java大数乱搞】
1057 N的阶乘 基准时间限制:1 秒 空间限制:131072 KB 分值: 0 难度:基础题 输入N求N的阶乘的准确值. Input 输入N(1 <= N <= 10000) Ou ...
- 洛谷 P1019 单词接龙【经典DFS,温习搜索】
P1019 单词接龙 题目描述 单词接龙是一个与我们经常玩的成语接龙相类似的游戏,现在我们已知一组单词,且给定一个开头的字母,要求出以这个字母开头的最长的“龙”(每个单词都最多在“龙”中出现两次),在 ...
- CSS3动画属性和flex弹性布局各个属性
[CSS3动画的使用] 1.声明一个关键帧(动画): @keynames name{ from{} to{} } 每个阶段的写法: ①可以直接使用from-to的写法 ②可以设置0%-100%的写法, ...
- android studio无法识别含有rcs包名的jar
http://blog.csdn.net/JingleYe/article/details/70048943 android studio无法识别含有rcs包名的jar android stu ...