文章目录
Ch.IV 对象和类
4.1 OOP概述:
这里都是概念, 在C++ 中接触的已经足够多了, 这里需要学的仅仅是UML部分:
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++相同, 仅仅记录不同的部分:
-
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);
-
Java类中的所有方法都需要在类内进行定义, 但是是否成为内联函数则是Java虚拟机的优化任务
这一点与C++有极大的不同
- 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
- Java类中的方法同样存在着隐式参数this, 但是使用的方法不同
C++使用成员访问运算符->
, Java中直接使用点运算符.
- 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();
工厂方法的好处:
- 与构造器相比, 工厂方法不必将名称设置的与类名相同, 可以有自己专属的名称, 更容易被理解与使用
- 可以使用工厂方法构造其他类, 如父类或子类, 这也是构造器无法实现的
类中的main方法:
-
最外层的包装类中的main函数必须被设置为static静态就是这个原因
在程序运行之初, 没有任何的对象, 所以需要将main设置为static以直接调用
-
除了最外层的包装类, 其他类中也可以设置main方法
此main函数用于单元测试, 可单独运行main方法执行其中的代码
-
在IDEA中, 直接通过快捷方式运行
-
而在命令行中, 就要使用java解释器+需要测试的类名, 如:
java fucker
-
4.5 方法参数:
Java中所有的方法使用的都是按值传递
, 不存在C++中的三种传递方式
但是由于Java类对象的指针实质, 在方法的参数为类对象时, 会发生指针传递, 遵循各种指针传递的特性
4.6 对象构造:
这里主要介绍Java中的自定义类的构造函数的编写
-
支持构造器の重载
-
构造器会尝试初始化类中的域
-
编译器同样会创建无参构造器 (默认构造函数)
-
Java类构造器可以直接在函数体内显示初始化变量, 无需使用C++中的初始化列表
实际上, C++的初始化列表是很特殊的语法, 只在C++中适用
由于Java中的类对象实际上为指针, 所以无需这种特殊语法
-
内部作用域的变量同样会覆盖外部同名的域
此时可以使用
this
访问被覆盖的域 -
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中使用包的方法有两种:
-
直接使用:
在使用的方法前加上包的前缀:java.time.LocalDate today = java.time.LocalDate.now() ;
-
使用
import
引入包:
可以省去前缀可以省去前缀
IDEA中已经开启的自动
import
的功能-
引入单一的类:
import java.time.LocalDate; LocalDate today= LocalDate.now;
-
引入一个包中的所有类:
import java.util.* ; //会包含util里的所有类
如果
import
的两个包中存有相同的类或者是静态方法, 则编译器会报错, 此时需要使用前缀区分二者 -
几个常用的Java包:
自定义包:
这里就相当于是C++中的多文件编译, 创建自己的.h头文件和相应的.cpp文件, 而后加入编译
Java中的包实际上就是一个文件夹, 里头放着定义有目标类的.java文件, 而包的路径就是文件夹的路径
这里仅仅对IDEA中创建自定义包的操作进行说明:
-
在现有的Module中新建一个package
-
在现有Module中新建一个Java Class, 或是直接在package中新建一个Java Class
-
在新建的Java Class文件中的最上头添加, 而后
Alt+Enter
选择添加入目标包package packagePath;
此方法适用于将现有的
.java
文件添加入包中 -
而后可以开始在包中编写相应的代码, 并且可以设置其访问限制
注意这里有一个默认设置, 如果在包前没有说明访问限制, 则会被默认设置为
package-private
注意, 添加入包的.java
文件必须放到对应的位置上
即在文件内的package的路径必须和文件存放的路径相同, 否则会出现程序编译通过, 但无法运行的问题!
4.8 类路径:
完全是针对于命令行环境进行的配置, 这里仅仅简单的阅读了一下, 后期如需使用, 需要详细学习
4.9 文档注释:
主要是javadoc, 这玩意类似于Latex, 有自己的语法, 并且可以通过编译生成一个类似于API文档的帮助文档
水挺深…
但是这个玩意对于多人开发是相当重要的, 暂时放到后期进行学习!
优先使用CSDN的教程, 介绍的更为详细一点
4.10 类设计技巧:
这里的设计思维和C++相同, 不做过多的阐述