JAVA//对象与类

本文介绍了面向对象编程的基本概念,包括实例域、方法、对象状态和访问器/更改器方法。讨论了Java的访问控制修饰符,如private、default、protected和public的用法。此外,讲解了类之间的依赖、聚合和继承关系,以及对象变量与引用的区别。还阐述了Java中的构造器、私有方法、封装好处、final域、静态域和静态方法。最后,提到了包的作用、导入类的方式、静态导入以及类路径的概念。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 面向对象编程概述

对象中的数据称为实例域,操纵数据的过程称为方法

每个特定的类实例(对象)都有一组特定的实例域值。这些值的集合就是这个对象的当前状态( state )

无论何时,只要向对象发送一个消息,它的状态就有可能发生改变。对象状态的改变必须通过调用方法实现
(如果不经过方法调用就可以改变对象状态,只能说明封装性遭到了破坏。)

java访问控制和C++不同:
private : 被其修饰的属性以及方法只能被该类的对象访问,其子类不能访问,更不能允许跨包访问。

default: 即不加任何访问修饰符,通常称为“默认访问权限“或者“包访问权限”。该模式下,只允许在同一个包中进行访问。

protected: 被其修饰的属性以及方法只能被类本身的方法及子类访问即使子类在不同的包中也可以访问。

public: 被其修饰的类、属性以及方法不仅可以跨类访问,而且允许跨包访问。

1.1 类之间的关系

1.依赖 use a
如果一个类的方法操纵另一个类的对象,我们就说一个类依赖于另一个类 。
2.聚合 has a
聚合关系意味着类 A 的对象包含类 B 的对象。
3.继承 is a

对象变量不是一个对象,需要用新构造的对象初始化这个变量或者让该变量引用一个已经存在的对象。

一个对象变量并没有实际包含一个对象,而仅仅引用一个对象,这个引用只是形为和引用一样,并不是对象变量类型为引用。很多人错误地认为 Java 对象变量与 C++ 的引用类似。然而,在 C++ 中没有空引用, 并且引用不能被赋值。可以将 Java 的对象变量看作 C++ 的对象指针。(但是用起来还是当对象用的)

只访问对象而不修改对象的方法有时称为访问器方法,而会修改对象状态的方法更改器方法

1.2 用户自定义类

文件名必须与 public 类的名字相匹配。在一个源文件中, 只能有一个公有类,但可以有任意数目的非公有类。

如果用到的类处于另一个源文件内,该如何编译?
一种是把所有源文件都将编译成类文件
或者只编译main所在的类,编译器会自动编译他所使用过的类所在的源文件

1.2.1 构造器

构造器与类同名。在构造类的对象时, 构造器会运行,以便将实例域初始化为所希望的状态。
在这里插入图片描述
不要在构造器中定义与实例域重名的局部变量。这些变量只能在构造器内部访问,且屏蔽了同名的实例域

1.2.2 隐式参数和显式参数

关键字 this 表示隐式参数,显式参数是明显地列在方法声明中的。

C++在类内定义的方法自动称为内联方法。JAVA的所有的方法都必须在类的内部定义, 但并不表示它们是内联方法。是否将某个方法设置为内联方法是 Java 虚拟机的任务。即时编译器会监视调用那些简洁、经常被调用、 没有被重载以及可优化的方法。

1.2.3 封装的好处

1.可以改变内部实现(比如修改域的名字,类型等等),除了该类的方法之外,不会影响其他代码。
2.易于定位错误:某个域值出现了错误, 只要调试可以操作该域值的方法就可以了。
3.可以增加自定义检查,减少错误。更改器方法可以执行错误检查, 然而直接对域进行赋值将不保证进行这些处理。

注意不要编写返回可变对象的访问器方法,如下操作将会导致hireday可被其他类修改:
在这里插入图片描述
如果一定要返回hireday,需要clone它:
在这里插入图片描述

1.2.4 私有方法

将一个计算代码划分成若干个独立的辅助方法。通常, 这些辅助方法不应该成为公有接口的一部分

1.2.5 final实例域

可以将实例域定义为 final,构建对象时必须初始化这样的域。并且在后面的操作中, 不能够再对它进行修改

final 修饰符大都应用于基本 (primitive ) 类型域,或不可变(immutable) 类的域(如果类中的每个方法都不会改变其对象, 这种类就是不可变的类。例如,String类就是一个不可变的类)。

将对象变量定义为final表示final 存储在对象变量中的对象引用不会再指示其他同类的对象。但是这个对象还是可以更改的。

final类似于const。JAVA中没有const。是保留关键字,表示以后可能将const定义为关键字。

1.3 静态域和静态方法

1.3.1 静态域

如果将域定义为 static, 无论创建多少个对象(包括0个),每个类中只有一个这样的域。通用的,通过类名访问静态域

在绝大多数的面向对象程序设计语言中, 静态域被称为类域。术语“ static” 只是沿用了 C++ 的叫法, 并无实际意义。

1.3.2 静态方法

静态方法是一种不能向对象施操作的方法。

静态方法没有this隐式参数,只可以访问静态域。(因为不能作用于对象,也就无法访问实例域)

同静态域一样,静态方法用类名调用。

在 C++ 中,使用类名::操作符访问自身作用域之外的静态域和静态方法,java使用的是类名.
JAVA和C++都可以用对象.静态成员的形式访问静态成员,效果和用类名一样。

什么时候使用静态方法:
方法不需要访问对象状态,或者只需要访问类的静态域
方法其所需参数都是通过显式参数提供。

工厂方法(FactoryMethod)模式:定义一个创建产品对象的工厂接口,将产品对象的实际创建工作推迟到具体子工厂类当中。鉴于调用工厂方法时还没有创建对象,所以工厂方法一般定义为静态方法
在这里插入图片描述
每一个类都可以有一个 main 方法,有main方法的类生成的字节码是可执行的。这是一个常用于对类进行单元测试的技巧。

2. 方法参数

Java 程序设计语言总是采用按值调用。也就是说, 方法得到的是所有参数值的一个拷贝,特别是,方法不能修改传递给它的任何参数变量的内容。

当传递参数类型为对象时,是可以修改的,因为java的对象实际上是对象的引用。虽然传参时还是以拷贝方式,但是拷贝的是对象的引用
但是如前文所说,java的引用和C++的引用是不同的,更类似于指针和引用的结合(类似于引用,可以直接通过对象变量修改对象;类似于指针,可以将对象变量重新绑定,并且对象变量无需在定义时就初始化),传参时也和C++引用不同,所以不能交换两个对象引用参数的值(交换的是指向对象的引用的拷贝,是没有效果的)。

3. 对象构造

3.1 重载

返回类型不是方法签名的一部分。也就是说, 不能有两个名字相同、 参数类型也相同却返回不同类型值的方法。

3.2 默认域初始化

如果在构造器中没有显式地给域赋予初值,那么就会被自动地赋为默认值: 数值为 0、布尔值为 false、 对象引用为 null。

3.3 无参数构造器

如果在编写一个类时没有编写构造器, 那么系统就会提供一个无参数构造器。这个构造器将所有的实例域设置为默认值。

仅当类没有提供任何构造器的时候, 系统才会提供一个默认的构造器。

3.4 显式域初始化

可以在类定义中, 直接将一个值赋给任何域,初始值不一定是常量值。可以调用方法对域进行初始化。

在 C++ 中, 不能直接初始化类的实例域。所有的域必须在构造器中设置,通过初始值列表进行初始化。

3.5 调用另一个构造器

如果构造器的第一个语句形如 this(…), 这个构造器将调用同一个类的另一个构造器。
在这里插入图片描述

3.6 初始化块

在一个类的声明中,可以包含多个代码块。只要构造类的对象,这些块就会被执行。首先运行初始化块,然后才运行构造器的主体部分。
在这里插入图片描述
事实上,创建对象/调用构造器的具体处理步骤如下:
1 ) 所有数据域被初始化为默认值(0、false 或 null。)
2 ) 按照在类声明中出现的次序, 依次执行所有域初始化语句初始化块
3 ) 如果构造器第一行调用了第二个构造器,则执行第二个构造器主体
4 ) 执行这个构造器的主体

提供一个初始化值, 或者使用一个静态的初始化块来对静态域进行初始化:“
提供初始值:private static int nextld = 1;
静态初始化块:
在这里插入图片描述
在类第一次加载的时候, 将会进行静态域的初始化。与实例域一样,除非将它们显式地设置成其他值, 否则默认的初始值是 0、 false 或 null。 所有的静态初始化语句以及静态初始化块都将依照类定义的顺序执行

3.7 对象析构与 finalize 方法

Java 有自动的垃圾回收器,不需要人工回收内存, 所以 Java 不支持析构器。某些对象使用了内存之外的其他资源, 例如,文件使用了系统资源的另一个对象的句柄。在这种情况下,当资源不再需要时, 将其回收和再利用将显得十分重要

可以为任何一个类添加 finalize 方法。finalize 方法将在垃圾回收器清除对象之前调用。在实际应用中,不要依赖于使用 finalize 方法回收任何短缺的资源, 这是因为很难知道这个方法什么时候才能够调用

4. 包

使用包的主要原因是确保类名的唯一性。假如两个程序员不约而同地建立了 Employee类。只要将这些类放置在不同的包中, 就不会产生冲突。

4.1 导入类

一个类可以使用所属包中的所有类, 以及其他包中的公有类( public class)。 我们可以采用两种方式访问另一个包中的公有类。
第一种方式是在每个类名之前添加完整的包名。
另一种方式是使用 import 语句。import 语句是一种引用包含在包中的类的简明描述。一旦使用了 import 语句,在使用类时,就不必写出包的全名了。

可以使用 import 语句导人一个特定的类或者整个包的所有类
import java.time.LocalDate; //导入特定类
import java.util .*; //导入整个包

只能使用星号(*) 导入一个包, 而不能使用 import java.* 导入以 java 为前缀的所有包。

如果导入的两个包包含同名类,那么在使用该类时会产生编译错误。要正确使用该类,需在每个类名的前面加上完整的包名。如下:
在这里插入图片描述
C++ 程序员经常将 import 与 #include 弄混。 实际上, 这两者之间并没有共同之处。在 C++ 中,必须使用 include 将外部特性的声明加载进来,这是因为 C++ 编译器无法查看任何文件的内部,除了正在编译的文件以及在头文件中明确包含的文件。Java编译器可以查看其他文件的内部,只要告诉它到哪里去查看就可以了
#include相当于给C++看文件内部的权限,using和import是指明文件以及所需名字的位置。

事实上,和java的import类似的是C++的命名空间机制,和import对应的是using指令

4.2 静态导入

在import后加static可以导入包内的静态方法和静态域。
在这里插入图片描述
同样的,可以导入特定的方法或域:
在这里插入图片描述

4.3 将类放入包中

源文件编译生成类文件。

如果没有在源文件中放置 package 语句, 这个源文件中的类就被放置在一个默认包( defaulf package ) 中。默认包是一个没有名字的包。在此之前,我们定义的所有类都在默认包中,默认包在目录下。
在这里插入图片描述

如下的package语句告诉编译器,把Emplyee的编译结果即Employee类放在/com/horstmann/corejava目录下。
在这里插入图片描述
源文件,java和类文件,class是在同一目录下的。所以 ,如果用了package,最好保证源文件真的在你所说的目录下,也就是类的路径必须与包名匹配,否则,编译虽然不会有问题,但是解释器将找不到类文件位置

编译器对文件 (带有文件分隔符和扩展名 .java 的文件)进行操作。而 Java 解释器加载类(带有 . 分隔符 )。
javac com/myconipany/Payrol1App.java //编译器操作文件
java com.mycompany.PayrollApp //解释器加载类

4.4 包作用域

可以将类文件放在某些重要类的目录下,就可以访问预定义类的default或是public成员,从而达到操纵系统的目的。

为了解决这个问题:
1.把核心类成员声明为private,即增加封装性
2.修改了类加载器, 明确地禁止加载用户自定义的、 包名以“ java.” 开始的类
3.通过包密封 ( package sealing) 机制来解决将各种包混杂在一起的问题。如果将一个包密封起来, 就不能再向这个包添加类了。

4.5 类路径

类路径是JAVA编译器或者JAVA虚拟机用来查找类或者源文件的起始目录

import是告知要导入包的名字,类路径告诉编译器从哪里开始找。

类文件也可以存储在 JAR(Java 归档)文件中,在一个 JAR 文件中, 可以包含多个压缩形式的类文件和子目录。
JAR 文件使用 ZIP 格式组织文件和子目录。

想要将一个类被多个程序共享:
1.把类放到一个目录(以及该目录的子目录)中,这个目录是包树状结构的基目录。
2.将 JAR 文件放在一个目录中
3.设置类路径(classpath)。类路径是所有包含类文件的路径的集合(包括普通类文件和JAR)。linux下,路径之间用:隔开,windows下,路径之间用;隔开。

类路径包括
1.基目录
2.当前目录
3.JAR 文件(具体到JAR包)
从 Java SE 6 开始,可以在 JAR 文件目录中指定通配符,即可以批量指定目录下的JAR包。windows可以用*,但是UNIX不能使用*,防止shell进一步扩展。

由于运行时库文件( rt.jar 和在 jre/lib 与 jre/lib/ext 目录下的一些其他的 JAR 文件) 会被自动地搜索, 所以不必将它们显式地列在类路径中

在这里插入图片描述
在搜寻类时,先搜寻在 jre/lib 和jre/lib/ext 目录下的归档文件中所存放的系统类文件。然后搜寻类路径所列出的目录和归档文件

编译器还要查看源文件( Source files) 是否比类文件新。如果是这样的话,那么源文件就会被自动地重新编译

最好采用 -classpath (或 -cp) 选项指定类路径(整个指令应该书写在一行中):
在这里插入图片描述
也可以通过设置 CLASSPATH 环境变量完成这个操作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值