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 see socSec

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. Inherited variables may be used with super-dot. 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 aa, String ss) { age=aa; name=ss; }
}

class Cat extends Animal { // subclass using extends
  public int cuteness;
  
  // calling inherited constructor with super:
  public Cat(int aa, String ss) { super(aa,ss); 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 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 n2 = 9; // becomes: new Integer(9)
 n2=null; // 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. 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.

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 bytesOutOut = new ByteArrayOutputStream();
ObjectOutputStream byteWriter = new ObjectOutputStream(bytesOutOut);

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

byte[] dogS = bytesOutOut.toByteArray(); // convert to normal byte array

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

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

// pull out whatever's there. It will be an "Object", but we know it's 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 run a bit slower.

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

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

Some explanations confusingly say that the block is thread-safe, giving the false impression that no other thread can use that exact block. But, of course, any other block starting with synchronized (c1) fights for the same lock. A synchronized object can be faked by adding synchronized(this) to the start of every member function.

To start a thread, create a class inheriting from java.lang.Thread. It requires you to 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:

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. 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);
int xx = MaxAge.get("cow"); // 17. note: Integer to int is auto-unboxing

Function Pointers

Java doesn't have them. You need to do it with subclassing (if you're a C# user, it's the exact way you can sort using a subclassing of the Comparator interface. A function pointer in Java is just a plain old pointer to a class holding your function.

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 is legal, but 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. Shortcut #1 allows us to create an anonymous subclass. This does the same as the first few example, sorts cats by age:

myList.sort( new java.util.Comparator()
  {  // anon subclass overriding one (the only) function:
    public int compare(Cat a, Cat b) { return a.age-b.age; } // -1/0/+1
  }
);

Leave out the name of the class (it has none) and overload every function. The official name is an anonymous inner class (don't try to figure it out -- the name doesn't relate well to other Java phrases).

The super shortcut is for interfaces with a single function (which is common). Use just the body of that one function (the first example in this section). They call that a lambda function, since it feels like just a function.

To make it even easier to pretend, they predefine interfaces with common function types . 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:

// this is not assigning a function to a function pointer:
Predicate<Cat> isKitten = c -> c.age<3;
getSomeCats( C, isKitten );

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(); // <-only overload this
  default d=boolean tooExpensive() { return cost>10; }
}

buyable can be used with c-< c.age*2, which replaces cost.

enums

Basic Java enums appear to 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

Additional data is 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; // store extra data here
  
  // 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

You can write member functions using the extra values, in the usual way.

Bizarrely, these extra values do not need to be const/final. When you create an enum, the system makes one actual object for each value. c1=color.RED finds that object and assigns it. Changing the prettyness of any RED changes it for the single master copy.

 

 

Comments. or email adminATtaxesforcatses.com