学习笔记5(类和对象)

一、实例和静态

1、实例

类中的实例都有自己的字段,再每个类中都可以创建一系列实例对象,并将值储存在实例字段中。

box.setLength(10);

这段代码将10储存在一个被box引用的实例的字段中,你可以把实例字段看作类中某个特定实例的一部分。当你调用一个实例方法时,它也只会在特定的实例的实例字段中执行操作。

2、静态

我们也可以创建不属于类的实例的字段或方法,称为静态字段和静态方法。当值存储在静态字段中时,就不会存储在类的实例当中,甚至当类的实例不存在时,就可以生成值存储在类的静态字段中。静态方法不会对任何实例执行操作,它们只能在静态字段上运行。

(1)静态字段

当字段前有关键字static时,就说明这是一个实例字段。在同一个类中,静态字段可以被所有的实例所共享。

/**
 This class demonstrates a static field.
 */

  public class Countable
 {
 private static int instanceCount = 0;

  /**
 The constructor increments the static
 field instanceCount. This keeps track
 of the number of instances of this
 class that are created.
 */

 public Countable()
 {
 instanceCount++;
 }

 /**
 The getInstanceCount method returns
 the number of instances of this class
 that have been created.
 @return The value in the instanceCount field.
 */

public int getInstanceCount()
 {
 return instanceCount;
 }
}

注意“private static int instanceCount = 0”永远只会赋值一次

接下来,每当构造器执行一次(也就是每当countable类的对象被创建一次),instancecount字段就将增加一次,增加后的instancecount字段的值将储存在类中,也就是说,instancecount字段的值并不会随着countable类的对象的不同而改变,也不会创建一个countable对象就重新变成0,基于这种特性,可以用instancecount字段的值表示创建的countable对象的数量。

  /**
  This program demonstrates the Countable class.
  */
 
  public class StaticDemo
  {
  public static void main(String[] args)
  {
  int objectCount;

 // Create three instances of the
 // Countable class.
 Countable object1 = new Countable();
 Countable object2 = new Countable();
 Countable object3 = new Countable();

 // Get the number of instances from
 // the class's static field.
 objectCount = object1.getInstanceCount();
 System.out.println(objectCount +
 " instances of the class " +
 "were created.");
 }
}

得到的输出为:

3 instances of the class were created.

虽然创建了三个countable对象,但是静态字段的值并不会复制三次,它永远只会赋值一次。

 虽然最后返回的是“object1.getInstanceCount()”,但是无论是object1还是object2,得到的结果都是一样的。

(2)静态方法

当一个类有静态方法的时候,就不一定要为这个类创建对象了,在调用静态方法的时候,只需要写类的名字就行了,而调用实例方法的时候,则需要写类中对象的名字(比如上面的getInstanceCount就是实例方法,所以调用的时候要写object1,这个object1就是对象而非类)。

 /**
 This class demonstrates static methods.
 */

  public class Metric
 {
 /**
 The milesToKilometers method converts a
 distance in miles to kilometers.
 @param m The distance in miles.
 @return The distance in kilometers.
 */

 public static double milesToKilometers(double m)
 {
 return m * 1.609;
 }

 /**
 The kilometersToMiles method converts
 a distance in kilometers to miles.
 @param k The distance in kilometers.
 @return The distance in miles.
 */

 public static double kilometersToMiles(double k)
 {
 return k / 1.609;
 }
 }

 调用milesToKilometers方法:

kilometers = Metric.milesToKilometers(10.0);

 这里的Metric就是类的名字,而非实例的名字。

小结

静态方法常用于对数据进行操作而不需要储存数据的情况,比如上面的公里英里转换器,只需要执行“转换”这个操作,而不需要对数据进行储存,所以就适合使用静态方法。

静态方法的局限就是它的所有成员都必须是静态的,比如静态方法调用的方法也必须是静态方法。反过来,如果一个方法使用了任何的静态字段,那么这个方法也应该是静态方法。

静态方法内部不能直接调用非静态方法,但是可以在调用之前实例化非静态方法所在的类,再用类的方法来调用。

public static void changeRectangle(Rectangle r)
 {
 r.setLength(0.0);
 r.setWidth(0.0);
 }
 }

二、将对象作为参数传入方法

1、一个地址两个名字

下面这个程序将一个三角形对象传入方法:

/**
  This program passes an object as an argument.
  */
 
  public class PassObject
  {
  public static void main(String[] args)
  {
  // Create a Rectangle object.
 Rectangle box = new Rectangle(12.0, 5.0);

 // Pass a reference to the object to
 // the displayRectangle method.
 displayRectangle(box);
 }

 /**
 The displayRectangle method displays the
 length and width of a rectangle.
 @param r A reference to a Rectangle
 object.
 */

 public static void displayRectangle(Rectangle r)
 {
 // Display the length and width.
 System.out.println("Length : " + r.getLength() +
 " Width : " + r.getWidth());
 }
 }

值得注意的是,在这个程序中,主函数里的box是一个对rectangle类的对象的引用,而传参的时候的形参r也是一个对rectangle类的对象的引用,box和r本质上是一样的,它们都是这个对象的地址,只不过叫两个名字。

 

 2、传实例与传基本数据类型的区别

当我们传一个基本数据类型的参数的时候,我们是将原来的值复制了一遍,然后再传递给方法,当方法想改变参数的值的时候,改变的只是复制以后的值,对于原来的值并不会有影响。

但是当我们传一个实例的时候,我们传递的就是实例的地址,这意味着方法是有权限去访问该实例的,所以方法是可以修改原来实例的内容的。

/**
  This program passes an object as an argument.
  The object is modified by the receiving method.
  */
 
  public class PassObject2
  {
  public static void main(String[] args)
  {
 // Create a Rectangle object.
 Rectangle box = new Rectangle(12.0, 5.0);

 // Display the object's contents.
 System.out.println("Contents of the box object:");
 System.out.println("Length : " + box.getLength() +
 " Width : " + box.getWidth());

 // Pass a reference to the object to the
 // changeRectangle method.
 changeRectangle(box);

 // Display the object's contents again.
 System.out.println("\nNow the contents of the " +
 "box object are:");
 System.out.println("Length : " + box.getLength() +
 " Width : " + box.getWidth());
 }

 /**
 The changeRectangle method sets a Rectangle
 object's length and width to 0.
 @param r The Rectangle object to change.
 */

 public static void changeRectangle(Rectangle r)
 {
 r.setLength(0.0);
 r.setWidth(0.0);
 }
 }

Contents of the box object: Length :

2.0 Width : 5.0

Now the contents of the box object are:

Length : 0.0 Width : 0.0

三、从方法中返回一个对象

  import javax.swing.JOptionPane;
 
  /**
  This program demonstrates how a method
  can return a reference to an object.
  */
 
  public class ReturnObject
  {
 public static void main(String[] args)
 {
 BankAccount account;

 // Get a reference to a BankAccount object.
 account = getAccount();

// Display the account's balance.
 JOptionPane.showMessageDialog(null,
 "The account has a balance of $" +
 account.getBalance());

 System.exit(0);
 }

 /**
 The getAccount method creates a BankAccount
 object with the balance specified by the
 user.
 @return A reference to the object.
 */

 public static BankAccount getAccount()
 {
 String input; // To hold input
 double balance; // Account balance

 // Get the balance from the user.
 input = JOptionPane.showInputDialog("Enter " +
 "the account balance.");
 balance = Double.parseDouble(input);

// Create a BankAccount object and return
 // a reference to it.
 return new BankAccount(balance);
 }
 }

getAccount方法返回了一个对象,在主函数里,将这个对象赋给了变量account,然后对account变量(也就是bankaccount类对象的引用)调用bankaccount类的方法,得到了余额。

ps:(1)在方法头中要写明返回值的类型,就和int,double一样

(2)返回对象的时候,要注意写关键字new

return new BankAccount(balance);

此语句使用new关键字创建BankAccount对象,并将balence作为参数传递给构造器。然后从方法返回对象的地址。

 四、toString方法

我们经常需要输出一个对象的状态,比如对bankaccount类:

BankAccount account = new BankAccount(1500.0);
System.out.println("The account balance is $" +
account.getBalance());

因为输出一个对象的状态的这种需求太过常见了,所以在java中,这种方法被统称为toString方法,我们可以尝试自己写一个toString方法。

1、准备工作

 

 此类有两个字段:symbol(符号)和sharePrice(股价)。“符号”字段保存交易的交易符号公司的股票。这是一系列短字符,用于标识股票上的股票交易。例如,XYZ公司的股票可能具有交易符号XYZ。股价字段保存股票的当前每股价格。

2、代码

 /**
  The Stock class holds data about a stock.
  */
 
  public class Stock
  {
  private String symbol; // Trading symbol of stock
  private double sharePrice; // Current price per share
 
 /**
 Constructor
 @param sym The stock's trading symbol.
 @param price The stock's share price.
 */

 public Stock(String sym, double price)
 {
 symbol = sym;
 sharePrice = price;
 }

 /**
 getSymbol method
 @return The stock's trading symbol.
 */

 public String getSymbol()
 {
 return symbol;
 }

 /**
 getSharePrice method
 @return The stock's share price
 */

 public double getSharePrice()
 {
 return sharePrice;
 }

 /**
 toString method
 @return A string indicating the object's
 trading symbol and share price.
 */

 public String toString()
 {
 // Create a string describing the stock.
 String str = "Trading symbol: " + symbol +
 "\nShare price: " + sharePrice;

 // Return the string.
 return str;
 }
 }

可以这样调用:

Stock xyzCompany = new Stock ("XYZ", 9.62);
System.out.println(xyzCompany.toString());
Trading symbol: XYZ
Share price: 9.62

但是事实上,没有必要显式调用toString方法。如果你写了一个toString方法,当你将这个对象传递给print或者println方法的时候,java将自动调用toString方法。所以,可以省略 “.toString”:

System.out.println(xyzCompany);

输出的结果是一样的。

System.out.println("The stock data is:\n" + xyzCompany);

上面的代码也会调用toString方法:

The stock data is:
Trading symbol: XYZ
Share price: 9.62

 五、equals方法

如果你想比较两个对象的各项属性是否相等,不能用==来比较,它只会比较两个对实例的引用变量的地址是否相同,所以此时你需要自己的equals方法。

public boolean equals(Stock object2)
{
 boolean status;
 // Determine whether this object's symbol and
 // sharePrice fields are equal to object2's
 // symbol and sharePrice fields.
 if (symbol.equals(object2.symbol) &&
 sharePrice == object2.sharePrice)
 status = true; // Yes, the objects are equal.
 else
 status = false; // No, the objects are not equal.
 
 // Return the value in status.
 return status;
}

这个方法返回一个布尔类型的值,并接受一个stock类型(就是之前股票的例子)的对象object2作为参数。因为是进行两者的比较,所以自然需要比较两个stock对象,这里已经将object2当参数传进来了,我们就可以对object1使用这个方法,这样equals方法本身就可以访问object1,也可以访问作为参数传入的object2。

if语句后的“symbol.equals(object2.symbol)”中,第一个symbol是object1的symbol变量,而object2.symbol指的就是object2的symbol变量。在同一个类中(这里都是stock类型的实例),可以直接用“对象名.变量名”的语法来访问同类中另外一个实例的属性。而如果希望在另一个类中访问这个symbol变量,则只能使用get方法了。下面是另一个“对象名.变量名”语法的例子:

public static void main (String[] args){
        Rectangle box=new Rectangle(2,1);
        Rectangle box1=new Rectangle(4,1);
        if(box.length==box1.length)
            System.out.println("yes");
    }

还是“symbol.equals(object2.symbol)”,这里的equals是string类的equals方法,因为symbol是string类型的变量。

对equals方法的调用:

/**
  This program uses the Stock class's equals
  method to compare two Stock objects.
  */
 
  public class StockCompare
  {
  public static void main(String[] args)
  {
 // Create two Stock objects with the same values.
 Stock company1 = new Stock("XYZ", 9.62);
 Stock company2 = new Stock("XYZ", 9.62);

 // Use the equals method to compare the objects.
 if (company1.equals(company2))
 System.out.println("Both objects are the same.");
 else
 System.out.println("The objects are different.");
 }
 }

ps:每个类在一开始都有自己的equals方法,初始的equals方法和==操作符的用法一模一样。如果你没有写自己的equals方法,那么当你调用equals方法的时候就会调用这个方法了,后面还会详细介绍。

六、复制对象的方法

1、简介

当我们简单地用 = 将一个对象赋值给另一个对象的时候,只是将一个对象的地址传递给了第二个对象,只是一个对象有两个名字而已。

 所以如果希望再创建一个相同的对象,需要新创建一个对象(new),然后将旧变量的每一个字段都赋值给新变量的相应字段,介绍两种方法。

2、copy方法

copy方法将旧对象的每一个字段当做新创建的对象的构造器的参数(注意不是copy方法本身的参数)。在copy方法内部,读取旧对象的各字段,然后调用这个对象所在类的构造器,将旧对象的字段当做构造器的参数,创建一个和旧对象一样的新对象。

public Stock copy()
{
 // Create a new Stock object and initialize it
 // with the same data held by the calling object.
 Stock copyObject = new Stock(symbol, sharePrice);
 // Return a reference to the new object.
 return copyObject;
}

调用如下:

/**
  This program uses the Stock class's copy method
  to create a copy of a Stock object.
  */
 
  public class ObjectCopy
  {
  public static void main(String[] args)
  {
 // Create a Stock object.
 Stock company1 = new Stock("XYZ", 9.62);

 // Declare a Stock variable
 Stock company2;

 // Make company2 reference a copy of the object
 // referenced by company1.
 company2 = company1.copy();
}
}

3、Copy Constructors

另一个创建新的复制对象的方法就是使用复制构造器。复制构造器直接将原来的一整个对象当做参数传入构造器,然后在构造器内部读取传入的这个对象的各字段,将各字段赋值给新的对象。

public Stock(Stock object2)
{
 symbol = object2.symbol;
 sharePrice = object2.sharePrice;
}

调用如下:

// Create a Stock object.
Stock company1 = new Stock("XYZ", 9.62);
// Create another Stock object that is a copy of the company1 object.
Stock company2 = new Stock(company1);

如果写了复制构造器,在调用构造器的时候到底调用哪一个就取决于传入的参数类型了(重载)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值