Java学习——Java动态性之反射机制(reflection)
动态性:
- 动态语言:程序在运行时,可以改变程序结构或者变量类型。典型的动态语言:Python、ruby、JavaScript等。
- C、C++、Java不是动态语言,但是Java可以称之为准动态语言。但是Java有一定的动态性、我们可以利用反射机制、字节码操作获得类似于动态语言的特性。这样就使得Java更加灵活
反射机制:
- 指的是在运行时再加载一些新的类
- 程序在运行状态时,可以动态加载一个只有名称的类,对于任意一个已经加载的类都能知道这个类的所有信息(属性、方法);对于任何一个对象,都能调用它的任何一个属性和方法
- 这个就是我们在反射中常用的一段代码,用来通过类名获取这个类的字节码对象
Class clazz = Class.forName("cn.xiyou.Scanner.MyTest");
- 加载完类之后,在堆内存中就产生了一个class类型的对象(一个类只有一个class对象),这个对象包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像是一面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为:反射。
利用反射获取相关信息:
先创建一个标准的实体类:
public class Student {
private String name;
private int age;
private int id;
public Student() {
}
public Student(String name, int age, int id) {
this.name = name;
this.age = age;
this.id = id;
}
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;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
反射机制获取相关信息:
public class Test01 {
public static void main(String[] args) throws Exception {
/**
* 对象是表示或封装一些数据。
* 一个类被加载后,JVM会创建一个对应该类的Class对象,类的整个结构信息会被放到对应的Class对象中去
*/
//利用反射获取类的信息(类名、属性、方法、构造器),注意:一个类只有一个Class对象
Class clazz = Class.forName("cn.反射机制.demo01.Student");
/*
* 1.获取类的名字
*/
String clazzName = clazz.getName();//(包名+类名)
System.out.println(clazzName);
String clazzSimpleName = clazz.getSimpleName();//类名
System.out.println(clazzSimpleName);
System.out.println("----------------这是一个华丽的分割线----------------");
/*
* 2.获得属性信息
*/
//获取public的属性
Field[] fields = clazz.getFields();
for (Field field : fields) {
System.out.println(field);
}
//获取所有的属性(不论public和private)
Field[] declaredFields = clazz.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println("属性:"+declaredField);
}
//获取某个属性(不论public和private)
Field name = clazz.getDeclaredField("name");
System.out.println(name);
System.out.println("----------------这是一个华丽的分割线----------------");
/*
*3.获取方法信息
*/
//获得所有public的方法
Method[] methods = clazz.getMethods();
for (Method method : methods) {
System.out.println(method);
}
//获取所有的方法(不论public和private)
Method[] declaredMethods = clazz.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
System.out.println("方法:"+declaredMethod);
}
//获取指定方法,参1:方法名,参2:传递的参数类型的class对象
Method declaredMethod = clazz.getDeclaredMethod("getAge");
System.out.println(declaredMethod);
Method declaredMethod1 = clazz.getDeclaredMethod("setAge", int.class);
System.out.println(declaredMethod1);
System.out.println("----------------这是一个华丽的分割线----------------");
/*
*4.获取构造方法信息
*/
//获取所有的构造器
Constructor[] constructors = clazz.getDeclaredConstructors();
for (Constructor constructor : constructors) {
System.out.println("构造器:"+constructor);
}
//获取无参构造
Constructor constructor = clazz.getDeclaredConstructor(null);
System.out.println("无参构造:"+constructor);
//获取有参构造
Constructor constructor1 = clazz.getDeclaredConstructor(String.class,int.class,int.class);
System.out.println("有参构造:"+constructor1);
}
}
通过反射动态操作:构造器、方法、属性:
/**
* 通过反射动态操作:构造器、方法、属性
*/
public class Test02 {
public static void main(String[] args) throws Exception {
Class clazz = Class.forName("cn.反射机制.demo01.Student");
/*
* 1.通过反射动态操作构造方法,构造对象
*/
//通过调用的是无参构造器构造对象
Student student = (Student) clazz.newInstance();
System.out.println(student);
//通过调用的是有参构造器构造对象
Constructor c = clazz.getDeclaredConstructor(String.class,int.class,int.class);
Student student2 = (Student) c.newInstance("张三", 18, 02165055);
System.out.println(student2.getName()+"==="+student2.getAge()+"==="+student2.getId());
/*
* 2.通过反射操作方法
*/
//通过反射调用方法
Student student3 = (Student) clazz.newInstance();
Method method = clazz.getDeclaredMethod("setName", String.class);
method.invoke(student3,"李四");
/*
* 3.通过反射操作属性
*/
Student student4 = (Student) clazz.newInstance();
Field f = clazz.getDeclaredField("name");
//setAccessible(true)意思是:这个属性关闭安全检查,直接可以访问(就可以访问private的属性了),提高效率
f.setAccessible(true);
f.set(student4,"王五");//给student4的name设置值
String sname = (String) f.get(student4);//获取student4的name值
System.out.println(sname);
}
}
- 我们还需要知道,反射固然强大,但是它在执行效率上还是不如正常执行。那么我们可以适当的使用setAccessible()打开关闭访问检查用来提高效率。
反射操作泛型(Generic)(了解):
Java中的泛型是在编译器给javac使用的,确保数据的安全性和免掉强制类型转换的麻烦。但是一旦编译完成,所有的泛型都会被擦除。那么Java新增了ParameterizedType,GenericArrayType,TypeVariable和WildcardType几种类型来代表不能被归一到class类中的类型但是又和原始类型齐名的类型。
- ParameterizedType:表示一张参数化的类型,比如:Collection< String >
- GenericArrayType:泛型数组类型
- TypeVariable:是各种泛型变量的公共父接口
- WildcardType:代表一种通配符类型表达式,比如?,?extends Number,?super Integer
反射操作注解(了解):
public class Test03 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
Class clazz = Class.forName("cn.反射机制.demo01.Student");
//获取这个类的所有注解
Annotation[] annotations = clazz.getDeclaredAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
//获取类指定注解的值
MyTable tb = (MyTable) clazz.getAnnotation(MyTable.class);
System.out.println(tb.value());
//获得类的属性的注解
Field f = clazz.getDeclaredField("name");
MyField myField = f.getAnnotation(MyField.class);
System.out.println(myField.name()+"--"+myField.type()+"--"+myField.length());
}
}