【Java学习笔记】18.反射与注解的应用
反射为Java程序在运行时提供了动态的能力,而注解允许通过一定的方式编写描述类的元数据。这些元数据可以为编译器提供信息,也可以进入字节码文件在运行时使用。这一章,将详细介绍这两种特性的原理与使用。
1.反射
反射是Java语言提供的一项非常吸引人的特性。利用反射可以在运行时对程序进行动态的控制,开发使用反射特性的程序需要使用一套专门的工具类,这些类大部分位于java.lang.reflect包中。
2.Class类的使用实例
Class类属于java.lang包,不需要使用import语句引入特殊的包就能使用,其对象代表一个类,携带相应的信息,主要包括构造器、方法、成员变量等。其实在Java程序运行的过程中,每个类被加载后都在内存中产生一个对应的Class类对象,在普通应用程序中这些对象由系统自动维护,不需要开发人员关心。
提示:Class类中,“C”为大写,如果小写就变成了声明类的Java的关键字。
Class类提供了很多用来加载及获得对应信息的方法。
注意:
- 不可以使用Class类的构造器来创建Class类对象,只能使用其提供的静态工厂方法来获取对象
- Field、Method、Constructor为java.lang.reflect包中的类,他们的对象分别代表成员变量、方法、构造器的信息
- package类在java.lang包中,其对象代表一个包,携带包的相应信息、
【例1】Class类的简单使用
//用来被加载类的父类
class MyFather {//父类的公共成员变量public int memerFather;//父类的公共方法public void methodFather() {System.out.println("我是从父类继承过来的方法");}}class MySon extends MyFather{//子类的公共成员变量public int memerSonPulic;//子类的私有成员变量private int memerSonPrivate;//子类的私有方法public void methodSonProtected() {System.out.println("我是子类自己的方法methodSonProtected");}//子类的公共方法public void methodSonPulic() {System.out.println("我是子类自己的方法methodSonPulic");}}//主类
public class Sample31_1 {public static void main(String[] args) {try {//加载指定的类Class c=Class.forName("org.example.MySon");//创建加载类的对象MySon ms = (MySon)c.newInstance();//调用创建对象的方法System.out.println("========调用创建对象的方法==========");ms.methodSonProtected();ms.methodSonPulic();ms.methodFather();//打印类的详细信息System.out.println("========打印类的Class信息==========");System.out.println(c.getName()+"类自己声明了"+c.getDeclaredFields().length+"个成员变量");System.out.println(c.getName()+"类对外公布的方法有"+c.getMethods().length+"个.");}catch(Exception e){e.printStackTrace();}}
}
-
定义了两个类,MySon&MyFather,其中MySon继承自MyFather,两个类中各种有一些方法与成员变量。
-
在主方法中首先加载了MySon类,然后通过Class对象的newInstance()方法创建了对象,并调用了对象中的方法,最后打印了加载类的信息。
-调用newInstance()方法创建对象后,返回的是Object类型的引用,需要进行恰当的强制类型转换才能够当做具体类型使用。 -
通过newInstance()方法创建的对象与通过普通方式创建的对象在使用上完全相同。
-
通过Class类可以获得所加载类的信息,打印出的信息与MySon类的具体情况是符合的。
- 因为还有从Object类间接继承而来的方法,所以打印出来MySon对外公布的方法有11个。
想要获取一个类对应的Class对象不一定要用forName()方法进行加载,java.lang.Oject类就提供了一个名为getClass()的方法来获取对象所在类对应的Class对象,方法签名如下:
public final Class getClass()
因为此方法时pulic类的,而Object类是Java中所有类的父类,因此所有类的对象都可以使用这个方法。
【例2】Class类的简单使用
package org.example;//用来被加载类的父类
class MyFather {//父类的公共成员变量public int memerFather;//父类的公共方法public void methodFather() {System.out.println("我是从父类继承过来的方法");}}class MySon extends MyFather{//子类的公共成员变量public int memerSonPulic;//子类的私有成员变量private int memerSonPrivate;//子类的私有方法public void methodSonProtected() {System.out.println("我是子类自己的方法methodSonProtected");}//子类的公共方法public void methodSonPulic() {System.out.println("我是子类自己的方法methodSonPulic");}}//主类
public class Sample31_1 {public static void main(String[] args) {MyFather mf = new MyFather();//加载指定的类Class c=mf.getClass();//打印类的详细信息System.out.println("========加载类"+c.getName()+"的信息==========");System.out.println(c.getName()+"类自己声明了"+c.getDeclaredFields().length+"个成员变量");System.out.println(c.getName()+"类对外公布的方法有"+c.getMethods().length+"个.");//创建加载类的对象MySon ms = new MySon();//获取class对象c = ms.getClass();//打印类的详细信息System.out.println("========加载类"+c.getName()+"的信息==========");System.out.println(c.getName()+"类自己声明了"+c.getDeclaredFields().length+"个成员变量");System.out.println(c.getName()+"类对外公布的方法有"+c.getMethods().length+"个.");}}
通过getClass()方法,也可以获得类对应的class对象。
3. 数组与Class类
Java中的数组是对象,因此数组对象也可以调用getClass()方法获取其对应的Class对象
【例】
package service;import java.io.File;
import java.io.Reader;
import java.util.Properties;public class Test {public static void main(String[] args) {//创建数组对象String[] stringArray = new String[4];int[][] intArray = new int[9][9];//获取数组对应的Class类Class sc = stringArray.getClass();Class ic = intArray.getClass();//打印两个类对应的类名System.out.println("一维String数组对应的类名为:"+sc.getName()+"。");System.out.println("二维int数组对应的类名为"+ic.getName()+"。");}
}
- 数组对象也有他们对应的类,类名是
[Ljava.lang.String;
[[I
- 以‘[’开头的是数组,两个‘[’代表二维数组,几维数组后有几个‘[’
- 如果是对象引用型数组,则表示在维数的几个‘[’后面跟上"L<类名>",例:三维Oject数组对应的类名为’[[[Ljava.lang.Object;'。
- 如果是基本类型数组,则在表示维数的’['后跟上“<类型代号>”,如 二维boolean型数组对应的类名为"[[Z"
基本类型 | 代号 |
---|---|
boolean | Z |
byte | B |
short | S |
char | C |
int | I |
long | J |
double | D |
float | F |
数组对应的类比较特殊,没有提供可用的构造器,因此无法直接使用Class类提供的静态工厂方法newInstance()来创建对象。
4. 精准判断对象类型
instanceof关键字无法用来判断对象的精确类型,如果需要判断对象的精确类型,就需要使用反射技术。
package reflect;//