java基础之反射

1.什么是反射

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

2.反射的原理

创建Class对象时,JVM先从磁盘找到Class文件,将其加载到jvm内存,创建Class对象空间,并创建其相关类信息。

反射的本质是得到Class对象后,反向获取Class对象的各种信息。

3.反射的优缺点及作用

优点:

使用反射,我们就可以在运行时获得类的各种内容,进行反编译,对于Java这种先编译再运行的语言,能够让我们很方便的创建灵活的代码,这些代码可以在运行时装配,无需在组件之间进行源代码的链接,更加容易实现面向对象。

缺点:

(1)反射会消耗一定的系统资源,因此,如果不需要动态地创建一个对象,那么就不需要用反射;

(2)反射调用方法时可以忽略权限检查,因此可能会破坏封装性而导致安全问题。

作用:

1,在运行时判断任意一个对象所属的类;

2,在运行时构造任意一个类的对象;

3,在运行时判断任意一个类所具有的成员变量和方法;

4,在运行时调用任意一个对象的方法;

5,生成动态代理。

 

获得构造函数的方法 
    Constructor getConstructor(Class[] params)//根据指定参数获得public构造器

    Constructor[] getConstructors()//获得public的所有构造器

    Constructor getDeclaredConstructor(Class[] params)//根据指定参数获得public和非public的构造器

    Constructor[] getDeclaredConstructors()//获得public的所有构造器 


获得类方法的方法 
    Method getMethod(String name, Class[] params),根据方法名,参数类型获得方法

    Method[] getMethods()//获得所有的public方法

    Method getDeclaredMethod(String name, Class[] params)//根据方法名和参数类型,获得public和非public的方法

    Method[] getDeclaredMethods()//获得所以的public和非public方法 


获得类中属性的方法 
    Field getField(String name)//根据变量名得到相应的public变量

    Field[] getFields()//获得类中所以public的方法

    Field getDeclaredField(String name)//根据方法名获得public和非public变量

    Field[] getDeclaredFields()//获得类中所有的public和非public方法 

 

4.反射常用到的类

java.lang.Class;

java.lang.reflect.Constructor; Constructor 提供关于类的单个构造方法的信息以及对它的访问权限。

java.lang.reflect.Field; Field 提供有关类或接口的单个字段的信息,以及对它的动态访问权限。

java.lang.reflect.Method;  Method 提供关于类或接口上单独某个方法(以及如何访问该方法)的信息。

java.lang.reflect.Modifier; Modifier 类提供了 static 方法和常量,对类和成员访问修饰符进行解码。

 

5.反射的使用

公用类UserInfo

public class UserInfo {  
      
    private Integer id;  
    public String userName;  
    private String password;
    public UserInfo() {
	}
    public UserInfo(Integer id) {
		this.id = id;
	}
    public UserInfo(String userName) {
		this.userName = userName;
	}
    public UserInfo(Integer id, String userName) {
		this.id = id;
		this.userName = userName;
	}
    protected UserInfo(String userName, String password) {
		this.userName = userName;
		this.password = password;
	}
	public UserInfo(Integer id, String userName, String password) {
		this.id = id;
		this.userName = userName;
		this.password = password;
	}
	public Integer getId() {  
        return id;  
    }  
    public void setId(Integer id) {  
        this.id = id;  
    }  
    public String getUserName() {  
        return userName;  
    }  
    public void setUserName(String userName) {  
        this.userName = userName;  
    }  
    public String getPassword() {  
        return password;  
    }  
    public void setPassword(String password) {  
        this.password = password;  
    }  
      
    @Override  
    public String toString(){
        return this.getClass().getName();  
    }  
      
} 

获取Class对象的三种方式

//1.通过类实例对象的getClass()方法获取Class;Object -> getClass()方法,如:public final native Class<?> getClass();
UserInfo user = new UserInfo();
Class userInfoClass = user.getClass();
System.out.println("类名:"+userInfoClass.toString());

//2.通过类的Class属性获取Class;任何数据类型(包括基本数据类型)都有一个"静态"的class属性;
userInfoClass = UserInfo.class;
System.out.println("类名:"+userInfoClass.toString());

//3.通过Class类的静态方法forName(String className); className 为所需类的完全限定名,如:com.zyf.eflying.reflect.UserInfo
userInfoClass = Class.forName("com.zyf.eflying.reflect.UserInfo");
System.out.println("类名:"+userInfoClass.toString());

//注意,在运行期间,一个类,只有一个Class对象产生;
//三种方式中,常用第三种,第一种对象都有了还要反射干什么,第二种需要导入类包,依赖太强,不导包就抛编译错误。
//一般都使用第三种,一个字符串可以传入也可以写在配置文件中等多种方法。

//运行结果
//类名:class com.zyf.eflying.reflect.UserInfo
//类名:class com.zyf.eflying.reflect.UserInfo
//类名:class com.zyf.eflying.reflect.UserInfo


/**
 * 通过Class对象获取父类
 */
Class superClass = userInfoClass.getSuperclass();
System.out.println("父类名:"+superClass.toString());

/**
 * 判断是否为某个类的实例
 */
System.out.println(userInfoClass.isInstance(user));
System.out.println("" instanceof String);

通过反射获取实例对象

//使用Class对象的newInstance()方法来创建Class对象对应类的实例。
Class<UserInfo> c = UserInfo.class;
Object userInfo = c.newInstance();
System.out.println(userInfo.toString());

//输出结果
com.zyf.eflying.reflect.UserInfo

获取构造方法

//先通过Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来创建实例对象,这种方法可以用指定的构造器构造类的实例。
//获取公有、无参的构造方法,并通过构造方法获取实例对象
Constructor<UserInfo> con = c.getConstructor(null);//null即使用无惨构造函数
Object u = con.newInstance();
System.out.println(u);

//获取公有、Integer类型的构造方法,并通过构造方法获取实例对象
Constructor<UserInfo> con1 = c.getConstructor(Integer.class);//getConstructor()的参数为构造方法的参数的Class对象
Object u1 = con1.newInstance(1);
System.out.println(u1);

//获取所有公有构造方法
Class<UserInfo> clazz = UserInfo.class;
Constructor[] conArray = clazz.getConstructors();
for(Constructor cons : conArray){
	System.out.println(cons);
}

//获取所有的构造方法(包括:私有、受保护、默认、公有)
conArray = clazz.getDeclaredConstructors();
for(Constructor cons : conArray){
	System.out.println(cons);
}

//获取公有的、三个参数的构造方法
con = clazz.getConstructor(Integer.class,String.class,String.class);
//1>、因为是三个参数的构造方法所以类型是Integer.class,String.class,String.class;如果是无参的可以写成null,不写也可以:这里需要的是参数的class类型,切记是类型
//2>、返回的是这个三个参数构造函数。
System.out.println("con = " + con);
//创建指定三个参数的对象
Object o = con.newInstance(1,"1","1");
System.out.println(o);

//获取两个参数的私有的构造方法
con = clazz.getDeclaredConstructor(String.class,String.class);
System.out.println("con = " + con);
//创建对象
Object o1 = con.newInstance("1","1");
System.out.println(o1);

输出结果: 

com.zyf.eflying.reflect.UserInfo
com.zyf.eflying.reflect.UserInfo
**********************所有公有构造方法*********************************
public com.zyf.eflying.reflect.UserInfo(java.lang.Integer,java.lang.String,java.lang.String)
public com.zyf.eflying.reflect.UserInfo(java.lang.Integer,java.lang.String)
public com.zyf.eflying.reflect.UserInfo()
public com.zyf.eflying.reflect.UserInfo(java.lang.Integer)
public com.zyf.eflying.reflect.UserInfo(java.lang.String)
************所有的构造方法(包括:私有、受保护、默认、公有)***************
public com.zyf.eflying.reflect.UserInfo(java.lang.Integer,java.lang.String,java.lang.String)
protected com.zyf.eflying.reflect.UserInfo(java.lang.String,java.lang.String)
public com.zyf.eflying.reflect.UserInfo(java.lang.Integer,java.lang.String)
public com.zyf.eflying.reflect.UserInfo()
public com.zyf.eflying.reflect.UserInfo(java.lang.Integer)
public com.zyf.eflying.reflect.UserInfo(java.lang.String)
**********************获取公有的、三个参数的构造方法*********************************
con = public com.zyf.eflying.reflect.UserInfo(java.lang.Integer,java.lang.String,java.lang.String)
com.zyf.eflying.reflect.UserInfo
**********************获取私有的构造方法*********************************
con = protected com.zyf.eflying.reflect.UserInfo(java.lang.String,java.lang.String)
com.zyf.eflying.reflect.UserInfo

获取对象的字段

Class<UserInfo> c = UserInfo.class;
//获取所有公有的字段
Field[] fieldArray = c.getFields();
for(Field f : fieldArray){
	System.out.println(f);
}

//获取所有的字段(包括私有、受保护、默认的)
fieldArray = c.getDeclaredFields();
for(Field f : fieldArray){
	System.out.println(f);
}

//获取指定名称的公有字段**并调用
Field f = c.getField("userName");
System.out.println(f);

//获取一个对象
Object obj = c.getConstructor().newInstance();//产生Student对象--》Student stu = new Student();
//为字段设置值
f.set(obj, "刘德华");//为Student对象中的name属性赋值--》stu.name = "刘德华"

//验证
UserInfo stu = (UserInfo)obj;
System.out.println("验证姓名:" + stu.userName);


//获取指定名称的私有字段****并调用
f = c.getDeclaredField("id");
System.out.println(f);
f.setAccessible(true);//暴力反射,解除私有限定
f.set(obj, 1);
System.out.println("验证id:" + stu.getId());

输出结果: 

public java.lang.String com.zyf.eflying.reflect.UserInfo.userName
************获取所有的字段(包括私有、受保护、默认的)********************
private java.lang.Integer com.zyf.eflying.reflect.UserInfo.id
public java.lang.String com.zyf.eflying.reflect.UserInfo.userName
private java.lang.String com.zyf.eflying.reflect.UserInfo.password
*************获取公有字段**并调用***********************************
public java.lang.String com.zyf.eflying.reflect.UserInfo.userName
验证姓名:刘德华
**************获取私有字段****并调用********************************
private java.lang.Integer com.zyf.eflying.reflect.UserInfo.id
验证id:1

获取方法

//1.获取Class对象
Class uClass = Class.forName("com.zyf.eflying.reflect.UserInfo");
//2.获取所有公有方法
System.out.println("***************获取所有的”公有“方法*******************");
uClass.getMethods();
Method[] methodArray = uClass.getMethods();
for(Method m : methodArray){
	System.out.println(m);
}
System.out.println("***************获取所有的方法,包括私有的*******************");
methodArray = uClass.getDeclaredMethods();
for(Method m : methodArray){
	System.out.println(m);
}
System.out.println("***************获取公有的setUserName()方法*******************");
Method m = uClass.getMethod("setUserName", String.class);
System.out.println(m);
//实例化一个Student对象
Object obj1 = uClass.getConstructor().newInstance();
m.invoke(obj1, "刘德华");

System.out.println("***************获取私有的getName()方法******************");
m = uClass.getDeclaredMethod("getName", String.class);
System.out.println(m);
m.setAccessible(true);//解除私有限定
Object result = m.invoke(obj1, "张三");//需要两个参数,一个是要调用的对象(获取有反射),一个是实参
System.out.println("返回值:" + result);

输出结果:

***************获取所有的”公有“方法*******************
public java.lang.String com.zyf.eflying.reflect.UserInfo.toString()
public java.lang.Integer com.zyf.eflying.reflect.UserInfo.getId()
public void com.zyf.eflying.reflect.UserInfo.setUserName(java.lang.String)
public void com.zyf.eflying.reflect.UserInfo.setId(java.lang.Integer)
public java.lang.String com.zyf.eflying.reflect.UserInfo.getPassword()
public void com.zyf.eflying.reflect.UserInfo.setPassword(java.lang.String)
public java.lang.String com.zyf.eflying.reflect.UserInfo.getUserName()
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()
***************获取所有的方法,包括私有的*******************
public java.lang.String com.zyf.eflying.reflect.UserInfo.toString()
private java.lang.String com.zyf.eflying.reflect.UserInfo.getName(java.lang.String)
public java.lang.Integer com.zyf.eflying.reflect.UserInfo.getId()
public void com.zyf.eflying.reflect.UserInfo.setUserName(java.lang.String)
public void com.zyf.eflying.reflect.UserInfo.setId(java.lang.Integer)
public java.lang.String com.zyf.eflying.reflect.UserInfo.getPassword()
public void com.zyf.eflying.reflect.UserInfo.setPassword(java.lang.String)
public java.lang.String com.zyf.eflying.reflect.UserInfo.getUserName()
***************获取公有的setUserName()方法*******************
public void com.zyf.eflying.reflect.UserInfo.setUserName(java.lang.String)
***************获取私有的getName()方法******************
private java.lang.String com.zyf.eflying.reflect.UserInfo.getName(java.lang.String)
返回值:张三

反射main方法

public class Student {
	public static void main(String[] args) {
		System.out.println("main方法执行了。。。");
	}
}

测试类

public class Main {
	
	public static void main(String[] args) {
		try {
			//1、获取Student对象的字节码
			Class clazz = Class.forName("com.zyf.eflying.reflect.Student");
			
			//2、获取main方法
			Method methodMain = clazz.getMethod("main", String[].class);//第一个参数:方法名称,第二个参数:方法形参的类型

			//3、调用main方法
			//methodMain.invoke(null, new String[]{"a","b","c"});
			//第一个参数,对象类型,因为方法是static静态的,所以为null可以,第二个参数是String数组,这里要注意在jdk1.4时是数组,jdk1.5之后是可变参数
			//这里拆的时候将  new String[]{"a","b","c"} 拆成3个对象。。。所以需要将它强转。
			methodMain.invoke(null, (Object)new String[]{"a","b","c"});//方式一
			// methodMain.invoke(null, new Object[]{new String[]{"a","b","c"}});//方式二			
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

输出结果:

main方法执行了。。。

利用反射创建数组

package com.zyf.eflying.reflect;

import java.lang.reflect.Method;
import java.util.ArrayList;

public class Demo {
	public static void main(String[] args) throws Exception{
		ArrayList<String> strList = new ArrayList<>();
		strList.add("aaa");
		strList.add("bbb");
		
		//strList.add(100);
		//获取ArrayList的Class对象,反向的调用add()方法,添加数据
		Class listClass = strList.getClass(); //得到 strList 对象的字节码 对象
		//获取add()方法
		Method m = listClass.getMethod("add", Object.class);
		//调用add()方法
		m.invoke(strList, 100);
		
		//遍历集合
		for(Object obj : strList){
			System.out.println(obj);
		}
	}
}

通过反射越过泛型检查

package com.zyf.eflying.reflect;

import java.lang.reflect.Method;
import java.util.ArrayList;

/*
 * 通过反射越过泛型检查
 * 例如:有一个String泛型的集合,怎样能向这个集合中添加一个Integer类型的值?
 */
public class Demo1 {
	public static void main(String[] args) throws Exception{
		ArrayList<String> strList = new ArrayList<>();
		strList.add("aaa");
		strList.add("bbb");
		
		//strList.add(100);
		//获取ArrayList的Class对象,反向的调用add()方法,添加数据
		Class listClass = strList.getClass(); //得到 strList 对象的字节码 对象
		//获取add()方法
		Method m = listClass.getMethod("add", Object.class);
		//调用add()方法
		m.invoke(strList, 100);
		
		//遍历集合
		for(Object obj : strList){
			System.out.println(obj);
		}
	}
}

控制台输出:

aaa
bbb
100

利用反射运行配置文件内容

package com.zyf.eflying.reflect;
public class Student {
	public void show(){
		System.out.println("is show()");
	}
}

配置文件

className = com.zyf.eflying.reflect.Student
methodName = show

测试类

package com.zyf.eflying.reflect;

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

/*
 * 我们利用反射和配置文件,可以使:应用程序更新时,对源码无需进行任何修改
 * 我们只需要将新类发送给客户端,并修改配置文件即可
 */
public class Demo2 {
	public static void main(String[] args) throws Exception {
		//通过反射获取Class对象
		Class stuClass = Class.forName(getValue("className"));//"cn.fanshe.Student"
		//2获取show()方法
		Method m = stuClass.getMethod(getValue("methodName"));//show
		//3.调用show()方法
		m.invoke(stuClass.getConstructor().newInstance());
		
	}
	
	//此方法接收一个key,在配置文件中获取相应的value
	public static String getValue(String key) throws IOException{
		Properties pro = new Properties();//获取配置文件的对象
		FileReader in = new FileReader("D:\\workspace\\eclipse\\vk\\zyf-mongodb\\src\\main\\java\\com\\zyf\\eflying\\reflect\\pro.txt");//获取输入流
		pro.load(in);//将流加载到配置文件对象中
		in.close();
		return pro.getProperty(key);//返回根据key获取的value值
	}

}

控制台输出

is show() 

 

需求:

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

package com.zyf.eflying.reflect;
public class Student2 {
	public void show2(){
		System.out.println("is show2()");
	}
}

配置文件更改:

className = com.zyf.eflying.reflect.Student2
methodName = show2

 输出结果:

is show2()

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值