------- android培训、java培训、期待与您交流! ----------
黑马程序员----反射
静态编译:在编译的时候就可以确定类型,绑定对应的对象即可通过编译。
动态编译:在运行的时候确定类型后再绑定对象。多态的运用,降低了类的耦合性,增加了灵活性。
反射:动态编译的过程。提供了在程序运行过程中对接受到的对象的进行分析与操作的过程的能力。在程序编译时无法得知对象类型的情况下,那么只有在运行的过程中由已经得到的信息得出对象的信息的能力即是反射。灵活的特性导致了性能的降低,因为JVM对命令的解释慢于对命令的执行。
反射机制:即在程序运行的时候对自身的信息的获取的机制,一般情况下只需要获得类的名字就可以通过反射机制获取类的所有信息。
Class类:Java程序中的各个Java类属于的同一类事物 , 其实例对象就是对应的字节码。
元数据:描述数据结构的结构,其中描述数据结构的就是类。所以元数据就是描述类的结构,即描述Class类。
注意:不论是任何对象是否被限定,即使是私有的,都可以通过特定的方法获取。
获得Class对象的三种方法:
调用属性(最常用,方便简单):
Class<String> c =String.class;// String.class:即JVM中表示String类的字节码
使用forName()方法:
Class<String> cla = (Class<String>) Class.forName("java.lang.String");//必须使用全限定名
调用Object的getClass方法:
Class c3 = newString().getClass();
Class方法:
boolean isArray() :判断指定的 Class 对象是否是一个数组类型。
boolean isPrimitive() :判断指定的 Class 对象是否是一个基本类型(boolean、byte、char、short、int、long、float、double和void)
Eg: if(short.class.isPrimitive());
包装类和Void类的静态常量TYPE字段,表示其字节码:
Class<?> in3 = Integer.TYPE;
Integer.TYPE == int.class ;
Integer.class == int.class;
注意:包装类与基本数据类型的字节码是不一样的
获取类的属性信息:
Eg:
import java.lang.reflect.Modifier;
class A {}
interface B{}
interface C{}
public class BaseDemo3 extends A implements B,C
{
public class C{}//内部类
public interface D{}//内部接口
public static void main(String[]args)
{
Class<BaseDemo3> c =BaseDemo3.class; //不论类还是接口都可以获得对象。
System.out.println(c);
System.out.println(c.getPackage());//得到包名
System.out.println(c.getName());//得到全限定名
System.out.println(c.getSimpleName());//得到类的简称
System.out.println(c.getSuperclass().getSimpleName());//先获取父类,再获取父类的简称
System.out.println(c.getInterfaces());//获得接口
Class[] arr =c.getInterfaces();
for (Class cla : arr)
{
System.out.println(cla);
}
Class[] cl =c.getClasses();//获得public修饰的类
System.out.println(cl.length);//判断内部类是是否有加上public,没有返回0,有返回2。
for (Class class1 : cl)
{
System.out.println(class1);
}
int i = c.getModifiers();//获得修饰符
System.out.println(i);//返回1表示public
System.out.println(Modifier.toString(i));//直接打印出修饰符关键字
}
}
获得字段,构造方法和方法:
方法有:
类Constructor:描述类中的构造方法。
Constructor<T> getConstructor(Class<?>... parameterTypes)
获得Class对象表示类的指定的public构造方法;
Constructor<?>[] getConstructors()
获得Class对象表示类的所有public构造方法;
Constructor<T> getDeclaredConstructor(Class<?>...parameterTypes)
获得Class对象表示类的指定的构造方法,和访问权限无关;
Constructor<?>[] getDeclaredConstructors()
获得Class对象表示类的所有构造方法,和访问权限无关;
Eg:
import java.lang.reflect.Constructor;
class Emp
{
private String name;
private int age;
private Emp() {}
Emp(String name){}
public Emp(String name,intage){}
}
public class ConstructorDemo
{
public static voidmain(String[] args) throws Exception
{
Class<Emp> c =Emp.class; //获得类
Constructor[] con =c.getConstructors();//返回public修饰的构造方法
for (Constructor cons : con)
{
System.out.println("c.getConstructors()"+cons);
}
Constructor c1 =c.getConstructor(String.class,int.class); //返回public修饰的指定的构造器
System.out.println(c1);
con =c.getDeclaredConstructors();//暴力反射,获得全部构造函数,不论是否private
for (Constructor cons : con)
{
System.out.println("c.getDeclaredConstructors()="+cons);
}
}
}
Method类用于描述类中的方法:
Class<MethodDemo> c = MethodDemo.class;
Method getMethod(String name, Class<?> ... parameterTypes)
返回该Class对象表示类和其父类的指定的public方法;
Method me =c.getMethod("main", String[].class);
System.out.println("main"+me);
Method[] getMethods():
返回该Class对象表示类和其父类的所有public方法;
Method[] m = c.getMethods();
for (Method method : m)
{
System.out.println(method);
}
Method getDeclaredMethod(String name, Class<?>... parameterTypes)
返回该Class对象表示类的指定的方法。和访问权限无关,但不包括继承的方法;
me =c.getDeclaredMethod("show");
System.out.println(me);
me =c.getMethod("toString");
System.out.println(me);
Method[] getDeclaredMethods(): 获得类所有的方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法;
m = c.getDeclaredMethods();
for (Method method : m)
{
System.out.println("c.getDeclaredMethods()="+method);
}
Field类用于描述类中的字段:
Class<MethodDemo> c = MethodDemo.class;
Field fi = c.getField("c");
Field getFields()获得public修饰的字段
Field[] f = c.getFields();
for (Field field : f)
{
System.out.println(field);
}
获得指定的字段:
Field fi =c.getField("c");
System.out.println(fi);
获得到全部字段:
f =c.getDeclaredFields();
for (Field field : f)
{
System.out.println(field);
}
获得注释:
Annotation[] a =c.getAnnotations();
System.out.println(a.length);
for (Annotationannotation : a)
{
System.out.println(annotation);
}
获得指定的注解
Deprecated d =c.getAnnotation(Deprecated.class);
System.out.println(d);
创建对象:
针对有非私有得无参数的构造方法的对象,可以用newInstance()方法创建该Class对象的实例。
Eg:
System.out.println(new Demo());//传统方式创建对象
Class<Demo> clz = Demo.class;
Demo d = clz.newInstance();//使用反射的方式
针对指定的构造函数,使用Constructor的newInstance()方法创建对象类的实例。如果这个构造方法被私有化了,可以设置访问为true。
Eg:
public static voidmain(String[] args) throws Exception
{
Class<Demo> c =Demo.class;
Constructor< Demo> con = c.getDeclaredConstructor();//调用默认的构造函数
System.out.println(con);
con.setAccessible(true);//设置private构造函数为允许访问
Demo d = con.newInstance();//用私有的构造方法创建对象
System.out.println(d);
con =c.getDeclaredConstructor(String.class);
System.out.println(con);//私有
con.setAccessible(true);//设置为允许访问
p =con.newInstance("liuzhao");//用私有的构造方法创建了指定的对象
System.out.println(p);
}
调用字段
Eg:
public static voidmain(String[] args) throws Exception
{
Class<Cat> clz = Car.class;//反射获取类
Field[] f1 = clz.getDeclaredFields();//获取字段
Car c = clz.newInstance();
f1 = clz.getDeclaredField("name");//获取特定字段
f1.setAccessible(true);
f1.set(c, andy);
int i = fi.getInt(c); //获取基本数据类型表示的字段值。
System.out.println(i);//打印
Field f = clz.getDeclaredField("num");//获取特定字段
f.setAccessible(true);//设为可访问状态
//若该字段的类型是引用数据类型则使用,void set(Object obj, Object value);
f.set(c, 4);//反射赋值
// 若该字段的类型是引用数据类型则使用,Object get(Object obj);
Object o = fi.get(c);
System.out.println(o); //打印
}
获取泛型信息
方法有:
Type gType = f.getGenericType();//对于字段,得到泛型的类型后将Type对象强转为ParameterizedType,其表示增加泛型后的类型
Type getRawType()//返回被泛型限制的类型;
Type[] getActualTypeArguments()//返回泛型参数类型;
注意:Type类中没有任何的方法,所以需要强转后调用子类的方法。
Type[] getActualTypeArguments():获得表示此类型实际类型参数的 Type对象的数组。
Type getOwnerType():获得Type对象,表示此类型是其成员之一的类型。
Type getRawType():获得Type对象,表示声明此类型的类或接口。
Eg:
Class c = GenericType.class;//获得类
Field f =c.getDeclaredField("name");//获得特定字段
Type t = f.getGenericType();//对于字段,得到泛型的类型
ParameterizedType pt =(ParameterizedType)t;//强转到子类
t = pt.getRawType();//声明类型的类或接口
System.out.println(t);
Type[] t1 =pt.getActualTypeArguments();//获得表示此类型实际类型参数的 Type对象的数组
for (Type type : t1)
{
System.out.println(type);
}
调用方法
Object invoke(Object obj,Object... args): 执行该底层方法来调用方法,其中obj表示调用底层方法的对象,args表示传递的实际参数。如果底层方法是静态的,那么可以忽略指定的 obj 参数。如果底层方法所需的形参个数为 0,则所提供的 args 数组长度可以为 0 或null。 若底层方法返回的是数组类型,invoke方法返回的不是底层方法的值,而是底层方法的返回类型;
Eg:
Class<Demo> c =Demo.class;
Method m =c.getMethod("play", Integer.class);//方法名与参数列表
Object o =m.invoke(c.newInstance(), 123);//调用方法
System.out.println(o);
m = c.getDeclaredMethod("specplay");//特定的方法
m.setAccessible(true); //私有化的方法可视化
o = m.invoke(c.newInstance());//调用方法
m =c.getMethod("staticplay");//特定的静态方法
m.invoke(null);//静态方法的调用
注意:对于要调用可变参数的方法,一般把可变参数都当做是对应的都使用Object[]封装,例如play(int…a)当做play(int[] a),后作为Object数组处理;
Eg:
public static voidmain(String[] args) throws Exception
{
Class<MethodTest> c =MethodTest.class;
Method m =c.getMethod("play",int[].class);//获得方法
m.invoke(null,newint[]{1,2,3});//调用方法
m =c.getMethod("play",Person [].class);//获得方法
m.invoke(null,new Object[]{new Person[]{newPerson()}});//使用方法
}