zoukankan      html  css  js  c++  java
  • Java核心技术 对象与类

    对象与对象变量:

    要想使用对象,就必须首先构造对象,并指定其初始状态,然后,对对象应用方法。

    在Java中,使用构造器构造新实例。构造器是一种特殊的方法,用来构造并初始化对象。

    在实际开发中,通常需要将对象存放在一个变量中来达到多次使用的目的。

    在这里,birth指向了Date对象的存储的地方的引用。

    Date birthday = new Date();

    在对象与对象变量直接存在着一个重要的区别,例如:

    Date deadline;  //deadline dosen't refer to any object;

    定义了一个对象变量,它可以引用Date类型的对象,但是,一定要认识到:变量deadline不是一个对象,实际上也没有引用一个对象。并且不能用任何Date方法应用与这个变量上

    s = deadline.toString(); // not yet

    在这里,将会产生编译错误。

    首先,必须初始化变量deadline,可以用新构造的对象初始化,也可以引用一个已存在的变量。

    deadline = new Date() //新构造对象
    //
    //或者
    Date birthday = new Date();
    deadline = birthday;  //引用一个已存在对象  

    当引用一个已存在的变量时,两个变量引用同一个对象:

    在Java开发中,一定要认识到,一个对象变量并没i月实际包含一个对象,而仅仅使用一个对象,任何对象变量的值都是对存储在另外一个地方的一个对象的引用。

     

     

     

    构造器:

    首先来看一个Employee类:

    class Employee
    {
    // instance fields
    private String name;
    private double salary;
    private LocalDate hireDay;
    // constructor 构造器
    public Employee(String n, double s, int year, int month, int day)
    {
    name = n;
    salary = s;
    hireDay = LocalDate.of(year, month, day);
    }
    // a method
    public String getName()
    {
    return name;
    }
    // more methods
    . . .
    }

    该类的构造器与类同名,在构造Employee类的对象时,构造器会运行,并将实例初始化为所希望的状态。

    例如,当使用下面代码实例化时:

    new Employee("James Bond", 100000, 1950, 1, 1);

    将会把实例域设置为:

    name = "James Bond";
    salary = 100000;
    hireDay = LocalDate.of(1950, 1, 1); // January 1, 1950

    构造器与其他方法有一个区别,即构造器伴随着new操作符被调用并且没有返回类型,而不能通过一个对象调用构造器,如下将会产生编译错误。

    james.Employee("James Bond", 250000, 1950, 1, 1) // ERROR

    重载:

    一个类可以拥有多个构造器。

    如果在一个类中,拥有多个方法,每个方法具有相同的名字,不同的参数,便产生了重载。编译器必须挑选出具体执行那个方法。

    通常,多个具有相同名字不同参数的方法,是通过它们的签名来辨识的。

    什么是方法的签名?

    一个方法的签名即为它的方法名称,以及参数类型。注意,返回类型并不属于一个方法的签名!也就是说,不能具有相同名字,相同参数,而返回类型不同的两个方法。

     

    默认域初始化:

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

    这是域与局部变量的主要不同点。局部变量必须明确地初始化。

     

    无参数的构造器:

    很多类都包含一个无参数的构造函数,对象由构造函数创建时,状态会被设置为适当的默认值,如下为Employee类的无参数构造函数:

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

    如果一个类没有编写构造器的话,那么系统就会提供一个无参数构造器。

     

    调用另一个构造器:

    如果构造器的第一个语句形如this(...),这个构造器将会调用同一个类的另一个构造器,下面是一个典型的例子:

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

    当调用new Employee(60)时,Employee(double)构造器将调用Employee(String,double)构造器。

     

    初始化块:

     通常,初始化数据域有三种方法,一种是在构造器设置,一种是在声明中赋值,还有一种是初始化话块,只要构造类的对象,这些块就会被执行。

    class Employee
    {
    private static int nextId;
    private int id;
    private String name;
    private double salary;
    // object initialization block  初始化块
    {
    id = nextId;
    nextId++;
    }
    public Employee(String n, double s)
    {
    name = n;
    salary = s;
    }
    public Employee()
    {
    name = "";
    salary = 0;
    }
    . . .
    }

     由于初始化数据域有多种途径,所以可能会有些混乱,下面是调用构造器的具体处理步骤:

    1.所有数据域被初始化默认值。

    2.按照类声明中出现的次序,依次执行所有域初始化语句和初始化块。

    3.如果构造器第一行调用了第二个构造器,则执行第二个构造器的主体。

    4.执行这个构造器的主体。

     

    不要编写返回引用可变对象的访问器方法:

    例如:

    class Employee
    {
    private Date hireDay;
    . . .
    public Date getHireDay()
    {
    return hireDay; // Bad
    }
    . . .
    }

     

    这样会破坏封装性,即可以不通过Hireday的设置器方法就能改变Hireday,具体如下代码:

    Employee harry = . . .;
    Date d = harry.getHireDay();
    double tenYearsInMilliSeconds = 10 * 365.25 * 24 * 60 * 60 * 1000;
    d.setTime(d.getTime() - (long) tenYearsInMilliSeconds);
    // let's give Harry ten years of added seniority

    出错的原因即为d和harry.hireDay引用同一个对象,参见图:

    实际上,返回对象应对其进行克隆,并返回该克隆对象。

     

     

     

     

    final实例域:

    可以将实例域定义为final,构建对象时必须初始化这样的域,也就是说,确保在每一个构造器执行之后,这个域的值被设置,并且在后面的从早中,不能够在对它进行修改。

    通常,不推荐是哦那个final修饰符应用于可变的类,如下:

    private final Date hiredate;

    这仅仅意味着,存储在hiredate变量的对象引用不可变,也就是说永远指向同一个对象引用,但任何方法都可以对hiredate引用的对象调用setTime更改器来改变该对象。

     

    静态域与静态方法:

    如果将域定义为static,每个类中只有一个这样的域。而每一个对象对于所有的实例域却都有一份自己的拷贝。静态域是所有对象共享的,如这里给Employee类添加一个实例域id和一个静态域nextId:

    class Employee
    {
    private static int nextId = 1;
    private int id;
    . . .
    }

    换句话说,如果有1000个Emplyee对象,那么就有1000个id实例域,但却只有一个nextId静态域。即使没有实例对象时,静态域也是存在的,它是属于类,而不是属于对象。

    静态方法同理。

     

    方法参数:

     Java中的参数是一种值引用。

    首先,观察一下基本数据类型(数字、布尔值)。

    public static void tripleValue(double x) // doesn't work
    {
    x = 3 * x;
    }
    double percent = 10;
    tripleValue(percent);

    当我们将percent作为参数给一个方法时,经过执行后,percent本身并没有改变,这个很容易理解。

    接下来看一下对象引用:

    public static void tripleSalary(Employee x) // works
    {
    x.raiseSalary(200);
    }

    当调用如下语句时:

    harry = new Employee(. . .);
    tripleSalary(harry);

    具体的操作过程如下:

    1.x被初始化harry值的拷贝,这是一个对象的引用。

    2.raiseSalary方法应用于这个对象的引用,x和harry同时引用的那个Employee对象的薪金提高了200%。

    3.方法结束后,参数变量x不再使用。当然,对象变量harry继续引用那个薪金增3倍的对象。

    看到这里,或许会认为Java的对象参数是引用调用,实际上,并不是。

    依然是按值调用,传给tripleSalary函数的实际上是harry值的拷贝,而harry值指向了一个对象引用,所以x也指向了该引用,所以才会引起变化。

    可以通过下面这个例子得出结果:

    public static void swap(Employee x, Employee y) // doesn't work
    {
    Employee temp = x;
    x = y;
    y = temp;
    }

    如果Java是按引用调用的话,那么执行下面语句后,a与b应该互换。

    Employee a = new Employee("Alice", . . .);
    Employee b = new Employee("Bob", . . .);
    swap(a, b);
    // does a now refer to Bob, b to Alice?

    但在测试中,并没有互换,所以传入的实际上是a,b的拷贝,也就是一个对象引用,这个方法交换的是这两个拷贝。

    最终,函数结束时,变量x,y被丢弃了,原来的ab对象变量依然引用之前的对象。

  • 相关阅读:
    今天才知道还有这个地址 MS 的
    提供一个在线翻译,多语言互译的好网址.
    程序员有多少读过《人性的弱点》?项目经理呢?
    [下载]ASP.NET开发技巧集锦
    真不好意思,关于小猪妹(妖精)的
    poj 1330 LCA问题 (LCA问题转化为RMQ || tarjan算法)
    poj 2688 (bfs+dfs)
    Codeforces Round #143 (Div. 2) B
    poj 1160 dp
    poj 1032(整数拆分)
  • 原文地址:https://www.cnblogs.com/Darlietoothpaste/p/6699880.html
Copyright © 2011-2022 走看看