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

做完了第一版的图书管理系统, 决定这段时间静下心好好看一看书, 一边复习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文件

Java学习笔记 --- 反射机制_第1张图片
可以看到红色的目录下就是编译链接后生成的.class文件只有主类和外部类, 但事实上并非如此, 我们在终端下使用javac来编译一下试试看.

Java学习笔记 --- 反射机制_第2张图片
可以清楚地看到, 有三个.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"); 

//get
System.out.println(idF.get(o));  

4. 获取方法, 构造方法

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

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

你可能感兴趣的:(Java)