第13章:Java反射机制

13.1 理解Class类并实例化Class类对象

13.1.1 反射概述

       1)Java Reflection

             Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内                            部信息,并能直接操作任意对象的内部属性及方法

            正常方式:引入需要的包类名称->通过new实例化->取得实例化对象

           反射方式:实例化对象->getClass()方法->得到完整的包类名称

       2)Java反射机制提供的功能

              a)在运行时判断任意一个对象所属的类

              b)在运行时构造任意一个类的对象

              c)在运行时判断任意一个类所具有的成员变量和方法

              d)在运行时调用任意一个对象的成员变量和方法

              e)生成动态代理

       3)有了反射,可以通过反射创建一个类的对象,并调用其中的结构

    @Test
    public void test2() throws Exception{
       Class clazz = Person.class;
       
       //1.创建clazz对应的运行时类Person类的对象
       Person p = (Person)clazz.newInstance();
       //2.通过反射调用运行时类的指定的属性
       Field f1 = clazz.getField("name");//public
       f1.set(p,"LiuDeHua");
       Field f2 = clazz.getDeclaredField("age");//private
       f2.setAccessible(true);
       f2.set(p, 20);
       //3.通过反射调用运行时类的指定的方法
       Method m1 = clazz.getMethod("show");//不带参数
       m1.invoke(p);
       Method m2 = clazz.getMethod("display",String.class);//带参数
       m2.invoke(p,"CHN");
    }

   

    4)反射的源头:java.lang.Class

          我们创建了一个类,通过编译(javac.exe,生成对应的.class文件。之后我们使用java.exe加载(JVM的类加载器                         完成的)。此.class文件,此.class文件加载到内存以后,就是一个运行时类,存在在缓存区。那么这个运行时类本                         身就是一个Class的实例!

 a)每一个运行时类只加载一次!

         b)有了Class的实例以后,我们才可以进行如下的操作:

               >创建对应的运行时类的对象

              >获取对应的运行时类的完整结构(属性、方法、构造器、内部类、父类、所在的包、异常、注解、...

              >调用对应的运行时类的指定的结构(属性、方法、构造器)

              >反射的应用:动态代理

    5)获取Class的实例(4种)

         a)调用运行时类本身的.class属性

               Class clazz = Person.class;

       b)通过运行时类的对象获取

             Person p = new Person();

             Class clazz = p.getClass();

       c)通过Class的静态方法获取.通过此方式,体会一下,反射的动态性。

             String className = "com.lidong.java.Person";

             Class clazz = Class.forName(className);

       d)(了解)通过类的加载器

             String className = "com.lidong.java.Person";

             ClassLoader classLoader = this.getClass().getClassLoader();

            Class clazz = classLoader.loadClass(className);

       6)ClassLoader

             类加载器是用来把类(class)装载进内存的。JVM规范定义了两种类型的类加载器:启动类加载器(bootstrap)                          和用户自定义加载器(user-defined class loader)。JVM在运行时会产生3个类加载器组成的初始化加载器层次结                          构

第13章:Java反射机制     

  ClassLoader loader = this.getClass().getClassLoader();
  InputStream is = loader.getResourceAsStream("com\\atguigu\\java\\jdbc.properties");
  Properties pros = new Properties();
  pros.load(is);
  String name = pros.getProperty("user");
  System.out.println(name);
  String password = pros.getProperty("password");
  System.out.println(password);

      

13.2 运行时创建类对象并获取类的完整结构

13.2.1 有了Class对象,能做什么?

       1)创建类的对象:调用Class对象的newInstance()方法

              a)类必须有一个无参数的构造器

              b)类的构造器的访问权限需要足够

                    String className = "com.atguigu.java.Person";

                    Class clazz = Class.forName(className);

                    Object obj = clazz.newInstance();

                    Person p = (Person)obj;

       2)没有无参的构造器就不能创建对象了吗?

            不是!只要在操作的时候明确的调用类中的构造方法,并将参数传递进去之后,才可以实例化操作。步骤如下:

  1.   通过Class类的getDeclaredConstructor(Class ... parameterTypes)取得本类的指定形参类型的构造器

  2.   向构造器中的形参中传递一个对象数组进去,里面包含了构造器中所需的各个参数。

            String className = "com.atguigu.java.Person";

            Class clazz = Class.forName(className);

            Constructor cons = clazz.getDeclaredConstructor(String.class,int.class);

            cons.setAccessible(true);

            Person p = (Person)cons.newInstance("罗伟",20);

  1.  在Constructor类中存在的一个方法

           public T newInstance(Object ... initargs)

           这些是反射机制应用最多的地方

13.2.2 通过反射调用类的完整结构

       1)获取对应的运行时类的属性             

       @Test
       public void test(){
       Class clazz = Person.class;
       //getFields():只能获取到运行时类中及其父类中声明为public的属性
       Field[] fields1 = clazz.getFields();
       for(int i = 0;i < fields1.length;i++){
           System.out.println(fields1[i]);
       }
       System.out.println();
       //getDeclaredFields():获取运行时类本身声明的所有的属性
       Field[] fields2 = clazz.getDeclaredFields();
       for(Field f : fields2){
           System.out.println(f.getName());
       }
    }

    2)获取属性的各个部分的内容:权限修饰符 变量类型 变量名      

       @Test
       public void test(){
       Class clazz = Person.class;
       Field[] fields = clazz.getDeclaredFields();
       for(Field f : fields){
           //1.获取每个属性的权限修饰符
           int i = f.getModifiers();
           String str = Modifier.toString(i);
           System.out.print(str + " ");
           //2.获取属性的类型
           Class type = f.getType();
           System.out.print(type.getName() + " ");
           //3.获取属性名
           System.out.print(f.getName());
           System.out.println();
       }
}

3)调用运行时类指定的属性                

       @Test
       public void test() throws Exception{
       Class clazz = Person.class;
       //1.获取指定的属性
       //getField(String fieldName):获取运行时类中声明为public的指定属性名为fieldName的属性
       Field name = clazz.getField("name");
       //2.创建运行时类的对象 
       Person p = (Person)clazz.newInstance();
       System.out.println(p);
       //3.将运行时类的指定的属性赋值
       name.set(p,"Jerry");
       System.out.println(p);
       System.out.println("%"+name.get(p));
       System.out.println();
       //getDeclaredField(String fieldName):获取运行时类中指定的名为fieldName的属性
       Field age = clazz.getDeclaredField("age");
       //由于属性权限修饰符的限制,为了保证可以给属性赋值,需要在操作前使得此属性可被操作。
       age.setAccessible(true);
       age.set(p,10);
       System.out.println(p);
    }

4)获取运行时类的方法

       @Test
       public void test(){
       Class clazz = Person.class;
       //getMethods():获取运行时类及其父类中所有的声明为public的方法
       Method[] m1 = clazz.getMethods();
       for(Method m : m1){
           System.out.println(m);
       }
       System.out.println();
       
       //getDeclaredMethods():获取运行时类本身声明的所有的方法
       Method[] m2 = clazz.getDeclaredMethods();
       for(Method m : m2){
           System.out.println(m);
       }
    }

       

5)获取方法的各个部分的内容:注解 权限修饰符 返回值类型 方法名 形参列表 异常   

       @Test
       public void test(){
       Class clazz = Person.class;
       Method[] me = clazz.getDeclaredMethods();
       for(Method m : me){
           //1.注解
           Annotation[] an = m.getAnnotations();
           for(Annotation a : an){
              System.out.println(a);
           }
           //2.权限修饰符
           String str = Modifier.toString(m.getModifiers());
           System.out.print(str + " ");
           //3.返回值类型
           Class returnType = m.getReturnType();
           System.out.print(returnType.getName() + " ");
           //4.方法名
           System.out.print(m.getName() + " ");
           //5.形参列表
           System.out.print("(");
           Class[] params = m.getParameterTypes();
           for(int i = 0;i < params.length;i++){
                System.out.print(params[i].getName() + " args-" + i + " ");
           }
           System.out.print(")");
           //6.异常类型
           Class[] exps = m.getExceptionTypes();
           if(exps.length != 0){
              System.out.print("throws ");
           }
           for(int i = 0;i < exps.length;i++){
              System.out.print(exps[i].getName() + " ");
           }
           System.out.println();
       }
    }

6)调用运行时类中指定的方法



       @Test
       public void test() throws Exception{
       Class clazz = Person.class;
       
       //getMethod(String methodName,Class ... params):获取运行时类中声明为public的指定的方
         法
       Method m1 = clazz.getMethod("show");
       Person p = (Person)clazz.newInstance();
       
       //调用指定的方法:Object invoke(Object obj,Object ... obj)
       Object returnVal1 = m1.invoke(p);
       System.out.println(returnVal1);
       Method m2 = clazz.getMethod("toString");
       Object returnVal2 = m2.invoke(p);
       System.out.println(returnVal2);
       
       //对于运行时类中静态方法的调用
       Method m3 = clazz.getMethod("info");
       m3.invoke(Person.class);
       
       //getDeclaredMethod(String methodName,Class ... params):获取运行时类中声明了的指定的
         方法
       Method m4 = clazz.getDeclaredMethod("display",String.class,Integer.class);
       m4.setAccessible(true);
       Object value = m4.invoke(p,"CHN",10);
       System.out.println(value);
    }

       

7)获取运行时类的父类

        @Test

        public void test(){

       Class clazz = Person.class;

       Class superClass = clazz.getSuperclass();

       System.out.println(superClass);

       }

8)获取带泛型的父类

        @Test

        public void test(){

       Class clazz = Person.class;

       Type type = clazz.getGenericSuperclass();

       System.out.println(type);

       }

9)获取父类的泛型

        @Test

        public void test(){

       Class clazz = Person.class;

       Type type = clazz.getGenericSuperclass();

       ParameterizedType param = (ParameterizedType)type;

       Type[] ars = param.getActualTypeArguments();

       System.out.println(((Class)ars[0]).getName());

       }

10)获取实现的接口

        @Test

        public void test(){

       Class clazz = Person.class;

       Class[] interfaces = clazz.getInterfaces();

       for(Class i : interfaces){

           System.out.println(i);

       }

       }

11)获取所在的包

        @Test

        public void test(){

       Class clazz = Person.class;

       Package pack = clazz.getPackage();

       System.out.println(pack);

       }

12)获取注解

        @Test

        public void test(){

       Class clazz = Person.class;

       Annotation[] anns = clazz.getAnnotations();

       for(Annotation a : anns){

                   System.out.println(a);

       }

       }

13.3 通过反射调用类的指定方法、指定属性

13.3.1 调用指定方法

       1)通过反射,调用类中的方法,通过Method类完成。步骤:

             a)通过Class类的getMethod(String name,Class ... parameterTypes)方法取得一个Method对象,并设置此方                                法操作时所需要的参数类型

             b)之后使用Object invoke(Object obj,Object[] args)进行调用,并向方法中传递要设置的obj对象的参数信                                       息。

第13章:Java反射机制


       @Test
       public void test() throws Exception{
       Class clazz = Person.class;
       
       //getMethod(String methodName,Class ... params):获取运行时类中声明为public的指定
       的方法
       Method m1 = clazz.getMethod("show");
       Person p = (Person)clazz.newInstance();
       
       //调用指定的方法:Object invoke(Object obj,Object ... obj)
       Object returnVal1 = m1.invoke(p);
       System.out.println(returnVal1);
       Method m2 = clazz.getMethod("toString");
       Object returnVal2 = m2.invoke(p);
       System.out.println(returnVal2);
       
       //对于运行时类中静态方法的调用
       Method m3 = clazz.getMethod("info");
       m3.invoke(Person.class);
       
       //getDeclaredMethod(String methodName,Class ... params):获取运行时类中声明了的指
       定的方法
       Method m4 = clazz.getDeclaredMethod("display",String.class,Integer.class);
       m4.setAccessible(true);
       Object value = m4.invoke(p,"CHN",10);
       System.out.println(value);
    }
        @Test
        public void test() throws Exception{
       String className = "com.atguigu.java.Person";
       Class clazz = Class.forName(className);
       Constructor cons = clazz.getDeclaredConstructor(String.class,int.class);
       cons.setAccessible(true);
       Person p = (Person)cons.newInstance("罗伟",20);
       System.out.println(p);
    }

13.3.2 调用指定的属性

       @Test
       public void test() throws Exception{
       Class clazz = Person.class;
       //1.获取指定的属性
       //getField(String fieldName):获取运行时类中声明为public的指定属性名为fieldName的属性
       Field name = clazz.getField("name");
       //2.创建运行时类的对象 
       Person p = (Person)clazz.newInstance();
       System.out.println(p);
       //3.将运行时类的指定的属性赋值
       name.set(p,"Jerry");
       System.out.println(p);
       System.out.println("%"+name.get(p));
       System.out.println();
       //getDeclaredField(String fieldName):获取运行时类中指定的名为fieldName的属性
       Field age = clazz.getDeclaredField("age");
       //由于属性权限修饰符的限制,为了保证可以给属性赋值,需要在操作前使得此属性可被操作。
       age.setAccessible(true);
       age.set(p,10);
       System.out.println(p);
    }

 

13.4 动态代理

13.4.1 Java动态代理

       1)静态代理,特征:代理类和目标对象的类都是在编译期间确定下来,不利于程序的扩展。同时,每一个代理类只                             能为一个接口服务,这样一来程序开发中必然产生过多的代理

interface ClothFactory{
    void productCloth();
}
//被代理类
class NikeClothFactory implements ClothFactory{
    @Override
    public void productCloth() {
       System.out.println("Nike工厂生产一批衣服");
    }   
}
//代理类
class ProxyFactory implements ClothFactory{
    ClothFactory cf;
    public ProxyFactory(ClothFactory cf){
       this.cf = cf;
    }
    @Override
    public void productCloth() {
       System.out.println("代理类开始执行,收代理费$1000");
       cf.productCloth();
    }
    
}
public class TestClothProduct {
    public static void main(String[] args) {
       NikeClothFactory nike = new NikeClothFactory();
       //创建被代理类的对象
       ProxyFactory proxy = new ProxyFactory(nike);
       //创建代理类的对象
       proxy.productCloth();
    }
}

       2)动态代理(体会反射):客户通过代理类来调用其他对象的方法,并且是在程序运行时根据需要动态创建目标类                             的代理对象

             a)调试    

             b)远程方法调用


interface Subject {
    void action();
}

// 被代理类
class RealSubject implements Subject {
    public void action() {
       System.out.println("我是被代理类,记得要执行我哦!么么~~");
    }
}
class MyInvocationHandler implements InvocationHandler {
    Object obj;// 实现了接口的被代理类的对象的声明
    // ①给被代理的对象实例化②返回一个代理类的对象
    public Object blind(Object obj) {
       this.obj = obj;
       return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj
              .getClass().getInterfaces(), this);
    }
    //当通过代理类的对象发起对被重写的方法的调用时,都会转换为对如下的invoke方法的调用
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
           throws Throwable {
       //method方法的返回值时returnVal
       Object returnVal = method.invoke(obj, args);
       return returnVal;
    }
}
public class TestProxy {
    public static void main(String[] args) {
       //1.被代理类的对象
       RealSubject real = new RealSubject();
       //2.创建一个实现了InvacationHandler接口的类的对象
       MyInvocationHandler handler = new MyInvocationHandler();
       //3.调用blind()方法,动态的返回一个同样实现了real所在类实现的接口Subject的代理类的对
         象。
       Object obj = handler.blind(real);
       Subject sub = (Subject)obj;//此时sub就是代理类的对象
       sub.action();//转到对InvacationHandler接口的实现类的invoke()方法的调用 
    }
}

       3)代理设计模式原理:使用一个代理将对象包装起来,然后用该代理对象取代原始对象。任何对原始对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原始对象上

13.4.2 动态代理与AOP(Aspect Orient Programming)

       1)前面介绍的Rroxy和InvocationHandler,很难看出这种动态代理的优势,下面介绍一种更实用的动态代理机制

       第13章:Java反射机制

       第13章:Java反射机制

             改进后说明:代码段1、代码段2、代码段3和深色代码段分离开了,但代码段1,2,3又和一个特定的方法A耦合了!                          最理想的效果是:代码块1、2、3既可以执行方法A,又无序在程序中以硬编码的方式直接调用深色代码的方法


interface Human {
    void info();
    void fly();
}
// 被代理类
class SuperMan implements Human {
    public void info() {
       System.out.println("我是超人!我怕谁!");
    }
    public void fly() {
       System.out.println("I believe I can fly!");
    }
}
class HumanUtil {
    public void method1() {
       System.out.println("=======方法一=======");
    }
    public void method2() {
        System.out.println("=======方法二=======");
    }
}
class MyInvocationHandler implements InvocationHandler {
    Object obj;// 被代理类对象的声明

    public void setObject(Object obj) {
       this.obj = obj;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
           throws Throwable {
       HumanUtil h = new HumanUtil();
       h.method1();

       Object returnVal = method.invoke(obj, args);

       h.method2();
       return returnVal;
    }
}
class MyProxy {
    // 动态的创建一个代理类的对象
    public static Object getProxyInstance(Object obj) {
        MyInvocationHandler handler = new MyInvocationHandler();
       handler.setObject(obj);

       return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj
              .getClass().getInterfaces(), handler);
    }
}
public class TestAOP {
    public static void main(String[] args) {
       SuperMan man = new SuperMan();//创建一个被代理类的对象
       Object obj = MyProxy.getProxyInstance(man);//返回一个代理类的对象
       Human hu = (Human)obj;
       hu.info();//通过代理类的对象调用重写的抽象方法
       System.out.println();
       hu.fly();
    }
}