02-对象与类

2019-12-10  本文已影响0人  bin_lifecycle

1.面向对象的程序设计:oop

1.1 面向对象关注的是数据本身

类:构造对象的蓝图或模板,类构造 ( construct ) 对象的过程称为创建类的实例 ( instance ) .

面向过程与面向对象的程序设计对比:

面向过程与面向对象的程序设计对比

区别:

  开发程序的时候,面向过程比较理想化,一个程序如果需要2000个过程来对一组全局数据进行操作,那么出错就需要在这2000个过程中找,但是如果将其分解为100个类,每个类20个方法, 后者更易于程序员掌握 , 也容易找到bug,假设给定对象的数据出错了, 在访问过这个数据项的20 个方法中查找错误要比在 2000 个过程中查找容易得多。

1.2 对象到的3个主要特性:

  1.对象的行为是用可调用的方法定义的。(行为即方法)
  2.每个对象都保存着描述当前特征的信息。 这就是对象的状态 。 对象的状态可能会随着时间而发生改变, 但这种改变不会是自发的 。 对象状态的改变必须通过调用方法实现( 如果不经过方法调用就可以改变对象状态, 只能说明封装性遭到了破坏 )
  3 对象的状态并不能完全描述一个对象 。每个对象都有一个唯一的身份 ( identity )。 例如, 在一个订单处理系统中 , 任何两个订单都存在着不同之处 ’即使所订购的货物完全相同也是如此。 需要注意作为一个类的实例, 每个对象的标识(内存空间地址)永远是不同的 , 状态常常也存在着差异

小结:对象的3大特性:行为(方法),状态(字段),标识(唯一标识,内存空间地址),对象的这些关键特性在彼此之间相互影响着
例如:对象状态影响影响行为
  如果一个订单 “ 已送货 ” 或 “ 已付款 ” , 就应该拒绝调用具有增删订单中条目的方法。 反过来 ,如果订单是 “ 空的 ” , 即还没有加人预订的物品 , 这个订单就不应该进人 “ 已送货 ” 状态 。

1.3 类之间的关系:

依赖 ( dependence ) , 即 “uses - a ” 关系,如果一个类的方法操纵另一个类的对象, 我们就说一个类依赖于另一个类

聚合 ( aggregation ) , 即 “ has - a ” 关系 , 聚合关系意味着类 A 的对象包含类 B 的对象

继承 ( inheritance ) , 即 “ is - a ” 关系, 一般而言 , 如果类 A 扩展类 B , 类 A 不但包含从类 B 继承的方法, 还会拥有一些额外的功能,java中只有单继承关系

2.使用预定义类
对象与对象变量(引用):

对象引用指向对象
2个对象引用指向同一个对象

在 Java 中, 任何对象变量的值都是对存储在另外一个地方的一个对象的引用 。 new 操作符的返回值也是一个引用.

3.用户自定义类

隐式参数没有出现在方法声明中。在每一个方法中,关键字 this 表示隐式参数

4.静态域与静态方法,static关键字

说说构造器的一些缺陷:

  • 构造器无法命名。 构造器的名字必须与类名相同,当有多个重载,参数类型、返回值不同等多种情况下(比如Date函数重载),对于使用者来说可能阅读要查阅每个参数的意义了才能不调用错误的构造器。
  • 当使用构造器时, 无法改变所构造的对象类型

5.方法参数

将参数传递给方法 ( 或函数 ) 的一些专业术语:

一个方法可以修改传递引用所对应的变量值 , 而不能修改传递值调用所对应的变量值。

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

Java 中方法参数的使用情况:

对象的引用指向不会被方法修改

图中拷贝的是引用的值
交换引用后,原来拷贝的引用依然指向原来的对象

也就是说,java中的引用参数在传递时,传递过来的只是该引用的拷贝值,就算对这个拷贝值进行操作,也不会改变该引用原来的指向。

这里直接就说明了,为什么java是值传递而不是引用传递了,因为java传递引用参数时只是传递了该引用的拷贝值而已,并没有影响原来的引用指向,所以方法不可能让原来的引用指向一个新的对象

6.对象构造

也就是说,一个方法的签名确定下来,返回值就只能是一种类型了,不可能有多个。

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

当调用 new Employee ( 60000 ) 时 , Employee ( double ) 构造器将调用 Employee ( String, double )构造器。采用这种方式使用 this 关键字非常有用, 这样对公共的构造器代码部分只编写一次即可。

实际上, Java 还有第三种机制 , 称为初始化块 ( initializationblock ) 。 在一个类的声明中 ,可以包含多个代码块。 只要构造类的对象 , 这些块就会被执行,

class Employee{
    private static int nextld ;
    private int id ;
    private String name ;
    private double salary ;
    // object initialization block
    {
      id = nextld ;
      nextld + + ;
    }
    public Employee (String n , double s){
      name = n ;
      salary = s ;
    }

    public Employee (){
      name = "";
      salary = 0 ;
    }
}

在这个示例中, 无论使用哪个构造器构造对象 , id 域都在对象初始化块中被初始化 。 首先运行初始化块, 然后才运行构造器的主体部分
这种机制不是必需的,也不常见 。 通常会直接将初始化代码放在构造器中。

调用构造器的具体处理步骤:

在类第一次加载的时候 , 将会进行静态域的初始化。 与实例域一样 , 除非将它们显式地设置成其他值,否则默认的初始值是 0 、 false 或 null 。 所有的静态初始化语句以及静态初始化块都将依照类定义的顺序执行 。

//静态初始化块,对静态域进行初始化
static {
  Random generator = new Random () ;
  nextld = generator . nextlnt ( 10000 ) ;
}

代码清单:

ConstructorTest

import java.math.BigDecimal;
import java.util.Random;

/** 类初始化测试
 * Create by wangbin
 * 2019-12-11-14:56
 */
public class ConstructorTest {
    public static void main(String[] args) {
        Employee[] employees = new Employee[3];

        employees[0] = new Employee(new BigDecimal(1000));
        employees[1] = new Employee("小马",new BigDecimal(2000));
        employees[2] = new Employee();
        for (Employee e : employees){
            System.out.println(e.toString());
        }
    }
}

Employee

import java.math.BigDecimal;
import java.util.Random;

/** 构造器执行顺序 静态代码块,初始化代码块,构造器主体与代码位置的先后顺序无关
 * Create by wangbin
 * 2019-12-11-14:33
 */
public class Employee {
    private static int nextId;
    private Integer id;
    private String name;
    private BigDecimal salary;

    //静态初始化化块
    static {
        System.out.println("--静态代码块执行--");
        Random generator = new Random();//构造一个新的随机数生成器
        nextId = generator.nextInt(1000);//返回一个0到n-1的随机数
    }

    //即使在类的后面定义 , 仍然可以在初始化块中设置域 。
    // 但是 , 为了避免循环定义 ,不要读取在后面初始化的域
    //初始化代码块,建议将初始化块放在域定义之后,
    {
        id = nextId;
        nextId++;
        System.out.println("--初始化块代码执行--");
    }
    
    public Employee() {
        System.out.println("--无参构造器执行--");
    }

    public Employee(BigDecimal salary) {
        this.salary = salary;
    }


    public Employee(String name, BigDecimal salary) {
        this.name = name;
        this.salary = salary;
    }

    public Employee(Integer id, String name, BigDecimal salary) {
        this.id = id;
        this.name = name;
        this.salary = salary;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public BigDecimal getSalary() {
        return salary;
    }

    public void setSalary(BigDecimal salary) {
        this.salary = salary;
    }
}

main方法执行结果:

image.png

小结:造器执行顺序 :静态代码块>初始化代码块>构造器主体,与代码位置的先后顺序无关

可以为任何一个类添加 finalize 方法。finalize 方法将在垃圾回收器清除对象之前调用。在实际应用中,不要依赖于使用finalize 方法回收任何短缺的资源 , 这是因为很难知道这个方法什么时候才能够调用。如果某个资源需要在使用完毕后立刻被关闭 , 那么就需要由人工来管理。 对象用完时 ,可以应用一个 close 方法来完成相应的清理操作。

7 包
Java 允许使用包 ( package ) 将类组织起来。 借助于包可以方便地组织自己的代码 , 并将自己的代码与别人提供的代码库分开管理。

 Date time = new Date();

注意:发生命名冲突的时, 就需要注意包的名字了 。 例如 ,
java .util 和 java . sql 包都有日期 ( Date ) 类 。 如果在程序中导入了这两个包 :

import java . util . * ;
import java . sql . * ;

在程序使用 Date 类的时候 , 就会出现一个编译错误 :

Date today ; / / Error java . util . Date  or  java . sql . Date ?

此时编译器无法确定程序使用的是哪一个 Date 类。 可以采用增加一个特定的import 语句来解决这个问题 :

import java . util . * ;
import java . sql . * ;
import java . util . Date ; //导入util包下的Date类

如果这两个 Date 类都需要使用, 又该怎么办呢 ?
答案是,在每个类名的前面加上完整的包名

java.util.Date deadline = new java. util . Date ( ) ;
java.sql.Date today = new java . sql . Date ( . . . ) ;

在包中定位类是编译器 (compiler ) 的工作。 类文件中的字节码肯定使用完整的包名来引用其他类。

out.println ( " Goodbye , World ! " ) ; // System.out
exit(9) ; // System.exit

另外, 还可以导入特定的方法或域
import static java.lang.System.out;

package com.horstiann.corejava ;
public class Employee{
}

8 类路径

  类存储在文件系统的子目录中 。类的路径必须与包名匹配。 类文件也可以存储在 JAR ( Java 归档 ) 文件中 。 在一个 JAR文件中, 可以包含多个压缩形式的类文件和子目录, 这样既可以节省又可以改善性能 。

为了使类能够被多个程序共享, 需要做到下面几点:

9 文档注释

10 类设计技巧

private String street ;
private String city ;
private String state ;
private int zip ;

  不可变类是指创建该类的实例后,该实例的实例变量是不可改变的。java中已有类,例如Double和String等。
  LocalDate 类以及 java.time 包中的其他类是不可变的-----------没有方法能修改对象的状态。类似 plusDays 的方法并不是更改对象, 而是返回状态已修改的新对象 。
  更改对象的问题在于,如果多个线程试图同时更新一个对象, 就会发生并发更改 。 其结果是不可预料的。 如果类是不可变的 , 就可以安全地在多个线程间共享其对象 。
  因此, 要尽可能让类是不可变的 , 这是一个很好的想法 。 对于表示值的类 , 如一个字符串或一个时间点, 这尤其容易 。 计算会生成新值 ,而不是更新原来的值。

上一篇 下一篇

猜你喜欢

热点阅读