类的反射机制
1.反射的概念
反射主要是指程序可以访问、检测和修改它本身状态或行为的一种能力。
在计算机科学领域,反射是一类应用,它们能够自描述和自控制。
这类应用通过某种机制来实现对自己行为的描述和检测,并能根据自身行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义。
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象
1.1 Java的反射机制
在Java中的反射机制,被称为Reflection 。 它允许运行中的Java程序对自身进行检查,并能直接操作
程序的内部属性或方法
注意一点JVM并不是在一开始就把一个程序就所有的类都加载到内存中,而是到用的时候才把它加载进
来,当Java虚拟机载入一个类的时候,它就会自动创建一个Class类()的实例来表示这个类,该实例中包
含了完整的类的结构信息。
ps:Class本身就是一个单独的类,java.lang.Class
这个对象就像一面镜子将类的结构展示给我们,所以我们形象的称之为“反射”![img]
图源:https://blog.csdn.net/sirie/article/details/81435090
1.2 反射的功能
1、在运行时判断任意一个对象所属的类;
2、在运行时构造任意一个类的对象;
3、在运行时判断任意一个类所具有的成员变量和方法;
4、在运行时调用任意一个对象的方法;
5、生成动态代理
之后会有举例说明
2 反射的三种方式
2.1 直接通过类名.Class获得
Class<User> userClass = User.class;
System.out.println(userClass);//class com.goktech.User
2.2 通过对象的getClass()方法获取
User user = new User();
Class userClass = user.getClass();
System.out.println(userClass);//class com.goktech.User
2.3 通过全类名获取
使用Class 类,但是可能会抛出classNotFoundException的异常
Class c = null;
try {
c = Class.forName("com.goktech.User");
System.out.println(c);//class com.goktech.User
System.out.println(c.getName());//com.goktech.User
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
3、反射中常用的方法
测试用的User类
public class User {
private String name;
public int age;
private String school;
public User() {
}
public User(String name, int age) {
this.name = name;
this.age = age;
}
private User(String name, int age, String school) {
this.name = name;
this.age = age;
this.school = school;
}
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 String getSchool() {
return school;
}
public void setSchool(String school) {
this.school = school;
}
public void say(){
System.out.println("什么都没说:");
}
public void say(String message){
System.out.println("说了:"+message);
}
public void say(String message,Integer age){
System.out.println("说了:"+message+",年龄:"+age);
}
@Override
public String toString() {
..........
}
}
通过全限定类名获取类
Class c = null;
try {
c = Class.forName("com.goktech.User");
//其他方法写这里
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
3.1 获取类的构造方法
注意:私有的都带有Declared,要看清每个constructor后有没有加s
//获取所有的构造方法(有参+无参、共有+私有)
Constructor[] constructors = c.getConstructors();
System.out.println(Arrays.toString(constructors));
//[public com.goktech.User(java.lang.String,int), public com.goktech.User()]
//获取公有的无参构造方法
Constructor constructor = c.getConstructor(null);
System.out.println(Arrays.toString(new Constructor[]{constructor2}));
//[public com.goktech.User()]
//获取公有的有参构造
constructor = c.getConstructor(new Class[]{String.class,int.class});
System.out.println(constructor);
//[public com.goktech.User(java.lang.String,int)]
//获取私有的有参构造
constructor = c.getDeclaredConstructor(new Class[]{String.class,int.class,String.class});
System.out.println(constructor);
//private com.goktech.User(java.lang.String,int,java.lang.String)
3.2 获取字段(属性)
//获取所有公有字段(私有不可获得)
Field[] fields = c.getFields();
for (Field field: fields) {
System.out.println(field); //public int com.goktech.User.age
}
//获取所有字段(私有+公有)
fields = c.getDeclaredFields();
for (Field field: fields) {
System.out.println(field);
//private java.lang.String com.goktech.User.name
// public int com.goktech.User.age
//private java.lang.String com.goktech.User.school
}
//获取指定字段的filed
Field name = c.getField("name");
//若这个字段是私有的得加上下面一行才能获取
//但是由于是私有字段还是不能访问,可以进行属性修改,结合实例化对象使用
name.setAccessible(true);
3.3 获取方法
//获取所有方法
Method[] methods = c.getDeclaredMethods();
System.out.println(Arrays.toString(methods));
//获取class对象的所有方法,包括父类
Method[] allMethods = c.getMethods();
//因为获取了父类的方法,所以这里打印出的的方法比上面多了Object类的方法
System.out.println(Arrays.toString(allMethods));
//获取指定参数名的方法
//第一个参数是方法的名字,后面参数是方法中的参数,没有就写null
Method method = c.getDeclaredMethod("setAge", int.class);
System.out.println(method);
3.4 使用反射创建对象
//方法一:通过Class的newInstance()方法,常用
Object o = c.newInstance();
//方法二通过Constructor的newInstance()方法 ,先使用Class对象获取指定的Constructor对象 ,再调用
//Constructor对象的newInstance
Field field = clazz.getField("password");
Constructor constructor = clazz.getConstructor();
Object object = constructor.newInstance();
System.out.println("实例化出来的对象:"+object);
//为属性设置值
field.set(object, "123456");
User user = (User)object;
System.out.println(user);
3.5 利用Java反射机制对参数的值进行修改
//获得类
Class c = Class.forName("com.goktech.User");
Object o = c.newInstance();//获得类的实例
//获得User类中指定的属性
Field nameField = c.getDeclaredField("name");
//因为是私有的所以要解除访问控制
nameField.setAccessible(true);
//通过nameField 的get方法可以获得name值
String name = (String) nameField.get(o);
System.out.println(name);//null
//通过通过nameField的set方法可以设置值
nameField.set(o,"zhangsan");
name = (String) nameField.get(o);
System.out.println(name);//zhangsan
3.6 invoke方法实现动态调用
//获得类
Class c = Class.forName("com.goktech.User");
Object o = c.newInstance();//获得类的实例
Method m1 = c.getDeclaredMethod("say", String.class);
Method m2 = c.getDeclaredMethod("say");
m1.invoke(o,"helloword");//说了:helloword
m2.invoke(o);//什么都没说:
Method m3 = c.getDeclaredMethod("say", String.class, Integer.class);
m3.invoke(c.newInstance(),"my name is..",20);//说了:my name is..,年龄:20