类和对象(两万字超详解析)(二)

本文涉及的产品
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
简介: 类和对象(两万字超详解析)

5. 对象的构造及初始化


5.1 如何初始化对象


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


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


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

如果是对象:


public static void main(String[] args) {
  Date d = new Date();
  d.printDate();
  d.setDate(2021,6,9);
  d.printDate();
} 
  // 代码可以正常通过编译


输出:


f34fc21ccd174144943e26bc4320bd7b.png

需要调用之前写的SetDate方法才可以将具体的日期设置到对象中。

通过上述例子发现两个问题:


  1. 每次对象创建好后调用SetDate方法设置具体日期,比较麻烦,那对象该如何初始化?
  2. 局部变量必须要初始化才能使用,为什么字段声明之后没有给值依然可以使用?

这就要讲到构造方法了。


5.2 构造方法


5.2.1 概念


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


public class Date {
    public int year;
    public int month;
    public int day;
    // 构造方法:
// 名字与类名相同,没有返回值类型,设置为void也不行
// 一般情况下使用public修饰
// 在创建对象时由编译器自动调用,并且在对象的生命周期内只调用一次
    public Date(int year, int month, int day){
        this.year = year;
        this.month = month;
        this.day = day;
        System.out.println("Date(int,int,int)方法被调用了");
    }
    public void printDate(){
        System.out.println(year + "-" + month + "-" + day);
    }
    public static void main(String[] args) {
// 此处创建了一个Date类型的对象,并没有显示调用构造方法
        Date d = new Date(2021,6,9); // 输出Date(int,int,int)表明方法被调用了
        d.printDate(); // 输出结果:2021-6-9
    }
}


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


5.2.2 特性


  1. 名字必须与类名相同
  2. 没有返回值类型,设置为void也不行
  3. 创建对象时由编译器自动调用,并且在对象的生命周期内只调用一次(相当于人的出生,每个人只能出生一次)
  4. 构造方法可以重载(用户根据自己的需求提供不同参数的构造方法)
public class Date {
    public int year;
    public int month;
    public int day;
    // 无参构造方法
    public Date(){
        this.year = 1900;
        this.month = 1;
        this.day = 1;
    }
    // 带有三个参数的构造方法    
    public Date(int year, int month, int day) {   
        this.year = year;
        this.month = month;
        this.day = day;
    }
    public void printDate(){
        System.out.println(year + "-" + month + "-" + day);
    }
    public static void main(String[] args) {
        Date d = new Date();
        d.printDate();
        Date b = new Date(2022,11,11);
        b.printDate();
    }
}


输出:


4242abc75aee4554b37e76a0b07f075c.png


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

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


public class Date {
    public int year;
    public int month;
    public int day;
    public void printDate(){
        System.out.println(year + "-" + month + "-" + day);
    }
    public static void main(String[] args) {
        Date d = new Date();
        d.printDate();
    }
}


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


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


下面代码便是这种错误:


public class Date {
    public int year;
    public int month;
    public int day;
    public Date(int year, int month, int day) {
        this.year = year;
        this.month = month;
        this.day = day;
    }
    public void printDate(){
        System.out.println(year + "-" + month + "-" + day);
    }
    public static void main(String[] args) {
// 如果编译器会生成,则生成的构造方法一定是无参的
// 则此处创建对象是可以通过编译的
// 但实际情况是:编译期报错
        Date d = new Date();  //这里编译器会报错,因为用户定义了构造函数,所以编译器不会再生成无参构造函数
        d.printDate();
    }
}


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

public class Date {
    public int year;
    public int month;
    public int day;
    // 无参构造方法--内部给各个成员赋值初始值,该部分功能与三个参数的构造方法重复
    // 此处可以在无参构造方法中通过this调用带有三个参数的构造方法
    // 但是this(1900,1,1);必须是构造方法中第一条语句
    public Date() {
        //System.out.println(year); 注释取消掉,编译会失败
        this(1900, 1, 1);  //必须是构造方法第一条语句
        //this.year = 1900;
        //this.month = 1;
        //this.day = 1;
    }
    // 带有三个参数的构造方法
    public Date(int year, int month, int day) {
        this.year = year;
        this.month = month;
        this.day = day;
    }
}


注意:


  1. this(…)必须是构造方法中第一条语句
  2. 不能形成环 (成环会导致一直循环)
public Date(){
  this(1900,1,1);  //这里调用有参构造方法
}
public Date(int year, int month, int day) {
  this();   //又去调用无参构造方法,然后又回来,一直循环
}


7.绝大多数情况下使用public来修饰,特殊场景下会被private修饰(后面会讲)


5.3 默认初始化


在上文中提出的第二个问题:为什么局部变量在使用时必须要初始化,而成员变量可以不用呢?


public class Date {
    public int year;
    public int month;
    public int day;
    public Date(int year, int month, int day) {
// 成员变量在定义时,并没有给初始值, 为什么就可以使用呢?
        System.out.println(this.year);
        System.out.println(this.month);
        System.out.println(this.day);
    }
    public static void main(String[] args) {
        //int a;
        //System.out.println(a);     //此处a没有初始化,编译时报错:
        Date d = new Date(2021,6,9);  
    }
}


输出:

a130f85ac5cf45ffb9cb3097e9e728dd.png


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


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


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


  1. 检测对象对应的类是否加载了,如果没有加载则加载
  2. 为对象分配内存空间
  3. 处理并发现安全问题

比如:多个线程同时申请对象,JVM要保证给对象分配的空间不冲突

  1. 初始化所分配的空间(关键明白这一点)

即:对象空间被申请好之后,对象中包含的成员已经设置好了初始值,比如:


8ccbe6ff5de54bae98f77a769551575f.png


  1. 设置对象头信息(关于对象内存模型)
  2. 调用构造方法,给对象中各个成员赋值


5.4 就地初始化


在声明成员变量时,就直接给出了初始值。


public class Date {
    public int year = 1900;
    public int month = 1;
    public int day = 1;
    public Date(){
    }
    public Date(int year, int month, int day) {
    }
    public static void main(String[] args) {
    Date d1 = new Date(2021,6,9);
    Date d2 = new Date();
    }
}


注意:代码编译完成后,编译器会将所有给成员初始化的这些语句添加到各个构造函数中。


6. 封装


6.1 封装的概念


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

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

对于计算机使用者而言,不用关心内部核心部件,比如主板上线路是如何布局的,CPU内部是如何设计的等,用户只需要知道,怎么开机、怎么通过键盘和鼠标与计算机进行交互即可。因此计算机厂商在出厂时,在外部套上壳

子,将内部实现细节隐藏起来,仅仅对外提供开关机、鼠标以及键盘插孔等,让用户可以与计算机进行交互即可。


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


6.2 访问限定符


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

ae7f081501ee421b9b5a499c071bbe78.png


注:默认就是default不加修饰限定符的意思,可就是可以访问。


理解:

public:可以理解为一个人的外貌特征,谁都可以看得到

default: 对于自己家族中(同一个包中)不是什么秘密,对于其他人来说就是隐私了

private:只有自己知道,其他人都不知道


【说明】

protected主要是用在继承中,继承部分详细介绍

default权限指:什么都不写时的默认权限

访问权限除了可以限定类中成员的可见性,也可以控制类的可见性


例:


class Computer {
    private String cpu; // cpu
    private String memory; // 内存
    public String screen; // 屏幕
    String brand; // 品牌---->default属性
    public Computer(String brand, String cpu, String memory, String screen) {
        this.brand = brand;
        this.cpu = cpu;
        this.memory = memory;
        this.screen = screen;
    }
    public void Boot(){
        System.out.println("开机~~~");
    }
    public void PowerOff(){
        System.out.println("关机~~~");
    }
    public void SurfInternet(){
        System.out.println("上网~~~");
    }
}
public class Date{
    public static void main(String[] args) {
        Computer p = new Computer("HW", "i7", "8G", "13*14");
        System.out.println(p.brand); // default属性:只能被本包中类访问
        System.out.println(p.screen); // public属性: 可以任何其他类访问
        // System.out.println(p.cpu); // private属性:只能在Computer类中访问,不能被其他类访问
    }
}


注意:一般情况下成员变量设置为private,成员方法设置为public。


相关文章
|
6天前
|
存储 Java API
详细解析HashMap、TreeMap、LinkedHashMap等实现类,帮助您更好地理解和应用Java Map。
【10月更文挑战第19天】深入剖析Java Map:不仅是高效存储键值对的数据结构,更是展现设计艺术的典范。本文从基本概念、设计艺术和使用技巧三个方面,详细解析HashMap、TreeMap、LinkedHashMap等实现类,帮助您更好地理解和应用Java Map。
20 3
|
27天前
|
存储 编译器 C语言
C++类与对象深度解析(一):从抽象到实践的全面入门指南
C++类与对象深度解析(一):从抽象到实践的全面入门指南
44 8
|
27天前
|
存储 编译器 数据安全/隐私保护
【C++篇】C++类与对象深度解析(四):初始化列表、类型转换与static成员详解2
【C++篇】C++类与对象深度解析(四):初始化列表、类型转换与static成员详解
25 3
|
27天前
|
编译器 C++
【C++篇】C++类与对象深度解析(四):初始化列表、类型转换与static成员详解1
【C++篇】C++类与对象深度解析(四):初始化列表、类型转换与static成员详解
43 3
|
26天前
|
Python
深入解析 Python 中的对象创建与初始化:__new__ 与 __init__ 方法
深入解析 Python 中的对象创建与初始化:__new__ 与 __init__ 方法
15 1
|
20天前
|
程序员 开发者 Python
深度解析Python中的元编程:从装饰器到自定义类创建工具
【10月更文挑战第5天】在现代软件开发中,元编程是一种高级技术,它允许程序员编写能够生成或修改其他程序的代码。这使得开发者可以更灵活地控制和扩展他们的应用逻辑。Python作为一种动态类型语言,提供了丰富的元编程特性,如装饰器、元类以及动态函数和类的创建等。本文将深入探讨这些特性,并通过具体的代码示例来展示如何有效地利用它们。
22 0
|
2月前
|
设计模式 存储 人工智能
深度解析Unity游戏开发:从零构建可扩展与可维护的游戏架构,让你的游戏项目在模块化设计、脚本对象运用及状态模式处理中焕发新生,实现高效迭代与团队协作的完美平衡之路
【9月更文挑战第1天】游戏开发中的架构设计是项目成功的关键。良好的架构能提升开发效率并确保项目的长期可维护性和可扩展性。在使用Unity引擎时,合理的架构尤为重要。本文探讨了如何在Unity中实现可扩展且易维护的游戏架构,包括模块化设计、使用脚本对象管理数据、应用设计模式(如状态模式)及采用MVC/MVVM架构模式。通过这些方法,可以显著提高开发效率和游戏质量。例如,模块化设计将游戏拆分为独立模块。
149 3
|
2月前
|
JavaScript 前端开发 API
Javaweb之javascript的BOM对象的详细解析
BOM为Web开发提供了强大的API,允许开发者与浏览器进行深入的交互。合理使用BOM中的对象和方法,可以极大地增强Web应用的功能性和用户体验。需要注意的是,BOM的某些特征可能会在不同浏览器中表现不一致,因此在开发过程中需要进行仔细的测试和兼容性处理。通过掌握BOM,开发者能够制作出更丰富、更动态、更交互性的JavaWeb应用。
29 1
|
3月前
|
缓存 Java 开发者
Spring高手之路22——AOP切面类的封装与解析
本篇文章深入解析了Spring AOP的工作机制,包括Advisor和TargetSource的构建与作用。通过详尽的源码分析和实际案例,帮助开发者全面理解AOP的核心技术,提升在实际项目中的应用能力。
40 0
Spring高手之路22——AOP切面类的封装与解析
|
3月前
|
JSON 图形学 数据格式
Json☀️ 一、认识Json是如何解析成类的
Json☀️ 一、认识Json是如何解析成类的

推荐镜像

更多