Java 反射

本文深入探讨了Java反射机制,它是框架设计的核心,允许程序在运行时动态获取类的信息并调用其方法。内容包括:反射的概述、Class类的API、获取Class对象的三种方式、构造方法、成员变量、成员方法的获取和使用,以及反射在配置文件读取和泛型检查中的应用。通过实例展示了反射的强大功能和使用场景。
摘要由CSDN通过智能技术生成

反射是框架设计的灵魂

使用的前提条件:必须先得到代表的字节码的Class,Class类用于表示.class文件(字节码))

一、反射的概述

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象.

Java中编译类型有两种:
静态编译:在编译时确定类型,绑定对象即通过。
动态编译:运行时确定类型,绑定对象。动态编译最大限度地发挥了Java的灵活性,体现了多态的应用,可以减低类之间的耦合性。

Java反射是Java被视为动态(或准动态)语言的一个关键性质。这个机制允许程序在运行时透过Reflection APIs取得任何一个已知名称的class的内部信息,包括其modifiers(诸如public、static等)、superclass(例如Object)、实现之interfaces(例如Cloneable),也包括fields和methods的所有信息,并可于运行时改变fields内容或唤起methods。

Reflection可以在运行时加载、探知、使用编译期间完全未知的classes。即Java程序可以加载一个运行时才得知名称的class,获取其完整构造,并生成其对象实体、或对其fields设值、或唤起其methods。

反射(reflection)允许静态语言在运行时(runtime)检查、修改程序的结构与行为。
在静态语言中,使用一个变量时,必须知道它的类型。在Java中,变量的类型信息在编译时都保存到了class文件中,这样在运行时才能保证准确无误;换句话说,程序在运行时的行为都是固定的。如果想在运行时改变,就需要反射这东西了。

实现Java反射机制的类都位于java.lang.reflect包中:
Class类:代表一个类
Field类:代表类的成员变量(类的属性)
Method类:代表类的方法
Constructor类:代表类的构造方法
Array类:提供了动态创建数组,以及访问数组的元素的静态方法
一句话概括就是使用反射可以赋予jvm动态编译的能力,否则类的元数据信息只能用静态编译的方式实现,例如热加载,Tomcat的classloader等等都没法支持。

反射就是把java类中各种信息反射成一个个java对象
例如:一个类有:成员变量、方法、构造方法、包等等信息,利用反射技术可以对一个类进行解剖,把个个组成部分映射成一个个对象。
(其实:一个类中这些成员方法、构造方法、在加入类中都有一个类来描述)
如图是类的正常加载过程:反射的原理在与class对象。
熟悉一下加载的时候:Class对象的由来是将class文件读入内存,并为之创建一个Class对象。
在这里插入图片描述

二、查看Class类在java中的api详解(1.7的API)

在这里插入图片描述

Class 类的实例表示正在运行的 Java 应用程序中的类和接口。也就是jvm中有N多的实例每个类都有该Class对象。(包括基本数据类型)
Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的defineClass 方法自动构造的。也就是这不需要我们自己去处理创建,JVM已经帮我们创建好了。
没有公共的构造方法,方法共有64个

三、反射的使用(这里使用Student类做演示)

1、获取Class对象的三种方式

1.1 Object ——> getClass();
1.2 任何数据类型(包括基本数据类型)都有一个“静态”的class属性
1.3 通过Class类的静态方法:forName(String className)(常用)
其中1.1是因为Object类中的getClass方法、因为所有类都继承Object类。从而调用Object类来获取**
常使用第三种获取Class对象,第一种已经new出对象了,第二种需要导入对象的jar包,依赖太强,一般都第三种,一个字符串可以传入也可写在配置文件中等多种方法。

        //第一种getClass()
        Student student = new Student();
        Class class1 = student.getClass();
        System.out.println(class1.getName());
        //第二种任何对象都有一个静态的class属性
        Class class2 = Student.class;
        System.out.println(class1 == class1);
        //第三种通过类的真实路径获取
        try {
            Class class3 = Class.forName("com.itcast.demo.reflect.Student");
            System.out.println(class2 == class3);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

2、获取构造方法

public class Student {//---------------构造方法-------------------
    //(默认的构造方法)
    Student(String str){
        System.out.println("(默认)的构造方法 s = " + str);
    }

    //无参构造方法
    public Student(){
        System.out.println("调用了公有、无参构造方法执行了。。。");
    }

    //有一个参数的构造方法
    public Student(char name){
        System.out.println("姓名:" + name);
    }

    //有多个参数的构造方法
    public Student(String name ,int age){
        System.out.println("姓名:"+name+"年龄:"+ age);//这的执行效率有问题,以后解决。
    }

    //受保护的构造方法
    protected Student(boolean n){
        System.out.println("受保护的构造方法 n = " + n);
    }

    //私有构造方法
    private Student(int age) {
        System.out.println("私有的构造方法   年龄:" + age);
    }

}
      //获取Class对象

        Class classStudent = Class.forName("com.itcast.demo.reflect.Student");

        //1.获取所有公有构造方法
        Constructor[] constructors = classStudent.getConstructors();
        System.out.println("**********************所有公有构造方法*********************************");
        for (Constructor constructor : constructors) {
            System.out.println(constructor);
        }

        ///2.获取所有构造方法(公有、私有、默认、受保护)

        Constructor[] declaredConstructors = classStudent.getDeclaredConstructors();
        System.out.println("**********************所有的构造方法*********************************");
        for (Constructor declaredConstructor : declaredConstructors) {
            System.out.println(declaredConstructor);
        }

        //3.获取公有无参构造方法,因为是无参的构造方法所以类型是一个null,不写也可以:这里需要的是一个参数的类型,切记是类型

        Constructor constructor = classStudent.getConstructor();

        Student student = (Student) constructor.newInstance();
        System.out.println("student对象"+student);

        //4.获取私有构造方法

        Constructor declaredConstructor = classStudent.getDeclaredConstructor();
        Student studentDeclared = (Student) declaredConstructor.newInstance();
        System.out.println("私有构造方法获取对象"+studentDeclared);
        System.out.println("私有构造方法"+declaredConstructor);
        

3、获取成员变量

  @Test
    public void testVariable()
            throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException, IllegalAccessException,
            InvocationTargetException, InstantiationException {

        //获取Class对象

        Class personClass = Class.forName("com.itcast.demo.reflect.Person");

        Person person = (Person) personClass.getConstructor().newInstance();
        //1获取所有的公有字段
        Field[] fields = personClass.getFields();
        System.out.println("**********************获取所有的公有字段*********************************");
        for (Field field : fields) {
            System.out.println(field);
        }

        //2获取所有字段(私有,受保护,默认,公有)
        Field[] declaredFields = personClass.getDeclaredFields();
        System.out.println("**********************获取所有的字段*********************************");
        for (Field declaredField : declaredFields) {
            System.out.println(declaredField);
        }

        //3获取某个特定的公有字段
        Field name = personClass.getField("name");
        System.out.println("**********************获取某个特定的公有字段*********************************");
        System.out.println(name);

        //4获取某个特定的(公有或者私有)字段
        Field name1 = personClass.getDeclaredField("name");
        Field age = personClass.getDeclaredField("age");
        //对象中添加数据
        age.set(person,20);
        name.set(person,"北京");
        System.out.println(name1);
        System.out.println(age);
        System.out.println(person);
    }

由此可见
调用字段时:需要传递两个参数:
Object obj = stuClass.getConstructor().newInstance();//产生Student对象–》Student stu = new Student();
//为字段设置值
f.set(obj, “刘德华”);//为Student对象中的name属性赋值–》stu.name = “刘德华”
第一个参数:要传入设置的对象,第二个参数:要传入实参


public class Student {
	public Student(){
		
	}
	//**********字段*************//
	public String name;
	protected int age;
	char sex;
	private String phoneNum;
	
	@Override
	public String toString() {
		return "Student [name=" + name + ", age=" + age + ", sex=" + sex
				+ ", phoneNum=" + phoneNum + "]";
	}
  @Test
    public void testVariable()
            throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException, IllegalAccessException,
            InvocationTargetException, InstantiationException {

        //获取Class对象

        Class personClass = Class.forName("com.itcast.demo.reflect.Person");

        //创建person
        Person person = (Person) personClass.getConstructor().newInstance();
        //1获取所有的公有字段
        Field[] fields = personClass.getFields();
        System.out.println("**********************获取所有的公有字段*********************************");
        for (Field field : fields) {
            System.out.println(field);
        }

        //2获取所有字段(私有,受保护,默认,公有)
        Field[] declaredFields = personClass.getDeclaredFields();
        System.out.println("**********************获取所有的字段*********************************");
        for (Field declaredField : declaredFields) {
            System.out.println(declaredField);
        }

        //3获取某个特定的公有字段
        Field name = personClass.getField("name");
        System.out.println("**********************获取某个特定的公有字段*********************************");
        System.out.println(name);

        //4获取某个特定的(公有或者私有)字段
        Field name1 = personClass.getDeclaredField("name");
        Field age = personClass.getDeclaredField("age");
        //对象中添加数据
        age.set(person,20);
        name.set(person,"上海");
        System.out.println(name1);
        System.out.println(age);
        System.out.println(person);
    }

4、获取成员变量


 
public class Student {
	//**************成员方法***************//
	public void show1(String s){
		System.out.println("调用了:公有的,String参数的show1(): s = " + s);
	}
	protected void show2(){
		System.out.println("调用了:受保护的,无参的show2()");
	}
	void show3(){
		System.out.println("调用了:默认的,无参的show3()");
	}
	private String show4(int age){
		System.out.println("调用了,私有的,并且有返回值的,int参数的show4(): age = " + age);
		return "abcd";
	}
public static void main(String[] args) throws Exception {
		//1.获取Class对象
		Class stuClass = Class.forName("fanshe.method.Student");
		//2.获取所有公有方法
		System.out.println("***************获取所有的”公有“方法*******************");
		stuClass.getMethods();
		Method[] methodArray = stuClass.getMethods();
		for(Method m : methodArray){
			System.out.println(m);
		}
		System.out.println("***************获取所有的方法,包括私有的*******************");
		methodArray = stuClass.getDeclaredMethods();
		for(Method m : methodArray){
			System.out.println(m);
		}
		System.out.println("***************获取公有的show1()方法*******************");
		Method m = stuClass.getMethod("show1", String.class);
		System.out.println(m);
		//实例化一个Student对象
		Object obj = stuClass.getConstructor().newInstance();
		m.invoke(obj, "刘德华");
		
		System.out.println("***************获取私有的show4()方法******************");
		m = stuClass.getDeclaredMethod("show4", int.class);
		System.out.println(m);
		m.setAccessible(true);//解除私有限定
		Object result = m.invoke(obj, 20);//需要两个参数,一个是要调用的对象(获取有反射),一个是实参
		System.out.println("返回值:" + result);
	}

由此可见:
m = stuClass.getDeclaredMethod(“show4”, int.class);//调用制定方法(所有包括私有的),需要传入两个参数,第一个是调用的方法名称,第二个是方法的形参类型,切记是类型。
System.out.println(m);
m.setAccessible(true);//解除私有限定
Object result = m.invoke(obj, 20);//需要两个参数,一个是要调用的对象(获取有反射),一个是实参
System.out.println(“返回值:” + result);//

5、获取main方法

public class TestMain {

    public static void main(String[] args) {

        System.out.println("通过反射获取main方法");
    }
}

@Test
public void testMain()
throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException,
InstantiationException {

    //获取main方法
    Class<?> mainClass = Class.forName("com.itcast.demo.reflect.TestMain");
    Method main = mainClass.getMethod("main", String[].class);
    //第一个参数,对象类型,因为方法是static静态的,所以为null可以,第二个参数是String数组,这里要注意在jdk1.4时是数组,jdk1.5之后是可变参数
    //这里拆的时候将  new String[]{"a","b","c"} 拆成3个对象。。。所以需要将它强转。
    TestMain testMain = (TestMain) mainClass.getConstructor().newInstance();
    main.invoke(null, (Object)new String[]{"1","2","3"});//方式一
    main.invoke(testMain, new Object[]{new String[]{"a","b","c"}});//方式二
    System.out.println(main);
}

6、反射方法的其它使用之—通过反射运行配置文件内容

student类:
public class Student {
	public void show(){
		System.out.println("is show()");
	}
}

配置文件以txt文件为例子(pro.txt):
className = cn.fanshe.Student
methodName = show

测试类:
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Properties;
/*
 * 我们利用反射和配置文件,可以使:应用程序更新时,对源码无需进行任何修改
 * 我们只需要将新类发送给客户端,并修改配置文件即可
 */
public class Demo {
	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("pro.txt");//获取输入流
		pro.load(in);//将流加载到配置文件对象中
		in.close();
		return pro.getProperty(key);//返回根据key获取的value值
	}
}

控制台输出:
is show()

6、反射方法的其它使用之—通过反射越过泛型检查

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);
		}
	}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值