javac *.java
CLASSPATH
.class file, the compiler can also go out looking for corresponding source (.java) files
and compiles them
g++ *.cpp is exactly the same as if you had written g++ ....cpp for each .cpp file in the directory
#include'd files into the source file
class C {
…
void f() {
int x;
}
…
private int x;
}
{}'s)(, exception-handler parameter)
this in constructor or set methods - class vs local scope
this in methods
void f() {
…
Employee e = new Employee(…);
…
}
e is created upon entry to the method; and destroyed upon exit
Employee object is created when new is invoked;
when its lifetime ends is not our concern in Java; in C++ it;s is destroyed when
delete is invoked.
int x, y;
y = 7;
x = y;
// 12 = x; // 12 is not an lvalue
clone):
SomeClass that has (as needed) the methods shown and the following instance variable:
class SomeClass {
…
iprivate int intVar;
private String stringVar;
}
dest.intVar = src.intvar; dest.stringVar = src.stringVar;where
src (the ref var to the source object) and dest (ref var to the destination object) can be the receiver (this, a parameter to the method (we will add the suffix Parm for clarity), or
a local variable; we'll have examples of each below.
new
class SomeClass {
SomeClass(SomeClass srcParm) { // constructor taking object of same class
intVar = srcParm.intVar; // or this.intVar = srcParm.intVar
stringVar = srcParm.stringVar;
}
…
private int intVar;
private String stringVar;
}
Usage:
SomeClass dest = new SomeCLass(src);
class SomeClass {
…
void copy(SomeCLass r1) { // copy method taking object of same class
intVar = r1.intVar; // overwrites existing value of instance variable; again this.intVar = r1.intVar
stringVar = r1.stringVar;
}
}
copy method, the source is the receiver
class SomeClass {
…
SomeClassvoid copy() { // copy method taking object of same class
SomeCLass result = new SomeClass();
result.intVar = intVar1; // or result.intVar = this.intVar
result.stringVar = stringVar;
}
}
SomeClass dest = src.copy();or
dest = src.copy(); // redirects existing reference to new object
class SomeClass {
…
static void copy(SomeCLass destParm, SomeCLass srcParm) {
destParm.intVar = srcParmr1.intVar1;
destParm.stringVar = srcParmr1.stringVar;
}
}
copy(dest, src));
class Integer {
Integer(int val) {this.val = val;}
Integer(Integer integer) {val = integer.val;} // copy constructor
…
void copy(Integer integer) {val = integer.val;} // in-place copy method
…
int val;
}
…
Integer i1 = new Integer(3); // uses 'int' constructor
Integer i2 = new Integer(12); // uses 'int' constructor
Integer i3 = new Integer(i1); // uses copy constructor — clone
Integer i4 = i1; // reference assignment — alias
i2.copy(i3); // in-place copy method — clone
i2 = new Integer(i4); // copy constructor — clone
Some classes are deliberately designed so that their objects — once initialized — cannot be modified
a + b, the result of the operation is
a new value
a += b, the result of the operation is
stored in the left operand
class ImmutableColor {
public ImmutableColor(int r, int g, int b) {
this.r = r;
this.g = g;
this.b = b;
}
…
public ImmutableColor lighter() {
if (r < 255 && g < 255 && b < 255)
return new ImmutableColor(r+1, g+1, b+1)
else
return null;
}
private final int r, g, b;
}
final in the declaration of the instance variables. This is because they will not be modified once initialized in the
constructor.
final variables do not need to be initialized at point of declaration; they can be given their initial (and only) value within
the constructor.
Color object (rather than modifying the receiver).
null if the color cannot be made lighter
class MutableColor {
public MutableColor(int r, int g, int b) {
this.r = r;
this.g = g;
this.b = b;
}
…
public MutableColor makeLighter() {
if (r < 255 && g < 255 && b < 255) {
r++;
g++;
b++
return this;
}
else
return null;
}
private int r, g, b;
}
makeLighter) to better reflect the semantics of the method
MutableColor color = new MutableColor(100, 34, 67); System.out.print(color.makelighter().makeLighter().makeMoreTransparent();
String vs ArrayListString vs ArrayList
String
String, it is therefore referencing a single value, which is not expected to change
String object, one should not be able to change the contents of
the String object as it will affect the value for both references
setCharAt method for String. as that would involve an in-place modification to the String object
String object
String s = "Hello world!"; s = s.substring(0, 5);
ArrayList
ArrayList is a container, essentially containing a dynamically sized array
resize method presented above is at the heart of
the dynamic sizing
ArrayList to change
ArrayList of Employee objects, if we were to iterate the list and give all employees a 10% raise, we
would want that reflected whenever any other part of the system access an employee object.
ArrayList itself is merely the container, it's the elements that are of real interest
ArrayList object, modifications made by one to the contents of the ArrayList object
is (typically) of interest to all
ArrayList methods are therefore in-place; i.e., they do not create a new container; rather they modify
elements in the receiving container
ArrayListaList = new ArrayList (); aList.add(12);
swap(a, b); // a & b are arguments
…
void swap(int x, int y) {…} // x & y are parameters
static void swap(int x, int y) {
int t = x;
x = y;
y = t;
}
static void swap(String x, String y) {
String t = x;
x = y;
y = t;
}
static void swap(int [] arr, int index1, int index2) {
int t = arr[index1];
arr[index1] = arr[index2];
arr[index2] = t;
}
in this scenario, we are not modifying the parameters — in particular the array reference — but rather elements of the object (i.e., the array) it references
class Pair {
Pair(int first, int second) {
this.first = first;
this.second = second;
}
public int first, second; // making them public so we can get to them - not relevant to the discussion
}
Pair pair = new Pair(a, b);
swap(pair);
a = p.first;
b = p.second;
static void swap(Pair p) {
int t = p.first;
p.first - p.second;
p.second = t;
}
again, we are not modifying the parameter (the object reference), but rather what it is referencing — instance variables of
the object (i.e., the Pair) it references
int [] arr = {1, 2, 3};
inc(a);
…
void inc(int [] arr) {
for i in 0; i < arr.length; i++)
arr[i]++;
}
int [] arr = {1, 2, 3};
resize(a);
…
void resize(int [] arr) {
int [] temp = new int [arr.length * 2];
for (int i = 0; i < arr.length; i++)
temp[i] = arr[i];
arr = temp;
}
int [] resize(int [] arr) {
int [] temp = new int [arr.length * 2];
for (int i = 0; i < arr.length; i++)
tempi] = arr[i];
return temp;
}
int [] arr = {1, 2, 3};
…
a = resize(a);
s = s.substring(2, 6);
void f(&x) {
x++
}
x is an alias of a
x is the same thing as referring to a
ArrayList<Integer>
ArrayList<Integer> i_aList = new ArrayList<Integer>();
i_aList.push_back(12);
…
ArrayList<String> s_aList = new ArrayList<String>();
s_aList.push_back("Hello");
ArrayList alist = new ArrayList();
ArrayList class, and it has Object as its element type
vector<int> i_vect;
i_vect.add(12);
…
vector<string> s_vect;
s_vect.add("Hello");
class C {
…
void print(int i) {…}
void print(String s) {…}
…
}
…
f(3); // print(int)
f("Hello"); // print(String)
f(3.5); // ???
f(new Integer(2)); // ???
class C {
…
int print(int i) {…}
double print(int i) {…}
…
}
…
int i = f(3); // compile-time error
double d = f(3); // compile-time error
System.out.println(f(3)); // compile-time error
class C {
…
void print(int i) {…}
…
}
class D extends C {
…
void print(String s) {…}
…
}
class C {
…
void f(int i) {…}
…
}
…
class D extends C {
…
void f(int i) {…}
…
}
C c = new C();
c.f(); // C.f is called
D d = new D();
d.f(); // D.f is called
c = d; // upcast (legal)
c.f(); // D.f is called! … the receiver is a D!
f() vs f(3) detectable by compiler
class Sup {
…
void f() {…}
…
}
class Sub extends Sup {
…
void f() {…}
…
}
class App {
…
Sup sup;
sup = new Sup();
sup.f();
sup = new Sub();
sup.f();
…
}
sup
+ and +=
<< and >>
== and !=
System.out and System.in ??
size and isEmptysize is implemented, isEmpty can be simply coded as
class ArrayList {
int size() {logic to determine number of elements in the container}
boolean isEmpty() {return size() == 0;}
…
}
size to be abstract at the collection superclass (interface) level, we can then actually provide the above definition
of isEmpty (which depends on size) at that level.
abstract class Collection {
abstract int size();
boolean isEmpty() {return size() == 0;} // one isEmpty implementation for all Collection (sub)classes
…
}
class ArrayList extends Collection {
int size() {logic to determine number of elements in an ArrayList}
…
}
class HashSet extends Collection {
int size() {logic to determine number of elements in a HashSet}
…
}
…
toString Methods
class Student {
…
public String toString() {return id + " " + name + "\n" + address + "\n" + courses;}
…
int id;
Name name; // each of these classes has its own toString method
Address address;
ArrayList courses;
}
the toString method leverages the toString methods of its component instance variables that are objects
(class types).
toString of a class from scratch; rather you use the toStrings of the class' instance variables
class C {
C(C c) {this.copy(c)} // leverages / delegates to workhorse
void copy(C c) { // workhorse
this.i = c.i;
this.d = c.d;
this.s = new String(c.s); // deep copy, since we're in a copy method
}
int i;
double d;
String s;
}
+ and +=
+ takes two operands and produces a new value, leaving the operands untouched
+= takes two operands and stores the result in the first operands (the second remains untouched); the value of the expression is the first
operand (containing the result of the operation)
Rational Class
class Rational {
…
Rational mul(Rational r) {…}
Rational mulInPlace(Rational r) {…}
…
int num, denom;
}
Rational r1 = new Rational(1, 2), r2 = new Rational(3, 4); Rational r3 = r1.mul(r2); // r3 is pointing at a new Rational containing 3/8; r1 and r2 are unchanged r1.mulInPlace(r2); // r1 is still pointing at the same object as before, but the num/denom is now 3/8 r2 = r2.mul(r2); // r2 is pointing at a new (different) Rational object containing 9/16
r1.mul(r2) is like r1 * r2 (operation produces new value; operands are not modified)
r1.mulInPlace(r2) is like r1 *= r2 (operation places result in first operand; second is untouched)
size can't call isEmpty
Rational class againa c a * c - * - = ----- b d b * d
mul Method
Rational mul(Rational r) {
Rational result = new Rational(this.num * r.num, this.denom * r.denom);
return result;
}
Rational
mulInPlace Method
Rational mulInPlace(Rational r) {
this.num *= r.num;
this.denom *= r.denom;
return this;
}
void would be acceptable; we return the receiver to allow chaining
r1.mulInPlace(r2).mulInPlace(r3)); // r1 *= r2 * r3;
mulInPlace Leveraging mulmul);
mulInPlace) then leverages mul
Rational mulInPlace(Rational r) {
Rational result = this.mul(r);
this.copy(result);
return this;
}
this.copy(result);
copy method in the class
Rational copy(Rational r) {
this.num = r.num;
this.denom = r.denom;
}
mul is copied to result and then
never used again
mul Leveraging mulInPlacemulInPlace);
mul) then leverages mulInPlace
Rational mul(Rational r) {
Rational result = new Rational(this);
return result.mulInPlace(r);
}
Rational result = new Rational(this);
this), as it contains the result of the operation
mulInPlace's Return ValuemulInPlace to have a return value — the effect of the operation was to modify the receiver —
r1 in the above example r1.mulInPlace(r2)
mul that leverages mulInPlace, we see that having a return value is useful:
return result.mulInPlace(r);as opposed to
result.mulInPlace(r); return result;
mul and mulInPlace:
r1 = r2.mul(r3.mulInPlace(r4));which is equivalent to:
r1 = r2 * r3 *= r4;
class Employee {
…
Name name;
}
name defaults to null (as mentioned above), i.e., it
needs to be initialized to some string before it can be used.
name prior to such initialization result in a NullPointerException …
NullPointerException means that the error occurs as soon as we try to work with
name; as opposed to working with 'garbage' data which may not result in an error until much
later in the code.
class Employee {
…
Name name; // This poses a problem of initialization
};
name is an actual Name object,
not a reference.
class Window {
Window(int width, int height) {
this.width width;
this.height - height;
}
int getWidth() {return width;}
int getHeight() {return height;}
private int width, height;
}
class BorderedWindow extends Window {
Borderedwindow(boolean thinBorder, int width, int height) {
super(width, height);
if (thinBorder) {
borderWidth = getWidth() * .05; // requires that width of superclass has been initialized
borderHeight = getHeight() * .05;
}
else {
borderWidth = getWidth() * .1;
borderHeight = getHeight() * .1;
}
}
int borderWidth, borderHeight;
}
Window, and thus does not interact with its constructor
Window) portion is created as part of the subclass (BorderedWindow) to invoke the constructor of the superclass (Window)
BorderedWindow)constructor invokes the superclass' (Window) using the super keyword …