Java for programmers

home

 

Java is roughly a simplified C++ using Reference Types (including garbage collection that runs when it feels like it). It prefers to be feature-lite, for example no call-by-reference and no properties.

Misc

For reference types, == is pointer compare. You have to override equals(T other) to get a real compare. Confusingly, classes you write get a free useless equals -- all it does is compare pointers, the same as ==.

final means constant (but note it has 2 other meanings, in classes). Using a possibly uninitialized variable is an error. Java does not have paren-less properties (but, strangely, array size, and nothing else, is A.length).

The dot is double-used for member access and also for scope resolution: c1.useThisCat() and also Cats.useaCat(c1).

Types are int, double, float (0.1f), char ('a'), boolean (lower-case true/false), and String. Note that String is upper-case, to remind us that it's an object (but it acts like a value-type -- Strings are immutable).

boolean b = true; // longer than normal
final float f = 0.1f; // constant

The foreach loop syntax is odd: for(Cat c : CatList). The requirement to list the type (Cat c) seems extra, but it's required.

// a foreach loop:
for(int n : MyArray) System.out.println("value is " + n);

Basic arrays are Reference types, always made with new. The type looks like itemType[]. They have one function: length (no parens). They are range-checked (throwing ArrayIndexOutOfBoundsException).

int[] A = new int[7];
void useArray(Cat[] C) ...

There's no call-by-reference. It was deliberately removed from the language. If you've seen C#, Java has nothing like a struct. That's seen as overly complicated with dubious benefits to speed.

Main entry

Java starts from main(string[] Args), the same as C++, except it must be inside of a dummy class, with the same name as the file:

// jtest.java:
import java.io.*; // typical leading imports

public class jtest {
  public static void main(String[] ags) throws IOException {
    System.out println("main entry)");
  }
}

namespaces

Java imposes a rigid structure on files. Namespaces (which it calls packages) must be in a directory with that name -- if you want a utils namespace, you need a utils directory, and every utils-file must be in it. Sub-namespaces (utils.math) require sub-directories.

Public classes must be in a file named after them. Generally one class per file. But a file can include, not as the first thing, any number of non-public "helper" classes. And, of course, nested classes are fine. Sample structure:

// file system:
Animals // everything in the Animal namespace must be here:
  Dogs.java // Dog class, plus non-public dog-using classes
  Cats.java // Cat class

To officially be in a namespace (a package) the start of the file needs package pckgName. It's a bit redundant. There's no reason not to include it (why would you put a file inside Animals otherwise?), and the only option for your package is the name of the directory. Sample cats.java:

// Cats.java:
package Animals; // must be Animals, since we're in that directory

public class Cats { ... } // class must be named Cats, to match filename

// non-public, so naming rules don't apply:
class CatHelper1 { ... }
class CatHelper2 { ... }

Includes (importing packages) are fairly flexible. Of course you can use Animals.Cats everywhere. Or a star gets everything, or list what you want by name:

import Animals.*; // cats and dogs
import Animals.Cats; // only cats:

public class UseAnimals {
  Animals.Dogs d1; // full path name, for fun
  Cats c1; // since we imported

Anything not in a package (without the package xyz; on top), goes into a global namespace. These files can be anywhere (but putting them anywhere but at the top seems confusing).

Java wants everything to be a class. To create naked global functions and variables, use a fake class with all static members. The common form is final to prevent inheritance and a private constructor to precent creating an instance:

// directory utils, file catUtils:

package utils; // include this in a package, since why not

final class catUtils {
  private catUtils() {} // private constructor prevents "new"
  
  public int oldestCat=24; // global variable
  
  public static bool checkCats(Cat c1, Cat c2) { ... } // global function
}

There's a special import syntax to get all or some static members of a class:

import static catUtils.*; // get all
import static catUtils.checkCats; // select only this

  // ...
  if( checkCats(c1,c2) ) // instead of the usual catUtils.checkCats

Java private and public work the usual way -- within the class only, or everyone. There's no file scope. Surprisingly, the default scope is package -- everything in the same namespace. There's no keyword for this. For example:

class Cat {
  int socSec; // package scope
  ...
 }

Cat c1; c1.socSec=6; // anyone in Animals can do this, no one outside

I suppose you can think of this as C++'s friend functions. Anyone in the same package is assumed to know enough to not mess those variables up.

Classes

From within a constructor, this(x,y) calls another. this-dot is optional in front of every member variable. Exs:

public class Animal() {
  String name; // note: capital S, package scope
  
  public Animal(String name) { this.name = name; }
  public Animal() { this("no name"); } // call the other constructor
}

Only single inheritance is allowed, using extends. Instead of the optional this-dot, inherited variables may have super-dot in front. Calling your superclass constructor is super(x,y). If you use it, it must be the first thing:

class Animal { // boring superclass
  public String name;
  public int age;
  public Animal(int age, String name) { this.age=age; this.name=name; }
}

class Cat extends Animal { // subclass using extends
  public int cuteness;
  
  // calling inherited constructor with super:
  public Cat(int age, String name) { super(age,name); this.cuteness=5; }
  
  // using inherited vars (super and this are optional):
  public int abc() { return super.age + this.cuteness; }
}

Class functions are always virtual. There are no keywords for it because anything in a base class can always be overridden, and any subclass function shadowing another is always an override. It's so simple that you don't even need to know anything -- functions just work the "normal" way!

But, to avoid misspellings, you're now encouraged to use the optional decorator @override. Its only purpose is to throw compile errors (if there was nothing to override):

  // throws an error if superclass has no cost function. Optional:
  @override
  public void cost() { ... }

final in front of the class name disallows subclassing. If you know C#, it's the original sealed. final in front of a function disallows overriding that one function:

// no sublasses of Cat:
final class Cat extends Animal { // note that we we be a subclass

  // scratch can't be overloaded (redundant since no sub-Cat's):
  public final void scratch() { ... }
}

Java pioneered fake multiple inheritance using formal abstract interfaces. These can only have functions, implicitly public and abstract. Interfaces are inherited with implements, with a comma for more than one:

// two interfaces:
public interface pettable { void petFor(double amount); }
public interface buyable { double cost(); }

class Cat extends Animal implements pettable, buyable {
  ...
  public void petFor(double timeSecs) { ... }
  public float cost { return 5; }
}

Very confusingly, interfaces can have static variables, but without the static in front (so they look like normal variables, but aren't):

public interface cowStuff {
  int maxCows; // a static
  ...
}

cowStuff.maxCows=9;

Nested classes get free backpointers

Nested classes (which Java calls Inner Classes) have an invisible free backlink to the parent class that created them. Obviously, this means they must be created by an instance of that class. Member functions do that automatically. This is actually very intuitive. Here Tree is a nested class in Forest and can use naked variables (temperature) from the Forest which created it:

class Forest {
  public int temperature; // <- our Trees can see this
  
  void makeATree() {
  	Tree tr = new Tree()); // <-- this tree automatically backlinks to this Forest
	...
  }
  
  // nested Tree class:
  class Tree {
    public grow() {
      height += temperature/10; // <- temperature of my forest
      // alternately: += Forest.this.temperature/10;
    }
    
    public float height;    
  } // end Tree
} // end Forest

This is the way many new users assume nested classes work anyway: if it has no implicit link to an instance of the enclosing class, why did you make it nested?

If you're making a Tree from outside a Forest (which will probably never will), you need to select an owning Forest and put it before the new:

Forest f1 = new Forest();
Tree t1 = f1.new Tree(); // <- special syntax to specify the owning forest

There's no rule preventing you from using the "wrong" nested class. Here f2 uses a Tree owned by f1:

Forest f1, f2;
Forest.Tree t1 = f1.new Tree(); // t1 owned by f1
f2.useTree(t1); // t1's member functions will use f1's variables, not f2's

If you want to make a "normal" nested class (with no magic backpointer and no rules requiring an owning outer class) add static: static public class Tree.

Auto-Boxing

Like many languages with reference types, Java would be happy with no value types at all. As a hack, it provides classes for all basic types, with automatically conversion back and forth. This is called "boxing".

The class types are Integer, Float, Double, Character, and Boolean:

 Integer n1 = new Integer(5); // the long way
 n1=8; // auto-convert int to Integer
 int n2 = n1; // ditto,"unboxing" n1 Integer into n2 int
 
 Integer n3 = 9; // becomes: new Integer(9)
 n3=null; // fine, since these are references
 

The general idea is you'll often need to use a reference type, like sorting a list. This allows that, but also allows transparently using 6 or 7.3 without needing to know it's being converted to Integer or Double.

Exception Handling

Java requires any possible thrown exception be mentioned in the function using throws (of course, if you catch it, it's not a thrown exception):

  public void doStuff() throws IOException {
  
  // if we do this, no need for the "throws" above:
  try {
    // do something that might cause an error
  }
  catch(IOException ioe) { ... } // handle the error 
  finally { ... } // optional. always runs, even if there's an uncaught exception

finally is optional. It will run for uncaught exceptions, or exceptions in a catch -- anything which would normally abort.

You can have multiple catch blocks, or use an | (vertical var) to handle several in one (the other method is to catch all Exceptions):

  // either error:
  catch(IOException | NullPointerException excp) { ... } 
  // can have more than one:
  catch(OutOfMemoryError oome) { ...}

Serialization

The most common way to serialize in Java is a 3rd party library, like for JSON. Java's built-in serialization is considered less useful, but still fun to see.

Adding implements Serializable lets most Java classes serialize themselves. You generally don't need to write any extra functions. It will also serialize all references (it assumes you're the "owner"). If you have a cross-reference, transient tells it not to serialize. But you'll need to re-construct it later.

In this case, each savableCat owns its myCollar. The serializer will save it along with us. But myUltimateCollar is pointing to a demo collar in a common list, and shouldn't be saved with us. Saved mucID will later be used to reconstruct it:

class savableCat implements Serializable { // nothing added below for this
  int age; string name;
  CatCollar myUltimateCollar; // I "own" this instance. It will be saved with me
  transient CatCollar myUltiateCollar;
  int mucID; // a way to find myUltimateCollar after reading everything
}
boring serialization example

The process of actually serializing is a pain. You need to create an ObjectOutputStream, backed by a file or a byte output stream, then write your object to it:

Dog d = new Dog(); // set dog values

ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
ObjectOutputStream byteWriter = new ObjectOutputStream(bytesOut);

// byteWriter can write any class, writing one dog now:
byteWriter.writeObject(d); // writes out Dog d to the backing bytesOut

byte[] dogS = bytesOut.toByteArray(); // convert to normal byte array
f.write(dogS);

Reading back is the reverse process, from a file or byte array:

ByteArrayInputStream bytesIn = new ByteArrayInputStream(dogS);
ObjectInputStream dogReader = new ObjectInputStream(bytesIn);
// dogReader is ready to read whatever is in dogS

// pull out whatever's there. It will be an "Object", but we know its a Dog:
Dog d2=(Dog)dogReader.readObject();

Threads

Java has 2 ways to try to make things thread-safe. Adding synchronized in front of any function limits access to one thread at a time (for member functions, this is obviously for each instance):

public int synchronized getSomething(...) { ...}

Some container classes have synchronized versions, which I assume are full of these.

You can't declare an object to be synchronized. But every object has a built-in lock which anyone can attempt to acquire. The start of the block waits for it to be free, and it's released at the end:

synchronized(c1) { // wait for and acquire c1's lock 
  // no one else can use c1 until we finish
}

Some explanations confusingly say that the block is thread-safe. It is, but the block isn't locked -- the variable is. No other blocks using synchronized (c1) c1 can be entered, either. To fake a synchronized object, have every member function lock itself, using synchronized(this) at the start.

To start a thread, create a class inheriting from java.lang.Thread. Overload the run() function. Run it with new Thread(new myThreadClass()).start(); (start runs run()). Here's a cutesy example running a nested Thread class 3 times, synching on ourself. With the synch, they run in sequence. Without, the numbers from each thread will interleave:

Long thread-using code
class threadRunner {
  public void runThreeThreads() {
	    threadTest t1 = new threadTest(); // t1 is ready to be spawned
	    threadTest t2 = new threadTest();
	    threadTest t3 = new threadTest();
	    
	    new java.lang.Thread(t1).start(); // starting the threads
	    new java.lang.Thread(t2).start();
	    new java.lang.Thread(t3).start();
	  }

  // nested class for the Tread (so we can use the free backpointer)
  class threadTest extends java.lang.Thread { // <- the actual Thread
  
    public void run() { // <- "run" is run inside the thread
      synchronized(threadRunner.this) { // synch on our common backpointer
        for(int i=0;i<10;i++) { // print 1 to 10 with a delay
          for(int j=0;j<1000000;j++) {} // delay
            System.out.println(i);
        }
      }
    }
  }
}

Container types

Containers live in the java.util package. ArrayList is the normal array-like container. Vector is the slightly slower thread-safe array-backed list:

ArrayList<String> A1 = new ArrayList<String>(); // most common type

A1.add("cow"); // to end
A1.add(0,"moo"); // inserting -- add with index as 1st thing

A1[1]; // cow, basic [] look-up
A1.size(); // 2 (why is it called size? Oh, well)

A1.remove(1); // by index
A1.remove("cow"); // search for item

LinkedList is the linked list type. It's not synchronized -- there are some funny synched versions. It has a variety of identical functions to remove or add or read from the front or back: addFirst, addLast, removeFirst/Last, push and pop, pollFirst/Last, and peek's and offer's. Pretty much, all you need to use it as a stack or queue.

The basic hash-map is HashMap. Since Java doesn't overload operators, It uses put and get:

// note how we need to use Integer, which is a reference type:
HashMap<String, Integer> MaxAge = new HashMap<String, Integer>(); 

MaxAge.put("cow",17); // instead of MaxAge["cow"]=17
int xx = MaxAge.get("cow"); // 17. note: Integer to int is auto-unboxing

Function Pointers

Java doesn't have them: you can't pass around functions, and functions aren't 1st-class objects. But you can accomplish the same thing using subclassing. Java was created just as Design Patterns were getting popular, and it uses the Command pattern. If you're a devoted C# user, the Comparator interface is how Java handles it.

Suppose you want to take a string->int function as input. Instead of saying that, create an abstract class with one named string->int function, using that as the input:

Sub-classes as function holders
// class to establish "I take any string to int function":
interface StoIntFunc {
  int theFunc(String w); // subclasses will have this function
}

// this function takes a string->int, using StoIntFunc to say that:
void someFunc(Cat c1, StoIntFunc catNameConverter) {
  ...
  // Notice how we run it by looking inside and using the name:
  int x = catNameConverter.theFunc(c1.name); // <-calling the passed function
}

// create the real function, inside a specially-made subclass:
class cnc implements StoIntFunc {
  public int theFunc(String w) { return w.indexOf('z'); }
}

someFunc(new cnc()); // calling it, passing the function we just wrote

For simple functions like that, the system is a huge pain. Java has some pre-written (so all you do is write the second half). Or you may have several functions for something specific: keyUp, keyDown, keyHeld. The abstract class groups them, gives them nice names, and is a nice hint when it's seen later.

Anonymous functions

Since java doesn't have function pointers, it can't have anonymous functions. But it has a special rule to fake them. This next line is legal, and looks as if we're passing a standard compare function, as a function. It's intended for general use. But it's not what it seems:

// use built-in sort to sort cats by age:
C.sort( (c1,c2)-> c2.age-c1.age );

For real, sort requires you to pass an instance of a subclass of the Comparator interface, overloading the compare function. The compiler expands our code into this:

C.sort( 
  new java.util.Comparator() // special syntax to create anon subclass
    {
      // completely ordinary overriding func, using our body:
      public int compare(Cat c1, Cat c2) { return c1.age-c1.age; }
      // NOTE: compare output regarded as: positive, negative, or zero.
    }
);

The second line is special syntax to create an anonymous subclass. Inside, we can see how it added public int compare, which is the only function, with our code as the body. The official name is an anonymous inner class (which doesn't make sense, since it's a subclass, but it's fine). The super-shortcut -- writing only the naked body of the single member function -- is officially called a lambda function.

In general, you'd only write out an anonymous inner class if you had 2 or more functions to overload. Most interfaces purposely only have one, allowing us to use a lambda function.

Lambda functions still need an interface having a single function with that signature. To be nice, Java predefines common ones. For example Predicate<T> is a class with a single bool(T) function named test. This line looks like it's setting a function pointer variable, but is really creating an anonymous subclass:

// looks like an anon function assigned to func pointer,
// really is creating and assigning a subclass of Predicate<Cat>:
Predicate<Cat> isKitten = c -> c.age<3;
getSomeCats( C, isKitten ); // passing a function (but really a predicate object)

// the code taking it treats it exactly as what it really is, a Predicate sub-class:
public Cat[] getSomeCats(Cat[] C, Predicate<Cat> useThisCat) {
  ...
  // We still use it like a class, calling the "test" member function:
  if( useThisCat.test( C[i] ) ...
}

Java goes even further to make this trick nice. If your interface has several functions, you can add default in front of all but the one you want to auto-overload. default also allows us to write a body in an interface. Here we can write a utility tooExpensive function:

public interface buyable {
  double cost(Cat c); // <-this one is auto-overloaded
  default d=boolean tooExpensive(Cat c) { return cost(c)>10; }
}

Now we can write buyable<Cat> catBuy = c->c.age*2;. We get an anon subclass where tooExpensive is true for cats 6 years or older.

enums

Basic Java enums work as usual. Java also allows the modern trick of attaching any amount of extra data. And also member functions using this extra data. Basic enums:

// basic enum:
enum color { RED,GREEN,BLUE; }
color c1 = color.RED; // note how the type goes in front

When you create an enum, the system makes one actual object for each value. c1=color.RED finds the first object and aims c1 at it.

Additional data can be stored in variables you declare, listed in parens after each enum value, and set with an implicitly run constructor:

enum color {
  // extra data goes in parens after each:
  RED(3.2), GREEN(4.7), BLUE(6.8);
  public double prettyness; // the var storing those values, one for each item
  
  // run automatically at start-up to assign values in parens:
  color(int val) { prettyness=val; }
}

color c1=color.RED; c1.prettyness; // 3.2
color.BLUE.prettyness; // 6.8

To sum up, color is like a class which automatically creates 3 copies of itself. And it also acts as a container for those items.

You can write member functions, which act on each item, using those extra values. For example bool isPretty() { return prettyness>5; }. Used with if(c1.isPretty()).

Bizarrely, these extra values do not need to be const/final. Each color can change it's prettyness: color.BLUE.prettyness=14; and affects every reference to color.BLUE.

 

 

Comments. or email adminATtaxesforcatses.com