Java学习笔记 --- 反射机制

来源:互联网 发布:python做时间序列分析 编辑:程序博客网 时间:2024/05/18 02:14

做完了第一版的图书管理系统, 决定这段时间静下心好好看一看书, 一边复习Java基础和Servlet&JSP, 再一边学一学框架, 写读书笔记将学习到的知识点记录下来.


反射是什么?

反射,一种计算机处理方式。是程序可以访问、检测和修改它本身状态或行为的一种能力。

这是百度里给出的概念, 可能大多数人跟我刚开始一样, 不是很理解这句话. 那么我们先从Java文件编译链接后生的可执行文件说起吧.

首先, 大家都明白, 一个.java文件经过编译链接会生成.class文件, 准确的来说, 应该是一个类经过编译会生成一个.class文件. 如果一个类里面除了主类还有其他类, 不管是内部类还是外部类, 每个类都会各自生成一个.class文件. 可能有人通过查看IDEA下classes目录会发现, 一个类经过编译链接后, 其内部类并不会生成独立的.class文件,但是实质上是会生成的, 只是IDEA下并没有显示出有这个.class文件而已, 我们在终端下来测试对比一下.

先来看一段Java代码:

public class test {    public static void main(String[] args){        System.out.println("test");    }    public class test1{        public void main(String[] args){            System.out.println("test1");        }    }}class test2{    public static void main(String[] args){        System.out.println("test2");    }}

看一下它在IDEA下生成的.class文件

这里写图片描述
可以看到红色的目录下就是编译链接后生成的.class文件只有主类和外部类, 但事实上并非如此, 我们在终端下使用javac来编译一下试试看.

这里写图片描述
可以清楚地看到, 有三个.class文件, 内部类test1也被编译链接生成了一个.class文件.
分析这些的主要目的是想说, 每一个类都会生成对应的.class文件. 但是.class文件并不能直接运行在操作系统上, 它需要运行于JVM之上, 这也是为什么Java可以跨平台的原因. 那么Java的反射机制到底是什么呢? 简单点来讲, 就是反编译: .class -> .java. 也就是说, 我们可以通过反射机制, 从.class文件里去访问原来的.java文件里面类的属性方法, 甚至去创建新的对象.
在编程语言里, 有动态语言和静态语言之分, 动态语言是指程序在运行时可以改变其结构, 而静态语言不能. Java本身是属于静态语言, 它并不能在运行时改变程序的结构, 但是Java本身提供了反射机制, 这也使得Java具有了一丢丢动态语言的性质. 什么意思呢? 就是反射机制允许Java程序在运行时透过Reflection API取得任何一个已知名称的class的内部信息.

反射的功能和使用

好, 明白了反射是什么, 那我们来看看Java的反射机制都能干哪些事?

  • 在运行时构造任意一个类的对象
  • 在运行时创建新类对象
  • 在运行时调用任一个对象的方法
  • 在运行时判断任意一个对象所属的类
  • 在运行时判断任意一个类所具有的成员变量和方法

在JDK中, 主要由以下类来实现Java反射机制, 这些类都位于java.lang.reflect包中.

Class类: 代表一个类
Field类: 代表一个类的成员变量(成员变量包括数据成员/方法成员/类成员等, 成员变量也称为类的属性)
Method类: 代表一个类的方法
Constructor类: 代表一个类的构造方法
Array类: 提供了动态创建数组, 以及访问数组的元素的静态方法
Proxy类和InvocationHandler接口: 提供了动态生成代理类以及实例的方法

那么下面我们来看一看如何使用反射.

1. 反射机制获取类

反射机制获取类有三种方法, 如下:

//第一种方式:  Class c1 = Class.forName("Employee"); //第二种方式:  //java中每个类型都有class 属性.  Class c2 = Employee.class;  //第三种方式:  //java语言中任何一个java对象都有getClass 方法  Employee e = new Employee();  Class c3 = e.getClass(); //c3是运行时类 (e的运行时类是Employee)  

2. 创建对象

前面使用反射机制获取到了类, 那么现在就用获取到的类来创建对象.

//第一种方式Class c =Class.forName("Employee");  //第二种方式//创建此Class 对象所表示的类的一个新实例  Object o = c.newInstance(); //调用了Employee的无参数构造方法.  

3. 获取属性

获取属性有获取所有属性和获取指定属性两种, 先来看一下获取所有属性.

//首先先获取整个类(以Integer这个类为例)Class c = Class.forName("java.lang.Integer");//然后获取该类的所有Field(属性)Field[] fs = c.getDeclaredFields();//定义字符串, 存储属性StringBuffer sb = new StringBuffer();//通过追加的方法,将每个属性拼接到此字符串中  //最外边的public定义  sb.append(Modifier.toString(c.getModifiers()) + " class " + c.getSimpleName() +"{\n"); //获取里边的每一个属性  for(Field field:fs){    sb.append("\t");//空格     sb.append(Modifier.toString(field.getModifiers())+" ");//获得属性的修饰符,例如public,static等等    sb.append(field.getType().getSimpleName() + " ");//属性的类型的名字      sb.append(field.getName()+";\n");//属性的名字+回车  }  sb.append("}");  System.out.println(sb);

下面是获取特定的属性.
我们通过传统方式和反射方式来对比看一下如何获取指定属性:

//传统方式:/* User u = new User(); u.age = 12; //set System.out.println(u.age); //get */  //反射方式//获取类  Class c = Class.forName("User");  //获取id属性  Field idF = c.getDeclaredField("id");  //实例化这个类赋给o  Object o = c.newInstance();  //打破封装  idF.setAccessible(true); //使用反射机制可以打破封装性,导致了java对象的属性不安全。  //set//给o对象的id属性赋值"110"  idF.set(o, "110"); //getSystem.out.println(idF.get(o));  

4. 获取方法, 构造方法

这里关于获取方法和构造方法, 暂时先给出一张方法表, 具体细节等到下次再详细总结.

方法关键字 含义 getDeclaredMethods() 获取所有的方法 getReturnType() 获得方法的放回类型 getParameterTypes() 获得方法的传入参数类型 getDeclaredMethod(“方法名”,参数类型.class,……) 获得特定的方法 构造方法关键字 含义 getDeclaredConstructors() 获取所有的构造方法 getDeclaredConstructor(参数类型.class,……) 获取特定的构造方法 父类和父接口 含义 getSuperclass() 获取某类的父类 getInterfaces() 获取某类实现的接口