数据库2:表与表的关系、数据库的三大范式、多表查询、mysql的事务、JDBC、数据库连接池、Apache开源工具类库、java注解类

– 数据库的备份和还原
– 两种方式:
– 图形界面话:简单直观
– 选择数据库—右键----备份---->选择导出的路径(结构以及数据都保存)
– 将之前存在库,然后选择 执行指定sql脚本----> 选择指定的sql脚本—进行执行即可!
– 命令行方式
– 备份:mysqldump -uroot -p密码 备份数据库名称 > 保存的本地地址
– 还原:
– 将原来删除,新建库,使用库, source 将保存的本地地址

1.表与表的关系

一对一:是一种特例 (应用场景不多) 举例: 人和身份证 ;一个人对应一张身份证

一对多的关系,多对多的关系 员工表和部门表 一部门可以有多个员工,一个员工属于某一个部门的(员工表中有一个外键)

2.数据库的三大范式

1NF :数据库表的每一列都是不可分割的原子数据项不能出现复合项

2NF:第二范式就是在第一范式的基础上所有列(所有字段)完全 依赖于主键列(主键字段)。

3NF:在第二范式2NF基础上,非主键字段不能传递依赖于主键(外键解决)

3.多表查询

即在某个sql语句中查询多张表

用于测试多表查询的表

CREATE DATABASE ee_2106;

-- 部门表
CREATE TABLE dept(
 id INT PRIMARY KEY AUTO_INCREMENT, -- 部门编号
  NAME VARCHAR(20)  -- 部门名称
  
 );
 INSERT INTO dept (NAME) VALUES ('开发部'),('市场部'),('财务部');

-- 员工表
CREATE TABLE emp (
 id INT PRIMARY KEY AUTO_INCREMENT,
  NAME VARCHAR(10), 
  gender CHAR(1), -- 性别 
  salary DOUBLE, -- 工资 
  join_date DATE, -- 入职日期 
  dept_id INT, 
  FOREIGN KEY 
(dept_id)
 REFERENCES dept(id) -- 外键,关联部门表(部门表的主键)
  );
INSERT INTO emp(NAME,gender,salary,join_date,dept_id)VALUES('孙悟空','男',7200,'2013-02-24',1); 
 INSERT INTO emp(NAME,gender,salary,join_date,dept_id)VALUES('猪八戒','男',3600,'2010-12-02',2);
INSERT INTO emp(NAME,gender,salary,join_date,dept_id)VALUES('唐僧','男',9000,'2008-08-08',2);
 INSERT INTO emp(NAME,gender,salary,join_date,dept_id) VALUES('白骨精','女',5000,'2015-10-07',3);
   INSERT INTO emp(NAME,gender,salary,join_date,dept_id)  VALUES('蜘蛛精','女',4500,'2011-03-14',1);
3.1内连接

隐式内连接:通过where语句作为连接条件来查询(按照外键关联关系)

操作步骤:

1)查询那张表

2)要查询的是指定表中的哪个字段

3)表和表之间的关联关系问题

-- 需求:查询员工表和部门表的所有信息

SELECT
	*
FROM 
     emp ,  dept ;
-- 查询多张表:员工表5条记录,部门表有3条记录  (A*B=15)
-- 出现迪尔卡乘积的原因:没有连接条件
-- 通过外键解决字段冗余问题 员工表的部门dept_id关联与部门表的主键id

-- 多表查询之 内连接查询
-- 隐式内连接-----通过where 语句作为连接条件来查询(按照外键关联关系)
-- 操作步骤:
-- 1)查询哪张表
-- 2)要查询的是指定表中的哪个字段
-- 3)表和表之间的关联关系问题

-- 1)员工表和部门表
-- 2)查询员工表的中name,gender,salary以及部门表的部门名称
-- 3)连接条件:dept_id= 部门表的id
SELECT
	e.name  '员工姓名' ,	
	e.`gender` '性别',
	e.`salary` '工资',
	e.`dept_id` ,
	d.id ,
	d.`name` '部门名称'	
FROM	
	emp  e,dept d -- 员工表的别名e  ,部门表的别名d

WHERE 
	e.`dept_id` = d.`id` ;
	
-- 内连接之显示内连接 
--  (实际开发中,多去使用where条件查询,查询速度快 ,sql优化的一种方式)
-- select <字段列表> 
-- from 表名1 
-- (inner) join 
--  表名2  
-- on 关联关系(连接条件);

SELECT 
	t1.*,-- 员工表中的所有字段 (实际开发中,* 不建议使用!)
	t2.`name` -- 部门表的部门名称
FROM
	emp t1
-- inner  可以省略,不写
JOIN  dept t2

ON 
	t1.`dept_id` = t2.`id` ; 
	
	
-- 给员工表插入一条数据
INSERT INTO emp(NAME,gender,salary,join_date) 
VALUES('高圆圆','女',12000,'2021-8-14'); 


-- 要查询所有的员工表信息的同时关联查询部门表信息(部门名称即可)
-- 即使员工没有部门信息,也得查询出来...
SELECT 
     t1.`name` '员工名称',
     t1.`gender` '性别',
     t1.`salary` '工资',
     t1.`join_date` '入职日期',
     t2.`name` '部门名称'
FROM 
     emp t1
INNER JOIN dept t2
ON
      t1.`dept_id` = t2.`id` ; -- 交集的条件

通过内连接查询存在问题 :只是查出了交集部分的数据,没有部门编号的员工并没有查询出来,此时内连接就不能使用了使用 多表查询之外连接查询.

3.2外连接

左外连接 left (outer) join: 将两种表中左表的数据全部进行查询并且以及他们交集部分的数据全部查询(通用)

右外连接 right (outer) join

-- 语法格式 
-- select
 -- 字段列表
 -- from
 -- 表名1 -- 左表
 -- left outer join 表名2 
 -- on 连接条件 ;
SELECT 
	t1.`name` '姓名',
	t1.`gender` '性别',
	t1.`salary` '工资',
	t1.`join_date` '入职日期',
	t1.`dept_id` ,
	t2.`name` '部门名称'


FROM
	emp  t1 -- 左表(见图中的A表)
LEFT 
-- outer
 JOIN   
  dept t2   -- (图中的B表)
 ON 
    t1.`dept_id` = t2.`id` ; -- 交集部分数据
 

SELECT 
	*
FROM
    dept t1
RIGHT OUTER JOIN  emp t2

ON
    t2.`dept_id` = t1.`id` ;
3.3子查询

也叫嵌套查询

-- 情况1:单行单列的情况:通用使用运算符进行操作
-- 需求:查询最高工资的员工信息
-- 1)查询最高工资是多少 : 聚合函数
/*
select
	max(salary)
from 
	emp ;   -- 12000
	*/
-- 2) 查询等于最高工资的员工信息
/*
select
	emp.`name`,
	emp.`salary`,
	emp.`gender`,
	emp.`join_date`
from
	emp 
where 
	salary = 12000 ;
	*/
	
-- 一步走: 将12000的sql语句代入过来即可
SELECT
	emp.`id`,
	emp.`name`,
	emp.`salary`,
	emp.`gender`,
	emp.`join_date`
FROM
	emp 
WHERE 
	salary = (SELECT MAX(salary) FROM  emp ) ;
	
-- 查询工资小于平均工资的员工信息
-- 1)查询平均工资是多少
/*
select 
	avg (salary)
	 from emp ;
*/
-- 2)查询员工工资小于	6883.333....的员工信息
8
SELECT  
	*
FROM 
	emp
WHERE 
	salary < (SELECT AVG (salary) FROM emp ) ;



-- 子查询的第二种情况 :多行多列的情况 ---使用关键字in(集合数据)
-- 需求:查询 在财务部或者市场部的员工信息
-- 分步骤
-- 1)查部门表中财务部和市场部的id是多少
/*
select
	id 
from
	dept 
where 
	name = '市场部' or name = '财务部' ;-- 2 , 3
*/	

-- 2)在员工表中查 id为 财务部 (3) 或者市场部(2)的员工信息 

SELECT
	t.`name` '姓名',
	t.`gender` '性别',
	t.`salary` '工资',
	t.`join_date` '入职日期',
	t.`dept_id` '部门编号'
FROM
	emp t
WHERE 
     t.`dept_id` = 2 OR t.`dept_id` = 3 ;
     
 -- 优化  步骤2)sql语句 or---->in(集合数据) 
 -- 在xxx数据内  /not in     exists /not exists /any(sql)
 SELECT
	t.`name` '姓名',
	t.`gender` '性别',
	t.`salary` '工资',
	t.`join_date` '入职日期',
	t.`dept_id` '部门编号'
FROM
	emp t
WHERE 
      t.`dept_id` IN(
		SELECT
			id 
		FROM
			dept 
		WHERE 
		NAME = '市场部' 
		OR 
		NAME = '财务部') ; 
		

-- 子查询的情况3:
-- 使用select语句查询的结果作为一个虚表,使用这个虚表然后继续和其他表进行查询

-- 查询入职日期大于'2011-3-14号的员工信息以及所在的部门信息

-- 入职日期 join_date > '2011-03-14' 将查询作为虚表 和部门表继续进行查询
	SELECT   * FROM emp WHERE join_date > '2011-03-14';
-- 可以使用左外连接
SELECT
	t2.*, -- 员工的所有信息
	t1.`name` '部门名称'	
FROM
	dept t1 -- 部门表
LEFT OUTER JOIN 
	(SELECT   * FROM emp WHERE join_date > '2011-03-14') t2
ON
	t1.`id` = t2.`dept_id` ;
3.4多表查询

多表查询:查询哪个表/哪个字段/连接条件

4.mysql的事务

在业务操作过程中,一次性可能同时操作多个sql语句,防止操作多个sql语句时候出现问题,将这整个业务操作看成一个整体,进行处理这些多个sql要么一次性全部执行成功,要么同时失败!

例如在转账的时候,收钱和扣钱同时完成才算完成.

-- 将转账业务操作看成整体,开启事务
START TRANSACTION ;  -- 开启手动提交事务(默认自动提交)

-- 执行业务 操作
UPDATE account SET balance = balance - 500 WHERE NAME = 'zhangsan' ;	

-- 中间出问题了

UPDATE account SET balance = balance + 500  WHERE NAME = 'lisi' ;

-- 事务回滚:回滚到在操作语句之前的状态 (余额都是1000)
ROLLBACK ;

-- 如果转账中没问题,将手动事务进行提交
COMMIT;
4.1事务的特点

ACID

原子性:就是事务操作业务的中sql,要么同时执行成功,要么同时失败!

一致性:操作sql语句的前后,总数量保持不变 转账之前:1000 1000 转账之后:500 1500

隔离性: 将业务使用事务管理,业务和业务之间分离,事务和事务之间不能相互影响---->事务是独立的

持久性: 如果一旦事务被提交了,是永久存储的,即使关机也还存在!

4.2隔离级别

read uncommitted ; 读未提交 安全性最差 不能有效防止脏读

read committed ;读已提交 安全相对第一个高一些,能够有效脏读,不能够防止可重复读的问题

repeatable read :可重复读 能够有效防止脏读,可重复读

serializable :串行话

-- 查看全局的隔离级别的命令
-- 查询隔离级别 select @@tx_isolation;  mysql5.5/5.7都可以 
-- SELECT @@transaction_isolation; mysql8.0

-- 设置隔离级别 set global transaction isolation level 级别字符串;
SELECT @@transaction_isolation; 

-- 隔离级别不同,会出现不同的问题
-- 脏读:一个事务读取另一个没有提交的事务(最严重的问题)
-- 不可重复读: 一般都是update语句影响,两个事务中,一个事务读取的前后的内容不一致!
-- 幻读:一般insert/delete :影响两个事务中,前后数量不一致!

5.JDBC

JDBC(Java Database Connectivity)

DBC的本质就是能够实现sun公司提供的java.sql包下的相关的接口的实现类,数据库厂商提供这一驱动jar包

原生java连接数据库的七大步骤

1.导包 mysql-connector-java-5.1.25.jar	也叫数据库连接驱动jar包

2.注册驱动	
Class.forName(drivceClass);

3.获取数据库的连接对象
DriverManager.getConnection(url,user,password)
第一个参数 url:jdbc:mysql://localhost:3306/day32_817
+?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true(如果是Mysql8.0以后的话就加)
第二个参数 user:root
第三个参数 password:你的密码(123456)
    
4.准备静态sql语句	String sql = "sql语句"

5.通过数据库连接对象创建执行对象Statement
conn.createStatement(sql)
    
6.执行更新或查询操作
stat.executorQuery()	stat.executorUpdate()
    
7.释放资源
conn.close()
stat.close()    
5.1JDBC封装工具类

可以降低程序代码的冗余度,通过工具类进行获取连接对象和关闭资源

//properties配置文件
url=jdbc:mysql://localhost:3306/day32_817
user=root
password=1871659329
jdbcDriver=com.mysql.jdbc.Driver	

//工具类
package jdbcutils;

import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;

public class JdbcUtils {
    private static String url = null ;
    private static String user = null ;
    private static String password = null ;
    private static String drivceClass = null ;

    static {
        try {
            Properties prop = new Properties();
            InputStream in = JdbcUtils.class.getClassLoader().getResourceAsStream("jdbc.properties");
            prop.load(in);

            url = prop.getProperty("url");
            user = prop.getProperty("user");
            password = prop.getProperty("password");
            drivceClass = prop.getProperty("jdbcDriver");

            Class.forName(drivceClass);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    // 获取连接类对象
    public static Connection getConnection(){
        Connection conn = null;

        try {
            conn = DriverManager.getConnection(url,user,password);

        } catch (SQLException e) {
            e.printStackTrace();
        }
        return conn;
    }

    // 关闭资源
    public static void close(Statement stmt,Connection conn){
        close(null,stmt,conn);
    }

    public  static void close(ResultSet rs, Statement stmt, Connection conn){
        if(rs!=null){
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

        if(stmt !=null){
            try {
                stmt.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

        if(conn!=null){
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {

        Connection connection = JdbcUtils.getConnection();
        System.out.println(connection);

    }
}

6.数据库连接池

是一种容器(集合),存储数据库连接对象;防止用户操作数据库的时候频繁的创建连接对象,而且当
数据库连接Connection使用完毕,就得对资源进行释放close().这样做是很消耗内存空间的.

而数据库连接池是固定的可重用的连接对象的容器.

连接池技术:

c3p0

dbcp

druid(德鲁伊) 阿里开源项目 推荐使用

连接池本质就是需要实现sun公司提供的DataSource实现类(物理数据源代替 DriverManager)

步骤

1)导包 druid-1.1.10.jar DruidDataSource 实现类

2)准备好数据库连接池的配置文件

3)读取配置文件

如何创建DataSource接口对象?\

DruidDataSource-------> 此时会使用工厂模式 DruidDataSourceFactory工厂类提供静态功能:创建数据源DruidDataSourcepublic static DataSource createDataSource(Properties properties) throws Exception

6.1使用数据库连接池的工具类
// peoperties配置文件
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/day32_817
username=root
password=1871659329
#
initialSize=5
#
maxActive=10
#
maxWait=3000

//工具类
package jdbcutils;

import com.alibaba.druid.pool.DruidDataSourceFactory;

import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

/**
 * 工具类---->DataSource----->获取数据库的连接对象 Connection以及后期管理事务
 *
 * 获取连接对象----静态方法
 * 关闭资源-----静态方法
 */
public class DruidDbutils {

    //成员变量位置
    private  static DataSource ds ;
    //为了保证线程安全:每一线程使用自己的Connection (张三/李四)
    private static ThreadLocal<Connection>  t1 = new ThreadLocal<>() ; //提供线程的局部变量保存连接对象
    //通过工具类获取conn连接对象后,如果不释放,那么同一代码块当中获取到的连接对象是同一个
	
    //构造方法私有化
    private DruidDbutils(){}

    //静态代码块
    static{
        try {
            //读取数据库连接池的配置文件----->通过DruidDataSourceFactory工厂类创建DataSource
            //创建一个属性集合列表
            Properties prop = new Properties() ;
            //读取druid.properties
            InputStream inputStream = DruidDbutils.class.getClassLoader().getResourceAsStream("druid.properties");

            //将资源文件所在的输入流加载列表中
            prop.load(inputStream);
            //底层子实现类:DruidDataSource
            ds = DruidDataSourceFactory.createDataSource(prop);
            //System.out.println("数据源获取成功");

        }catch (Exception e) {
            e.printStackTrace();
        }
    }
    //提供静态方法:单独获取数据源
    public static DataSource getDataSource(){
        return ds ;
    }


    //定义事务代码
    //定义开启事务
    public static void startAffairs(){
        Connection conn = DruidDbutils.getConnection();
        try {
            conn.setAutoCommit(false);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    //定于回滚事务
    public static void rollBack(){
        Connection conn = DruidDbutils.getConnection();
        try {
            conn.rollback();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    //定义提交的方法
    public static void jdbcCommit(){
        Connection conn = DruidDbutils.getConnection();
        try {
            conn.commit();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    //获取连接对象Connection静态功能
    public static Connection getConnection(){
        //从ThreadLocal中获取局部变量的副本:Connection
        /**
         *   public T get()  :从线程中获取局部变量的副本!
         */
        Connection conn =  null ;
        try {
            conn  =  t1.get();
            if(conn==null){
                //如果空,需要从数据库的连接池中获取连接对象
                conn  = ds.getConnection();
                //获取到之后,每一线程执行自己的Connection
                //将获取到的连接对象 绑定到当前线程中
                t1.set(conn);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        //如果不为空,说明ThreadLocal线程中已经存在Connection
        return conn ;
    }

    //关闭(释放资源)资源
    public static void close(ResultSet rs, Statement stmt,Connection conn)  {
        if(rs!=null){
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(stmt!=null){
            try {
                stmt.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(conn!=null){
            try {
                conn.close();
                //关闭之后,归还到连接池中,需要从当前线程中解绑
                t1.remove();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    public static void close( Statement stmt,Connection conn)  {
        close(null,stmt,conn);
    }

    public static void main(String[] args) {
        // DataSource ds = DruidJdbcUtils.getDataSource();
        //System.out.println(ds);


        Connection connection = DruidDbutils.getConnection();
        System.out.println(connection);
    }
}

7.Apache开源工具类库

apache提供开源工具类库 commons-dbutils :通用jdbc工具

将查询的结果或者更新操作进行封装 (PreparedStatement—>executeUpdate()/executeQuery()进行了封装)

1)导包 核心工具包

commons-dbutils-1.7.jar 依赖包 commons-logging:支持相关的

commons-collections:工具类中涉及其他的api

2)获取数据库的连接对象Connection

3)获取执行对象:通过物理数据源DataSource:java.sql.DataSource(sun公司提供一个接口)---->替代了DriverManager QueryRunner

//基本使用
public class DbUtilsDemo {
    public static void main(String[] args) throws SQLException {

        //1)创建执行对象QueryRunner  (执行对象)
        // public QueryRunner(DataSource ds) { //数据源
        //        super(ds);
        //    }
        //属于自动提交
        QueryRunner queryRunner = new QueryRunner(DruidJdbcUtils.getDataSource()) ;


        //需要给ee_2106的admin表 添加一条数据
        //插入数据
        String sql = "insert into admin(username,gender,age,address,phone) values(?,?,?,?,?)" ;
        //执行通用方法:update()DML语句 ,query()DQL

       // updateS(String sql,Object...parames):参数1sql 参数2:可变参数,可以使用多个实际参数

        int count = queryRunner.update(sql, "王宝强", "男", 20, "西安市", "13566662222");
        System.out.println(count);

        //不需要关闭资源

    }
}

//实现dao接口的使用案例
package com.qf.dao.impl;

import com.qf.dao.AdminDao;
import com.qf.pojo.Admin;
import com.qf.utils.DruidJdbcUtils;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;

import java.sql.SQLException;
import java.util.List;

/**
 * @author Kuke
 * @date 2021/8/19
 * 针对Admin的数据库访问接口实现类
 */
public class AdminDaoImpl implements AdminDao {


    @Override
    public List<Admin> findAll() {
        try {
            //创建执行对象
            QueryRunner qr = new QueryRunner(DruidJdbcUtils.getDataSource()) ;

            //准备sql语句
            String sql = "select *  from admin" ;
            //执行查询
            //query(String sql,ResuletSetHandler handler)
            //参数1:sql
            //参数2:结果集的处理
            //子类:BeanListHandler<T> 将查询的多条记录封装到List集合中
            //List<当前指定的javeBean实体>
            // BeanListHandler<将查询的结果封装实体类型>(当前实体的类Class)

            List<Admin> list = qr.query(sql, new BeanListHandler<Admin>(Admin.class));
            return list ;
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }


    //通过id查询Admin实体
    @Override
    public Admin findAdminById(int id) {
        try {
            //创建执行对象
            QueryRunner qr = new QueryRunner(DruidJdbcUtils.getDataSource()) ;
            //sql
            String sql = "select * from admin where id = ?" ;
            //执行查询
            //query(String sql,ResultSetHandler handler,Object...params)
            //将查询的某一条记录封装到实体类(JavaBean)中,使用到类BeanHandler<T>
            Admin admin = qr.query(sql, new BeanHandler<Admin>(Admin.class), id);
            return admin;
        } catch (SQLException e) {
            e.printStackTrace();
        }

        return null;
    }

    //查询总记录数
    @Override
    public long selectTotalCount() {
        //QueryRunner执行对象
        try {
            QueryRunner qr = new QueryRunner(DruidJdbcUtils.getDataSource()) ;
            //sql
            String sql = "select count(id) from admin" ;
            //查询一些单行单列的数据(总记录数),使用单类ScalerHandler<>
            long count = (Long)qr.query(sql, new ScalarHandler<>());
            return count ;
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return 0;
    }


    //修改Admin

    @Override
    public int updateAdmin(Admin selectAdmin) {
        try {
            //QueryRunner
            QueryRunner qr = new QueryRunner(DruidJdbcUtils.getDataSource()) ;
            //sql
            String sql = "update admin set username = ? ,gender =?,age = ?,address = ? ,phone =? where id = ?" ;
            //更新操作
            int count = qr.update(sql,
                    selectAdmin.getUsername(),
                    selectAdmin.getGender(),
                    selectAdmin.getAge(),
                    selectAdmin.getAddress(),
                    selectAdmin.getPhone(),
                    selectAdmin.getId());
            System.out.println(count);
            return count ;
        } catch (SQLException e) {
            e.printStackTrace();
        }

        return 0;
    }
}

8.java注解类

Java中针对 类的编译,加载,运行时提供一种特殊的代码标记,可以被解析,通过反射实现.

注解的本质就是一个接口

普通注解:
  @Override:一般方法中使用,标记这个方法是否是重写方法
  @SupressWarning:压制警告 (项目部署上线的时候,项目中不能出现黄色警告线)
  @Deprecated:标记方法已经过时
  @FunctionalInterface:标记某个接口是否为函数式接口接口中有且仅有一个抽象方法
 
元注解
  @Target:当前这个注解能够使用的范围
       ElementType[] value(); value属性("抽象方法名") 返回值枚举数组类型
       ElementType 枚举中的都是常量字段 (等价于 public static final ....)
                   TYPE,能够使用在类上或者接口上
                   FIELD:能够使用在成员变量上
                   METHOD:能够使用在成员方法上
 @Retention:标记当前这个某个注解保留的阶段
           RetentionPolicy value(); value属性 它的返回值枚举类
           RetentionPolicy 
                   SOURCE      原码编译阶段
                   CLASS       类的加载,反射阶段
                   RUNTIME     类的运行阶段
                   
 注解(本质接口)中的方法名----- 称为"属性"
               属性中可以是什么样的数据类型呢?
                  基本数据类型
                  String类型
                  枚举类型
                  注解类型
                  以上类型的数组格式
 
              如果某个注解中有且只有一个属性,value
                      public @interface XXX{
 
                         String value() ;
                     }
 
                     在别的一些类上使用这个注解的时候,那么赋值value属性可以不写
  @XXX(value=("hello"))
  class Demo{}

       自定义注解
               1)自定义一个注解 @Annotation
               2)在加上一些元注解,标记当前你自己的定义这个的@Target:作用的位置
  @Retention:保留的阶段(Runtime,source,class)
               3)如何解析自定义注解
                  反射的方式
                  public <A extends Annotation> A getAnnotation(Class<A> annotationClass)
                  参数:表示当前注解类型的Class
8.1自定义注解

为了满足程序的开闭原则(对代码关闭,对功能扩展开放),原本可以使用配置文件的方式实现,如今可以通过注解的方式也可以实现

例如

//工人类
package annotation;

public class Worker {
    public void love(){
        System.out.println("爱生活,爱高圆圆");
    }
}

//学生类
package annotation;

public class Student {
    public void love(){
        System.out.println("爱学习,爱Java");
    }
}

//自定义注解类
package annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    String className();
    String methodName();
}

//测试类
package annotation;

import java.lang.reflect.Method;

//通过修好className的内容就可以实现调用不同类的方法,而无需修改代码
@MyAnnotation(className = "annotation.Worker",methodName = "love")
public class Test {
    public static void main(String[] args) throws Exception {
        Class testClass = Test.class;
        MyAnnotation annotation = (MyAnnotation) testClass.getAnnotation(MyAnnotation.class);
        String className = annotation.className();
        String methodName = annotation.methodName();

        Class clazz = Class.forName(className);
        Object o = clazz.newInstance();
        Method method = clazz.getMethod(methodName);
        Object invoke = method.invoke(o);

    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值