Java反射

反射:允许对成员变量,成员方法和构造方法的信息进行编程访问。

所以,想要获取 成员变量,成员方法和构造方法,得首先获取 class对象。

1.获取class对象

① Class.forname("全类名")

② 类名.class

③ 对象.getClass()

public class Demo1 {
    public static void main(String[] args) throws ClassNotFoundException {
        //1.第一种方式(全类名:包名 + 类名)
        Class clazz1 = Class.forName("com.mihoyo.classDemo.Student");
        System.out.println(clazz1);//class com.mihoyo.classDemo.Student

        //2.第二种方式
        Class clazz2 = Student.class;
        System.out.println(clazz1 == clazz2);//同一个对象 --> true

        //3.第三种方式
        Student s = new Student();
        Class clazz3 = s.getClass();
        System.out.println(clazz1 == clazz3);//true
        System.out.println(clazz2 == clazz3);//true
    }
}

细节:

第一种方式最为常用;

第二种方式一般更多是当作参数使用;

第三种方式较为局限,必须已经有了这个类的对象,才能使用。

2.获取构造方法(Constructor对象)

(1)Class类中用于获取构造方法的方法

Ⅰ. 返回多个构造方法对象

注意:

getConstructors只能获取被 public 修饰的构造方法,否则报错

getDeclaredConstructors可以获取所有的构造方法。

Ⅱ. 返回单个构造方法对象 

注意:

①  getConstructor 只能获取被 public 修饰的单个构造方法,否则报错

        getDeclaredConstructor 可以获取所有的单个构造方法。

② 这两个方法的参数必须与对应的构造方法保持一致

(2)Constructor类中用于创建对象的方法

 注意:

① 权限修饰符对应的常量值如下:

② 由于该构造方法是 private 修饰,外界访问不到,所以不能直接创建对象,也就会抛出异常。

      可以使用暴力反射,来强行创建其对象。

3.获取成员变量(Field对象)

(1)Class类中用于获取成员变量的方法

Ⅰ. 返回多个成员变量对象

Ⅱ. 返回单个成员变量对象 

注意:方法中的实参为:成员变量名

(2)Field类中用于创建对象的方法

 

public class Demo3 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
        //1.获取class字节码文件对象
        Class clazz = Class.forName("com.mihoyo.field.Student");

        //2.返回单个成员变量对象
        Field field = clazz.getDeclaredField("name");

        //获取成员变量的权限修饰符
        int modifiers = field.getModifiers();
        System.out.println(modifiers);

        //获取成员变量的名字
        String name = field.getName();
        System.out.println(name);

        //获取成员变量的数据类型
        Class<?> type = field.getType();
        System.out.println(type);

        //获取成员变量记录的值
        Student s = new Student("zhangsan", 23, "男");
        //暴力反射
        field.setAccessible(true);
        //get方法的参数:成员变量所属的对象
        String value = (String) field.get(s);
        System.out.println(value);

        //修改成员变量记录的值(参数:对象名,修改后的值)
        field.set(s, "lisi");
        System.out.println(s);
    }
}

 运行结果:

 

4.获取成员方法(Method对象)

(1)Class类中用于获取成员变量的方法

Ⅰ. 获取多个成员方法对象

public class Demo1 {
    public static void main(String[] args) throws ClassNotFoundException {
        //1.获取class字节码文件对象
        Class clazz = Class.forName("com.mihoyo.method.Student");

        //2.获取所有的public成员方法对象
        Method[] methods1 = clazz.getMethods();
        for (Method method : methods1) {
            System.out.println(method);
        }

        System.out.println("----------------------");

        //获取所有的成员方法对象
        Method[] methods2 = clazz.getDeclaredMethods();
        for (Method method : methods2) {
            System.out.println(method);
        }
    }
}

 运行结果:

注意:

①  getMethods 获取的 public 成员方法,包含父类中所有的 public 成员方法

② getDeclaredMethods 获取的成员方法,不能获取父类的,只能获取本类中所有的

Ⅱ. 获取单个成员方法对象

注意:

指定单个成员方法时,第一个参数为方法名,后面的参数为方法的形参。

(因为方法可以重载,相同的方法名,不同的形参)。 

(2)Method类中用于创建对象的方法

public class Demo3 {
    public static void main(String[] args) throws NoSuchMethodException, ClassNotFoundException, InvocationTargetException, IllegalAccessException {
        //1.获取class字节码文件对象
        Class clazz = Class.forName("com.mihoyo.method.Student");

        //2.获取单个指定的成员方法
        Method method = clazz.getDeclaredMethod("eat", String.class);

        // 获取方法的修饰符
        int modifiers = method.getModifiers();
        System.out.println(modifiers);

        // 获取方法的名字
        String name = method.getName();
        System.out.println(name);

        // 获取方法的形参
        Parameter[] parameters = method.getParameters();
        for (Parameter parameter : parameters) {
            System.out.println(parameter);
        }

        //获取方法的抛出的异常
        Class[] exceptionTypes = method.getExceptionTypes();
        for (Class exceptionType : exceptionTypes) {
            System.out.println(exceptionType);
        }

        //运行方法
        Student s = new Student();
        //暴力反射
        method.setAccessible(true);
        //参数一:表示方法的调用者
        //参数二:表示在调用方法的时候传递的实参
        String result = (String) method.invoke(s, "汉堡包");
        System.out.println(result);
    }
}

运行结果:

 5.作用

 Question1:对于任意一个对象,设计一个方法,将对象的所有字段名和值,保存到文件中去。

//把对象里面所有的成员变量名和值保存到本地文件中
public static void saveObject(Object obj) throws IllegalAccessException, IOException {
    //1.获取字节码文件的对象
    Class clazz = obj.getClass();
    //2. 创建IO流
    BufferedWriter bw = new BufferedWriter(new FileWriter("reflect\\a.txt"));
    //3. 获取所有的成员变量
    Field[] fields = clazz.getDeclaredFields();
    for (Field field : fields) {
        field.setAccessible(true);
        //获取成员变量的名字
        String name = field.getName();
        //获取成员变量的值
        Object value = field.get(obj);
        //写出数据
        bw.write(name + "=" + value);
        bw.newLine();
    }
    bw.close();
}

 Question2:利用反射,结合配置文件(.properties),动态创建对象,并调用方法

public class MyReflectDemo {
    public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        //1.读取配置文件中的信息
        Properties prop = new Properties();
        FileInputStream fis = new FileInputStream("myreflect\\prop.properties");
        prop.load(fis);
        fis.close();
        System.out.println(prop);

        //2.获取全类名和方法名
        String className = (String) prop.get("classname");
        String methodName = (String) prop.get("method");

        System.out.println(className);
        System.out.println(methodName);

        //3.利用反射创建对象并运行方法
        Class clazz = Class.forName(className);

        //获取构造方法
        Constructor con = clazz.getDeclaredConstructor();
        Object o = con.newInstance();
        System.out.println(o);

        //获取成员方法并运行
        Method method = clazz.getDeclaredMethod(methodName);
        method.setAccessible(true);
        method.invoke(o);
    }
}

细节:

① 由于类名和方法名是通过配置文件得到的,得到的类名也是一个字符串。

所以创建对象时,需要利用多态创建一个 Object 对象。

② 通过修改配置文件中的类名和方法名,就可以动态创建不同的对象,调用不同的方法。

  • 15
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值