Java反射
简介
Reflection(反射)是Java被视为动态语言的关键。
反射机制允许程序在执行期借助Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。
加载完类之后,在堆内存的方法区中就产生了一个Class类型对象(一个类只有一个Class对象)。
这个对象就包含了完整的类的结构信息,可以通过这个对象看到类的结构。
这个对象像一面镜子,透过镜子看到类的结构,所以形象的称为:反射。
Class类
对象照镜子后可以得到的信息:某个类的属性,方法和构造器,某个类实现的接口
- Class本身也是一个类
- Class对象只能由系统建立对象
- 一个加载的类在JVM中只会有一个Class实例
- 一个Class对象应的是一个加载到HVM的.class文件
- 每个类的实例都会记得自己由那个Class实例所生成
- 通过Class可以完整的得到一个类中的所有被加载的结构
- Class类是Reflection的根源,针对任何你想动态加载,运行的类,唯有先获得相应的Class对象
Class类得常用方法
反射相关得主要API
Class类与java.lang.reflect类库一起对反射的概念进行了支持。
该类库包含了Field、Method以及Constructor类(每个类都实现了Member接口)。
- Java.lang.Class:代表一个类
- Java.lang.reflect.Method:代表类的方法
- Java.lang.reflect.Field:代表的成员变量
- Java.lang.reflect.Constructor:代表的构造器
这些类型的对象是由JVM在运行时创建的。
获取运行时类的完整结构
获取Class类得实例
每个类都有一个Class对象。
一旦某个类的Class对象被载入内存,它就被用来创建这个类的所有对象。
方式一
如果你已经拥有一个类型的对象,那就可以通过调用getClass()方法来获取Class引用了。
Student student=new Student();
Class c1=student.getClass();
方式二
使用Class.forName(),你不需要为了获得Class引用而持有该类型的对象。
可能抛出ClassNotFoundException。
Class c2=null;
try {
c2=Class.forName("text.Student"); //text包里的Student类
} catch(ClassNotFoundException e) {
System.out.println("运行时异常");
}
注意:在传递给Class.forName()的字符串中,你必须使用全限定名(包含包名)。
方式三
通过类名.class获得,该方法最为安全可靠,程序性能最高。
Class c3=Student.class;
注意:由.class来创建Class对象的引用时,不会自动初始化Class对象。
方式四
基本内置类型的包装类都有。
类字面常量不仅可以用于普通的类,也可以应用于接口、数组以及基本数据类型。
对于基本数据类型的包装类,还有一个标准字段TYPE。
TYPE字段是一个引用,指向对应的基本数据类型的Class对象。
Class<Integer> c4=Integer.TYPE;
获得父类类型
Class c5=c1.getSuperclass();
获取类的所有信息
//获得Class对象
Class c1=User.class;
//获得类的名称
System.out.println(c1.getName()); //获得包名 + 类名
System.out.println(c1.getSimpleName()); //获得类名
//获得类的属性
Field[] fs=c1.getFields(); //只能找到public属性
fs=c1.getDeclaredFields(); //找到全部属性
//获得指定属性
Field name= c1.getDeclaredField("name"); //参数属性名
//获得类的方法
Method[] methods = c1.getMethods(); //获得本类及其父类的所有public方法
methods=c1.getDeclaredMethods(); //获得本类的所有方法,包括私有的(private、protected、默认以及public)的方法。
//获得指定方法,如果方法有参数,就需要放入对应的参数类型
//public Method[] getMethod(methodName,parameterTypes);
//参数methodName:表示获取的方法的名字,parameterTypes:表示获取的方法的参数的Class类型
Method shows= c1.getMethod("shows",String.class);
//获得构造器
Constructor[] constructors = c1.getConstructors();//获得本类及其父类的所有public构造器
Constructor[] declaredConstructors = c1.getDeclaredConstructors();//获取当前Class所表示的类的所有构造器(包括public、protected、private修饰的)
//获得指定构造器
Constructor declaredConstructor = c1.getDeclaredConstructor();
递归获取父类
Class tempClass = c.class;
//获取自己和所有父类的属性(包括Object类的属性)
while (tempClass != null) {//当父类为null的时候说明到达了最上层的父类(Object类).
fieldList.addAll(Arrays.asList(tempClass .getDeclaredFields()));
tempClass = tempClass.getSuperclass(); //得到父类,然后赋给自己
}
//想屏蔽Object类的影响,可以为while循环再添加一个条件
!tmpClass.getName().toLowerCase().equals("java.lang.object")
动态的创建对象,通过反射
//获得Class对象
Class c1=User.class;
//构造一个对象
User user1 =(User)c1.newInstance(); //本质是调用类的无参构造器
//通过构造器创建对象
Constructor constructor = c1.getDeclaredConstructor(String.class);
User user2 =(User)constructor.newInstance("wkf");
//通过反射调用普通方法
User user3=(User)c1.newInstance();
//通过反射获取一个方法
Method show= c1.getDeclaredMethod("show",String.class);
//invoke:激活(对象,方法参数)
show.invoke(user3, "锵锵锵");
//通过反射操作属性
User user4=(User)c1.newInstance();
Field name=c1.getDeclaredField("name");
//不能直接操作私有属性,需要关闭程序的安全检测,属性或者方法的setAccessible(true)
name.setAccessible(true);//关闭权限检测
name.set(user4,"wkf");
创建类的对象:调用Class对象的newInstance()方法
- 类必须有一个无参构造方法
- 类的构造方法访问权限需要足够
setAccessible
Method和Field,Constructor对象都有setAccessible(true)方法
作用:启动和禁用访问安全检查的开关
参数为true则指示的对象在使用时取消Java语言访问检查
- 提高反射的效率
- 使得原本无法访问的私有成员也可以访问
参数值为false则指示的对象应该实施Java语言访问检查
性能对比分析
反射操作泛型
Java采用泛型擦除的机制来引入泛型
Java中的泛型仅仅是给编译器Javac使用的,确保数据的安全和免区强制类型转换问题
但是,一旦编译完成,所有和泛型有关的类型全部擦除。
为了通过反射操作这些类型,Java新增了ParameteizedType,GenericArrayType,TypeVariable和WildcardType几种类型来代表不能被归一到Class类中的类型
但是又和原始类型齐名的类型
ParameterizedType //表示一种参数化类型,比如Collection<String>
GenericArrayType //表示一种元素类型是参数化类型或者类型变量的数组类型
TypeVariable //是各种类型变量的公共接口
WildcardType //代表一种通配符类型表达式
示例
//获取方法
Method method = User.class.getMethod("test01",Map.class,List.class);
//获取泛型的参数类型
Type[] genericParameterTypes = method.getGenericParameterTypes();
for (Type t : genericParameterTypes) {
System.out.println(t);
//判断是否是结构化参数类型
if (t instanceof ParameterizedType) {
//强转,获得真实参数信息
Type[] actualTypeArguments = ((ParameterizedType)t).getActualTypeArguments();
for (Type tt:actualTypeArguments) {
System.out.println(tt);
}
}
}
反射操作注解
//通过反射获得注解
public class Test12 {
public static void main(String[] args) throws NoSuchFieldException, SecurityException {
Tablex annotation = People.class.getAnnotation(Tablex.class);//获得指定注解
//通过反射获得注解
Annotation[] annotations = People.class.getAnnotations();
for (Annotation annotation2 : annotations) {
System.out.println(annotation2);
}
System.out.println(annotation.value());
Field field = People.class.getDeclaredField("id");
Fieldx f = field.getAnnotation(Fieldx.class);
System.out.println(f.col());
System.out.println(f.type());
System.out.println(f.legth());
}
}
@Tablex("db_People")
class People{
@Fieldx(col="id",type="int",legth = 10)
private int id;
@Fieldx(col="name",type="varcher",legth = 3)
private String name;
@Fieldx(col="age",type="int",legth = 10)
private int age;
public People() {
super();
}
public People(int id, String name, int age) {
super();
this.id = id;
this.name = name;
this.age = age;
}
public int getId() {
return id;
}
public void setId(int id) {
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;
}
@Override
public String toString() {
return "People [id=" + id + ", name=" + name + ", age=" + age + "]";
}
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Tablex{
String value();
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface Fieldx{
String col();
String type();
int legth();
}