Java 面向对象:对象、字段和方法

禁止码迷,布布扣,豌豆代理,码农教程,爱码网等第三方爬虫网站爬取!

面向对象编程

Java 肯定也是个面向对象的编程语言啦,刚好 Python 也是,所以这里关于不多讲面向对象编程的事情了,直接左转 Python 面向对象编程

私有化

关键字 public 被称之为访问修饰符,用于控制程序的其他部分对这段代码的访问级别。public 定义的方法表示任何类的任何方法都可以调用,public 定义的字段语序程序中任何方法对其进行修改和读取。
但是使用 public 字段会破坏封装,我们会经常需要让字段只能被所在的类自身来调用,这时类中的状态就可以用 private 来定义。

private String name;
private double salary;
private int id;

对于方法来说,一个方法的实现可以被拆分出几个辅助方法,这些辅助方法对于外界来说不应该成为公共接口的一部分。此时也可以用 private 来定义,使得方法被定义为私有方法。这种做法还有个好处,当你原来的方法重新设计而不需要辅助方法时,由于是私有方法,你不用担心这个方法会被其他程序所需要,因此可以直接删除。如果它是共有地的方法,即使你本人不再使用,也不能确定在其他程序中也不再使用。

字段访问器/更改器

在编写类的时候,如果我们需要获得或设置实例字段的值,最好提供如下内容:

  1. 一个私有的数据字段;
  2. 一个公有的字段访问器方法;
  3. 一个公有的字段修改器方法;

例如对于 Employee 类有 name 和 salary 2 个字段,则我们就需要为这 2 个字段提供访问器修改器方法。

class Employee {
      private String name;
      private double salary;

      //salary 字段访问器
      public double getSalary() {
            return salary;
      }
      //name 字段访问器
      public String getName() {
            return name;
      }
      //salary 字段修改器
      public void setSalary(double salary) {
            this.salary = salary;
      }
      //name 字段修改器
      public void setName(String name) {
            this.name = name;
      }
}

这么做是很有价值的,首先对于只读字段,把其设置为 private 就可以保护它不收外界的破坏,例如 Employee 类的 name 字段。这个时候当外界想要访问时,就得使用 getter 方法()。如果不是只读字段,setter/getter 方法也需要存在。如果类之中的某个属性出现了错误,且其设置为为 pubilc,则我们无法得知是哪个方法把这个属性搞坏了,因为任意类的任意方法都能改这个属性。而如果属性统统设置为 private,我们就能很快地定位是哪些方法动了这个属性,使得调试工作变得容易开展。这个时候这些属性就不能乱动了,只能用 setter/getter 方法来对字段进行访问或更改。

声明局部变量

如果可以用变量的初始值推导对象类型,则可以用 var 关键字声明局部变量,此时不用指定类型。例如下面 2 行代码具有相同含义,使用 var 关键字可以不用谢类型名。

Employee staff = new Employee[3];
var staff = new Employee[3];

var 关键字只能用于方法中的局部变量,参数和字段的类型必须声明。var 关键字只能用在 Java 10 之后的版本。

显/隐式参数

例如这个方法,它涉及到 2 个参数,一个是位于方法名后面括号中的显式参数。另一个则是隐式参数,它是方法名之前的对象,是方法调用的目标或接收者。

public void raiseSalary(double byPercent){
      double raise = salary * byPercent / 100;
      salary += raise;
}

隐式参数没有出现在方法声明中,可以使用 this 关键字指示,用 this 来写方法可以将实例字段与局部变量明显地区分开。

public void raiseSalary(double byPercent){
      double raise = this.salary * byPercent / 100;
      this.salary += raise;
}

final 实例字段

若实例字段定义为 final,则字段就必须在构造时初始化,并且之后不能再修改该字段。例如:

private final String name;

对于类型为基本类型或不可变类(类中的所有方法都不会改变其对象)的字段,使用 final 很合适。

静态

静态字段

将字段定义为 static 的字段称之为静态字段,该字段属于整个类而不是单个对象,因此每个类中该字段只有一个。对于非静态字段来说,每个对象都有一个该字段的副本。例如需要为同一个类的每个对象都进行编号,可以定义一个静态字段:

private static int nextId = 1;
private int id;

使用下面的方法就可以进行动态分配,注意 nextId 对于这个类来说是唯一的,因此可以给出不会重复的 id。

public void setId(){
      id = nextId;      //set id to next available id
      nextId++;
}

静态常量

当 static 关键字和 final 关键字同时用来定义字段时,该字段就是个静态常量,例如 Math 类定义静态常量 PI。

public static final double PI = 3.14159265358979323846;

静态方法

当方法被定义为静态方法时,该方法不在对象上执行(或者说没有 this 参数)。例如 Math 类的 pow 方法,该方法实现幂运算是不需要 Math 对象。
由于不在对象上执行,因此静态方法不能访问实例字段,但是可以访问静态字段。例如:

public static int getNextId(){
      return nextId; // returns static field
}

因此当方法不需要访问对象状态,或方法只需要访问类的静态字段时,可以将方法定义为静态方法。静态方法可以用对象调用,但为了不产生混淆,一般用类名来调用静态方法。

main 方法

main 方法不对任何对象进行操作,因此它也是个静态方法,静态的 main 方法将执行并构造程序所需的对象。

public static void main(String[] args){
      String greeting = "Hello,world!";
      System.out.println(greeting);
}

方法参数

按值调用表示方法接收调用者提供的值,引用调用表示方法接受的是调用者提供的变量地址。方法可以修改按引用传递的变量的值,而不能修改按值传递的变量的值。
Java 采用按值调用,也就是说方法得到的是所有参数值的一个副本,并不能修改传递给他的参数。不过方法可以改变对象参数状态,这是因为方法虽然得到的是对象引用的副本,但是原来的对象引用和副本的引用都是引用了同一个对象。也就是说方法不能修改基本数据类型的参数,但是可以改变对象参数的状态

构造器

重载

如果多个方法有相同的名字但是参数不同,就会出现重载,编译器必须挑选出具体要使用哪个方法。Java 会根据各个方法首部中的参数和特定方法中调用的值的类型进行重载解析,通过匹配选择正确的方法,若编译器没有匹配成功就会报错。例如

public Employee(String n, double s){
      name = n;
      salary = s;
}

public Employee(double s){
      this("Employee #" + nextId, s);
}

Java 允许重载任何方法,因此每个方法都必须指定方法名和类型。

无参构造器

如果在构造器中没有显式地为字段设置初始值,呢字段会自动赋予默认值:数值为 0、bool 值为 false、对象引用为 null。这或许并不是一个好机制,因为如果没有明确字段的初始化,就会影响代码的可读性。
当一个类没有编写构造器时,Java 会提供一个无参构造器,该构造器所有的实例字段会被设置为默认值。例如:

private int id;
private String name = ""; // instance field initialization
private double salary;

public Employee(){
      // name initialized to ""--see above
      // salary not explicitly set--initialized to 0
      // id initialized in initialization block
}

当类中没有任何构造器时,才会得到一个无参构造器。若类中提供了至少一个构造器,但是没有提供无参数的构造器,则构造对象是不提供参数就会产生错误。

显式字段初始化

在类的定义时,直接为字段设置一个有意义的初始值是个很好的做法。例如:

private String name = ""; // instance field initialization

初始值不一定是常量值,也可以利用方法调用初始化一个字段。例如:

private int id = assignId();
private static int nextId = 1;

private static in assignId(){
      int id = nextId;
      nextId++;
      return id;
}

调用构造器

如果构造器的第一个语句为 this(……),则该构造器将调用同一个类的另一个构造器。

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

初始化块

在一个类的声明中可以包括多个代码块,当类构造对象时,这些代码块会被自动执行。例如:

private int id;
private String name = ""; // instance field initialization
private double salary;
  
// object initialization block
{
      id = nextId;
      nextId++;
}

如果静态字段需要用复杂的代码初始化,可以使用静态代码块,例如:

private static int nextId;
  
// static initialization block
static{
      var generator = new Random();
      // set nextId to a random number between 0 and 9999
      nextId = generator.nextInt(10000);
}

类第一次被加载时,静态字段的初始化就会被自动进行。

析构器

因为 Java 的资源回收时候自动完成的,因此 Java 不支持析构器。但是如果调用了内存以外的资源,例如文件,则就需要用 close 方法将资源关闭。

类的设计

设计一个类的一些建议如下:

  1. 保证数据私有,不要破坏封装性;
  2. 数据都要初始化,不应当以来 Java 的默认值;
  3. 不要在类中使用过多的基本类型,而是用其他类来替换;
  4. 并不是所有的字段都需要单独的字段访问器和字段更改器;
  5. 分解有过多职责的类,例如类可以明显地分为两个简单的类;
  6. 类名和方法要尽量体现它们的职责;
  7. 优先使用不可变类,即没有方法可以修改对象状态的类。

参考资料

《Java 核心技术 卷Ⅰ》,[美]Cay S.Horstmann 著,林琪 苏钰涵 等译,机械工业出版社

posted @ 2020-08-06 00:09  乌漆WhiteMoon  阅读(587)  评论(0编辑  收藏  举报