——Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ——-
1.反射概述
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法; 对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
动态获取类中信息,就是java反射。可以理解为对类的解剖。
如果想要对指定名称的字节码文件进行加载并获取其中的内容并调用,这时就使用到了反射机制。
2.获取字节码对象的三种方式
a:Object类的getClass()方法,判断两个对象是否是同一个字节码文件
b:静态属性class,锁对象
c:Class类中静态方法forName(),读取配置文件
package cn.itcast.bean;
public class Person
{
private int age;
private String name;
public Person(int age,String name){
super();
this.age = age;
this.name = name;
System.out.println("Person param run..." + this.name + ":" + this.age);
}
public Person(){
super();
System.out.println("person run");
}
public void show(){
System.out.println(name + "...show run..." + age);
}
private void privateMethod(){
System.out.println("method run");
}
public void paramMethod(String str,int num){
System.out.println("paramMethod run..." + str + ":" + num);
}
public static void staticMethod(){
System.out.println("static method run...");
}
}
import cn.itcast.bean.Person;
//要想要对字节码文件进行解剖,必须要有字节码文件对象。
public class ReflectDemo
{
public static void main(String[] args) throws ClassNotFoundException {
getClassObject_1();
System.out.println("--------------------");
getClassObject_2();
System.out.println("--------------------");
getClassObject_3();
}
/*
* 获取字节码对象的方式:
* 方式一:Object类中的getClass()方法的。
* 想要用这种方式,必须要明确具体的类,并创建对象。
* 麻烦。
*/
public static void getClassObject_1(){
Person p = new Person();
Class clazz = p.getClass();
Person p1 = new Person();
Class clazz1 = p1.getClass();
System.out.println(clazz == clazz1);
}
/*
* 方式二:任何数据类型都具备一个静态的属性.class来获取其对应的Class对象。
* 相对简单,但是还是要明确用到类中的静态成员。
* 还是不够扩展。
*/
public static void getClassObject_2(){
Class clazz = Person.class;
Class clazz1 = Person.class;
System.out.println(clazz == clazz1);
}
/*
* 方式三:只要通过给定的类的字符串名称就可以获取该类,更为扩展。
* 可以用Class类中的方法完成。
* 该方法就是forName。
* 这种方法只要有名称即可,更为方便,扩展性更强。
*/
public static void getClassObject_3() throws ClassNotFoundException {
//可以把类的字符串名称写到配置文件中,然后读取出来。
String className = "cn.itcast.bean.Person";
Class clazz = Class.forName(className);
System.out.println(clazz);
}
}
结果为:
person run
person run
true
--------------------
true
--------------------
class cn.itcast.bean.Person
示例:获取Class中的构造函数
package com.heima.reflect;
import java.lang.reflect.Constructor;
import com.heima.bean.Person;
/*
* Class类的newInstance()方法是使用该类无参的构造函数创建对象,
* 如果一个类没有无参的构造函数, 就不能这样创建了,
* 可以调用Class类的getConstructor(String.class,int.class)方法
* 获取一个指定的构造函数
* 然后再调用Constructor类的newInstance("张三",20)方法创建对象
*/
public class Demo3_Constructor {
public static void main(String[] args) throws Exception {
// TODO 自动生成的方法存根
Class clazz = Class.forName("com.heima.bean.Person");
//Person p = (Person) clazz.newInstance(); 通过无餐构造创建对象
//System.out.println(p);
Constructor c = clazz.getConstructor(String.class,int.class); //获取有参构造
Person p = (Person) c.newInstance("张三",23); //通过有参构造创建对象
System.out.println(p);
}
}
结果为:
Person [name=张三, age=23]
示例:获取Class的字段
package com.heima.reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import com.heima.bean.Person;
/*
* Class.getField(String)方法可以获取类中的指定字段(可见的),
* 如果是私有的可以用getDeclaedField("name")方法获取,(可以获取任意权限的字段,getField只能获取public修饰的字段)
* 通过set(obj, "李四")方法可以设置指定对象上该字段的值,
* 如果是私有的需要先调用setAccessible(true)设置访问权限,
* 用获取的指定的字段调用get(obj)可以获取指定对象中该字段的值
*/
public class Demo4_Field {
public static void main(String[] args) throws Exception {
// TODO 自动生成的方法存根
Class clazz = Class.forName("com.heima.bean.Person"); //获取Person的字节码文件
Constructor c = clazz.getConstructor(String.class,int.class); //获取有参构造
Person p = (Person) c.newInstance("张三",29); //通过有参构造对象
// Field f = clazz.getField("name"); //获取姓名字段
// f.set(p, "李四"); //修改姓名的值
// System.out.println(p);
Field f = clazz.getDeclaredField("name"); //暴力反射获取字段
f.setAccessible(true); //去除私有权限
f.set(p, "李四");
System.out.println(p);
}
}
结果为:
Person [name=李四, age=29]
示例:获取Class中的方法
package com.heima.reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import com.heima.bean.Person;
/*
* * Class.getMethod(String, Class...) 和
* Class.getDeclaredMethod(String, Class...)方法
* 可以获取类中的指定方法,调用invoke(Object, Object...)
* 可以调用该方法,Class.getMethod("eat") invoke(obj)
* Class.getMethod("eat",int.class) invoke(obj,10)
*/
public class Demo5_Method {
public static void main(String[] args) throws Exception {
// TODO 自动生成的方法存根
Class clazz = Class.forName("com.heima.bean.Person");
Constructor c = clazz.getConstructor(String.class,int.class);
Person p = (Person)c.newInstance("张三",23);
Method m = clazz.getMethod("eat");
m.invoke(p);
Method m1 = clazz.getMethod("eat",int.class);
m1.invoke(p, 10);
}
}
结果为:
今天吃了一顿金钱豹
今天吃了10顿金钱豹
注意: 所谓的框架就是对外提供一些接口,也就是功能扩展的标准,由实现类按照这个接口标准去实现。框架内部如果需要操纵这些实现类的对象完成某些操作,那么只需要把这些实现类的全名(包名+类名)写在某个配置文件中,框架代码只需要读取这个配置文件,就可以获取这个实现类的字节码文件,然后利用反射技术创建这个实现类的对象并且调用相应的方法完成一些操作。
用于描述字节码的类就是Class类,创建对象,可以提取字节码文件中的内容,如字段、构造函数、一般函数。该类就可以获取字节码文件中的所有内容,那么反射就是依靠该类完成的。想要对一个类文件进行解剖,只要获取到该类的字节码文件对象即可。