java核心技术Note——第四章 对象与类

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/sha18751330807/article/details/83097322

面向对象程序设计概述

面向对象程序设计(简称OOP)是当今主流的程序设计泛型,它取代了面向过程的程序设计。面向对象的程序是由对象组成的,每个对象包含对用户公开的特定功能部分和隐藏的实现部分。
对于一些规模较小的问题,将其分解为面向过程的开发方式比较理想。而面向对象更加适用于解决较大规模的问题。

类是构造对象的模板,由类构造对象的过程称为创建类的实例。
封装是面向对象的三大原则之一,它从形式上将数据和行为组合在一个包中,并对对象的使用者隐藏了数据的实现方法。实现封装的关键在于绝不能让类中的方法直接地访问其他类的实例域,程序仅能通过对象的方法与对象数据进行交互。封装给予了对象“黑盒”的特征,这是提高重用性和可靠性的关键。这意味着一个类可以全面地改变存储数据的方式,只要仍旧使用同样的方法操作数据,其他对象就不会知道或介意发生的变化。
继承是另一原则,它同通过扩展一个类来建立另外一个新的类。在扩展一个类时,扩展后的新类具有所扩展类的全部属性和方法。在新类中,只需提供适用于新类的新方法和新数据域就可以了。

类之间的关系

常见关系有:

  • 依赖(“uses-a”)
  • 聚合(“has-a”)
  • 继承(“is-a”)
    …待补充
    需了解UML

对象

在Java中使用构造器构造新实例,构造器是一种特殊的方法,用来构造并初始化对象。例如:
Date birthday = new Date();左侧定义了一个对象变量birthday,它引用了new出来的Date对象。即表达式new Date()构造了一个Date类型的对象,并且它的值是对新创建对象的引用。这个引用存储在变量birthday中。
PS:

  • 一个对象变量并没有包含一个对象,而仅仅引用一个对象。
  • 局部变量不会自动地初始化为null,而必须通过调用new或将他们设置为null进行初始化。
  • Java对象存储在堆中

用户自定义类

  • Java中变量默认类型是default(不显式指出情况下)
  • 一个源文件中只能有一个public类,但可以有任意数目的非public类,且源文件名必须与public类的名字相同。
  • 构造器与类同名,并且伴随着new操作符的执行而被调用。注意:在创建对象数组时,new操作符只是创建数组空间,未调用对象构造器,如:
Employee[] staffs = new Employee[10];   // 此处new未调用构造器
staff[1] = new Emplolyee(...)// 此处new调用了对象的构造器
  • 类中的非静态方法都含有隐式参数this,它是调用方法的类对象。PS:静态方法可以直接通过类型调用,因此不存在隐式参数。
  • Java中所有的方法必须在类的内部定义,但并不表示他们都是内联方法。是否将某个方法设置为内联方法是java虚拟机的任务。

内联函数是在程序编译时,编译器将程序中出现的内联函数表达式用内联函数体替换,以目标代码的增加来换取时间的节省。

封装的优点

  1. 可以改变内部实现,除了该类的方法之外,不会影响其他代码。
  2. 更改器可以执行错误检查,然而直接对域进行赋值将不会进行这些处理。
    注意:编写访问器方法时,不要返回引用可变对象的变量。如:
class A{
  private  Date hireDay;
  A(...){...}   // 构造器,这里对hireDay进行了初始化

  // get访问器
  public Date getHireDay(){
    return hireDay;   // 返回可变对象的引用hireDay
  }
}
// 如果在其他类中访问这个类
A a = ...;
Date d = a.getHireDay();
d.setTime(...);   // 调用Date类的更改器方法

这样破坏了类的封装性,因为d和a.hireDay引用同一个对象,对d调用更改器就可以改变这个a对象的私有状态!因此,如果需要返回一个可变对象的引用,应该对它进行clone。对象clone是指存放在另一个位置上的对象副本。因此,上述访问器修改如下:

public Date getHireDay(){
   return hireDay.clone();   // 返回可变对象的克隆
}

基于类的访问权限

class Employee{
  ...
  public boolean equals(Employee other){
    return name.equals(other.name);
  }
}
// harry是一个Employee对象,我们一般会这样调用
if(harry.equals(boss))...

这个方法表明了Employee类的方法可以访问Employ类的任何一个对象的私有域,而非仅限于访问隐式参数的私有域。

私用方法

有些辅助方法,不应该成为共有接口的一部分,我们会将它们定义为私有方法。

final实例域

实例域定义为final时,构建对象时必须初始化这样的域,可以指定默认值或在构造器中初始化,后面的操作中不能在对final变量进行修改。
final修饰符大都应用于基本类型域(int, char…)或不可变类的域,例如String类。注意:不应该使用final修饰可变类,例如:private final Date hireDay;其中hreDay变量中的对象引用在构造之后不能被改变(即不能再指向其他对象),并不意味hireDay对象是一个常量,可以直接调用hireDay对象的更改器改变hireDay的域。如下:

public class Test {
    private final Date hireDay;

    public Test(int year, int month, int day){
        GregorianCalendar calendar = new GregorianCalendar(year, month-1, day);
        hireDay = calendar.getTime();
    }

    public static void main(String[] args) {
        Test t = new Test(2018, 8, 12);
        System.out.println(t.hireDay);
        Date date = new Date();
        t.hireDay.setTime(1000);
        System.out.println(t.hireDay);
    }
}

结果如下:

静态域与静态方法

静态域

  • 静态域的修饰符为static,静态域(即静态变量)是属于类的,它不属于任何独立的对象。而每一个对象对于所有的实例域都有自己的一份拷贝。
  • 静态变量使用的比较少,但静态常量却使用得比较多,且一般声明为公有静态常量(因为是final即使是公有域也不会被修改)。如:public static final PrintStream out = ...

静态方法

将方法定义为static,它就成为了静态方法。

  • 静态方法通过类调用,没有隐式参数(虽然可以通过对象调用,但易误解方法类型,因此建议使用类名调用);
  • 静态方法不能访问类的实例域,只能访问类静态域;
    如下情况考虑使用静态方法:
  1. 一个方法不需要访问对象状态,其所需参数都是通过显示参数提供;
  2. 一个方法只需要访问类的静态域;

补充:

  • 静态方法还有一个常见用途是定义工厂方法。
  • main方法必须定义为静态,因为程序启动时还没有任何一个对象,静态的main方法将执行并创建程序所需的对象。
  • 每个类可以有一个main方法,这有利于进行单元测试。

方法参数

Java语言采用按值调用,即方法得到的是所有参数值的一个拷贝,它不能修改实参内容。
然而方法参数共有两种类型:基本数据类型和对象引用。对于基本数据类型,方法不能修改基本数据类型形参对应的实参内容。而对象引用作为参数参数时,就不同了。此时方法得到的是对象引用的拷贝,对象引用及其他的拷贝同时引用同一个对象。因此通过调用更改器改变对象参数的状态。
总结:

  • 一个方法不能修改一个基本数据类型的参数;
  • 一个方法可以改变一个对象参数的状态;
  • 一个方法不能让对象参数引用一个新的状态(例如:交换对象的方法);如:
// 此函数的确交换了形参x,y指向的对象,但并未改变实参的指向
public static void swap(Employee x, Employee y){
  Employee temp = x;
  x = y;
  y = temp;
}
// 调用方法
Employee a = ...
Employee b = ...
swap(a, b);

对象构造

  • 一个类可以有多个构造器,这种特征是重载。
    补充:函数相同但参数个数或类型不同就是重载,注意:重载不包括返回类型。要完整的描述一个方法需要指出方法名以及参数类型,它们组成了方法的签名。

  • 如果在构造器中未对域进行初始化,它们会自动初始化为默认值:数值为0,布尔值为false,对象引用为null。但方法中的局部变量不会自动初始化为默认值。

  • 仅当类没有提供任何构造器时,系统会提供一个默认的无参构造器,无参构造器将所有实例域都设置为默认值。ps:建议自行提供无参构造器。

  • 由于类的构造器方法可以重载,所以可以采用多种形式设置类的实例域的初始状态。也可以在声明域时中,直接初始化,初始值可以是常量或者方法返回值。

  • 构造器方法中,第一个语句形如this(…),它将调用同一个类的另一个构造器。

  • 除了在构造器中设置值和在声明中赋值,初始化块机制也可以初始化数据域
    调用构造器的具体处理步骤:

    1. 所有数据域被初始化为默认值
    2. 按照在类声明中的次序,依次执行所有的初始化语句和初始化块
    3. 如果构造器第一行调用了第二个构造器,则执行第二个构造器主体
    4. 执行构造器主体
      可以设置静态的初始化块来初始化静态变量,如:
    static
    {
      Random generator = new Random();
      // nextId是静态域
      nextId = generator.nextInt(10000);
    }
    

    ps:在第一次加载类时就会进行静态域的初始化。静态初始语句以及静态初始化快都将按照类定义中的顺序执行。

    • Java不支持析构器
    • 包机制的主要作用是确保类名的唯一性,package与import语句类似于c++中的namespace和using指令。要想将一个类放入包中,就必须将包的名字放在源文件的开头。如package com.horstmann.corejava; 另外,嵌套的包之间没有任何关系 。

    类设计技巧:

    1. 一定要保证数据私有
    2. 一定要对数据初始化
    3. 不要在类中使用过多的基本类型,即用其他的类替代多个相关的基本类型的使用,这样能方便理解和修改
    4. 不是所有的域都需要独立的域访问器和域更改器
    5. 将指责过多的类进行分解
    6. 类名和方法名要等体现它们的指责

猜你喜欢

转载自blog.csdn.net/sha18751330807/article/details/83097322