Java反射(review)

165 阅读4分钟

「这是我参与2022首次更文挑战的第22天,活动详情查看:2022首次更文挑战

前言

说到这个反射这个概念就不用我多说了,java反射有三种方式可以获取到类对象,同时也对应到了java的类在计算机当中的三种状态。

源码阶段

class类对象阶段

Runtime阶段

image.png

这三种方式就得到了一个类。

同一个字节码文件三种方式获取的Class对象都是同一个

通过这个对象,我们就能够去实例化一个对象。

当然这个都是不是我们的重点,重点是我们如何获取通过class对象来实例组装一个对象,也就是值的设置,这个在spring里面太常见了。通过反射我们就能获取对象,然后把这个对象在放在我们的对象池里面,也就是实现了单例的模式的对象管理。

获取成员变量

首先看到上面的那张图片我们是知道了,我们有三种东西,会在class里面,那么同样的确是可以获取到里面的所有内容的。在反射面前没有任何隐私可言。当然这里后面其实还有一个也是基于反射实现的一个玩意叫做内省,这个相当于一个加强包,这么理解,它里面是有一个叫做描述器的玩意,其实就是获取了我们上述的三种属性。这个很简单,我们可以直接使用反射直接实现。

我们先定义一个类

public class Dog {
    public String name;
    private Integer age;
    String Adress;

    public Dog() {
    }

    public Dog(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public void run() {
        System.out.println("run");
    }
    public void bark(String bark){
        System.out.println(bark);
    }

}

现在我查看所有的Field

public class Test {

    public static void main(String[] args) throws ClassNotFoundException {
       Class<?> cls =  Class.forName("com.huterox.Reflect.Dog");
        Field[] fields = cls.getFields();
        for(Field field:fields){
            System.out.println(field);
        }
    }
}

查看结果

image.png

这种方式只会返回public的结果,所以此时我想要获取所有的。

Field[] fields = cls.getDeclaredFields();

image.png

那么现在我们能够获取,那么其实我们也可以直接设置一个值,但是我们得搞清楚,就是我们设置一个值是对一个对象设置一个值,所以我们得找到一个对象,没对象怎么操作。所以先忙我们先new一个对象看看。

public class Test {

    public static void main(String[] args) throws Exception {
        Class<?> cls = Class.forName("com.huterox.Reflect.Dog");
        Dog dog = new Dog();
        Field name = cls.getField("name");
        name.set(dog,"小明");
        System.out.println(dog.name);
    }
}

之后我们就看到了

image.png

但是不够,我想要设置这个私有的行不行,当然可以但是这里要注意设置一下权限(也就暴力反射)

不过这里注意我们现在设置一下这个age(设置这个Adress也是OK的),所以我们得设置一下那个get方法。

image.png

public class Test {

    public static void main(String[] args) throws Exception {
        Class<?> cls = Class.forName("com.huterox.Reflect.Dog");
        Dog dog = new Dog();
        Field name = cls.getDeclaredField("age");
        name.setAccessible(true);
        name.set(dog,5);
        System.out.println(dog.getAge());
    }
}

这样就行了。

现在我们有一个输出。当然我们是通过new的方式先创建了一个对象,那么现在显然我们直接new的话还是自己在管理对象之间的关系,所以我们得通过反射,那么这里就是获取构造方法了。

获取构造方法

这个构造方法其实只有一种功能那就是帮助我们创建对象。

public class Test {

    public static void main(String[] args) throws Exception {
        Class<?> cls = Class.forName("com.huterox.Reflect.Dog");
        
        Dog dog = (Dog) cls.getConstructor(String.class,Integer.class).newInstance("小明",5);
//        cls.newInstance()无参构造
        System.out.println(dog);
    }
}

搞定。

获取方法

这个也是分两个嘛,一个是

Method[] methods = cls.getMethods();

还有是

Method[] methods = cls.getDeclaredMethods();

我们这边就调用两个方法吧。

public class Test {

    public static void main(String[] args) throws Exception {
        Class<?> cls = Class.forName("com.huterox.Reflect.Dog");
        Dog dog = (Dog) cls.newInstance();

        Method run = cls.getMethod("run");
        Method brak =cls.getMethod("bark", String.class);

        run.invoke(dog);
        brak.invoke(dog,"Fuck");
    }
}

那么基本上其实这些。但是这里我还想要说一下其他的东西例如类加载器加载资源

加载资源

首先是针对我们的在src下面的资源文件,有时候我们可能会在那里面配置资源文件。 例如: 现在读取这个hello.txt

image.png

此时我们就可以直接通过类加载器读取路径。 首先单独这样的方法是获取编译之后的根目录,相当于src目录。

public class Test {

    public static void main(String[] args) throws Exception {
        String path = Test.class.getClassLoader().getResource("").getPath();
        System.out.println(path);
    }
}

现在hello.txt就在下面,所以我们直接这样。

public class Test {

    public static void main(String[] args) throws Exception {
        String path = Test.class.getClassLoader().getResource("hello.txt").getPath();
        System.out.println(path);
    }
}

同时我们也可以直接使用getResource

public class Test {

    public static void main(String[] args) throws Exception {
        String path = Test.class.getResource("").getPath();
        System.out.println(path);
    }
}

这样返回的是当前类编译后所在的目录, 如果你这样


        String path = Test.class.getResource("/").getPath();

那么你得到的就是根目录。 所以这里hello.txt在根目录下(src),那么直接这样


        String path = Test.class.getResource("/hello.txt").getPath();

当然还有一种方式

InputStream resourceAsStream = Test.class.getResourceAsStream("hello.txt")

直接返回流, 这个里面其实写包名就行了 “com.huterox.xxx.txt”但是我们直接在src下面就不用了。 以上其实就是关于反射的内容其实很简单。