Chapter 5: Creating and Using Classes and Objects ======= = ======== === ===== ======= === ======= Main idea: intro to object-oriented design Classes, instance variables, methods, constructors ... similar to C++ The Account class --- ------- ----- Account class: keeps track of bank account details. Instance variables: name, address, accountNumber, balance Methods: displayDetails, inCredit (true iff balance is nonnegative), credit, debit (add/subtract specified amount to/from balance), main Skeleton of Account class definition: public class Account { String name; String address; String accountNumber; double balance; ... other stuff here } The displayDetails Method --- -------------- ------ ... prints the account details public void displayDetails() { System.out.println("Name: " + name); System.out.println("Address: " + address); System.out.println("Account number: " + accountNumber); System.out.println("Balance: " + balance); } public? void? void is as in C++. public is an access modifier. Regarding access: A package is a related collection of Java classes. Access modifiers: public: accessible by all methods in all packages protected: accessible by all methods in this class or any of its subclasses private: accessible only by methods of this class If no access modifier given, then the method has package access. That is, accessible by all classes and subclasses. in the package The inCredit method --- -------- ------ inCredit is of boolean type, since it returns a boolean value. public boolean inCredit() { return balance >= 0.0; } The credit, debit methods --- ------ ----- ------- credit, debit are of void type, since they have no return value. public void credit(double amount) { balance += amount; } public void debit(double amount) { balance -= amount; } The main method --- ---- ------ ... creates one or more Account instances, and does stuff with same. Simple example: public static void main(String args[]) { Account azw = new Account(); azw.name = "Aaron Werschulz"; azw.address = "123 Main Street, Somewhere NJ"; azw.accountNumber = "123-45-6789"; azw.balance = 150.23; azw.displayDetails(); azw.credit(12.75); azw.displayDetails(); azw.debit(14.92); azw.displayDetails(); if (azw.inCredit()) System.out.println("In credit"); else System.out.println("Overdrawn"); } Using constructors ----- ------------ We can add the constructor method that instantiates the instance variables: public Account (String theName, String theAddress, String theAccountNumber, double theBalance) { name = theName; address = theAddress; accountNumber = theAccountNumber; balance = theBalance; } Then the main method above can be simplified: public static void main(String args[]) { Account azw = new Account("Aaron Werschulz", "123 Main Street, Somewhere NJ", "123-45-6789", 150.23); azw.displayDetails(); azw.credit(12.75); azw.displayDetails(); azw.debit(14.92); azw.displayDetails(); if (azw.inCredit()) System.out.println("In credit"); else System.out.println("Overdrawn"); } Encapsulating data ------------- ---- Good idea: principle of least privilege. Generally, instance variables are private (perhaps protected), and methods are public. The class's interface to the outside world is given by the methods. Since the instance variables are private, implementor can change her mind without worrying about damaging client code. Example: A TimeOfDay class might decide to use hours, minutes, and seconds as instance variables. It might otherwise decide to use (only) seconds. If instance variables are private, it won't matter if implementor changes his mind. Let's revise the previous application. Subdivide into two classes: Account: contains instance variable defns and methods. NewAccount: contains main method and creats an instance of the Account class. We put the Account class in the file Account.java. Note that we use a DecimalFormat object to make a nicer-looking printout. import java.text.*; public class Account { private String name; private String address; private String accountNumber; private double balance; private DecimalFormat money = new DecimalFormat("$0.00"); public Account (String theName, String theAddress, String theAccountNumber, double theBalance) { name = theName; address = theAddress; accountNumber = theAccountNumber; balance = theBalance; } public void displayDetails() { System.out.println("Name: " + name); System.out.println("Address: " + address); System.out.println("Account number: " + accountNumber); System.out.println("Balance: " + money.format(balance)); } public void setName(String customerName) { name = customerName; } public void setAddress(String customerAddress) { address = customerAddress; } public void setAccountNumber(String customerNumber) { accountNumber = customerNumber; } public void setBalance (double customerBalance) { balance = customerBalance; } public String getName() { return name; } public String getAddress() { return address; } public String getAccountNumber() { return accountNumber; } public double getBalance() { return balance; } public void credit(double amount) { balance += amount; } public void debit(double amount) { balance -= amount; } public boolean inCredit() { return balance >= 0.0; } } Note the use of getters and setters. We put the NewAccount class in NewAccount.java: public class NewAccount { public static void main(String args[]) { Account azw = new Account("Aaron Werschulz", "123 Main Street, Somewhere NJ", "123-45-6789", -3.75); azw.displayDetails(); if (azw.inCredit()) System.out.println("In credit"); else System.out.println("Overdrawn"); azw.setAddress("1 Marsh St, Cranford NJ"); System.out.println("Confirming new address: " + azw.getAddress()); azw.credit(42.75); azw.displayDetails(); azw.debit(14.92); azw.displayDetails(); } } Then $ javac NewAccount.java $ java NewAccount Name: Aaron Werschulz Address: 123 Main Street, Somewhere NJ Account number: 123-45-6789 Balance: -$3.75 Overdrawn Confirming new address: 1 Marsh St, Cranford NJ Name: Aaron Werschulz Address: 1 Marsh St, Cranford NJ Account number: 123-45-6789 Balance: $39.00 Name: Aaron Werschulz Address: 1 Marsh St, Cranford NJ Account number: 123-45-6789 Balance: $24.08 Calling methods ------- ------- Suppose that A and B are classes. Let m be a method of A. If access is OK, then class B can contain code such as A a = new A; followed by a reference to a.m() On the other hand, if m2 is another method of A, it will only make a reference of the form m(), i.e., we don't prepend with an object name. Inded, we're talking about the "current object". Example: We might add the method creditReport() to the Account class. Its definition might be: public void creditReport() { String m; displayDetails(); if (inCredit()) m = "in credit"; else if (getBalance() > -100) m = "overdrawn: send notification"; else if (getBalance() > -1000) m = "overdrawn: send warning letter"; else m = "overdrawn: send supsend account notification"; System.out.println("Account " + m); } Then our main() method (within the NewAccount class) might well be public class NewAccount { public static void main(String args[]) { Account azw = new Account("Aaron Werschulz", "123 Main Street, Somewhere NJ", "123-45-6789", -3.75); azw.creditReport(); azw.credit(600.00); azw.creditReport(); } } When we compile and run the program, we get the following output Name: Aaron Werschulz Address: 123 Main Street, Somewhere NJ Account number: 123-45-6789 Balance: -$3.75 Account overdrawn: send notification Name: Aaron Werschulz Address: 123 Main Street, Somewhere NJ Account number: 123-45-6789 Balance: $596.25 Account in credit One possible improvement --- -------- ----------- To print an Account, need to do azw.displayDetails(); If we could do System.out.println(azw); then Account would resemble other datatypes. Can do this if we provide a toString() method. In our case, we would replace Account's displayDetails() method with the following: public String toString() { return "Name: " + name + "\n" + "Address: " + address + "\n" + "Account number: " + accountNumber + "\n" + "Balance: " + money.format(balance); } A note on the Java compiler - ---- -- --- ---- -------- The Java compiler is quasi-make-like. If the class Foo involves class Bar, then javac Foo.java will also compile Bar.java *if necessary*, i.e., if Bar.java is newer than Bar.class. This means that in the previous example, you need only do javac NewAccount.java and Account.java will be compiled if necessary. Command-line arguments for applications ------- ---- --------- --- ------------ The String[] parameter of a public class's public static void main() method contains the command-line parameters. Example: The following program prints out its command-line arguments: public class PrintArgs { public static void main(String args[]) { for (int i = 0; i < args.length; i++) System.out.println("Arg " + i + ": " + args[i]); } } Then $ java PrintArgs sis boom bah Arg 0: sis Arg 1: boom Arg 2: bah This also explains what the String[] parameter in main does. This Java program is essentially equivalent to the C++ program #include int main (int argc, char *argv[]) { for (int i = 0; i < argc; i++) cout << "Arg " << i << ": " << argv[i] << endl; } for which we have $ ./printargs sis boom bah Arg 0: ./printargs Arg 1: sis Arg 2: boom Arg 3: bah (However, note that in the C++ version, argv includes the name of the program from the command line as its initial parameter; this doesn't happen with the Java version.)