Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法和属性,这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制,简单说:能动态获取类中的信息(类中所有),就是java的反射,也可以理解为对类的解剖
反射机制的基本应用场景:
比如一个程序的应用程序(TomCat),为了提高其扩展性,会对外暴露一个接口,在外部定义一个类实现这个接口,但是在应用程序内部无法new对象,所以应用程序会提供一个配置文件,使用接口者可以将写好的并且实现接口的类名,写进配置文件,而应用程序利用反射机制会获取到指定名称的类的字节码文件并加载其中的内容进行调用,这就是反射机制的基本应用。
总结:反射技术提高了程序的扩展性,且应用简单,用户和程序之间的桥梁变为了配置文件
想要对一个类文件进行解剖,只要获取该类的字节码文件即可。
现实中的事物,不断向上抽取其共性内容,可以得到类来描述事物,反射机制中,也是将字节码向上抽取,用Class来描述字节码文件,那么就可以产生对象,就可以提供方法,获取字节码文件的成员,如:名称、字段、构造函数、一般函数等所有内容,反射就是依靠该类来完成的
反射呢,就是拿到.class文件,得到构造器,....构造对象,反着用,而不是得到.java文件
要想解剖一个类,必须要得到其字节码文件,就必须用到Class对象
定义演示类
public class Man {
private int age;
private String name;
public Man() {
super();
System.out.println("Man run");
}
public Man(int age, String name) {
super();
this.age = age;
this.name = name;
System.out.println("Man run yes"+this.name+this.age);
}
public void show(){
System.out.println(age+"..show.."+name);
}
private void privatemethod(){
System.out.println(age+"..method.."+name);
}
public void print(int num,String line){
System.out.println(num+"..print.."+line);
}
public static void staticmethod(){
System.out.println("..staticmethod..");
}
}
获取Class对象方式
要想对字节码文件进行解剖,必须获取字节码文件对象
获取字节码文件对象的方式有三种:
1.Object类中的getClass方法
用这种方式,必须明确具体的类,并创建对象
public static void get_Class_Object() {
Man man1 = new Man();
Class<?> clas1 = man1.getClass();
Man man2 = new Man();
Class<?> clas2 = man2.getClass();
System.out.println(clas1==clas2);
}
2.任何数据类型都具备一个静态属性.class来获取其对应Class对象
相对简单,但是还是需要明确类中的静态成员,扩展性不高
public static void get_Class_static() {
Class<?> cls1 = Man.class;
Class<?> cls2 = Man.class;
System.out.println(cls1==cls2);
}
3.只要通过给定的类的字符串名称就可以获取该类(重点掌握)
可以用Class类中的方法来完成
该方法就是forName();这中方式只要有名称即可
API:forName(String className)
返回与带有给定字符串名的类或接口相关联的 Class
对象。
API:forName(String name, boolean initialize,ClassLoader loader)
使用给定的类加载器,返回与带有给定字符串名的类或接口相关联的 Class
对象。
public static void get_Class() throws ClassNotFoundException {
//G:\java\fan\bin\fan\Man.class
//而默认的classpath路径是src、bin
String classname = "fan.Man";//明确是哪个包中的类
Class<?> cls = Class.forName(classname);
System.out.println(cls);
}
注意抛异常
获取Class中的构造函数
Man man = new Man();以前new对象,先根据被new的类的名称,寻找该类的字节码文件,并加载进内存,并创建该字节码文件对象,并接着创建该字节码文件对应的Man对象
API:newInstance()
创建此 Class 对象所表示的类的一个新实例。如同用一个带有一个空参数列表的new
表达式实例化该类。如果该类尚未初始化,则初始化这个类。
实际上就是Man中的空参的构造函数
Man类中有空参构造函数
public static void reflect_Demo() throws ClassNotFoundException, InstantiationException, IllegalAccessException {
String classname = "fan.Man";
//找寻该名称类文件,并加载进内存,产生Class对象
Class<?> cls = Class.forName(classname);
Object obj = cls.newInstance();
}
Man类中无空参构造函数
Man man = new Man(11,"a");
如果直接调用上述方法,会抛出初始化异常InstantiationException
如果有空参构造函数,但是,是私有的private Man(){},会抛出无效访问异常IllegalAccessException
所以:当获取指定名称类中所体现的对象时,而对象初始化不使用空参数构造时,应该先获取其构造函数,通过字节码文件对象即可完成,getConstructor
API:getConstructor(Class<?>... parameterTypes)
返回一个 Constructor
对象,它反映此 Class
对象所表示的类的指定公共构造方法。
API:getDeclaredConstructors()
返回 Constructor
对象的一个数组,这些对象反映此 Class
对象表示的类声明的所有构造方法。
而Constructor对象中有方法new对象
API:newInstance(Object... initargs)
使用此 Constructor
对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。
public static void reflect_Demo() throws Exception {
String classname = "fan.Man";
Class<?> cls = Class.forName(classname);
//参数数组,一个int基本数据类型对应的字节码是int.class
Constructor<?> constructor = cls.getConstructor(int.class,String.class);
//获取到了指定构造函数的对象
Object obj = constructor.newInstance(11,"a");
}
只不过在配置文件的时候需要写明类名和构造函数的参数信息
PS:一般被反射的类一般都是带有空参,因为获取实例方便
获取Class中的字段
API:getField(String name)
返回一个 Field
对象,它反映此 Class
对象所表示的类或接口的指定公共成员字段。
API:getFields()
返回一个包含某些 Field
对象的数组,这些对象反映此 Class
对象所表示的类或接口的所有可访问公共字段。
API:getDeclaredField(String name)
返回一个 Field
对象,该对象反映此 Class
对象所表示的类或接口的指定已声明字段。
API:getDeclaredFields()
返回 Field
对象的一个数组,这些对象反映此 Class
对象所表示的类或接口所声明的所有字段。
public static void reflect_Demo() throws Exception {
String classname = "fan.Man";
Class<?> cls = Class.forName(classname);
Object object = cls.newInstance();
//Field field = cls.getField("name");//有age、name字段,只能获取公有
Field field = cls.getDeclaredField("name");//只能获取本类,包括私有
System.out.println(field);
//获取字段的值.........
//Object obj = field.get(object);
//System.out.println(obj);//反问失败因为其是私有的
//对私有字段的访问取消权限检查,-> 暴力访问
field.setAccessible(true);//Field的父类方法,修改其访问权限
field.set(object, "a");//设置值
Object obj = field.get(object);
System.out.println(obj);//
}
API:AccessibleObject 类是 Field、Method 和 Constructor 对象的基类。它提供了将反射的对象标记为在使用时取消默认 Java 语言访问控制检查的能力。
PS:不建议用暴力访问的方式访问数据
获取指定Class中的方法
获取一个无参方法
public static void reflect_Demo() throws Exception {
String classname = "fan.Man";
Class<?> cls = Class.forName(classname);
// Method[] methods = cls.getMethods();//获取本类及其父类所有公共方法
// methods = cls.getDeclaredMethods();//只获取本类中,所有方法,包括私有
// for(Method M : methods){
// System.out.println(M);
// }
//拿一个无参方法
Method method = cls.getMethod("show", null);//注意与拿构造函数不同,方法名,方法参数列表
//Object obj = cls.newInstance();//无参构造
//method.invoke(obj, null);
Constructor<?> constructor = cls.getConstructor(int.class,String.class);
Object obj = constructor.newInstance(1,"a");//有参构造
method.invoke(obj, null);//对带有指定参数的指定对象调用由此 Method 对象表示的底层方法。
}
获取有参方法
public static void reflect_Demo() throws Exception {
String classname = "fan.Man";
Class<?> cls = Class.forName(classname);
Method method = cls.getMethod("print", int.class,String.class);//方法参数列表
Object obj = cls.newInstance();
method.invoke(obj, 11,"a");//对获取到的方法进行调用
}
反射练习:
public class Computer {
public void run(){
System.out.println("Computer is run....");
}
public void UseInter(Inter p){//对外暴露接口,提高扩展性
if(p!=null){
p.open();
p.close();
}
}
}
public interface Inter {
public void open();
public void close();
}
public class NetCard implements Inter{
public void open() {
System.out.println("NetCard open....");
}
public void close() {
System.out.println("NetCard close...");
}
}
public class SoundCard implements Inter {
public void open() {
System.out.println("SoundCard open...");
}
public void close() {
System.out.println("SoundCard close...");
}
}
public class Main {
public static void main(String[] args) throws Exception{
Computer com = new Computer();
com.run();
//com.UseInter(new SoundCard());//多态,但是这样写需要修改已有代码
//利用放射机制来完成,获取Class文件,在内部实现创建对象
File conFile = new File("G:\\java\\fan\\bin\\fan\\inter.properties");
Properties pro = new Properties();
FileInputStream fis = new FileInputStream(conFile);
pro.load(fis);//将流中信息加载进集合
for(int x = 0; x < pro.size();x++){
String intername = pro.getProperty("Inter"+(x+1));
Class<?> cls = Class.forName(intername);//用Class去加载这个Inter子类
Object obj = cls.newInstance();//创建Inter对象
Inter p = (Inter)obj;
com.UseInter(p);
}
fis.close();
}
}
因为还没有学习XML,所以用properties代替
可以说是涵盖了反射的所有基础内容