Java反射

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文件内去配置。
Java反射_第1张图片
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文档
Java反射_第2张图片
Paste_Image.png
Java反射_第3张图片
Paste_Image.png

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

Java反射_第4张图片
Paste_Image.png
Java反射_第5张图片
Paste_Image.png

你可能感兴趣的:(Java反射)