JAVA之反射技术总结

概述

  设计框架的灵魂

  1. 框架:半成品软件。可以在框架的基础上进行软件开发,简化编码。
  2. 反射:将类的各个组成部分封装为其他对象

好处

  1. 可以在程序运行过程中,操作这些对象。
  2. 可以解耦,提高程序的可扩展性。

三个阶段

  1. Source源代码阶段
    包含:
    1. .java文件
    2. .class文件
  2. Class类对象阶段
    通过类加载器classLoader 将字节码文件.class文件转化成class对象
  3. Runtime运行阶段
    创建对象:new 类()

获取class对象

  因为class对象,存在与三个不同的时间。因此获取class对象在三个不同的时间有不同的方式。

Source源代码阶段获取

  Class.forName("全类名") : 将字节码文件加载进内存,返回class对象

Class类对象阶段获取

  类名.class : 通过类名的属性class获取

Runtime运行阶段获取

  对象名.getClass() : getClass( )方法在object类中定义着。

样例

package main;

public class Main {

	public static void main(String[] args) throws ClassNotFoundException {
		// TODO Auto-generated method stub
		
		// 1. Class.forName("全类名") : 将字节码文件加载进内存,返回class对象
		Class cls1 = Class.forName("main.Student");
		System.out.println(cls1);
		// 2. 类名.class : 通过类名的属性class获取
		Class cls2 = Student.class;
		System.out.println(cls2);
		// 3. 对象名.getClass() : getClass( )方法在object类中定义着。
		Student stu = new Student("张三", 18);
		Class cls3 = stu.getClass();
		System.out.println(cls3);
		System.out.println(cls1 == cls2);
		System.out.println(cls1 == cls3);
	}
}

运行结果:
class main.Student
class main.Student
class main.Student
true
true

结论:
  从运行结果来看。同一个字节码文件(*.class)在一次程序运行过程中, 只会被加载一次, 不论通过哪一种方式获取的Class对象都是同一个。

使用class对象

  我们都知道一个class对象里面有很多方法和变量,正是因为这些构成了我们的class对象。因此使用class对象大致从:成员变量、构造方法、成员方法、类名这几个方面进行使用。
  共性: 如果要想使用class对象里面的私有方法、成员变量可使用暴力反射技术。将获取到的方法、变量使用内置方法 setAccessible 忽略权限修饰符的安全检查。

    具体步骤:

    1. 利用getDeclaredxxx获取到私有方法、成员变量。
    2. 对其精确到某一个方法、成员变量使用 .setAccessible 即可。

Student类源码

package reflect;


public class Student {

	String name;
	private int age;
	
	public int x = 1;
	public int y ;
	
	
	public Student() {
		super();
	}
	private Student(String name) {
		this.name = name;
	}

	public Student(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}

	public String getName1() {
		return name;
	}

	public void setName1(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	@Override
	public String toString() {
		return "Student [name=" + name + ", age=" + age + "]";
	}
	
	public String show(String name) {
		return "Student [name=" + name + ", age=" + this.age + "]";
	}
	
	private int add(int x,int y) {
		System.out.println("类里面的:" + (x + y));
		return x + y;
	}
}

获取成员变量

返回值方法解释
FieldgetField(String name)获取所有public修饰的成员变量
Field[ ]getFields()获取指定public修饰的成员变量
FieldgetDeclaredField(String name)获取指定的成员变量
Field[ ]getDeclaredFields()获取所有成员变量,无关权限修饰符

使用成员变量

  1. 获取值
返回值方法解释
Objectget(Object obj)返回该所表示的字段的值 Field ,指定的对象上
  1. 设置值
返回值方法解释
voidset(Object obj, Object value)将指定对象参数上的此 Field对象表示的字段设置为指定的新值

综合样例

package main;

import java.lang.reflect.Field;

public class Main {

	public static void main(String[] args) throws Exception {
		// TODO Auto-generated method stub
		
		Student stu = new Student("张三", 18);
		Class cls = stu.getClass();
		
		System.out.println("================");
		// 1. 获取所有public修饰的成员变量
		Field[] field = cls.getFields();
		for(Field i : field) {
			System.out.println(i);
		}
		System.out.println("=================");
		// 2. 获取指定public修饰的成员变量
		Field x = cls.getField("x");
		System.out.println(x);
		System.out.println("=================");
		// 3. 获取所有成员变量,无关权限修饰符
		Field[] field1 = cls.getDeclaredFields();
		for(Field i : field1) {
			System.out.println(i);
		}
		System.out.println("=================");
		// 4. 获取指定成员变量,无关权限修饰符
		Field name = cls.getDeclaredField("name");
		System.out.println(name);
		
		// 将获取到的私有变量。设置忽略权限修饰符。才可进行下面get。set操作
		// 如果不是私有的。则可:无需设置
		name.setAccessible(true);		
		Object name1 = name.get(stu);
		System.out.println(name1);
		name.set(stu, "李四");
		Object name2 = name.get(stu);
		System.out.println(name2);
	}
}

运行结果:
=================
public int main.Student.x
public int main.Student.y
=================
public int main.Student.x
================
java.lang.String main.Student.name
private int main.Student.age
public int main.Student.x
public int main.Student.y
=================
java.lang.String main.Student.name
张三
李四

获取构造方法

返回值方法解释
Constructor< T >getConstructor(类<?>… parameterTypes)获取所有public修饰的构造方法
Constructor< T >[ ]getConstructors()获取指定public修饰的构造方法
Constructor< T >getDeclaredConstructor(类<?>… parameterTypes)获取指定的构造方法
Constructor< T >[ ]getDeclaredConstructors()获取所有构造方法,无关权限修饰符

使用构造方法

  创建对象

返回值方法解释
TnewInstance(Object… initargs)使用指定的初始化参数来创建和初始化构造函数的声明类的新实例

综合样例

package reflect;

import java.lang.reflect.Constructor;

public class ConstrutorTest {

	public static void main(String[] args) throws Exception {
		// TODO Auto-generated method stub
		
		Student stu = new Student("张三", 18);
		
		Class cls = stu.getClass();
		
		// 1. 获取所有public修饰的构造方法
		Constructor[] con1 =  cls.getConstructors();
		for(Constructor i : con1) {
			System.out.println(i);
		}
		System.out.println("==================");
		
		// 2. 获取指定public修饰的构造方法
		// 参数使用,基本数据类型.class
		Constructor con2 = cls.getConstructor(String.class,int.class);
		Constructor con3 = cls.getConstructor();
		System.out.println(con2);
		System.out.println(con3);
		System.out.println("==================");
		// 3. 获取所有构造方法,无关权限修饰符
		Constructor[] con4 = cls.getDeclaredConstructors();
		for(Constructor i : con4) {
			System.out.println(i);
		}
		System.out.println("==================");
		
		// 4. 获取指定的构造方法
		Constructor con5 = cls.getDeclaredConstructor(String.class);
		System.out.println(con5);
		
		// 利用这个私有的构造方法。进行创建对象
		// 之前一定要使用暴力反射
		con5.setAccessible(true);
		// 参数选择。就是构造函数的那些参数
		Object stu1 = con5.newInstance("张三");	
		System.out.println(stu1);
	}
}

运行结果:
public reflect.Student(java.lang.String,int)
public reflect.Student()
==================
public reflect.Student(java.lang.String,int)
public reflect.Student()
==================
public reflect.Student(java.lang.String,int)
private reflect.Student(java.lang.String)
public reflect.Student()
==================
private reflect.Student(java.lang.String)
Student [name=张三, age=0]

获取成员方法

返回值方法解释
MethodgetMethod(String name, 类<?>… parameterTypes)获取指定public修饰的成员方法
Method[ ]getMethods()获取所有public修饰的成员方法
MethodgetDeclaredMethod(String name, 类<?>… parameterTypes)获取指定的成员方法
Method[ ]getDeclaredMethods()获取所有成员方法,无关权限修饰符

使用成员方法

  执行方法

返回值方法解释
Objectinvoke(Object obj, Object… args)在具有指定参数的方法对象上调用此方法对象表示的底层方法
StringgetName()得到方法名

综合样例

package reflect;

import java.lang.reflect.Method;

public class MethodTest {

	public static void main(String[] args) throws Exception {
		// TODO Auto-generated method stub
		Student stu = new Student();
		Class cls = stu.getClass();
		
		
		// 1. 获取指定public修饰的成员方法
		// 第一个参数:方法名
		// 第二个参数:该方法各种参数。字节码
		Method method1 = cls.getMethod("show",String.class);
		Method method2 = cls.getMethod("toString");
		System.out.println(method1);
		System.out.println(method2);
		System.out.println("======================");
		// 2. 获取所有public修饰的成员方法
		// 打印出来会有很多。因为 每个类继承了 Object类 。Object类下面有很多内置方法
		Method[] method3 = cls.getMethods();
		for(Method i : method3) {
			System.out.println(i);
		}
		System.out.println("=====================");
		
		// 3. 获取所有成员方法,无关权限修饰符
		Method[] method4 = cls.getDeclaredMethods();
		for(Method i : method4) {
			System.out.println(i);
		}
		System.out.println("====================");
		// 4. 获取指定的成员方法
		Method method5 = cls.getDeclaredMethod("add",int.class,int.class);
		System.out.println(method5);
		
		// 同理使用私有的成员方法
		// 需要暴力反射
		method5.setAccessible(true);
		Object sum = method5.invoke(stu,2,3);
		System.out.println("主方法的:" + sum);
		
		String name = method5.getName();
		System.out.println(name);
	}

}

运行结果:
public java.lang.String reflect.Student.show(java.lang.String)
public java.lang.String reflect.Student.toString()
================================================
public java.lang.String reflect.Student.toString()
public java.lang.String reflect.Student.show(java.lang.String)
public void reflect.Student.setName1(java.lang.String)
public java.lang.String reflect.Student.getName1()
public void reflect.Student.setAge(int)
public int reflect.Student.getAge()
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
================================================
private int reflect.Student.add(int,int)
public java.lang.String reflect.Student.toString()
public java.lang.String reflect.Student.show(java.lang.String)
public void reflect.Student.setName1(java.lang.String)
public java.lang.String reflect.Student.getName1()
public void reflect.Student.setAge(int)
public int reflect.Student.getAge()
================================================
private int reflect.Student.add(int,int)
类里面的:5
主方法的:5
add

反射技术综合案例—创建任意对象

书写一个简单‘框架’,在不修改任何代码的前提下,可创建任意对象,并执行里面的任意方法

步骤:

  1. 将需要创建的对象的全类名和需要执行的方法定义在配置文件中。
    键值对之间只能由空格或者等于符号隔开。如下所示:
    className=reflect.Teacher
    methodName=work
  2. 在程序中加载读取配置文件。命名为:以 properties 为后缀的文件
  3. 使用反射技术来加载类文件进内存
  4. 创建对象
  5. 执行方法

好处:
6. 当我要创建其他对象的时候,只需要修改配置文件即可
7. 当工程巨大的时候,修改配置文件而不去动源代码。减少了报错的原因

源代码:

package reflect;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Properties;

public class Main {
	public static void main(String[] args) throws Exception {
		// 步骤:
		// 1. 加载配置文件
		//   1.1 创建properties对象
		//   1.2 加载配置文件,转换为一个集合
		//     1.2.1 获取目录下的配置文件
		Properties pro = new Properties();
		ClassLoader classLoader = Main.class.getClassLoader();
		// 此处获取的路径根据src目录书写下去。
		// 类加载器首先获取,src下面的文件。再通过getResourceAsStream找到对应的文件。返回一个字节流
		// 返回用于读取指定资源的输入流。 
		InputStream inputStream = classLoader.getResourceAsStream("reflect\\pro.properties");
		pro.load(inputStream);			// {methodName=study, className=reflect.Student}
		
		// 2. 获取配置文件里面的数据
		String className = pro.getProperty("className");
		String methodName = pro.getProperty("methodName");
		
		// 3. 利用反射技术。加载该类进入内存
		Class cls = Class.forName(className);
		
		// 4. 创建这样一个对象
		// 如果想创建有参构造函数。参数使用,基本数据类型.class
		Constructor con = cls.getDeclaredConstructor();
		// 如果有参。写真实数据
		Object obj = con.newInstance();
		// 5. 获取成员方法
		Method method = cls.getDeclaredMethod(methodName);
		method.setAccessible(true);			// 暴力反射
		// 6. 执行方法	
		method.invoke(obj);
	}
}
package reflect;

public class Teacher {

	public void work() {
		System.out.println("同学们上课");
	}

}

运行结果:
方法写的有什么输出语句则对应输出。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值