• 第八节:类和对象【二】【this引用和包】


    目录

    🥇1. this引用

    📘1.1 为什么要有this引用

    📒1.2 什么是this引用

    📗1.3 this引用的特性

    🎒2. 对象的构造及初始化

    📕2.1 如何初始化对象

    📙2.2 构造方法

    📒2.3 默认初始化

    📁3. 封装

    📘3.1 封装的概念

    📗3.2 访问限定符

    📕3.3 封装扩展之包


     

    🥇1. this引用

    📘1.1 为什么要有this引用

    ➡️先看一个日期类的例子:

    1. public class DateUtil {
    2. public int year;
    3. public int mouth;
    4. public int day;
    5. public void setDate(int y, int m, int d) {
    6. year = y;
    7. mouth = m;
    8. day = d;
    9. }
    10. public void show() {
    11. System.out.println("年: "+year+" 月: "+mouth+" 日: "+day);
    12. }
    13. public static void main(String[] args) {
    14. DateUtil dateUtil = new DateUtil();
    15. dateUtil.setDate(2022,11,7);
    16. dateUtil.show();
    17. DateUtil dateUtil2 = new DateUtil();
    18. dateUtil2.setDate(2022,11,7);
    19. dateUtil2.show();
    20. DateUtil dateUtil3 = new DateUtil();
    21. dateUtil3.setDate(2022,11,7);
    22. dateUtil3.show();
    23. }
    24. }

    🌈以上代码定义了一个日期类,然后main方法中创建了三个对象,并通过DateUtil类中的成员方法对对象进行设置和打印,代码整体逻辑非常简单,没有任何问题。

    🙈但是细思之下有以下两个疑问:

    1️⃣形参名不小心与成员变量名相同

    1. public void setDate(int year, int month, int day){
    2. year = year;
    3. month = month;
    4. day = day;
    5. }

    🙈那函数体中到底是谁给谁赋值?成员变量给成员变量?参数给参数?参数给成员变量?成员变量参数?估计自己都搞不清楚了。

    2️⃣11三个对象都在调用setDate和printDate函数,但是这两个函数中没有任何有关对象的说明,setDate和show函数如何知道打印的是那个对象的数据呢?

    ✨一切让this引用来揭开这层神秘的面纱。

    📒1.2 什么是this引用

    this引用指向当前对象(成员方法运行时调用该成员方法的对象),在成员方法中所有成员变量的操作,都是通过该引用去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编译器自动完成。

    1. public class DateUtil {
    2. public int year;
    3. public int month;
    4. public int day;
    5. public void setDate(int year, int month, int day){
    6. this.year = year;
    7. this.month = month;
    8. this.day = day;
    9. }
    10. public void show(){
    11. System.out.println(this.year + "/" + this.month + "/" + this.day);
    12. }
    13. }

    ❗❗❗注意:this引用的是调用成员方法的对象

    1. public static void main(String[] args) {
    2. DateUtil dateUtil = new DateUtil();
    3. dateUtil.setDate(2022,11,7);
    4. dateUtil.show();
    5. DateUtil dateUtil2 = new DateUtil();
    6. dateUtil2.setDate(2022,11,7);
    7. dateUtil2.show();
    8. DateUtil dateUtil3 = new DateUtil();
    9. dateUtil3.setDate(2022,11,7);
    10. dateUtil3.show();
    11. }

    f52ccd8734cc4a8992f1b2d0da6f3b64.png

    📗1.3 this引用的特性

    1️⃣this的类型:对应类类型引用,即哪个对象调用就是哪个对象的引用类型

    2️⃣this只能在"成员方法"中使用❗❗❗

    3️⃣在"成员方法"中,this只能引用当前对象,不能再引用其他对象

    4️⃣this是“成员方法”第一个隐藏的参数,编译器会自动传递,在成员方法执行时,编译器会负责将调用成员方法对象的引用传递给该成员方法,this负责来接收

    ✨在代码层面来简单演示--->注意:下图右侧中的Date类也是可以通过编译的

    7c636fd0f34646ee94908bbc8470d653.png

    🎒2. 对象的构造及初始化

    📕2.1 如何初始化对象

    📜通过前面知识点的学习知道,在Java方法内部定义一个局部变量时,必须要初始化,否则会编译失败。

    1. public static void main(String[] args) {
    2. int a;
    3. System.out.println(a);
    4. }
    5. // Error:(26, 28) java: 可能尚未初始化变量a

    ✅要让上述代码通过编译,非常简单,只需在正式使用a之前,给a设置一个初始值即可。如果是对象:

    1. public static void main(String[] args) {
    2. DateUtil dateUtil = new DateUtil();
    3. dateUtil.setDate(2022,11,7);
    4. dateUtil.show();
    5. DateUtil dateUtil2 = new DateUtil();
    6. dateUtil2.setDate(2022,11,7);
    7. dateUtil2.show();
    8. DateUtil dateUtil3 = new DateUtil();
    9. dateUtil3.setDate(2022,11,7);
    10. dateUtil3.show();
    11. }
    12. // 代码可以正常通过编译

    🔺需要调用之前写的SetDate方法才可以将具体的日期设置到对象中。通过上述例子发现两个问题:

    1️⃣每次对象创建好后调用SetDate方法设置具体日期,比较麻烦,那对象该如何初始化?

    2️⃣局部变量必须要初始化才能使用,为什么字段声明之后没有给值依然可以使用?

    📙2.2 构造方法

    2.2.1 概念

    ✨构造方法(也称为构造器)是一个特殊的成员方法,名字必须与类名相同,在创建对象时,由编译器自动调用,并且在整个对象的生命周期内只调用一次

    1. public class DateUtil {
    2. //成员变量: 定义在方法外部 类的内部的变量
    3. public int year;
    4. public int month;
    5. public int day;
    6. // 构造方法:
    7. // 名字与类名相同,没有返回值类型,设置为void也不行
    8. // 一般情况下使用public修饰
    9. // 在创建对象时由编译器自动调用,并且在对象的生命周期内只调用一次
    10. public DateUtil(int year,int month) {
    11. //this();
    12. this.year = year;
    13. this.month = month;
    14. System.out.println("调用了带有个参数的构造方法!");
    15. }
    16. public void show() {
    17. int a = 10;
    18. System.out.println("年:"+this.year+" 月:"+this.month+" 日:"+this.day);
    19. }
    20. public static void main2(String[] args) {
    21. // 此处创建了一个DateUtil类型的对象,并没有显式调用构造方法
    22. DateUtil dateUtil = new DateUtil(2022,11,7);// 输出DateUtil(int,int,int)方法被调用了
    23. dateUtil.show();
    24. }
    25. }

    ❗❗❗注意:构造方法的作用就是对对象中的成员进行初始化,并不负责给对象开辟空间。

    2.2.2 特性

    1️⃣ 名字必须与类名相同
    2️⃣ 没有返回值类型,设置为void也不行
    3️⃣创建对象时由编译器自动调用,并且在对象的生命周期内 只调用一次(相当于人的出生,每个人只能出生一次)
    4️⃣构造方法 可以重载(用户根据自己的需求提供不同参数的构造方法)
    1. public class DateUtil {
    2. //成员变量: 定义在方法外部 类的内部的变量
    3. public int year;
    4. public int month;
    5. public int day;
    6. public DateUtil() {
    7. System.out.println("不带参数的构造方法," +
    8. "这个方法如果没有写,Java会默认提供!");
    9. }
    10. public DateUtil(int year,int month,int day) {
    11. this.year = year;
    12. this.month = month;
    13. this.day = day;
    14. System.out.println("调用了带有3个参数的构造方法!");
    15. }
    16. public DateUtil(int year,int month) {
    17. this.year = year;
    18. this.month = month;
    19. System.out.println("调用了带有个参数的构造方法!");
    20. }
    21. public void setDate(int year,int month,int day) {
    22. this.year = year;
    23. this.month = month;
    24. this.day = day;
    25. }
    26. public void show() {
    27. System.out.println("年:"+this.year+" 月:"+this.month+" 日:"+this.day);
    28. }
    29. public static void main(String[] args) {
    30. DateUtil dateUtil = new DateUtil(2022,11,7);
    31. dateUtil.show();
    32. }

    👉上述构造方法:名字相同,参数列表不同,因此构成了方法重载

    602c30d160bc468dbb4b7dcd2788d132.png

    如果你没有写任何的构造方法,此时java会帮我们提供一个默认的,不带参数的构造方法!

    如果自己写了,就会调用你自己写的,只会被调用1次

    不管你写了啥构造方法,java都不会给你提供不带参数的构造方法了!!!

    5️⃣如果用户没有显式定义,编译器会生成一份默认的构造方法,生成的默认构造方法一定是无参的。

    1. public class DateUtil {
    2. //成员变量: 定义在方法外部 类的内部的变量
    3. public int year;
    4. public int month;
    5. public int day;
    6. public void show() {
    7. int a = 10;
    8. System.out.println("年:"+this.year+" 月:"+this.month+" 日:"+this.day);
    9. public static void main(String[] args) {
    10. DateUtil dateUtil = new DateUtil();
    11. dateUtil.show();
    12. }
    13. }

    👉上述DateUtil类中,没有定义任何构造方法,编译器会默认生成一个不带参数的构造方法。

    ❗❗❗注意:一旦用户定义,编译器则不再生成。

    6️⃣构造方法中,可以通过this调用其他构造方法来简化代码

    1. public class DateUtil {
    2. //成员变量: 定义在方法外部 类的内部的变量
    3. public int year;
    4. public int month;
    5. public int day;
    6. // 无参构造方法--内部给各个成员赋值初始值,该部分功能与三个参数的构造方法重复
    7. // 此处可以在无参构造方法中通过this调用带有三个参数的构造方法
    8. // 但是this(1900,1,1);必须是构造方法中第一条语句
    9. public DateUtil() {
    10. //System.out.println(year); 注释取消掉,编译会失败
    11. this(1999,9,9);
    12. //this.year = 1900;
    13. //this.month = 1;
    14. //this.day = 1;
    15. System.out.println("不带参数的构造方法," +
    16. "这个方法如果没有写,Java会默认提供!");
    17. }
    18. // 带有三个参数的构造方法
    19. public DateUtil(int year,int month,int day) {
    20. this.year = year;
    21. this.month = month;
    22. this.day = day;
    23. System.out.println("调用了带有3个参数的构造方法!");
    24. }
    25. public static void main(String[] args) {
    26. DateUtil dateUtil = new DateUtil();
    27. dateUtil.show();
    28. }
    29. }

    8400d5a952fe4a85964f8ba4fbca0b34.png

    注意:this(...)必须是构造方法中第一条语句            不能形成环

    1. public DateUtil() {
    2. this(1999,9,9);
    3. System.out.println("不带参数的构造方法," +
    4. "这个方法如果没有写,Java会默认提供!");
    5. }
    6. public DateUtil(int year,int month,int day) {
    7. this();//error
    8. this.year = year;
    9. this.month = month;
    10. this.day = day;
    11. System.out.println("调用了带有3个参数的构造方法!");
    12. }

    53ed005843564f5a9d2314c55e5ccdd7.png

    7️⃣绝大多数情况下使用public来修饰,特殊场景下会被private修饰(后序讲单例模式时会遇到)

    👉总结:(this不能调用静态的成员方法和属性,这个后边就会讲到,这里只是简单说一下)

    this.data:访问当前对象的成员变量

    this.func():访问当前对象的成员方法

    this():访问当前对象的其他构造方法

    📒2.3 默认初始化

    1. public class Date {
    2. public int year;
    3. public int month;
    4. public int day;
    5. public Date(int year, int month, int day) {
    6. // 成员变量在定义时,并没有给初始值, 为什么就可以使用呢?
    7. System.out.println(this.year);
    8. System.out.println(this.month);
    9. System.out.println(this.day);
    10. }
    11. public static void main(String[] args) {
    12. // 此处a没有初始化,编译时报错:
    13. // Error:(24, 28) java: 可能尚未初始化变量a
    14. // int a;
    15. // System.out.println(a);
    16. Date d = new Date(2021,6,9);
    17. }
    18. }

    80622627c9f04ecbac44fbc61ea662cc.png

    ❓❓要搞清楚这个过程,就需要知道 new 关键字背后所发生的一些事情:

    Date d = new Date(2021,6,9);

    ➡️在程序层面只是简单的一条语句,在JVM层面需要做好多事情,下面简单介绍下:

    1. 检测对象对应的类是否加载了,如果没有加载则加载
    2. 为对象分配内存空间
    3. 处理并发安全问题
        比如:多个线程同时申请对象,JVM要保证给对象分配的空间不冲突
    4. 设置对象头信息(关于对象内存模型后面会介绍)
    5. 调用构造方法,给对象中各个成员赋值
    6. 初始化所分配的空间
        即:对象空间被申请好之后,对象中包含的成员已经设置好了初始值,比如:

    fd4d800fb73a40b4bd0a6a19e5fefc90.png

    📁3. 封装

    📘3.1 封装的概念

    📖面向对象程序三大特性:封装、继承、多态。而类和对象阶段,主要研究的就是封装特性。何为封装呢?简单来说就是套壳屏蔽细节

    比如:对于电脑这样一个复杂的设备,提供给用户的就只是:开关机、通过键盘输入,显示器,USB插孔等,让用户来和计算机进行交互,完成日常事务。但实际上:电脑真正工作的却是CPU、显卡、内存等一些硬件元件。

    6f477b5e517e4f7aa150b8832c6b9ecc.png

    ➡️对于计算机使用者而言,不用关心内部核心部件,比如主板上线路是如何布局的,CPU内部是如何设计的等,用户只需要知道,怎么开机、怎么通过键盘和鼠标与计算机进行交互即可。因此计算机厂商在出厂时,在外部套上壳子,将内部实现细节隐藏起来,仅仅对外提供开关机、鼠标以及键盘插孔等,让用户可以与计算机进行交互即可

    🙉总结:

    封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互

    📗3.2 访问限定符

    ✨Java中主要通过类和访问权限来实现封装:类可以将数据以及封装数据的方法结合在一起,更符合人类对事物的认知,而访问权限用来控制方法或者字段能否直接在类外使用。Java中提供了四种访问限定符:

    ac9342810c544dc3b72a044270185f95.png

    比如:

    public:可以理解为一个人的外貌特征,谁都可以看得到
    default: 对于自己家族中(同一个包中)不是什么秘密,对于其他人来说就是隐私了
    private:只有自己知道,其他人都不知道
    🔺【 说明
    protected主要是用在继承中,继承部分详细介绍
    default权限指:什么都不写时的默认权限
    访问权限除了可以限定类中成员的可见性,也可以控制类的可见性

    1️⃣private 只能在当前类当中才能使用

    1. public class Computer {
    2. private String cpu; // cpu
    3. private String memory; // 内存
    4. public String screen; // 屏幕
    5. String brand; // 品牌---->default属性
    6. public Computer(String brand, String cpu, String memory, String screen) {
    7. this.brand = brand;
    8. this.cpu = cpu;
    9. this.memory = memory;
    10. this.screen = screen;
    11. }
    12. public void Boot(){
    13. System.out.println("开机~~~");
    14. }
    15. public void PowerOff(){
    16. System.out.println("关机~~~");
    17. }
    18. public void SurfInternet(){
    19. System.out.println("上网~~~");
    20. }
    21. }
    22. public class TestComputer {
    23. public static void main(String[] args) {
    24. Computer p = new Computer("HW", "i7", "8G", "13*14");
    25. System.out.println(p.brand); // default属性:只能被本包中类访问
    26. System.out.println(p.screen); // public属性: 可以任何其他类访问
    27. // System.out.println(p.cpu); // private属性:只能在Computer类中访问,不能被其他类访问
    28. }
    29. }
    注意:一般情况下成员变量设置为private,成员方法设置为public。

    📕3.3 封装扩展之包

    3.3.1 包的概念

    ➡️在面向对象体系中,提出了一个软件包的概念,即:为了更好的管理类,把多个类收集在一起成为一组,称为软件包。有点类似于目录。比如:为了更好的管理电脑中的歌曲,一种好的方式就是将相同属性的歌曲放在相同文件下,也可以对某个文件夹下的音乐进行更详细的分类。

    a8e6a6e28ec04ac5bff1fa756c5f3261.png

    ➡️在Java中也引入了包,包是对类、接口等的封装机制的体现,是一种对类或者接口等的很好的组织方式,比如:一个包中的类不想被其他包中的类使用。包还有一个重要的作用:在同一个工程中允许存在相同名称的类,只要处在不同的包中即可

    3.3.2 导入包中的类

    ✨Java 中已经提供了很多现成的类供我们使用. 例如Date类:可以使用 java.util.Date 导入 java.util 这个包中的 Date类.

    1. public class Test {
    2. public static void main(String[] args) {
    3. java.util.Date date = new java.util.Date();
    4. // 得到一个毫秒级别的时间戳
    5. System.out.println(date.getTime());
    6. }
    7. }

    1️⃣但是这种写法比较麻烦一些, 可以使用 import语句导入包.

    1. import java.util.Date;
    2. public class Test {
    3. public static void main(String[] args) {
    4. Date date = new Date();
    5. // 得到一个毫秒级别的时间戳
    6. System.out.println(date.getTime());
    7. }
    8. }

    2️⃣如果需要使用 java.util 中的其他类, 可以使用 import java.util.*

    1. import java.util.*;
    2. public class Test {
    3. public static void main(String[] args) {
    4. Date date = new Date();
    5. // 得到一个毫秒级别的时间戳
    6. System.out.println(date.getTime());
    7. }
    8. }

    3️⃣但是我们更建议显式的指定要导入的类名. 否则还是容易出现冲突的情况.

    1. import java.util.*;
    2. import java.sql.*;
    3. public class Test {
    4. public static void main(String[] args) {
    5. // util 和 sql 中都存在一个 Date 这样的类, 此时就会出现歧义, 编译出错
    6. Date date = new Date();
    7. System.out.println(date.getTime());
    8. }
    9. }
    10. // 编译出错
    11. Error:(5, 9) java: 对Date的引用不明确
    12. java.sql 中的类 java.sql.Date 和 java.util 中的类 java.util.Date 都匹配

    4️⃣在这种情况下需要使用完整的类名

    1. import java.util.*;
    2. import java.sql.*;
    3. public class Test {
    4. public static void main(String[] args) {
    5. java.util.Date date = new java.util.Date();
    6. System.out.println(date.getTime());
    7. }
    8. }

    5️⃣可以使用import static导入包中静态的方法和字段。

    1. import static java.lang.Math.*;
    2. public class Test {
    3. public static void main(String[] args) {
    4. double x = 30;
    5. double y = 40;
    6. // 静态导入的方式写起来更方便一些.
    7. // double result = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
    8. double result = sqrt(pow(x, 2) + pow(y, 2));
    9. System.out.println(result);
    10. }
    11. }
    ❗❗❗ 注意事项: import 和 C++ 的 #include 差别很大. C++ 必须 #include 来引入其他文件内容, 但是 Java 不需要import 只是为了写代码的时候更方便. import 更类似于 C++ 的 namespace 和 using 

    3.3.3 自定义包

    🔺基本规则

    在文件的最上方加上一个 package 语句指定该代码在哪个包中.
    包名需要尽量指定成唯一的名字, 通常会用公司的域名的颠倒形式(例如 com.bit.demo1 ).
    包名要和代码路径相匹配. 例如创建 com.bit.demo1 的包, 那么会存在一个对应的路径 com/bit/demo1 来存储 代码.
    如果一个类没有 package 语句, 则该类被放到一个默认包中.

    操作步骤:

    1️⃣在 IDEA 中先新建一个包: 右键 src -> 新建 -> 包

    46d936495dff4a57b0a29dbb559bb888.png

    2️⃣在弹出的对话框中输入包名, 例如 com.bit.demo1

    a8ccffdc415c4f3db76bf95e051b1950.png

    3️⃣在包中创建类, 右键包名 -> 新建 -> 类, 然后输入类名即可.

    6906e94ea4684c2c98d641b525437629.png

    4️⃣此时可以看到我们的磁盘上的目录结构已经被 IDEA 自动创建出来了

    d8603b3c3678427f965b832cd76451c9.png5️⃣同时我们也看到了, 在新创建的 Test.java 文件的最上方, 就出现了一个 package 语句

     f1cb06910a044e309adfaf2034b826c5.png

    3.3.4 包的访问权限控制举例

    ✨Computer类位于com.bit.demo1包中,TestComputer位置com.bit.demo2包中:

    1. package com.bit.demo1;
    2. public class Computer {
    3. private String cpu; // cpu
    4. private String memory; // 内存
    5. public String screen; // 屏幕
    6. String brand; // 品牌
    7. public Computer(String brand, String cpu, String memory, String screen) {
    8. this.brand = brand;
    9. this.cpu = cpu;
    10. this.memory = memory;
    11. this.screen = screen;
    12. }
    13. public void Boot(){
    14. System.out.println("开机~~~");
    15. }
    16. public void PowerOff(){
    17. System.out.println("关机~~~");
    18. }
    19. public void SurfInternet(){
    20. System.out.println("上网~~~");
    21. }
    22. ///
    23. package com.bite.demo2;
    24. import com.bite.demo1.Computer;
    25. public class TestComputer {
    26. public static void main(String[] args) {
    27. Computer p = new Computer("HW", "i7", "8G", "13*14");
    28. System.out.println(p.screen);
    29. // System.out.println(p.cpu); // 报错:cup是私有的,不允许被其他类访问
    30. // System.out.println(p.brand); // 报错:brand是default,不允许被其他包中的类访问
    31. }
    32. }
    33. // 注意:如果去掉Computer类之前的public修饰符,代码也会编译失败

    6.3.5 常见的包

    1. java.lang:系统常用基础类(String、Object),此包从JDK1.1后自动导入。
    2. java.lang.reflflect:java 反射编程包;
    3. java.net:进行网络编程开发包。
    4. java.sql:进行数据库开发的支持包。
    5. java.util:是java提供的工具程序包。(集合类等) 非常重要
    6. java.io:I/O编程开发包。

     

     

     

     

     

  • 相关阅读:
    Grub启动Linux引导到BIOS问题的解决
    uniapp实战项目 (仿知识星球App) - - 封装组件
    jmh测试实践(针对不同准备数据测试)
    Vue解决Echarts组件使用ID不能复用问题
    项目环境稳定性指标建设之路
    赠送天翼云电脑,解决一点园子的商业化烦恼
    【LeetCode-SQL每日一练】——2. 第二高的薪水
    数学建模 -- 层次分析法(AHP)
    查询企业信息的四种方法
    ElasticSearch更新数据后查不到的问题
  • 原文地址:https://blog.csdn.net/m0_72161237/article/details/127754323