Java反射

本文详细介绍了Java反射机制,包括如何通过.class、对象的getClass()和Class.forName获取类字节码对象,进一步展示了如何利用反射获取构造方法实例化对象,以及获取成员变量和方法进行访问和执行。通过实例演示了如何处理私有属性和方法,以及如何在List集合中添加字符串。
摘要由CSDN通过智能技术生成

Java反射

概述:

反射是指在加载类型之后,通过该类的字节码对象来获取需要的信息,然后通过获取到 的信息来实现对应的功能,这种机制就是反射。由于这种动态性,可以极大的增强程序 的灵活性和扩展性。

获取类字节码对象的方式:(使用反射的前提)

(1)类名.class

(2)对象名的.getClass()

(3)Class.forName(类的全类名)

代码

public class TestClass {
    public static void main(String[] args) throws ClassNotFoundException {
        //获取Student类的字节码对象
        //使用类的静态属性class
        Class<? extends Student> stu = Student.class;
        //使用对象的方法:getClass
        Class<? extends Student> stu1 = new Student().getClass();
        //使用Class类的静态方法:forName
        //使用的最多
        //虽然参数给的是字符串:但是底层会根据传递的字符串,
        // 去class文件中寻找有没有和字符串相同的类名,
        // 如果有这个和字符串相同的类名,就直接加载了,
        // 如果没有就抛出异常
        //特点:虽然第三种方式不是最简单的,但是参数是一个字符串,该字符串来源广泛,所以使用第三种方式可以提高程序的扩展性。
        Class<?> stu2 = Class.forName("Day22.Student");
    }
}

反射获取字节码对象中的构造方法并实例化

1、概述:根据类的字节码对象获取该类的构造方法,并创造该类对象

2、获取构造方法的方式:

(1)getConstructors():返回所有公共的构造方法对象

(2)getDeclaredConstructors():返回所有构造方法对象

(3)getConstructor():返回空参构造对象

(4)getConstructor(Class<?>… parameterTypes):

返回单个指定参数的公共有参构造方法对象

(5)getDeclaredConstructor(Class<?>…parameterTypes):

返回单个指定参数的有参构造方法对象

3、通过构造方法对象,创建类的实例:

newInstance(Object…initargs)

如果是空参构造,就不需要给出实参

如果是有参构造对象,就需要给出对应的实际参数

代码

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class TestClass1 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        Class<?> c = Class.forName("Day22.Student");
        //获取字节码对象中的构造方法
        //Constructor构造方法对象所属的类型
        //Constructor<?>[] con = c.getConstructors();获取所有公有的构造方法
        Constructor<?>[] con = c.getConstructors();
        //遍历
        for (Constructor<?> con1 : con) {
            System.out.println(con1);
        }
        System.out.println("-----------");
        //获取所有构造方法包括私有的构造方法
        Constructor<?>[] con2 = c.getDeclaredConstructors();
        for (Constructor<?> constructor : con2) {
            System.out.println(constructor);
        }
        System.out.println("----------------");
        //获取空参构造方法
       Constructor<?> con3= c.getConstructor();
        System.out.println(con3);
        System.out.println("--------------");
        //获取有参构造
        Constructor<?> con4 = c.getConstructor(String.class, int.class);
        System.out.println(con4);
        System.out.println("----------------");
        //获取所有私有有参构造
        Constructor<?> con5 = c.getDeclaredConstructor(String.class);
        System.out.println(con5);
        System.out.println("----------------");
        //通过获取的构造方法对象,来创建实例
        Student s=(Student)con3.newInstance();
        System.out.println(s+s.getName()+s.getAge());
        System.out.println("---------------");
        Object o = con4.newInstance("张三", 20);//多态,父类引用指向子类对象,方法左右
        System.out.println(o);
        System.out.println("--------------");


    }
}

反射获取成员变量并使用

获取方法:

(1)getFields():返回所有公共成员变量对象

(2)getDeclaredFields():返回所有成员变量对象

(3)getField(String name):返回指定的公共成员变量对象

(4)getDeclaredField(String name):返回单个成员变量对象

访问成员属性的方法:

(1)set(Object obj,Object value): 用于给obj对象中的该成员变量赋value值

(2)get(Object obj):用于获取obj对象的指定成员变量值

暴力反射:

​ 概述:如果类型中的某些属性是私有化的,那么就不能直接使用方法访问该属性

		  所以只能使用暴力反射来强制访问。

​ 相关方法:

		isAccessible():判断当前属性对象是否需要参与虚拟机的检测

		setAccessible(boolean flag):将当前对象设置为不需要被虚拟机检测

代码

import java.lang.reflect.Field;

public class TestField {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, InstantiationException, IllegalAccessException {
        Class<?> c = Class.forName("Day22.Student");
        //获取字节码对象中的所有的属性:public
        System.out.println("遍历输出字节码对象中的所有属性(public)");
        Field[] fs = c.getFields();
        for (Field f : fs) {
            System.out.println(f);
        }
        //获取所有属性:包括私有
        System.out.println("获取所有属性包括私有");
        Field[] df = c.getDeclaredFields();
        for (Field field : df) {
            System.out.println(field);
        }
        System.out.println("获取其中一个属性根据属性的名称指定字符串传递---------------------");
        //获取其中的一个属性根据属性的名称指定字符串传递
        Field address = c.getField("address");
        System.out.println(address);
        System.out.println("获取私有属性-----------");
        //获取私有的属性
        Field name = c.getDeclaredField("name");
        System.out.println(name);
        System.out.println("通过空参创建对象,并且根据属性名获取属性值----------");
        //通过空参创建对象,并且根据属性名获取属性值
        Object o = c.newInstance();
        System.out.println(address.get(o));
        address.set(o,"北京");
        System.out.println(address.get(o));
        System.out.println("判断属性是否需要经过虚拟机的检测,如果为false,表示属性需要经过虚拟机检测-----");
        //isAccessible():该方法是判断属性是否需要经过虚拟机的检测,如果为false,表示属性需要经过虚拟机检测
        //在检测的时候如果发现私有修饰,不能直接使用,如果是共有的可以直接使用
        System.out.println(name.isAccessible());//false
        System.out.println(address.isAccessible());//false
        //将属性的检测方式设置为true之后,表示该属性在被使用时不需要经过检测,可以直接使用
        name.setAccessible(true);
        System.out.println(name.get(o));
        name.set(o,"张三");
        System.out.println(name.get(o));
    }

}

反射获取成员方法并使用

1、方法:

(1)getMethods():返回所有公共成员方法对象

(2)getDeclaredMethods():返回所有成员方法对象

(3)getMethod(String methodName, Class<?>…parameterTypes):

返回指定的公共成员方法对象

(4)getDeclaredMethod(String methodName, Class<?>…parameterTypes):

返回指定的成员方法对象

2、执行方法的方式:

invoke(Object o,Class…paramsTypre)

3、如果方法是已经非静态方法,需要传递一个对象执行

4、如果方法是一个静态方法,不需要传入对象,直接传入Null

代码

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

public class TestClassGetMethod {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
        Class<?> c = Class.forName("Day22.Student");
        //获取字节码对象中的所有的方法:public 包括父类中继承的
        Method[] m = c.getMethods();
        for (Method method : m) {
            System.out.println(method);
        }

        //获取所有的方法,包括私有的,但是父类中继承的没有重写的方法方法不获取
        Method[] dm = c.getDeclaredMethods();
        for (Method method : dm) {
            System.out.println(method);
        }
        //获取某一个指定的方法
        Method m1 = c.getMethod("show");
        Method m2 = c.getMethod("show", String.class);
        Method m3 = c.getMethod("getNum");
        Method m4 = c.getDeclaredMethod("print");
        Method m5 = c.getMethod("useStatic");
        Object o = c.newInstance();
        //执行方法,invoke
        // 如果执行的方法是私有的,使用暴力反射,如果不是直接使用
        //如果方法有参数就给实参,没有就不给
        m1.invoke(o);
        m2.invoke(o,"你好");
        Object invoke = m3.invoke(o);//100->Integer->Object
        System.out.println(invoke);
        m4.setAccessible(true);
        m4.invoke(o);
        m5.invoke(null);//如果方法是静态的,可以不给对象,给null代替即可


    }
}

练习

需求:

定义一个List集合(泛型定义为Integer),如:ArrayList list = new ArrayList<>();

要求利用反射的原理,在集合中添加若干字符串并且不报错,最终输出集合中的内容

代码

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
public class TestExercises {
    public static void main(String[] args) throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        ArrayList<Integer> list = new ArrayList<>();
        Class<? extends ArrayList> c = list.getClass();
        //参数擦除:当某个类型的方法的参数定义的是泛型时,在使用反射调用该方法的时候,参数的类型是Object类型,而不能是泛型类型
        Method add = c.getMethod("add", Object.class);//类中使用的是泛型所以就不能用指定的数据类型,只能用Object
        add.invoke(list,"asdf");
        System.out.println(list);
    }
}

反射练习

1、自定义一个Stu类型

属性:String name; int age (私有化,定义公共的访问方式)

方法:toString(展示当前属性的信息)

2、手动将这个类型的全类名写在文件中的第一行

3、读取文件中的类名,并创建一个该类对象(通过有参构造创建)

4、使用toString方法展示对象属性

5、使用公共的访问方法给属性改值,并再次调用show方法展示属性值

代码

import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Exercises1 {
    public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException, NoSuchFieldException {
        //创建字符高速缓冲流对象
        BufferedReader br = new BufferedReader(new FileReader("Day13/src/main/java/Day22/a.txt"));
        //调用readLine()方法读取文件中的一行
        String s = br.readLine();
        //使用反射获取类字节码对象
        Class<?> c = Class.forName(s);
        //获取有参构造
        Constructor<?> con = c.getConstructor(String.class,int.class);
        //创建对象
        Object o = con.newInstance("张三", 20);
        //获取toString方法
        Method toString = c.getMethod("toString");
        //运行toString方法传入对象
        Object o1 = toString.invoke(o);
        System.out.println(o1);
        //获取setName方法
        Method setName = c.getMethod("setName",String.class);
        //修改对象的name属性
        setName.invoke(o,"李四");
        System.out.println(toString.invoke(o));
        //获取成员变量
        Field name = c.getDeclaredField("name");
        Field age = c.getDeclaredField("age");
        //因为成员变量已经私有化,所以需要使用暴力反射
        name.setAccessible(true);
        age.setAccessible(true);
        name.set(o,"王五");
        age.set(o,60);
        System.out.println(toString.invoke(o));
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值