反射: 框架设计的灵魂
框架:半成品软件。可以在框架的基础上进行软件的开发,简化编码
反射:将类的各个组成部分封装为其他对象,这就是反射机制。
将类的成员变量封装为Field对象,构造函数封装为Constructor对象,将成员方法封装为Method对象
反射的好处:
- 在程序的运行过程中,操作这些对象。 eg: 在程序中输入这样一行代码:
String str="abc"; str.
输入str.时会提示你一大堆的方法,这些方法从哪来的,这里就运用了反射机制,你在此处定义了一个字符串,会把字符串的字节码文件加载进内存,在内存中有一个class类对象,它已经把所有的方法抽取出来,封装为Method对象,把所有的方法放进method数组里,提示时,只需把数组里的每一个数组成员拿出来,名字显示出来,展示在列表里就可以了 - 可以解耦(降低程序的耦合性),提高程序的可扩展性
(反射:将类的各个组成部分封装为其他对象,想要获取和操作这些对象关键是获取字节码 Class类对象)
获取Class对象的方式:(三种方式分别对应着java代码经历的三个阶段)
- (Java代码在第一个阶段,意味着你的Java代码只有字节码文件,没有进内存,我们需要手动的加载进内存,生成一个字节码文件,生成一个Class对象)
Class.forName("全类名");
将字节码文件加载进内存,返回Class对象 ------多用于配置文件,将类名定义在配置文件中。读取文件,加载类 - (已经将字节码文件加载进内存了,还没有真正的类对象)
类名.class;
通过类名的属性class来获取-----多用于参数的传递 - (已经有类对象了,通过对象的方法获取)
对象.getClass();
这个方法封装在object类里,在object类中定义,被所有对象继承下来------多用于对象的获取字节码的方式
同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,不论通过哪一种方式获取的Class对象都是同一个
class对象功能:
获取的功能:
在反射面前没有私有公有之分,都可以设置 获取到
使用的Person类的定义
package cn.cast.domain;
public class Person {
private String name;
private int age;
public String a;
protected String b;
String c;
private String d;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public Person() {
}
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;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", a='" + a + '\'' +
", b='" + b + '\'' +
", c='" + c + '\'' +
", d='" + d + '\'' +
'}';
}
public void eat(){
System.out.println("eat...");
}
public void eat(String food){
System.out.println("eat..."+food);
}
}
Field :成员变量
操作:
1.设置值 void set(Object obj,Object value)
2.获取值get(Object obj)
3.忽略访问权限修饰符的安全检查setAccessible(true);
暴力反射
package cn.cast.reflect;
import cn.cast.domain.Person;
import java.lang.reflect.Field;
public class ReflectDemo2 {
public static void main(String[] args) throws Exception {
//1.获取person的Class对象
Class personclass= Person.class;
/**
* 获取成员变量们
* Field[] getFields():获取所有public修饰的成员变量
* Field getField(String name) 获取指定名称的public修饰的成员变量
* Field[] getDeclaredFields():获取所有的成员变量,不考虑修饰符
* Field getDeclaredField(String name) 获取指定名称的成员变量
*/
//1.Field[] getFields()
Field[] fields=personclass.getFields();
for(Field field:fields){
System.out.println(field);
}
System.out.println("-----------");
//2.Field getField(String name)
Field a=personclass.getField("a");
//获取成员变量a的值
Person p=new Person();
Object value=a.get(p);//以前获取成员变量的值是 p.a 现在是a.get()把对象传进去,因为成员变量是在对象内部存在,我们需要传一个真正的对象
System.out.println("value = " + value);
a.set(p,"roxanne");
System.out.println(p);
System.out.println("-----------");
//3.Field[] getDeclaredFields();获取所有的成员变量
Field[] declaredfields= personclass.getDeclaredFields();
for (Field declaredfield : declaredfields) {
System.out.println(declaredfield);
}
//4.Field getDeclaredField(String name)
Field d= personclass.getDeclaredField("d");
//私有的不能直接被访问,所以我们需要忽略访问权限修饰符的安全检查
d.setAccessible(true);//暴力反射
Object value2=d.get(p);
System.out.println("value2 = " + value2);
}
}
Constructor:构造方法
创建对象:
T newInstance(Object... initargs)
如果使用空参数构造方法创建对象,操作可以简化:Class对象的newInstance方法
package cn.cast.reflect;
import cn.cast.domain.Person;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
public class ReflectDemo3 {
public static void main(String[] args) throws Exception {
//1.获取person的Class对象
Class personclass= Person.class;
/**
* 获取构造方法们
* Constructor<?>[] getConstructor()
* Constructor<T>[] getConstructor(类<?>...parametersTypes)
* Constructor<T>[] getDeclaredConstructor(类<?>...parametersTypes) 使用私有的构造器创建对象,也需要调用构造器对象的setAccessible()方法来使用
* Constructor<?>[] getDeclaredConstructor()
*/
Constructor constructor=personclass.getConstructor(String.class,int.class);//构造方法通过传的参数不同来区分,要求的参数类型是不同参数的class对象
System.out.println("constructor = " + constructor);//constructor = public cn.cast.domain.Person(java.lang.String,int)
//创建对象
Object person=constructor.newInstance("roxanne",23);
System.out.println("person = " + person);//person = Person{name='roxanne', age=23, a='null', b='null', c='null', d='null'}
System.out.println("-----------");
//利用空参来创建
Constructor constructor1=personclass.getConstructor();//构造方法通过传的参数不同来区分,要求的参数类型是不同参数的class对象
System.out.println("constructor1 = " + constructor1);//constructor1 = public cn.cast.domain.Person()
//创建对象
Object person1=constructor1.newInstance();
System.out.println("person1 = " + person1);//person1 = Person{name='null', age=0, a='null', b='null', c='null', d='null'}
Object o=personclass.newInstance();
System.out.println("o = " + o);//o = Person{name='null', age=0, a='null', b='null', c='null', d='null'}
}
}
Method:方法对象
执行方法:
Object invoke(Object obj,Object... args)//需要传真实对象和方法实际的参数列表
获取方法名称: String getName:获取方法名
package cn.cast.reflect;
import cn.cast.domain.Person;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
public class ReflectDemo4 {
public static void main(String[] args) throws Exception {
//1.获取person的Class对象
Class personclass= Person.class;
/**
* 获取成员方法们
* 不带declared的只能获取公共的,带declared不考虑修饰符,什么都能获取到,但要使用setAccessible()暴力反射
* Method[] getMethods()
* Method getMethod(String name,类<?>...parameterTypes)
*
* Method[] getDeclaredMethods()
* Method getDeclaredMethod(String name,类<?>...parameterTypes)
*/
//获取指定名称的方法
Method eatmethod=personclass.getMethod("eat");//方法三要素 方法名 返回值列表 参数列表,确定一个方法需要两个参数 方法名 参数列表(因为重载是方法名一样 参数列表不一样)
Person p=new Person();
//执行方法
eatmethod.invoke(p);
Method eatmethod2=personclass.getMethod("eat",String.class);
eatmethod2.invoke(p,"饭");
System.out.println("----------");
//获取所有public修饰的方法
Method[] methods= personclass.getMethods();
for (Method method : methods) {
System.out.println(method);
System.out.println(method.getName());
//不仅只有你设定的 还有继承自object的方法
}
//获取类名
String className=personclass.getName();
System.out.println("className = " + className);//className = cn.cast.domain.Perso
}
}