730 likes | 966 Views
Jiafan Zhou. Separating the things that change from the things that stay the same. Access specifiers to allow the library creator to say what is available to the client programmer and what is not. From “most access” to “least access”: public protected package access private.
E N D
Separating the things that change from the things that stay the same
Access specifiers to allow the library creator to say what is available to the client programmer and what is not. • From “most access” to “least access”: • public • protected • package access • private
Library components are placed into packages. • Packages are controlled by package keyword in Java. • A package is what becomes available when you use the import keyword. import java.util.*; // import all the classes inside this package • If you want to bring in a single class, you can name that class in the import statement import java.util.ArrayList; // import only ArrayList class
import vs. static import • Import imports the library classes into the target class so that they can be used • Static import takes this further. It imports all the library class static members into the target class. • Consider the following example: import java.lang.* public class TestStatic {public static void main(String[] args) { System.out.println(Integer.MAX_VALUE); System.out.println(Integer.toHexString(32)); } } Note: Because all the classes in the java.lang package are explicitly imported, we can ignore the first import statement.
import vs. static import import static java.lang.System.out; (1) import static java.lang.Integer.*; (2) public class TestStatic {public static void main(String[] args) { out.println(MAX_VALUE); out.println(toHexString(32)); } } (1) In this case, we do a static import on the System class, the static member “out” object. (2) In this case, we import the wildcard, “I want to do a static imports of ALL THE STATIC members in the Integer class”.
Suppose you have a class “MyClass.java” package mypackage; public class MyClass { // . . . • To use this class you have to mypackage.MyClass m = new mypackage.MyClass(); • import keyword can make this much cleaner import mypackage.*; // . . . MyClass m = new MyClass();
.java file is compiled to .class file • place all the .class files for a particular package into a single directory • use the hierarchical file structure • By convention, the first part of the package name is the reversed Internet domain name of the creator of the class: package net.zhous.mail ; // must be placed as first statement in the file class MyClass { //…
Place specifier in front of each definition for each member in your class (both field and method) public class MyClass{ public MyClass(){ } private String name; protected void Calculate(){ //… } public String getName(){ //… } }
public: everybody, everywhere, can access it. • package access (by default): the other classes in that package can access the member. • protected: an inherited class can access a protected member as well as a public member (but not private members). • private: no other class except itself can access.
public: it is available to everyone, in particular to the client programmer who uses the library package net.zhous.dessert; public class Cookie { public Cookie() { System.out.println("Cookie constructor"); } void bite() { System.out.println("bite"); } } import net.zhous.dessert.*; public class Dinner { public Dinner() { System.out.println("Dinner constructor"); } public static void main(String[] args) { Cookie x = new Cookie(); // x.bite(); // Can't access } }
The private keyword means that no one can access that member except the class that contains that member, inside methods of that class. class Sundae { private Sundae() {} static Sundae makeASundae() { return new Sundae(); } } public class IceCream { public static void main(String[] args) { //! Sundae x = new Sundae(); Sundae x = Sundae.makeASundae(); } }
protected: all the sub-classes can access it and it also gives package accesses permission. import c05.dessert.*; public class ChocolateChip extends Cookie { public ChocolateChip() { System.out.println("ChocolateChip constructor"); } public static void main(String[] args) { ChocolateChip x = new ChocolateChip(); //! x.bite(); // Can't access bite; } }
public class Cookie { public Cookie() { System.out.println("Cookie constructor"); } protected void bite() { System.out.println("bite"); } }
Access Levels Class Package Subclass World public Y Y Y Y protectedY Y Y N packageY Y N N privateY N N N public > protected > package (default) > private
To separate the interface from the implementation. • Client programmers can’t do anything but send messages to the public interface. • Then you are free to change anything that’s not public (e.g., package access, protected, or private) without breaking client code.
If other programmers use your class, you want to ensure that errors from misuse cannot happen. Access levels can help you do this. • Use the most restrictive access level that makes sense for a particular member. Use private unless you have a good reason not to. • Avoid public fields except for constants. Public fields tend to link you to a particular implementation and limit your flexibility in changing your code.
public or package access • public: client can create object from this class. • package access: supportive class
There can be only one public class per compilation unit (file). • It can have as many supporting package-access classes as you want. • If you have more than one public class inside a compilation unit, the compiler will give you an error message. • The name of the public class must exactly match the name of the file
class Sandwich { // Uses Lunch void f() { new Lunch(); } } // Only one public class allowed per file: public class Lunch { void test() { // Can't do this! Private constructor: //! Soup priv1 = new Soup(); Soup priv2 = Soup.makeSoup(); Sandwich f1 = new Sandwich(); Soup.access().f(); } } class Soup { private Soup() {} // (1) Allow creation via //static method: public static Soup makeSoup() { return new Soup(); } // (2) Create a static object and return a // reference // upon request.(The "Singleton" pattern): private static Soup ps1 = new Soup(); public static Soup access() { return ps1; } public void f() {} }
Java: code reuse • Composition - create objects of your existing class inside the new class and reuse the functionality of the code, not its form • Inheritance - take the form of the existing class and add code to it without modifying the existing class
class WaterSource { private String s; WaterSource() { System.out.println("WaterSource()"); s = new String("Constructed"); } public String toString() { return s; } } public class SprinklerSystem { private String valve1, valve2; private WaterSource source; private int i; private float f; public String toString() { return "valve1 = " + valve1 + "\n" + "valve2 = " + valve2 + "\n"+"i = " + i + "\n" + "f = " + f + "\n" + "source = " + source; } public static void main(String[] args) { SprinklerSystem sprinklers = new SprinklerSystem(); System.out.println(sprinklers); } } Output: "valve1 = null", "valve2 = null", "i = 0", "f = 0.0", "source = null"
toString() is called when the compiler wants a String but it has an object. • Override this method which is inherited from Object class. • obj.toString() is automatically called when executing System.out.println(obj).
Recall the initialization, three way: • At the point the objects are defined. WaterSource source = new WaterSource(); • In the constructor for that class. WaterSource source = null; SprinklerSystem() { source = new WaterSource(); } • Right before you actually need to use the object. (lazy initialization) • ..... source = new WaterSource();
You’re always doing inheritance when you create a class. • Unless you explicitly inherit from some other class, you implicitly inherit from Java’s standard root class Object. • Syntax: by putting a keyword extends followed by the name of the base class. • public MySubClass extends MyBaseClass { //… } • When you do this, you automatically get all the fields and methods in the base class
public class Detergent extends Cleanser { // Change a method: public void scrub() { append(" Detergent.scrub()"); super.scrub(); // Call base-class version } // Add methods to the interface: public void foam() { append(" foam()"); } // Test the new class: public static void main(String[] args) { Detergent x = new Detergent(); x.dilute(); x.apply(); x.scrub(); x.foam(); System.out.println(x); Cleanser.main(args); } } class Cleanser { private String s = new String("Cleanser"); public void append(String a) { s += a; } public void dilute() { append(" dilute()"); } public void apply() { append(" apply()"); } public void scrub() { append(" scrub()"); } public String toString() { return s; } public static void main(String[] args) { Cleanser x = new Cleanser(); x.dilute(); x.apply(); x.scrub(); System.out.println(x); } } "Cleanser dilute() apply() Detergent.scrub() scrub() foam()", "Testing base class:” "Cleanser dilute() apply() scrub()"
Inheritance doesn’t just copy the interface of the base class. • When you create an object of the derived class, it contains within it a subobject of the base class. • This subobject is the same as if you had created an object of the base class by itself • It’s essential that the base-class subobject be initialized correctly. • Java automatically inserts calls to the base-class constructor in the derived-class constructor.
class Art { Art() { System.out.println("Art constructor"); } } class Drawing extends Art { Drawing() { System.out.println("Drawing constructor"); } } public class Cartoon extends Drawing { public Cartoon() { System.out.println("Cartoon constructor"); } public static void main(String[] args) { Cartoon x = new Cartoon(); // Art art = new Cartoon(); ??? } } "Art constructor", "Drawing constructor", "Cartoon constructor"
Preceding example use default constructors. • If you want to call a base-class constructor that has an argument • You must explicitly write the calls to the base-class constructor. • Use keyword super and the appropriate argument list.
class Game { Game(int i) { System.out.println("Game constructor: " + i); } } class BoardGame extends Game { BoardGame(int i) { super(i); System.out.println("BoardGame constructor: " + i); } } public class Chess extends BoardGame { Chess() { super(11); System.out.println("Chess constructor"); } public static void main(String[] args) { Chess x = new Chess(); // Game game = new Chess()??? } } "Game constructor: 11", "BoardGame constructor: 11", "Chess constructor” Question: what will happen if you don’t call the super class constructor in BoardGame’s constructor? Answer: compiler will complain that it can’t find a constructor of the form Game( ) Note: the call to the base-class constructor must be the first thing you do in the derived-class constructor
Composition: has-a • Inheritance: is-a
class Engine { public void start() {} public void rev() {} public void stop() {} } class Wheel { public void inflate(int psi) {} } class Window { public void rollup() {} public void rolldown() {} } class Door { public Window window = new Window(); public void open() {} public void close() {} } public class Car { public Engine engine = new Engine(); public Wheel[] wheel = new Wheel[4]; public Door left = new Door(); public Door right = new Door(); // 2-door public Car() { for(int i = 0; i < 4; i++) wheel[i] = new Wheel(); } public static void main(String[] args) { Car car = new Car(); car.left.window.rollup(); car.wheel[0].inflate(72); } }
Protected: when you want to make something hidden from the world at large and allow access for members of derived classes. • Available to anyone who inherits from this class or anyone else in the same package. (protected provides package access as well)
Inheritance describes the relationship between new class (subclass) and inherited class (base class) • The new class is a type of the existing class.
A base class called “Instrument” that represents musical instruments and a derived class called “Wind” • Instrument class has a play( ) method, so will Wind instruments. • we can say that a Wind object is also a type of Instrument
class Instrument { public void play() {} static void tune(Instrument i) { // ... i.play(); } } // Wind objects are instruments // because they have the same interface: public class Wind extends Instrument { public static void main(String[] args) { Wind flute = new Wind(); Instrument.tune(flute); // Upcasting } } The act of converting a Wind reference into an Instrument reference is called upcasting Why it is called upcasting?
Upcasting: going from a more specific type to a more general type • The derived class is a superset of the base class. • It might contain more methods than the base class, but it must contain at least the methods in the base class. • This is why the compiler allows upcasting without any explicit casts or other special notation
This cannot be changed • final keyword can be used to data, methods and classes • We have seen the final data (both primitive type and object reference)
Two reasons for final methods: • “lock” – to prevent any inheriting class from changing its meaning • Efficiency - to turn any calls to that method into inline calls.
Any private methods in a class are implicitly final. • you can’t access a private method • you can’t override it • Adding a final keyword in front of a private method makes no difference.
class OverridingPrivate2 extends OverridingPrivate { public final void f(){ System.out.println("OverridingPrivate2.f()”; } public void g(){ System.out.println("OverridingPrivate2.g()"); } } public class FinalOverridingIllusion { public static void main(String[] args) { OverridingPrivate2 op2 = new OverridingPrivate2(); op2.f(); op2.g(); // You can upcast: OverridingPrivate op = op2; // But you can't call the methods: //! op.f(); //! op.g(); // Same here: WithFinals wf = op2; //! wf.f(); //! wf.g(); } } class WithFinals { // Identical to "private" alone: private final void f() { System.out.println("WithFinals.f()"); } // Also automatically "final": private void g() { System.out.println("WithFinals.g()"); } } class OverridingPrivate extends WithFinals { private final void f() { System.out.println("OverridingPrivate.f()"); } private void g() { System.out.println("OverridingPrivate.g()"); } }
Overriding 44
Overriding can only occur if something is part of the base-class interface. • If a method is private, it isn’t part of the base-class interface. • It is just some code that’s hidden away inside the class. • It is unreachable and effectively invisible. • If a method is static, it cannot be overridden by any methods in the subclasses. Static method is class-level.
When a class is final which means it can not be inherited by any class. • Use it when you don’t want subclassing for security reason final class Dinosaur { //… } //! class Further extends Dinosaur {} // error: Cannot extend final class 'Dinosaur’ • Note that the fields of a final class can be final or not, as you choose.
Previously we’ve seen the initialization order within a class. • Now let’s take a look at inheritance initialization
class Insect { private int i = 9; protected int j; Insect() { System.out.println("i = " + i + ", j = " + j); j = 39; } private static int x1 = print("static Insect.x1 initialized"); static int print(String s) { System.out.println(s); return 47; } } public class Beetle extends Insect { private int k = print("Beetle.k initialized"); public Beetle() { System.out.println("k = " + k); System.out.println("j = " + j); } private static int x2 = print("static Beetle.x2 initialized"); public static void main(String[] args) { System.out.println("Beetle constructor"); Beetle b = new Beetle(); } } "static Insect.x1 initialized", "static Beetle.x2 initialized", "Beetle constructor", "i = 9, j = 0", "Beetle.k initialized", "k = 47", "j = 39"
JavaBean 49
JavaBeans Properties Naming Rules If the property is not a boolean, the getter method's prefix must be get. For example, getSize() is a valid JavaBeans getter name for a property named "size.” If the property is a boolean, the getter method's prefix is either get or is. For example, getStopped() or isStopped() are both valid JavaBeans names for a boolean property. The setter method's prefix must be set. For example, setSize() is the valid JavaBean name for a property named size. To complete the name of a getter or setter method, change the first letter of the property name to uppercase, and then append it to the appropriate prefix (get, is, or set). Setter method signatures must be marked public, with a void return type and an argument that represents the property type. Getter method signatures must be marked public, take no arguments, and have a return type that matches the argument type of the setter method for that property.