【C++】---类和对象(中)默认成员函数 和 操作符重载

前言:

假如一个类中既没有成员变量也没有成员函数,那么这个类就是空类,空类并不是什么都没有,因为所有类都会生成如下6个默认成员函数:
在这里插入图片描述

一、构造函数

1、构造函数的定义及其特性

对于日期类对象,我们可能会忘记调用Init函数进行初始化,C++为了解决这个问题,引入构造函数进行初始化。

#include<iostream>
using namespace std;

class Date
{
private:
	int _year;
	int _month;
	int _day;
public:
	void Init(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
		cout << _year << "/" << _month << "/" << _day << endl;
	}
};

int main()
{
	Date d1;
	d1.Init(2024, 2, 11);
	d1.Print();
	return 0;
}

构造函数一个特殊的成员函数名字与类名相同,创建类类型对象时由编译器自动调用,以保证每个数据成员都有 一个合适的初始值,并且在对象整个生命周期内只调用一次
特性:
1.构造函数是特殊的成员函数,需要注意的是,构造函数虽然名称叫构造,但是构造函数的主要任务并不是开空间创建对象,而是初始化对象。
2.其特征如下:

  • 函数名与类名相同。
  • 无返回值。
  • 对象实例化时编译器自动调用对应的构造函数。
  • 构造函数可以重载
  • 如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦用户显式定义了编译器将不再生成!

3.默认构造函数是我们不传参就可以调用的函数

  • 我们什么都没写,编译器自动生成的
  • 我们自己写的:无参的构造函数
  • 我们自己写的:全缺省构造函数
    这三类只能存在一个,注意后两个:不能同时存在的原因:当定义一个不带参数的类对象时,编译器不能确定到底要调用我们写的无参默认构造函数还是要调用我们写的带参全缺省默认构造函数,会报“对重载函数的调用不明确错误”。
    (1) 我们什么都没写,编译器自动生成的
#include<iostream>
using namespace std;
 
class Date
{
private:
	int _year;
	int _month;
	int _day;
};
 
int main()
{
	Date d1;//调用编译器自动生成的默认构造函数
 
	return 0;
}

(2)我们自己写的:无参的构造函数

#include<iostream>
using namespace std;
 
class Date
{
public:
    
	//1.无参默认构造函数:初始化对象
	Date()
	{
		_year = 2024;
		_month = 2;
		_day = 12;
	}
	
private:
	int _year;
	int _month;
	int _day;
};
 
int main()
{
	Date d1;
 
	return 0;
}

(3)我们自己写的:全缺省构造函数

#include<iostream>
using namespace std;
 
class Date
{
public:
    //2.带参全缺省默认构造函数:初始化对象
	Date(int year = 2024, int month = 2, int day= 12)
	{
		_year = year;
		_month = month;
		_day = day;
	}
 
private:
	int _year;
	int _month;
	int _day;
};
 
int main()
{
	Date d1;//调用带参默认构造函数
 
	return 0;
}

2、编译器自动生成的默认构造函数

关于编译器生成的默认成员函数,很多童鞋会有疑惑:不实现构造函数的情况下,编译器会生成默认的构造函数。但是看起来默认构造函数又没什么用?d对象调用了编译器生成的默认构造函数,但是d对象_year/_month/_day,依旧是随机值。也就说在这里编译器生成的默认构造函数并没有什么用??
解答:C++把类型分成内置类型(基本类型)和自定义类型。内置类型就是语言提供的数据类型,如:int/char…,自定义类型就是我们使用class/struct/union等自己定义的类型,
编译器生成默认的构造函数对内置类型不做处理
而对自定义类型成员会调用的它的默认成员函数

看看下面的程序,就会发现编译器生成默认的构造函数会对自定类型成员_t调用的它的默认成员函数:

class Time
{
public:
 Time()
 {
 cout << "Time()" << endl;
 _hour = 0;
 _minute = 0;
 _second = 0;
 }
private:
 int _hour;
 int _minute;
 int _second;
};
class Date
{
private:
 // 基本类型(内置类型)
 int _year;
 int _month;
 int _day;
 // 自定义类型
 Time _t;
};
int main()
{
 Date d;
 return 0;
}

二、析构函数

1、析构函数的定义及其特性

析构函数用来完成类的资源清理工作,编译器在销毁对象时,会自动调用析构函数。
特性:

(1)析构函数名是在类名前加上字符 ~。

(2)无参数无返回值。(析构函数不能重载,一个类有且仅有一个析构函数)

(3)一个类有且只有一个析构函数。若未显式定义,系统会自动生成默认的析构函数

(4)对象生命周期结束时,C++编译系统系统自动调用析构函数。

2、多对象的析构顺序

假如这个类有多个对象,那么析构的先后顺序是什么?
多对象的析构顺序 :局部对象(后定义先析构) -> 局部的静态(后定义先析构) ->全局对象(后定义先析构)

// 局部对象(后定义先析构) -> 局部的静态(后定义先析构) ->全局对象(后定义先析构)
class Date
{
private:
	int _year;
	int _month;
	int _day;
public:
	Date(int year)
	{
		_year = year;
	}
	~Date()
	{
		// 调用一次 析构函数 我就打印一次
		cout << "~Date()->" << _year << endl;
	}
};

void func()
{
	// 局部域
	Date d4(4);
	static Date d5(5);
	static Date d10(10);
}

// 全局域
Date d6(6);
static Date d7(7);
Date d8(8);
static Date d9(9);

int main()
{
	// 局部域
	Date d1(1);
	Date d2(2);
	static Date d3(3);
	func();
	return 0;
}

3、编译器自动生成的默认析构函数

当不写析构函数时,编译器会自动生成默认的析构函数,不过这个默认的析构函数什么也不做,不需要清理资源。那么编译器自动生成的默认析构函数到底有什么用呢?

同析构函数

(1)对于内置类型,不会处理

(2)对于自定义类型,会调用它的析构函数

三、拷贝构造函数

1、拷贝构造函数定义及特性

1.拷贝构造函数只有单个形参,该形参是对同类类型对象的引用(一般常用const修饰),在用已存在的类类型对象创建新对象时由编译器自动调用。
2.特征:
拷贝构造函数也是特殊的成员函数,其特征如下:

  • 拷贝构造函数是构造函数的一个重载形式。
  • 拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错因为会引发无穷递归调用

思考:为什么使用传值方式会引发无穷递归调用?
C++规定:对自定义类型的函数传值传参时,都会调用拷贝构造函数!!!
在这里插入图片描述

#define _CRT_SECURE_NO_WARNINGS 1 
#include <stdio.h>
#include <iostream>

using namespace std;

class Date
{
private:
	int _year;
	int _month;
	int _day;
public:
	void Print()
	{
		cout << _year << "/" << _month << "/" << _day << endl;
	}
	// 构造函数
	Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	//拷贝构造函数
	//Date(Date d)// 错误写法 使用传值方式编译器直接报错,因为会引发无穷递归调用
	Date(const Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
	// 析构函数
	~Date()
	{

	}
};

int main()
{
	Date d1(2024,2,12);
	Date d2(d1);
	d1.Print();
	d2.Print();
	return 0;
}

拷贝构造函数也是构造函数,函数名和类型名相同,参数是同类型对象的引用,由编译器自动调用。
因此,对于自定义类型的对象,一般推荐使用传引用传参,虽然传值传参也可以,但是要调用拷贝构造

(1)对其拷贝构造函数的理解

#include <iostream>
using namespace std;

class Date
{
private:
	int _year;
	int _month;
	int _day;
public:
	void Print()
	{
		cout << _year << "/" << _month << "/" << _day << endl;
	}

	Date(int year = 2024, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	// 析构函数
	~Date()
	{
		cout << "~Date()" << endl;//在析构函数内打印,调用一次就打印一次
	}
	//拷贝构造函数
	Date(const Date& d)  // Date (Date* this ,Date &d)
		// 因为d2调用拷贝构造函数,所以&d2=this, (d是d1的别名)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}


};

int main()
{
	Date d1(2024,2,16);// 调用构造函数

	Date d2(d1);// 调用拷贝构造函数,就是在定义对象的时候直接用拷贝构造函数
	// Date(&d2,Date& d1)

	d2.Print();

	return 0;
}

在这里插入图片描述

2、深浅拷贝

1.传值传参:是浅拷贝
浅拷贝的缺点:
两个数组的指针指向同一块空间,当进行free时只能 free一个,那么另外一个就会变成“野指针”!
动态开辟资源的,浅拷贝都不行,因为浅拷贝只会仅仅copy数据。
在这里插入图片描述
2.传引用传参:是深拷贝:深拷贝还需要我们自己去写,编译器不会自动生成。
在这里插入图片描述
在这里插入图片描述
在判断一个类里面需不要写拷贝构造函数,根据具体情况而定。如果没有动态开辟的程序,就不需要写深拷贝。

3、编译器自动生成的拷贝构造函数

若未显式定义,系统会生成默认拷贝构造函数。 同构造函数和析构函数不同:

(1)拷贝构造函数对内置类型依次按照字节序完成拷贝,即浅拷贝或值拷贝。

假如不写拷贝构造函数,照样正常打印:

#define _CRT_SECURE_NO_WARNINGS 1 
#include <stdio.h>
#include <iostream>

using namespace std;

class Date
{
private:
	int _year;
	int _month;
	int _day;
public:
	void Print()
	{
		cout << _year << "/" << _month << "/" << _day << endl;
	}
	// 构造函数
	Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	拷贝构造函数
	Date(Date d)// 错误写法 使用传值方式编译器直接报错,因为会引发无穷递归调用
	//Date(const Date& d)
	//{
	//	_year = d._year;
	//	_month = d._month;
	//	_day = d._day;
	//}
	// 析构函数
	~Date()
	{

	}
};

int main()
{
	Date d1(2024,2,12);
	Date d2(d1);
	d1.Print();
	d2.Print();
	return 0;
}

在这里插入图片描述

四、赋值运算符重载函数

1、运算符重载

1.定义:对于内置类型来说,语言层面本身就支持已经定义好的运算符,但对于自定义类型来说不行, C++中规定运算符重载的原因是:让自定义类型可以像内置类型一样使用运算符,想重载哪个运算符就重载哪个运算符。
运算符重载的语法:
函数原型:返回值类型 operator操作符(参数列表)
2.特性:

  1. 不能通过连接其他符号来创建新的操作符:比如operator@
  2. 重载操作符必须有一个类类型参数
  3. 用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不 能改变其含义
  4. 作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this指针
  5. (.*) (::) (sizeof) (?;) (.) 注意以上5个运算符不能重载。这个经常在笔试选择题中出现。

运算符重载实例:

(1)==运算符重载

#include<iostream>
using namespace std;
 
class Date
{
public:
	//构造函数
	Date(int year = 2024, int month = 2, int day = 16)
	{
		_year = year;
		_month = month;
		_day = day;
	}
 
//成员变量公有
public:
	int _year;
	int _month;
	int _day;
};
 
//operator==运算符重载
bool operator==(Date x1, Date x2)
{
	return x1._year == x2._year
		&& x1._month == x2._month
		&& x1._day == x2._day;
}
 
int main()
{
	Date d1(2024, 2, 15);
	Date d2(2024, 2, 16);
 
    //两种调用方式:
	//1.可读性不强
    operator==(d1, d2);
 
    //2.当编译器看到==自定义类型,会去检查日期类有没有==的重载运算符,如果有重载会转换成operator==(d1, d2)去调用operator==函数
	d1 == d2;
 
	return 0;
}

2、运算符重载和函数重载的区别:

运算符重载和函数重载,虽然都使用了重载,但是两者之间没有关联:

(1)函数重载时支持定义同名函数

(2)运算符重载是为了让自定义类型可以像内置类型一样去使用运算符。

3、赋值运算符重载

1.赋值运算符重载语法格式:

  • 参数类型:const T&,传递引用可以提高传参效率
  • 返回值类型:T&,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值
  • 检测是否自己给自己赋值
  • 返回*this :要复合连续赋值的含义

赋值运算符重载和拷贝构造的区别:

(1)拷贝构造函数:对即将要创建的新对象进行初始化(不过初始化的内容是将一个已经存在的对象拷贝给他)

(2)赋值运算符重载:两个已经存在的对象进行赋值

2.赋值运算符重载代码 以及如何调用!

class Date
{
private:
	int _year;
	int _month;
	int _day;
public:
	void Print()
	{
		cout << _year << "/" << _month << "/" << _day << endl;
	}

	Date(int year = 2024, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	// 析构函数
	~Date()
	{
		cout << "~Date()" << endl;//在析构函数内打印,调用一次就打印一次
	}
	//拷贝构造函数
	Date(const Date& d)  // Date (Date* this ,Date &d)
		// 因为d2调用拷贝构造函数,所以&d2=this, (d是d1的别名)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
	 //赋值运算符重载
	// d1=d2   //d1.operator=(&d1,d2)
	Date& operator=(const Date& d) // void Date& operator=(&d1,const Date& d)
	{
		if (this != &d) // 对d取地址,判断this的值和d的地址是否相同,如果不是自己给自己赋值,才需要拷
		{
			_year = d._year;
			_month = d._month;
			_day = d._day;
		}

		return *this;
	}

};

int main()
{
	//Date d1(2024,2,16);// 调用构造函数

	//Date d2(d1);// 调用拷贝构造函数,就是在定义对象的时候直接用拷贝构造函数
	 Date(&d2,Date& d1)

	//d2.Print();

	Date d1(2024, 2, 16);
	d1.Print();
	Date d2;
	d2.Print();

	d2 = d1;

	d2.Print();
	return 0;
}

在这里插入图片描述

4、const修饰类的成员函数

定义:将const修饰的类成员函数称为const成员函数,const修饰类成员函数,实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改。

比如有如下场景:假如把Date类的operator==运算符重载函数写错了

bool operator==(const Date& d)
{
	return (_year == d._year)
		&& (_month == d._month)
		&& (_day == d._day);
}

将其中的一个"==“错写成”=" :

bool operator==(const Date& d) //bool operator==(Date* this,const Date& d)
{
	return (_year == d._year)
		&& (_month = d._month)
		&& (_day == d._day);
}

虽然编译没有问题,但是这会导致this的值被修改了,并且执行结果也错误:

int main()
{
	Date d1(2024, 2, 16);
	Date d2(2024, 3, 16);
 
	cout << (d1 == d2) << endl;
	d1.Print();
	d2.Print();
 
	return 0;
}

这不符合要求,仅仅是比较而已,但是被比较对象的值却被修改了。const最大的作用是保护对象和变量,d2传给了d,d是d2的别名,const已经保护了d,那d1如何保护呢?由于this是隐含的,那么const为了保护this,应该如何加?把const加在成员函数的后面,叫做const修饰成员函数

bool operator==(const Date& d) const
{
	return (_year == d._year)
		&& (_month = d._month)
		&& (_day == d._day);
}

总结:
1.const引用:我是你的别名,但我不能修改你(是之前:权限的缩小)
比如:const int& c = a; // c是a的别名,但由于const修饰c所以说不能通过c来修a的值。
2.如果有const的修饰,那么说明这个变量是只能读的!如果没有const修饰,普通的变量它是可读可写的!
在这里插入图片描述

五、总结:

1.构造函数和析构函数
如果我们不写,编译器对内置类型不做处理,自定义类型会调用它的构造函数和析构函数进行处理

2.拷贝构造和赋值运算符重载
如果我们不写,内置类型会完成浅拷贝,自定义类型会调用它的拷贝构造函数和赋值运算符重载函数。

3.取地址操作符重载和const取地址操作符重载
一般不需要重载,编译器默认生成的已经够用,重载没有价值。


好了,今天的分享就到这里了
如果对你有帮助,记得点赞👍+关注哦!
我的主页还有其他文章,欢迎学习指点。关注我,让我们一起学习,一起成长吧!
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/389461.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

pytest教程-10-allue2生成html报告

领取资料&#xff0c;咨询答疑&#xff0c;请➕wei: June__Go 上一小节我们学习了pytest-html生成html报告的方法&#xff0c;本小节我们讲解一下使用allue2生成html报告。 自动化测试执行完成后我们需要展示给其他人看&#xff0c;这就要有自动化测试报告了。复杂的测试报告…

稳态准直太阳光模拟器

概述 稳态准直太阳光模拟器是一种用于模拟太阳光的设备。它能够产生高强度、高稳定性的太阳光&#xff0c;用于太阳能电池、太阳能热利用等领域的研究和实验。 稳态准直太阳光模拟器通常由以下几个部分组成&#xff1a; 光源&#xff1a;采用强度和颜色温度与太阳光接近的光源…

Rust 语言学习杂谈 (end) (各种工作中遇到的疑难杂症)

1.在运行 “cargo build --release” 的时候&#xff0c;到底发生了什么&#xff1f; 源 (GPT4.0) : 当我们运行 cargo build --release 命令时&#xff0c;实际上在进行一系列复杂的步骤来编译和构建 Rust 项目的发布版本。这个过程大致可以分解为以下几个步骤&#xff1a;…

2024年【危险化学品经营单位安全管理人员】考试报名及危险化学品经营单位安全管理人员考试资料

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 危险化学品经营单位安全管理人员考试报名是安全生产模拟考试一点通总题库中生成的一套危险化学品经营单位安全管理人员考试资料&#xff0c;安全生产模拟考试一点通上危险化学品经营单位安全管理人员作业手机同步练习…

基于GPT一键完成数据分析全流程的AI Agent: Streamline Analyst

大型语言模型&#xff08;LLM&#xff09;的兴起不仅为获取知识和解决问题开辟了新的可能性&#xff0c;而且催生了一些新型智能系统&#xff0c;例如旨在辅助用户完成特定任务的AI Copilot以及旨在自动化和自主执行复杂任务的AI Agent&#xff0c;使得编程、创作等任务变得高效…

单片机学习笔记---直流电机驱动(PWM)

直流电机介绍 直流电机是一种将电能转换为机械能的装置。一般的直流电机有两个电极&#xff0c;当电极正接时&#xff0c;电机正转&#xff0c;当电极反接时&#xff0c;电机反转 直流电机主要由永磁体&#xff08;定子&#xff09;、线圈&#xff08;转子&#xff09;和换向器…

正向代理和反向代理

文章目录 概述正向代理反向代理主要用途 概述 正向代理和反向代理都是网络中常见的代理类型&#xff0c;用于在客户端和服务器之间进行通信。 正向代理&#xff08;Forward Proxy&#xff09;是位于客户端和目标服务器之间的代理服务器。当客户端发送请求时&#xff0c;请求会…

批量梯度下降、随机梯度下降、小批量梯度下降

一、批量梯度下降&#xff08;Batch Gradient Descent,BGD&#xff09; 在批量梯度下降中&#xff0c;每次迭代都使用整个训练集的数据进行梯度计算和参数更新。也就是说&#xff0c;每次迭代都对所有的样本求取梯度&#xff0c;然后更新参数。由于要处理整个训练集&#xff0c…

算法沉淀——优先级队列(堆)(leetcode真题剖析)

算法沉淀——优先级队列 01.最后一块石头的重量02.数据流中的第 K 大元素03.前K个高频单词04.数据流的中位数 优先队列&#xff08;Priority Queue&#xff09;是一种抽象数据类型&#xff0c;它类似于队列&#xff08;Queue&#xff09;&#xff0c;但是每个元素都有一个关联的…

WEB APIs(2)

应用定时器可以写一个定时轮播图&#xff0c;如下 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport&qu…

如何在电脑和 SD 卡上恢复已删除 MOV等视频文件

MOV 是 Apple 创建的多媒体容器。您可能已经意识到&#xff0c;用 macOS QuickTime Player 录制的视频是以 MOV 格式保存的&#xff0c;而且 MOV 在 Windows 上也兼容。我们可能已经保存了很多 MOV 格式的视频。但是&#xff0c;如果这些 MOV 文件丢失或被意外删除怎么办&#…

Python二级考试笔记

Python二级考试笔记【源源老师】 01. 字符串 1. 常规功能合集 字符串本身有一些功能&#xff0c;有些之前运用过&#xff0c;这里总结如下&#xff1a; # 功能一&#xff1a;判断字符串类型 print(type("Hello")) print(str(123)) # 转换# 功能二&#xff1a;连…

OpenCV Mat实例详解 一

OpenCV中的Mat是一个类&#xff0c;它用存储图像信息。由两部分数据组成&#xff1a;矩阵头和像素值矩阵。矩阵头包含矩阵尺寸、存储方法、存储地址等信息&#xff0c;而像素值矩阵则存储实际的像素值数据。 Mat类在OpenCV中有十分重要的作用&#xff0c;图像信息的载入、保存、…

【知识图谱--第三讲知识图谱的存储与查询】

知识图谱的存储与查询 基于关系型数据库的知识图谱存储基于原生图数据库的知识图谱存储原生图数据库实现原理浅析 基于关系型数据库的知识图谱存储 基于原生图数据库的知识图谱存储 原生图数据库实现原理浅析

每日一题——LeetCode1437.是否所有1都至少相隔k个元素

方法一 两次遍历&#xff1a; 第一次遍历保存所有1的位置到res里&#xff0c;第二次遍历res检查是否所有相邻元素之间间隔都>k var kLengthApart function(nums, k) {let res[]for(let i0;i<nums.length;i){if(nums[i]1){res.push(i)}}for(let i1;i<res.length;i){…

Python 中实现线性搜索算法

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站零基础入门的AI学习网站~。 前言 线性搜索算法&#xff0c;也称为顺序搜索算法&#xff0c;是一种简单但常用的搜索技术&#xff0c;用于查…

VMwareWorkstation17.0虚拟机安装Windows2.03完整详细步骤图文教程

VMwareWorkstation17.0虚拟机安装Windows2.03完整详细步骤图文教程 第一篇 下载Windows2.03第二篇 配置Windows2.03虚拟机机器环境第三篇 启动Windows2.03系统 第一篇 下载Windows2.03 1.Windows2.0原版软盘下载地址是 暂不提供&#xff0c;后续更新 2.Windows2.03虚拟机镜像下…

P1228 地毯填补问题题解

题目 相传在一个古老的阿拉伯国家里&#xff0c;有一座宫殿。宫殿里有个四四方方的格子迷宫&#xff0c;国王选择驸马的方法非常特殊&#xff0c;也非常简单&#xff1a;公主就站在其中一个方格子上&#xff0c;只要谁能用地毯将除公主站立的地方外的所有地方盖上&#xff0c;…

MATLAB知识点:poissrnd函数(★★☆☆☆)生成泊松分布的随机数

讲解视频&#xff1a;可以在bilibili搜索《MATLAB教程新手入门篇——数学建模清风主讲》。​ MATLAB教程新手入门篇&#xff08;数学建模清风主讲&#xff0c;适合零基础同学观看&#xff09;_哔哩哔哩_bilibili 节选自第3章&#xff1a;课后习题讲解中拓展的函数 在讲解第三…

开源PDF工具 Apache PDFBox 认识及使用(知识点+案例)

文章目录 前言源码获取一、认识PDFBox二、导入依赖三、基础功能demo1&#xff1a;读取pdf所有内容demo2&#xff1a;读取所有页内容&#xff08;分页&#xff09;demo3&#xff1a;添加页眉、页脚demo4&#xff1a;添加居中45文字水印demo5&#xff1a;添加图片到右上角 参考文…