java复习笔记————反射机制小总结

感觉反射理解还是有难度的,大家一起学习!

反射的概念和作用

在之前的的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();
		}
		
	}

}

在控制台的结果
java复习笔记————反射机制小总结_第1张图片

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());
	}
}


在控制台的结果
java复习笔记————反射机制小总结_第2张图片
java复习笔记————反射机制小总结_第3张图片

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);
		
	}

}

在控制台的结果
java复习笔记————反射机制小总结_第4张图片

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");
	}
}

在控制台的结果
java复习笔记————反射机制小总结_第5张图片
java复习笔记————反射机制小总结_第6张图片

获取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();
		}
		
	}
}


在控制台的结果
java复习笔记————反射机制小总结_第7张图片

通过.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

在控制台的结果

java复习笔记————反射机制小总结_第8张图片

泛型和反射

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);
		}
	}
}

在控制台的显示
java复习笔记————反射机制小总结_第9张图片

可以看见101不是string类型也可以放进去,并且输出

你可能感兴趣的:(java)