Java之了解反射机制

Java反射机制

一、概念

什么是反射?

   在运行状态中,对于任意一个类,都能够获取到这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法和属性(包括私有的方法和属性),这种动态获取的信息以及动态调用对象的方法的功能就称为java语言的反射机制。通俗点讲,通过反射,该类对我们来说是完全透明的,想要获取任何东西都可以

Class 类与 java.lang.reflect 类库一起对反射的概念进行了支持,该类库包含了 Field,Method,Constructor 类 (每个类都实现了 Member 接口)。这些类型的对象时由 JVM 在运行时创建的,用以表示未知类里对应的成员。

这样你就可以使用 Constructor 创建新的对象,用 get() 和 set() 方法读取和修改与 Field 对象关联的字段,用 invoke() 方法调用与 Method 对象关联的方法。另外,还可以调用 getFields() getMethods() 和 getConstructors() 等很便利的方法,以返回表示字段,方法,以及构造器的对象的数组。这样匿名对象的信息就能在运行时被完全确定下来,而在编译时不需要知道任何事情。
 

二、反射机制的具体使用

1.获取Class对象(三种方式)

  1. 通过使用Object中的getClass();方法来获取,但是需要先new一个对象;
  2. 任何数据类型(基本数据类型+引用数据类型)都有一个“静态”的Class属性:类名.class,但是需要import;
  3. 通过Class类的静态方法Class.forName(String className); 其中className为类的全称(带包名)。

由于前两种方法都是在知道该类的情况下获取该类的字节码对象,因此不会有异常,但是 Class.forName() 方法如果写错类的路径会报 ClassNotFoundException 的异常。

package com.misert.reflect;
class ObjectDemo{
    //定义一个类
}
public class ReflectDemo {
	public static void main(String[] args) {
		//1.第一种实现方式
		ObjectDemo od = new ObjectDemo();
		Class<?> objDemo1 = od.getClass();
		
		//2.第二种实现方式
		Class<?> objDemo2 = ObjectDemo.class;
		
		//3.第三种实现方式
		Class<?> objDemo3 = null;
		try {
			objDemo3 = Class.forName("com.misert.reflect.ObjectDemo");
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}
}

2.通过反射获取构造方法

在Java中,对象的创建是通过new+构造器,而要想通过反射来创建对象,则需要先通过反射获取到构造器,通过构造来创建对象。下面来说下如何通过反射获得构造器:

  • 单个获取:
    • Constructor<T> getConstructor(类<?>... parameterTypes);  获取单个public的构造方法;
    • Constructor<T> getDeclaredConstructor(类<?>... parameterTypes);  获取单个全权限的构造方法;
    • 对于类<?>... parameterTypes  具体写法如 String.class ,无参数时可以不写也可以写null
  • 批量获取:
    • Constructor<?>[] getConstructors();   获取所有的public的构造方法;
    • Constructor<?>[] getDeclaredConstructors();   获取所有的构造方法(全权限);
  • 通过上述方法我们可以获得一个Constructor对象,通过Constructor的newInstance(Object...initargs);来进行实例化。
//定义一个Person类
class Person{
    //public构造方法
	public Person() {
		System.out.println("public构造方法");
	}
	public Person(String name){
		System.out.println("public构造方法 ,参数为:"+ name);
	}
	//protected构造方法
	protected Person(int id) {
		System.out.println("protected构造方法 ,参数为:"+id);
	}
	//私有构造方法
	private Person(String name, int id) {
		System.out.println("private构造方法 ,参数为: " + name +" + "+id);
	}
}

具体实现操作

String classname = "com.misert.reflect.Person";
//1.加载类
Class<?> class1 = Class.forName(classname);
//Class class1 = Person.class;
//2.获得单个public构造器
System.out.println("=============获得单个构造器===============");
//获得public 构造方法
Constructor<Person> con1 = (Constructor<Person>) class1.getConstructor(String.class);
Person p1 = con1.newInstance("hello");
//获得private构造方法
Constructor<Person> con2 = (Constructor<Person>) class1.getDeclaredConstructor(String.class,int.class);
con2.setAccessible(true);// 值为true则指示反射的对象在使用时应该取消Java语言访问检查。
Person p2 = con2.newInstance("java Reflect",10); //创建实例
System.out.println("=============获得所有的构造器===============");
//获得所有的public的构造方法
Constructor[] conArray1 = class1.getConstructors();
System.out.println("总共有多少个public构造器"+conArray1.length);
//获得所有的构造方法
Constructor[] conArray2 = class1.getDeclaredConstructors();
System.out.println("总共有多少个构造器"+conArray2.length);

注意:通过Constructor访问private构造方法进行newInstance()时,需要先将setAccessible(true);值为true则表示反射的对象在使用时应该取消Java语言访问检查。

Class类中的newInstance()与Constructor中的newInstance()的区别:Class类中的newInstance()只能调用public的无参数的构造方法,而Constructor中的则可以调用所有的构造方法。

3.通过反射获得成员变量

  • 单个获取:
    • Field<T> getField(String name);  获取一个public的名为name的成员变量(字段);
    • Field<T> getDeclaredField(String name);  获取单个全权限的名为name的成员变量;
  • 批量获取:
    • Field<T>[] getFields();  获所有的public的成员变量(字段);
    • Field<T>[] getDeclaredFields();  获取所有的成员变量;
class Person{
	//定义字段
	private String name ;
	public String title ;
	public String text;
	protected int id;
	double salary;
}

通过反射获取属性获取属性:

String classname = "com.misert.reflect.Person";
//1.加载类
Class<?> class1 = Class.forName(classname);
//2.创建person对象
Person p1 = (Person)class1.newInstance();

System.out.println("=============单个获得属性 ===============");
//访问public属性
Field title = class1.getField("title");
System.out.println(title.getType());
title.set(p1, "标题");
System.out.println(title.getName()+p1.title);

//访问private属性
Field name = class1.getDeclaredField("name");
System.out.println("name字段: "+name.getType());
name.setAccessible(true);
name.set(p1, "张三");
System.out.println("p1的name字段值为:"+name.get(p1));

System.out.println("=============获得所有属性 ===============");
//所有public的字段
Field[] fs1 = class1.getFields();
for (Field field : fs1) {
	System.out.println(field.getName());
}
System.out.println("-----------------------------------");
//所有的字段
Field[] fs2 = class1.getDeclaredFields();
for (Field field : fs2) {
	System.out.println(field.getName());
}
	

输出结果:

public构造方法
=============单个获得属性 ===============
class java.lang.String
title标题
name字段: class java.lang.String
p1的name字段值为:张三
=============获得所有属性 ===============
title
text
-----------------------------------
name
title
text
id
salary

注意:

几个常用的Field类的方法

Object get(Object obj)  //返回指定对象obj的该字段的值 

void set(Object obj, Object value); //设置obj对象的当前Field字段的值为value

访问私有属性时,需要通过Field的setAccessible(true);才能访问和操作。

4.通过反射访问成员方法 

  • 单个获取:
    • Method getMethod(String name,Class<?>...paramType);  获取一个public的成员方法;
    • Method getDeclaredMethod(String name,Class<?>...paramType);  获取单个全权限的成员方法;
  • 批量获取:
    • Method[] getMethods();  获所有public的成员方法(包含所有父类的方法,如Object的方法)
    • Method[] getDeclaredMethods();  获取所有的成员方法(但不包括继承的);
class Person{ 
    //定义一些成员方法
   public void getName() {
		System.out.println("getName()");
	}
	public void getTitle(String title) {
		System.out.println("getTitle():"+ title);
	}
	private int getId() {
		return 1234;
	}
	protected void getText(String name,int id) {
		System.out.println("getText(): "+name+" , "+id);
	}
	void getSalary() {
		System.out.println("getSalary()");
	}
}

 通过方式访问:

public class ReflectTest {
	public static void main(String[] args) throws Exception{
		String classname = "com.misert.reflect.Person";
		//1.加载类
		Class<?> class1 = Class.forName(classname);
		Person p1 = (Person)class1.newInstance();
		System.out.println("=============获得单个成员方法===============");
		//获得public的成员方法
		Method m1 = class1.getMethod("getTitle", String.class);
		m1.invoke(p1, "hello");  //执行方法
		//获得private成员方法
		Method m2 = class1.getDeclaredMethod("getId", null);
		m2.setAccessible(true);
		System.out.println((int)m2.invoke(p1)+"");
		System.out.println("=============获得所有的成员方法===============");
		//获得所有public方法(包括继承来的)
		Method[] ms1 = class1.getMethods();
		for (Method method : ms1) {
			System.out.println(method.getName());
		}
		System.out.println("-----------------");
		//获得所有权限方法(不包括继承得到的)
		Method[] ms2 = class1.getDeclaredMethods();
		for (Method method : ms2) {
			System.out.println(method.getName());
		}
    }
}

同样的,在访问private成员方法时,需要通过Method设置setAccessible(true);

通过发射调用方法:Method下的 -----> public Object invoke(Object obj, Object... args);

  • obj为要调用该方法的对象,如果是static的方法,则可以为null
  • args为实参列表,如果没有参数可以不写

3. 反射的应用

 1. 通过反射运行配置文件内容

定义一个Student类

public class Student {
	public void show(){
		System.out.println("is show()");
	}
    public void show1(String name){
		System.out.println("is show1():"+name);
	}
}

写一个pro.properties配置文件:

classname=com.misert.reflect.Student
methodname=show1
param=String

测试类:通过反射读取配置文件

package com.misert.reflect;

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Properties;

public class ReflectUse {
	public static void main(String[] args) {
	    
		String classname = getValue("classname");
		String methodname = getValue("methodname");
		String paramType = getValue("param");
		System.out.println(classname+","+methodname);
		Class<?> clazz ;
		try {
			clazz = Class.forName(classname);
			Method m = clazz.getMethod(methodname,paramType.getClass() );
			m.invoke(clazz.newInstance(), "hello world !");
			
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} 
		
	}
	public static String getValue(String name) {
        //1.创建Properties对象
		Properties p = new Properties();
		try {
            //2.创建BufferedInputStreamd对象,给文件插上管道
			BufferedInputStream bin = new BufferedInputStream(
					new FileInputStream("src/com/misert/reflect/doc/pro.properties"));
            //3.load配置文件
			p.load(bin);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
        //4.根据key值查询并返回value
		return p.getProperty(name);
	}
}

 需求:

当我们升级这个系统时,不要Student类,而需要新写一个Person的类时,这时只需要更改的pro.properties文件内容就可以了。代码就一点不用改动。

   

关于Class.forName()进一步了解可以参考:https://www.cnblogs.com/beast-king/p/6807257.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值