JAVA-反射机制详解

2022-07-30 12:28:29

1 反射的概念

(1)JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
(2)要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象。
(3)Java属于先编译再运行的语言,程序中对象的类型在编译期就确定下来了,而当程序在运行时可能需要动态加载某些类,这些类因为之前用不到,所以没有被加载到JVM。通过反射,可以在运行时动态地创建对象并调用其属性,不需要提前在编译期知道运行的对象是谁。

2 反射的原理

下图是类的正常加载过程、反射原理与class对象:
Class对象的由来是将.class文件读入内存,并为之创建一个Class对象。
image.png

3 反射的优缺点:

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

2、缺点:
(1)反射会消耗一定的系统资源,因此,如果不需要动态地创建一个对象,那么就不需要用反射;
(2)反射调用方法时可以忽略权限检查,因此可能会破坏封装性而导致安全问题。

4 反射的用途:

1、反编译:.class–>.java

2、通过反射机制访问java对象的属性方法构造方法

3、当我们在使用IDE,比如Ecplise时,当我们输入一个对象或者类,并想调用他的属性和方法是,一按点号,编译器就会自动列出他的属性或者方法,这里就是用到反射。

4、反射最重要的用途就是开发各种通用框架。比如很多框架(Spring)都是配置化的(比如通过XML文件配置Bean),为了保证框架的通用性,他们可能需要根据配置文件加载不同的类或者对象,调用不同的方法,这个时候就必须使用到反射了,运行时动态加载需要的加载的对象。

5 反射的使用

5.1 获取Class对象的三种方式

(1)Object–>getClass()
(2)任何数据类型(包括基本的数据类型)都有一个“静态”的class属性
(3)通过Class类的静态方法:forName(String className)(最常用

5.1.1 Object.getClass()

Object类中的getClass方法、因为所有类都继承Object类。从而调用Object类来获取。

5.1.2 获取Class对象的三种方式代码实现

package com.pikaqiu.fs;import com.pikaqiu.entity.User;publicclassFanShe{publicstaticvoidmain(String[] args){// 第一种:Object.getClass();
		User user=newUser();
		ClassuserClass1= user.getClass();
		System.out.println(userClass1);// 第二种: java中每个类型都有class 属性
		ClassuserClass2= User.class;
		System.out.println(userClass2);
		System.out.println(userClass1== userClass2);// 第三种:Class.forName(String className)try{
			ClassuserClass3= Class.forName("com.pikaqiu.entity.User");
			System.out.println(userClass3);
			System.out.println(userClass1== userClass3);}catch(ClassNotFoundException e){
			e.printStackTrace();}}}

注意:在运行期间,一个类,只有一个Class对象产生

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

5.2 通过反射获取构造方法

5.2.1 批量的方法

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

5.2.2 获取单个的方法,并调用

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

5.2.3 调用构造方法

Constructor–>newInstance(Object… initargs)

5.2.4 代码实现

User类:

package com.pikaqiu.entity;import org.springframework.beans.factory.annotation.Value;import org.springframework.stereotype.Component;import org.springframework.stereotype.Controller;import org.springframework.stereotype.Repository;import org.springframework.stereotype.Service;import javax.naming.Name;@Component(value="user")publicclassUser{public String name;publicint age;//(默认的构造方法)User(String name){
        System.out.println("(默认)的构造方法 s = "+ name);}//无参构造方法publicUser(){
        System.out.println("调用了公有、无参构造方法执行了。。。");}//有一个参数的构造方法publicUser(char name){
        System.out.println("姓名:"+ name);}//有多个参数的构造方法publicUser(String name,int age){
        System.out.println("姓名:"+name+"年龄:"+ age);//这的执行效率有问题,以后解决。}//受保护的构造方法protectedUser(boolean n){
        System.out.println("受保护的构造方法 n = "+ n);}//私有构造方法privateUser(int age){
        System.out.println("私有的构造方法   年龄:"+ age);}}

构造函数调用:

package com.pikaqiu.fs;import jdk.nashorn.internal.runtime.logging.DebugLogger;import java.lang.reflect.Constructor;import java.lang.reflect.InvocationTargetException;import java.net.SocketTimeoutException;import java.util.zip.CheckedOutputStream;publicclassGetConstructor{publicstaticvoidmain(String[] args)throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException{// 1. 加载Class对象
		Classname= Class.forName("com.pikaqiu.entity.User");// 包名.类名// 2. 获取所有共有构造函数
		System.out.println("**********************所有公有构造方法*********************************");
		Constructor[] constructors= name.getConstructors();for(Constructor constructor: constructors){
			System.out.println(constructor);}// 2. 获取所有的构造方法(包括:私有、受保护、默认、公有)
		System.out.println("************所有的构造方法(包括:私有、受保护、默认、公有)***************");
		Constructor[] declaredConstructors= name.getDeclaredConstructors();for(Constructor declaredConstructor: declaredConstructors){
			System.out.println(declaredConstructor);}// 3. 获取单个公有、无参的构造方法
		System.out.println("*****************获取公有、无参的构造方法*******************************");
		Constructor constructor= name.getConstructor(null);
		System.out.println(constructor);//1>、因为是无参的构造方法所以类型是一个null,不写也可以:这里需要的是一个参数的类型,切记是类型//2>、返回的是描述这个无参构造函数的类对象。// 4. 获取私有构造方法,并调用
		System.out.println("******************获取私有构造方法,并调用*******************************");
		Constructor declaredConstructor= name.getDeclaredConstructor(int.class);
		System.out.println(declaredConstructor);// 暴力反射
		declaredConstructor.setAccessible(true);// 取消java语言访问检查
		Object o= declaredConstructor.newInstance(33);
		System.out.println(o);}}

运行结果:

**********************所有公有构造方法*********************************
public com.pikaqiu.entity.User(java.lang.String,int)
public com.pikaqiu.entity.User()
public com.pikaqiu.entity.User(char)
************所有的构造方法(包括:私有、受保护、默认、公有)***************
private com.pikaqiu.entity.User(int)
protected com.pikaqiu.entity.User(boolean)
public com.pikaqiu.entity.User(java.lang.String,int)
com.pikaqiu.entity.User(java.lang.String)
public com.pikaqiu.entity.User()
public com.pikaqiu.entity.User(char)
*****************获取公有、无参的构造方法*******************************
public com.pikaqiu.entity.User()
******************获取私有构造方法,并调用*******************************
private com.pikaqiu.entity.User(int)
私有的构造方法   年龄:33
com.pikaqiu.entity.User@5e481248

5.3 获取成员变量并调用

5.3.1 批量的方法

  1. Field[] getFields():获取所有的"公有字段"
  2. Field[] getDeclaredFields():获取所有字段,包括:私有、受保护、默认、公有;

5.3.2 获取单个的方法

  1. public Field getField(String fieldName):获取某个"公有的"字段;
  2. public Field getDeclaredField(String fieldName):获取某个字段(可以是私有的)

5.3.3 设置字段的值

Field --> public void set(Object obj,Object value):

  • 参数说明:
    • obj:要设置的字段所在的对象;
    • value:要为字段设置的值;

5.3.4 代码实现

User类

package com.pikaqiu.entity;import org.springframework.beans.factory.annotation.Value;import org.springframework.stereotype.Component;import org.springframework.stereotype.Controller;import org.springframework.stereotype.Repository;import org.springframework.stereotype.Service;import javax.naming.Name;@Component(value="user")publicclassUser{public String name;protectedint age;char sex;private String phone;@Overridepublic StringtoString(){return"User{"+"name='"+ name+'\''+", age="+ age+", sex="+ sex+", phone='"+ phone+'\''+'}';}}

成员变量调用

package com.pikaqiu.fs;import com.pikaqiu.entity.User;import jdk.nashorn.internal.runtime.Source;import jdk.nashorn.internal.runtime.logging.DebugLogger;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.InvocationTargetException;import java.net.SocketTimeoutException;import java.util.zip.CheckedOutputStream;publicclassGetConstructor{publicstaticvoidmain(String[] args)throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException{// 1. 加载Class对象
		Classname= Class.forName("com.pikaqiu.entity.User");// 包名.类名// 2.获取字段
		System.out.println("************获取所有公有的字段********************");
		Field[] fields= name.getFields();for(Field field: fields){
			System.out.println(field);}

		System.out.println("************获取所有的字段(包括私有、受保护、默认的)********************");
		Field[] declaredFields= name.getDeclaredFields();for(Field declaredField: declaredFields){
			System.out.println(declaredField);}

		System.out.println("*************获取公有字段**并调用***********************************");
		Field name1= name.getField("name");
		System.out.println(name1);// 获取一个对象
		Object o= name.getConstructor().newInstance();//产生User对象--》User user = new User();// 为字段值赋值
		name1.set(o,"haha");
		User o1=(User) o;
		System.out.println("name:"+ o1.name);

		System.out.println("**************获取私有字段****并调用********************************");
		Field phone= name.getDeclaredField("phone");
		System.out.println(phone);
		phone.setAccessible(true);
		phone.set(o,"2341241234");
		System.out.println(o1);}}

结果:

************获取所有公有的字段********************
public java.lang.String com.pikaqiu.entity.User.name
************获取所有的字段(包括私有、受保护、默认的)********************
public java.lang.String com.pikaqiu.entity.User.name
protected int com.pikaqiu.entity.User.age
char com.pikaqiu.entity.User.sex
private java.lang.String com.pikaqiu.entity.User.phone
*************获取公有字段**并调用***********************************
public java.lang.String com.pikaqiu.entity.User.name
调用了公有、无参构造方法执行了。。。
name:haha
**************获取私有字段****并调用********************************
private java.lang.String com.pikaqiu.entity.User.phone
User{name='haha', age=0, sex= , phone='2341241234'}

5.4 获取成员方法并调用

5.4.1 批量的方法

  1. public Method[] getMethods():获取所有"公有方法";(包含了父类的方法也包含Object类)
  2. public Method[] getDeclaredMethods():获取所有的成员方法,包括私有的(不包括继承的)

5.4.2 获取单个的:

  1. public Method getMethod(String name,Class<?>… parameterTypes):

    • 参数:
      • name : 方法名;
      • Class … : 形参的Class类型对象
  2. public Method getDeclaredMethod(String name,Class<?>… parameterTypes)

5.4.3 调用方法:

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

5.4.4 代码实现

User类

package com.pikaqiu.entity;import org.springframework.beans.factory.annotation.Value;import org.springframework.stereotype.Component;import org.springframework.stereotype.Controller;import org.springframework.stereotype.Repository;import org.springframework.stereotype.Service;import javax.naming.Name;@Component(value="user")publicclassUser{// 成员方法publicvoidshow1(String s){
        System.out.println("调用了:公有的,String参数的show1(): s = "+ s);}protectedvoidshow2(){
        System.out.println("调用了:受保护的,无参的show2()");}voidshow3(){
        System.out.println("调用了:默认的,无参的show3()");}private Stringshow4(int age){
        System.out.println("调用了,私有的,并且有返回值的,int参数的show4(): age = "+ age);return"abcd";}}

获取成员方法

package com.pikaqiu.fs;import com.pikaqiu.entity.User;import jdk.nashorn.internal.runtime.Source;import jdk.nashorn.internal.runtime.logging.DebugLogger;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;import java.net.SocketTimeoutException;import java.util.zip.CheckedOutputStream;publicclassGetConstructor{publicstaticvoidmain(String[] args)throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException{// 1. 加载Class对象
		Classname= Class.forName("com.pikaqiu.entity.User");// 包名.类名//2.获取所有公有方法
		System.out.println("***************获取所有的”公有“方法*******************");
		name.getMethods();
		Method[] methodArray= name.getMethods();for(Method m: methodArray){
			System.out.println(m);}
		System.out.println("***************获取所有的方法,包括私有的*******************");
		methodArray= name.getDeclaredMethods();for(Method m: methodArray){
			System.out.println(m);}
		System.out.println("***************获取公有的show1()方法*******************");
		Method m= name.getMethod("show1", String.class);
		System.out.println(m);//实例化一个Student对象
		Object obj= name.getConstructor().newInstance();
		m.invoke(obj,"刘德华");

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

结果

***************获取所有的”公有“方法*******************
public java.lang.String com.pikaqiu.entity.User.toString()
public void com.pikaqiu.entity.User.setName(java.lang.String)
public void com.pikaqiu.entity.User.show1(java.lang.String)
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.pikaqiu.entity.User.toString()
public void com.pikaqiu.entity.User.setName(java.lang.String)
private java.lang.String com.pikaqiu.entity.User.show4(int)
void com.pikaqiu.entity.User.show3()
public void com.pikaqiu.entity.User.show1(java.lang.String)
protected void com.pikaqiu.entity.User.show2()
***************获取公有的show1()方法*******************
public void com.pikaqiu.entity.User.show1(java.lang.String)
调用了公有、无参构造方法执行了。。。
调用了:公有的,String参数的show1(): s = 刘德华
***************获取私有的show4()方法******************
private java.lang.String com.pikaqiu.entity.User.show4(int)
调用了,私有的,并且有返回值的,int参数的show4(): age = 20
返回值:abcd

5.5 反射的应用

5.5.1 泛型擦除

将已存在的ArrayList集合中添加一个字符串数据

package com.pikaqiu.fs;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;import java.util.ArrayList;import java.util.Arrays;publicclassFanShenTest{publicstaticvoidmain(String[] args)throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException{
		ArrayList<Integer> list=newArrayList<Integer>();// 添加元素到集合
		list.add(1);
		list.add(newInteger("234"));
		list.add(newInteger<
  • 作者:皮卡丘的情绪
  • 原文链接:https://blog.csdn.net/hansome_hong/article/details/123330296
    更新时间:2022-07-30 12:28:29