注解和反射

注解

什么是注解?

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

注解是从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");
    }
}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,864评论 6 494
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,175评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,401评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,170评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,276评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,364评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,401评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,179评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,604评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,902评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,070评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,751评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,380评论 3 319
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,077评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,312评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,924评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,957评论 2 351

推荐阅读更多精彩内容

  • 1. 注解 1.1 注解的定义 注解就是源代码的元数据,通熟的讲就是代码中的标签。注解就有如下的特点: 注解是一个...
    犹梦渔樵阅读 213评论 1 0
  • 一.Java反射机制。 1.反射机制的定义。 Java反射机制是指在运行状态中,对于任意一个类,都能知道这个类的所...
    大鹏的鹏阅读 473评论 0 0
  • 注解  一般情况下,注解是用来给程序员、编译器提示的,就比如 @NoNull 就是用来提示不为空,在null时,编...
    ZuYuan阅读 1,621评论 0 5
  • 最近在研究阿里开源的ARouter框架,碰到了较多注解的使用。结合ButterKnife开源框架的使用,越来越体会...
    zizi192阅读 3,271评论 0 11
  • 基础 自定义注解 反射 动态语言是一类在运行时可以改变其结构的语言,Java不是动态语言,但Java可以称之为“准...
    小王_Ryan阅读 179评论 0 1