反射 and 注解《笔记》

反射 and 注解

反射机制有什么用?

  • 通过Java语言中的反射机制可以操作字节码文件。
  • 优点是类似黑客。(可以读和修改字节码文件)
  • 通过反射机制可以操作代码片段。(class文件)

获取类的字节码

Class.forName()
	1.静态方法
	2.方法的参数是一个字符串
	3.字符串需要的是一个完整类名
	4.完整类名必须带有包名。java.lang也不能省略
    
//java中任何一个对象都有一个方法:getClass()
    String s = "abc";
	Class x =s.getClass();//x表示String.class字节码文件,x代表String类型

//第三种方式,java语言中任何一种类型,包括基本数据类型,都有.class属性

	Class z = String.class;
	Class k = Date.class;
	Class f = int.class;
	Class e = double.class;

反射机制的好处

  • 可以只需要修改properties中的配置文件,例如下面例子中的className后面的values,就可以不需要动下面的任何代码,就可以创建一个实例
public static void main(String[] args) throws Exception {
        FileReader reader = new FileReader("classinfo.properties");
        Properties properties = new Properties();
        properties.load(reader);
        reader.close();

        String className = properties.getProperty("className");
        //System.out.println(className);

        //通过反射机制实例化对象
        Class c = Class.forName(className);
        Object o = c.newInstance();
        System.out.println(o);
    }

Class.forName()发生了什么?

  • Class.forName()这个方法的执行会导致:类加载。

    重点

    • 如果只希望一个类的静态代码块执行,其它代码一律不执行
    • 可以使用:Class.forName(“完整类名”);
    • 这个方法的执行会导致类加载,类加载时,静态代码块执行。

路径问题

//这种方式的路径缺点是:移植性差,在IDEA中默认的当前路径是project的根
//这个代码离开了IDEA就无效了
FileReader reader = new FileReader("classinfo.properties");
//通用路径
//使用这个通用方式的前提是:这个文件必须在类路径下
//凡是在src下的都是类路径下
//src是类的根路径


//获取的是绝对路径
String path = Thread.currentThread().getContextClassLoader().getResource("").getPath();

/*解释:
		Thread.currentThread()  当前线程对象
		getContextClassLoader()  是线程对象的方法,可以获取到当前线程的类加载器对象
		getResource()     [获取资源]这是类加载器对象的方法,当前线程的类加载器默认从类的根路径下加载资源
//直接以流的形式返回
InputStream reader = Thread.currentThread().getContextClassLoader().getResourceAsStream("");

资源绑定器

  • 只能绑定xxx.properties文件。并且这个文件必须在类路径下。文件拓展名也必须是properties,""中不能加后缀properties

  • 并且在写路径的时候,路径后面的拓展名不用写

  • ResourceBundle bundle = ResourceBundle.getBundle("");
    String  className = bundle.getString("className");
    

类加载器

  • 启动类加载器 rt.jar
  • 扩展类加载器 ext/*.jar
  • 应用类加载器 classpath

双亲委派机制

  • 优先从启动类加载器中加载,这个称为”父“
  • ”父“无法加载到,再从扩展类加载器中加载,这个称之为”母“
  • 如果都加载不到,才会考虑从应用类加载器中加载。直到加载到为止

回顾

  • 什么是反射机制?反射机制有什么用?

    • 操作字节码文件
    • 可以让程序更加灵活
  • 反射机制相关的类在哪个包下?

    • java.lang.reflect.*
  • 反射机制相关的主要的类?

    • java.lang.Class
    • java.lang.Method
    • java.lang.Constructor
    • java.lang.Field
  • 在java中获取Class的三种方式?

    • Class c = Class.forName("完整的类名");
      
    • Class c = 对象.getClass();
      
    • Class c = int.class();
      Class s = String.class();
      
  • 获取了Class之后,可以调用无参数构造方法来实例化对象

    • Class c = Class.forName("完整的对象");
      Object obj = c.newInstance();
      
    • newInstance()默认调用的是无参构造方法,如果原类中没有无参构造方法,就会报**“实例化”异常**

  • 如果你只想让一个类的“静态代码块”执行的话,你可以怎么做?

    • Class.forName(“该类的类名”);
    • 这样类就加载了,类加载的时候静态代码块也跟着执行了!!
  • 类路径是什么?就是项目名之前的所有东西,在IDEA中被src所指代

  • 关于类路径的问题

    • //绝对路径
      String path = Thread.currentThread().getContextClassLoader().getResource("xxx.yyy").getPath();
      

      通用方式,不会收到环境移植的影响

    • //以流的形式返回
      InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream("xxx.yyy");
      
    • //以.properties结尾的文件特别使用资源绑定器
      ResourceBundle bundle = ResourceBundle.getBundle("xxx");
      String value = bundle.getString(key);
      

反射属性Field

  • Field翻译为字段,其实就是属性/成员

  • Filed[] fields = c.getFields();//只能获取public属性
    Filed[] fields = c.getDeclaredFields();//获取所有的属性
    
    //获取权限修饰符
    int modifiers = field.getModifiers();//返回的修饰符是一个数字,每个数字是修饰符的代号!
    //利用toString方法来获取名称
    String s = Modifier.toString(modifiers);
    //Class中也有getModifiers()方法
    
  • 通过反射机制,反编译一个类的属性Field

  •   Class c = Class.forName("Strudent");
            Field[] fields = c.getDeclaredFields();
            StringBuilder s = new StringBuilder();
            s.append(Modifier.toString(c.getModifiers()) + " class " + c.getSimpleName() + " {\n" );
    
            for (Field field : fields ) {
                s.append("\t"+Modifier.toString(field.getModifiers()) + " ");
                s.append(field.getType().getSimpleName() + " ");
                s.append(field.getName());
                s.append(";\n");
            }
    
            s.append("}");
            System.out.println(s);
    

怎么通过反射机制访问一个java对象的属性?

  • 给属性赋值set

  • 获取属性的值get

  • 		Class studentclass = Class.forName("Strudent");
            Object object = studentclass.newInstance();
            
            //获取balance属性(根据属性的名字来获取Field)
            Field balance = studentclass.getDeclaredField("balance");
    
            //给object对象(Student对象)的balance属性赋值
            
            /*
            * 虽然使用的是反射机制,但是三要素缺一不可:
            * 要素1:object对象
            * 要素2:balance属性
            * 要素3:2222值
            * 
            * 反射机制让代码复杂了,但是为了“灵活”,值得
            * */
    
            balance.set(object,2222);
    		//读取属性的值:
    		//两个要素:获取obj对象的no属性的值
            Object o1 = balance.get(object);
            System.out.println(o1);
    
  • 这种方式不能访问私有属性

  • 打破封装

    • balance.setAccessible(true);
      

可变长度参数

  • 语法:类型…(注意,一定是三个点)
  • 只能出现在最后,只能有一个
  • 可变长度参数可以当成一个数组来看待

Method***************十分重要

  • 通过反射机制调用以个对象的方法?

  • Class c = Class.forName("");
    Object obj = c.newInstance();
    
    
    /*要素分析
    	要素1:对象
    	要素2:方法名
    	要素3:实参列表
    	要素4:返回值
    */	
    //获取对象
    Method xx = c.getDeclaredMethod("方法名",实参列表.class,....);
    //调用方法
    Object invoke = xx.invoke(obj, 属于参数类型的值);
    

Constructor

  • 反射机制调用构造方法
 Class c= Class.forName("Strudent");
        //调用无参构造方法1
        Object obj = c.newInstance();
        System.out.println(obj);

        //调用有参数的构造方法
        Constructor con = c.getDeclaredConstructor(int.class);
        Object newobj = con.newInstance(22);
        System.out.println(newobj);

        //调用无参构造方法2
        Constructor con2 = c.getDeclaredConstructor();
        Object newobj2 = con2.newInstance();
        System.out.println(newobj2);

注解(annotation)

  • 注解Annotation是一种引用数据类型。编译之后也是生成xxx.class文件

  • 怎么自定义注解?语法格式?

    • [ 修饰符列表 ] @interface 注解类型名{}
  • 注解怎么使用?用在什么地方?

    • 注解使用的语法格式是:
      • @注解类型名
    • 注解可以出现在类上、属性上、方法上、变量上等
      • 注解还可以出现在注解类型上
  • 关于JDK lang包下的Override注解

    • @Override这个注解只能注解方法
    • @Override这个注解是给编译器参考的,和运行阶段没有关系
    • 凡是java中的方法带有这个注解的,编译器都会进行编译检查,如果这个方法不是重写父类的方法,编译器报错
    • 标识性注解,给编译器做参考的
  • 元注解

    • 什么是元注解?

      • 用来标注“注解类型”的“注解”,称为元注解
      • 常用的元注解:Target、Retention
    • Target

      • Target注解用来标注“被标注的注解”可以出现在哪些位置上

      • @Target(ElementType.METHOD)//表示“被标注的注解”只能出现在方法上
        
    • Retention

      • Retention注解用来标注“被标注的注解”最终保存在哪里

      • @Retention(RetentionPolicy.SOURCE)//表示该注解只被保留在java源文件中
        @Retention(RetentionPolicy.CLASS)//表示该注解只被保留在class文件中
        @Retention(RetentionPolicy.RUNTIME)//表示该注解只被保留在class文件中,并且可以被反射机制所读取到
        
  • 关于JDK lang包下的Deprecated注解

    • Deprecated注解标注的元素已过时
    • 这个注解主要是向其它程序员传达一个信息,告知已过时,有更好的解决方案存在
  • 我们通常可以在注解中定义属性

    • public @interface MyAnnotation{
      	String name();
          //看着像一个方法,但实际上称之为一个属性
      }
      
    • 当我们使用这个注解的时候,就要给这个属性赋值

      @MyAnnotation(name = "zhangsan")//如果用default设置默认值,就可以不用写了
      public void doSome(){
      	
      }
      
    • 如果一个注解属性的名字是value的话,用注解的时候,属性名可以不写

    • 属性的类型可以是

      • 八种基本数据类型+String,Class,枚举类型,以及他们的数组形式

反射注解

  •   //获取这个类,方法同理
            Class c = Class.forName("User");
            //判断类上面是否有@MyAnnotation
            boolean s = c.isAnnotationPresent(MyAnnotation.class);
            System.out.println(s);
            //获取该注解对象
            Annotation annotation = c.getAnnotation(MyAnnotation.class);
            System.out.println(annotation);
            //获取属性
            MyAnnotation myAnnotation = (MyAnnotation) annotation;
            String value = myAnnotation.value();
            System.out.println(value);
    
  • //注解的元注解一定要是Retention((RetentionPolicy.RUNTIME)
        @Retention(RetentionPolicy.RUNTIME)
        public @interface MyAnnotation {
            String value() default "我是一个中国人";
        }
    

注解的作用

  • 未来都是通过注解来开发,利用反射机制读注解

Foreach的使用

for ( 类型 名字: 传值) {
            System.out.println(名字.方法);
        }

可以不需要管长度,直接遍历完,一般用于数组

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值