注解和反射(AOP学习基础)

请添加图片描述

注解和反射

注解入门

什么是注解

1.Annotation的作用:

​ 不是程序本身,可以对程序作出解释。(相当注释的作用)

可以被其他程序(比如:编译器)读取。

2.Annotation的格式:

​ 注解是以“@注释名”在代码中存在的,还可以添加一些参数值。

3.Annotation在哪里使用

​ 可以附加在package,class,method,field等上面,相当于给他们添加额外的辅助信息,我们可以通过反射机制编程实现对这些元数据的访问。

Java内置注解

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

@Deprecated:此注解通常用来修饰方法,属性,类,表示不建议使用,或使用有危险。

@SuppressWarning:用来抑制编译时的警告信息。需要添加信息。

例:

@SuppressWarnings(“all”)

@SuppressWarnings(value={“unchecked”,“deprecation”})

元注解

元注解的作用就是负责注解其他注解,java定义了4个标准的元注解,他们被用来提供对其他annotation类型作说明。

@Target:用来描述注解使用范围

@Retention :表示需要在什么级别保存该注释信息,用于描述注解的生命周期(SOURCE<CLASS<RUNTIME)所以一般都使用RUNTIMME。

@Documented :说明该注解将被包含在Javadoc中

@Inherited:说明子类可以继承父类中该注解

自定义注解

@interface用来声明一个注解,格式:public @interface 注解名{定义内容}。

每个方法实际上是声明了一个配置参数。

方法的名称就是参数的名称

返回值类型就是参数的类型(返回值只能是基本类型,Class,String,enum)

可以通过default来声明参数的默认值

如果只有一个参数成员, 一般参数名为value

注解原始必须有值,我们定义注解元素时,经常使用空字符串,0作为默认值。

@Target({ElemntType.TYPE,ElemntType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation{
    //注解参数:参数类型+参数名();
    String name() default "";//设置默认值
    int age() default 0;
    int id() default -1;//如果默认值为-1表示不存在。
    
    String[] schools() default {"清华","北大"}
}

class test1{
    //自定义注解使用
    @MyAnnotation(age=18,name="baba")
    public static void test(){
	
    }
}

反射学习

静态语言和动态语言

静态语言:运行时结构不可变的语言就是静态语言。如Java,c…Java不是动态语言,但Java可以称为”准动态语言“。即Java具有一定的动态性。

动态语言:在运行时可以改变其结构的语言:例如新的函数、对象、代码插入,已有的函数可以被删除或是其他结构上的变化。如:C#、js、PHP、Python…

Java Reflection

Reflection是Java被视为动态语言的关键,反射机制允许程序在执行期间借助于Reflect API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。

Class c = Class.forName("java.lang.String");

(JDBC的driver:类加载和导包还是不清楚)

加载完类后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的架构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以称为反射。

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

反射的作用:判断所属的类、创建对象、获取泛型、处理注解、动态代理(AOP的基础)

Java反射的优点:可以实现动态创建对象和编译,体现出很大的灵活性。

Java反射的缺点:对性能有影响。使用反射基本上是一种解释操作,我们可以告诉JVM我们的操作。这类操作通常比较慢。

Class 类

在Object类中定义了以下方法。

public final Class getClass()

以上的方法返回值的类型是一个Class类,是Java反射的源头,实际上所谓反射从程序的运行结果来看也很好理解。每一个类只有一个Class对象

特点:

Class本身也是一个类

Class对象只能由系统创建

一个加载的类在JVM中只会由一个Class实例

一个Class对象对应JVM一个.class文件

每个类的实例都可以获取它的Class

常用方法:

static ClassforName(String name)返回指定类名name的Classu对象

Object newInstance()调用缺省构造函数,返回Class对象的一个实例

getName()返回此Class对象所表示的实体的名称

Class getSuperClass()返回当前Class对象父类的Class

Class[] getinterfaces()获取当前Class对象的接口Class

ClassLoader getClassLoader()返回该类的类加载器

Construct[] getConstructors()返回包含某些Construct对象的数组

Method getMothed(String name,Class… T)返回一个Method对象,此对象的形参为paramType

Field[] getDeclaredFields()返回Field对象的一个数组

//方式一:通过对象获取
Class c1 = person.getClass();
//方式二:forname获取
Class c2 = Class.forName("com.fbw.pojo.Student");
//方式三:通过类名.Class获取
Class c3 = Student.class;
//方式四:基本内置类型的包装类的Type属性
Class c4 = Integer.TYPE;

/*源码解读
     * The {@code Class} instance representing the primitive type
     * {@code int}.
     *
     * @since   JDK1.1
     
    @SuppressWarnings("unchecked")
    public static final Class<Integer>  TYPE = (Class<Integer>) Class.getPrimitiveClass("int");
*/

//获取父类类型
Class c5 = c1.getSuperclass();

哪些类型可以有Class对象

注意几个特别的:interface、[]数组、enum枚举、annotation注解。

测试:

结果:

注意:

Java内存分析

当程序主动使用某个类时,如果该类还未被加载到内存,则系统会通过如下步骤进行初始化。

**加载:**将class文件字节码内容加载到内存,并将这些静态数据转换成方法区的运行时数据结构,然后生成一个Class对象。

**链接:**将Java类的二进制代码合并到JVM的运行状态中的过程。

​ 验证:确保加载的类信息符合JVM规范,没有安全问题。

​ 准备:正式为类变量分配内存并设置类变量默认初始值的阶段,这些内存都将在方法区中进行分配

​ 解析:虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程。

初始化:

​ 执行类构造器<clinit>()方法过程。这个方法是由编译期自动收集类中所有类变量(static)的赋值动作和静态代码块中的语句合并产生的。(类构造器是构造类信息的,不是构造对象的)

​ 当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先初始化父类。

​ 虚拟机会保证一个类的<clinit>()方法在多线程中被正确加锁和同步

分析类初始化

类的主动引用(一定会发生类的初始化)

类的被动引用(不会发生类的初始化)

​ 当访问一个静态域时,只有真正声明这个域的类才会被初始化。如:当通过子类引用父类的静态变量,不会导致子类初始化

(在编译链接初始化时,静态的东西已经加载)

​ 通过数组定义类引用,不会触发此类的初始化

(开辟空间)

​ 引用常量不会触发此类的初始化

(常量在编译链接时,已经加载)

类加载器的作用

类加载器的作用:将class文件字节码内容加载到内存中,并将这些静态数据转会为方法区的运行时数据结构,然后在堆中生成Class的对象,作为方法区中类数据的访问入口。

类缓存:(不常听到)标准的Java SE类加载器可以按要求查找类,但一旦某个类被加载到类加载器中,它将维持一段时间(缓存)。不过JVM垃圾回收机制可以回收这些Class对象。

类加载器作用是用来把类装载进内存。JVM规范定义了如下类型。

总结:Java的核心类库是由Bootstap Classloader(根加载器),根加载器是由c/c++编写的,Java无法直接获取它的对象。用户自定义类是由System Classloader(系统加载器)加载的。

机制:

双亲委派机制,类加载是由上自下的,当父类可以加载时就不会给子类加载。例如:自定义一个String类是无法被加载的。

创建运行时类的完整结构
Class c1 = Class.forName("com.fbw.test01");
//获取Class对象
c1.getName()//获取包名+类名
c1.getSimpleName()//获取类名
    
/*****************************************************/
//属性获取
File[] fields = c1.getFileds()//获取public属性

fields = c1.getDeclaredFields()//找到全部属性
    
Field name = c1.getDeclaredField("name")
    
/*****************************************************/
//方法获取
Method[] methods = c1.getMethods()//获取本来及其父类的全部public方法
methods = c1.getDeclaredMethods()//获得本类的所有方法
c1.getMethod("getName",null)//获取指定方法,指定参数类型的Class
c1.setMethod("setName",String.calss)//参数的类型的Class
//重载
/*****************************************************/
//构造器获取
Constructor[] constructors = c1.getConstructors()
//获取public的构造方法
constructors = c1.getDeclaredConstructors()
//获取全部的构造方法
    
Constructor constructor = c1.getDeclaredConstructor(String.calss,int.class)
//获取指定class,传入参数类型Class
反射创建对象

创建类的对象:调用Class对象的newInstance()方法

​ 类必须有一个无参构造器

​ 类的构造器的访问权限必须足够

	Class c1 = Class.forName("");
	User user = (User)c1.newInstance();//本质上是调用了类的无参构造
	//如果没有无参构造器就会报错

创建类的对象:当上一种条件无法满足,调用getDeclaredConstructor(Class …)取得本类的指定形式构造器

​ 通过Construct实例化对象

Constructor constructor = c1.getDeclaredConstructor(String.class);
User user2 = constructor.newInstance("String");

通过反射调用普通方法

Method setName = c1.getDeclaredMethod("setName",String.class);
setName.invoke(user2,"name");

注意:invoke方法,传入调用的对象,传入方法 参数。返回值为调用方法的返回值,需要强转。

method.getGenericParameterTypes();//获取方法参数类型,返回一个Type数组
method.getGenericExceptionTypes();//获取方法抛出异常类型,返回Type数组
method.getGenericReturnTypes();//获取方法返回值类型,返回一个Type

Type对象下面在泛类介绍时分析。

通过反射操作属性

Field name = c1.getDclaredField("name");
name.set(user2,"newName");

注意:set方法也需要传入对象和参数。但是,当属性是private修饰时不能访问修改。

setAccessible方法修改权限管理。

Field name = c1.getDclaredField("name");
name.setAccessible(true);//设置权限管理,可直接访问
name.set(user2,"newName");

Method、Field和Construct对象都有setAccessible方法。

性能对比分析

可见关闭检测也能提高运行速率。

反射操作泛型

Java采用泛型擦除的机制来引入泛型,Java中的泛型仅仅是给编译器javac使用的,确保数据的安全和免去强转类型转换问题,但是,一旦编译完成,所有和泛型相关的类型全部被擦除。(简而言之,泛型是保证编译安全,运行时被擦除)

为了提高反射操作这些类型,Java新增加了ParameterizedType,GenericArrayType,TypeVariable和WildcaedType几种类型代表不能被归一到Class类中的类型,但是又和原始类型齐名的类型。

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

这些方法的父类是Type 通过类型判断,来获取各类Type。

反射获取注解信息
Class c1 = Class.forName("com.fbw.User");

//获取的是类层面的注解
Annotation[] annotations = c1.getAnnotations();
TableKuang tableKuang = (TableKuang)c1.getAnnotation(TableKuang.class);//TableKuang为自定义注解
tableKuang.value();//获取注解值


//获取参数的注解
Field f = c1.getDeclaredField("name");
FieldKuang fieldKuang = f.getAnnotation(FieldKuang.class);//自定义注解
fieldKuang.name();//注解属性
fieldKuang.type();//注解属性
总结

学习注解、反射、Class、泛类等,以及它们在内存中过程。这篇学习的主要目的是为了学习理解AOP(面向切面编程)做准备。

请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值