Java反射

简介

反射是框架设计的灵魂 (使用的前提条件:必须先得到代表的字节码的Class,Class类用于表示.class文件(字节码))

  1. JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
  2. 反射是通过Class对象(字节码文件),来知道某个类的所有属性和方法。也就是说通过反射我们可以获取构造器,对象,属性,方法(原本不知道)
  3. 要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象

Java的四大机制

  1. java反射机制
  2. 垃圾回收机制
  3. 异常处理机制
  4. 事件处理机制

java反射过程

在这里插入图片描述

获取Class对象的三种方式

要想使用反射,必须先得到代表的字节码的Class对象,Class类用于表示.class文件(字节码)

1.通过该类的对象去获取到对应的Class对象(基本不用它)

//第一种方式: student--->Class对象  通过getClass()方法
//Student是一个空类
Student student = new Student();
//这里我们就省去泛型了,Class<?>
Class stuClass = student.getClass();  //获取到了对应的Class对象

System.out.println(stuClass);
System.out.println(stuClass.getName());  //获取Class对象的名字
输出:
class fanshe.Student
fanshe.Student

但是需要注意的是,第一种我们基本不用,这里显然和反射机制相悖(你有类对象 student 还去用反射获取Class类对象干嘛,多此一举)

2.通过类名.class静态属性获取(比较简单)

//第二种方式:  每个类创建后 都会有一个默认的静态的class属性 用于返回该类的class对象
//需要注意的是:  任何数据类型(包括基本数据类型)都有“静态”的class属性
Class stuClass2 = Student.class;
System.out.println("是否为同一个class对象?"+(stuClass==stuClass2));
结果:true

这里需要注意的是,这种方式虽然比较简单,但是需要导包,不然会编译错误(对比第三种,全限定类名方式)

3.通过Class类中的静态方法 forName()方法获取(最为常见)

//第三种方式:  Class.forName("fanshe.Student");  注意参数一定为该类的全限定类名
try {
	Class stuClass3 = Class.forName("fanshe.Student");
	//System.out.println(stuClass3); 输出仍然是class fanshe.Student
	System.out.println("是否为同一个class对象?"+(stuClass3==stuClass2));
} catch (ClassNotFoundException e) {
	e.printStackTrace();
}
结果:true

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

根据反射入口对象(class)获取类的各种信息

  1. getConstructors() 获取所有的构造方法

Constructor<?>[] con=cls.getConstructors();

  1. getSuperclass() 获取父类

Class<?> sup=cls.getSuperclass();

  1. 获取当前类(只有本类的)的所有方法和属性,包括私有的

Method[] ms = cls.getDeclaredMethods(); //获取当前类的所有方法
Field[] fs = cls.getDeclaredFields(); //所有属性

  1. getMethods() 获取此类的所有public方法(父类的,实现接口的,自己的)
	try {
		Class<?> cls = Class.forName("entity.User");
		Method[] ms=cls.getMethods();
			for(Method m:ms) {  //遍历所有方法
				System.out.println(m.getName());
			}
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
  1. 通过获取到的构造器创建对象:
//这里我们需要捕获一下异常
//因为我们的构造器是私有的,不能在其他类去创建,所以我们用了setAccessible()这个方法,从而可以创建
/*
private Student(String name,int age,boolean sex){
		System.out.println("用private修饰的Student的含有三个参数的构造器:"+name+age+sex);
}*/
try {
	con2.setAccessible(true);   //忽略构造器的访问修饰符,解除私有限定
	Object object = con2.newInstance("张三",10,true);  //这句话就相当于new Student("张三",10,true);
	Student student = (Student) object;  //指向子类对象的父类引用重新转为子类引用
} catch (Exception e) {
	e.printStackTrace();
} 

//输出:用private修饰的Student的含有三个参数的构造器:张三10true

注意:​ xxx.setAccessible(true) 是为了解除私有限定

通过反射获取对象的实例,并操作对象

  1. class.newInstance() ,并强转类型,然后就可以操作对象了,主要是调用方法。
try{
	Class<?> cls = Class.forName("entity.User");
	User user = (User) cls.newInstance();
	user.setUsername("zs");
	user.setSax("男");
	System.out.println(user.getUsername()+"\t"+user.getSex());
} catch (ClassNotFoundException){
	e.printStackTrace();
}
  1. 操作属性,可以操作类里面的public属性和private属性
    如果属性是private,正常情况下是不允许外界操作属性值,这里可以用Field类的setAccessible(true)方法,暂时打开操作的权限
    在这里插入图片描述
  2. 调用方法也一样,可以调用私有的方法,null是因为这个方法没有参数
    在这里插入图片描述

invoke方法

public class TestClassLoad {  
    public static void main(String[] args) throws Exception {  
        Class<?> clz = Class.forName("A");  
        Object o = clz.newInstance();  
        Method m = clz.getDeclaredMethod("hello", null);  
        m.invoke(o);   
    }
    static class A{
        public void hello() {
            System.out.println("hello world");
        }
    }  
}  

上面就是最常见的反射使用的例子,前两行实现了类的装载、链接和初始化(newInstance方法实际上也是使用反射调用了方法),后两行实现了从class对象中获取到method对象然后执行反射调用。下面简单分析一下后两行的原理。

设想一下,如果想要实现method.invoke(action,null)调用action对象的myMethod方法,只需要实现这样一个Method类即可:

Class Method{
     public Object invoke(Object obj,Object[] param){
        A instance=(A)obj;
        return instance.foo();
     }
}

反射的原理之一其实就是动态的生成类似于上述的字节码,加载到jvm中运行。

Java反射实例

先创建一个封装的User的对象

package entity;

public class User {
	private String username;
	private String sex;
	public String getUsername() {
		return username;
	}
	public void setUsername(String username) {
		this.username = username;
	}
	public String getSex() {
		return sex;
	}
	public void setSex(String sex) {
		this.sex = sex;
	}
	
	public void eat() {
		System.out.println("jdmd");
	}
}

测式类Tset,使用java反射

package test;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import entity.User;

public class Test {
	public static void main(String[] args) {
		try {
			Class<?> cls = Class.forName("entity.User"); //注意此字符串必须是真实路径,就是带包名的类路径,包名.类名
			
			System.out.println(cls.getName());	//获取包名的类路径名
			
			System.out.println(cls.getClass());	//获取包的类型
			
			//获取所有属性
			Field[] fs = cls.getDeclaredFields();
			for(Field f: fs) {
				System.out.println(f.getName());
			}
			
			//获取所有方法,并且包括父类的方法
			Method[] ms=cls.getMethods();
			for(Method m:ms) {
				System.out.println(m.getName());
			}
			
			//通过反射获取公开方法并使用
			Object object = cls.newInstance();
			Method m  = cls.getDeclaredMethod("eat", null);
			m.invoke(object);
			
			//通过反射获取构造方法并使用
			User obj = (User) cls.newInstance();
			Method method = cls.getDeclaredMethod("setUsername", java.lang.String.class);
			method.invoke(obj,"aaa");
			System.out.println(obj.getUsername());
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}  catch (SecurityException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InstantiationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} 
	}
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值