感觉反射理解还是有难度的,大家一起学习!
反射的概念和作用
在之前的的java类的学习中我们有以下的集中用法
- 已知一个类的类名、以及类中的方法、属性、构造方法等
- 调用构造方法创建对象
- 使用对象调用方法或属性
那么我们现在提出这样一个问题:
如果仅仅知道一个类的类名,能否动态得到类的定义信息,包括哪些方法,属性等?
答案:反射
Java反射的概念
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;
对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取信息以及动态调用对象的方法的功能称为java语言的反射机制
Java反射的作用
动态获取类的信息,进一步实现需要的功能
例如:Spring框架通过XML文件描述类的基本信息,使用反射机制动态装配对象
Java反射相关的类主要包括
- Class 类型
- Constructor 构造方法
- Method 方法
- Field 属性
- ……
- 除了Class外,其他类都位于java.lang.reflect包中
其中最重要的是Class,因为反射的使用都是从Class开始的
Class类
Class类是Java反射机制的基础,通过Class类,可以得到一个类的基本信息
Class类中的主要方法
- getMethod:返回类中某一个方法的实例
- getMethods: 返回类中所有方法的实例
- getField:返回类中某一个属性的实例
- getFields:返回类中所有属性的实例
- getConstructor:返回类中的一个构造方法的实例
- Class类中还有若干getXXX方法,可以获得类的基本信息
怎么调用Class类的方法
首先要获取Class类的实例,获得Class类实例的常用方法有如下三个:
- 任何类都继承到了getClass方法,任意对象可以调用getClass方法获得Class实例
- 类名.class方式:适用于通过类名获得Class实例的情况
任何类名加.class即返回Class实例,例如 Class clazz=String.class; - Class类的静态方法 forName(String name):适用于通过类型获得Class实例的情况,尤其类名是变量
例如:Class.forName(className);
package 反射.Day_04_19_Code;
import java.lang.reflect.Method;
public class TestClass {
public static void main(String[] args) {
String s="hello";
// 使用对象名获得Class实例
Class<String> clazz1=(Class<String>) s.getClass();
// 使用类名获得Class实例,类名必须是常量
Class clazz2=String.class;
try {
// 使用类名获得Class实例,类名可以是变量
Class clazz3=Class.forName("java.lang.String");
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Method[] methods=clazz1.getMethods();
for(Method m:methods){
System.out.println(m.getName());
}
System.out.println("==============================");
// 获得Student类的Class对象
try {
Class sClazz=Class.forName("反射.Day_04_19_Code.Student");
Class superClazz=sClazz.getSuperclass();
System.out.println(superClazz.getName());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
在控制台的结果
Constructor类
Constructor类可以通过getXXX方法获得构造方法的基本信息,例如:
- getName:返回构造方法的名字
- getParameterTypes:返回构造方法的参数类型
除了获得构造方法的基本信息,还可以创建实例
- newInstance(Object… initargs) :创建实例
Constructor实例通过Class实例获得,Class类中定义了如下方法
- Constructor getConstructor(Class… parameterTypes) :通过指定参数类型,返回构造方法实例。
- Constructor[] getConstructors() :返回该类的所有构造方法实例。
package 反射.Day_04_17_Code;
import java.lang.reflect.Constructor;
class Test_04 {
public Test_04() {
System.out.println(" public Test_04() {...}");
}
public Test_04( int x ) {
System.out.println(" public Test_04( int x ) {...}");
}
protected Test_04( char y ) {
System.out.println(" public Test_04( char y ) {...}");
}
private Test_04( String z ) {
System.out.println(" public Test_04( String z ) {...}");
}
}
public class Code_004 {
public static void main(String[] args) throws Exception {
// 获取到 类型的信息
Class c4 = Class.forName("反射.Day_04_17_Code.Test_04");
// 获取 类型中对构造方法的“描述”
Constructor[] arr = c4.getConstructors();
Constructor[] brr = c4.getDeclaredConstructors();
for (Constructor constructor : arr) {
System.out.println(constructor);
}
System.out.println("---------------------------------------------------------");
for (Constructor constructor : brr) {
System.out.println(constructor);
}
System.out.println("---------------------------------------------------------");
Constructor con1 = c4.getConstructor(null);
System.out.println(con1);
System.out.println("---------------------------------------------------------");
Constructor con2 = c4.getConstructor(int.class);
System.out.println(con2);
System.out.println("---------------------------------------------------------");
Constructor con3 = c4.getDeclaredConstructor(char.class);
System.out.println(con3);
System.out.println("---------------------------------------------------------");
Constructor con4 = c4.getDeclaredConstructor(String.class);
System.out.println(con4);
System.out.println("=========================================================");
// 创建实例对象 —— 方法1
Test_04 test = (Test_04)c4.newInstance();
System.out.println(test.getClass());
System.out.println(test.hashCode());
System.out.println("---------------------------------------------------------");
/*
* newInstance() 有两个!!!
*
* 第一个是 Object 类中提供的版本,此版本只能调用 无参构造方法!!!
*
* public T newInstance() {...}
*
*
* 第二个是 Constructor 类中提供的版本,此版本可以调用 有参构造方法!!!
*
* public T newInstance(Object ... initargs) {...}
*
*
*/
// 创建实例对象 —— 方法2
Test_04 contest = (Test_04)con1.newInstance(null);
System.out.println(contest.getClass());
System.out.println(contest.hashCode());
System.out.println("---------------------------------------------------------");
Test_04 contest1 = (Test_04)con2.newInstance(101);
System.out.println(contest1.getClass());
System.out.println(contest1.hashCode());
System.out.println("---------------------------------------------------------");
con4.setAccessible(true);
Test_04 contest2 = (Test_04)con4.newInstance("qwerdf123");
System.out.println(contest2.getClass());
System.out.println(contest2.hashCode());
}
}
在控制台的结果
Field类
Field类将类的属性进行封装,可以获得属性的基本信息、属性的值,也可以对属性进行赋值
- getName:返回属性的名字
- getXXX:例如,getFloat返回该属性float类型的值
- setXXX:例如,setFloat为属性赋值float类型的值
package 反射.Day_04_17_Code;
import java.lang.reflect.Field;
class Test_02 {
public int num_01;
private int num_02;
public void fun_01() {
}
private void fun_02() {
}
public Test_02() {
}
private Test_02( int x ) {
}
@Override
public String toString() {
return " num_01 = " + num_01 + " num_02 = " + num_02;
}
}
public class Code_002 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, SecurityException, InstantiationException, IllegalAccessException {
// 获取类型信息
Class c2 = Class.forName("反射.Day_04_17_Code.Test_02");
System.out.println( c2 );
// 获取所有公开的属性
Field[] arr = c2.getFields();
// 获取所有的属性
Field[] brr = c2.getDeclaredFields();
for (Field field : arr) {
System.out.println(field);
}
System.out.println("---------------------------------------------------------");
for (Field field : brr) {
System.out.println(field);
}
System.out.println("---------------------------------------------------------");
Field f1 = c2.getField("num_01");
System.out.println(f1);
System.out.println("---------------------------------------------------------");
Field f2 = c2.getDeclaredField("num_02");
System.out.println(f2);
System.out.println("=========================================================");
// 创建实例对象
Test_02 test = (Test_02)c2.newInstance();
System.out.println(" f1 = " + test.num_01 );
System.out.println(test);
System.out.println("---------------------------------------------------------");
// 利用 field 修改对象里面的公有属性值
f1.set(test, 101);
System.out.println(" f1 = " + test.num_01 );
System.out.println(test);
System.out.println("---------------------------------------------------------");
// 利用 field 修改对象里面的所有属性值
// 需要“穿透”原有的访问控制权限
f2.setAccessible(true);
f2.set(test, 202);
System.out.println(test);
}
}
在控制台的结果
Method类
Method类将类中的方法进行封装,可以动态获得方法的信息,例如
- getReturnType:获得方法返回值类型
- getName:获得方法名字
- getParameterTypes:获得方法参数类型
除了动态获得方法信息外,Method还能动态调用某一个对象的具体方法
- invoke(Object obj, Object… args) :使用obj调用该方法,参数为args
package 反射.Day_04_17_Code;
import java.lang.reflect.Method;
class Test_03 {
public void fun_01() {
System.out.println(" public void fun_01() {...}");
}
public void fun_01( int x ) {
System.out.println(" public void fun_01( int x ) {...}");
}
private void fun_02() {
System.out.println(" private void fun_02() {...}");
}
private void fun_02( String str ) {
System.out.println(" private void fun_02( String str ) {...}");
}
protected void fun_03() {
System.out.println(" protected void fun_03() {...}");
}
}
public class Code_003 {
public static void main(String[] args) throws Exception {
// 获取类型信息
Class c3 = Class.forName("反射.Day_04_17_Code.Test_03");
// 获取公开的方法
Method[] arr = c3.getMethods();
// 获取所有的方法
Method[] brr = c3.getDeclaredMethods();
for (Method method : arr) {
System.out.println(method);
}
System.out.println("---------------------------------------------------------");
for (Method method : brr) {
System.out.println(method);
}
System.out.println("---------------------------------------------------------");
Method m1 = c3.getMethod("fun_01");
System.out.println(m1);
Method m2 = c3.getMethod("fun_01", int.class);
System.out.println(m2);
Method m3 = c3.getDeclaredMethod("fun_02");
System.out.println(m3);
Method m4 = c3.getDeclaredMethod("fun_02", String.class);
System.out.println(m4);
System.out.println("=========================================================");
// 创建实例对象
Test_03 test = (Test_03)c3.newInstance();
// 对象.方法() 固定写法
// test.fun_01();
m1.invoke(test);
m2.invoke(test, 101);
System.out.println("---------------------------------------------------------");
m3.setAccessible(true);
m3.invoke(test);
m4.setAccessible(true);
m4.invoke(test, "qwerDF123");
}
}
在控制台的结果
获取main方法
大家知道怎么获取main方法吗?下面就给大家介绍下
package 反射.Day_04_20_Code;
public class Code_001_TestMain {
public static void main(String[] args) {
System.out.println(" public class Code_001_TestMain {...}");
}
}
package 反射.Day_04_20_Code;
import java.lang.reflect.Method;
public class Code_001 {
public static void main(String[] args) {
try {
// 获取 类型信息
Class tm = Class.forName("Day_04_20_Code.Code_001_TestMain");
// 获取 main 方法
Method m_main = tm.getMethod("main", String[].class);
// 调用 main 方法
// m_main.invoke( null, (Object)new String[]{"qwer","DF","123"} );
m_main.invoke( null, new Object[]{ new String[]{"qwer","DF","123"} } );
} catch ( Exception e ) {
e.printStackTrace();
}
}
}
在控制台的结果
通过.properties配置文件进行反射(模拟项目中xml文件)
好处:
- 可以不使用import导入外部文件
- 可以直接通过获取配置文件中的内容来得到所有的属性、方法、构造方法、也可以设置属性的值
测试文件代码
package 反射.Day_04_20_Code;
public class Code_003_Test {
public int num_01;
protected int num_02;
private int num_03;
public void fun_01() {
System.out.println(" public void fun_01() {...}");
}
protected void fun_02() {
System.out.println(" protected void fun_02() {...}");
}
private void fun_03() {
System.out.println(" private void fun_03() {...}");
}
public void fun_01( int x ) {
System.out.println(" public void fun_01( int x ) {...}");
}
public static void fun_static( ) {
System.out.println(" public static void fun_static( ) {...}");
}
public static void fun_static( String str ) {
System.out.println(" public static void fun_static( String str ) {...}");
}
}
利用反射的代码
package 反射.Day_04_20_Code;
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Properties;
public class Code_003 {
public static void main(String[] args) {
try {
// 反射流程前的准备
Properties pro = new Properties();
//FileReader fr = new FileReader("configuration_test.txt");
//加载配置文件
//pro.load();
pro.load(Code_003.class.getResourceAsStream("configuration_test.properties"));
//fr.close();
//获取className的值
String cn1 = pro.getProperty("className");
//获取属性名称
String fn1 = pro.getProperty("fieldName");
//获取私有的属性名称
String fn3 = pro.getProperty("fieldName_private");
//获取方法名称
String mn1 = pro.getProperty("methodName");
//获取私有方法名称
String mns = pro.getProperty("methodName_static");
// System.out.println(cn1);
// System.out.println(fn1);
// System.out.println(fn3);
// 反射流程
//huoqu Class类的实例
Class test = Class.forName(cn1);
//创建实例
Object obj = test.newInstance();
// 属性的访问
Field f1 = test.getField(fn1);
System.out.println(f1);
//设置非私有属性
f1.set(obj, 101); // obj.num_01 = 101;
System.out.println(f1.get(obj)); // obj.num_01
System.out.println("---------------------------------------------------------");
//获取私有的属性
Field f3 = test.getDeclaredField(fn3);
//击穿私有的访问权限
f3.setAccessible(true);
System.out.println(f3);
//设置私有的属性
f3.set(obj, 101); // obj.num_03 = 303;
//通过get方法得到设置的值,因为是私有属性所以用get
System.out.println(f3.get(obj)); // obj.num_03
System.out.println("=========================================================");
// 方法的访问
//获取无参的方法
Method m1 = test.getMethod(mn1);
//调用invoke,不传入参数
m1.invoke(obj);
// m1.invoke(obj, 101); // 方法对象没找对
//获取参数类型为int的方法
Method m2 = test.getMethod(mn1,int.class);
//使用invoke方法,并且传入一个int类型的值
m2.invoke(obj,101);
System.out.println("---------------------------------------------------------");
Method ms1 = test.getMethod(mns);
ms1.invoke(null);
//获取参数类型为String的方法
Method ms2 = test.getMethod(mns,String.class);
//使用invoke方法,并且传入一个String类型的值
ms2.invoke(null,"qwerdf123");
} catch (Exception e) {
e.printStackTrace();
}
}
}
configuration_test.properties的内容
className = 反射.Day_04_20_Code.Code_003_Test
fieldName = num_01
methodName = fun_01
fieldName_private = num_03
methodName_static = fun_static
在控制台的结果
泛型和反射
package 反射.Day_04_20_Code;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
public class Code_004 {
public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
ArrayList<String> str_arr = new ArrayList<String>();
str_arr.add("qwer");
str_arr.add("DF");
// 获取 ArrayList的类型信息
Class arr_class = str_arr.getClass();
// 获取此类中的add方法
Method m1 = arr_class.getMethod("add", Object.class);
// 调用add方法
m1.invoke(str_arr, 101);
// 打印集合
for (Object obj : str_arr) {
System.out.println(obj);
}
}
}
在控制台的显示
可以看见101不是string类型也可以放进去,并且输出