JavaSE--重新学习_查漏补缺(14)--反射机制_Class 类_通过反射调用类的完整结构

一、反射机制

  • 反射机制:就是通过一个抽象的类名能够在自己记忆(加载类的内存)中找到相匹配的类的具体信息。
    前提是:已经在JVM中已经加载过这个类。

  • Java反射机制提供的功能

  1. 在运行时判断任意一个对象所属的类。
  2. 在运行时构造任意一个类的对象。
  3. 在运行时判断任意一个类所具有的成员变量和方法。
  4. 在运行时调用任意一个对象的成员变量和方法。
  5. 生成动态代理。
  • 反射相关的主要API
  1. java.lang.Class:代表一个类
  2. java.lang.reflect.Method:代表类的方法
  3. java.lang.reflect.Field:代表类的成员变量
  4. java.lang.reflect.Constructor:代表类的构造方法

二、Class 类

  • 反射可以得到的信息:某个类的属性、方法和构造器、某个类到底实现了哪些接口。对于每个类而言,JRE 都为其保留一个不变的 Class 类型的对象。一个 Class 对象包含了特定某个类的有关信息。

  • 注意:

    1. Class本身也是一个类
    2. Class 对象只能由系统建立对象
    3. 一个类在 JVM 中只会有一个Class实例
    4. 一个Class对象对应的是一个加载到JVM中的一个.class文件
    5. 每个类的实例都会记得自己是由哪个 Class 实例所生成
    6. 通过Class可以完整地得到一个类中的完整结构
  • 在Object类中定义了以下的方法,此方法将被所有子类继承:
    public final Class getClass()
    以上的方法返回值的类型是一个Class类,此类是Java反射的源头, 实际上所谓反射从程序的运行结果来看也很好理解,即:可以通过对象反射求出类的名称

  • Class类的常用方法:
    在这里插入图片描述

  • 实例化Class类对象(四种方法)

    1. 前提:若已知具体的类,通过类的class属性获取,该方法
      最为安全可靠,程序性能最高
      实例:Class clazz = String.class;
    2. 前提:已知某个类的实例,调用该实例的getClass()方法获
      取Class对象
      实例:Class clazz = “www.xyd.com”.getClass();
    3. 前提:已知一个类的全类名,且该类在类路径下,可通过
      Class类的静态方法forName()获取,可能抛出 ClassNotFoundException
      实例:Class clazz = Class.forName(“java.lang.String”);
    4. 其他方式(不做要求)
      ClassLoader cl = this.getClass().getClassLoader();
      Class clazz4 = cl.loadClass(“类的全类名”);
    • 下列代码实现了前三种Class类对象的创建
package test;

public class Test {
	public static void main(String[] args) {
		Person p = new Person();
		Class clazz = p.getClass();//clazz对象中就包含对象p所属的Person类的所有的信息
		
		Class c0 = Person.class;//通过类名.class创建指定类的Class实例
		
		Class c1 = p.getClass();//通过一个类的实例对象的getClass()方法,获取对应实例对象的类的Class实例
		
		try {
			//通过Class的静态方法forName(String className)来获取一个类的Class实例
			//forName(String className)方法中的参数是你要获取的Class实例的类的全路径(包名.类名)
			Class c2 = Class.forName("test.Person");//这个是获取Class实例的常用方式
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		
	}
}

----------------------------------一个类的极限情况?-------------------------------------
在这里插入图片描述

三、通过反射调用类的完整结构

Field、Method、Constructor、Superclass、Interface、Annotation

  1. 实现的全部接口
  2. 所继承的父类
  3. 全部的构造器
  4. 全部的方法
  5. 全部的Field
    使用反射可以取得
    1. 实现的全部接口
      public Class<?>[] getInterfaces()
      确定此对象所表示的类或接口实现的接口。

    2. 所继承的父类
      public Class<? Super T> getSuperclass()
      返回表示此 Class 所表示的实体(类、接口、基本类型)的父类的 Class。

    3. 全部的构造器
      public Constructor[] getConstructors()
      返回此 Class 对象所表示的类的所有public构造方法。
      public Constructor[] getDeclaredConstructors()
      返回此 Class 对象表示的类声明的所有构造方法。
      Constructor类中:
      取得修饰符: public int getModifiers();
      取得方法名称: public String getName();
      取得参数的类型:public Class<?>[] getParameterTypes();

    4. 全部的方法
      public Method[] getDeclaredMethods()
      返回此Class对象所表示的类或接口的全部方法
      public Method[] getMethods()
      返回此Class对象所表示的类或接口的public的方法
      Method类中:
      public Class<?> getReturnType()取得全部的返回值
      public Class<?>[] getParameterTypes()取得全部的参数
      public int getModifiers()取得修饰符

    5. 全部的Field
      public Field[] getFields()
      返回此Class对象所表示的类或接口的public的Field。
      public Field[] getDeclaredFields()
      返回此Class对象所表示的类或接口的全部Field。
      Field方法中:
      public int getModifiers() 以整数形式返回此Field的修饰符
      public Class<?> getType() 得到Field的属性类型
      public String getName() 返回Field的名称。

    6. 类所在的包 Package getPackage() 。

  • 定义接口
package day14;

public interface Move {
	void moveType();
}

package day14;

public interface Study {
	void studyInfo();
}

  • 定义父类
package day14;

public class Person {
	public String name;
	int age;
}

  • 子类继承父类和接口
package day14;

import java.io.IOException;

public class Student extends Person implements Move,Study{
	public Student(){
		System.out.println("调用的是public Student()");
	}
	
	public Student(String school){
		this.school = school;
		System.out.println("调用的是public Student(String school)");
	}

	private Student(String name,int age){
		this.name = name;
		this.age = age;
		System.out.println("调用的是private Student(String name,int age)");
	}
	public String school;
	private String privateField;
	
	public void showInfo(){
		System.out.println("学校是:" + this.school);
	}
	
	@Override
	public void studyInfo() {
		System.out.println("学习的中学的知识");
	}

	@Override
	public void moveType() {
		System.out.println("骑自行车上学");
	}
	
	private void test(String name){
		System.out.println("这是私有方法private void test(String name)");
	}
	
	public String getSchool(){
		return this.school;
	}
	
	public void setInfo(String name,String school){
		this.name = name;
		this.school = school;
		System.out.println("这个是setInfo(String name,String school)方法");
	}
	
	public void setInfo(int age){
		System.out.println("这个是public void setInfo(int age)方法");
	}
}

  • 测试
package day14;

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

public class Test1 {
	public static void main(String[] args) {
		try {
			Class clazz = Class.forName("day14.Student");//通过包名.类名的字符串,调用Class.forName方法获取指定类的Class实例
			
			Class superClazz = clazz.getSuperclass();//获取父类
		System.out.println("父类:" + superClazz.getName());
		
		Class[] interfaces = clazz.getInterfaces();//获取当前类的所有接口
			for(Class c : interfaces){			System.out.println("接口:" + c.getName());
			}
			
			Constructor[] cons = clazz.getConstructors();//获取到类的公有的构造方法
			
			for(Constructor c : cons){
				System.out.println("构造方法名称:" + c.getName());//取得方法名称
				//getModifiers取得方法的修饰符,返回数组1代表public
				System.out.println("构造方法:" + c.getName() + "的修饰符是:" + c.getModifiers());
				
				Class[] paramClazz = c.getParameterTypes();
				for(Class pc : paramClazz){
					System.out.println("构造方法:" + c.getName() + "的参数类型是:" + pc.getName());
				}
			}
			
			Constructor[] cons1 = clazz.getDeclaredConstructors();//获取类的所有构造方法,包括公有和私有的
			for(Constructor c : cons1){
			
				System.out.println("-------------------------------------");
				
				System.out.println("构造方法名称:" + c.getName());//取得方法名称
				//getModifiers取得方法的修饰符,返回数组1代表public,返回数组2代表是private
				System.out.println("构造方法:" + c.getName() + "的修饰符是:" + c.getModifiers());//getModifiers取得方法的修饰符
				
				Class[] paramClazz = c.getParameterTypes();//获取构造方法的参数类型,有几个参数数组的元素就有几个
				for(Class pc : paramClazz){
					System.out.println("构造方法:" + c.getName() + "的参数类型是:" + pc.getName());
				}
				
				System.out.println("-------------------------------------");
			}
			
			//如果用反射的构造方法来创建对象
			try {
				Object obj = clazz.newInstance();//相当于调用Student类的无参公有的构造方法
				Student stu = (Student)obj;
				
				Constructor c = clazz.getConstructor(String.class);//指定获取有一个参数并且为String类型的公有的构造方法
				Student stu1 = (Student)c.newInstance("第一中学");//newInstance实例化对象,相当于调用public Student(String school)
				System.out.println(stu1.school);
				
				//通过反射机制,可以强制的调用私有的构造方法
				Constructor c = clazz.getDeclaredConstructor(String.class,int.class);//指定获取有两个参数(String,int)的构造方法
				
				c.setAccessible(true);//解除私有的封装,下面就可以对这个私有方法强制调用
				
				Student stu = (Student)c.newInstance("zhangsan",12);
				
			} catch (Exception e) {
				e.printStackTrace();
			}
			
			Method[] ms = clazz.getMethods();//获取到类的所有公有的方法
			Method[] ms = clazz.getDeclaredMethods();//获取类所有方法,包含公有和私有
			for(Method m : ms){
				System.out.println("方法名:" + m.getName());
				System.out.println("返回值类型:" + m.getReturnType());
				System.out.println("修饰符:" + m.getModifiers());				
				Class[] pcs = m.getParameterTypes();//获取方法的参数类型,是一个数组,方法有几个参数,数据就有几个元素
				if(pcs != null && pcs.length > 0){
					for(Class pc : pcs){
						System.out.println("参数类型:" + pc.getName());
					}
				}
				
				System.out.println("==============================================");
			
			}
			
			Field[] fs = clazz.getFields();//获取类的公有的属性,包含父类的公有属性
			
			Field[] fs = clazz.getDeclaredFields();//获取本类的(不包括父类的属性)所有的属性,包括私有
			
			for(Field f : fs){
				System.out.println("修饰符:" + f.getModifiers());
				System.out.println("属性的类型:" + f.getType());
				System.out.println("属性的名称:" + f.getName());
			}

			Package p = clazz.getPackage();//获取类所在的包
			System.out.println(p.getName());
			
			
			/**
			 * 注意:下面不论是反射调用setInfo还是test方法
			 * 都调用的obj对象的方法,obj对象时间上就是student对象
			 */
			Constructor con = clazz.getConstructor();//获取无参构造
			Object obj = con.newInstance();//使用无参构造创建对象
			
			Method m = clazz.getMethod("setInfo", String.class,String.class);//得到名称叫setInfo,参数是String,String的方法
			m.invoke(obj, "zhangsan","第一中学");//参数1是需要实例化的对象,后面的参数是调用当前的方法实际参数
			
			//如果想要调用一个私有方法
			Method m1 = clazz.getDeclaredMethod("test", String.class);//获取方法名为test,参数为1个String类型的方法
			
			m1.setAccessible(true);//解除私有的封装,下面可以强制调用私有的方法
			
			m1.invoke(obj, "李四");
			
			//调用一个重载方法
			Method m2 = clazz.getMethod("setInfo", int.class);//setInfo的重载方法
			m2.invoke(obj, 1);
			
			//有返回值的方法
			Method m3 = clazz.getMethod("getSchool");//这是获取方法名为getSchool并且没有参数的方法
			String school = (String)m3.invoke(obj);//调用有返回值的但是没有参数的方法
			System.out.println(school);
			
			
			//反射创建一个对象
			Constructor con = clazz.getConstructor();
			Student stu = (Student)con.newInstance();
			
			Field f = clazz.getField("school");//获取名称为school的属性
			
			f.set(stu, "第三中学");//对stu对象的school属性设置值"第三中学"
			String school = (String)f.get(stu);//获取stu对象的school属性的值
			System.out.println(school);
			
			//如果是私有的属性
			Field f1 = clazz.getDeclaredField("privateField");
			
			f1.setAccessible(true);//解除私有的封装,下面就可以强制的调用这个属性
			
			f1.set(stu, "测试私有属性");
			System.out.println(f1.get(stu));
			
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

  • 反射机制还可以调用指定方法和调用指定属性,以上代码均有体现。
发布了34 篇原创文章 · 获赞 1 · 访问量 518

猜你喜欢

转载自blog.csdn.net/weixin_44185736/article/details/105049185