2.反射的基本使用和理解
一,反射概述
1.概念
java语言属于静态语言,最基本的步骤我们都是写好代码(代码中的类和生成的对象都写死固定了)先进行编译,然后进行执行运行,
而反射的出现,给了java动态语言的一些特性,通过反射,我们可以通过反射的API,写好代码(此时的代码中的类和对象是不确定的),
也就是说编译阶段我们不能确定我们要造哪个类的对象,但是也把代码进行编译了,反射就可以允许程序在执行期间借助于Reflection API获得任何类的任何信息(属性,方法,构造器等),运行时候确定执行哪个类,这就是反射。
JVM加载完类之后, 在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象), 这个对象就包含了完整的你想生成的对象的类的所有信息。重点理解Class类型的对象,其实就是JVM中有一个唯一的类Class,而这个Class类会通过JVM加载不同的.class文件,生成对应的类的类对象,然后通过类对象生成你想要的对象实例。这点有点绕。
我们可以通过这个Class对象看到所有加载进JVM中的类的结构。 这个Class类对象就像一面镜子, 透过这个镜子看到所有类的结构,我们形象的称之为: 反射。
通过上面的图我们这样理解,JVN加载不同的xx.class字节码文件,然后JVM中默认有一个唯一的Class类,这个Class类通过读取xx.class字节码文件,对应生成了代表一个xx类的对象,这个对象不是我们程序调用的某个类对象,而是包含了xx类的所有信息的一个类对象。
举个例子:我们定义了一个Person类,编译成Person.class字节码文件并加载到JVM中,然后JVM中的Class类读取该Person.class文件并加载生成了一个Class类的对象,假如这个对象我们叫做personClass,这个对象包含了我们Person类的所有信息(属性,方法,权限修饰符,注解,构造器,等等一切信息),并且Class类对每一个class字节码文件生成的对象是唯一的,然后我们就可以利用这个对象personClass去实例化我们的person对象,这个person对象才是我们平时用的对象。
也就是说我们在生成一个类比如Person类的对象之前,JVM会对应的先通过Class类生成一个唯一类对象我们叫做personClass,也就是Person类的类对象personClass,然后通过该类对象personClass去生成我们对应的实例对象person。
2. 反射的基本使用和理解
首先先通过简单的使用反射,看看反射都可以获得什么,可以干什么,然后知道反射的基本使用和理解,然后再返回来分析反射获得的东西是内在原理。
2.1 反射的使用
首先我们定义一个Person类
package com.ethan.reflection;
public class Person {
private String name;
public int age;
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
private Person(String name) {
this.name = name;
}
public Person() {
System.out.println("Person()");
}
public void show(){
System.out.println("我是一个人");
}
private String showNation(String nation){
System.out.println("我来自" + nation);
return nation;
}
}
2.1 获取某个类的类对象
获取Person类的类对象的方式:
package com.ethan.reflection;
public class Demo01 {
public static void main(String[] args) throws Exception {
//1.创建了一个Person类的类对象,通过该对象可以得到Person类的所有信息
Class clazz1 = Person.class;//方式1
Class clazz2 = Class.forName("com.ethan.reflection.Person");//方式2
}
}
2.2 获取某个类的构造器对象
记住,万物皆对象,那么对于一个类中的方法,属性,注解,构造器都可以定义为一个类,也就可以生成对应的对象。
得到构造器的对象就可以利用该对象生成person对象,并且通过API得到Person类的构造器的其他信息
package com.ethan.reflection;
import java.lang.reflect.Constructor;
public class Demo01 {
public static void main(String[] args) throws Exception {
//1.创建了一个Person类的类对象,通过该对象可以得到Person类的所有信息
Class clazz1 = Person.class;//方式1
Class clazz2 = Class.forName("com.ethan.reflection.Person");//方式2
//2.得到clazz对象中的person类的public的构造器
Constructor cons = clazz1.getConstructor(String.class,int.class);
//2.得到clazz2对象中的person类的所有构造器
Constructor[] declaredConstructors = clazz2.getDeclaredConstructors();
//2.遍历出所有构造器的名字
for (Constructor declaredConstructor : declaredConstructors) {
System.out.println(declaredConstructor.getName());
}
}
}
2.3 通过构造器对象生成Person类的实例对象
//1.创建了一个Person类的类对象,通过该对象可以得到Person类的所有信息
Class clazz = Class.forName("com.ethan.reflection.Person");//方式2
//2.得到clazz对象中的person类的public的构造器
Constructor cons = clazz.getConstructor(String.class,int.class);
//3.利用该构造器生成person对象
Person pObj = (Person) cons.newInstance("Tom",12);
System.out.println(pObj.toString());
2.4 获取某个类的某个属性对象,并为这个具体的属性赋值
package com.ethan.reflection;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
public class Demo01 {
public static void main(String[] args) throws Exception {
//1.创建了一个Person类的类对象,通过该对象可以得到Person类的所有信息
Class clazz = Class.forName("com.ethan.reflection.Person");//方式2
//2.得到clazz对象中的person类的public的构造器
Constructor cons = clazz.getConstructor(String.class,int.class);
//3.利用该构造器生成person对象
Person pObj = (Person) cons.newInstance("Tom",12);
//4.通过反射调用person类的public类型的属性,并给Person类生成的pObj对象的该属性赋值。
Field field = clazz.getDeclaredField("age");
field.set(pObj, 88);
System.out.println(pObj.toString());
}
}
2.5 获取某个类的某个方法,并传参执行该方法
注意:对于私有的方法需要设置权限为true,才能够执行成功。
public class Demo01 {
public static void main(String[] args) throws Exception {
//1.创建了一个Person类的类对象,通过该对象可以得到Person类的所有信息
Class clazz = Class.forName("com.ethan.reflection.Person");//方式2
//2.得到clazz对象中的person类的public的构造器
Constructor cons = clazz.getConstructor(String.class,int.class);
//3.利用该构造器生成person对象
Person pObj = (Person) cons.newInstance("Tom",12);
//4.通过反射调用person类的public类型的属性,并给Person类生成的pObj对象的该属性赋值。
Field field = clazz.getDeclaredField("age");
field.set(pObj, 88);
System.out.println(pObj.toString());
//5.通过反射调用person类的public类型的show()方法,并执行
Method show = clazz.getDeclaredMethod("show");
show.invoke(pObj);
System.out.println("*******************************");
Method showNation = clazz.getDeclaredMethod("showNation", String.class);
//对于私有的方法需要设置权限为true
showNation.setAccessible(true);
showNation.invoke(pObj,"中国");
System.out.println(showNation.getName());
}
}
对于类的私有的属性,也都是可以获取和执行的,不再一一详述,代码如下:
public class Demo01 {
public static void main(String[] args) throws Exception {
//1.创建了一个Person类的类对象,通过该对象可以得到Person类的所有信息
Class clazz = Class.forName("com.ethan.reflection.Person");//方式2
//2.得到clazz对象中的person类的public的构造器
Constructor cons = clazz.getConstructor(String.class,int.class);
/*
* 通过反射,可以调用Person类的私有结构的。比如:私有的构造器、方法、属性
*/
//调用私有的构造器
Constructor cons1 = clazz.getDeclaredConstructor(String.class);
cons1.setAccessible(true);
Person p1 = (Person) cons1.newInstance("ethan");
System.out.println(p1);
//调用私有的属性
Field field2 = clazz.getDeclaredField("name");
field2.setAccessible(true);
field2.set(p1, "java");
System.out.println(p1);
//调用私有的方法
@SuppressWarnings("unchecked")
Method showNation = clazz.getDeclaredMethod("showNation",String.class);
showNation.setAccessible(true);
String nation = (String)showNation.invoke(p1, "zh_CN");
System.out.println(nation);
}
}
2.2 使用总结
-
反射创建对象跟直接new对象,怎么使用?
反射创建对象的前提是涉及到动态性和不确定性时候使用的,比如在javaweb时候,java代码已经在服务器后台运行着了,这时候你操作客户端不确定你是登陆网页还是注册网页,这时候就是不确定性和动态性,通过反射和客户端的相关url,可以动态的创建需要的对象。
在不涉及不确定性时候,大多数还是通过new对象来实现的。 -
反射与面向对象中的封装性是否矛盾?
不矛盾,面向对象的封装其实是建议性行为,私有的方法属性是供该类内部使用的,同样的功能是有更好的公有方法提供给你,一般私有的方法是供内部其他方法使用,不建议开发者使用的。而反射也不是说能够调用私有的,你就非得去使用私有的。大多数私有的方法其实只是为了强化类中公共的方法,提高了开发者调用的公共方法的安全性和健壮性,本质上私有的方法本来就不是让开发者使用的功能,是为类内部服务的,只能说两者部分矛盾,更多的根据情况使用。用户通过反射机制获取了所使用的类中私有成员的存取权限,这是毫无意义的,因为我们上面分析过了,大多数的这些成员是依附于其它public方法而存在的,基本上都是为了服务这些public成员的。
3. 获取运行时类的完整结构
其实就是上面讲的反射的基本使用的延申,就是将类的所有信息都获得一遍,这些信息包括这个类的构造器,方法,属性,注解,泛型,子类父类等等,便于我们利用这些信息,使用反射可以动态的生成我们想要的对象。有空再补充。