反射,反射,程序员的快乐~~框架设计的灵魂~

Java是一种动态语言机制。

什么是动态语言?

动态语言,是指程序在运行时可以改变其结构:新的函数可以被引进,已有的函数可以被删除等在结构上的变化。比如JavaScript便是一个典型的动态语言。
  
除此之外如Ruby、Python、OC等也都属于动态语言,而C、C++、Java等语言则不属于动态语言。

动态类型语言,就是类型的检查是在运行时做的,是不是合法的要到运行时才判断,例如JavaScript就没有编译错误,只有运行错误。

静态语言

而静态类型语言的类型判断是在运行前判断(如编译阶段),比如java就是静态类型语言,静态类型语言为了达到多态会采取一些类型鉴别手段,如继承、接口,而动态类型语言却不需要,

Java的反射机制被视为Java为准动态语言的主要的一个关键性质,这个机制允许程序在运行时透过反射取得任何一个已知名称的class的内部信息,包括:

正在运行中的类的属性信息,正在运行中的类的方法信息,正在运行中的类的构造信息,正在运行中的类的访问修饰符,注解等等。

动态语言无时不刻在体现动态性,而静态语言也在通过其他方法来趋近于去弥补静态语言的缺陷。

为什么么要使用反射:

反射是框架设计的灵魂

  • 框架:
    半成品软件。可以在框架的基础上进行软件开发,简化编码。学习框架并不需要了解反射,但是要是想自己写一个框架,那么就要对反射机制有很深入的了解。
  • 解耦,提高程序的可扩展性
  • 在运行时判断任意一个对象所属的类。
  • 在运行时构造任意一个类的对象。
  • 在运行时判断任意一个类所具有的成员变量和方法。
  • 在运行时调用任意一个对象的方法。

什么是反射:

定义:
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。

简单来说: 将类的各个组成部分封装成其他对象

反射机制的实现原理

在这里插入图片描述
Java代码在计算机中经历的三个阶段

  • Source源代码阶段:.java被编译成.class字节码文件。
  • Class类对象阶段:*.class字节码文件被类加载器加载进内存,并将其封装成Class对象(用于在内存中描述字节码文件),Class对象将原字节码文件中的成员变量抽取出来封装成数组Field[],将原字节码文件中的构造函数抽取出来封装成数组Construction[],在将成员方法封装成Method[]。当然Class类内不止这三个,还封装了很多,我们常用的就这三个。
  • RunTime运行时阶段:创建对象的过程new。

获取Class对象的方式:

  • Class.forname(“类全名”): 将字节码加载进内存,返回Class对象。 一般用于:
    配置文件,将类名定义在配置文件中,读取文件,加载类。
  • 类名.class: 通过类名的属性Class获取 一般用于: 参数传递
  • 对象.getclass()获取: getclass()方法在Object类中定义 一般用于: 对象获取字节码的方式
  • 补充: 同一个字节码文件(*.class)在一次程序运行中,只会被加载一次,不论通过哪一种方式获取的Class对象都是同一个。
public void Main() throws ClassNotFoundException {
    //方式一:Class.forName("全类名");
    Class cls1  = Class.forName("Test.Test");  //Person自定义实体类
    System.out.println("cls1 = " + cls1);

    //方式二:类名.class
    Class cls2 = Test.class;
    System.out.println("cls2 = " + cls2);

    //方式三:对象.getClass();
    Test tst= new Test();        
    Class cls3 =tst.getClass();
    System.out.println("cls3 = " + cls3);

    // == 比较三个对象
    System.out.println("cls1 == cls2 : " + (cls1 == cls2));    //true
    System.out.println("cls1 == cls3 : " + (cls1 == cls3));    //true
    //结论:同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,无论通过哪一种方式获取的Class对象都是同一个。
}

Class对象功能

获取功能:
1 获取成员变量们

Field[] getFields() :获取所有public修饰的成员变量
Field getField(String name)   获取指定名称的 public修饰的成员变量

Field[] getDeclaredFields()  获取所有的成员变量,不考虑修饰符
Field getDeclaredField(String name)
//需要忽略访问权限修饰符的安全检查 setAccessible(true):暴力反射,不然会报错

2 获取构造方法们

Constructor<?>[] getConstructors()  
Constructor<T> getConstructor(<?>... parameterTypes)  

Constructor<?>[] getDeclaredConstructors()  
Constructor<T> getDeclaredConstructor(<?>... parameterTypes)  

3 获取成员方法们

Method[] getMethods()  
Method getMethod(String name,<?>... parameterTypes)  

Method[] getDeclaredMethods()  
Method getDeclaredMethod(String name,<?>... parameterTypes)
//需要忽略访问权限修饰符的安全检查 setAccessible(true):暴力反射,不然会报错

4 获取全类名

String getName();

getClass()方法是Object类的方法,需要注意一点获取的类名是全类名(带有路径)

Field:成员变量

设置值 void set(Object obj, Object value)
获取值 get(Object obj)

package Test;
public class Test {
	public String a;
	protected String b;
	private String c;
	String d;
}


package Test;

import java.lang.reflect.Field;

public class Reflect {
	public static void main(String[] args) throws  Exception {
		Class Tst = Test.class;
		Test tst=new Test();
		System.out.println("-------------------测试getField--------------------");
		
		Field[] fields=Tst.getFields(); 
		for(Field f:fields)
		{
			System.out.println(f);
		}
		
		Field a=Tst.getField("a");
		a.set(tst, "我是设置值");
		System.out.println(a.get(tst));
		
		System.out.println("\n-------------测试getDeclaredField------------------");
		
		Field[] fields2=Tst.getDeclaredFields();
		for(Field f:fields2)
		{
			f.setAccessible(true);//不加出不来,详情请看上文
			System.out.println(f);
		}
		
		Field b=Tst.getDeclaredField("b");
		b.set(tst, "我是私有的设置值");
		System.out.println(b.get(tst));
		
	}
}

Constructor:构造方法

创建对象:T newInstance(Object… initargs)
注意:如果使用空参数构造方法创建对象,操作可以简化:Class对象的newInstance方法
作用就是用它来创建对象

package Test;

public class Test {
	public String a;
	//构造方法
	public Test( ) {}
	public Test(String a) {
		this.a = a;
	}
	 
}

package Test;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;

public class Reflect {
	public static void main(String[] args) throws Exception {
		Class Tst = Test.class;
		Constructor[] constructors = Tst.getConstructors();
		for (Constructor constructor : constructors) { // Constructor 对象reflect包下的 import java.lang.reflect.Constructor;
			System.out.println(constructor);
		}
		System.out.println("------------------无参构造函数创建对象----------------------");
		Constructor cst=Tst.getConstructor(); //获得无参构造函数
		System.out.println(cst);
		Object test=cst.newInstance(); //利用无参构造函数创建对象
		System.out.println(test);
		
		System.out.println("------------------有参构造函数创建对象----------------------");
		Constructor cst2=Tst.getConstructor(String.class); //获得有参构造函数
		System.out.println(cst2);
		Object test2=cst2.newInstance("张3");//利用有参构造函数创建对象
		System.out.println(test2);
		
		System.out.println("------------------基于Class创建对象----------------------");

		Object test3=Tst.newInstance();
		//只能用于无参构函数,而且已经被弃用,不建议使用
		System.out.println(test3);

	}
}

getDeclaredConstructor方法可以获取到任何访问权限的构造器,而getConstructor方法只能获取public修饰的构造器。
此外在构造器的对象内也有setAccessible(true);方法,并设置成true就可以操作了。

Method:方法对象

执行方法:Object invoke(Object obj, Object… args)
获取方法名称:String getName();

package Test;

public class Test {
	public String a;

	// 构造方法
	public Test() {
	}

	public Test(String a) {
		this.a = a;
	}

	public void do_Something()	{
		System.out.println("吃饭睡觉打豆豆");
	}
	public String do_Something(String s)	{
		System.out.println("吃饭睡觉打"+s);
		return "爽";
	}
}
package Test;

import java.lang.reflect.Method;

public class Reflect {
	public static void main(String[] args) throws Exception {
		Class Tst = Test.class;
		Test test = new Test();

		System.out.println("-----------------无参测试----------------------");
		Method mtd1 = Tst.getMethod("do_Something");// 获得无参的方法
		Object return_Value = mtd1.invoke(test); // 调用方法
		// 有返回值就得到一个值,没有就得到一个null
		System.out.println(return_Value);

		System.out.println("-----------------含参测试----------------------");
		Method mtd2 = Tst.getMethod("do_Something", String.class);// 获得无参的方法
		Object return_Value2 = mtd2.invoke(test, "张三"); // 调用方法
		// 有返回值就得到一个值,没有就得到一个null
		System.out.println(return_Value2);
	}
}

案例

  • 需求:写一个"框架",不能改变该类的任何代码的前提下,可以帮我们创建任意类的对象,并且执行其中任意方法

  • 实现:
    1. 配置文件
    2. 反射

  • 步骤:
    1. 将需要创建的对象的全类名和需要执行的方法定义在配置文件中
    2. 在程序中加载读取配置文件
    3. 使用反射技术来加载类文件进内存
    4. 创建对象
    5. 执行方法

//配置文件
className=cn.itcast.domain.Student   //可更改类名
methodName=sleep    //可更改方法名
package cn.itcast.reflect;

import cn.itcast.domain.Person;
import cn.itcast.domain.Student;

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

/**
 * 框架类
 */
public class ReflectTest {
    public static void main(String[] args) throws Exception {
        //可以创建任意类的对象,可以执行任意方法

        /*
            前提:不能改变该类的任何代码。可以创建任意类的对象,可以执行任意方法
         */
      /*  Person p = new Person();
        p.eat();*/
/*
        Student stu = new Student();
        stu.sleep();*/

        //1.加载配置文件
        //1.1创建Properties对象
        Properties pro = new Properties();
        //1.2加载配置文件,转换为一个集合
        //1.2.1获取class目录下的配置文件
        ClassLoader classLoader = ReflectTest.class.getClassLoader();
        InputStream is = classLoader.getResourceAsStream("pro.properties");
        pro.load(is);

        //2.获取配置文件中定义的数据
        String className = pro.getProperty("className");
        String methodName = pro.getProperty("methodName");


        //3.加载该类进内存
        Class cls = Class.forName(className);
        //4.创建对象
        Object obj = cls.newInstance();
        //5.获取方法对象
        Method method = cls.getMethod(methodName);
        //6.执行方法
        method.invoke(obj);


    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值