Java核心技术 卷1 Ch.4

Ch.IV 对象和类

4.1 OOP概述:

这里都是概念, 在C++ 中接触的已经足够多了, 这里需要学的仅仅是UML部分:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5Ymtpvon-1579508642885)(C:\Users\Janus II\AppData\Roaming\Typora\typora-user-images\image-20200117191351482.png)]

UML(Unified Modeling Language) 统一建模语言
用于描述类之间的关系

4.2 使用预定义类:

首先说明Java中类对象与C++ 的区别:

Java中的类对象更趋向于C++中的对象指针:

Date birthday; // Java
//实际上, 等同于
Date* birthday; // C++

所以, Java中很多关于类的操作, 都是有迹可循的:

  • Java类变量直接赋值时会发生浅拷贝

    深拷贝需要使用clone方法 (这个后头在讲)

  • Java类变量创建时需要使用new申请内存

  • Java类变量可被赋值为null, 而后则无法在使用

    但是Java中比C++友善的是, 使用null会抛出runtime error异常, 而不是UB

这也带来了很多问题:

  • 类的方法返回私有对象时, 会发生浅拷贝, 使得原先私有的数据成员的封装性受到破坏

    这里需要使用clone() 函数对数据成员进行深拷贝

    具体的使用丢到后头再讲

4.2.2 Java类库中的LocalDate类:

这玩意相当于C++的time(), 返回的是从现在到纪元时间1970年1月1日00:00:00的毫秒数

Java中表示时间的类有多个, 而LocalDate仅仅是其中一个罢了

这里仅仅是以LocalDate来介绍类的使用方法, 想要使用的话, 再看API吧

其中比较常用的API:

static Local Time now( )
//构造一个表示当前日期的对象
    
static LocalTime of ( int year , int month , int day )
//构造一个表示给定日期的对象
    
int getYear( )
int getMonthValue( )
int getDayOfMonth( )
//得到当前日期的年、 月和曰
    
DayOfWeek getDayOfWeek
//得到当前日期是星期几, 作为 DayOfWeek 类的一个实例返回 
//调用 getValue 来得到1 ~ 7 之间的一个数, 表示这是星期几, 1 表示星期一, 7 表示星期日。

Local Date piusDays( int n )
Local Date minusDays(int n)
//生成当前日期之后或之前 n 天的日期

4.3 自定义类:

这里大部分和C++相同, 仅仅记录不同的部分:

  1. Java类的构造函数的使用需要伴随着new

    上头说道Java中的类对象相当于C++中的类对象指针, 所以实例化类对象时需要使用new申请内存:

    staff[0] = new Employee("Carl Cracker", 75000, 1987, 12, 15);
    staff[1] = new Employee("Harry Hacker", 50000, 1989, 10, 1);
    staff[2] = new Employee("Tony Tester", 40000, 1990, 3, 15);
    
  2. Java类中的所有方法都需要在类内进行定义, 但是是否成为内联函数则是Java虚拟机的优化任务

这一点与C++有极大的不同

  1. Java中的类成员同样存在访问限制, 但是对于访问限制的设置, 与C++不同的是, Java中每个变量的访问限制需要单独设置:
class testClass{
    public static void main(String[] args) {
        System.out.printf("f@ck: %d\n", num1);
        return;
    }
    private static int num1=141614;		//每个成员的前头都需要单独声明访问限制
    private static int num2=141614;
}

且如果没有显式的声明成员的访问限制, 他们将被默认设置为public, C++中会被默认设置为private

  1. Java类中的方法同样存在着隐式参数this, 但是使用的方法不同

C++使用成员访问运算符->, Java中直接使用点运算符.

  1. Java类中的常量数据成员

和上头提到的一样, 使用final将成员设置为常量类型, 相当于C++的const

具有和C++常量成员相同的特性

4.4 静态域与静态方法:

Java中的域:

实际上就是Java类中的数据成员变量

就如Java中的函数成员称为方法类似, Java中的变量数据成员被称之为域

静态方法:

Java类中的静态方法:

对应于C++中的静态成员函数

唯一与C++有区别的就是, Java中访问静态方法的方法为直接只用.点运算符, 而C++中使用::作用域运算符:

Math.pow(x,a);
//调用Math中的静态方法pow

工厂方法:

对于Java中的静态方法, 还有一个典型的特有应用, 就是工厂方法

称之为工厂, 因为此方法可以用于实例化类
由于Java的垃圾回收机制, 以及Java类对象是类指针的实质, 所以可以在类的静态方法中实例化一个类, 并通过返回值赋给外部的指针

如:

NumberFormat currencyFormatter = NumberFormat.getCurrencylnstanceO;
NumberFormat percentFormatter = NumberFormat.getPercentlnstance()

工厂方法的好处:

  1. 与构造器相比, 工厂方法不必将名称设置的与类名相同, 可以有自己专属的名称, 更容易被理解与使用
  2. 可以使用工厂方法构造其他类, 如父类或子类, 这也是构造器无法实现的

类中的main方法:

  1. 最外层的包装类中的main函数必须被设置为static静态就是这个原因

    在程序运行之初, 没有任何的对象, 所以需要将main设置为static以直接调用

  2. 除了最外层的包装类, 其他类中也可以设置main方法

    此main函数用于单元测试, 可单独运行main方法执行其中的代码

    • 在IDEA中, 直接通过快捷方式运行

    • 而在命令行中, 就要使用java解释器+需要测试的类名, 如:

      java fucker
      

4.5 方法参数:

Java中所有的方法使用的都是按值传递, 不存在C++中的三种传递方式

但是由于Java类对象的指针实质, 在方法的参数为类对象时, 会发生指针传递, 遵循各种指针传递的特性

4.6 对象构造:

这里主要介绍Java中的自定义类的构造函数的编写

  1. 支持构造器の重载

  2. 构造器会尝试初始化类中的域

  3. 编译器同样会创建无参构造器 (默认构造函数)

  4. Java类构造器可以直接在函数体内显示初始化变量, 无需使用C++中的初始化列表

    实际上, C++的初始化列表是很特殊的语法, 只在C++中适用

    由于Java中的类对象实际上为指针, 所以无需这种特殊语法

  5. 内部作用域的变量同样会覆盖外部同名的域

    此时可以使用this访问被覆盖的域

  6. Java支持类内初始化

调用另一个构造器:

此功能类似于C++中的委托构造, C++在初始化列表中调用其他的构造函数, Java直接在构造器的函数体内使用, 通过this

public Employee(double s)
{
// calls Employee(String, double)
	this("Employee #" + nextld, s);
	nextld++;
}

初始化块:

Java特有语法, 不常用

在Java类的实现中, 可以有多个代码块, 只要构造类的对象, 这些代码块中的内容就会被执行

如:

class testClass{
    {
        System.out.printf("This is Initialization block\n");
    }
    private int a;
}

在实例化testClass时, 会输出This is Initialization block

此方法类似于C++的static静态变量的类外初始化, 当对于静态域或静态常量的初始化较为复杂时可以使用

析构器:

由于Java的垃圾回收机制, Java的类中没有析构器这个玩意

但是有相应的finalize方法:
在一个类对象进入销毁程序后, 在垃圾回收器正式回收类对象的内存前, finalize方法会被执行

但是由于Java的垃圾回收机制很玄学, 具有很强的不确定性, 所以无法保证这玩意能够被及时的执行, 甚至在程序结束时都不会被执行(程序始终未触发垃圾回收)

这部分比较复杂, 暂时不做拓展

4.7 包:

Java中的包的作用实际上就类似于C++中的头文件, 是前人为我们写好的现成的轮子!

关于#include 与 import:

C++中使用#include包含头文件
而Java中使用import引入需要使用的包

虽然二者在使用方法上相似, 但是两者在底层的实现上并没有相似之处:

  • C++编译器由于无法查看其它文件的内部, 所以需要使用#include对需要使用的头文件进行明确的指出

    而后编译器会在预处理期将这些#include的文件进行动态联编, 使得目标程序变大

  • Java编译器可以查看任何文件, 使用import仅仅是告诉程序需要到何处去寻找使用的类或方法, 编译器仅仅是做个引导, 并不会使最后的目标程序变大

C++中与import类似的功能是using namespace

包的导入:

在了解了Java包的机制之后, 可以更为方便的学习这个部分

类比于using namespace功能, Java中使用包的方法有两种:

  1. 直接使用:
    在使用的方法前加上包的前缀:

    java.time.LocalDate today = java.time.LocalDate.now() ;
    
  2. 使用import引入包:
    可以省去前缀

    可以省去前缀

    IDEA中已经开启的自动import的功能

    • 引入单一的类:

      import java.time.LocalDate;
      LocalDate today= LocalDate.now;
      
    • 引入一个包中的所有类:

      import java.util.* ;	//会包含util里的所有类
      

    如果import的两个包中存有相同的类或者是静态方法, 则编译器会报错, 此时需要使用前缀区分二者

几个常用的Java包:

img

自定义包:

这里就相当于是C++中的多文件编译, 创建自己的.h头文件和相应的.cpp文件, 而后加入编译

Java中的包实际上就是一个文件夹, 里头放着定义有目标类的.java文件

这里仅仅对IDEA中创建自定义包的操作进行说明:

  1. 在现有的Module中新建一个package

  2. 在现有Module中新建一个Java Class, 或是直接在package中新建一个Java Class

  3. 在新建的Java Class文件中的最上头添加, 而后Alt+Enter选择添加入目标包

    package packagePath;
    

    此方法适用于将现有的.java文件添加入包中

  4. 而后可以开始在包中编写相应的代码, 并且可以设置其访问限制

    注意这里有一个默认设置, 如果在包前没有说明访问限制, 则会被默认设置为package-private

注意, 添加入包的.java文件必须放到对应的位置上
即在文件内的package的路径必须和文件存放的路径相同, 否则会出现程序编译通过, 但无法运行的问题!

4.8 类路径:

完全是针对于命令行环境进行的配置, 这里仅仅简单的阅读了一下, 后期如需使用, 需要详细学习

4.9 文档注释:

主要是javadoc, 这玩意类似于Latex, 有自己的语法, 并且可以通过编译生成一个类似于API文档的帮助文档
水挺深…

但是这个玩意对于多人开发是相当重要的, 暂时放到后期进行学习!

优先使用CSDN的教程, 介绍的更为详细一点

4.10 类设计技巧:

这里的设计思维和C++相同, 不做过多的阐述

设置为package-private==

注意, 添加入包的.java文件必须放到对应的位置上
即在文件内的package的路径必须和文件存放的路径相同, 否则会出现程序编译通过, 但无法运行的问题!

发布了17 篇原创文章 · 获赞 7 · 访问量 1349

猜你喜欢

转载自blog.csdn.net/qq_42683011/article/details/104053412