Things You Need to Know in Java

(Eventually, Probably Not Today)


This page is about stuff that's in the Java language, and that you should know about when you read other people's programs -- and sometimes you need to use it yourself, but mostly not for the programs you are practicing on in this course. Bookmark this link and read through it once or twice, then come back when you need it.

Some of these things are in the language because the academics think they are "elegant" (beautiful, which as the saying goes, "is in the eye of the beholder") but tend to be more complicated and/or less useful than the stuff you learn in the main body of this tutorial. Your brain has a fixed and finite number of neurons. The more brain cells you use up learning trivia and stuff you do not need to know to write great programs, the less you will have left over to help you become a great programmer. It is said that the great (fictional) detective Sherlock Holmes, when told about the discovery of the planet Uranus, said "Thank you, but I shall now do my best to forget what you just told me," because it was not useful for solving crimes. That stuff will make you a fascinating conversationalist, but it will not make you a great programmer -- unless of course you are writing navigational software for interplanetary space craft. You get to choose: whether you want to be a fun person to be around, or if you want to have fun writing awesome programs. You cannot be both. None of us have a red "S" on our blue tights.
 

Contents

Six Things
ASCII Codes
Other Iterations
Switch
Type-Casting
String Tools
  "Unsafe" Java String Tools
  Use My "Safe" String Tools
  "Safe" Input Routines
Math Tools
Random Numbers
Exceptions
Threads
Objects and Classes
  What You Need To Know About Objects Today
  A Brief Tutorial on Objects
(more TBD)

Pedagogical Philosophy

OK, you don't need to know about our pedagogy in order to program in Java, you just need to do it. This section is for educators and other people who want a quick understanding of what we are all about. If you think knowing how I got here helps, you can read my bio. Steve is the other guy, and if you need to reach him and don't know how, ask me (contact info on my home page).

We teach Top-Down Design (using English) and Bottom-Up Implementation (using what I call "Six Things" that embody every programming language, first in my "Kitchen English" because everybody already understands those same six concepts in English, and then in Java (see "Why Java?"). But once you understand how to convert the idea of a program into those six concepts, you can program it in any ("Turing Complete") programming language (see "Why 6?"), merely by seeing how to represent each of the Six in that language. I know, because I once taught Lisp that way. It takes longer to understand that language's predefined library of subroutines, but in principle that is subsumed in the notion of understanding subroutines in that language. I/O is pretty much defined as library subroutines in almost every high-level programming language, so likewise.

Some people have a natural inclination to program. They "get it" right out of the box. Everybody else can learn to program, if they are willing to be what I call "Observant, Careful, and Determined," and a large part of our Kitchen English segment is designed to help them realize that -- apart the fact that computers are stupid, and only do exactly what you tell them to do, even if you didn't want to tell them to do that -- every aspect of computer programming has a parallel in what people do every day. Once they get past any lingering technophobia, and once they understand are willing to work with the fact that the computer has no "Do What I Mean" command, they can program as well as any geek. At least that's our current working theory.

Steve found a reference to Dr. Felienne Hermans on the internet somewhere, and she has made a study of teaching computer programming. Her insight is you give the student an explained example, then let them write exercise programs informed by that example. We think that's a pretty good idea, so we try to fit that model, more or less, except Steve wants them to get into writing "real" programs of their own choosing, because that will get their interest up.
 

Six Things

Every programming language I ever knew about, if it's "Turing Complete" (see "Why 6?"), can be fully defined in terms of these Six Things, and any program written in any Turing Complete language can be translated into any other Turing Complete language simply by mapping the statements in that language into the Six Things and then back out of them into the new language. If you know how to do that, you can jump from Java (or any other language, even English) to a new language quickly.

These are the Six Things you need to know. They are not numbered because there is no particular order they must be considered, except that subroutines are not a requirement for Turing Completeness and therefore always listed last:
 

Sequence

Every programmable computer does what it does in a specified order. Some fast computers overlap instructions so they operate in parallel, but there is extra hardware to force the results back into the specified order. Some computer and their corresponding programming languages (including Java) can do "threads" or "processes" in parallel, but the programs inside those threads or processes are strictly in sequence, and the communication between threads can be understood as I/O between separate computers.

Sequence is everywhere inviolate, except for three specified ways: Iteration (which sends the execution path backwards to do a specified sequence again multiple times), Conditionals (which selects one out of two more sequences to execute, and skips over the others) and Subroutines (which diverts the execution path to a totally different part of the program for a while, then returns to the next command in the current sequence).

Iteration

Every Turing Complete language has a way to specify a particular sequence of commands to repeated some (possibly unspecified) number of times before continuing with the commands after that iteration. The hardware may do this with GoTos and conditional branches, but all modern programming languages have a high-level structured way to do it, with special syntax for marking the beginning and end of that iteration, and the conditions for its termination.

Conditional

Every Turing Complete language has a way to specify that a particular sequence of commands is to be executed only when a specified condition is true, and skipped over otherwise. The hardware does this with a conditional branch (or skip + GoTo), and all the programming languages have a sybtactical form for identifying the condition and the size of the sequence to be executed or skipped. Most languages offer two or more alternatives, only one of which executes, which we consider to be merely variations on the simplest execute-or-skip model. Although the hardware may implement both iteration and conditionals with a conditional branch, there is a fundamental difference in that iteration branches backward, while conditionals branch forward.

Variables & Evaluation

Every programming language has a way to specify named places in memory where single calculated values may be stored. Most programming languages can also give names to aggregates of values, most often numbered sequences of the same kind of data (arrays), but increasingly also (objects) consisting of heterogenous (named) components. Every programming language has specified operators like addition and subtraction, and sometimes multiplication, division, and possibly also logical operators like shifts and boolean AND and OR, for computing values to be stored into variables, based on values that came from arbitrary (but possibly restricted by type) variables. Some programming languages (not including Java) let you copy whole aggregates using their variable assignment primitives, but obviously every language allows it by means of iteration and/or subroutines.

Input & Output (I/O)

Every programming language gives the programmer a way to input values for their variables from outside the computer, and to output values from those variables to the world outside the computer. Most modern programming languages (including Java)  do this only by means of predefined subroutines in the system library. Some hardware has special instructions to do I/O, but increasingly most of it is "memory-mapped" so the I/O is indistinguishable from variables. Occasionally a programming language will expose those I/O variables to the programmer; Java has syntax ("volatile") to do that, if the implementor so chooses, but it is rarely exposed to the general Java programmer.

Subroutines

Every programming language (including the hardware machine languages) gives the programmer a way to give a name to a sequence of commands and the data they are to operate on, so that the subroutine can be called from an arbitrary place in the program, and then the execution path can return to the next command after where it was called. Any subroutine has the options to accept values ("parameters" which are effectively among the variables used in the subroutine) and/or to return a computed value (in the hardware this is usually by means of a specified variable or "register") so that the subroutine ("function") value can be combined by means of operators with other values before being stored in a variable or else used to control a conditional or iteration.

Object-Oriented languages define a special way to aggregate data and subroutines into what they call "objects" and which function in essentially the same was as subroutines, only more so. The fundamental difference is that subroutines are code-centric, and are called from a place in the program, whereas objects are data-centric and can be used to keep a collection of variables together and (implicitly) passed as a parameter to each and every one of the subroutines in that object when it is called. This is useful in several ways, but we do not consider it to be a critical aspect of learning programming (see "Why Not Objects?" and also "OOPS Is Subroutines").

ASCII Codes

[Skip to the table]

You don't usually need to know the numeric values of the (Latin) alphabet character codes to program in Java, but sometimes it's helpful (in any language) if you are generating text algorithmically or packing text characters into integers (see "Packed Numbers" below). The code was invented in the 1960s, probably in reaction to several character codes (Baudot, BCD) in wide use where the alphabet was not sequential. It eventually became the first 128 codes in Unicode. It is composed of 95 "printable" (including space) data  characters, plus 33 control characters, of which only a half-dozen or so retain any semblance of their defined function. I omitted the rest from this table, but you can Google "ASCII" to get the full definition.

The table is usually laid out in 16- or 32-row columns to match the organization of the codes: the first 32 are control characters, then space as the first printable character, so that a natural sort puts it at the front, followed by 15 more assorted punctuation codes, mostly those characters already in wide use at the time (for example in Fortran), followed by the ten digits in numerical order, aligned so that the low four bits of each digit's code is its value in binary. The capital letters follow in their own column(s) again aligned so that the low five bits of each letter is the binary number representing that letter's position in the alphabet (A=1), followed by the lower-case alphabet similarly aligned, then the remaining slots filled with the remaining punctuation characters. The code is designed so that for 6-bit computers (like the IBM 704 I first programmed) you could lop off the control characters and the lower case characters, and the middle four columns became a credible 6-bit code. Of course IBM never did that, they invented "Extended BCD" for their 8-bit hardware, which was really goofy, but easy to translate from their punched-card codes that ran on their card sorting machines (invented for the 1890 census, and only slightly improved in almost a century). But the machines worked well! "Nobody ever got fired for buying IBM."

In deference to the existing TeleType(R) convention where paper tape errors were punched through with all ones (1111 1111), the final code is defined as a non-character ("Delete this character position"). The TeleType keyboard had an easy-to use key to punch through all eight holes when making a tape by hand, so that key got co-opted to mean "Erase previous character" and it is still used that way in many text editors (except it is now used as a reverse-backspace to delete characters on the right of the cursor, because ASCII already has a proper Backspace code). The original function disappeared with the paper tape that occasioned its use.

ASCII is defined as a 7-bit code because the Americans who invented all this stuff had no diacriticals or odd characters in our alphabet to fill up the other half of an 8-bit code, and more importantly, because the hardware was so flakey that they wanted to use that eighth bit as a check bit, so that a dropped bit could be automatically detected and retransmission requested. I worked for a while as the night operator at the Associated Press Pacific relay station in California during that time. We received the Far-East news wire by radio from Manila, and sent it along by landline to  New York, and the signal was often badly garbled by static in the radio reception. I often had to request another copy, then try to recover the original from the two mashups. In the 5-bit Baudot code, the letter "T" was only one bit different from "W" so the news writers tried to be helpful by repeating ("rpt") when they wanted to say "now" or "not" as in "It is now rpt not the opinion..." So I asked for a retransmission: "It is not rpt now the..." That happened a lot. I learned the Baudot code so I quickly recognized a single-bit dropout in a misspelled word and made the obvious correction. Sometimes it was so bad, I could not make any sense of it at all. Occasionally I re-invented what I thought they probably said, based on one or two clean letters in each word. It was a night job and badly messed up my sleep schedule, so when I found a day job, I left there. But it was memorable. I got to see what the far-east political leaders said about the assassination of Kennedy before anybody else in the USA. Not that anybody cared.
 

ASCII Table

 
Dec Hex Chr   Dec Hex Chr   Dec Hex Chr   Dec Hex Chr
0 00 NUL   32 20 SP   64 40 @   96 60 `
1 01   33 21 ! 65 41 A 97 61 a
2 02   34 22 " 66 42 B 98 62 b
3 03   35 23 # 67 43 C 99 63 c
4 04   36 24 $ 68 44 D 100 64 d
5 05   37 25 % 69 45 E 101 65 e
6 06   38 26 & 70 46 F 102 66 f
7 07   39 27 ' 71 47 G 103 67 g
8 08 BS 40 28 ( 72 48 H 104 68 h
9 09 TAB 41 29 ) 73 49 I 105 69 i
10 0A LF 42 2A * 74 4A J 106 6A j
11 0B   43 2B + 75 4B K 107 6B k
12 0C   44 2C , 76 4C L 108 6C l
13 0D CR 45 2D - 77 4D M 109 6D m
14 0E   46 2E . 78 4E N 110 6E n
15 0F   47 2F / 79 4F O 111 6F o
16 10   48 30 0 80 50 P 112 70 p
17 11   49 31 1 81 51 Q 113 71 q
18 12   50 32 2 82 52 R 114 72 r
19 13   51 33 3 83 53 S 115 73 s
20 14   52 34 4 84 54 T 116 74 t
21 15   53 35 5 85 55 U 117 75 u
22 16   54 36 6 86 56 V 118 76 v
23 17   55 37 7 87 57 W 119 77 w
24 18   56 38 8 88 58 X 120 78 x
25 19   57 39 9 89 59 Y 121 79 y
26 1A   58 3A : 90 5A Z 122 7A z
27 1B ESC 59 3B ; 91 5B [ 123 7B {
28 1C   60 3C   92 5C \ 124 7C |
29 1D   61 3D = 93 5D ] 125 7D }
30 1E   62 3E > 94 5E ^ 126 7E ~
31 1F   63 3F ? 95 5F _ 127 7F DEL

Notes

0. Null is "Not a character" used in C (and possibly also in Java, but they don't say) as the end of a character string.

8. BackSpace is what you type to back up over the previously typed character, usually thus erasing it.

9. Tab is used to separate columns in the text representation of a table, sometimes used in forms programs to jump to the next entry point.

10. On the old TeleType hardware, LineFeed rolled the paper up one line. In Unix systems it is the approved line separator.

13. On the old TeleType hardware, Carriage Return returned the print head back to the left margin. There is no LineFeed key on modern keyboards, so the Return key serves to notify the software that the user has finished the current line (or form) and the software should do whatever needs to be done in that situation. In the (original) MacOS that key code was the line separator between paragraphs, and there was a different key that served as a "Do What I Mean" such as hitting the default button in a form.

Most software up to that time used both a Carriage Return followed by a LineFeed as a line separator in files, because both were required for the TeleType hardware to get the print head in the proper position to print the next line. The physical motion of the print head took longer than one character time, so the LineFeed (a non-printing character) came after the return to add an extra character time to the return head motion. Modern hardware has software controlling everything so the user no longer needs to worry about hardware timing, but the Microsoft operating system preserves the tradition, probably in deference to legacy users and their hardware (and now also software). It's not a bad policy, it makes loyal users.

27. The Escape key had no hardware significance that I know of on the original TeleType, but software designers traditionally used it to signal that user's desire to "escape out of..." whatever the computer is doing.

127. I already mentioned the original and novel functions of the Delete key.

Other Iterations

Java has several different kinds of iteration commands or structures. You really need to know about only two, while and for. For-loops are useful because they tell other programmers -- and the compiler, which can use that information to make your program more efficient -- what you intend this loop to do and how many times it should run. While-loops (with break and continue) can handle every other kind of loop.

While-loops nominally test their control variable (or expression) at the beginning. The "do ... while(expression)" loop tests the condition after it has done the loop at least once. Fortran do-loops did that, so people got the idea it was important, because Fortran was the first compiler in wide use (it's still the preferred language for science programs), and because C was modelled after Fortran and Java from C. There, you have the whole reason to use do-while loops. You can do the same thing with a while(true) loop, with a break test at the end (or anywhere else inside the loop, as many as you want or need).

More recent versions of Java have other kinds of iterators that step through the members of non-linear data structures. I don't know anything about them, because I never used them. Maybe they work better than while loops when you need to do that, or maybe they only obscure what is going on, so that you can have bugs in your program that are really hard to find. If you run across one of these, you can Google it, and somebody (perhaps Oracle, if they are still in business if and after the Supreme Court decides in their favor and Google crushes Java) will tell you what they do and how. Of course if Oracle gets what they rightly deserve for being so greedy, Java will go away and be replaced with something else -- I hope not Golang, it's not nearly as well-done as Java, but it's something that Google owns and can control, which transition Oracle is unwittingly trying to force on the world -- and this whole tutorial will need to be replaced. sigh

Like I said, I never use these other loop types. You can if you want to, but it will not improve your standing among your peers.

PostScript: I wrote this section before King SCOTUS* (who wears Nine Black Robes) decided the Java case in favor of programmers and common sense. Now Java will be around for a long time. Your grandchildren will be programming in Java. Like Fortran, which was invented over 70 years ago, it won't look like anything you see here today, but they will call it "Java". Or if Oracle gets greedy again, maybe "Latte" or "Joe" or some other coffee-like name (I call mine "Turkish Demi-Tasse, A Stronger Brew than Java").
 

Switch

Like for-loops, the switch/case command structure more clearly spells out your intentions to other programmers and (especially) to the compiler. It was originally designed as a "structured" (single-entry, single exit) replacement for Fortran's (and Basic's) so-called "computed goto" as a fast way to do a many-way selection of one block of code to execute, which would otherwise require a (hard to read) nested series of if-else commands. In practice it's a little more costly than nested if-else commands (unless you have a dozen or more cases), so many compilers will convert a switch with not many cases into nested if-elses. The switch is much more readable, so go ahead an use it, the compiler will generally give you the best code.

The general form of a switch command is thus:

switch (expression) {
case A:
  // some code
  break;
case B:
case C:
  // other code
  break;
...
case Z:
  // more code
  break;
default:
  // do this if nothing takes
  break;}


The case labels (A, B, C,...Z) are constants of the same type as the expression, which must be a scalar type (with discrete ordered values like integer or character, not floating-point nor string). For example, if you are looking at typed-in characters, the expression would be the variable with that input, and the case labels would be each character ('r', 'p', 's' ...) that your program wants to respond to. The break statements are nominally optional -- it was intended to let you put more than one case label to a single block of code (like B and C in my example above) -- but with that one exception, you should never omit the break statements, it will make your code do bizarre and unintended things and breaks the promise of structured code. Unfortunately the Java compiler won't help you here. The default case is also optional, and if omitted, the cases for which there are no case labels fall out and do nothing (which is often a desirable and intended result).

The basic principle of scalar numbers is that they are countable in order, 1,2,3,4,... or a,b,c,... This is not true of real numbers (which floating point pretends to be, and almost succeeds at) where you can always insert another value between any two, like 1.5 between 1 and 2, or 3.141592 between 3.14159 and 3.1416. It is also not true of variable strings of characters using the standard dictionary sort, where you can always insert "ax" between "a" and "b" or "axe" between "ax" and "b". Boolean is a trivial case of scalar, because there are only two values, false and true, so you could make a switch on a boolean value, but why bother?

Hmm, I see I did another write-up on switch in Extras. You get my age, you forget things in a tutorial as long as this one. Better twice than never ;-)
 

Type-Casting

Java started out as a revision of C (presumably to fix some of the problems, and also to add OOPS) and C is a rather weakly typed language. One of the things they had to do in C to make it usable with the minimal compiler they could fit into the rather dinky PDP-11 is to allow prgrammers to evade the data type protections. This is absolutely necessary for a tiny number of situations in operating systems -- which C was designed to be used for, and Java explicitly not -- and a source of bugs everywhere. One of the language features to do this is to re-interpret the bits of one data type as if they were a different type, called "casting". What programmers really want is to re-arrange the bits so they mean the same thing in the other type -- which should be done in a function, and probably is, under the hood -- but somehow the same C syntax gets used for both, and programmers need to know (but are not told) when this is a freebie (and actual cast = re-interpretation of the same bits) or costs significant runtime (because it's a conversion, re-arranging the bits). Anyway, Java inherited this blunder unchanged, and its sole remaining purpose is to convert data between different types (if necessary). Sometimes the conversion happens automatically. The rule seems (sometimes) to be: "If you can get wrong answers, a cast is required," except when it isn't.

These casts and/or conversions consist in adding the destination type name inside parentheses in front of the value. Here is when you need to do this:

To cast (normal) integer to long or to convert integer to float and/or float to double, just use it in an expression that already has one or more operands in the destination type:
double big = 3.5+7; // the integer 7 is automatically "promoted"


To convert the other direction, where bits might be lost, an explicit cast (conversion) is required:

int small = (int)3.5; // the fractional '.5' is lost in the conversion
To re-interpret the bits of a character as a number (or back), just use it in an expression:
char lett = 'A'; lett = lett+2; // now lett == 'C'
To actually convert it (either way) requires a function.

To re-interpret an object reference as if it were really a super-class object, just use it there.

To re-interpret an object reference of a super-class type that happens to be a certain subclass requires an explicit cast if you want to access the subclass fields or methods. You get an exception (runtime error) if it's not that subclass.

String Tools

Why This Topic Is Here

(Skip down to "Use My Safe String Tools") (Skip down to "Unsafe Java String Tools")

Most of a programmer's life is spent debugging, and a very common debugging tool is to "print" out (it used to be the console line printer, but now it's usually to the screen or maybe a file) the values of variables at various parts of the program, so to see why it's not doing what you thought you told it to do. The Java designers knew that, which is why they created System.out.println for just that purpose, and why they made a simple (albeit confusing) "+" operator for combining labels and values onto a single print line, and why most values automatically convert to String in that context.

Besides debugging, text strings are a powerful (but slow) way to process data in an obvious and intuitive way, which is why all scripting languages that I know of, their basic (only) data type is string, and most of them have powerful and/or convenient tools for manipulating those strings.

Text manipulation is slow, probably slower than native data types by a couple orders of magnitude. For pedagogical programs like you will be writing in this course, that is inconsequential (nobody will even notice), but out in the Real World, writing real programs (the kind you get paid for) for real people, and processing real data -- not just a dozen lines, but thousands and millions of records -- you need to do it using native data types and objects as much as possible. But that is not today.

Java is not a scripting language, and its designers made no effort to make String manipulation particularly easy, other than for printing debug lines. This is one of the reasons Python is so popular as a beginning programming language. But we are not here to learn Python, we are here to learn a programming language that real programmers use in real programs, not the kinds of toy programs Python limits you to. On the other hand, we'd rather not make life more difficult for you than necessary.

Therefore, rather than force you to jump through all the arcane hoops Java puts in front of String users, I wrote a bunch of String utility functions that are much easier to use than Java's finest. Some of these are little more than wrappers around the corresponding Java tools, but set up so that they won't crash your program (and the compiler won't complain), basically I disabled the exceptions that Java strings keep throwing at you.

Some of this happens because I treat the String type as a basic type, and not an Object the way Java intended. There is a strong mathematical reason for that: we all have a gut feel for the way the order you add or multiply numbers does not affect the result (the mathematicians call it "commutative"), so that 2*3 = 3*2 and 4+5 = 5+4, stuff like that. The math in your computer works like that (but for a few odd exceptions with floating-point numbers), and Java does too. We have a gut feel that "numbers is numbers" so that 3+2.7 = 2.7+3 = 3.0+2.7 = 5.7; the computer hardware does not do that, integers and floating point (like their Java types int and float) are different types of data and you can't just add them together without converting the integers to floating point (or the other way around, but that would lose the fractional part). This is so natural in our minds that Java will do the conversion ("promotion") automatically.

Working with text, most of what you want to do is concatenate two strings together, and we all know that concatenation is more like subtraction than addition -- that's why choosing to use the "+" operator is A Bad Idea, but it's done now, it ain't gonna change -- in the sense that "John" + "hit" + "Mary" is not at all the same as "Mary" + "hit" + "John" in the same way that 5-3 gives a different result than 3-5. But there is something symmetrical about promoting numbers to strings when the other operand is a string, which Java does do, but not consistently: "2+3=" + 2+3 does not give you the same result as "2+3=" + 5 (String concatenated to a number promotes the number to String), but it's the same operator as between (2+3) (that was the mistake) and Java, like most programming languages, does sequential appications of the same operators in left-to-right order. The mistake is set in concrete, it ain't gonna change -- and the Java compiler won't warn you.

A single-keystroke for concatenation is too easy to use, it ain't gonna change. Just be careful.

Everything else is done with subroutines -- strictly, they are object methods, and you need a valid Object for the left operand, which is one of the sources of unsafety. I did away with that, mine are not Object methods, the two operands have equal rank and power, as you'd expect if you had not been brainwashed by the OOPS priests. It is perfectly possible to write non-OOPS code in an OOPS programming language, and we all did it, you included, because the first Java programs you wrote in this course are not OOPS (except for the stuff you didn't write).

Anyway, here are some Java string tools you might find useful, followed by my "safe" equivalents (plus a few extras that I have not seen in Java).
 

The (Unsafe) Java String Tools

I call these "unsafe" because using them can throw exceptions and crash your program. To use them safely, you must check your parameters -- especially the object they are run from -- and (ahem) "try" to "catch" all the exceptions. But they are all the Java library offers. I use my "safe" alternatives in GameEngine, and you are welcome to use them too, but eventually you need to write your own (or copy mine, you have my permission ;-) On separate lines I give my "safe" versions, which are linked to their definitions. To find out all about the Java versions, Google "Java name" (no quotes) where name is the name between the dot and the parenthesis in the description below:
 
 
   
Java: lxx = aStr.length();
(mine) lxx = StrLength(aStr);
Java: xCh = aStr.charAt(here);
(mine) xCh = CharAt(here,aStr);
Java: xStr = aStr.substring(here,lxx);
(mine) xStr = Substring(here,lxx,aStr);
Java: xStr = aStr.substring(here);
(mine) xStr = RestOf(here,aStr);
Java: lxx = aStr.indexOf(aWord);
(mine) lxx = NthOffset(0,aWord,aStr);
Java: lxx = aWord.compareTo(aStr);
(mine) lxx = SafeCompare(aWord,aStr);
Java: xStr = (""+(aNum));
(mine) xStr = CvInt2Str(aNum);
Java: lxx = Integer.parseInt(aStr);
(mine) lxx = SafeParseInt(aStr)
Java: xStr = (TorF) ? "tru" : "fls";
(mine) xStr = IffyStr(TorF,"tru","fls");
Java: xCh = System.in.read();
(mine) xCh = Zystem.ReadLetter();
Java: (too messy for one line,
see examples in Calculator)
(mine) String word = Zystem.ReadWord();
int number = Zystem.ReadInt();

 

Use My "Safe" String Tools

Most of these are tools I use all the time in my own code, adapted for use in the GameEngine. You can use them if they are helpful, or you can use the equivalent built-in Java methods (where they exist). You might want to copy the ones you like into your own private library of handy tools (or rewrite them more to your own liking). Or you can look inside to see how they work, then write all the safety tests into your own code. Each is linked to its documentation in the Game Engine page(s), which may be more complete than the brief description given here. You can find these and others in the complete Method Summary for JavaGame. All of the character offsets are 0-based, that is, an offset 0 means no characters between it and the front of the string. Don't forget to use the class name to call them, for example
String part = JavaGame.Substring(bgn,5,src);


static int StrLength(String aStr) // (Java version)

Gets the length of the String aStr without exception, even if (the Java object) aStr is null (length=0).


static String IffyStr(boolean whom, String tru, String fls) // (Java version)

A conditional expression usable in print lines where you might want to print out different labels or different data, based on the value of a boolean or comparison.


static String Substring(int here, int lxx, String aStr) // (Java version)

Gets a credible (possibly empty) substring without exception, even if (the Java object) aStr is null or the offset here and/or size lxx are outside the the bounds of the string.


static char CharAt(int here, String aStr) // (Java version)

Returns the single character at the offset here in aStr without exception, or the null character '\0' if there is none there.


static String RestOf(int here, String aStr) // (Java version)

Like Substring, but returns all the rest of the string after triming off the front here characters.


static int NthOffset(int whom, String aWord, String aStr) // (almost Java version)

Returns the offset of the string aWord in aStr. If there are more than one, whom>0 skips over that many before returning the offset of the next one. If it is not found, or if you ask it to skip more than there are, it returns -1. The not-quite equivalent Java version lets you skip some number of characters (instead of items).


static int Countem(String aWord, String aStr)

Returns the number of non-overlapping instances of aWord in aStr. For example, you can use this with aWord="\n" to find the number of lines.


static String NthItemOf(char delim, int whom, String aStr)

Given a string aStr with several items separated by a single character delim (like comma or tab or line break '\n'), this extracts data elements, making it easy to generate and parse test (and actual) data for small programs. In particular, when the delimiter is a space, the excess white space in normal text is ignored. The same purpose is partly served using the Java trim() method which removes spaces from the front and back of a word otherwise selected. Unlike normal C and Java numbering, the first line or word or other item is index 1 (not zero), so that normal line numbers work properly -- computers work as if zero is the first number, but real people know that one is first   For example, "Y2K" was a supposed computer problem (mostly fixed by the time the year arrived), but even though the year now starts with "20" we still call it the "21st" century, because there is no "year 0" and no "zeroth century." People are not computers.


static String ReplacAll(String nuly, String prio, String theText)

Returns a new string formed by replacing every instance of prio with nuly.


static String ReadWholeTextFile(String filename)
static void WriteWholeTextFile(String filename, String data)

Java has ways to read and write text files, but they are not nearly so simple as these two one-liners. The filename can be either a full path into any directory your computer gives you access to, or else a simple name and (Replit or) BlueJ puts it in the project folder (or at least it did that on this computer).


static String CvInt2Str(int whom) // (Java version)

The Java concatenate operator automatically converts numbers on the right to String, but it is error-prone. This function makes your intentions explicit, and doesn't fail if there's no string to concatenate to. Other number-to-text conversions must be more explicit...


static String SeeHex(String before, int whom, String after)

This converts your number to hexadecimal and attaches a prefix and suffix, making it easier to use in a debug print line.


static String HexIfMore(String before, int whom, String after)

This gives you the best of SeeHex and CvInt2Str: straight decimal if the number will fit into 16 bits, and hexadecimal (including an automatic prefix "0x" if larger. The assumption is that smaller numbers are really numbers, but larger numbers are probably packed data easier to read as hex by real people.


static String FormFixt(String before, int whom)

Some GameEngine data (notably the animation parameters) is stored as fixed-point fractions, nominally 8-bit integer part + 8-bit fraction in 16 bits -- some components are 2-bit integer + 4-bit fraction, which can be extracted into the larger format by a simple shift + mask (see "Packed Numbers") which is fast and easy -- and I needed some way to format these numbers in an understandable way. You can use it too, if you wish.


static int SafeParseInt(String aStr) // (Java version)

The previous four functions convert numbers to text; this one converts the other way. Java can do it too, but it's complicated with a lot of hassle and exceptions to worry about. I wrote this one from scratch a long time ago, and I keep using it. If it finds a number, you get that value, otherwise you get zero. It skips over leading white space, and the number ends at the first character that is obviously not part of the number (more white space, or else other characters). Numbers that begin with "0x" are read in as hexadecimal, so this is able to read the numbers off a text file that was generated using HexIfMore with no extra effort.

(Input routines in my class Zystem...)

static char Zystem.ReadLetter() // (Java version)
This reads characters one at a time from the current input line (including a linefeed at the end of each line). The first time it is called, the user must type in a whole line (ending with the Enter key) which becomes the current input line. After it is used up, another line from standard input (your keyboard) must be read in. Note, the English computer does not require Enter after input (but Java does), so your Java program will get these line end characters, which you can ignore by wrapping a while around this call, like this:
char letter; // declare it outside the loop
while (true) {
  letter = Zystem.ReadLetter();
  if (letter >= ' ') break;} // end of while


static String Zystem.ReadWord()

This reads standard input (your keyboard) looking for something that isn't blank. It skips over leading spaces, but keeps reading lines until it finds a word that isn't blank..


static int Zystem.ReadInt()

This reads standard input (your keyboard) looking for an integer. It skips over leading spaces, and stops at anything that is obviously not a number. It returns zero if it never found a number.

Math Tools

I did quite a lot of bit-packing (see "Packed Numbers" and "Bitwise Operators") to make the GameEngine fast and efficient, but mostly that's behind the curtains. If you decide to be a superstar programmer, you will need to learn those things, but for now you don't need to be concerned. If you need to convert numbers to or from text, the last five functions in the previous section (beginning CvInt2Str) do that. Here are four functions related to that. Don't forget to use the class name to call them, for example
int hibit = JavaGame.TopBit(valu);


static int SignExtend(int here)

Packing and unpacking numbers are easily done with shifts and masks (as explained in "Packed Numbers") which is fast and easy, no functions required. Extracting a signed number from the middle of a packed integer is a few more steps, so I have a function to do the most common situation, where you want the low 16 bits of a 32-bit integer as a signed number. If you want to do this yourself, it's not hard:
ihalf = (here&0x7FFF)-(here&0x8000);
It's based on the fact that the negative of any single bit is that bit replicated to the left all the way to the high end of its integer value. For example, '1' is the lowest bit in an integer (all other bits =0) so -1 is all bits =1. Integer 4 is binary 0100,  so -4 is FFFC = 1111 1111 1111 1100 (in 16 bits), to which you apply SignExtend to get the low 15 bits = 7FFC = 111 1111 1111 1100, then subtract 0x8000 = +FFFF8000 = +1111 1111 1111 1111 1000 0000 0000 0000, which gives you 0xFFFFFFFC = -4 in 32-bit hexadecimal = 1111 1111 1111 1111 1111 1111 1111 1100 binary.


static int TopBit(int whom)

If you have an integer with random bits in it -- like maybe the bits of a graphic image -- and you want to get to the least-significant (right-most, the way we write numbers) bit, it's a simple one-liner: if the number is in variable whom, the lowest non-zero bit is
lobit = whom&-whom;
The other end is much messier, but sometimes we need it, and TopBit is that function.


static int AddPair(int here, int thar)
static int PairNeg(int here)

Like I said, I pack a lot of data into single integers. It comes from the days when memory cost a lot more than today, but I like to think it still serves a purpose. You don't really need it for the games you will be writing in this course, but here it is anyway. AddPair adds two pairs of 16-bit numbers and returns a pair of 16-bit sums. It might not be faster than taking the numbers apart then adding the parts, then putting them back together, but it's certainly less messy.

To subtract a pair of numbers from another, you take its negative using PairNeg, then add using AddPair.

Random Numbers

Many games where the human plays against the computer -- or if the computer shuffles a deck of cards or rolls the dice -- require a random number generator. Java has a Random class to generate all kinds of random number, but the most common requirement in computer games is an integer within a specified range.

In C-based languages (like Java) all ranges start with element number zero, and Random follows this rule, so if you want your random number to begin at (for example) 1, you need to add +1 to the result you get back from nextInt. In this example we are asking for a number randomly between 1 and 10 (inclusive). See the comments for what numbers to tweak to get other ranges:

import java.util.Random;  // do this before your class declaration

public class Whatever {
  Random rn = new Random(); // "rn" is an instance of the Random class
  ...
  public static main() { // or any other subroutine in your code
    ...
    int aNum = rn.nextInt(10)+1; // get a random number 1..10
    ...

The argument (10) you give to nextInt is the number of different values you can get out, in this case a uniform distribution from 0 to 9 (inclusive). The +1 is for when you want the range to start at 1. If you want only nine possible values, 1..9 then you'd give it a parameter (9) and still add +1.

Dice have six faces, so you'd ask for  rn.nextInt(6)+1 (twice, once for each die). If you want to shuffle a deck of cards -- at first I was going to tell you how, but then I realized the internet probably has better algorithms than I do, so just Google "Java shuffle a deck of cards" and take your pick. You can't just get a random number nextInt(52) as a card number, because after you deal that card, you don't want its card coming up again until you reshuffle, but a truly random number might. But it would work correctly for a roulette wheel.
 

Exceptions

Bad Things Happen. Usually it's a bug in your code, but not necessarily. Sometimes the data is messed up -- perhaps bugs in the code of the guy who prepared that data, or maybe it was handmade. That's the worst kind.

Bad Things Happen, and robust software needs to deal with it. Sometimes it's a local problem and you can deal with it locally. Sometimes something Really Went Bad, and the only way to deal with it is to get all the way out and into some kind of recovery module. That's what exceptions are for. They are not efficient, so you want to use them rarely, but when you need them, that's what they're for.

Mostly you are not writing that kind of software in this course, and exceptions are a pain in the you-know-where. But exceptions are built into the Java language, and you must deal with them. For student programs you wrap an empty try-catch structure around your code and get on with what you are here to do. It's simple, one word and a brace at the top of the method, and one line at the bottom, like this:

try {
  // all the real code you are here for goes here
} catch (Exception ex) {}
That's all there is. If comething in your code breaks, the Java library code throws an exception, which jumps out to the nearest containing catch, and then (the third line above) does nothing. You will quickly see that your progam failed, and look at the console log, and there in red letters is says what kind of exception got thrown and where, and you get to figure out what went wrong and fix it.

When you get out into the Real World and you are getting paid to write real software for real people, or you go to college and you are being graded on it, then you can start to think about all the Bad Things that can Happen, and Google "Java exceptions" (or the equivalent spelling in whatever language you happen to be using) and read about what they do and how you can recover from them, and then write some code inside the braces of that catch clause so that your program recovers and does something reasonable. But that day is not today. Unless you are doing the Calculator project, and we have a whole page to explain more about exceptions there.
 

Threads

Computers run very fast, something like 2 billion instructions each second. Numbers like that are completely incomprehensible to us mere mortals. I read once about a guy who got on a helicopter in New York to fly to the airport or something like that, it was $100 fare, and there were a couple billionares also coming, and one of them was fumbling for his wallet, and the other one said, "Let me take care of it," and handed the clerk another BenFranklin. The guy who was writing about this event thought about it for a while, and realized that $100 to a billionaire is like ten cents to a millionaire, and less than a penny to you and me. Can you imagine flying across town for a penny? Your friend doesn't have one handy? "Let me do that, it's a nothing."

One line in your program is like two or five or ten machine instructions (count the operators and keywords for a reasonable estimate). Your computer can do a billion of those lines in a couple seconds. Most of the time the computer sits around twiddling its thumbs, waiting for you to tell it to do something. When you do, it takes off like a bat out of someplace, and finishes it, and then waits again. When you write a program that waits for input, that's what the computer does: it waits. If you have something else it could be doing while waiting, you must tell it to do that.

Threads are a way of letting the computer do something else while waiting. The mechanism is complicated and hard to get right, but somebody else did the hard thinking about it. When you are playing music on your computer, there's another thread running the music, and the computer jumps back and forth between the music player and your program. You don't need to think about it. You do need to think about it if your own program is so big and slow and has so many things going on and events to wait on, that it's hard to think about slicing and dicing all those tasks without bogging down. Then you use threads to do it. When that happens, Google "Java threads" and there will be a zillion websites eager to help you understand how to do that. But that day is not today.

Delaying one second for a dramatic pause is one thing you might want your program to do today, and Java happens to do that in its Thread class. Think of Thread.sleep as a system function with a dot in the middle of its name, sort of like System.out.println.
 

Objects and Classes

Why This Topic Is Here, and Not In the Main Tutorial

(Skip down to "What You Need To Know Today")

For many people Object-Oriented Programming (OOPS) is a "religion" (believing what you know ain't so, or more precisely, the definition of what is known to be true and obligatory, despite any contrary evidence). Some things in the real world fit nicely with OOPS, some do not, and the OOPS proponents stumble all over themselves to force-fit everything into their OOPS paradigm -- or else just give up and call them "static". In Java you see the first kind in Strings (which should have been made a primitive data type), and the second kind in the Math class.

Aside from the goofy (religious) notion that subroutines ("methods") should be attached to data "objects" and not otherwise, the conglomerate of technology that is OOPS has several useful and productive things to offer the programming community.

A. The override facility that is inherent in subclassing is the first time in the history of computer programming that we had type-safe callbacks. Modula-2 attempted to do it with a "procedure type" but it was not in Wirth's original design and came off rather clumsy. Besides, by then M2 was already a dying language. One of the reasons C programs crash all the time is that programmers just use a typeless callback, and if the call does not match the target, Kaboom! There is a whole industry devoted to trying to find the bugs in C programs after the fact, when in better languages like Java, that kind of bug isn't even possible.

B. When you are building a modular and extendable program like the Game Engine, objects are the only way to keep it under control. I know, I tried it both ways. OOPS is more work up-front, but large programs require that effort anyway, and the modularity in OOPS keeps it manageable. When programs grew beyond a few hundred lines 70 or 80 years ago, subroutines were absolutely essential for controlling the complexity. Objects are subroutines writ large. This is not unique with OOPS (other languages had packages and even C has separately compiled files that confer a degree of modularity) but Objects do a better job of closing off the back doors that programmers like to sneak around into to make programs unmaintainable.

C. I know of no other advantages particular to OOPS. Overloading and generics are cute ideas, but in large software they make the code unreadable. Strong types and structured code have been around in Algol since I was in college 60 years ago, and Algol did them better than Java does. Algol died of old age. Ada died of obesity. M2 died in the famine brought on when C-water overran the food crops. C and Java and Python are what we have left. And -- retch -- JavaScript. Java is far and away the best, but OOPS is only a tiny part of that.

Small programs are easier to write apart from OOPS -- we did that in this course. The Real World is made of huge programs -- most of them in C with more bugs than a downtown walkup flat. Strong data types and Objects (in that order) make robust large software possible. After you've done it for a while, you'll see what I mean. Or not. Not everybody is as analytical and introspective as I am. Whatever. Be your best.
 

What You Need To Know About Objects Today

Java requires each data structure to be in its own class, which is a separate source code file, a separate compilation, a separate yellow rectangle in the BlueJ  dashboard window. That's if you are creating your own data structures, which is really an advanced topic. For the training programs you are doing in this tutorial, and for the games you will be writing, that's mostly not going to happen. Somebody else already did all the classes you need to use. Farther down on this page is a brief introduction on how to do what you need to do, if it turns out you need to do it in a game you are writing for this course.

Anyway, in OOPS languages (including Java), you get access to subroutines (methods) in other parts of the program by reference to an object of the class where that method is defined, or if no object makes sense, by reference to the name of the class itself. System is a predefined class in Java, so when you want to print or read standard input, you use a public object defined in the System class, and then by that object you get to call the method.

When we started out, "System.out.println" was nothing more than a long name with a couple dots in it. Actually, you are using a method defined for the PrintStream class, and the println method is accessed by means of the object reference "out" which is a public static instance variable in the System class. The moniker "static" means that (outside the class, which in your case is always for class System) you use the name of the class + "." + the name of the variable or method to access it (as in: "System.out"). Once you have a variable or other object reference, you use that variable name + "." + the method name (in this case "println") to access (call) the method. If the class which that reference is an object of has its own static variables, you could see them by the variable name (instead of "println"), but that's probably deeper than you need to go today.

I wrote a simple class Zystem to make getting input simpler. You can 2-click its yellow box in BlueJ and see its contents. All its methods are static, so when you want to call one of them, you use the class name + "." + the method name, as for example, "Zystem.ReadLetter()" to read an input letter.

I wrote a huge hairy class "JavaGame" to comprise most of my GameEngine logic. Some of its methods and constants are static, so to use them you use the class name + "." + the method name, as for example, "JavaGame.CvInt2Str(num)" to safely convert an integer to a String, or "JavaGame.Arro_LEFT" to get the constant character value matching the left-arrow key used in this game engine. A few of the methods you might need to use are not static, so you need a reference to an instance of the game to access them. You created that instance -- actually I wrote that, you just used it -- in the main() program of your game class, but a copy named myGame is made in your game class, and you can use it. The generated code uses it to find the references to your widgets in the StartUp method in your game class, such as

theBall = myGame.FindListWgt("{theBall}");


I wrote two other classes, both a lot smaller than JavaGame, and you need more interaction with them. Mostly you will interact with widgets, which are objects of class GameWgt. There are a lot of methods for interacting with widgets, and you access these methods by using the name of your widget + "." + the name of the method, for example (if you wanted to move this ball to the top-left corner of the game board) theBall.SetPosn(0,0);

Your whole game is a subclass of my base class GameEvent, and most of what you do there is respond to events using event handlers, which are a special case of what I called "callbacks" above, methods that are carefully defined (in this case by me) so that when you declare such a method, it exactly conforms to the type signature I specified (or else the compiler or linker won't connect it), and it cannot crash from being the wrong type when the GameEngine calls your method because an event happened. All the event handlers you might need to use in your game are generated in stub form when you Build your game, so they are already in the correct form; all you need to do is add code to do what you want them to do. OOPS makes that possible, and it's a good thing.
 

A Brief Tutorial on Objects, for Future Reference

I was going to write a whole section on the other things you might need to know about Objects, but I see I already wrote most of it here in "OOPS Is Subroutines". Read that, then come back here for some stuff you mostly don't need until somebody hires you to write Java (or C++ or C# which are similar, but not as good), or else you get to a college Computer Science class and they want you to know these things.

Objects are a place to put data that goes together, so it stays together, along with whatever subroutines ("methods") thatoperate on that data. In a large programming shop you don't want rogue programmers messing with the data without knowing what it means, so the data inside objects is (by default, but often explicitly) "private" so nobody outside that class can even see it -- or maybe "protected" which is the same thing, with specified exceptions. You move data into and out of objects with private data by means of accessor functions (everything is "methods"), which (as the name implies) gives you specified access to the data. In the "OOPS Is Subroutines" previously referred to, there's an example of a class and subclass, with accessor functions WhoAmI() and DoWhat(), which let clients of this calls see the data they access (or maybe a curated version of it), but not modify it. Other access functions might allow the client to supply new data, but in a carefully controlled fashion. Most of the widget functions you will be using in your game are actually accessor functions.

Not really new with OOPS, but quite useful, is the bundling of data together into a new named data type. That means you can move this data around in a single chunk, with a single line of code -- the underlying hardware still must move all the parts separately, but the compiler takes care of that so you don't need to be concerned about keeping the parts together. In every language that has these data structures, you can pass such data as a parameter to a subroutine. Some of these languages can also pass a reference to the data (not all its parts) so the subroutine can modify parts of the data structure, and the original is what is being modified.

Java refers to its objects only by reference, you have no choice, so modifications always modify the original. That has its ups and downs. An advantage is that you can use an object as a subroutine parameter to get back multiple values all at once (you could anyway, in most languages). Another advantage is that you are on notice that all such parameter modifications alter the original, no exceptions, so you cannot accidentally get the wrong one. Choices are alkways opportunities to mistakenly picking the wrong one. Java -- especially its later versions -- is not particularly good at restricting that category of programming error, but they did this one right.

Anyway, if you need a subroutine that computes three floating-point numbers -- like the coordinates in 3-space -- you can define a class Point3 with three float values and pass them around as a single value. If you need a subroutine that computes five unrelated floating-point numbers, you can make a special class to return all five, or (more sensibly) you can define a carrier class FloatV with only one public float value, then pass to the subroutine five parameters of this type, then pick out the numbers in the calling routine where you need them. The advantage of a carrier class is that you can use it for any number of return values, as needed, without even the overhead of accessor functions.

[I might think of more that needs saying, but not today]
 

Revised: 2023 February 2
 
 

* SCOTUS = Supreme Court Of The United States, like the King of England in 1776 when our ForeFathers decided they didn't want any part of it, and like a high school Principal today, they have the final say: the Student Government can meet and debate and pass Resolutions, but if the Principal does not approve, nothing happens. Welcome to the Real World.