美文网首页
Java反射

Java反射

作者: yzw12138 | 来源:发表于2017-07-26 16:39 被阅读0次

    java通常是先有类再有对象,有对象我就可以调用方法或者属性。反射其实是通过Class对象来调用类里面的方法。通过反射可以调用私有方法和私有属性。大部分框架都是运用反射原理

    一、Class类的使用

    1.在面向对象的世界中,完事万物都是对象
    2.java语言中静态成员和普通数据类型不是对象
    3.类也是对象,是java.lang.Class的实例对象

    • 任何一个类都是Class的实例对象,有三种表示方式:
      Class c1 = myClass.class;
      Class c2 = myObject.getClass();
      Class c3 = Class.forName("com.myClass");
    class Foo{
        public void print()
        {
            System.out.println("it is Foo");
        }
    }
    public class ClassDemo1 {
    
        /**
         * @param args
         */
        public static void main(String[] args) {
            // TODO Auto-generated method stub
    
            //Foo类的实例对象
            Foo foo1 = new Foo(); //foo1表示Foo类的实例对象
    
            /*
             * Foo这个类本身也是一个实例对象,就是Class(java.lang.Class)类的实例对象
             * /任何一个类都是Class类的实例对象, 该实例对象有三种表示方法
             */
            //第一种 --> 任何一个类都有一个隐含的静态成员变量class
            Class c1 = Foo.class; //c1表示Class类的实例对象
    
            //第二种,已知该类的对象, 通过该类对象的getClass方法表示
            Class c2 = foo1.getClass(); //c2表示Class类的实例对象
    
            /*
             * 以上c1,c2都表示Class类的实例对象,但是这个实例对象又是说Foo这个类
             * c1,c2表示Foo类的类类型 ( class type ), 因为Foo类可以理解为Class类的实例对象
             * 也就是世界万物皆对象,类也是对象,是Class类的实例对象
             * 这个对象我们称为该类的类类型
             */
    
            //不管c1 or c2都代表了Foo类的类类型,一个类只可能是Class类的一个实例对象, 所以c1=c2
            System.out.println(c1==c2);//输出: true
    
            //第三种
            Class c3 = null;
            try {
                c3 = Class.forName("com.lxf.reflect.Foo");
            } catch (ClassNotFoundException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            System.out.println(c3==c2); //输出:true
    
            //我们可以通过类的类类型创建该类的实例--->通过c1 or c2 or c3创建Foo的实例
            try {
                Foo foo2 = (Foo)c2.newInstance(); //需要有无参数的构造方法
                foo2.print();
            } catch (InstantiationException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    
    }
    

    二.静态加载

    • 在编译java源文件的时候的加载类叫做静态加载,比如:Test.java文件如下
    class Test{
      public static void main(String[] args) {
          People p = new People();
          p.eat();
      }
    }
    

    编译Test.java

    javac Test.java
    这时候People.class字节码文件并没有,所以在编译的时候会报错
    

    创建People.java

    class People
    {
      public void eat()
      {
            System.out.println("我喜欢美食");
      }
    }
    

    此时在编译

    先编译People.java
    javac People.java //编译后会产生People.class字节码文件
    javac Test.java     //编译后会产生Test.class字节码文件
    

    执行Test

    java Test  //会输出:我喜欢美食
    

    三、动态加载

    通过Class a=Class.forName(arg[0]);此时为动态加载,因为编译时不知道使用哪个类,因此编译没有加载任何类,通过编译。运行时,根据 Javac office.java word (word为arg[0],也是类类型),去确定a是哪个类。这就是动态加载。
    实际问题举例:

    class offic
    {
       public static void main(String[] args)
       {
         if("Word".equals(args[0]))
         {
           Word w=new Word();
           w.start();
         }
         if("Excel".equals(args[0])
         {
           Excel e=new Excel();
           e.start();
         }
       }    
    }
    

    编译无法通过,Word类、Excel类和start()方法不存在。

    class Word 
    {
        public static void start(){
            System.out.println("Word.....start");
        }
    }
    

    创建Word类后,进行编译,仍会报错,Excel类不存在。如果我们只想用已经存在的Word类,此类方法也是无法实现的。因为new创建的对象是静态加载类,在编译时刻需要加载所有可能使用的类,不管你是否会用到。

    class OfficeBetter 
    {
        public static void main(String[] args) 
        {
            
            try
            {
                //动态加载类
                Class c=Class.forName(args[0]);
                //通过类类型创建该类对象
                OfficeAble oa=(OfficeAble)c.newInstance();
                oa.start();
            }
            catch (Exception e)
            {
                e.printStackTrace();
            }
        }
    }
    

    通过动态加载,编译过程中不会报错。编写借口OfficeAble直接创建对象,Word类和Excel类通过继承方式,实现不同方法。

    interface OfficeAble
    {
        public void start();
    }
    
    class Word implements OfficeAble
    {
        public void start(){
            System.out.println("Word.....start");
        }
    }
    
    • 基本数据类型,void关键字都存在类类型

    四、获取信息

    • 1、获取方法信息
      Method类,方法对象,一个成员方法就是一个Method对象
      getMethods()方法获取的是所有public函数,包括父类继承而来的
      getDeclaredMethods()获取的是所有该类自己声明的方法,不问访问权限
    /**
         * 打印类的信息,包括类的成员函数、成员变量(只获取成员函数)
         * @param obj 该对象所属类的信息
         */
    public static void printClassMethodMessage(Object obj){
            //要获取类的信息  首先要获取类的类类型
            Class c = obj.getClass();//传递的是哪个子类的对象  c就是该子类的类类型
            //获取类的名称
            System.out.println("类的名称是:"+c.getName());
            /*
             * Method类,方法对象
             * 一个成员方法就是一个Method对象
             * getMethods()方法获取的是所有的public的函数,包括父类继承而来的
             * getDeclaredMethods()获取的是所有该类自己声明的方法,不问访问权限
             */
            Method[] ms = c.getMethods();//c.getDeclaredMethods()
            for(int i = 0; i < ms.length;i++){
                //得到方法的返回值类型的类类型
                Class returnType = ms[i].getReturnType();
                System.out.print(returnType.getName()+" ");
                //得到方法的名称
                System.out.print(ms[i].getName()+"(");
                //获取参数类型--->得到的是参数列表的类型的类类型
                Class[] paramTypes = ms[i].getParameterTypes();
                for (Class class1 : paramTypes) {
                    System.out.print(class1.getName()+",");
                }
                System.out.println(")");
            }
        }
    
    • 2、获取成员变量信息
      成员变量是java.lang.reflect.Field的对象
      1、Field类封装了关于成员变量的操作
      2、Field[] fs = c.getFields()方法获取所有public的成员变量Field[]信息
      3、c.getDeclaredFields获取的是该类自己声明的成员变量信息
      4、field.getType()获得成员类型的类类型
      5、field.getName()获得成员的名称
      /**
         * 获取成员变量的信息
         * @param obj
         */
        public static void printFieldMessage(Object obj) {
            Class c = obj.getClass();
            /*
             * 成员变量也是对象
             * java.lang.reflect.Field
             * Field类封装了关于成员变量的操作
             * getFields()方法获取的是所有的public的成员变量的信息
             * getDeclaredFields获取的是该类自己声明的成员变量的信息
             */
            //Field[] fs = c.getFields();
            Field[] fs = c.getDeclaredFields();
            for (Field field : fs) {
                //得到成员变量的类型的类类型
                Class fieldType = field.getType();
                String typeName = fieldType.getName();
                //得到成员变量的名称
                String fieldName = field.getName();
                System.out.println(typeName+" "+fieldName);
            }
        }
    
    • 3、获取构造方法信息
      构造函数是java.lang.Constructor类的对象
      1、通过Class.getConstructor()获得Constructor[]所有公有构造方法信息
      2、建议getDeclaredConstructors()获取自己声明的构造方法
      3、Constructor.getName():String
      4、Constructor.getParameterTypes():Class[]
    /**
         * 打印对象的构造函数的信息
         * @param obj
         */
        public static void printConMessage(Object obj){
            Class c = obj.getClass();
            /*
             * 构造函数也是对象
             * java.lang. Constructor中封装了构造函数的信息
             * getConstructors获取所有的public的构造函数
             * getDeclaredConstructors得到所有的构造函数
             */
            //Constructor[] cs = c.getConstructors();
            Constructor[] cs = c.getDeclaredConstructors();
            for (Constructor constructor : cs) {
                System.out.print(constructor.getName()+"(");
                //获取构造函数的参数列表--->得到的是参数列表的类类型
                Class[] paramTypes = constructor.getParameterTypes();
                for (Class class1 : paramTypes) {
                    System.out.print(class1.getName()+",");
                }
                System.out.println(")");
            }
        }
    

    JAVA中extends 与implements有啥区别?

    1. 在类的声明中,通过关键字extends来创建一个类的子类。一个类通过关键字implements声明自己使用一个或者多个接口。
      extends 是继承某个类, 继承之后可以使用父类的方法, 也可以重写父类的方法; implements 是实现多个接口, 接口的方法一般为空的, 必须重写才能使用
      2.extends是继承父类,只要那个类不是声明为final或者那个类定义为abstract的就能继承,JAVA中不支持多重继承,但是可以用接口 来实现,这样就要用到implements,继承只能继承一个类,但implements可以实现多个接口,用逗号分开就行了

    五、方法的反射

    1、获得类类型 A a1 = new A(); Class c= a1.getclass;
    2、获得方法对象 Method getMet=c.getMethod("print",String.class,String.class);//忘了加引号 其中可以用new Class[]{}数组作为参数
    3、反射,m.invoke(调用方法的对象,参数); 参数可以 new Object[]{}
    4、反射结果和直接对象调用结果一样,本例都是 打印输出

    六、Java反射机制

    ——通过反射了解集合泛型的本质
    1:反射的操作都是编译之后的操作;就是运行阶段
    2:java中集合的泛型是防止错误输入的;只在编译阶段有效,只要绕过编译就无效啦
    1:注解是一个什么东西——一种技术,使用特定语法,能过比较简单的实现一些比较牛逼的功能
    2:注解的功能什么?也就是都有什么类型的注解?注解都能干什么?还是说什么都能干?
    3:有些注解是人家提供的,如果想自己编写注解,该怎么玩?然后就是什么情况下自己编写注解比较好?怎么才能又快又好的编写自己的注解?
    4:想了解一下,框架中注解是怎么使用的比如:Spring框架

    七、Java自带的常用注解:

    • @Overrid (覆盖)覆盖了父类的方法
    • @Deprecated(弃用)过时了的方法,不能删掉,有可能导致错误,所以注解成过时。
    • @Suppvisewarnings(忽略)调用deprecated方法会导致警告,使用@Suppvisewarning("deprecation")注解会忽略掉这个警告
    常见第三方注解

    Spring:@Autowired @Service @Repository

    • @Autowired:可以对成员变量、方法和构造函数进行标注,来完成自动装配的工作
      用法:
    public class UserManagerImpl implements UserManager {
       @Autowired 
       private UserDao userDao;
    }
    
    • @Service:定义某个类为一个bean,则在这个类的类名前一行使用@Service("XXX"),就相当于讲这个类定义为一个bean,bean名称为XXX。而无需去xml文件内去配置。
    Paste_Image.png
    • 源码注解:只存在于源代码中,编译后就没有了。
    • 编译时注解:存在源代码和.class文件中
    • 注解的分类:
      按照运行机制分类:源码注解;编译时注解;运行时注解。
      按照来源分类:来自JDK(如override);来自第三方(如spring的autowired);自定义注解。
    • 元注解:给注解的注解。
    • 自定义注解的语法
      (1)使用@interface关键字定义注解
      (2)无参无异常声明成员
      (3)可用default为成员指定一个默认值
      (4)合法成员类型,包括:原始、String、Class、Annotation和Enumeration
      (5)如只有一个成员,则成员名必须为value(),且使用时可忽略"成员名="
      (6)无成员的注解称为标识注解
    • 元注解:注解自定义注解的注解
      @Target(作用范围):使用ElementType增加范围
      Annotation可被用于 packages、types(类、接口、枚举、Annotation类型)、类型成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch参数)
      1.CONSTRUCTOR:用于描述构造器
          2.FIELD:用于描述域
          3.LOCAL_VARIABLE:用于描述局部变量
          4.METHOD:用于描述方法
          5.PACKAGE:用于描述包
          6.PARAMETER:用于描述参数
          7.TYPE:用于描述类、接口(包括注解类型) 或enum声明
      @Rentention(作用时长):使用RetentionPolicy限制
      1.SOURCE:在源文件中有效(即源文件保留)
          2.CLASS:在class文件中有效(即class保留)
          3.RUNTIME:在运行时有效(即运行时保留)
      @Inherited(可继承性):允许子类继承
      如果继承的是借口,注释无法被继承;如果继承的时类,则只有类上的注释可以被继承,方法上的注释无法被继承。
      @Documented(生成文档):可生成在JavaDoc文档
    Paste_Image.png Paste_Image.png

    如果注解只有一个成员,则成员名必须取名为value(),在使用时可以忽略成员名和赋值号(=),这是一个约定俗成的规则,不这样使用也可以编译通过。
    解析注解:

    Paste_Image.png Paste_Image.png

    相关文章

      网友评论

          本文标题:Java反射

          本文链接:https://www.haomeiwen.com/subject/gbliqxtx.html