美文网首页
注解和反射

注解和反射

作者: 忒无聊了叭 | 来源:发表于2020-04-15 22:52 被阅读0次

注解

什么是注解?

通俗易懂的来说:注释是给人看的,注解可以给机器看。

注解是从JDK5.0引入的新技术。

内置注解

@Override:定义在java.lang.Override中,此方法只适用于修辞方法。表示一个方法声明打算重写父类中另一个方法。

@Deprecated:定义在java.lang.Deprecated中,表示当前方法已经过时,不建议使用或者已经存在了更好的方法来选择。

@SuppressWarnings:定义在java.lang.SuppressWarnings中,用来抑制编译时的警告信息。

元注解

@Target:表明在哪里使用这个注解,类或者方法

@Retention:表明在运行时使用还是在源码范围使用runtime>class>sources

@Documented//表明是否生成Javadoc

@Inherited//表明子类可以继承父类的注解

package com;

import java.lang.annotation.*;

@myAnnotation
public class Test1 {
}
//元注解
@Target(value = {ElementType.TYPE})//表明在方法还是类上使用。。
@Retention(value = RetentionPolicy.RUNTIME)//表明在运行时使用还是源码。。
@Documented//表明是否生成Javadoc
@Inherited//表明子类可以继承父类的注解
@interface myAnnotation{
}

自定义注解

package com;

import java.lang.annotation.*;

@myAnnotation3("自定义")
@myAnnotation2(age = 18)
public class Test2 {
}
@Target(value = {ElementType.TYPE})
@Retention(value = RetentionPolicy.RUNTIME)
@interface myAnnotation2{
    //注解的参数:参数类型+参数名();这里的括号不代表方法!!
    String name() default "";
    int age();
    int id() default -1;
    String[] schools() default {"安阳师范学院","软件学院"};
}

@Target(value = {ElementType.TYPE})
@Retention(value = RetentionPolicy.RUNTIME)
@interface myAnnotation3{
    String value();//不成为的规定,如果注解只有一个参数,最好用value进行定义参数名,这样标注时可以省略value
}

反射机制

先了解一下静态动态语言:

动态语言:是一类运行时可以改变结构的语言:例如新的函数、对象、甚至代码可以被引进。主要的动态语言:JavaScript、PHP、Python。

静态语言:与动态语言不同的就是,运行时无法改变自己的结构。例如Java、C、C++。

Java不是动态语言,但是我们可以称为准动态语言,就是因为反射机制的存在,因为反射机制,可以让java编程更具有灵活性,但是也会存在一些性能上的弊端。

1585377329185.png

获得反射对象

package com;

import lombok.Data;

//反射
public class reflection {
    public static void main(String[] args) throws ClassNotFoundException {
        Class a = Class.forName("com.User");
        Class b = Class.forName("com.User");
        Class c = Class.forName("com.User");
        Class d = Class.forName("com.User");
        System.out.println(a);
        //这里的hashcode都是一个值,代表相同的对象
        System.out.println(b.hashCode());
        System.out.println(c.hashCode());
        System.out.println(d.hashCode());
    }
}

@Data
class User{
    private String name;
    private int age;
    private String password;
}

Class类的创建方式

class类的创建方式

第一个阶段:源代码阶段,就是我们自己写的java文件和编译生成的.class字节码文件。

第二阶段:是字节码文件被类加载器加载后的文件。是Class类对象阶段。

第三阶段:就是运行时阶段,这时候我们可以根据创建好的对象获取Class对象。

三种方式获取Class对象:(对应上述三种阶段)

  • class.forName("全类名")

    *多用于配置文件,因为括号中写字符串

  • 类名.class

    *多用于参数的传递

  • 对象.getClass()

    *多用于对象获取字节码方式

结论:同一个字节码文件(*.class)在程序运行的过程中,只会被加载一次,无论使用哪一种方式获取的Class对象都是一样的。

package com;

import lombok.Data;

public class Test3 {
    public static void main(String[] args) throws ClassNotFoundException {
        Person person = new Student();
        //方式一:通过对象获取,运行时获取
        Class c1 = person.getClass();
        System.out.println(c1);
        //方式二:forName获得,源代码阶段获取,静态方法
        Class c2 = Class.forName("com.Student");
        System.out.println(c2);
        //方式三:通过类名.class获取,类加载器获取
        Class c3 = Student.class;
        System.out.println(c3);
        //方式四:基本内置类型的包装类都有一个TYPE属性
        Class c4 = Integer.TYPE;
        System.out.println(c4);
        //获得父类类型
        Class c5 = c1.getSuperclass();
        System.out.println(c5);
    }
}
@Data
class Person{
    public String name;
}
class Student extends Person{
    public Student() {
        this.name = "学生";
    }
}
class Teacher extends Person{
    public Teacher() {
        this.name = "老师";
    }
}

所有类型的Class对象

package com;

import java.lang.annotation.ElementType;

public class AllClass {
    public static void main(String[] args) {
        Class c1 = Object.class;    //类
        Class c2 = Integer.class;   //整型
        Class c3 = Comparable.class;    //接口
        Class c4 = int[].class;  //一维数组
        Class c5 = int[][].class;//二维数组
        Class c6= Override.class;//注解
        Class c7 = void.class;//void
        Class c8 = Class.class;//Class
        Class c9 = ElementType.class;//枚举
        
        System.out.println(c1);
        System.out.println(c2);
        System.out.println(c3);
        System.out.println(c4);
        System.out.println(c5);
        System.out.println(c6);
        System.out.println(c7);
        System.out.println(c8);
        System.out.println(c9);
    }
}

Class对象.png

Java内存分析

Java内存分析.png

方法区相当于一个特殊的堆。

类加载器 加载过程

听课的个人理解(不知道对不对):

首先加载内存,把各种类的数据转换成Class对象,存入堆中。然后进行链接,链接结束m=0,最后clinit方法从堆中取出来需要的类,合并代码。

反射对象Class对象功能:

以下从jdk文档中查到:

获取成员变量
Field[] getFields() :获取所有public修饰的成员变量
Field getField(String name)   获取指定名称的 public修饰的成员变量

Field[] getDeclaredFields()  获取所有的成员变量,不考虑修饰符
Field getDeclaredField(String name) 
package com.ClassForMethod;
import com.pojo.User;
import java.lang.reflect.Field;

//获取成员变量
public class test1 {
    public static void main(String[] args) throws NoSuchFieldException {
        User a = new User();
        Class c1 = a.getClass();
        //获取public修饰的成员变量
        Field[] fields = c1.getFields();
        for (Field f:fields){
            System.out.println(f);
        }
        System.out.println("=======================");
        //获取指定名称的public修饰的成员变量
        Field f = c1.getField("age");
        System.out.println(f);
        System.out.println("=======================");
        //获取所有的成员变量
        Field[] fields1 = c1.getDeclaredFields();
        for (Field fList:fields1){
            System.out.println(fList);
        }
        System.out.println("=======================");
        Field field = c1.getDeclaredField("sex");
        System.out.println(field);
    }
}
获取构造方法们
Constructor<?>[] getConstructors()  
Constructor<T> getConstructor(类<?>... parameterTypes)  

Constructor<?>[] getDeclaredConstructors()  
Constructor<T> getDeclaredConstructor(类<?>... parameterTypes)  
package com.ClassForMethod;

import com.pojo.User;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class test2 {
    public static void main(String[] args) throws Exception{
        User u = new User();
        Class classUser = u.getClass();
        //查询到public修饰的所有构造方法
        System.out.println("========查询到public修饰的所有构造方法==========");
        Constructor[] c1s = classUser.getConstructors();
        for (Constructor constructor:c1s){
            System.out.println(constructor);
        }
        //括号内如果不写就是获取无参,带参的就是获取带参,并且参数顺序要与原来位置一样
        System.out.println("========查询带参的构造方法==========");
        Constructor c2 = classUser.getConstructor();
        System.out.println(c2);
        System.out.println("========查询不带参的构造方法==========");
        Constructor c3 = classUser.getConstructor(String.class,int.class);
        System.out.println(c3);
        //我们可以获取带参的构造方法后进行赋值
        User user1 = (User)c3.newInstance("小明",18);
        System.out.println(user1);
        System.out.println("========查询不带参的构造方法==========");
        //对于一般的无参构造函数,我们都不会先获取无参构造器之后在进行初始化。而是直接调用Class类内的newInstance()方法
        Object user3 = classUser.newInstance();
        System.out.println(user3);
    }
}
获取成员方法们
Method[] getMethods()  
Method getMethod(String name, 类<?>... parameterTypes)  

Method[] getDeclaredMethods()  
Method getDeclaredMethod(String name, 类<?>... parameterTypes)
package com.ClassForMethod;

import com.pojo.User;

import java.lang.reflect.Method;

public class test3 {
    public static void main(String[] args) throws Exception{
        Class userClass = User.class;
        Method[] methods = userClass.getMethods();
        System.out.println("============查询public修饰的方法===============");
        for (Method m:methods){
            System.out.println(m);//查询出来很多的原因是我们的user类归属object类,所以这里有很多其他的
        }
        System.out.println("============查询public修饰带参的方法===============");
        Method method = userClass.getMethod("sleep");
        System.out.println(method);
        User u = new User("小明",1);
        method.invoke(u);//执行方法,放入对象。
        Method method2 = userClass.getMethod("sleep",String.class);
        System.out.println(method2);
        method2.invoke(u,"小红");//注意这里多个参数时,这里要传值
        
        
         Field field = userClass.getDeclaredField("name");
        field.setAccessible(true);//不能直接操作私有属性,我们先关闭安全监测
        field.set(u,"明明");
        System.out.println(u.getName());

    }
}
获取简单方法、简单类名
String getName() 
package com.ClassForMethod;

import com.pojo.User;

import java.lang.reflect.Method;

public class test04 {
    public static void main(String[] args) {
        Class user = User.class;
        Method[] methods = user.getMethods();
        for (Method m:methods){
            String easy = m.getName();
            System.out.println(easy);
        }
    }
}

注意:我们除了上面说可以用Declared修饰的方法获取所有的方法名、成员变量名。。。还可以使用暴力反射来获取。

xxx.setAccessible(true);

类加载器:

类加载器的作用:是把类(class)装载进内存,JVM规范定义了三种类型的类加载器。

引导类加载器:用C++编写,是JVM自带类的加载器,负责Java平台核心库。用来装载类库,这个加载器无法直接获取。

扩展类加载器:负责jre/lib/text目录下的jar包或者-D java.ext.dirs指定目录下的jar包装入工作库。

系统类加载器:负责java -classpath或者-D java.class.path所指的目录下的类与jar包装入工作,是最常用的加载器。

类加载器作用
package com;

public class classLoader {
    public static void main(String[] args) throws ClassNotFoundException {
        //获取系统类的加载器
        ClassLoader systemLoader = ClassLoader.getSystemClassLoader();
        System.out.println(systemLoader);

        //获取系统类加载器的父类加载器->扩展类加载器
        ClassLoader parent = systemLoader.getParent();
        System.out.println(parent);

        //获取扩展类加载器的父类加载器->根加载器(C++)
        ClassLoader parent1 = parent.getParent();
        System.out.println(parent1);

        //测试当前类是哪一个加载器加载的
        ClassLoader classLoader = Class.forName("com.classLoader").getClassLoader();
        System.out.println(classLoader);

        //测试jdk内置是哪一个加载器加载的
        ClassLoader sysClassLoder = Class.forName("java.lang.Object").getClassLoader();
        System.out.println(sysClassLoder);
    }
}

调用方法的三种性能检测:

package com;

import java.lang.reflect.Method;

public class performance {
    public static void main(String[] args) throws Exception {
        //性能测试,关闭了安全监测
        test01();
        test02();
        test03();
    }

    public static void  test01(){
        //普通方法调用
        User user = new User();
        long start = System.currentTimeMillis();
        for (int i = 0; i < 100000000; i++) {
            user.getName();
        }
        long end = System.currentTimeMillis();
        System.out.println(end-start+"ms");
    }
    public static void test02() throws Exception {
        //反射调用
        User user = new User();
        Class userClass = User.class;
        Method getName = userClass.getMethod("getName",null);
        long start = System.currentTimeMillis();
        for (int i = 0; i < 100000000; i++) {
         getName.invoke(user,null);
        }
        long end = System.currentTimeMillis();
        System.out.println(end-start+"ms");
    }
    public static void test03() throws Exception {
        //反射调用,关闭安全监测
        User user = new User();
        Class userClass = User.class;
        Method getName = userClass.getMethod("getName",null);
        long start = System.currentTimeMillis();
        getName.setAccessible(true);
        for (int i = 0; i < 100000000; i++) {
            getName.invoke(user,null);
        }
        long end = System.currentTimeMillis();
        System.out.println(end-start+"ms");
    }
}

相关文章

  • 注解和反射

    注解和反射

  • 一文搞懂反射泛型和反射注解以及通过注解方式写一个BaseDao

    反射泛型和反射注解概括起来就三步: 自定义注解 通过反射获取注解值 使用自定义注解 最终案例 通过自定义注解,将数...

  • 从Java到Kotlin(七)

    反射和注解 目录 1.反射1.1类引用1.2函数引用1.3属性引用 2.注解2.1声明注解2.2构造函数 1.反射...

  • 注解和反射

    (1)注解@Interface(2)@Retention 注解可以保留多长时间:RetentionPolicy....

  • 注解和反射

    注解 什么是注解? 通俗易懂的来说:注释是给人看的,注解可以给机器看。 注解是从JDK5.0引入的新技术。 内置注...

  • 注解和反射

    1、注解的基本理解https://www.jianshu.com/p/9471d6bcf4cf[https://w...

  • 注解和反射

    来源于狂神说笔记

  • 注解和反射

    注解 定义数据 定义数据 输入错误会提示 错误提示 下面解释 @Target:注解的作用目标 参数原文注解Elem...

  • 注解和反射

    https://www.bilibili.com/video/BV1p4411P7V3?p=1[https://w...

  • AS运行Annotation

    AS 运行 Annotation 注解分为运行时注解和编译时注解 运行时注解 原理就是反射和代理(Proxy ) ...

网友评论

      本文标题:注解和反射

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