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.
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)
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.
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:
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).
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.
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.
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.
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.
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").
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.
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
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.
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").
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
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 conversionTo 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.
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).
|
||
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(); |
|
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.
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..
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.
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 islobit = 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.
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 declarationThe 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.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
...
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.
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 {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.
// all the real code you are here for goes here
} catch (Exception ex) {}
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.
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.
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.
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.
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.