class JustAStruct { // no access modifiers, no methods, no constructors String name; int value; } public class ClassDemos { public static void main( String[] args ) { JustAStruct struct = new JustAStruct(); struct.name = "abcxyz"; struct.value = 123; System.out.println( "struct is " + struct.name + "--" + struct.value ); } } // struct is abcxyz--123Here, two simulated free functions have been added to model how OO programming used to be implemented in C: create data structions, create algorithms, and pass data structures to algorithms.
class JustAStruct { String name; int value; } public class ClassDemos { public static void set( JustAStruct str, String nam, int val ) { str.name = nam; str.value = val; } public static void report( JustAStruct str ) { System.out.println( "struct is " + str.name + "--" + str.value ); } public static void main( String[] args ) { JustAStruct struct = new JustAStruct(); set( struct, "abcxyz", 123 ); report( struct ); } } // struct is abcxyz--123A class is the co-location of state and behavior, or, the encapsulation of implementation behind a published interface. We have transitioned the "struct" into a class by:
class ARealClass { // private state, public methods private String name; private int value; public void set( String nam, int val ) { name = nam; value = val; } public void report() { System.out.println( "object is " + name + "--" + value ); } } public class ClassDemos { public static void main( String[] args ) { ARealClass object = new ARealClass(); object.set( "abcxyz", 123 ); object.report(); } } // object is abcxyz--123Initializing variables at the same time as they are created has always been a strongly encouraged practice. Classes provide a language feature called "constructors" for just that purpose. Whenever an object is created, a constructor from its class is called automatically so that the object can be put in an "appropriate initial state".
The compiler recognizes constructor definitions because they: have the same name as the class, have no return type, and accept zero or more arguments. In the example below, the constructor provides default values of "xxx" and 999 for the class's two private data members.
class ARealClass { // constructor added private String name; private int value; public ARealClass() { System.out.println( "ARealClass constructor" ); set( "xxx", 999 ); } public void set( String nam, int val ) { name = nam; value = val; } public void report() { System.out.println( "object is " + name + "--" + value ); } } public class ClassDemos { public static void main( String[] args ) { ARealClass object = new ARealClass(); object.report(); object.set( "abcxyz", 123 ); object.report(); } } // ARealClass constructor // object is xxx--999 // object is abcxyz--123Unlike C++, if the programmer forgets, and specifies a return type for a member function intended to serve as a constructor; Java will treat that function as a normal member function. The code below compiles, and the client of class ARealClass can call the method void ARealClass() just like any other member function.
class ARealClass { // constructors do not have a return type public ARealClass() { System.out.println( "constructor" ); } public void ARealClass() { System.out.println( "void method" ); } } public class ClassDemos { public static void main( String[] args ) { System.out.println( "main" ); ARealClass object = new ARealClass(); object.ARealClass(); } } // main // constructor // void methodFunction overloading was discussed in the Basics section. Below, the set() function can be called with 0, 1, or 2 arguments. Constructors can also be overloaded. That is not happening in the example. The only way to create an instance of class ARealClass is by supplying a single String argument. In main(), when no arguments are supplied, the compiler complains that no arguments is not an option.
In both Java and C++, if a class does not define any constructors, then a no-argument constructor (or "default constructor") is provided by the compiler automatically. But, as soon any explicit constructors are defined, then a default constructor is not supplied by the compiler.
class ARealClass { // function overloading, missing constructor private String name; private int value; public ARealClass( String name ) { System.out.println( "ARealClass constructor" ); set( name ); } public void set() { set( "xxx", 999 ); } public void set( String name ) { set( name, 999 ); } public void set( String nam, int val ) { name = nam; value = val; } public void report() { System.out.println( "object is " + name + "--" + value ); } } public class ClassDemos { public static void main( String[] args ) { // ARealClass object = new ARealClass(); // Error: No constructor matching ARealClass() found in class ARealClass ARealClass object = new ARealClass( "aaa" ); object.report(); object.set( "abcxyz", 123 ); object.report(); } } // ARealClass constructor // object is aaa--999 // object is abcxyz--123Four overloaded constructors are demonstrated below. There are now 4 ways of creating instances of this class.
class ARealClass { private String name; private int value; public ARealClass() { System.out.println( "ARealClass default constructor" ); set( "xxx", 999 ); } public ARealClass( String s ) { System.out.println( "ARealClass 1-String-arg constructor" ); set( s, 999 ); } public ARealClass( int v ) { System.out.println( "ARealClass 1-int-arg constructor" ); set( "xxx", v ); } public ARealClass( String s, int v ) { System.out.println( "ARealClass 2-arg constructor" ); set( s, v ); } public void set( String s, int v ) { name = s; value = v; } public void report() { System.out.println( "object is " + name + "--" + value ); } } public class ClassDemos { public static void main( String[] args ) { ARealClass obj1 = new ARealClass(); ARealClass obj2 = new ARealClass( "second" ); ARealClass obj3 = new ARealClass( 333 ); ARealClass obj4 = new ARealClass( "fourth", 444 ); obj1.report(); obj2.report(); obj3.report(); obj4.report(); obj1.set( "abcxyz", 123 ); obj1.report(); } } // ARealClass default constructor // ARealClass 1-String-arg constructor // ARealClass 1-int-arg constructor // ARealClass 2-arg constructor // object is xxx--999 // object is second--999 // object is xxx--333 // object is fourth--444 // object is abcxyz--123This is a group exercise. It is just like the first several examples at the top. We start with a data structure and algorithms, and the exercise is to convert it to a class with private attributes and public methods.
class RunningTotal { int total; int count; } public class ClassDemos { public static void add( RunningTotal str, int num ) { str.total += num; str.count++; } public static int getTotal( RunningTotal str ) { return str.total; } public static double getAverage( RunningTotal str ) { return (double)str.total / str.count; } public static void main( String[] args ) { RunningTotal rt = new RunningTotal(); rt.total = 0; rt.count = 0; System.out.print( "Add(a), Total(t), Avg(v), Exit(x): " ); char ch = Read.aChar(); while (ch != 'x') { if (ch == 'a') { System.out.print( " enter an integer: " ); add( rt, Read.anInt() ); } else if (ch == 't') System.out.println( " total is " + getTotal( rt ) ); else if (ch == 'v') System.out.println( " average is " + getAverage( rt ) ); System.out.print( "Add(a), Total(t), Avg(v), Exit(x): " ); ch = Read.aChar(); } } } // Add(a), Total(t), Avg(v), Exit(x): a // enter an integer: 5 // Add(a), Total(t), Avg(v), Exit(x): a // enter an integer: 6 // Add(a), Total(t), Avg(v), Exit(x): a // enter an integer: 7 // Add(a), Total(t), Avg(v), Exit(x): t // total is 18 // Add(a), Total(t), Avg(v), Exit(x): v // average is 6.0 // Add(a), Total(t), Avg(v), Exit(x): a // enter an integer: 8 // Add(a), Total(t), Avg(v), Exit(x): t // total is 26 // Add(a), Total(t), Avg(v), Exit(x): v // average is 6.5 // Add(a), Total(t), Avg(v), Exit(x): xThe "school solution". Unlike C++, Java allows data members to be statically initialized without the requirement for a constructor.
class RunningTotal { private int total = 0; private int count; public RunningTotal() { total = count = 0; } public void add( int num ) { total += num; count++; } public int getTotal() { return total; } public double getAverage() { return (double)total / count; } } public class ClassDemos { public static void main( String[] args ) { RunningTotal rt = new RunningTotal(); System.out.print( "Add(a), Total(t), Avg(v), Exit(x): " ); char ch = Read.aChar(); while (ch != 'x') { if (ch == 'a') { System.out.print( " enter an integer: " ); rt.add( Read.anInt() ); } else if (ch == 't') System.out.println( " total is " + rt.getTotal() ); else if (ch == 'v') System.out.println( " average is " + rt.getAverage() ); System.out.print( "Add(a), Total(t), Avg(v), Exit(x): " ); ch = Read.aChar(); } } } ////////////////////////////// lab - BasicClass \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\Java provides a way for the programmer to tell the compiler how to "map" from one constructor to another. The construct is called the "this function" here. If you use this construct, it has to be the first executable line of code in the body of a constructor. It really serves as a weak replacement for Java's lack of support for default arguments.
class ARealClass { // "this" function - private String name; // mapping from one ctor to another private int value; public ARealClass() { this( "xxx", 999 ); } public ARealClass( String s ) { this( s, 999 ); } public ARealClass( int v ) { this( "xxx", v ); } public ARealClass( String s, int v ) { System.out.println( "ARealClass 2-arg constructor" ); set( s, v ); } public void set( String s, int v ) { name = s; value = v; } public void report() { System.out.println( "object is " + name + "--" + value ); } } public class ClassDemos { public static void main( String[] args ) { ARealClass obj1 = new ARealClass(); ARealClass obj2 = new ARealClass( "second" ); ARealClass obj3 = new ARealClass( 333 ); ARealClass obj4 = new ARealClass( "fourth", 444 ); obj1.report(); obj2.report(); obj3.report(); obj4.report(); obj1.set( "abcxyz", 123 ); obj1.report(); } } // ARealClass 2-arg constructor // ARealClass 2-arg constructor // ARealClass 2-arg constructor // ARealClass 2-arg constructor // object is xxx--999 // object is second--999 // object is xxx--333 // object is fourth--444 // object is abcxyz--123Whenever an object is passed to println(), its class name and an address are output by default.
class ARealClass { // toString() function private String name; private int value; public ARealClass( String s, int v ) { name = s; value = v; } } public class ClassDemos { public static void main( String[] args ) { ARealClass object = new ARealClass( "abcxyz", 123 ); System.out.println( "object is " + object ); } } // object is ARealClass@f10d6714If you would like more meaningful output, then you can define a toString() method, and it will be called automatically when an instance of the class is passed to println(). Of course, you can also treat it like any other public member function, and call it directly.
class ARealClass { // toString() function private String name; private int value; public ARealClass( String s, int v ) { name = s; value = v; } public String toString() { return name + "--" + value; } } public class ClassDemos { public static void main( String[] args ) { ARealClass object = new ARealClass( "abcxyz", 123 ); System.out.println( "object is " + object ); System.out.println( "object is " + object.toString() ); } } // object is abcxyz--123 // object is abcxyz--123This is a puzzle. See if you can guess what functionality has been encapsulated in the class BlackBox before you review its implementation below.
public class ClassDemos { // BlackBox - a demo class and a riddle public static void main( String[] args ) { BlackBox bb = new BlackBox(); while (true) { System.out.print( "+1(i) -1(d) x2(t) /2(h) ^2(s): " ); char ch = Read.aChar(); if (ch == 'i') bb.increment(); else if (ch == 'd') bb.decrement(); else if (ch == 't') bb.dubble(); else if (ch == 'h') bb.half(); else if (ch == 's') bb.square(); System.out.println( " BlackBox is " + bb ); } } } // C:> java ClassDemos // +1(i) -1(d) x2(t) /2(h) ^2(s): i // BlackBox is 1 // +1(i) -1(d) x2(t) /2(h) ^2(s): t // BlackBox is 2 // +1(i) -1(d) x2(t) /2(h) ^2(s): i // BlackBox is 3 // +1(i) -1(d) x2(t) /2(h) ^2(s): s // BlackBox is 11 // +1(i) -1(d) x2(t) /2(h) ^2(s): d // BlackBox is 10 // +1(i) -1(d) x2(t) /2(h) ^2(s): d // BlackBox is 7 // +1(i) -1(d) x2(t) /2(h) ^2(s): s // BlackBox is 61The class BlackBox is just a "wrapper" around a single private int attribute. The only "magic" occurs in the toString() method, where the decimal value is converted to octal.
class BlackBox { private int value = 0; public void increment() { value++; } public void decrement() { value--; } public void dubble() { value *= 2; } public void half() { value /= 2; } public void square() { value *= value; } public String toString() { return Integer.toOctalString( value ); } }In C++, the first line of main() would actually allocate 3 instances of class ARealClass, and call the default constructor for each instance. In Java, you cannot allocate an array of default-initialized objects. The first new below allocates an array of ARealClass object references. The second new allocates each individual ARealClass object. If you know at compile-time how many objects need to be created, then you can collapse the two steps into something like the last logical line of main().
class ARealClass { // array of objects public ARealClass() { System.out.println( "default constructor" ); } public ARealClass( int i ) { System.out.println( "constructor " + i ); } } public class ClassDemos { public static void main( String[] args ) { ARealClass[] arr = new ARealClass[3]; // only allocates the array for (int i=0; i < 3; i++) arr[i] = new ARealClass( i ); // allocates each object System.out.println(); // allocates the array AND allocates each object ARealClass[] arr2 = { new ARealClass(), new ARealClass(3), new ARealClass(4), new ARealClass(5) }; } } // constructor 0 // constructor 1 // constructor 2 // // default constructor // constructor 3 // constructor 4 // constructor 5Here is a very rough checklist that can be used for architecting a class.
class RandomHuston { private int max; public RandomHuston( int m ) { max = m; } public int getNumber() { return (int) (Math.random() * max); } } public class ClassDemos { public static void main( String[] args ) { RandomHuston generator = new RandomHuston( 10 ); for (int i=0; i < 35; i++) System.out.print( generator.getNumber() + " " ); } } // 5 2 7 5 8 3 0 2 1 4 2 9 2 8 7 0 4 9 7 2 7 5 7 3 2 9 3 2 3 2 4 2 4 8 0In the example above, the only reason for having class ClassDemos is to be the "host" (or "repository") for main(). We could just as well collapse the two classes into one class. But that one class would have to have the name of its enclosing ".java" file. So in this case, the class RandomHuston has been retired.
public class ClassDemos { // class is the test driver private int max; public ClassDemos( int m ) { max = m; } public int getNumber() { return (int) (Math.random() * max); } public static void main( String[] args ) { ClassDemos generator = new ClassDemos( 10 ); for (int i=0; i < 35; i++) System.out.print( generator.getNumber() + " " ); } } // 9 6 0 1 0 3 3 5 5 1 4 7 2 0 2 3 4 3 3 5 2 0 5 0 1 0 6 2 1 8 1 1 4 3 9 ////////////////////////////// lab - GuessGame \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ ////////////////////////////// lab - StackLab \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ ////////////////////////////// lab - IntegerLab \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\Here is another example that can be used to reflect on the rough checklist presented above.
class RandomRange { // private int min; private int range; public RandomRange( int minn, int max ) { min = minn; range = max - min + 1; } public int getNumber() { return (int)(Math.random()*1000) % range + min; } } public class ClassDemos { public static void main( String[] args ) { if (args.length != 2) { System.out.println( "usage: ClassDemos min max" ); return; } int min = Integer.parseInt(args[0]); int max = Integer.parseInt(args[1]); RandomRange generator = new RandomRange( min, max ); int[] arr = new int[max+3]; for (int i=0; i < 100; i++) arr[ generator.getNumber() ]++; for (int i=0; i < arr.length; i++) System.out.print( (i<10?" ":"") + i + " " ); System.out.println(); for (int i=0; i < arr.length; i++) System.out.print( (arr[i]<10?" ":"") + arr[i] + " " ); System.out.println(); for (int i=0, total=0; i < arr.length; i++) { total += arr[i]; System.out.print( (total<10?" ":"") + total + " " ); } System.out.println(); } } // D:\Java> java ClassDemos // usage: ClassDemos min max // // D:\Java> java ClassDemos 1 6 // 0 1 2 3 4 5 6 7 8 // 0 14 23 22 9 18 14 0 0 // 0 14 37 59 68 86 100 100 100 // // D:\Java> java ClassDemos 2 4 // 0 1 2 3 4 5 6 // 0 0 33 32 35 0 0 // 0 0 33 65 100 100 100 // // D:\Java> java ClassDemos 3 4 // 0 1 2 3 4 5 6 // 0 0 0 53 47 0 0 // 0 0 0 53 100 100 100 ////////////////////////////// lab - NumberLab \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ ////////////////////////////// lab - FractionLab \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\static members (class attributes and class methods) work the same in Java as in C++. static attributes are shared across all instances of the class. Any object of the class can read and/or write to any static attribute. When one object changes the value of a static attribute, all objects will be affected. Unlike C++, static attributes can be initialized in the same line where they are declared.
static methods may be used before any instances of the class have been created. They can be accessed globally (just like C free functions) by simply prepending the class name and the "dot" operator. static methods (which include the main() function) may not access any data members that are not themselves static. Attempting to access non-static members will cause a compiler error.
class ARealClass { // "static" private int notShared; // class attribute private static int shared = 1; // class method public ARealClass() { // non-static member functions can notShared = shared++; // access all data members } public String toString() { return "id-" + notShared + ",shared-" + shared; } public void set( int in ) { shared = in; } public static int get() { // notShared++; // COMPILER ERROR! return shared; // static member functions can only } } // access static data members public class ClassDemos { public static void main( String[] args ) { ARealClass first = new ARealClass(); ARealClass second = new ARealClass(); first.set( 42 ); System.out.println( "first is " + first ); System.out.println( "second is " + second ); System.out.println( "shared is " + ARealClass.get() ); // NOT recommended - not obvious that this is a static method System.out.println( "shared is " + second.get() ); } } // first is id-1,shared-42 // second is id-2,shared-42 // shared is 42 // shared is 42The class ARealClass1 is defined at global scope. That means: it is globally accessible (which makes things easy, but less encapsulated), and it "pollutes" the global namespace (could conflict with some class name that already exists). If we define a class inside another class, then: it is less accessible, it doesn't "pollute", and it has access to the private members of the enclosing class. The bottom line is - there are trade-offs.
A class defined within another class can only be instantiated if the enclosing class has already been instantiated. This is different than the way it works in C++. There is a problem below in trying to create an instance of class ARealClass2. To make things more intuitive and easier, an "inner" class can be defined as static.
The code below produces the following files: ARealClass1.class, ClassDemos.class, ClassDemos$ARealClass2.class, and ClassDemos$ARealClass3.class.
class ARealClass1 { } // static inner class public class ClassDemos { class ARealClass2 { } static class ARealClass3 { } public static void main( String[] args ) { ARealClass1 a = new ARealClass1(); // ARealClass2 b = new ARealClass2(); // Error: No enclosing instance of class ClassDemos is in scope; // an explicit one must be provided when creating inner class // ClassDemos.ARealClass2, as in "outer. new Inner()" ARealClass2 b = new ClassDemos(). new ARealClass2(); ClassDemos c = new ClassDemos(); ARealClass2 d = c. new ARealClass2(); ARealClass3 e = new ARealClass3(); } }main() is a static member function of class ClassDemos. That means it can access other static members of the class, even private members. main() is not a member of class ARealClass. That means it can only access public and "friendly" members of that class.
class ARealClass { // static, private, friendly private static int privateAttribute; static int friendlyAttribute; // friendly } public class ClassDemos { private int nonStaticAttribute; private static int privateStaticAttribute; public static void main( String[] args ) { nonStaticAttribute = 42; // COMPILER ERROR! privateStaticAttribute = 42; // not an error ARealClass.privateAttribute = 24; // COMPILER ERROR! ARealClass.friendlyAttribute = 24; // not an error } }Beyond constructors, Java provides "anonymous" initialization blocks. The static initialization block is only executed the first time the class is loaded by the Java Virtual Machine. Notice that the ClassDemos static block happens immediately - because it is the class where main() is hosted. The ARealClass static block doesn't happen until the first reference to the class occurs. A non-static initialization block is executed every time an instance is created. Notice that it duplicates the functionality of the default constructor.
class ARealClass { static { System.out.println( "class initialization block" ); } { System.out.println( "instance initialization block" ); } public ARealClass() { System.out.println( "default constructor" ); } } public class ClassDemos { static { System.out.println( "ClassDemos static initialization block" ); } public static void main( String[] args ) { System.out.println( "main" ); new ARealClass(); new ARealClass(); } } // ClassDemos static initialization block // main // class initialization block // instance initialization block // default constructor // instance initialization block // default constructor ////////////////////////////// lab - Counted \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ ////////////////////////////// lab - SelfNamingLab \\\\\\\\\\\\\\\\\\\\\\\\\\\\\Just like people exchange business cards so that one individual will have the information necessary to make a "call back" on the other, objects can also exchange "references". The this reference represents the "address" of the current object. An object can pass its this reference to another, so that the other can subsequently send it a message. Below, the object that has had its first() method invoked, passes its this reference to the object it was passed as it invokes second(), so that the other object can invoke last() on it.
class ARealClass { // "this" reference - private String name; // an obj passing its "address" to another public ARealClass( String n ) { name = n; } public void first( ARealClass other ) { System.out.println( name + " is first" ); other.second( this ); } public void second( ARealClass other ) { System.out.println( name + " is second" ); other.last(); } public void last() { System.out.println( name + " is last" ); } } public class ClassDemos { public static void main( String[] args ) { ARealClass one = new ARealClass( "one" ); ARealClass two = new ARealClass( "two" ); one.first( two ); two.first( one ); } } // one is first // two is second // one is last // two is first // one is second // two is lastHere is another example of the same kind of design. main() creates a single Node object, and Node's constructor takes care of creating the next 5 Nodes. As each downstream Node is created, the upstream Node passes its "address" so that the downstream Node can intialize its "prev" attribute.
public class ClassDemos { // "this" reference - static class Node { // an obj passing its "address" to another private int value; private Node prev; private Node next; public Node( int v, Node p ) { value = v; prev = p; next = ((v == 6) ? null : new Node( v+1, this )); } public void goDown() { System.out.print( value + " " ); if (next != null) next.goDown(); else prev.goBack(); } public void goBack() { System.out.print( value + " " ); if (prev != null) prev.goBack(); } } public static void main( String[] args ) { Node root = new Node( 1, null ); root.goDown(); } } // 1 2 3 4 5 6 5 4 3 2 1Another purpose served by the this reference is returning the "address" of the current object from the current method invocation, so that a subsequent method invocation can be "cascaded" (or "chained") to the previous invocation. Here, both doThis() and doThat() promise to return an object of type ARealClass, and they follow through on that promise by returning their this reference. The main() is now free to ask that returned object to perform any service its class's public interface supports.
class ARealClass { // "this" reference - private String name; // an obj returning its "address" for cascading public ARealClass( String n ) { name = n; } public ARealClass doThis( String str ) { System.out.println( name + " doThis " + str ); return this; } public ARealClass doThat( String str ) { System.out.println( name + " doThat " + str ); return this; } } public class ClassDemos { public static void main( String[] args ) { ARealClass object = new ARealClass( "one" ); object.doThis( "here" ).doThat( "there" ).doThis( "every where" ); } } // one doThis here // one doThat there // one doThis every where ////////////////////////////// demo - DateHuston \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ ////////////////////////////// lab - Processor \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ ////////////////////////////// lab - LinkedListLab \\\\\\\\\\\\\\\\\\\\\\\\\\\\\Here is an example of aggregation - a class that has attributes that are instances of other classes. Whenever an instance of class ClassDemos is created, the constructor of ClassDemos is not called until all of the attributes have been initialized. Note that the Stereo attribute could also have been initialized when it was declared, but it is different just to demonstrate flow of control.
class ClassDemos { // aggregation or "hasa" relationship static class Engine { Engine() { System.out.println( "engine" ); } } static class Wheel { Wheel() { System.out.println( "wheel" ); } } static class Stereo { Stereo() { System.out.println( "stereo" ); } } private Engine eng = new Engine(); private Wheel[] whs = { new Wheel(), new Wheel() }; private Stereo ste; public ClassDemos() { System.out.println( "ClassDemos - car" ); ste = new Stereo(); } public static void main( String[] args ) { new ClassDemos(); System.out.println(); new ClassDemos(); } } // engine // wheel // wheel // ClassDemos - car // stereo // // engine // wheel // wheel // ClassDemos - car // stereoAggregation ("has a") is a "whole-part" relationship. When the containing class is instantiated, all the contained objects are automatically instantiated. The life-time of the contained objects are entirely determined by the life-time of the container object. The container object "owns" the contained objects.
Association ("uses a") is a "peer-peer" relationship. The life-times of the objects participating in this informal affiliation are not in any way tied to each other. They each hold a reference to the other for the purpose of collaboration or dialogue.
In this example, the "spouse" attribute is modelling the tighter, more fundamental relationship of aggregation. The "other" attribute is modelling the looser, more temporal relationship of association.
class PersonClass { private String name; private PersonClass spouse; // hasa => ownership private PersonClass other; // usesa => collaboration, simple dialogue public PersonClass( String n ) { name = n; } public PersonClass( String n, String s ) { this( n ); // the containing object creates the contained object System.out.println( name + " is creating " + s ); spouse = new PersonClass( s ); } public void meet( PersonClass o ) { greet( o ); other.greet( this ); } public void greet( PersonClass o ) { System.out.println( name + " is remembering " + o.name ); // the peer object remembers a reference to the other peer other = o; } public void talk() { System.out.println( name + " is talking" ); other.listen(); // tell peer object to listen spouse.listen(); // tell contained object to listen } public void listen() { System.out.println( " " + name + " is listening" ); } } public class ClassDemos { public static void main( String[] args ) { PersonClass fred = new PersonClass( "Fred", "Wilma" ); PersonClass barney = new PersonClass( "Barney", "Betty" ); fred.meet( barney ); System.out.println(); fred.talk(); System.out.println(); barney.talk(); } } // Fred is creating Wilma // Barney is creating Betty // Fred is remembering Barney // Barney is remembering Fred // // Fred is talking // Barney is listening // Wilma is listening // // Barney is talking // Fred is listening // Betty is listening ////////////////////////////// lab - Hasa \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ ////////////////////////////// lab - MasterMind \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
Modifier |
class |
data member |
member function, ctor |
public | Yes - may be accessed by anyone, anywhere | Yes - ditto | Yes - ditto |
protected | No/Yes - only inner classes may be protected | Yes - may be accessed by all subclasses, or by any class in the same package, as the class that owns the feature. More accessible than "friendly" features | Yes - ditto |
friendly | Yes - the default. May be accessed by any class in the same package. May NOT be used by subclasses that are not in the same package. | Yes - ditto | Yes - ditto |
private | No/Yes - only inner classes may be private | Yes - may only be accessed by an instance of the class | Yes - ditto |
final | Yes - a final class may not be subclassed (e.g. String) | Yes - a final data member may not be modified | Yes - a final member function may not be overridden |
abstract | Yes - may not be instantiated, it must be subclassed | No | Yes - no definition or body is provided in the defining class, the implementation must be provided by a subclass. A class with one or more abstract methods must itself be abstract. Any subclass of this class must define a body for the method, or declare itself to be abstract. |
static | Sortof - a class may have block(s) of initialization code that are surrounded by curly braces and labeled static. This code is executed exactly once, at the time the class is loaded. | Yes - shared by all instances of the class | Yes - can be accessed without an instance of the class, cannot reference any non-static members of the class |
native | No | No | Yes - the implementation of the method is to be found in an "outside" library. Native code is written in a different language and compiled for a single target machine type. |
transient | No | Yes - not output or serialized, not stored when instances of the class are "persisted" | No |
synchronized | No | No | Yes - the instance is "locked" while the method is running - no other threads may access any synchronized methods until the original method completes |
If you do not specify an access modifier for a class member it will be assigned "package" (or "friendly") accessibility. This means it is usable by all class methods in the same package. [It also means it is only usable by those methods (the glass is half empty).]
If a Java source file does not specify a package statement as the first non-comment statement in the file, then the classes contained in that file will be put in an unnamed default package that is always imported by other source files.A class member may be assigned the access modifier of private protected. This is like the "protected" feature in C++. The member is accessible to derived classes, but not to classes in the same package.
Java specifies that methods may be overridden to be less "private", but they may NOT be overridden to be more "private". Given the list of ascending "public-ness", you may change the access modifier of an overridden method in derived classes by moving "down" the list.
class ClassDemos { public final int id; private static int next = 1; public ClassDemos() { id = next++; } public static void main( String[] args ) { ClassDemos[] array = { new ClassDemos(), new ClassDemos(), new ClassDemos() }; // array[0].id = 99; // COMPILER ERROR! for (int i=0; i < array.length; i++) System.out.print( array[i].id + " " ); System.out.println(); } } // 1 2 3