框架学习必备!java基础学习之《反射》

框架学习必备!java基础学习之《反射》

动态语言和静态语言

动态语言

  • 是一类运行时可以改变结构的语言,例如新的函数,对象,甚至代码可以被引进,已有的函数可以被删除或者是其它结构上的变化
  • 主要动态语言:Objcet-C,C#,JavaScript,PHP,Python

静态语言

  • 运行时结构不可改变的语言,Java,C,C++
  • Java不是动态语言,但是可以称之为准动态语言,具有一定的动态性,我们可以通过反射机制获得类似动态语言的特性,时代码更加灵活

反射定义

反射能够分析类能力的程序叫做反射

反射机制可以用来:

  1. 在运行时分析类的能力
  2. 在运行时检查对象,例如,编写一个适用于所有类的tostring方法
  3. 实现泛型数组操作代码
  4. 利用Method对象,这个对象很像C++中的函数指针

Class类

在程序运行期间,java运行时系统始终为所有对象维护一个运行时类型标识,这个信息会跟踪每个对象所属的类,虚拟机利用运行时类型信息选择要执行的正确方法。而使用一个特殊的java类来访问这些信息,保存这些信息的类名为Class,Object类中的getClass()方法可以返回一个Class类型的实例。请看如下代码:

编写一个父类Student:

public class Student {
    private String name;
    private String school;
    private String sex;
    private int age;
}

编写一个子类XiaoMing继承Student:

public class XiaoMing extends Student{
    private String home;
    private String phone;
}

编写测试类主函数:

import java.util.Random;

public class Test {
    /**
     * 反射机制的测试
     * @param args
     */
    public static void main(String[] args) {
        Student s = new Student();
        Class<Student> aClass = s.getClass();
        System.out.println(aClass.getName());
        //输出“Student”
        System.out.println(aClass);
        //输出“class Student”

        Student s2=new XiaoMing();
        Class aClass2 = s2.getClass();
        System.out.println(aClass2.getName());
        //输出“XiaoMing”
        System.out.println(aClass2);
        //输出“class XiaoMing”

        //如果对象在一个包里,那么包名也可以作为类名的一部分
        Random random=new Random();
        Class cl=random.getClass();
        System.out.println(cl.getName());
        //输出java.util.Random

        //还可以使用静态方法Class.formName()获得类名对应的Class对象
        String name="java.util.Random";
        try {
            Class c=Class.forName(name);
            System.out.println(c);
            //输出class java.util.Random,代表获得这个包下对应类的Class
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

获得Class类其实有另一种更加便捷的方法,如果T是任意类型的java类型(或者void关键字),T.Class就是代表匹配的类对象。

//Class其实是个泛型类,只不过我们将他省略了
Class c3=Random.class;
System.out.println(c3);
//输出java.util.Random
Class c4=int.class;
System.out.println(c4);
//输出int

虚拟机为每个类型管理一个唯一的Class对象,可以用运算符”==“来实现两个类的对象比较:

同样是上面的例子,看如下代码

public class Test2 {
    public static void main(String[] args) {
        Student student=new Student();
        Student student1=new XiaoMing();
        System.out.println(student.getClass()==Student.class);//输出true
        System.out.println(student1.getClass()==XiaoMing.class);//输出true
        System.out.println(student1.getClass()==Student.class);//输出false
        /**
        *1.instanceof 的左右两边必须是引用类型,java 八大基本数据类型无法使用 instanceof 来进行对比
		 2.instanceof 用来判定左边的引用类型是否与右边的引用类型的类型是否相同,或左边引用类型是右边引用类型的子类或实现类(右边引用类型可以是类、抽象			类、接口)
		 3.instanceof 的对比结果为 boolean 类型,如果左右两边比对成功,返回 true ;否则返回 flase
		 4.null 与任何引用类型进行 instanceof 对比的结果都是 flase,null 不属于任何类型,更不属于 object 基类的派生类(子类),需要特别注意
		*/
        System.out.println(student1 instanceof Student);//输出true

        XiaoMing xiaoMing=new XiaoMing();
        System.out.println(xiaoMing.getClass()==XiaoMing.class);//输出true
        /**
         * 思考:那么, System.out.println(xiaoMing.getClass()==Student.class);会输出什么呢?
         */
    }
}

分析:

  1. 父类声明,父类实现变量student,当打印是否和Student.class相等时,因为是父类型的对象,应该打印true
  2. 父类声明,子类实现变量student1,与instanceof不同的是,当与Student比较时,会输出false,应为每一个类型是唯一的Class对象,子类实现,那么就是子类的类型,与父类进行比较,那么一定会输出false
  3. 子类声明,子类实现xiaoMing,与XiaoMing比较时,一定会输出true

思考题:

会报错,类型无法比较,其实运算符左边返回的是Class,但是运算符右边是Class,Class是一个泛型类,只不过我们通常不写,在这里进行比较时,泛型变量不一致,所以无法比较

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yom3TZPA-1627018937020)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210721122720970.png)]

创建类实例的另一种办法

如果又一个Class类型的对象,可以用它来创造实例,看一下代码

import java.lang.reflect.InvocationTargetException;

public class Test3 {
    public static void main(String[] args) {
        /**
         * 创建类实例的另一种方法
         */
        String s="Student";
        Object student=null;
        try {
            Class cl=Class.forName(s);
            student=cl.getConstructor().newInstance();
        } catch (ClassNotFoundException | NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        //判断是否真的创建了一个Student实例
        System.out.println(student.getClass()==Student.class);//输出true
    }
}

可以通过Class.formName来找到Class对象,再通过getConstrutor来创建一个Constructor对象,再用newInstance方法来实例化对象

一个类在内存中只有一个Class

public class test {


    public static void main(String[] args) throws ClassNotFoundException {
        Class c1 = Class.forName("User");
        System.out.println(c1);

        Class c2 = Class.forName("User");
        Class c3 = Class.forName("User");
        Class c4 = Class.forName("User");

        System.out.println(c2.hashCode());
        System.out.println(c3.hashCode());
        System.out.println(c4.hashCode());
    }
}
class User{
    private String name;
    private int id;
    private int age;
    public User(){

    }
    (此处为了简洁,省略有参构造方法以及get,set方法,请读者务必写上)
}
//输出class User
//1163157884
//1163157884
//1163157884

利用反射分析类的能力

三个类:

  1. Field:这个类描述被分析类的实例字段
  2. Method:这个类描述被分析类的类方法
  3. Construtor:这个类描述被分析类的构造器

例如:

Field[] field=Student.class.getDeclaredFields();
for (Field f:
     field) {
    System.out.println(f.getName().toString());
}
/**
 * 输出
 * name
 * school
 * sex
 * age
 */

具体可以查看api文档

调用任何方法和设置属性值

这一块内容在学习Spring时需要用到,而且底层基本上不会看见new这个关键字,所以我整理了一部分,按照这样的方法将field,method,和Construtor都自己学一遍是很有必要的,所以,请认真体会并且一定要实操,否则过目则忘!
User

public class User {
    private String name;
    public String sex;
    public User() {
    }

   (此处为了简洁,省略有参构造方法以及get,set方法,请读者务必写上)
}

通过反射获取对象的属性以及直接对其属性赋值

import java.lang.reflect.Field;

public class Main {

    /**
     * 关于对象属性的操作需要掌握的几个方法以及传参的意义
     */
    public static void main(String[] args) throws InstantiationException, IllegalAccessException, NoSuchFieldException {

        User user=User.class.newInstance(); //new一个新对象,利用无参构造器
        System.out.println(user);//输出“User{name='null', sex='null'}”,此时对象是空
        /**
         * 测试getDeclaredField方法
         */
        Field field=User.class.getDeclaredField("name");//获得指定参数名属性的Filed,private 和 public 都可以获取到
        field.setAccessible(true);//可以理解为将对象的属性设置为可编辑,不然会抛出一个IllegalAccessException异常提示:modifiers "private"
        field.set(user,"芜湖");//将用无参构造器构造出来的user设置name属性值为”芜湖“
        System.out.println(user);//此时输出为:{name='芜湖', sex='null'}
        System.out.println(field);//输出“private java.lang.String User.name”
        System.out.println(field.getName());//输出“name”,只输出名字

        /**
         * 测试getField方法
         */
        Field field1=User.class.getField("sex");//获得指定参数名属性的Filed,只能获取public
        field1.set(user,"男");//此时输出为User{name='芜湖', sex='男'},表名以及成功赋值
        System.out.println(user);

        /**
         * 测试getDeclaredFields方法
         */
        Field[] fields = User.class.getDeclaredFields();//获得对象类中的全部属性,private 和 public 都可以获取到
        Field[] fields1 = User.class.getFields();//获得对象类中的公共属性,只能获取 public
        System.out.println(field1);//输出“public java.lang.String User.sex”
        System.out.println(field1.getName());//输出“sex”

        for (Field field2 : fields) {
            /*  输出:
                name
                sex
             */
            System.out.println(field2.getName());
        }

        for (Field field2 : fields1) {
            /*
            输出:
            sex
             */
            System.out.println(field2.getName());
        }
    }


}

通过反射机制调用方法

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Main2 {
    /**
     * 关于对象的方法需要掌握的一些方法以及使用的技巧
     */

    public static void main(String[] args) throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        User user=User.class.newInstance();

        /**
         * 测试 getMethod 方法,此方法第一个参数为对象中的方法名,第二个参数为需要传入的值的类型class对象,例如传入String就是String.class,如果不需要传入就输入null
         * 执行方法需要使用获取到的Method中的invoke方法,返回对应的参数
         * invoke第一个参数是对象的实例,第二个参数是传入的值
         * 每次执行invoke都有返回值,返回值是Object,如果是void函数,那么返回的是个null,否则为该方法的实际返回值
         */
        Method method=user.getClass().getMethod("getName",null);
        Method method1=user.getClass().getMethod("setName", String.class);
        System.out.println(method);//输出”public java.lang.String User.getName()“
        Object invoke = method.invoke(user, null);
        System.out.println(invoke);//输出”null“
        Object a = method1.invoke(user, "芜湖");
        System.out.println(a);//输出”null“
        System.out.println(user);//输出”User{name='芜湖', sex='null'}“
    }
}

打印操作示例代码

import java.lang.reflect.Method;

public class Test03 {

    public static void main(String[] args) throws ClassNotFoundException {
        Class c1 = Class.forName("pojo");


        Method[] declaredMethods = c1.getDeclaredMethods();//本类中所有的方法甚至私有方法的也能打印
        for(Method method:declaredMethods)
        {
            System.out.println(method.toString());
        }

        System.out.println("===============================================");
        Method[] methods = c1.getMethods();//本类中所有的方法以及父类中所有的方法,但是都只能打印public方法,不能打印私有方法
        for(Method method:methods)
        {
            System.out.println(method.toString());
        }

        /**
         * 类似的还有Field,Construtor都是一样的用法,这里就不一一赘述,declared作为前缀的都是可以打印私有方法的
         */

    }

}
class pojo{
    private int a;
    private int b;
    private int c;

   (此处为了简洁,省略有参,无参构造方法以及get,set方法,请读者务必写上)
}

反射操作注解

ORM:对象映射关系,例如

class Student{	
	int id;	String name;	
	int age;
}

对应表

利用反射和注解完成类和表结构的映射关系

综合代码:

import java.lang.annotation.*;
import java.lang.reflect.Field;

public class Test04 {


    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {

        //获得Class
        Class c1=Class.forName("student");

        //通过反射机制获得类注解
        Annotation[] annotations = c1.getAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println(annotation.toString());//输出:”@TableAnnotation(value=db_student)“
        }

        //通过反射机制获得类注解的value值
        TableAnnotation annotation = (TableAnnotation)c1.getAnnotation(TableAnnotation.class);
        System.out.println(annotation.value());//输出:”db_student“

        //通过反射机制获取类中属性的注解

        Field[] declaredFields = c1.getDeclaredFields();//这是获取全部属性
        for (Field field : declaredFields) {
            FieldAnnotation annotation1 = field.getAnnotation(FieldAnnotation.class);
            System.out.println(annotation1);
            /*
            输出:
            @FieldAnnotation(columnName=id, columnType=int, length=10)
            @FieldAnnotation(columnName=name, columnType=varchar, length=10)
            @FieldAnnotation(columnName=age, columnType=int, length=10)
             */
        }

        Field name = c1.getDeclaredField("name");//获取单个属性的注解,再输出单个注解的属性值
        FieldAnnotation annotation12 = name.getAnnotation(FieldAnnotation.class);
        System.out.println(annotation12.columnName());//输出:”name“
        System.out.println(annotation12.columnType());//输出:”varchar“
        System.out.println(annotation12.length());//输出:”10“

    }

}


@TableAnnotation("db_student")//从这里可以读出,在数据库中,存储实体类的叫db_student
class student{
    @FieldAnnotation(columnName = "id",columnType = "int",length = 10)//从这里可以读出,在数据库中该字段名是columnName的值也就是id,数据库中的存储类型是int,长度是10
    private int id;
    @FieldAnnotation(columnName = "name",columnType = "varchar",length = 10)
    private String name;
    @FieldAnnotation(columnName = "age",columnType = "int",length = 10)
    private int age;

   (此处为了简洁,省略有参构造方法以及get,set方法,请读者务必写上)
}

//自定义注解,用于存储表名,作用在类上
@Target(ElementType.TYPE)//类注解
@Retention(RetentionPolicy.RUNTIME)//运行时有效
@interface TableAnnotation{
    String value();
}



//自定义注解,作用于类属性,可以用于表示数据库中字段的基本信息
@Target(ElementType.FIELD)//作用于属性
@Retention(RetentionPolicy.RUNTIME)
@interface FieldAnnotation{
    //规范:属性类型+属性名()
    String columnName();
    String columnType();
    int length();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值