当前位置: 首页 > news >正文

【Java核心计算 基础知识(第9版)】第4章 对象与类

本章要点
- 面向对象程序设计
- 使用预定义类
- 用户自定义类
- 静态域与静态方法
- 方法参数
- 对象构造
- 包
- 类路径
- 文档注释
- 类设计技巧


4.1 面向对象程序设计概述

  • 面向对象的程序是由对象组成的,每个对象包含对用户公开的特定功能部分和隐藏的实现部分。
  • 面向过程:算法 + 数据结构 = 程序
  • 面向对象:数据结构 + 算法 = 程序

     

4.1.1 类

  • (class)是构造对象的模板或蓝图。由类构造(construct)对象的过程称为创建类的实例(instance)。
  • 封装(encapsulation)指将数据和行为组合中一个包中,并对对象的使用者隐藏了数据的实现方式。
  • 对象中的数据成为实例域(instance field),操纵数据的过程称为方法(method)。对于每个特定的对象都有一组特定的实例域值,这些值的集合就是这个对象的当前状态(state)。
  • 通过扩展一个类来建立另外一个新的类的过程称为继承(inheritance)。扩展后的新类具有所扩展的类的全部非私有属性和方法。
    注意:子类不能继承父类的私有成员。
    • Java官方文档明确指出,私有成员无法被继承:
      A class inherits from its direct superclass and direct superinterfaces all the non-private fields of the superclass and superinterfaces that are both accessible to code in the class and not hidden by a declaration in the class.
      A private field of a superclass might be accessible to a subclass - for example, if both classes are members of the same class. Nevertheless, a private field is never inherited by a subclass.
    • 一个小例子可以验证,”Field fieldC = clazzC.getDeclaredField(“i”);”处将抛出NoSuchFieldException:
    public static void main(String args[]){
        Super super1 = new Super();
        Child child = new Child();
        Class<? extends Super> clazzS = super1.getClass();
        Class<? extends Super> clazzC = child.getClass();
        try {
            Field fieldS = clazzS.getDeclaredField("i");
            System.out.println(fieldS);
            Field fieldC = clazzC.getDeclaredField("i");
            System.out.println(fieldC);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        }
    }

4.1.2 对象

  • 对象的三个主要特性:
    • 行为(behavior)–可调用的方法。
    • 状态(state)–域当前的值。
    • 标识(identity)–用以辨别不同对象的唯一标识。

4.1.3 识别类

  • 面向对象编程,首先设计类,然后再往每个类中添加方法。
  • 类通常对应名词(如账户,地址,订单等),方法通常对应动词(如添加,删除,修改,取消,支付等)。

4.1.4 类之间的关系

  • 依赖(dependence),即”uses-a”关系,一个类的方法操纵另一个类的对象,即一个类依赖于另一个类。
  • 聚合(aggregation),即”has-a”关系,一个类的对象包含另一个类的对象(作为实例域)。
  • 继承(inheritance),即”is-a”关系,一个类由另一个类扩展而来。

4.2 使用预定义类

使用一个类的方法,只需要知道方法名和参数(即接口),而无需了解它的具体实现过程,这就是封装的关键所在。

4.2.1 对象与对象变量

  • 构造器(constructor)是一种特殊的方法,用来构造并初始化对象。
  • 构造器的名字与类名相同,构造对象时,在构造器前加new操作符。
  • 对象与对象变量:一个对象变量并没有实际包含一个对象,而仅仅是通过内存地址引用一个对象。
  • 对象变量的值为null时表示该变量目前没有引用任何对象。

4.2.2 Java类库中的GregorianCalendar类

  • Date类的实例表示一个特定的时间点,即距离UTC时间1970/01/01 00:00:00的毫秒数。
  • Java中保存时间(值)与给时间点命名(格式)是分开的,Date类表示时间点,Calendar类表示时间格式。GregorianCalendar类是Calendar的子类。
    • BuddhistCalendar 泰国佛历
    • JapaneseImperialCalendar 日本黄历
    • GregorianCalendar 标准日历
  • DateFormatSymbols类可以获取当前地区的日历格式信息。

注意:Calendar中月份的表示从0开始表示为0-12(第1个月到第13个月,考虑到如中国阴历中的闰月)而不是1-12,GregoranCalendar中只使用0-11。
注意:Calendar中星期几的表示为1-7,表示一个星期的第1天到第7天,星期日到星期六。但注意不是每个地区都把星期日作为一个星期的第1天。
注意:当使用Calendar类时,建议尽量使用常量,如Calendar.Sunday,Calendar.JANUARY.

4.2.3 更改器方法与访问器方法

  • 更改实例域的值的方法称为更改器方法(mutator method),返回实例域的值的方法称为访问器方法(accessor method)。通常写为setFieldName和getFieldName的形式,称为setter和getter。
  • 通常将域定义为private,而对外提供public的setter/getter。
  • 注意,当域为引用类型变量时,getter不要直接返回域的引用,而应该返回一个副本。否则,获得引用的调用者可以通过引用直接修改域的状态。

4.3 用户自定义类

  • 一个源文件(.java)只能有一个public类,且文件名与public类名必须相同。
  • 编译源文件时,每个类(包括接口/枚举类/内部类/局部内部类/匿名类等各种形式的类)生成一个.class文件。
  • 编译源文件时,编译器会自动编译所依赖的其他源文件。

4.3.4 从构造器开始

  • 构造器与类同名。
  • 每个类可以有1个或以上的构造器。
  • 构造器可以有0或以上的参数。
  • 构造器没有返回值。
  • 构造器总是和new一起调用。

4.3.7 基于类的访问权限

一个方法可以访问所属类的所有对象的所有数据,包括private数据。


4.4 静态域与静态方法

4.4.1 静态域(类域)

  • 如果将域定义为static,每个类中只有一个这样的域,它属于类,不属于任何的对象。相对的,每个对象对每个实例域都有一份属于拷贝。

4.4.3 静态方法

  • 静态方法是一种不能向对象实施操作的方法。可以认为,静态方法是没有this参数的方法。
  • 一般两种情况使用静态方法:
    • 一个方法不需要访问对象状态,其所需参数都是通过显式参数提供。
    • 一个方法只需要访问类的静态域。

4.5 方法参数

  • 按值调用(call by value)表示方法接收的是调用者提供的值。
  • 按引用调用(call by reference)表示方法接收的是调用提供的变量地址。
  • Java只有按值调用,当变量为引用类型时传递的是引用的地址值。

4.6 对象构造

4.6.1 重载

  • 重载(overloading)指同一个类中多个方法有相同方法名、不同的参数列表(参数个数、参数类型、参数顺序)。
  • 重载解析(overloading resolution):编译器根据重载的方法的参数列表与调用者传递的参数列表进行匹配以挑选出相应的方法。如果编译器找不到匹配的方法,或者找出多个可能的匹配,就会产生编译时错误。
  • 方法名和参数类型组成方法的签名(signature),返回类型不是方法签名的一部分。不允许有方法签名相同但返回类型不同的方法。

4.6.2 默认域初始化

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

4.6.3 无参数的构造器

  • 如果在编写一个类时没有编写构造器,系统会提供一个默认的无参数构造器,这个构造器将所有的实例域设置为默认值(已显式赋值初始化的例外)。
  • 如果类中提供了至少一个构造器,则系统不会提供默认的无参数构造器。

4.6.5 参数名

  • 用this指示实例域可以区分同名的实例域和局部变量。

4.6.6 调用另一个构造器

  • 通过this(…)可以在一个构造器内调用另一个构造器。

4.6.7 初始化块

  • 初始化数据域的三种方法:
    • 在初始化块中赋值;
    • 在构造器中赋值;
    • 在声明中赋值。
  • 概念:在类之内、方法之外用{}括起来的代码块,称为初始化块。在一个类的声明中可以包含多个代码块,这些代码块在构造对象时执行,且在构造器之前执行
  • 用static修饰的初始化块称为静态的初始化块,静态的初始化块仅在类加载时执行一次

4.6.8 对象析构与finalize方法

  • finalize方法将在垃圾回收器清除对象之前调用。

补充&总结

  • 一个例子:对象初始化过程
    • 加载Demo.class进方法区
    • 所有Demo中所有静态域被初始化为默认值
    • 如果Demo中有静态初始化语句或静态初始化块,按声明的顺序执行
    • main方法进栈
    • 因main方法调用Person类,加载Person.class,Person执行静态初始化
    • main方法中声明一个Person类型的引用变量joyce
    • 堆内存中开辟空间创建一个Person类型的对象(new Person())
    • person对象的所有实例域被初始化为默认值
    • 如果Person中有域初始化语句或域初始化块,按声明的顺序执行
    • new Person()调用的构造器进栈
    • 如果该构造器调用了其他构造器,其他构造器进栈并先于该构造器执行
    • 该构造器执行并弹栈
    • 将person的首地址值赋值给变量joyce
    • 执行joyce.name=”Joyce”给name赋值
    • 调用sayHello(),sayHello方法进栈
    • 执行sayHello方法并弹栈
    • main方法弹栈
public class Demo {
    public static void main(String args[]) {
        Person joyce = new Person();
        joyce.name = "Joyce";
        joyce.sayHello();
    }
}

class Person {
    String name;
    public void sayHello(){
        System.out.println("Hello, I'm " + name + ".");
    }
}

  • 成员变量(域)与局部变量的区别:
    • 在类中的位置不同:成员变量声明在方法外;局部变量声明中方法中;
    • 在内存中的位置不同:成员变量在堆(因对象在堆);局部变量在栈(因方法进栈运行);
    • 生命周期不同:成员变量与所在对象一致(创建 -> 回收);局部变量与所属方法一致(进栈 -> 出栈);
    • 初始化值不同:成员变量有默认初始化值;局部变量无默认初始化值;
    • 当成员变量与局部变量同名时,在方法中默认调用的是局部变量,可用this指示调用成员变量。

4.7 包

  • (package)用来组织类,如果硬盘中的目录结构。
  • 使用包的主要原因是确保类名的唯一性。全类名=包名.类名。
  • 从编译器的角度来看,嵌套的包之间没有任何关系

4.7.1 类的导入

  • 一个类可以使用所属包中的所有类,以及其他包中的public类
  • 有两种方式访问另一个包中的公有类
    • 通过全类名访问;
    • import导入需要引用的包或类;
    • 所属包中的其他类可以通过类名直接访问;
    • 当需要访问多个同名类时,需要用全类名加以区分。

4.7.2 静态导入

  • 可以通过import static package.class.staticMethod/staticField的方式导入静态方法和静态域。

4.7.3 将类放入包中

  • 在源文件中,在定义类的代码之前加上package packageName的语句即可将类放入指定的包中。
  • 如果没有声明package语句,则源文件中的类被放置在一个默认包中。
  • 源文件所在目录必须与包的结构相同。编译器不会检查目录结构,但若目录结构错误,编译后的程序将无法执行。

补充:访问修饰符

修饰符本类同包下不同包下子类不同包下无关类
privateY   
defaultYY  
protectedYYY 
publicYYYY

4.10 类设计技巧

  • 一定要保证数据私有
  • 一定要对数据初始化
  • 不要在类中使用过多的基本类型,将若干相关的基本类型域抽取为一个新的类
  • 不是所有的域都需要独立的getter和setter
  • 将职责过多的类进行分解
  • 类名和方法名要能够体现它们的职责

 

转载于:https://www.cnblogs.com/leeqihe/p/9061174.html

相关文章:

  • FPGA之verilog静态数码管小程序
  • 2017 Multi-University Training Contest 2 hdu 6047
  • TSX数据Skysense PS最简操作流程和处理结果视频
  • Java开发者必备的六款工具
  • Android笔记(预安装APK)
  • 14种排序算法和PHP数组
  • TYVJ1340 送礼物
  • 多线程面试
  • 加样针
  • MVC项目中使用百度地图
  • BZOJ1293 [SCOI2009]生日礼物 离散化
  • spring中bean 的属性id与name
  • 今日练习题整理
  • Wincc flexable的局势视图的组态
  • shell和vi编辑器
  • 【跃迁之路】【641天】程序员高效学习方法论探索系列(实验阶段398-2018.11.14)...
  • axios请求、和返回数据拦截,统一请求报错提示_012
  • conda常用的命令
  • Fundebug计费标准解释:事件数是如何定义的?
  • java 多线程基础, 我觉得还是有必要看看的
  • MobX
  • session共享问题解决方案
  • spring security oauth2 password授权模式
  • Vue源码解析(二)Vue的双向绑定讲解及实现
  • 老板让我十分钟上手nx-admin
  • 力扣(LeetCode)22
  • 面试总结JavaScript篇
  • 扑朔迷离的属性和特性【彻底弄清】
  • 使用Tinker来调试Laravel应用程序的数据以及使用Tinker一些总结
  • 小程序滚动组件,左边导航栏与右边内容联动效果实现
  • 最简单的无缝轮播
  • 国内唯一,阿里云入选全球区块链云服务报告,领先AWS、Google ...
  • ​马来语翻译中文去哪比较好?
  • #Linux杂记--将Python3的源码编译为.so文件方法与Linux环境下的交叉编译方法
  • #控制台大学课堂点名问题_课堂随机点名
  • #每天一道面试题# 什么是MySQL的回表查询
  • #前后端分离# 头条发布系统
  • #我与Java虚拟机的故事#连载18:JAVA成长之路
  • (+4)2.2UML建模图
  • (13)[Xamarin.Android] 不同分辨率下的图片使用概论
  • (2)STM32单片机上位机
  • (2015)JS ES6 必知的十个 特性
  • (免费领源码)python+django+mysql线上兼职平台系统83320-计算机毕业设计项目选题推荐
  • (十一)JAVA springboot ssm b2b2c多用户商城系统源码:服务网关Zuul高级篇
  • (转)Spring4.2.5+Hibernate4.3.11+Struts1.3.8集成方案一
  • .Net 8.0 新的变化
  • .NET HttpWebRequest、WebClient、HttpClient
  • .NET 表达式计算:Expression Evaluator
  • .NET学习教程二——.net基础定义+VS常用设置
  • .Net语言中的StringBuilder:入门到精通
  • .Net中间语言BeforeFieldInit
  • @RequestMapping用法详解
  • @transactional 方法执行完再commit_当@Transactional遇到@CacheEvict,你的代码是不是有bug!...
  • []使用 Tortoise SVN 创建 Externals 外部引用目录
  • [BZOJ1040][P2607][ZJOI2008]骑士[树形DP+基环树]