JAVA反射机制是在
运行状态中,对于任意一个
类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
JAVA反射(放射)机制:“
程序
运行时,允许改变程序结构或
变量
类型,这种语言称为
动态语言
”。从这个观点看,Perl,Python,Ruby是
动态语言
,C++,Java,C#不是动态语言。但是JAVA有着一个非常突出的动态相关机制:Reflection,用在Java身上指的是我们可以于运行时加载、探知、使用编译期间完全未知的classes。换句话说,Java程序可以加载一个运行时才得知名称的class,获悉其完整构造(但不包括methods定义),并生成其对象实体、或对其fields设值、或唤起其methods。
来点代码展现反射形态:
Object obj = new Person("xxc",99);
//获得Class的实例的三种方式
//1.通过对象的getClass()方法获得
Class clazz1 = obj.getClass();
//2.通过表示类名的字符串来获得,注意:这里的字符串必须是完整路径,否则找不到这个类
String className = "com.xxc.reflet.Person";
Class clazz2 = Class.forName(className);
//3.用类名.class的方式获取
Class clazz3 = Person.class;
System.out.println(clazz3);
System.out.println(clazz1 == clazz3);//true
System.out.println(clazz1 == clazz2);//true
System.out.println(clazz3 == clazz2);//true
//获取基本数据类型的Class对象,void也是一种类型。
Class intClazz = int.class;
System.out.println(intClazz);//打印int
Class voidClazz = void.class;
System.out.println(voidClazz);//打印void
/*
* 获得类中定义的所有属性
* getField(String name)返回一个属性,是这个类或接口当中的公共成员字段 返回类型 Field
* getFields()返回这个类或接口中所有公共成员字段 返回类型 Field[]
*
* getDeclaredField(String name)返回一个已声明的属性,可以是这个类或接口中任意权限的字段 返回类型 Field
* getDeclaredFields() 返回这个类或借口中所有已声明的成员字段,任意权限 返回类型 Field[]
*/
Class clazz = Person.class;
Field[] fields = clazz.getDeclaredFields();
for(Field f: fields){
System.out.println("属性名 "+f.getName());
System.out.println("类型: "+f.getType());
}
/*
* 用反射获取类中的成员字段:
* 1.先根据这个属性的名字,拿到这个属性
* 2.设置反射的对象在使用时应该取消 Java 语言访问检查,说白了就是不受权限的限制,可以访问private
* 3.返回指定对象上此 Field 表示的字段的值。 get(obj)参数的obj表示一个对象。返回obj对象里的字段。
*/
Field nameField = clazz.getDeclaredField("name");
nameField.setAccessible(true);
Object value = nameField.get(obj);
System.out.println(value);
/*
* 利用反射来改某个对象里的属性,第一个参数表示要修改哪个对象,第二个表示被修改属性的值。
* 至于修改哪个属性,并不是在参数里指定的,调用set方法的那个类就表示哪个属性
*/
nameField.set(obj, "xxm");
System.out.println(obj);
/*
* 获得类中定义的所有方法
* getMethod(String name, Class<?>... parameterTypes) 返回这个类或接口中公共的方法 返回类型Method 第一个参数表示哪个对象,第二个参数表示参数类型 int.class
* 因为光是指定函数名没用,重载的话就不能明确指出是哪一个方法。
* getMethods() 返回这个类或接口中所有公共的方法 返回类型是Method[]
*
* getDeclaredMethod(String name, Class<?>... parameterTypes) 返回这个类或接口中任意权限的方法 返回类型是Method
*
* getDeclaredMethods() 返回这个类或接口中任意权限的所有方法 返回类型是Method[]
*/
Object obj = new Person("xxc",99);
Class clazz = obj.getClass();
Method[] methods = clazz.getDeclaredMethods();
for(Method m : methods){
System.out.println(m.getName());//返回方法名
System.out.println(m.getReturnType().getName());//返回方法的返回类型,如果直接是m.getReturnType()则会在返回类型前加class java.lang.String
Class[] parameterTypes = m.getParameterTypes();//获取方法中的参数类型,并封装在一个数组中
for(Class parameterType : parameterTypes){//遍历参数类型数组
System.out.print("参数类型 "+parameterType.getName());//获取参数类型名称
}
}
Method method = clazz.getDeclaredMethod("run",String.class,int.class,String.class);//获取需要调用的方法对象 第一个参数是方法名字,第二个参数是参数类型
method.invoke(obj, "xxc",20,"男");//调用方法,第一个参数是具体对象,第二个参数是方法的实参
/*通过反射获取构造函数,以及其中的参数类型,构造函数名称,用构造函数创建对象。
*getConstructors() 获取某一个类中所有的公共权限构造函数
*getConstructor(Class<?>... parameterTypes) 获取某一个类中指定参数类型的公共权限构造函数
*
*getDeclaredConstructors() 获取某一个类中所有的任意权限的构造函数
*getDeclaredConstructor(Class<?>... parameterTypes) 获取某一个类中指定参数类型的任意权限的构造函数
*/
Object obj = new Person("xxc",99);
Class clazz = obj.getClass();
Constructor[] constructors = clazz.getDeclaredConstructors();
for(Constructor constructor : constructors){
System.out.println("构造函数名字"+constructor.getName());
System.out.println("构造函数的参数类型:");
Class[] parametersTypes = constructor.getParameterTypes();
for(Class parametersType : parametersTypes){
System.out.print(parametersType.getName());
System.out.println();
}
}
//调用有参构造函数,创建对象
/*
* 下面这句话的第二个参数能写数组的原因是因为在1.5前,这里的第二个参数就是数组类型。
* 它和可变参数最主要的区别就是,当调用的函数是没有参数的,那么可变参数的可以不用写,而用数组类型表示的需要写null
*/
Constructor constructor = clazz.getDeclaredConstructor(String.class,int.class);
//Constructor constructor = clazz.getDeclaredConstructor(new Class[]{String.class,int.class});JDK1.5之前用的
Person person = (Person)constructor.newInstance("xxx",99);//使用反射获取的有参构造函数创建对象
System.out.println(person);
persion类
public class Person {
private String name;
private int age;
Person(String name,int age){
this.name = name;
this.age = age;
}
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
public void run(String str,int age,String sex){
System.out.println(str+" "+age+" "+sex);
}
}
获取类或方法前的修饰:
Package pack = clazz.getPackage();//获取某个类的包名
System.out.println("包名--->"+pack.getName());
int num = clazz.getModifiers();//可以获取类的修饰内容
/*
* 以下这个方法这样用有局限性,因为一个类既有public修饰,又有abstract修饰
* Modifier.PUBLIC=1
* Modifier.abstract=1024
* 那么此时有两个修饰就是1025,所以再用Modifier.PUBLIC就不能正确判断了。
* 这时可以用Modifier.isPublic(num)来进行判断。
*/
if(num == Modifier.PUBLIC){
System.out.println("这个类的修饰符是PUBLIC");
}
if(Modifier.isPublic(num)){
System.out.println("这个类的修饰符是PUBLIC");
}
Method run = clazz.getDeclaredMethod("run",String.class,int.class,String.class);
if(Modifier.isPublic(run.getModifiers())){//函数中也有获取修饰符类型的方法
System.out.println("Run方法是PUBLIC修饰的");
练习:通过反射,做一个面向接口的,能计算任何形状面积的方法。
创建接口(此接口中有计算面积的抽象方法):
package com.xxc.reflet.test;
public interface InterfaceTest {
double getArea();
}
实现接口的类(圆)
package com.xxc.reflet.test;
public class ChangFangXing implements InterfaceTest{
private double height;
private double width;
public double getArea() {
return height*width;
}
}
实现接口的类(长方形)
package com.xxc.reflet.test;
public class ChangFangXing implements InterfaceTest{
private double height;
private double width;
public double getArea() {
return height*width;
}
}
测试:
package com.xxc.reflet.test;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.lang.reflect.Field;
public class RefletTest {
public static void main(String[] args) throws Exception {
System.out.println("请输入要计算的图形名称");
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String name = br.readLine();
Class clazz;
try {
clazz = Class.forName("com.xxc.reflet.test."+name);
} catch (Exception e) {
System.out.println("不存在这样的图形");
return;
}
InterfaceTest ift = (InterfaceTest)clazz.newInstance();
Field[] parameterType = clazz.getDeclaredFields();//获得所有的数据域
for(Field f : parameterType){
System.out.println("请输入:"+f.getName());
double num = Double.parseDouble(br.readLine());
f.setAccessible(true);//将所有的域设置为可访问的。
f.setDouble(ift, num);
}
double area = ift.getArea();
System.out.println("图形面积是:"+area);
}
}