「这是我参与2022首次更文挑战的第22天,活动详情查看:2022首次更文挑战」
前言
说到这个反射这个概念就不用我多说了,java反射有三种方式可以获取到类对象,同时也对应到了java的类在计算机当中的三种状态。
源码阶段
class类对象阶段
Runtime阶段
这三种方式就得到了一个类。
同一个字节码文件三种方式获取的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);
}
}
}
查看结果
这种方式只会返回public的结果,所以此时我想要获取所有的。
Field[] fields = cls.getDeclaredFields();
那么现在我们能够获取,那么其实我们也可以直接设置一个值,但是我们得搞清楚,就是我们设置一个值是对一个对象设置一个值,所以我们得找到一个对象,没对象怎么操作。所以先忙我们先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);
}
}
之后我们就看到了
但是不够,我想要设置这个私有的行不行,当然可以但是这里要注意设置一下权限(也就暴力反射)
不过这里注意我们现在设置一下这个age(设置这个Adress也是OK的),所以我们得设置一下那个get方法。
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
此时我们就可以直接通过类加载器读取路径。 首先单独这样的方法是获取编译之后的根目录,相当于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下面就不用了。 以上其实就是关于反射的内容其实很简单。