第1章 ◄Java基础►

在学习Spring之前我们需要对Java基础语法有一定的了解,Java中最重要的两个知识点是注解和反射。注解和反射在Spring框架中应用的最广泛。掌握注解和反射,有助于后面Spring的学习。

本章主要涉及的知识点:
● 注解基础:什么是注?怎么理解注解?什么是元注解?
● 注解应用:自定义注解、注解的应用场景。
● 反射:反射的定义、反射的应用。

注意
不管学习什么框架都需要先把Java基础夯实,基础打好之后才能厚积薄发。用到的时候不能只会用,不知道为什么这样用。学习编程还有最重要的一点就是需要勤动手,不能眼高手低,看着会做,真要动手时无从下手。

1.1 注解

​ 本节首先介绍注解的基本概念,理解什么是注解、注解的作用是什么。在此基础上通过示例动手操作加深理解。

1.1.1 什么是注解

​ 我们先看官方解释:它提供了一种安全的类似注释的机制,用来将任何的信息或元数据(metadata)与程序元素(类、方法、成员变量等)进行关联。为程序的元素(类、方法、成员变量)加上更直观、更明了的说明,这些说明信息与程序的业务逻辑无关,并且供指定的工具或框架使用。Annontation像一种修饰符一样,应用于包、类型、构造方法、方法、成员变量、参数及本地变量的声明语句中。Java注解是附加在代码中的一些元信息,便于一些工具在编译、运行时进行解析和使用,起到说明、配置的功能。注解不会也不能影响代码的实际逻辑,仅仅起到辅助性的作用,包含在java.lang.annotation包中。
​ 看着上面的解释是不是还是一头雾水?其实我们可以更通俗地理解一下。最近几年出现一个词“斜杠青年”,还有黄某某拍摄的广告语:给人贴标签、下定义,总是很容易,而我却不会因为一件事被定性。这里的斜杠青年、贴标签都是把某些属性附加给对象,和注解功能差不多,它提供了一种安全的类似注释的机制,用来将任何信息或元数据(metadata)与程序元素(类、方法、成员变量等)进行关联。我们可以再来理解一下这句话,这里的程序元素可以理解为人,信息或元数据理解为标签,把标签属性(信息或元数据)赋给人(程序元素)。
​ 上面两段基本把什么注解解释出来了,如果还是不知道注解是什么,那也没关系。其实我们在编程中已经用到或者看到过了,比如@Override、@Deprecated。是不是很熟悉?其实它们就是注解。

1.1.2 内置注解

​ 上面的@Override、@Deprecated都是Java中内置的注解,除了这两个还有其他的内置注解。这里列举了几个常用的内置注解以及它们的作用。

​ ● @Deprecated:编译器在编译阶段遇到这个注解时会发出提醒警告,告诉开发者正在调用一个过时的元素,比如过时的方法、过时的类、过时的成员变量。
​ ● @Override:提示子类要复写父类中被@Override修饰的方法。
​ ● @SuppressWarnings:阻止警告的意思。调用被@Deprecated注解的方法后,编译器会警告提醒,而有时候开发者会忽略这种警告,他们可以在调用的地方通过@SuppressWarnings:达到目的。
​ ● @SafeVarargs:参数安全类型注解。它的目的是提醒开发者不要用参数做一些不安全的操作,它的存在会阻止编译器产生unchecked这样的警告。它是在Java 1.7的版本中加入的。
​ ● @FunctionalInterface:函数式接口注解,这个是Java 1.8版本引入的新特性。函数式编程很火,所以Java 8也及时添加了这个特性。函数式接口(Functional Interface)就是一个具有一个方法的普通接口。

1.1.3 元注解

​ 通过前面的两小节,我们应该对注解有了一定的认识,下面进一步地了解一下注解。我们在自定义注解时会出现图1-1所示的一些选项。

图1-1
这些选项@Retention、@Target、@Documented其实就是元注解。在创建时配置这些元注解,我们也可以推断出元注解的作用是什么。元注解负责注解自定义注解。java.lang.annotation提供了5种元注解,专门注解其他的注解:

​ ● @Retention:什么时候使用该注解。
​ ● @Target:注解用于什么地方。
​ ● @Documented:注解是否将包含在JavaDoc中。
​ ● @Inherited:是否允许子类继承该注解。
​ ● @Repeatable:指定注解可重复使用。

1.@Retention定义注解的生命周期

​ ● RetentionPolicy.SOURCE:在编译阶段丢弃。这些注解在编译结束之后不再有任何意义,所以它们不会写入字节码。@Override和@SuppressWarnings都属于这类注解。
​ ● RetentionPolicy.CLASS:在类加载的时候丢弃。在字节码文件的处理中有用。注解默认使用这种方式。
​ ● RetentionPolicy.RUNTIME:始终不会丢弃,运行期也保留该注解,因此可以使用反射机制读取该注解的信息。我们自定义的注解通常使用这种方式。

2.@Target表示注解用于什么地方

默认值为任何元素,表示该注解用于什么地方。可用的ElementType参数包括:
● ElementType.CONSTRUCTOR:用于描述构造器。
● ElementType.FIELD:成员变量、对象、属性(包括enum实例)。
● ElementType.LOCAL_VARIABLE:用于描述局部变量。
● ElementType.METHOD:用于描述方法。
● ElementType.PACKAGE:用于描述包。
● ElementType.PARAMETER:用于描述参数。
● ElementType.TYPE:用于描述类、接口(包括注解类型)或enum声明。

3.@Documented是一个简单的Annotations标记注解

表示是否将注解信息添加在Java文档中。

4.@Inherited定义注解和子类的关系

@Inherited元注解是一个标记注解,阐述了某个被标注的类型是被继承的。如果一个使用了@Inherited修饰的annotation类型被用于一个class,那么这个annotation将被用于该class的子类。

5.@Repeatable指定注解可重复使用

使用@Repeatable修饰表示该注解可以为重复使用。

1.1.4 自定义注解

​ 元注解是负责注解自定义注解的。自定义注解时是有一些规则限制的,具体如下:
​ ● Annotation型定义为@interface,所有的Annotation会自动继承java.lang.Annotation这一接口,并且不能再去继承别的类或是接口。
​ ● 参数成员只能用public或默认(default)这两个访问权修饰。
​ ● 参数成员只能用基本类型byte、short、char、int、long、float、double、boolean八种基本数据类型和String、Enum、Class、annotations等数据类型,以及这一些类型的数组。
​ ● 要获取类方法和字段的注解信息,必须通过Java的反射技术来获取Annotation对象,因为除此之外没有其他获取注解对象的方法。

​ ● 注解也可以没有定义成员。
我们这里自定义一个注解来练习一下,主要用来演示自定义注解以及注解的继承。

1.定义CustomDescription注解

CustomDescription注解相当于标签。为了能多贴标签,又定义了注解容器CustomDescriptions。其中,@Retention(RUNTIME)表示在运行时环境也可以获取注解,@Inherited表示可继承,@Repeatable(CustomDescriptions.class)表示该注解可多次使用。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Inherited
@Repeatable(CustomDescriptions.class)
public @interface CustomDescription {}

CustomDescriptions容器:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Inherited
public @interface CustomDescriptions {CustomDescription[] value();
}

2.实现继承关系

​ 这里为了演示,我们创建了两个类:一个基类Person,一个子类Student。在Person类加两个自定义注解,在Student中加一个自定义注解。

Person:

@CustomDescription(description = "基类")
@CustomDescription(description = "人")
public class Person {private String name;public String getName() {return name;}public void setName(String name) {this.name = name;}
}

Student:

@CustomDescription(description = "学生")
public class Student extends Person {private String StudentId;public String getStudentId() {return StudentId;}public void setStudentId(String studentId) {StudentId = studentId;}
}

3.通过反射获取注解属性值

​ 这里我们想通过反射(可以先不要理解)获取Student类的注解值,那么问题来了,它是输出什么的呢?会输出“description:学生”吗?并不是,而是输出父类Person的注解。

public static void main(String[] args) {
// write your code hereCustomDescriptions annotation = new Student().getClass().getAnnotation(CustomDescriptions.class);for (CustomDescription h: annotation.value()){System.out.println("description" + h.description());}}

输出:
description:基类
description:人

如果想输出子类Student的注解该怎么设置呢?很简单,只需在子类Student上覆盖父类的注解就好。

@CustomDescription(description="学生")
@CustomDescription(description="人")
public classStudent extendsPerson
输出:
description:学生
description:人

​ 此时输出的就是子类的注解值了。这里我们还可以验证@Retention生命周期的作用,只需要把@Retention(RUNTIME)改成CLASS,再运行就会报错,因为main方法中的custormDescriptions对象是一个null空值。不过自定义注解一般来说都是使用@Retention(RUNTIME)。

1.1.5 注解使用场景介绍

​ 在上一小节通过实例学习了自定义注解的使用,之后就该解决怎么用的问题了。其实,注解应用的场景还是挺多的。
(1)使用注解做bean的属性值校验,例如在开发Java服务器端代码时,会要求对外部传来的参数合法性进行验证。hibernate-validator提供了一些常用的参数校验注解。
(2)使用注解做权限控制。例如,shiro框架中有5个权限注解,我们也可以自定义注解进行权限控制。
(3)代替配置文件功能,像Spring基于注解的配置,减少了xml的配置。
(4)可以生成文档,像Java代码注释中的@see、@param等。
这里只是列举了几个使用场景,其实还有很多地方可以使用注解。

1.2 反射

​ 本节首先介绍反射的基本概念,理解什么是反射,以及Class类和反射常用API,通过实例操作来学习反射的使用。

1.2.1 反射机制

​ 在上面自定义注解时我们也有提到反射,要获取类方法和字段的注解信息,必须通过Java的反射技术来获取Annotation对象。那么什么是反射呢?在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性,这种动态获取的信息以及动态调用对象的方法的功能称为Java语言的反射机制。它有点类似照妖镜的作用,不管是什么妖魔鬼怪(类或对象)都能看到它的真面目(获取类的属性方法、调用对象的属性方法)。

1.2.2 理解Class类

​ 反射机制可以动态获取类信息以及调用对象方法,那它是通过什么实现的呢?这就要介绍一下Class类了。首先明确Class也是一个类,只是它是一个描述类的类,也可以生成对象。对于每个类而言,在JRE中有且仅有一个不变的Class类型的对象,而这个Class类型的对象只能由系统建立,封装了当前对象所对应的类的信息,有哪些属性、方法、构造器以及实现了哪些接口等。每个类的实例都会记得自己是由哪个Class实例所生成的。
​ 要获取类信息或调用对象方法,肯定首先要获取到该类或对象对应的Class类的实例。一般获取Class对象有3种方式。

​ ● 通过类名获取,类名.class。
​ ● 通过对象获取,对象.getClass()。
​ ● 通过全类名获取,Class.forName(全类名)。
​ 这里我们可以使用字符串来做验证。

public static void main(String[] args) throws ClassNotFoundException {//字符串的列子Class clazz = null;//通过类名获取,类名.class。clazz = String.class;System.out.println(clazz);//通过对象获取,对象.getClass()。clazz = "ReflectionTest".getClass();//通过全类名获取,Class.forName(全类名)。clazz = Class.forName("java.lang.String");}

输出结果:

class java.lang.String
class java.lang.String
class java.lang.String

​ 通过3种方式获取到Class实例后,再了解一下Class类常用的方法(见表1-1)。

​ 表1-1 Class类常用的方法

1.2.3 反射的使用

​ 这里要着重介绍一下上面API的使用,因为在后面要学习的Spring中IOC的原理就是反射加工厂模式。学好反射API有助于理解Spring框架内部实现。为了演示Class方法的使用,在注解demo的基础上对Person、Student类进行了修改。

Person类:

@CustomDescription(description = "基类")
@CustomDescription(description = "人")
public class Person {private String name;public String getName() {return name;}public void setName(String name) {this.name = name;}public Person(){super();}public Person(String name){this.name = name;}public String PersonPublicMethod(String str){return str;}private String PersonPrivateMethod(String str){return str;}
}

Student

@CustomDescription(description = "学生")
public class Student extends Person {private String StudentId;public String getStudentId() {return StudentId;}public void setStudentId(String studentId) {StudentId = studentId;}public Student(String name,String studentId){super(name);StudentId = studentId;}public Student(String name){super(name);StudentId = "123456";}public String PersonPublicMethod(String str){return str;}private String PersonPrivateMethod(String str){return str;}
}

1.描述方法Method

​ 描述方法主要是4个获取方法(getMethods、getMethod、getDeclaredMethods、getDeclaredMethod)和1个调用方法(invoke)。

​ ● getMethods:获取clazz对应类中的所有方法,不能获取private方法,且获取从父类继承来的所有方法,包括私有父类的私有方法。
​ ● getMethod:获取clazz对应类中指定方法名和参数类型的方法,不能获取private方法,且获取从父类继承来的所有方法,包括私有父类的私有方法。因为存在同方法名不同参数这种情况,所以只有同时指定方法名和参数类型才能唯一确定一个方法。
​ ● getDeclaredMethods:获取所有方法,包括私有方法,所有声明的方法,都可以获取到,且只获取当前类的方法。
​ ● getDeclaredMethod:获取clazz对应类中指定方法名和参数类型的方法,包括私有方法,所有声明的方法,都可以获取到,且只获取当前类的方法。
​ ● invoke:执行方法,第一个参数表示执行哪个对象的方法,剩下的参数是执行方法时需要传入的参数,私有方法的执行必须在调用invoke之前加上一句“method.setAccessible(true);”

测试Method

Class<?> clazz = Class.forName("spring.tutorial.CusAnnontation.Student");
Method method = null;
Method[] methods = null;methods = clazz.getMethods();
for (Method mth : methods){System.out.print(mth.getName() + " ");
}
System.out.println();method = clazz.getMethod("StudentPublicMethod",String.class);
System.out.print(method.getName() + " ");
System.out.println();methods = clazz.getDeclaredMethods();
for (Method mth : methods){System.out.print(mth.getName() + " ");
}
System.out.println();method = clazz.getDeclaredMethod("StudentPrivateMethod",String.class);
System.out.print(method.getName() + " ");
System.out.println();Object obj = clazz.newInstance();
method.setAccessible(true);
String result = (String) method.invoke(obj, "inputParams");
System.out.println(result);

输出结果

getStudentId setStudentId StudentPublicMethod getName setName PersonPublicMethod wait wait wait equals toString hashCode getClass notify notifyAll
StudentPublicMethod
getStudentId setStudentId StudentPublicMethod StudentPrivateMethod
StudentPrivateMethod
inputParams

​ 上面我们基本可以实现通过类名创建对象、通过方法名执行方法。类名和方法名都是字符串,我们可以把它们放到一个配置文件中,根据配置文件来执行方法,这样就有点类似基于XML的Spring了。

2.描述字段Field

描述字段Field方法的使用和描述方法Method中方法的使用有点类似,也是4个获取字段的方法(getFields、getField、getDeclaredFields、getDeclaredField)。

​ ● getFields:获得某个类的所有公共(public)字段,包括父类中的字段。
​ ● getField:获取某个类public成员变量中指定变量名的字段,包括基类。
​ ● getDeclaredFields:获得某个类所有声明的字段,包括public、private和protected,但是不包括父类的声明字段。
​ ● getDeclaredField:获取某个类的所有成员变量指定变量名的字段,不包括基类。

3.描述构造器Constructor

​ 先介绍一下描述构造函数Constructor用到的方法,主要还是4个:getConstructors、getDeclaredConstructors、getConstructor、getDeclaredConstructor。和前面Method、Field用的方法进行比较,举一反三,我们也能大概了解这几个方法的使用。其实,在编程中有好多体现哲学思想的地方,有正有反,有阴有阳,学会思考,这样可以以点带面、触类旁通。

​ ● getConstructors:获取对应类中public类型的构造函数,且只获取当前类的构造函数。
​ ● getConstructor:获取对应类中public指定参数类型的构造函数,且只获取当前类的构造函数。
​ ● getDeclaredConstructors:获取对应类中所有构造函数,包括私有构造函数,且只获取当前类的构造函数。
​ ● getDeclaredConstructor:获取对应类中指定参数类型的方法,包括私有构造函数,且只获取当前类的方法。

4.描述注解Annotation

​ 描述注解主要用到getAnnotation(Class annotationClass)方法,返回该元素指定类型的注解,否则返回null。

1.3 小结

​ 我们回顾一下这一章的主要内容:在注解部分,学习了注解的定义、注解的理解、内置注解、元注解,通过自定义注解理解注解继承、多注解及注解生命周期;在反射部分,了解反射机制、Class类的理解,通过实例掌握反射中Method、Field、Constructor、Annotation相关API的使用,为后续学习Spring框架打下基础。

源码下载

[Spring 深度解析]第1章 Java基础相关推荐

  1. [Spring 深度解析]第2章 Spring基础

    第2章 ◄Spring基础► ​ 在上一章节中,我们学习了Java的注解与反射,在这一章节我们将了解一下Spring框架,并学习Spring框架中重要的编程思想控制反转(IOC).面向切面编程(AOP ...

  2. [Spring 深度解析]第7章 IoC容器的初始化过程

    7. IoC容器的初始化过程 ​ 简单来说,IoC容器的初始化是由前面介绍的refresh()方法来启动的,这个方法标志着IoC容器的正式启动.具体来说,这个启动包括BeanDefinition的Re ...

  3. [Spring 深度解析]第5章 Spring之DAO

    第5章 ◄Spring之DAO► ​ 在上一章节中,我们了解了Spring框架中的AOP模块,这一章节我们开始学习Spring框架中的DAO模块. 本章主要涉及的知识点: ​ ● JDBC基本用法:S ...

  4. [Spring 深度解析]第4章 Spring之AOP

    第4章 ◄Spring之AOP► 在上一章节中,我们大致了解了Spring核心容器,了解了IOC思想在Spring中的具体应用Bean容器以及Bean的配置与使用,这一章我们将开始学习AOP在Spri ...

  5. [Spring 深度解析]第3章 核心容器

    第3章 ◄核心容器► ​ 在上一章节中,我们大致了解了Spring框架,并学习了控制反转(IOC)和面向切面编程(AOP)两个重要的编程思想,这一章我们将开始学习Spring框架中的核心容器. 本章主 ...

  6. [Spring 深度解析]第6章 Spring的IoC容器系列

    6. Spring的IoC容器系列 ​ IoC容器为开发者管理对象之间的依赖关系提供了很多便利和基础服务.有许多IoC容器供开发者选择,SpringFramework的IoC核心就是其中一个,它是开源 ...

  7. JavaSE入门0基础笔记 第二章Java基础语法

    JavaSE入门0基础笔记 第二章Java基础语法 1.运算符 1.1算术运算符 1.1.1运算符和表达式 1.1.2 算术运算符 1.1.3字符的"+"操作 1.1.4 字符串中 ...

  8. 使用java实现面向对象编程第二章_java面向对象编程——第二章 java基础语法

    第二章 java基础语法 1.java关键字 abstract boolean break byte case catch char class const continue default do d ...

  9. 第 2 章 Java 基础

    第 2 章 Java 基础 1.58 同城 java 字符串常量池 1.1.面试题代码 代码 /*** @ClassName StringPool58Demo* @Description TODO* ...

最新文章

  1. php redis decr_对于高并发的问题你知道怎么处理吗?php接口如何处理并发问题
  2. linux终端默认密码是多少,linux基础懂多少?两个小问题考考你
  3. bladex自定义分页
  4. 怎么两边同时取ln_男生“两边铲光”发型out了?试试这4款吧,剪完清爽又帅气...
  5. 爬虫-06-通用爬虫与聚焦爬虫
  6. python 元组是什么_python元组是什么?python元组的用法介绍
  7. 存储过程 触发器 约束
  8. (转自孟岩的CSDN)理解矩阵
  9. 实习 | 京东金融个人风险管理中心(地点:北京)
  10. python学习之老男孩python全栈第九期_day012知识点总结
  11. UNIX发展历史流程图
  12. vscode 配置c++环境 万能头文件
  13. Android10手机无法拉起微信小程序解决方案
  14. 微型计算机接口技术论文,微机原理与接口技术课程论文
  15. java服务器接收coap协议数据_CoAP协议-以Java为例的Californium初步实现
  16. 进栈顺序为abcd则出栈顺序为_进栈顺序为ABCDEFG 有可能的出站顺序是什么
  17. STM32与jink连接烧录程序
  18. 深度解析javaScript常见数据类型检查校验
  19. Vistor-访问者模式
  20. SQL快速入门、查询(SqlServer)[郝斌SqlServer完整版]

热门文章

  1. Science:英国Castrillo组揭示微生物群与根内皮的协调支持植物营养平衡!
  2. 微生物组—宏基因组分析专题研讨会(线上课已开通,可以报名,线下课程时间待定)...
  3. Plant Physiol. | 中科院遗传所储成才研究组发现水稻NLR类抗病基因突变导致的白叶枯病感病机制...
  4. 一作解读:EID-2017-宏基因组测序在新发腹泻病毒鉴定中的应用
  5. python语言标号_Python 编码为什么那么蛋疼?
  6. R语言使用aov函数进行双因素方差分析(Two-way factorial ANOVA)、在双因素方差分析中,受试者被分配到由两个因素交叉分类形成的组(Two-way factorial ANOVA)
  7. pandas使用groupby函数计算dataframe数据中每个分组的N个数值的滚动最小值(rolling min)、例如,计算某公司的多个店铺每N天(5天)的滚动销售额最小值
  8. R语言parse函数、deparse函数、expression函数实现字符串和表达式的转换实战
  9. python使用imbalanced-learn的SMOTEENN方法同时进行上采样和下采样处理数据不平衡问题
  10. R使用lm构建多变量线性回归模型