欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

反射学习笔记

程序员文章站 2024-03-23 14:24:52
...

反射学习笔记

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

获取Class对象

​ java文件编译后生成的class文件(字节码文件)是唯一的,所有的对象都是通过class文件来创建。一个java文件对应一个字节码文件,一个字节码文件(后缀名为class的文件)对应一个Class对象。

​ 有三种方式可以获取到一个类的Class对象,通过使用Class对象,可以获取到类的相关信息(例如类的构造器、方法和属性)。Class本身也是一个类型,Class提供了一系列的方法来获取类的相关信息。

​ 所有的引用数据类型都具备一个class属性,所有的对象都有一个getClass()方法,对于基本数据类型也都有一个class属性(例如 int.class)。

方式一:

​ 所有对象都可以调用继承自Object类的getClass()方法,该方法返回对象对应的Class对象。

代码演示:

//p代表一个Person类的对象		
Person p = new Person();
//调用对象p的getClass方法,获取到一个Class对象,用cls接收
Class cls = p.getClass();


同一个类的对象,对应的Class对象是一致的:

	public static void main(String[] args)  {
		//获取p1的class对象
		Person p1 = new Person();
		Class cls1 = p1.getClass();
		//获取p2的class对象		
		Person p2 = new Person();
		Class cls2 = p2.getClass();
		//同一个类只有一个class文件,class文件所对应的类型就是Class
		System.out.println(cls1 == cls2);
	}
/*程序运行结果
true
*/

方式二:

每一个类都具有一个.class静态属性,用对象调用class属性获取到类的Class对象

	public static void main(String[] args)  {
    	//类名.class 得到一个Class对象
		Class cls1 = Person.class;
		Class cls2 = Person.class;
        //比较两次获得的Class对象是否相同
		System.out.println(cls1 == cls2);
	}
/*程序运行结果
true
*/

方式三:

​ 前两种方式都是在已知类名的情况下获得Class对象,使用的前提是类中有对应的java文件,并不常用。
​ 通常情况下在使用第三方的jar包时,能知道的仅仅是通过build path添加到功能的class文件的路径及名称。

​ 这时候需要用到Class类中的静态方法forName()

​ Class.forName()返回的是一个Class对象。
​ Class.forName()的作用是使JVM查找指定路径类的class文件,然后将class文件读入内存,为该类生成一个class对象作为访问类型信息的入口。

public static void main(String[] args) throws ClassNotFoundException  {   	
		//获取路径下的Person.class文件,返回一个该class文件对应的Class对象,如果找不到Person.class文件,则会出现ClassNotFoundException,因此要抛出异常
		Class cls1 = Class.forName("cn.study.reflex.Person");
		
    	//使用前两种方式获取Person的Class对象
		Person p = new Person();
		Class cls2 = p.getClass();		
		Class cls3 = Person.class;
		
		//测试获取到的Class对象是否和另外两个方式获取到的一样
		System.out.println(cls1 == cls2 );
		System.out.println(cls1 == cls3);
}
/*程序运行结果
true
true
*/

通过反射获取类的构造器

1.获取构造器:
1).批量获取构造器:
public Constructor[] getConstructors():获取所有"公有的"构造器。
public Constructor[] getDeclaredConstructors():获取所有的构造器(包括私有、受保护、默认、公有)

​ 2).单个获取构造器:
​ public Constructor getConstructor(Class… parameterTypes):获取单个的"公有的"构造器。
​ public Constructor getDeclaredConstructor(Class… parameterTypes):获取"某个构造器"可以是私有的,或受保护、默认、公有。

2.调用构造器:
Constructor–>newInstance(Object… initargs):实例化一个对象。

获取单个构造器

用到的方法:

​ getConstructor() :获取单个的"公有的"构造器,获取到的具体某个构造器由传入的参数决定,参数是类型的Class对象,如果没有与参数匹配的构造器,则会出现异常,要捕获或抛出异常。

​ newInstance():实例化一个对象。

​ getDeclaredConstructor():特性和上面的getConstructor() 方法差不多,区别在于获取的构造器可以是私有的,受保护的、默认的、公有的。

代码演示:

/**
 * 一个用于测试的类
 */
public class Person {
		
		private int  age;
		
		private String name;
				
		//公有无参构造器
		public Person() {
			System.out.println("公有无参构造器执行");
		}
		//公有带参构造器
		public Person(int age, String name) {
			super();
			this.age = age;
			this.name = name;
			System.out.println("公有带参构造器执行--age:"+age+",name:"+name);
		}
				
		//私有的构造器
		private Person(int age) {
			this.age = age;
			System.out.println("私有构造器执行--age:"+age);
		}
		
		//受保护的构造器
		protected Person(String name) {
			this.name = name;
			System.out.println("受保护构造器执行--name:"+name);
		}
		
}
	/**
	 * 获取单个构造器演示
	 * @throws Exception
	 */	
public static void main(String[] args) throws Exception  {
			
		//获取指定路径的Person.class文件对应的Class对象
		Class cls = Class.forName("cn.yunhe.study.Person");
		
		//没有参数所以获取的是类中的无参公有构造器,获取到的构造器由Constructor类型变量的接收
		Constructor con1 =  cls.getConstructor();
		//该语句获取的是类的带参公有构造器,这里的异常处理是向上抛出异常
		Constructor con2 =  cls.getConstructor(int.class,String.class);
		
		//获取"私有的"构造器
		Constructor con3 = cls.getDeclaredConstructor(int.class);
		//获取"受保护的"构造器
		Constructor con4 = cls.getDeclaredConstructor(String.class);
		
		//newInstance()方法可以调用构造器实例化一个对象,以下是通过不同的构造器创建对象
		Person p1 = (Person) con1.newInstance();
		Person p2 = (Person) con2.newInstance(12,"李四");
		System.out.println(con3);//私有的构造器
		Person p4 = (Person) con4.newInstance("王五");
	
}
/*程序运行结果
公有无参构造器执行
公有带参构造器执行--age:12,name:李四
private cn.study.reflex.Person(int)
受保护构造器执行--name:王五
*/

批量获取构造器

用到的方法:

​ public Constructor[] getConstructors():获取所有"公有的"构造器,返回一个所有公有构造器组成的Constructor类型的数组。
​ public Constructor[] getDeclaredConstructors():获取一个所有的构造器(包括私有、受保护、默认、公有) 组成的Constructor类型的数组。

代码演示:

	/**
	 * 批量获取构造器演示
	 * @throws Exception
	 */
	public static void main(String[] args) throws Exception  {
		//获取指定路径的Person.class文件对应的Class对象
		Class cls = Class.forName("cn.study.reflex.Person");
		
		//获取所有的公有构造器
		Constructor[] con1 = cls.getConstructors();
		//获取所有的构造器
		Constructor[] con2 = cls.getDeclaredConstructors();
	
		//遍历公有构造器的数组
		for(Constructor con : con1) {
			System.out.println(con);
		}
		System.out.println("---------------------------");
		//遍历所有构造器的数组
		for(Constructor con : con2) {
			System.out.println(con);
		}
	}
/*程序运行结果
public cn.study.reflex.Person(int,java.lang.String)
public cn.study.reflex.Person()
---------------------------
protected cn.study.reflex.Person(java.lang.String)
private cn.study.reflex.Person(int)
public cn.study.reflex.Person(int,java.lang.String)
public cn.study.reflex.Person()
*/

通过反射获取类中方法

获取类中方法需要用到的:

1.获取方法

1)批量获取:
public Method[] getMethods()

​ 获取所有"公有方法"(包含了父类的方法也包含Object类)
​ public Method[] getDeclaredMethods()

​ 获取所有的成员方法,包括私有的(不包括继承的)
​ 2)单个获取:
​ public Method getMethod(String name,Class<?>… parameterTypes)

​ 获得该类某个公有的方法。参数: “name” 是方法名,"Class … " 是方法形参的Class类型对象。
​ public Method getDeclaredMethod(String name,Class<?>… parameterTypes)

​ 获得该类某个方法

2.调用方法:
Method --> public Object invoke(Object obj,Object… args):
参数说明:
obj : 要调用方法的对象;
args:调用方式时所传递的实参;

部分代码演示:

/**
 * 一个用于测试的类
 */
public class Person {
		
		private int  age;
		
		private String name;
		
		public int getAge() {
			return age;
		}
		public void setAge(int age) {
			this.age = age;
		}
		
		public String getName() {
			return name;
		}
		public void setName(String name) {
			this.name = name;
		}
		private void pm() {
			System.out.println("私有方法");
		}
	
}
	/**
	 * 批量获取类中方法演示
	 * @throws Exception
	 */
	public static void main(String[] args) throws Exception  {
		Class cls = Class.forName("cn.study.reflex.Person");
		
		//参数一:方法名 参数二:参数类型
		Method method1 = cls.getMethod("setAge", int.class);
		Method method2 = cls.getDeclaredMethod("pm");
		Method[] method3 = cls. getDeclaredMethods();
		
		System.out.println(method1);
		System.out.println(method2);
		System.out.println("----------------");
		
		//遍历获取到的类中的所有成员方法
		for(Method m :method3) {
			System.out.println(m);
		}
	}
/*程序运行结果
public void cn.study.reflex.Person.setAge(int)
private void cn.study.reflex.Person.pm()
----------------
public java.lang.String cn.study.reflex.Person.getName()
public void cn.study.reflex.Person.setName(java.lang.String)
private void cn.study.reflex.Person.pm()
public void cn.study.reflex.Person.setAge(int)
public int cn.study.reflex.Person.getAge()
*/

通过反射获得类中属性

1.获取类中属性

​ 1)批量获取
​ Field[] getFields()
​ 获得所有公有的属性对象
​ Field[] getDeclaredFields()
​ 获得所有属性对象,包括:私有、受保护、默认、公有;
​ 2)单个获取
​ public Field getField(String fieldName)
​ 获得某个公有的属性对象;
​ public Field getDeclaredField(String fieldName)
​ 获得某个属性对象(可以是私有的)

2.设置变量的值:
Field --> public void set(Object obj,Object value):
参数说明:
1.obj:要设置的变量所在的对象;
2.value:要为变量设置的值;

部分代码演示:

/**
 * 一个用于测试的类
 */
public class Person {
		
		private int  age;
		
		private String name;
		
		public double a;
}		
	/**
	 * 批量获取类中属性演示
	 * @throws Exception
	 */
	public static void main(String[] args) throws Exception  {
		Class cls = Class.forName("cn.study.reflex.Person");

		Field[] f1 = cls.getFields();
		Field[] f2 = cls.getDeclaredFields();
		

		for(Field f :f1) {
			System.out.println(f);
		}
		System.out.println("------------");
		for(Field f :f2) {
			System.out.println(f);
		}
	}
/*程序运行
public double cn.study.reflex.Person.a
------------
private int cn.study.reflex.Person.age
private java.lang.String cn.study.reflex.Person.name
public double cn.study.reflex.Person.a
*/