反射(Class类、Constructor类、Field类、Method类)

反射:通过类的Class对象来操作。可以得到属性对象,方法对象,构造器对象。可以通过Class对象和构造器对象来实例化对象。但是,通过Class对象来实例化时不能传递参数,通过构造器时可以传递参数。
1.Class类实例化的几种方式:

  • 可以通过类名.class;
  • 可以通过对象名.getClass;
  • 可以通过Class的静态方法forName方法(传递类的路径名)来获取Class对象。
  • 对于包装器类型来说,可以通过类名.TYPE来获取。
public class Maintest {
    public static void main(String[] args)throws Exception{
        Integer i=new Integer(12);
        Class i1=Integer.class;
        Class i2=i.getClass();
        Class i3=Class.forName("java.lang.Integer");
        Class i4=Integer.TYPE;//基本数据类型的Class类
        Class i5=int.class;
        System.out.println(i1==i2&&i2==i3);
        System.out.println(i4==i5);
    }
}

运行结果都为true
2.Class类的方法:可以通过Class方法获得各类所实现的接口,该类的全名,该类的直接父类。获得所有的构造器,获得所有的方法及成员变量。getDeclaredxxx表示获得所有的,getxxx表示获得public修饰的。

public class Maintest {
    public static void main(String[] args)throws Exception{
        Class c=String.class;
        System.out.println(c.getName());//输出类的全名
        System.out.println(Arrays.toString(c.getInterfaces()));//输出类所实现的接口
        System.out.println(c.getSuperclass());//输出类的直接父类
    }
}

运行结果:

java.lang.String
[interface java.io.Serializable, interface java.lang.Comparable, interface java.lang.CharSequence]
class java.lang.Object

自定义一个Person类,只有两个私有的name和age属性,get和set方法,有参和无参构造。
获得构造器的方法:

public class Maintest {
    public static void main(String[] args)throws Exception{
        Class per=Person.class;
        Constructor[] constructors=per.getDeclaredConstructors();//获取所有构造
        Constructor[] constructors1=per.getConstructors();//获取public修饰的构造
        for(Constructor con:constructors)//所有
        {
            System.out.println(con);
        }
        System.out.println("-------------------------");
        for(Constructor con:constructors1)//public修饰的
        {
            System.out.println(con);
        }
    }
}

运行结果:

private test.Person()
public test.Person(java.lang.String,int)
-------------------------
public test.Person(java.lang.String,int)

获得成员变量对象,并通过每个成员变量对象来获得变量名,变量类型,变量修饰符。也可以通过getDeclaredField方法传递变量名来获得指定的变量。

public class Maintest {
    public static void main(String[] args)throws Exception{
        Class per=Person.class;
        Field[] allF=per.getDeclaredFields();
        System.out.println("属性名\t\t"+"属性类型\t\t"+"修饰符");
        for(Field field:allF)
        {
            System.out.println(field.getName()+"\t\t"+field.getType()+"\t\t"+field.getModifiers());
        }
    }
}

结果:

属性名		属性类型		修饰符
name		class java.lang.String		2
age		int		2

获得方法对象与之类似,可以通过方法名、形参来获得指定的方法。通过invoke方法传递对象和实参来调用传过去对象的该方法。方法对象可以通过getParameterTypes方法获得方法的参数列表。
3.创建对象并实例化的几种方法:

  • new的时候调用有参构造;
  • 通过Class类得到有参构造器的对象,通过这个构造器对象实例化并传参。
  • 创建好空对象,通过Class对象获得get和set方法,调用方法传递参数
  • 创建好空对象,通过Class对象获得成员变量Field对象,如果为私有,将其设置为可访问的。然后直接set值
public class Maintest {
    public static void main(String[] args)throws Exception{
        Class p=Person.class;
       //实例化Person对象
        //第一种
        Person person1=new Person("zhangsan",24);
        System.out.println(person1);
        //第二种 通过反射得到特定构造方法。通过构造器实例化对象(传参)
        Constructor constructor=p.getDeclaredConstructor(String.class,int.class);
        Person person2=(Person) constructor.newInstance(new Object[]{"lisi",23});
        System.out.println(person2);
        //第三种 空对象已经创建好 通过反射获得get和set方法并调用。
        Person person3=(Person) p.newInstance();//空对象,name为null,age为0
        Method setname=p.getDeclaredMethod("setName", String.class);//获得到方法对象,输入类型避免重载
        Method setage=p.getDeclaredMethod("setAge", int.class);
        setname.invoke(person3,"luck");
        setage.invoke(person3,22);
        System.out.println(person3);
        //第四种 和第三种类似,只不过是直接操作私有成员
        Person person4=new Person();//空对象
        Field name=p.getDeclaredField("name");
        Field age=p.getDeclaredField("age");
        name.setAccessible(true);//将私有变量设置为可直接操作的
        age.setAccessible(true);
        name.set(person4,"Dimond");
        age.set(person4,23);
        System.out.println(person4);
    }
}

运行结果:

Person{name='zhangsan', age=24}
Person{name='lisi', age=23}
Person{name='luck', age=22}
Person{name='Dimond', age=23}

setAccessible不仅可以对私有属性用,还可以对私有构造器用,同样也可以对私有方法用。可以用getDeclaredxxx来获取对象。

对数组的操作,可以通过Array类,这个类就相当于数组类的构造器对象。

public class Maintest {
    public static void main(String[] args)throws Exception{
        int[] i= (int[])Array.newInstance(int.class,10);
        System.out.println(Arrays.toString(i));//数组中为0
        Array.setInt(i,8,888);//将第八个数设置为888
        System.out.println(Arrays.toString(i));
    }
}

运行结果:

[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 888, 0]

代理模式:代理对象来代理A对象,要用和A相同的方法。客户端访问A的方法时,不直接访问,而是通过代理间接访问。因此代理中要有A的引用(关联关系)。又因为代理类中的方法和A中的方法一样,因此他们实现同一个接口即可。可以通过反射来实现动态代理。

猜你喜欢

转载自blog.csdn.net/Hello_1024/article/details/83447246