JAVA反射学习小结
将类的各个组成部分封装为其他对象
可以参照一下上面类加载的图片,可以知道反射就是将里面的成员方法封装为Method对象
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BDGXOTvL-1626659342271)(D:\Program Files (x86)]\IDEAProJect\Java入门笔记\Java入门\java进阶\image-20210713131749315.png)
好处
-
可以再程序运行期间 , 操作这些对象
-
可以解耦 , 提高程序的可扩展性
Class对象的获取方式
因为要使用反射来获取成员方法构造方法和成员变量需要用到Class类对象,所以需要学会获取Class类对象
以下为获取方法
- 在源码阶段使用
- Class.forName(“全类名”); //将字节码文件加载进内存,返回class对象
- 多用于配置文件中,将类名定义在配置文件中.
- 用于读取文件,加载类
- Class.forName(“全类名”); //将字节码文件加载进内存,返回class对象
- 在Class对象阶段
- 类名.class //通过类名的class属性来获取
- 多用于参数的传递
- 类名.class //通过类名的class属性来获取
- 运行时阶段使用
- 对象.getClass(); //在Object中定义的通用方法
- 用于对象来获取字节码文件
- 对象.getClass(); //在Object中定义的通用方法
//1.Class.forName("全类名");
//这里的全类名指的就是将package后面的内容加上 .文件名
Class cls1 = Class.forName("po.Person");
System.out.println(cls1);
//class po.Person
//2. 类名.class
Class cls2 = Person.class;
System.out.println(cls2);
//3. 对象.class();
Person p = new Person();
Class cls3 = p.getClass();
System.out.println(cls3);
//同一个字节码文件(.class)在一次程序运行中只会被加载一次,无论通过哪一种方式获取的Class对象都是同一个
Class对象的功能
获取功能
获取成员变量
- Field[] getFields() 获取所有public修饰的成员变量
- Field getField(String name) 获取指定的public修饰的成员变量
- Field[] getDeclaredFields()
- Field getDeclaredField(String name)
Class personClass = Person.class;
Field[] fields = personClass.getFields();
for (Field field : fields) {
//遍历获取到的成员变量们
System.out.println(field);
}
System.out.println("------------");
Field a = personClass.getField("a");
//成员变量有2个方法,get和set
Person p = new Person();
p.a = "abc";
Object o = a.get(p);
System.out.println(o);
//输出abc,为a变量所对应的值
a.set(p,"张三");
System.out.println(p.a);
//输出张三,为a所被修改后的值
System.out.println("------------");
Field[] declaredFields = personClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println(declaredField);
//获取成员方法们
/*这个是不考虑修饰符的
* private java.lang.String po.Person.Name
private int po.Person.age
public java.lang.String po.Person.a
protected java.lang.String po.Person.b
java.lang.String po.Person.c
private java.lang.String po.Person.d*/
}
Field name = personClass.getDeclaredField("Name");
p.setName("王五");
//这样直接获取到的私有的成员变量会报错
//所以要使用暴力反射
name.setAccessible(true);
Object o1 = name.get(p);
System.out.println(o1);
//输出设置的name为王五
成员变量的使用
//这里的a是获取的成员变量,定义在Person这个类中的public String a;
//成员变量有2个方法,get和set
Person p = new Person();
p.a = "abc";
Object o = a.get(p);
System.out.println(o);
//输出abc,为a变量所对应的值
a.set(p,"张三");
System.out.println(p.a);
//输出张三,为a所被修改后的值
获取构造方法们
- Constructor<?>[] getConstructors()
- Constructor getConstructor(类<?>… parameterTypes)
- Constructor getDeclaredConstructor(类<?>… parameterTypes)
- Constructor<?>[] getDeclaredConstructors()
Class personClass = Person.class;
Constructor constructor = personClass.getConstructor(String.class, int.class);
//因为用于区分构造方法的主要是靠传入的参数,所以这里我们传入String和int类型的数据
System.out.println(constructor);
//获取到的constructor对象主要是用来创建对象用的
Object object = constructor.newInstance("张三", 18);
System.out.println(object);
//Person{Name='张三', age=18, a='null', b='null', c='null', d='null'},调用对应的Person的toString方法
System.out.println("-----------------");
//也可以使用空参的构造器
Constructor constructor1 = personClass.getConstructor();
Object o = constructor1.newInstance();
//这样这里就不用传递参数了
System.out.println(o);
//Person{Name='null', age=0, a='null', b='null', c='null', d='null'}
//为了简化这部分的操作,class里面也有个newInstance方法可以使用
Object o1 = personClass.newInstance();
System.out.println(o1);
//用于调用空参的构造方法
//Person{Name='null', age=0, a='null', b='null', c='null', d='null'}
//constructor.setAccessible(true);
//这里一样可以使用暴力破解的方法
获取成员方法们
- Method[] getMethods()
- Method getMethod(String name, 类<?>… parameterTypes)
- Method[] getDeclaredMethods()
- Method getDeclaredMethod(String name, 类<?>… parameterTypes)
Class personClass = Person.class;
Method eat_Method = personClass.getMethod("eat");
//获取方法名称
Person p = new Person();
eat_Method.invoke(p);
//这个invoke需要传入对象和传入参数
//我在吃饭
//用传入的参数.class来区分重构的方法
Method eat_Method2 = personClass.getMethod("eat",String.class);
eat_Method2.invoke(p,"shit");
//我在次shit
System.out.println("-------------------");
//获取所有public修饰的方法
Method[] methods = personClass.getMethods();
for (Method method : methods) {
System.out.println(method);
//这里打印的除了Person中定义的public方法,还有Object所定义的方法
String name = method.getName();
System.out.println("方法名为"+name);
//可以像下面这样获取方法
if(name.equals("setAge")){
method.invoke(p,18);
}
}
System.out.println(p);
//Person{Name='null', age=18, a='null', b='null', c='null', d='null'}
获取类名
- String getName()
Class personClass = Person.class;
//获取类名
String name = personClass.getName();
System.out.println(name);
//po.Person
通过反射越过泛型检查
如果在定义一些含有泛型的变量的时候,在调用该变量的一些方法就会有一个泛型的检查(如定义一个ArrayList的集合),这里可以通过反射来绕开这个检查
ArrayList<String> strList = new ArrayList<>();
strList.add("aaa");
strList.add("bbb");
// strList.add(100);
//获取ArrayList的Class对象,反向的调用add()方法,添加数据
Class listClass = strList.getClass(); //得到 strList 对象的字节码 对象
//获取add()方法
Method m = listClass.getMethod("add", Object.class);
//这里传入Object.class是关键
//调用add()方法
m.invoke(strList, 100);
//遍历集合
for(Object obj : strList){
System.out.println(obj);
}
//aaa
//bbb
//100
案例
写一个框架,在不改变该类的任何代码的情况下,可以创建任意的对象并执行其中任意的方法
实现
- 配置文件
- 反射
/*加载配置文件*/
Properties pro = new Properties();
//使用的这个Properties类可以将 .properties后缀的文件加载进内存
ClassLoader classLoader = 案例.class.getClassLoader();
//获取目前执行的这个案例的类加载器
InputStream is = classLoader.getResourceAsStream("pro.properties");
pro.load(is);
//加载进内存后就是获取方法和类
String className = pro.getProperty("className");
String methodName = pro.getProperty("methodName");
//获取类对象,将该类加载进内存
Class cls = Class.forName(className);
//创建对象
Object o = cls.newInstance();
//获取方法对象
Method method = cls.getMethod(methodName);
//运行方法
method.invoke(o,"shi");