Page tree

Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

英文标题【Object and Classes】

Java 是一种面向对象的语言。作为一个面向的语言,Java 具有面向对象的特性,Java 能够支持下面的一些基本概念 −

  • 多态(Polymorphism)
  • 继承(Inheritance)
  • 封装(Encapsulation)
  • 抽象(Abstraction)
  • 类(Classes)
  • 对象(Objects)
  • 实例(Instance)
  • 方法(Method)
  • 消息传递(Message Passing)

在这个章节中,我们将会重点进入 Java 的一个概念 - 类和对象。

  • 对象(Object )− 对象具有状态和行为。例如:一条狗具有状态 - 颜色,名字,品种;同时还具有行为 - 摇动尾巴,叫唤,吃东西。一个对象就是一个类的实例。

  • 类(Class )− 一个类可以定义一些模板或蓝图,这些用来描述一类对象的状态和行为。

Panel

本页中的内容:

Table of Contents
maxLevel2
minLevel2

相关页面:

我们以狗为例子,在这个例子中。我们定义了一个狗的类,这个用于描述狗的定义。

在实际中,我们可能会遇到各种不同类型的狗,这些类型的狗都会有自己的状态和行为,我们成为对象。

例如,我们现在要描述一条拉布拉多犬,那么我们可以用狗这个类创建拉布拉多犬这个对象,在 Java 中使用关键字 new 进行创建。

Java 中的对象

现在让我们深入了解什么是对象。看看周围真实的世界,会发现身边有很多对象,车,狗,人等等。所有这些对象都有自己的状态和行为。

拿一条狗来举例,它的状态有:名字、品种、颜色,行为有:叫、摇尾巴和跑。

如果你拿软件对象来对比实际世界中的对象,你会发现他们直接有非常大的相似性。

软件对象也有状态和行为。软件对象的状态就是属性,行为通过方法体现。

在软件开发中,方法操作对象内部状态的改变,同时对象和对象直接的通信也是通过方法来实现的。

Java 中的类

一个类可以被看做是从一个对象中创建出来的蓝图。

请参考下面一个方法的示例。

例如

Code Block
languagejava
public class Dog{
	String breed;
	int age;
	String color;

	void barking() {
	}

	void hungry() {
	}

	void sleeping() {
	}
}


一个对象可以包含有下面的一些变量类型。

  • 局部变量(Local variables − 变量被定义在方法或者构造方法中,这种变量被称为局部变量。变量声明和初始化都是在方法中,方法结束后,变量就会自动销毁。

  • 实例变量(Instance variables)— 这个变量也被称为成员变量。这个变量定义在类中,方法体之外的变量。这种变量在创建对象的时候实例化。成员变量可以被类中方法、构造方法和特定类的语句块访问。

  • 类变量(Class variables — 这个变量也被称为静态变量。类变量也声明在类中,方法体之外,但必须声明为static类型。

一个类可以拥有多个方法,在上面的例子中:barking()、hungry() 和 sleeping() 都是Dog类的方法。

下面是 Java 语言中有关类的更多一些重要概念。

实例变量静态变量(类变量)

实例变量声明在一个类中,但在方法、构造方法和语句块之外;
当一个对象被实例化之后,每个实例变量的值就跟着确定;
实例变量在对象创建的时候创建,在对象被销毁的时候销毁;
实例变量的值应该至少被一个方法、构造方法或者语句块引用,使得外部能够通过这些方式获取实例变量信息;
实例变量对于类中的方法、构造方法或者语句块是可见的。一般情况下应该把实例变量设为私有。通过使用访问修饰符可以使实例变量对子类可见;
实例变量具有默认值。数值型变量的默认值是0,布尔型变量的默认值是false,引用类型变量的默认值是null。变量的值可以在声明时指定,也可以在构造方法中指定;
实例变量可以直接通过变量名访问。但在静态方法以及其他类中,就应该使用完全限定名:ObejectReference.VariableName。

类变量也称为静态变量,在类中以static关键字声明,但必须在方法构造方法和语句块之外。
无论一个类创建了多少个对象,类只拥有类变量的一份拷贝。
静态变量除了被声明为常量外很少使用。常量是指声明为public/private,final和static类型的变量。常量初始化后不可改变。
静态变量储存在静态存储区。经常被声明为常量,很少单独使用static声明变量。
静态变量在程序开始时创建,在程序结束时销毁。
与实例变量具有相似的可见性。但为了对类的使用者可见,大多数静态变量声明为public类型。
默认值和实例变量相似。数值型变量默认值是0,布尔型默认值是false,引用类型默认值是null。变量的值可以在声明的时候指定,也可以在构造方法中指定。此外,静态变量还可以在静态语句块中初始化。
静态变量可以通过:ClassName.VariableName的方式访问。
类变量被声明为public static final类型时,类变量名称最好使用大写字母。如果静态变量不是public和final类型,其命名方式与实例变量以及局部变量的命名方式一致。

请参考下面的示例代码:

示例代码 (GitHub

Expand
title单击此处查看 testStaticVariableChange 源代码...
Code Block
languagejava
package com.ossez.lang.tutorial.tests;

import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import junit.framework.TestCase;

/**
 * Object of VariableOssez
 * 
 * @author YuCheng
 *
 */
class OssezVariable {
	int s1, s2;
	static int s3;

	OssezVariable(int x, int y, int z) {
		s1 = x;
		s2 = y;
		s3 = z;
	}
}

/**
 * 
 * @author YuCheng
 *
 */
public class VariableTest extends TestCase {

	private final static Logger logger = LoggerFactory.getLogger(VariableTest.class);

	/**
	 * Do RetsServerConnection Test
	 */
	@Test
	public void testStaticVariableChange() {

		OssezVariable objA = new OssezVariable(1, 2, 3);
		logger.debug("s1/s2/s3 - [{}]/[{}]/[{}]", objA.s1, objA.s2, OssezVariable.s3);

		OssezVariable objB = new OssezVariable(4, 5, 6);
		logger.debug("s1/s2/s3 - [{}]/[{}]/[{}]", objA.s1, objA.s2, OssezVariable.s3);
		logger.debug("s1/s2/s3 - [{}]/[{}]/[{}]", objB.s1, objB.s2, OssezVariable.s3);
	}

}


运行的结果为

Code Block
languagetext
2018/11/21 14:03:18 DEBUG [com.ossez.lang.tutorial.tests.VariableTest] - s1/s2/s3 - [1]/[2]/[3]
2018/11/21 14:03:18 DEBUG [com.ossez.lang.tutorial.tests.VariableTest] - s1/s2/s3 - [1]/[2]/[6]
2018/11/21 14:03:18 DEBUG [com.ossez.lang.tutorial.tests.VariableTest] - s1/s2/s3 - [4]/[5]/[6]

在这个代码中,在对 objB 类实例化的时候,我们修改了静态变量 s3 的值。

因为静态变量是类变量,因此在 objB 对象实例化的时候,前期实例化的对象 objA 中的 s3 的值也一同再次实例化了。

从这个例子我们可以看出,静态变量在对象实例化的时候如果修改了值,那么这个类已经实例化的所有对象都会被修改。



构造方法

在我们讨论类的时候,一个重要的概念就是有关构造方法。每一个类都会有一个构造方法。

尽管你没有显式的定义构造方法,Java 的编译器也会默认为该类创建一个构造方法。

在每一个新对象创建的时候,最少都会调用一次构造方法。构造方法的名字必须与类的名字相同,在一个类中,可以允许有多个构造方法。

下面是有关构造方法的的示例:

示例

Code Block
languagejava
public class VariableTest extends TestCase {

	private final static Logger logger = LoggerFactory.getLogger(VariableTest.class);

	public VariableTest() {
	}

	public VariableTest(String name) {
		// 这个构造方法有一个参数名为 name 
	}
}

Java 同时还支持 单例模式,这种模式能够让你针对一个类只能创建一个实例。

在单例模式中有 2 种类型的的构造方法,有关单例模式的 2 种构造方法,请参考 单例模式 页面中的内容。

创建一个对象

如我们上面提到的,一个类为对象提供了蓝图。因此基本上来说一个对象的创建是从类来创建的。在 Java 中关键字 new 被用来创建一个新的对象。

从一个类中来创建一个对象有下面 3 个步骤:

  • 申明(Declaration )− 声明一个对象,包括对象名称和对象类型。

  • 实例化(Instantiation )− new 关键字被用来创建这个对象。

  • 初始化(Initialization )−  new 关键字被用来创建对象的时候,同时也会调用构造方法来对对象进行初始化。

下面吃创建对象的一个示例:

示例(GitHub

Code Block
package com.ossez.lang.tutorial.objplusclass;

/**
 * 
 * @author YuCheng
 *
 */
public class CreateObject {
	public CreateObject(String name) {
		// This constructor has one parameter, name
		System.out.println("小狗的名字是: " + name);
	}

	public static void main(String[] args) {
		// Following statement would create an object myPuppy
		CreateObject myPuppy = new CreateObject("Tomcat");
	}
}


我们将上面的程序编译后输出的结果如下

运行输出

小狗的名字是: Tomcat


访问实例变量和方法

实例变量和方法可以通过创建对象来访问。访问实例变量请参考下面完整的路径:

Code Block
// 实例化一个对象
ObjectReference = new Constructor();

// 访问创建的对象中的变量
ObjectReference.variableName;

// 访问创建的对象中的方法
ObjectReference.methodName();

示例

下面的示例展示了如何访问一个类的实例变量和方法。

Code Block
public class Puppy {
    int puppyAge;

    public Puppy(String name) {
        // 这个构造方法有一个参数, name.
        System.out.println("Name chosen is :" + name);
    }

    public void setAge(int age) {
        puppyAge = age;
    }

    public int getAge() {
        System.out.println("Puppy's age is :" + puppyAge);
        return puppyAge;
    }

    public static void main(String[] args) {
        /* 创建对象 */
        Puppy myPuppy = new Puppy("tommy");

        /* 调用类的方法来设置 puppy 的年龄 */
        myPuppy.setAge(2);

        /* 调用类的另外一个方法来获得 puppy 年龄 */
        myPuppy.getAge();

        /* 你也可以通过下面的方法来访问实体变量 */
        System.out.println("Variable Value :" + myPuppy.puppyAge);
    }
}

如果我们对上面的代码进行编译后运行的结果如下:

输出

Name chosen is :tommy
Puppy's age is :2
Variable Value :2

源文件申明规则

在本节的最后部分,我们将学习源文件的声明规则。当在一个源文件中定义多个类,并且还有import语句和package语句时,要特别注意这些规则。


  • 一个源文件中只能有一个public类
  • 一个源文件可以有多个非public类
  • 源文件的名称应该和public类的类名保持一致。例如:源文件中public类的类名是Employee,那么源文件应该命名为Employee.java。
  • 如果一个类定义在某个包中,那么package语句应该在源文件的首行。
  • 如果源文件包含import语句,那么应该放在package语句和类定义之间。如果没有package语句,那么import语句应该在源文件中最前面。
  • import语句和package语句对源文件中定义的所有类都有效。在同一源文件中,不能给不同的类不同的包声明。

类有若干种访问级别,并且类也分不同的类型:抽象类和final类等。这些将在访问控制章节介绍。

除了上面提到的几种类型,Java还有一些特殊的类,如:内部类、匿名类。

Java 包

In simple words, it is a way of categorizing the classes and interfaces. When developing applications in Java, hundreds of classes and interfaces will be written, therefore categorizing these classes is a must as well as makes life much easier.包主要用来对类和接口进行分类。当开发Java程序时,可能编写成百上千的类,因此很有必要对类和接口进行分类。

Import 语句

In Java if a fully qualified name, which includes the package and the class name is given, then the compiler can easily locate the source code or classes. Import statement is a way of giving the proper location for the compiler to find that particular class.

For example, the following line would ask the compiler to load all the classes available in directory 在Java中,如果给出一个完整的限定名,包括包名、类名,那么 Java 编译器就可以很容易地定位到源代码或者类。

Import 语句总是给编译器如何找到需要的类的正确路径。

例如,import java.io.*; 的包的引入命令行将会让编译器载入 java_installation/java/io

import java.io.*;

路径下的所有类


一个简单的例子

For our case study, we will be creating two classes. They are Employee and EmployeeTest.

First open notepad and add the following code. Remember this is the Employee class and the class is a public class. Now, save this source file with the name Employee.java.

The Employee class has four instance variables - name, age, designation and salary. The class has one explicitly defined constructor, which takes a parameter.

Example

在下面的例子中,我们创建两个类:Employee 和 EmployeeTest

首先你可以打开你习惯使用的记事本,然后输入下面的代码。请注意 Employee 类是一个 public 类。然后将你输入的源代码保存为 Employee.java 文件。

Employee 类有 4 个实体变量: name, age, designation 和 salary。这个类只有一个确定的构造函数,这个构造函数被用来对参数进行初始化。

示例

Code Block
import java.io.*;

public class Employee {

    String name;
    int age;
    String designation;
    double salary;

    // This is the constructor of the class Employee
    public Employee(String name) {
        this.name = name;
    }

    // Assign the age of the Employee  to the variable age.
    public void empAge(int empAge) {
        age = empAge;
    }

    /* Assign the designation to the variable designation.*/
    public void empDesignation(String empDesig) {
        designation = empDesig;
    }

    /* Assign the salary to the variable
	salary.*/
    public void empSalary(double empSalary) {
        salary = empSalary;
    }

    /* Print the Employee details */
    public void printEmployee() {
        System.out.println("Name:" + name
);
        System.out.println("Age:" + age
);
        System.out.println("Designation:" + designation
);
        System.out.println("Salary:" + salary);
    }

}

As mentioned previously in this tutorial, processing starts from the main method. Therefore, in order for us to run this Employee class there should be a main method and objects should be created. We will be creating a separate class for these tasks.

Following is the EmployeeTest class, which creates two instances of the class Employee and invokes the methods for each object to assign values for each variable.

Save the following code in EmployeeTest.java file.

正如我们前面所提到的,程序的开始运行时通过 main 方法开始的。为了让 Employee 类能够运行,我们需要为我们的程序创建一个 main 方法,这个方法我们将在不同的类中进行创建。

我们需要创建的类的名字为 EmployeeTest,该类实例化 2 个 Employee 类,并调用方法设置变量的值。

将下面的代码保存为 EmployeeTest.java  文件。




Code Block
import java.io.*;

public class EmployeeTest {

    public static void main(String args[]) {
        /* Create two objects using constructor */
        Employee empOne = new Employee("James Smith");
        Employee empTwo = new Employee("Mary Anne");

        // Invoking methods for each object created
        empOne.empAge(26);
        empOne.empDesignation("Senior Software Engineer");
        empOne.empSalary(1000);
        empOne.printEmployee();

        empTwo.empAge(21);
        empTwo.empDesignation("Software Engineer");
        empTwo.empSalary(500);
        empTwo.printEmployee();
    }
}

Now, compile both the classes and then run EmployeeTest to see the result as follows −

Output

}




现在你可以对程序进行编译后,运行 EmployeeTest,你应该能够看到下面的输出:


输出


C:\> javac Employee.java
C:\> javac EmployeeTest.java
C:\> java EmployeeTest
Name:James Smith
Age:26
Designation:Senior Software Engineer
Salary:1000.0
Name:Mary Anne
Age:21
Designation:Software Engineer
Salary:500.0