Java基础(下)之反射

反射 idea编辑器代码提示的底层

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

获取class对象的三种方式

  1. 源代码阶段,获取类的字节码文件:Class.forName("全类名") 全类名 = 包名.类名,最常用
  2. 加载阶段:类名.class,一般当作参数传入,例如synchronize(Student.class){}
  3. 运行阶段:对象.getClass() 具有局限性,需要提前创建该类的对象
    image
class Main {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        //方式一:编译时期 通过Class.forName(全类名)读取字节码文件获取Class类
        Class clazz1 = Class.forName("com.example.helloworld.Student");

        //方式二:加载时期 通过类名.class获取Class类
        Class clazz2 = Student.class;

        //方式三:运行时期 通过对象名.getClass获取Class类
        Student s1 = new Student("ZJQ",24);
        Class clazz3 = s1.getClass();

        System.out.println(clazz1 == clazz2);//true
        System.out.println(clazz2 == clazz3);//true
    }
}

获取类的构造方法

image
Java中,可以使用访问控制符来保护对类、变量、方法和构造方法的访问。Java 支持 4 种不同的访问权限。

  • default (即默认,什么也不写): 在同一包内可见,不使用任何修饰符。使用对象:类、接口、变量、方法。
  • private : 在同一类内可见。使用对象:变量、方法。 注意:不能修饰类(外部类)
  • public : 对所有类可见。使用对象:类、接口、变量、方法
  • protected : 对同一包内的类和所有子类可见。使用对象:变量、方法。 注意:不能修饰类(外部类)。

获取修饰符:Constructor.getModifiers
获取名字:Constructor.getName
获取形参:Constructor.getParameters
创建对象:Constructor.newInstance

public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        //方式一:编译时期 通过Class.forName(全类名)读取字节码文件获取Class类
        Class clazz1 = Class.forName("com.example.helloworld.Student");

        Constructor[] cons1 = clazz1.getConstructors();//获取所有公开的构造方法
        Constructor[] cons2 = clazz1.getDeclaredConstructors();//获取所有构造方法(包括私有)

        for (Constructor con : cons1) {
            System.out.println(con);
        }
        System.out.println();
        for (Constructor con : cons2) {
            System.out.println(con);
        }

        System.out.println();

        //获取单个公开的构造方法
        Constructor con1 = clazz1.getConstructor(String.class,int.class);
        System.out.println(con1);
        System.out.println(con1.getModifiers());//获取该方法的修饰符 1:public
        Student s1 = (Student) con1.newInstance("ZJQ",24);//利用构造方法创建对象
        System.out.println(s1);

        //获取单个任意构造方法,包括私有
        Constructor con2 = clazz1.getDeclaredConstructor();
        System.out.println(con2);
        System.out.println(con2.getModifiers());//获取该方法的修饰符 2:private
        con2.setAccessible(true);//暴力反射:临时取消权限校验,否则无法创建对象
        Student s2 = (Student) con2.newInstance();//利用构造方法创建对象
        System.out.println(s2);
    }

获取成员变量

image

class Main {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchFieldException {
        //方式一:编译时期 通过Class.forName(全类名)读取字节码文件获取Class类
        Class clazz1 = Class.forName("com.example.helloworld.Student");

        //获取类的所有成员变量,包括私有
        Field[] fields1 = clazz1.getDeclaredFields();
        for (Field field : fields1) {
            System.out.println(field);
        }
        System.out.println();
        //获取类的所有公开成员变量
        Field[] fields2 = clazz1.getFields();
        for (Field field : fields2) {
            System.out.println(field);
        }

        //获取类的某个公开成员变量
        Field name = clazz1.getField("gender");
        System.out.println(name);
        Field age = clazz1.getDeclaredField("age");
        System.out.println(age);

        System.out.println(name.getName());//获取成员变量的名称
        System.out.println(name.getModifiers());//获取成员变量的修饰符
        System.out.println(name.getType());//获取成员变量的类型

        Constructor con1 = clazz1.getConstructor(String.class,int.class,String.class);
        Student s1 = (Student) con1.newInstance("ZJQ",24,"男");
        age.setAccessible(true);//暴力反射:临时取消权限校验,否则无法访问私有属性
        int value = (int)age.get(s1);//获取对象中属性的值
        age.set(s1,value + 1);//设置对象中属性的值
        System.out.println(s1);
    }
}

获取成员方法

image

class Main {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchFieldException {
        //方式一:编译时期 通过Class.forName(全类名)读取字节码文件获取Class类
        Class clazz1 = Class.forName("com.example.helloworld.Student");

        //获取所有公共的方法对象(包括继承自父类的所有公共方法)
        Method[] methods = clazz1.getMethods();
        for (Method method : methods) {
            System.out.println(method);
        }
        System.out.println();
        //获取此类的所有方法对象(不包括父类的,含私有方法)
        Method[] methods1 = clazz1.getDeclaredMethods();
        for (Method method : methods1) {
            System.out.println(method);
        }

        //获取某个公共方法
        Method method1 = clazz1.getMethod("eat", String.class);
        System.out.println(method1);
        //获取某个任意方法(含私有方法)
        Method method2 = clazz1.getDeclaredMethod("sleep", int.class);
        System.out.println(method2);

        //获取方法的名称
        System.out.println(method2.getName());
        //获取方法的权限修饰符
        System.out.println(method2.getModifiers());

        //获取方法的参数
        Parameter[] params = method2.getParameters();
        for (Parameter param : params) {
            System.out.println(param);
        }

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

        Constructor con1 = clazz1.getConstructor(String.class,int.class,String.class);
        Student s1 = (Student)con1.newInstance("ZJQ", 24, "man");
        //暴力反射:临时取消权限校验,否则无法访问私有方法
        method2.setAccessible(true);
        //调用某个对象的该方法
        int res = (int)method2.invoke(s1, 10);
        System.out.println(res);
    }
}

反射到底有什么用?

  1. 获取某个类里面的所有信息(内部结构),根据类内部结构执行其它业务逻辑;
  2. 结合配置文件,动态的创建对象并调用对象;

示例一:对于任意一个对象,都可以把对象所有的字段名和值,保存到文件中去

public static void save_obj(Object o, File path) throws IOException, IllegalAccessException {
        if(o == null || path == null) throw new NullPointerException();
        Class clazz = o.getClass();
        File file = new File(path,clazz.getName());
        BufferedWriter bw = new BufferedWriter(new FileWriter(file));
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            String name = field.getName();
            field.setAccessible(true);
            Object value = field.get(o);
            bw.write(name + "=" + value.toString() + '\n');
        }
        bw.close();
    }
}

示例二:从配置文件中读取对象并调用方法

package com.example.helloworld;
import java.io.*;
import java.lang.reflect.*;
import java.util.Properties;

class Main {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchFieldException, IOException {
        //1.读取配置文件中的信息
        File config_file = new File("prop.properties");
        FileReader fr = new FileReader(config_file);
        Properties prop = new Properties();//配置文件类
        prop.load(fr);//读取文件
        fr.close();
        System.out.println(prop);

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

        //3.通过反射获取类,构造方法和成员方法,创建对象,运行成员方法
        Class clazz = Class.forName(className);
        Method method = clazz.getMethod(methodName,param.getClass());
        Constructor con1 = clazz.getDeclaredConstructor();
        con1.setAccessible(true);
        Object obj = con1.newInstance();
        method.setAccessible(true);
        method.invoke(obj,what);
    }
}

动态代理

特点:无侵入的给代码增加各种额外的功能
代理中就是对象要被代理的方法,Java中通过接口实现代理,代理和对象需要实现同一个接口,接口中就是被代理的所有方法。
image

为Java对象创建一个代理

image
案例:现有BigStar类,具有sing、dance方法需要被代理,请为其创建一个代理,通过代理调用sing和dance时。
BigStar.java

package com.example.helloworld;

public class BigStar implements Star{
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public BigStar(String name) {
        this.name = name;
    }

    @Override
    public String sing(String name){
        System.out.println(this.name + "正在唱"+ name);
        return "谢谢";
    }

    @Override
    public void dance(){
        System.out.println(this.name + "正在跳舞");
    }

}

Star.java

package com.example.helloworld;

public interface Star {
    //我们可以把所有想要被代理的方法定义在接口中

    public abstract String sing(String name);

    public abstract void dance();

}

ProxyUtil.java

package com.example.helloworld;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class ProxyUtil {
    /// 给一个明星对象创建代理并返回
    public static Star createProxy(BigStar bigStar){
        // 参数一:用于指定哪个类加载器,去加载生成的代理类,一般指定为当前类的加载器即可
        // 参数二:指定接口的数组,这些接口用于指定生成的代理长什么样,即有哪些方法
        // 参数三:用来指定生成的代理对象要干什么事情
        Star star = (Star) Proxy.newProxyInstance(ProxyUtil.class.getClassLoader(),
                new Class[]{Star.class},
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        if("sing".equals(method.getName())){
                            System.out.println(bigStar.getName() + "的代理准备话筒,收钱");
                        }else if("dance".equals(method.getName())){
                            System.out.println(bigStar.getName() + "的代理准备服装、场地,收钱");
                        }
                        //调用被代理的对象的方法,若方法有返回值,需要返回
                        return method.invoke(bigStar,args);
                    }
                }
        );
        return star;
    }

    public static void main(String[] args) {
        BigStar bigStar = new BigStar("cxk");
        Star proxy = createProxy(bigStar);
        String res = proxy.sing("只因你太美");
        System.out.println(res);
        proxy.dance();
    }
}
posted @ 2026-03-05 15:35  安河桥北i  阅读(2)  评论(0)    收藏  举报