1.表和表之间的关系
一对多的关系:
分类表和商品表
部门表和员工表
多对多的关系
采用中间表 学生表和课程表
2.数据库的三大范式
三大范式:呈递次规范(每一个都依赖上一次);
1Nf:第一次范式
数据库表中的每一列都是不可在拆分的原子数据
2NF:第二范式
满足第一范式的基础上
每一张表的非主键字段必须完全依赖与主键字段,不能产生部分依赖
3NF:第三范式
满足第二范式的基础上,非主键字段必须完全依赖主键字段,非主键字段不能产生传递依赖
3.多表查询
分类:
内连接查询(连接调试及室内不多得情况下,尽量采用隐式内连接)
隐式内连接
where语句作为条件
语法:
select
字段列表 1)查询哪些字段
from
表名1,表名2 2)查询哪些表
where
连接条件; 3)连接条件
举例:
查询部门表和员工表的所有信息
SELECt
*
FROM
emp, 员工表
dept 部门表
WHERe
emp.`dept_id` = dept.`id` ;
显示内连接
语法:
select
字段类别
from
表名1
inner join 表名2
on
连接条件;
举例:
查询员工表的信息,同时关联查询部门表的部门名称
SELECt
e.id '员工编号',
e.`NAME` '员工姓名',
e.`gender` '性别',
e.`join_date` '入职日期',
d.`name` '部门名称'
FROM
emp e
INNER JOIN dept d -- inner 可以省略不写
ON
e.`dept_id` = d.`id` ;
外连接查询
左外连接
可以将表名1中所有的数据全部查询以及他们的交集的数
语法:
select
字段列表
from 表名1
left outer(outer可以省略) join 表名2
on
连接条件;
举例:查询所有员工的信息以及他们的部门名称
SELECt
e.*, -- 显示所有员工信息
d.`name` '部门名称'
FROM
emp e -- 将emp表数据全部查询
LEFT JOIN -- 中间outer省略
dept d
ON
e.`dept_id` = d.`id` ;
右外连接
将右表中所有的数据全部查询以及他们的交集的数
语法:
select
字段列表
from 表名1
right outer join 表名2
on
连接条件;
子查询
情况1:单行单列
利用where条件后面可以携带比较运算符<=,>=,<,>,赋值运算符=
举例:查询工资最高的员工的信息
select max(salary) from emp;
情况2:多行多列
查询数据的时候,利用or或者in语句,
举例:查询在2号或者3号部门的员工的信息
SELECt
emp.`id` '员工编号',
emp.`NAME` '员工姓名',
emp.`gender` '性别',
emp.`salary` '工资',
emp.`join_date` '入职日期',
emp.`dept_id` '部门编号'
FROM
emp
WHERe dept_id IN
(SELECt
id
FROM
dept
WHERe NAME = '市场部'
OR NAME = '财务部') ;
子查询之复杂查询
举例:查询入职日期大于'2019-03-14'号的员工信息以及所在部门的信息
SELECt
e.`id` '员工编号',
e.`NAME` '员工姓名',
e.`gender` '性别',
e.`salary` '工资',
e.`join_date` '入职日期',
d.`name` '部门名称'
FROM
emp e , -- 员工表
dept d -- 部门表
WHERe
e.`join_date` > '2019-03-14'
AND
e.`dept_id` = d.`id` ;
视图
创建视图:
create view 视图名 as select语句;
举例:
CREATE VIEW
myview -- 一张虚表:名称 myview
AS
SELECT id,NAME,gender,age FROM stu_test
WHERe id < 4 ;
修改或者添加/删除的时候,可能会造成基本的数据被更改,
添加视图的检查语言:当前虚拟表中不满足条件的时候,直接报错
修改视图:
alter view 视图名字 as select 语句 条件查询 with check option;
有得情况不能修改视图
1.当基本表使用聚合函数的时候
2.视图中不能使用系统或者用户变量的
3.临时表中不能创建视图
4.数据库的事务
事务的概念:
在执行某个业务功能中,当这个业务功能他能同时执行多个sql语句的时候,将这多个sql语句的执行过程看成一个整体,要么同时执行成功,要么同时执行失败.
举例:转账
START TRANSACTION ;开启事务
UPDATE account SET balance = balance - 500 WHERe id = 1 ;
UPDATE account SET balance = balance + 500 WHERe id = 2 ;
如果没有问题就提交事务
commit 提交事务
如果出现了问题,直接回滚
rollback;
事务出现的本身,就是为了解决业务出现的不正常的数据,特点:
ACID:传统型事务
-- 原子性:事务是一个不可再拆分的,是一个整体;
-- 一致性:事务在操作业务数据的前后,总量保持不变的
-- 隔离性:事务和事务之间,不能互相影响
-- 持久性:对数据的修改,一旦提交commit,对数据的操作都是永久性的.
5.反射
定义:
jvm在加载类的时候,引导类加载器--校验java代码的而语法,如果语法存在问题,编译报错,没有问题--执行代码.
宗旨:
要想获取类的或者接口的字节码文件对象
获取构造器对象Constructor,创建对象
获取成员变量所在的对象Field,给成员变量赋值
获取成员方法所在的对象Method,调用成员方法并去使用它;
java代码的三个阶段
source:源码阶段
class:编译--类加载阶段
runtime:运行阶段
三种获取字节码文件对象的方法
public class RelectDemo {
public static void main(String[] args) throws ClassNotFoundException {
//创建两个Person对象
// Person p = new Person() ;
// Person p2 = new Person() ;
// System.out.println(p);
//System.out.println(p2);
// System.out.println(p == p2) ;
System.out.println("--------------------------------") ;
//如何获取类的字节码文件对象
//方式1:Object类的getClass() 获取
//public final Class> getClass()
System.out.println("--------------------------------") ;
//方式2:任意Java类型(自定义类型/sun公司提供任意的类)的class属性
Class personClazz = Person.class ;
System.out.println(personClazz);//class com.qf.reflect_01.Person
// System.out.println(personClazz == p2.getClass());
System.out.println("--------------------------------") ;
//方式3:反射类中字节码文件对象 Class 里面静态方法,返回值是当前类本身 ,参数为String
//public static Class> forName(String className) throws ClassNotFoundException :使用比较多的功能,因为参数为String,可以作为配置文件的内容
// Class c = Class.forName("Person");//参数必须是类的全限定名称:包名.类名
Class c = Class.forName("com.qf.reflect_01.Person");//参数必须是类的全限定名称:包名.类名
System.out.println(c); //class com.qf.reflect_01.Person
System.out.println(c==personClazz);
// System.out.println(c==p2.getClass());
}
}
5.1通过反射获取类的构造器对象Constructor
public class ReflectDemo2 {
public static void main(String[] args) throws Exception {
//没有使用反射
Person p = new Person();
System.out.println(p);
System.out.println("----------------------------------");
//使用反射--获取构造方法所在的Constructor对象 ---->创建类的实例
//1)获取当前类的字节码文件对象 class xx_01.Person
Class c = Class.forName("com.qf.reflect_01.Person");
//System.out.println(c);
//2)通过Class类中:获取这个类所有的公共的构造方法
//public Constructor>[] getConstructors()
//public Constructor>[] getDeclaredConstructors():获取这个类中所有的构造方法,包括私有,或者受保护的
// Constructor[] constructors = c.getConstructors();
// }
//获取单个的公共的构造方法所在的Constructor
//public Constructor getConstructor(Class>... parameterTypes)
//参数:参数类型的class---->就是类型的字节码文件对象 :可变参数,可能有参数类型
Constructor con = c.getConstructor();
//通过Constructor 创建当前类实例 public T newInstance(Object... initargs) :参数:实际参数 值
Object obj = con.newInstance();
System.out.println(obj);
System.out.println("-------------------------------------");
// Person p2 = new Person("高") ;
//反射的创建创建带一个参数的类的实例
// Person(String name){
// this.name = name ;
// }
//public Constructor getDeclaredConstructor(Classs>... parameterTypes):获取单个的指定的构造方法,参数为参数类型的class
Constructor constructor = c.getDeclaredConstructor(String.class);//参数类型的字节码文件对象
System.out.println(constructor);
//当前获取的构造方法Constructor:是一个默认的并不是公共的,所以要使用取消Java语言访问检查
//public void setAccessible(boolean flag)
constructor.setAccessible(true);//参数为true 取消Java语言访问检查
//创建当前类的实例(Person)
Object myObject = constructor.newInstance("高") ;
System.out.println(myObject) ;
System.out.println("------------------------------------") ;
// 之前写法
Person p2 = new Person("高",42) ;
System.out.println(p2);
System.out.println("-----------------------------------------------------");
//通过反射获取public Person(String name,int age){} 创建Person类实例
//获取Person类的字节码文件对象
Class personClazz = Class.forName("com.qf.reflect_01.Person") ;
//获取构造器方法所在的Constructor对象
Constructor personCon = personClazz.getConstructor(String.class,int.class) ;
//创建当前Person类的实例
Object personObj = personCon.newInstance("赵", 45);
System.out.println(personObj) ;
System.out.println("---------------------------------------------") ;
//使用反射之前
// Person p3 = new Person("高",42,"西安市鄠邑区") ;
//private Person(String name,int age,String address){} 创建Person类实例
//获取Person类的字节码文件对象
Class personClazz2 = Class.forName("com.qf.reflect_01.Person") ;
//获取构造方法所在的Constructor对象:指定的构造方法
Constructor personCon2 =
personClazz2.getDeclaredConstructor(String.class, int.class, String.class);
//取消java语言访问检查public void setAccessible(boolean flag)
personCon2.setAccessible(true) ;
//创建当前类实例
Object personObj2 = personCon2.newInstance("李", 39, "北京市朝阳区");
System.out.println(personObj2) ;
System.out.println("---------------------------------------------") ;
//另外一种方式:创建当前类实例
//只要能够获取当前类的字节码文件对象Class ---- 直接newInstance
//public T newInstance()
Class myClazz = Class.forName("com.qf.reflect_01.Person");
Object personObj3 = myClazz.newInstance(); //默认执行的这个了无参构造方法,公共的
System.out.println(personObj3);
}
}
5.2通过反射获取成员变量的类对象,并且进行赋值
public class ReflectDemo3 {
public static void main(String[] args) throws Exception {
//之前的给成员变量赋值
//现在要使用反射的方式
//1)获取当前类的字节码文件对象
Class personClazz = Class.forName("com.qf.reflect_01.Person") ;
//2)通过反射获取成员变量所在的Field类对象
//public Field[] getFields():获取所有的公共的字段的类对象
// Field[] fields = personClazz.getFields();
//public Field[] getDeclaredFields():获取所有的字段(成员变量)的类对象,包括默认,受保护,私有
//2)获取公共的无参构造方法所在的Constructor
Constructor con = personClazz.getConstructor();
Object obj = con.newInstance(); //Person类对象
System.out.println(obj) ;
//3)现在获取指定的默认的name所在的字段类对象Field
//public Field getDeclaredField(String name): 参数为指定的属性名称
Field nameField = personClazz.getDeclaredField("name") ;
//name字段:在person类中是默认的修饰符:
nameField.setAccessible(true); //取消Java语言访问检查
//Field
//public void set(Object obj, Object value) :将参数值绑定在当前类的实例
//给成员变量赋值
nameField.set(obj,"高") ;
System.out.println(obj) ;
// private int age ; //年龄
System.out.println("--------------------------------------") ;
Field ageField = personClazz.getDeclaredField("age");
//取消Java语言访问检查
ageField.setAccessible(true) ;
//赋值
ageField.set(obj,42) ;
System.out.println(obj) ;
// public String address ; //地址
//通过反射的方式赋值--获取成员变量类对象赋值Field
System.out.println("---------------------------------") ;
Field addressField = personClazz.getField("address");
//直接赋值
addressField.set(obj,"西安市鄠邑区") ;
System.out.println(obj) ;
}
}
5.3通过反射获取成员方法所在的类的对象Mthod,并且用方法调用
public class ReflectDemo {
public static void main(String[] args) throws Exception {
//之前的写法
//创建Person类对象
Person p = new Person() ;
p.show();
System.out.println("--------------------------------------") ;
//现在反射来操作
//1)获取Person类的字节码文件对象
Class pClazz = Class.forName("com.qf.reflect_01.Person") ;
// 获取所有的公共的成员方法的类对象Method
//public Method[] getMethods(): 获取当前类的公共的成员方法,以及包括继承的类的公共方法
//Method[] methods = pClazz.getMethods();
//public Method[] getDeclaredMethods():获取所有的成员方法类对象,只包含自己的,不包括继承的
//通过构造器创建当前Person类对象
Object obj = pClazz.getConstructor().newInstance();
//2)获取公共的show方法所在的类对象并且调用这个方法
//public Method getMethod(String name,Class>... parameterTypes)
//参数1:成员方法的方法名
//参数2:成员方法携带参数类型的class
Method showMethod = pClazz.getMethod("show") ;
//底层方法调用Method里面调用机制
//public Object invoke(Object obj,Object... args):调用当前这个方法,并且方法的实际参数绑定在指定的 当前类实例上
//返回值:如果当前方法本身void,单独调用即可,如果有返回值,直接返回Object:代表任何Java类对象
showMethod.invoke(obj);
System.out.println("-----------------------------------") ;
//调用这个方法
// private String function2(String s){
// return s ;
// }
//获取指定的私有的成员方法所在类对象Method
//public 方法 getDeclaredMethod(String name,Class>... parameterTypes)
//参数1:成员方法的方法名
// //参数2:成员方法携带参数类型的class
Method function2Method = pClazz.getDeclaredMethod("function2", String.class) ;
//私有方法,取消Java语言访问检查
function2Method.setAccessible(true) ;
//调用
//public Object invoke(Object obj,Object... args):
Object returnObj = function2Method.invoke(obj, "hello,高圆圆"); //相当于Object obj = new String() ;
System.out.println(returnObj) ;
// void method(String str){
// System.out.println(str);
// }
System.out.println("-----------------------------") ;
Method mMethod = pClazz.getDeclaredMethod("method", String.class);
//取消java语言访问检查
mMethod.setAccessible(true) ;
//调用
mMethod.invoke(obj,"jdbc") ;
//反射方式调用
System.out.println("-----------------------------") ;
Method functionMethod = pClazz.getDeclaredMethod("function");
functionMethod.setAccessible(true) ;
Object returnObj2 = functionMethod.invoke(obj);
System.out.println(returnObj2) ;
}
}
5.4动态代理
目的:使用代理角色帮助真实角色完成事情,程序中,就是对业务功能进行增强
动态代理---是在程序中过程中,通过反射获取代理对象!
* jdk动态代理:基于接口
*
* 核心类:Proxy
* java.lang.reflect.Proxy:反射代理
* 静态功能:
* public static Object newProxyInstance(ClassLoader loader,
*
* Class>[] interfaces,
* InvocationHandler h)
* 参数1:当前基于接口的类加载
* 参数2:实现接口的列表的Class
* 参数3:基于代理的处理程序的接口
举例
public class UserTest {
public static void main(String[] args) {
//针对用户模块实现增删查改的功能
UserDao ud = new UserDaoImpl() ; //真实角色
ud.add() ;
ud.delete() ;
ud.update() ;
ud.find() ;
System.out.println("--------------------------------------------") ;
//实际开发中,添加/删除/修改/查询业务功能----都会存在系统监控:校验权限
UserDao ud2 = new UserDaoImpl2() ;
ud2.add();
ud2.delete() ;
ud2.update() ;
ud2.find() ;
System.out.println("---------------------------------------------------------") ;
//使用jdk的动态代理实现:代理对象的创建
//对ud产生代理对象 :ud真实角色
//创建基于代理的处理的接口对象
MyInvocationHandler handler = new MyInvocationHandler(ud) ;
//
UserDao udProxy = (UserDao) Proxy.newProxyInstance(
ud.getClass().getClassLoader(),
ud.getClass().getInterfaces(),
handler
);
udProxy.add() ;
udProxy.delete() ;
udProxy.update() ;
udProxy.find();
}
}
public interface UserDao {
void add() ;
void delete() ;
void update() ;
void find() ;
}
public class MyInvocationHandler implements InvocationHandler {
//代理的角色可以是任意Java类对象
private Object target ; //目标对象
public MyInvocationHandler(Object target){//Object target = new XXX() ;多态
this.target = target ;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("权限校验") ;
Object obj = method.invoke(target, args);
System.out.println("产生日志记录");
return obj; //代理对象
}
}
public class UserDaoImpl implements UserDao {
@Override
public void add() {
System.out.println("用户添加功能");
}
@Override
public void delete() {
System.out.println("删除功能");
}
@Override
public void update() {
System.out.println("修改功能");
}
@Override
public void find() {
System.out.println("查询功能");
}
}
6.JDBC
定义:java连接数据库
七大步骤:
//1)导入mysql驱动jar包
//2)注册驱动
Class.forName("com.mysql.cj.jdbc.Driver") ;
//3)获取数据库的连接对象
Connection conn = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/库名?characterEncoding=utf8&....",
"root",
"123456"
) ;
//4)准备sql语句
String sql = "update 表名 set 字段名称 = 值 where 条件" ;//查询语句
//5)获取执行对象
Statement stmt = conn.createStatement() ;
//6)执行sql语句
int count = stmt.executeUpdate(sql) ;//executeQuery(sql语句)--->ResultSet
//7)释放资源
//结果集对象.close() ;
执行对象.close() ;
连接对象.close() ;
举例:DDL/DML语句操作,增,删,改
public class JDBCDemo2 {
public static void main(String[] args) {
Connection connection = null ;
Statement stmt = null ;
try {
//原生的7个步骤
//注册驱动
Class.forName("com.mysql.cj.jdbc.Driver") ;
//获取数据库的连接对象
connection = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/javaee_2110?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true",
"root",
"123456"
);
//sql语句
String sql = "CREATE TABLE account(" +
" id INT PRIMARY KEY AUTO_INCREMENT, " +
" NAME VARCHAr(10), " +
" money INT " +
") " ;
//获取执行对象:通过Connection
stmt = connection.createStatement();
//执行sql:通过executeUpdate(String sql)
int count = stmt.executeUpdate(sql);
System.out.println("创建成功"+count) ;
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} finally {
//释放资源
if(stmt!=null){
try {
stmt.close() ;
} catch (SQLException e) {
e.printStackTrace();
}
}
if(connection!=null){
try {
connection.close() ;
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
执行DQL语句,查询
public class JdbcExecuteQueryDemo {
public static void main(String[] args) {
Connection connection = null ;
Statement stmt = null ;
ResultSet rs = null ;
//注册驱动
try {
Class.forName("com.mysql.cj.jdbc.Driver") ;
//获取连接对象
connection = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/javaee_2110?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true",
"root",
"123456"
) ;
//sql
//查询账户表所有数据
String sql = "select * from account" ;
//获取执行对象
stmt = connection.createStatement() ;
//执行查询操作
// ResultSet executeQuery(String sql):将
rs = stmt.executeQuery(sql) ;
System.out.println("账户idt账户名称t账户金额");
while(rs.next()){
int id = rs.getInt(1) ;
String name = rs.getString(2);
int money = rs.getInt(3);
System.out.println(id+"tt"+name+"tt"+money) ;
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}finally {
if(rs!=null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(stmt!=null){
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(connection!=null){
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
例题: 查询javaee_2110数据库中student的所有数据,将每一条学生数据封装到Student学生类中
*将所有的数据结果,封装List集合中,最终将结果遍历出来!
public class JdbcTest {
public static void main(String[] args) {
//调用一个方法---->结果List
List list = findAllStudent() ;
for(Student student:list){
System.out.println(student);
}
}
//获取所有的学生数据,将学生数据都封装List中
public static List findAllStudent() {
Connection connection = null ;
Statement stmt = null ;
ResultSet rs = null ;
try {
//获取连接对象
connection = JdbcUtils.getConnection();
//sql
String sql = "select * from student";
//获取执行对
stmt = connection.createStatement() ;
//获取结果集对象
rs = stmt.executeQuery(sql) ;
//创建ArrayList
ArrayList arrayList = new ArrayList<>() ;
//声明Student类型的变量
Student student = null ;
//遍历结果
while(rs.next()){
//封装学生数据
student = new Student() ;
//通过索引值/列的标签名称获取内容
int id = rs.getInt("id");
String name = rs.getString("name");
int age = rs.getInt("age");
String sex = rs.getString("sex");
String address = rs.getString("address");
int math = rs.getInt("math");
int english = rs.getInt("english");
student.setId(id) ;
student.setName(name); ;
student.setAge(age); ;
student.setSex(sex);
student.setAddress(address);
student.setMath(math);
student.setEnglish(english);
//添加集合中
arrayList.add(student) ;
}
return arrayList ;
} catch (SQLException e) {
e.printStackTrace();
}
return null ;
}
}
预编译:
PreparedStatement prepareStatement(String sql):获取预编译对象,同时将参数化的sql发送给数据库
* 通过预编译的sql语句对象,它的内存中就可以对参数?(占位符)进行赋值
public class PreparedStatementDemo {
public static void main(String[] args) {
Connection conn = null ;
PreparedStatement stmt = null ;
try {
//获取连接对象
conn = JdbcUtils.getConnection();
//现在要javaee_2110的student表中插入数据
//参数化的sql
// String sql = "insert into student(name,age,sex,address,math,english) values(?,?,?,?,?,?)" ;
String sql = "update student set id = ? where name = ?" ;
//PreparedStatement prepareStatement(String sql):获取预编译对象,同时将参数化的sql发送给数据库
stmt = conn.prepareStatement(sql);
//预编译对象内存中存储了这些字段名称,需要它赋值
//void setXXX(int parameterIndex,XXX x)
//参数1:就是第几个占位符号(占位符的索引值,从1开始)
//参数2:赋的实际参数---根据字段类型
//参数赋值
stmt.setInt(1,10) ;
stmt.setString(2,"陈天宇");
//在执行之前,需要进行参数赋值
//int executeUpdate()
//ResultSet executeQuery()
int count = stmt.executeUpdate();
System.out.println("影响了"+count+"行");
} catch (Exception e) {
e.printStackTrace();
}finally {
JdbcUtils.close(stmt,conn);
}
}
}
面试题: PreparedStatement和Statement 区别
* 前者:预编译对象,执行都是参数化的sql
* 写的sql的参数都是占位符号?,可以有效防止sql注入(不存在字符串拼接)
* sql就执行一次,可以发送不同的参数进行赋值,执行sql效率相对大于Statement
* 后者:普通的执行对象,每次指定的都是静态的sql,存在硬编码,而且存在字符串拼接,
* 可以造成sql注入,不安全
* 执行sql效率低,每次executeUpdate/Query(String sql)都要发送sql
6.1注解
注解不是注释,注释--->针对代码解释说明的文字,普通注释不能被解析,除非文档注释
*注解:---标记类,方法,参数,成员变量,由一种特殊的含义 (能被解析的)
*
* 注解的本质就是接口interface---->前面加入了@标记
* 注解中的属性----->(就是接口中的方法名)
* 注解的分类
* jdk内置注解
* @Overrid:标记当前这个方法是否为重写方法: 重写了类/抽象类或者接口
* @Deprecated:标记某个方法是过时方法
* @SuppressWarnings:压制警告, 一般企业中, 项目部署上线的时候,代码中不能黄色警告线!
* @FunctionalInteface:函数式接口 :jdk8后,
* 接口如果一个抽象方法,那么这个接口就可以是函数式接口----使用lambda表达式
* 内置注解底层依赖的元注解
* @Target(ElementType.METHOD)
* 标记当前这个注解所作用的范围
* 属性:
* ElementType[] values()--->返回值枚举的数组--->里面一些常量
* TYPE,当前注解可以作用在类上
* FIELD,当前这个注解可以作用成员属性上
METHOD, ...当前这个注解可以作用在方法上
* @Retention(RetentionPolicy.SOURCE) :
* 当做标记@Override注解的保留的阶段
* RetentionPolicy value();
*
* 返回值枚举类型
* SOURCE :原码编译阶段
* CLASS: 类 的加载
* RUNTIME:运行阶段
* 注解中的属性(方法名)---返回值类型 (5种 类型!)
* String
* * 枚举类型Enum
* * 注解类型@Annotation
* * 基本类型
* * 以上类型的数组