Alexi Member
Posts : 145 Reputation : 1 Join date : 2010-09-08 Age : 31 Location : Sweden.
| Subject: Basics of Java. Fri Sep 10, 2010 12:04 pm | |
| The Basics of Java Table of Contents
- Introduction
- Quick Reference
- Fields/Variables
- Methods
- Arrays
- Strings
- Flow and Control
- Classes
- Object Oriented Design
- Generics
- Data Structures
- Efficiency, Big-O
- Multi-Threading
- Basic Bytecode
- How this tutorial applies to RSPS
- Practice and project ideas
- Links
Introduction- Spoiler:
Java is a object oriented programming language that was originally developed by James Gosling at Sun Micro-systems. The language is a interpreted language that is compiled into Bytecode. The JVM then interprets the Bytecode at runtime. This is a very powerful feature, giving Java the ability to live up the phrase "Write once, run anywhere", and is the foundation of Java's portability. Any good Java programmer should definently understand at least some basic bytecode, as knowing what your code actually compiles down to is a very powerful bit of knowledge that will help you understand how sometimes 4 lines of code can be better than 3 lines of code when compiled, or how one way of doing something is better than another. Java's syntax is very similar to C's. It is a very powerful language that is still one of the top languages in use today (along side C, C++, and .NET languages). [You must be registered and logged in to see this image.]^ [You must be registered and logged in to see this link.]It is a simpler language unless you venture to some of its lower level aspects, and is a great language to learn as your first experience in programming. (will be expanded) Quick Reference- Spoiler:
Primitive Data Types:Data Type - Value Range - Default Value - Memory Size int - -2,147,483,648 to 2,147,483,647 - 0 - 32 bits (4 bytes) short - -32,768 to 32,767 - 0 - 16 bits (2 bytes) byte - -128 to 127 - 0 - 8 bits (1 byte) long - -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 - 0 - 64 bits (8 bytes) float - ~1.4 E -45 to 3.4028235 E 38 - 0.0 - 32 bits(4 bytes) double - ~1.79769313486231570 E 308 to 4.94065645841246544 E -324 - 0.0 - 64 bits (8 bytes) boolean - true/false - false - not precisely defined but it reprsents 1 bit char - \u0000 to \uffff (0 to 65,535) - \u0000 (0) - 16 bits (2 bytes) Remember that Objects have a default value of null. Uses: int: The default numeric primitive short: Numeric primitive that is more compact within large arrays byte: Smallest numeric primitive, useful for compactness within large arrays long: Large numeric primitive used to represent very large numbers float: Decimal primitive that uses less space than double, useful for large arrays double: Default decimal primitive char: represents a single character in unicode boolean: represents true or false values Special Escape Sequences: (Used in String literals)\t tab \\ backslash " double quote ' single quote \r carriage return \b back space \f form feed Access modifiers:modifier - effect - use public This field/method can be referred to inside of the class and outside of the class. private This field/method can only be accessed inside of this class. protected This field/method can be accessed inside of this class, and any of its subclasses.(REMEMBER, protected access also gives it package access). No access modifier at all when declared in a global context gives the field/method package access. Packaged access means it can be accessed inside of that class, and by any class inside the same folder as that class. (will be expanded) OperatorsArithmetic Operators+ - Addition, also used to append Strings - - Subtraction / - Division * - Multiplication % - Modulus (Remainder operator) returns the remainder through division Incremental Operators++ - increments by 1 -- - decrements by 1 Equality Operators== - returns true if the values on either side are equal >= - returns true if the value on the left is greater than or equal to the value on the right <= - returns true if the value on the left is less than or equal to the value on the right > - returns true if the value on the left is greater than the value on the right < - returns true if the value on the left is less than the value on the right != - returns true if the values on either side are not equal Conditional Operators&& - condition "and" || - condition "or" Bitwise/Bit Shift Operators& - bitwise "and" ^ - bitwise exclusive "or" | - bitwise inclusive "or" >> - shifts the pattern to the right << - shifts the pattern to the left >>> - shifts a 0 into the leftmost position Other Operatorsinstanceof - type comparison, returns true of the object on the left is of the same type of the value on the right ? : - condition operator, shortcut for "if - else". ConventionsConventions are severely lacking in this community. PLEASE USE THEM! Classes: the first letter in each word in a classname should be capitalized. Class names should be nouns and should be relevant to the job they perform/represent. The code within the class should be organized in this order:
- package
- imports
- class or interface declaration
- static fields
- instance fields
- constructors
- methods
Classes and methods should be well commented. The code itself should also be clean. Method names should be verbs, first letter of the first word lowercase, the first letter of words following should be capitalized. Field should follow the same capitalization pattern of method names, except the names should be nouns. Final field should have capitalized names. You should indent for each block of brackets, indention should traditionally be 4 spaces (most people use tab for simplicity). for more information on conventions see this link: [URL="http://java.sun.com/docs/codeconv/html/CodeConvTOC.doc.html"]Conventions[/URL] Example of conventioned class: - Code:
-
package test;
import java.io.File;
public class Test { private final int FILE_COUNT = 5; private boolean isDone = false; public Test(String[] files) { for (int i = 0; i < files.length; i++) { closeFile(files[i]); } } private void closeFile(String fileName) { new File(fileName).close(); } }
Note that the code above is pointless. Also, notice that even though the brackets used after the for loop in the constructor are not needed, its good habit to use them anyone to prevent errors. Encapsulation is also a very good technique to follow in class design, we will cover that when we get the classes though. (will be expanded) Fields/Variables- Spoiler:
In Java, both the terms variable and field are used, in the tutorial we will use both terms. A field is essentially a variable that holds a saved value. There are 4 types of fields:
- Instance FieldsAn instance field is a field that has a global scope within a class, they are called instance fields as they are non-static and each instance of that particular class has a separate "version", or value, of this field.
- Class FieldsClass variables, like instance fields, also have a global scope in a class. The difference being that Class Fields are static, meaning that each instance of that class all shares that same "version" or value of the class field.
- Local FieldsLocal Fields have a scope ONLY inside of the method that it is declared it. As a result, Local Fields do not have access modifiers as they are not global. You treat them like any other field, its just that they typically hold temporary values that only can be found in that method.
- ParametersParameters are not fields, they are variables, this is one of the few cases where the terms differ. A parameter is essentially like a local field. Parameters in Java work through call by value (for the most part), and are used to "send" a method a value when called.
Just remember the general differences for now, I will show a couple examples. Alright, declaring a field is very simple: TYPE NAME (= VALUE)*; Example: - Code:
-
int number = 5;
*being optional, if you don't declare a variable with a value the compiler will assign it a default value listed in the data section of the tutorial. This basically tells the computer to break off a chunk of memory depending on the type, and assign it the value of 5. Assuming this is in a method, it has no access modifier. The syntax is the same for all types: - Code:
-
int iNumber = 5; byte bByte =5; char cChar = 'f'; float fFloat = 5.7;
They all have the same syntax. Now creating a new instance of a class is a bit different but we can explain that later. Now, we know how to declare a field. Lets put it to some use. Math in Java (or pretty much all languages that have a C based syntax) is fairly obvious: - Code:
-
int a = 5; int b = 3; int c = a + b;
Makes sense? the value of c is the values of a and b added together. The same syntax for any math operator. - Code:
-
int a = 5; int b = 3; int c = a / b; c = a * b; c = a - b;
Notice that I didn't include the type in front of c the second and third times I assigned it values. You only include the fields type when you declare it, after that the compiler knows the type and name of the variable, so you just refer to it by its name to reassign values or use its value. This is a general overview of Fields/variables, I'll explain in more detail as far as different access modifiers etc when we get to that point. Methods- Spoiler:
Think of a method as something that you throw values into (or at least can), it performs some sort of task, and then returns a value (if its return type isn't void at least). Its hard to come up with good metaphors =P. All in all, a method is a series of statements. Statements always end in a semicolon, if you remember from the examples having to do with fields. If you also remember parameters, a parameter is a value that is given to the method as an argument when it is called, that value is usually required for that method to do its task (why have parameters for no reason?). Multiple methods can have the same name if they have parameters that are different, this is called overloading. A methods declaration syntax is a bit different: ACCESS MODIFIER* SPECIAL MODIFIER* TYPE NAME (PARAMETERS) { STATEMENTS } *being optional once again. Example: - Code:
-
public int getDouble(int a) { int b = a * 2; return b; }
Now you may be thinking, "WHAT THE FFUUU-", but don't worry! It's simple! I'll explain this in order. This method is public, meaning it can be access within and outside of its class/object. This method is also of type int, meaning this method returns an int value. It takes a single parameter of type int, meaning that when this method is called, it must be given an int as an argument. The brackets enclose the body of the Method, for every { you need a }, its a very simple rule that when not followed can lead to unhappy time wasted searching and counting brackets. A return statement exits the method with the value specified, any code beyond a return statement will not be called and usually you will get an error about unreachable code at compile-time. If the method is of type void (which has no value remember), then you use an empty return statement by just using the word return. You can use a return statement to exit a method early as well. As of now, this method does nothing. It is just declared and exists. The method doesn't actually "perform" until it is called. Example of how to call this method: - Code:
-
getDouble(5);
Notice, that like fields, we only need to use the method name. Once the method is declared you just access it through its name. Notice also that within the parenthesis we included the number 5. Remember our method having a single parameter of int? There's the argument to fulfill the methods parameters. If a method has parameters, you MUST provide them when called, meaning the following call would not compile: - Code:
-
getDouble();
or - Code:
-
getDouble(5, 5, 5);
Your arguments when you call the method must match the methods parameters. Now, remember how we said the method is of type int? That means the method returns an int value, meaning the method call itself can be treated as an int. In a method, if it is not of type void, you MUST return a value. The return type void essentially means that the method doesn't have a return value, in which case the return statement isn't required. To return a value, you just use the keyword "return" followed by the value. For example: - Code:
-
int a = getDouble(5);
The value of a is now 10. Make sense? You can also create a method of ANY type. Meaning any primitive type, or any object (as long as the class exists, we will explain that later). Here is a second example: - Code:
-
private double divide(double f, double g) { return f / g; }
This may look more complicated than the previous example, but it's not! This method is private, meaning it can only be called within its class. It is of type double, meaning it returns a double value. It also takes 2 parameters, both of type double. Now the return may look a bit different, it is essentially the same thing as - Code:
-
private double divide(double f, double g) { double result = f / g; return result; }
Both are the exact same thing, the first just being more compact. It would be called like this: - Code:
-
double hey = divide(6.0, 3.0);
hey now being 2.0. Now, we will learn our first standard library method (YAY!). It being System.out.println. Now for now, ignore the System.out., just focus on what the method does. It basically prints whatever argument you give it to the command prompt. Example: - Code:
-
System.out.println("Hello world!");
Now to combine what we have done so far: - Code:
-
private void getDouble(double f) { return f * 2.0; }
private void test() { System.out.println(divide(getDouble(5.0), 2.0)); }
Woah, thats complicated right? Nope! Lets review. This method is private, meaning it can only be called in its class. It is of type void, meaning it has no return value. It has no parameters, meaning it doesn't need any arguments, but you still need to include the parenthesis when you call it. Now this part make look kind of crazy to you right now, but its simple. Basically, this will print the value returned by divide when we pass it arguments of the value of getDouble given the argument of 5.0, and the second argument of 2.0. That may sound complicated, but lets step through call by call: getDouble(5.0) returns 10.0, so we are essentially calling divide(10.0, 2.0), which in turn returns 5.0, so System.out.println is given the value of 5.0 to print to the command prompt. Just think of it this way, the computer just treats the method calls as the values they return in this situation. We will get more in depth with methods later on when we discuss classes, (that's going to be the fun part of the tutorial). Arrays- Spoiler:
Alright, up to this point we know how to declare some basic values, do some basic math, and understand the over all basics of methods. Now we can discuss arrays! Arrays are essentially a section of memory statically allocated so that values of the same type can be organized in sequence. Put simpler, an array basically holds multiple values of the same type in a multiple "slots". The general syntax for an array uses [ ]. - Code:
-
int oneToFive[] = { 1, 2, 3, 4, 5 };
That would be an array of int's. Arrays are very useful for storing values in an organized value. You can get the length of an array by using the arrays length field: - Code:
-
int oneToFive[] = { 1, 2, 3, 4, 5 }; System.out.println(oneToFive.length);
That would print the number 5 because there are 5 slots in that array. Now you can also declare an empty array: - Code:
-
int[] oneToFive = new int[5];
Notice the square brackets, they are now after int, and notice the number within the square brackets at the end, that is the length of the array. Ignore the new operator for now, that will be explained later on. Accessing specific values in an array is just as easy: - Code:
-
int[] oneToFive = new int[5]; oneToFive[0] = 1; oneToFive[1] = 2; oneToFive[2] = 3; oneToFive[3] = 4; oneToFive[4] = 5;
Now you may be wondering why I only went up to four, remember, the index to the first slot in an array is always the number 0. So in an array that has the length of 5, the slots would be 0, 1, 2, 3, 4. Arrays work hand in hand with loops, which will be explained soon. You can create an array of any type, primitive or object: - Code:
-
double nums[] = {0.0, 2.3, 4.4}; System.out.println(nums[2]);
That would print the number 4.4, as the arrays lenght is 3, so the last slot in the array has an index of 2. Strings- Spoiler:
Since Strings are essentially the most common object used in Java, I decided I might as well dedicate an entire section to them. A string basically represents a string of characters. Strings are implicitly created meaning you won't have to use the new operator unless you're converting a byte array or something else into a String. - Code:
-
String hello = "Hello!";
There are plenty of nifty String methods that make strings very easy to use. The equals(String) method, very self explanatory, its case sensitive, and checks to see if 2 strings are equal. - Code:
-
if ("hello".equals("hello"))
Remember, Strings are implicitly created, meaning that "hello" is literally treated like a String object, which is why the above code is legal. Another version of the equals method is equalsIgnoreCase(String), it does the same thing but without case sensitivity. Here are some common string methods: toLowerCase() - returns a lower case version of the string toUpperCase() - returns a upper case version of the string charAt(int) - returns the character at the specified index in the string, after all, a string is an array of characters at its core. substring(int), substring(int, int) - returns a string based off of the given index's. indexOf(String) - returns the index of the first occurrence of the specified string. lastIndexOf(String) - returns the index of the last occurrence of the specified string. startsWith(String) - returns whether or not the string begins with the specified string. endsWith(String) - returns whether or not the string ends with the specified string. split(String) - returns an array of string resulting from splitting the current string at every occurrence of the specified string, similar to the StringTokenizer. toCharArray() - returns a character array created from the string. getBytes() - returns an array of bytes from the string. Flow and Control- Spoiler:
Now we get to the more logic part of programming. Without control and flow, it is impossible to write a program. Coniditional StatementsWe will start of with the if statement: - Code:
-
int f = 4; if (f == 4) { System.out.println("F is four"); }
Remember those curly brackets from methods? Well here they are again. Brackets are used to enclose blocks of code. For instance, the above code prints "F is four". The if statement simply checks if the condition specified is true, if so it will execute the code within its block, if not it will continue, ignoring the code in its block. If we have a conditional statement followed by only a single statement to execute, brackets are not needed. If a conditional statement is not followed by no brackets it will only execute the first statement after the condition. The same is true for loops. Now for some logical operators, they are defined in the very beginning of this tutorial if you need to look back. Like in the above example, == is used in checking for values, = is the ASSIGNMENT operator, it is not the same as == and cannot be used to generate the same effect. Now in addition to ==, we have !=, &&, and ||, meaning equals, not equals, and, and or respectively. An example of the and: - Code:
-
int f = 4; if (f > 3 && f < 5) { System.out.println("F is four"); }
This code will also print "F is four", and is fairly self explanatory. If f is greater than 3, and f is less than 5.... is how it should be read, picture it like that in your head and it will make more sense. The || operator can be used in a similar way to mean "or". - Code:
-
int f = 4; if (f == 3 || f == 4) { System.out.println("F is four or three"); }
This code will print "F is four or three", as f has the value of 4. I suggest going to the top of this page and reviewing the operators used with conditional statements to make sure you know them. Now working along side if the if statement, are the else if statement and the else statement. They work as would be expected: - Code:
-
int f = 3; if (f == 4) { System.out.println("F is four"); } else if (f != 4) { System.out.println("F is not four"); }
This code would print "F is not four". Read it can be pictured like this: "If f equals 4 ... else if f does not equal four...". Now we also have the else statement, which stands alone as a "last resort". - Code:
-
int f = 2; if (f == 4) { System.out.println("F is four"); } else if (f == 3) { System.out.println("F is three"); } else { System.out.println("F is not four or three"); }
This code would print "F is not four or three", and Read it can be pictured as "If f equals 4 ... else if f equals 3 ... else". The else block is executed if none of the other conditions are true. We also have a sort of shortcut, called the conditional(ternary) operator. Its syntax may look weird but its very simple: - Code:
-
CONDITION ? IF TRUE : IF FASE
Example: - Code:
-
boolean ass = true; System.out.println(ass ? "ass" : "no ass");
That code would print "ass", as ass is true. Notice how we used it to represent a value, this would do the same without the shortcut: - Code:
-
boolean ass = true; if (ass) { System.out.println("ass"); } else { System.out.println("no ass");
The operator doesn't really provide much other than simplicity and cleaner (sometimes) code. You can also "shortcut" it on booleans, if you noticed in the above example rather than having: - Code:
-
if (ass == true)
I simply used: - Code:
-
if (ass)
Reason being that a coniditional statement always evaluates to either true or false, so having ass == true is similar to having true == true or false == true (depending if ass is true or false), which is basically pointless. Since we can use - Code:
-
if (ass)
You may be wondering how we would "shortcut" check if ass is false, that's when we use the ! operator, which simply represents "not": - Code:
-
if (!ass)
Meaning if ass is false. Now for more control, next we will explain loops. LoopsNow, at this point what we have covered has been very procedural and tedious. Loops are a very powerful tool in any programming language and another fundamental part of control flow. A loop cycles until the specified condition is no longer true, they have a couple different styles though. Exiting a loop early can be done by using the break keyword, if you wish to end a specific cycle early but not entirely exit the loop you would use the continue keyword. There are a couple types of loops. For LoopThis is the most commonly used loop. Loops are very simple to understand and use. First I will show the syntax of the for loop: - Code:
-
for(int i = 0; i < 20; i++)
Lets explain each part of the loop: - Code:
-
for(INITIALIZATION ; CONIDITION ; INCREMENTATION)
Simply put, the first "slot" in the for loop is executed before the loop begins, it is usually used for declaring your index variable. The second "slot" in the for loop is the conidition, the loop will continue to cycle until that conidition is false. The last "slot" in the for loop is the incrementation, this is called after each time the loops cycles and is usually used to control the condition (or the amount of times the loop cycles). Example: - Code:
-
for(int i = 0; i < 20; i++) { System.out.println(i); }
The above code will print i until i is no longer less than 20. This idea of looping using an index is very useful when used with arrays: - Code:
-
int nums[] = {3, 5, 2, 3}; for (int i = 0; i < nums.length; i++) { System.out.println(nums[i]); }
This loops through every slot in the array and prints its value. Remember that the first slot in an array is 0, so the last slot in an array is the arrays length - 1. The "slots" in a for loop don't always have to be meet, meaning this would result in an infinite loop: - Code:
-
for (;;)
You can fill in virtually any conidition, initialization, and post-cycle statement that you wish: - Code:
-
String a =""; for(;a.length() != 5; a += "a")
Is fairly self explanatory, appends the letter "a" as long as the strings length is 5. While LoopThis loop is probably even simpler than a for loop: - Code:
-
while(CONIDION)
This basically continues to run until the conidition is false. It can be set up for comparison to a for loop: - Code:
-
int i = 0; while (i < 20) { //do stuff i++; }
This essentially would work like a for loop. Now you may have noticed the 2 forward slashes. That is called a comment. You can place comments into your code and the compiler will ignore that line. // comments an entire line /* */ comments everything between the asterics. The while loop is useful when you don't have to loop with a numeric value. It also is useful for infinite loops, which are used more with threads which will be explained later on. But since coniditions always evaluate to true or false, the following syntax is legal and will create a never ending loop: - Code:
-
while (true)
Do While LoopThe do while is very similar to a while loop. The only real difference is that it gaurentees at least 1 cycle of the loop as the conidition is checked at the end of the loop rather than the beginning like a while loop. - Code:
-
int i = 5; do { System.out.println("looped"); } while (i != 5);
That would print "looped" once, as the conidition isn't checked until the end of the loop. Note that the syntax is a little different. For Each LoopThis loops was essentially designed as a sort of shortcut replacement for iterators, which are used with data structures, which we won't talk about until later. We won't use the for each loop until later when we discuss data structures, but we might as well cover here as it fits in. here is the syntax for a for each loop: - Code:
-
for (TYPE NAME : ITERABLE)
It basically loops through every value in a structure or array, assigning each value to the specified variable each cycle until there are no more values left. - Code:
-
int nums[] = {3, 4, 3, 2 }; for (int i : nums) { System.out.println(i); }
This will loop through each value in the array and print it. This loop is really meant to be used with a collection of some sort, and really shouldn't be used in any other way. Lets compare: - Code:
-
class a { int nums[] = {2, 4, 6, 2, 3, 3, 3, 3, 3}; void b() { for (int i = 0; i < nums.length; i++); } void c() { for (int i : nums); } }
Now lets look at the bytecode generated: - Code:
-
void b(); Code: 0: iconst_0 1: istore_1 2: iload_1 3: aload_0 4: getfield #2; //Field nums:[I 7: arraylength 8: if_icmpge 17 11: iinc 1, 1 14: goto 2 17: return
void c(); Code: 0: aload_0 1: getfield #2; //Field nums:[I 4: astore_1 5: aload_1 6: arraylength 7: istore_2 8: iconst_0 9: istore_3 10: iload_3 11: iload_2 12: if_icmpge 26 15: aload_1 16: iload_3 17: iaload 18: istore 4 20: iinc 3, 1 23: goto 10 26: return
Notice the difference? Only use the for each loop when working with something that can't get iterated through using an index. Error CheckingJava's error checking is excellent compared to C's, (or even C++'s though they have a similar concept) in my opinion. Error checking has a couple routes. First off, basic error checking (which really isn't much of error checking, more like invalid input checking) is fairly simple: - Code:
-
int doubleNumber(int num) { if (num < 1) return; return num * 2; }
The line checking to make sure that num is positive would be an example of basic input checking. Java has a feature for runtime errors called exceptions, exceptions when caught and dealt with are very simple to manage and make solving problems much easier. When ignored however, searching for runtime errors can become tedious and painstaking. Catching an exception is very simple using the try-catch clause: try { /*statements that may produce exceptions */ } catch (EXCEPTION) { /* code to execute if an exception exception occured } Example: - Code:
-
String number = "wut"; try { int a = Integer.parseInt(number); } catch(NumberFormatException e) { e.printStackTrace(); }
When compiled, this code has no issues, but once ran its a different story. When the program tries to parse "wut" from a String to an integer and exception is thrown for obvious reasons, but the exception is caught and the a trace of calls up until the exception is printed making the cause easier to find. Then the program will continue. Now if we didn't catch that exception, the program would run and then crash once the exception is thrown without being caught. All exceptions in Java are subclasses of the Exception class, so if you have a block of code that could possibly throw multiple exceptions, if you don't have a specific task for each exception you could just catch Exception: - Code:
-
try { /* code */ } catch(Exception e) { /* code */ }
That would catch any exception thrown in that block. Sometimes, a try-catch statement is required though, and this a good practice. You simple just add a "throws" statement along side the method declaration: - Code:
-
public int parse(String a) throws NumberFormatException { Integer.parseInt(a); }
This would force you to check for the exception NumberFormatException when calling the parse method: - Code:
-
try { parse("25353"); } catch(NumberFormatException e) { System.out.println("Error parsing String to Int"); }
We can also forcely throw exceptions within our code using the "throw" statement: - Code:
-
public int parse(String a) { if (a.length() < 1) throw new NumberFormatException(); else Integer.parseInt(a); }
Notice that we used the new operator, Exceptions are objects. There is also a balance between exiting a method early if a problem occurs, which is sometimes not forceful enough and the user may not even know that an error occurred, and throwing an exception, which is sometimes to forceful. This is called asserting. - Code:
-
public int parse(String a) { assert a.length() > 0; Integer.parseInt(a); }
If the length of a is less than 1 than an AssertionError is thrown. This assures that from any point after the assert statement that the asserted condition is true. Switch StatementA switch statement is used for testing a variable for multiple numeric values. You can switch types other than just integer primitives (byte, int, long, short) like char's and enum's, but in reality chars and enums are actually numerical at the core. A switch statement is generally a pimped out series of goto statements used for checking the value of a variable, it is generally faster and cleaner than using a series of coniditional statements and this community severely lacks the use of the switch. The syntax is fairly simple: switch (VARIABLE) { case VALUE: //code } of course with as many cases as you need. You can also use the break keyword in switch statements to exit the switch, a common mistake is to not use the switch statement, since in reality a switch is a complex series of goto statements and labels, the code will not stop executing after that specific case is over unless you break: - Code:
-
int num = 1; switch(num) { case 1: System.out.println("one"); case 2: System.out.println("two"); case 3: System.out.println("three"); }
The above code would print "one", "two", and "three" as the program would jump to case 1 and execute from there onward. To prevent this, we use the break statement: - Code:
-
int num = 1; switch(num) { case 1: System.out.println("one"); break; case 2: System.out.println("two"); break; case 3: System.out.println("three"); break; }
This would print only "one" as it would break out of the switch at the end of the case. Not using break statements can also be a good trick, for example: - Code:
-
int num = 3; if (num == 3 || num == 2) System.out.println("Num is two or three"); else System.out.println("Num is not two or three");
would be the same as: - Code:
-
int num = 3; switch (num) { case 2: case 3: System.out.println("Num is two or three"); break; default: System.out.println("Num is not two or three"); break; }
Now if num is 2, it would jump to case 2 and continue to run until it hits a break, the same being with case 3. You may be wondering what the default statement is. Default is jumped to if the value of the variable doesn't match any other cases, meaning if num wasn't 2 or 3 it would jump to the default case. Classes- Spoiler:
Here's the fun part of the tutorial. A class is basically a template that defines the data and actions that specific objects derived from that class can use. It is probably the most important aspect of Java, and it goes hand in hand with object oriented design. Every java program must include at least a single class. It is required, unlike other languages that allow stand-alone procedural code, java is entirely object oriented. declaring a class is fairly simple: (ACCESS MODIFIER)* class NAME *being optional example: - Code:
-
public class Test { //methods etc. }
If you read my description of a class, you probably noticed the word object, and you may have noticed when we discussed the String. An object is a specific instance of a class. Unless static, every object has its own "version" of the classes data (or public field). This is the difference between static and non-static fields and methods. Non-static fields and methods must be accessed and called on a specific object, while static methods are not called on a specific object and therefore cannot directly modify non-static data. Example: - Code:
-
public class Test { public int number = 5; }
It would be illegal code to attempt this: - Code:
-
Test.number = 3;
Because number is non-static, therefore we must access it through a specific instance of that class (object). Creating an instance of an class is fairly simple also, this is where the "new" operator comes in. Every class has a constructor, whether you define it or not. A constructor is called when you create a new instance of a class. Constructors are usually used for the purpose of giving that instance specific data that it needs in order to be used. A constructor is declared just like a method except that it has no type and its name is the same name of the class. Example: - Code:
-
public class Test { public int number;
public Test() { number = 5; } }
Now we can create a new instance of that class using the "new" operator: - Code:
-
public Test test = new Test();
Like that. Now we can also access non-static number: - Code:
-
test.number;
like so. Accessing data/methods from a class/object is done using a period (.). Now constructors can also take parameters, just like any other method: - Code:
-
public class Test { public int number;
public Test(int i) { number = i; } }
- Code:
-
public Test test = new Test(5);
Like that. AVOID STATIC. Static is typically used for mathematical operations where calling on a specific object isn't necessary, or if the class is abstract and therefore cannot be initialized (we will get into that later). Other than that it is bad practice and causes spaghetti code and bad design. Classes are also where usability, practicality, and efficiency all clash. Faster is not always better, and neither is prettier. Using a stable and effective combination of both, while keeping your code reusable and easy to understand is only gained through planning and practice. This the point in programming where structure becomes more important, and in order to avoid "glue code", you should plan and think things out before jumping into a project. An example would be when I wrote my basic chat system/instant messaging thing. I rewrote the same project 3 separate times, as I would get half way through and realize that I could have done something better, or that my code was ugly and I found myself creating more and more spaghetti code. Unfortunately, I learned how and why to plan by messing up many times, and I'm a fond believer in the concept of learning though trial and error (though tutorials always help too ). Spaghetti code is when you have unorganized code that is very difficult to follow and understand. As beginners, this is a very common thing to encounter as people tend to just "write whatever works" without caring about its overall design. This may be fine for simple tasks, or even small pieces of work, but in the long run it is a very error prone and pain staking habit. This kind of code is known to produce "interesting" errors that are very difficult to find, as tracing through your code in a logical manner is very difficult without it being properly designed. Glue code is usually a result of both bad design/lack of design and spaghetti code. It is when you end having to write, well, crappy code to fix or extend onto your project. If you find yourself having to go back and rewrite half your methods, add methods that are basically copy and pasted from another place in your project and modifying a few lines, or are forced to overuse static, then you're most likely dealing with glue code. But, for now you're probably just thinking "I don't care, I just want my free runescapes to have more players!", that's great, and I realize that the majority of people won't hesitate to add more glue code and spaghetti code into their massive piles of static and illogical code even after reading this tutorial, but I hope my rambling helps at least one person who is reading this tutorial to actually learn something, and not just figure out "how to ad dem ints". Anyway, back on topic. Similar to the concept of static being used for mathematical operations, it is also used in what is called a utility class. You can divide classes into 3 unofficial categories: Utility ClassesA purely static class, has static fields and methods. An example would be the Math class from java's library, or the misc class from the majority of RSPS bases. These typically are used for storing methods that perform some sort of algorithm and don't need to be called on a specific object. Actor ClassesThese typically perform some sort of job. This is one of the more common classes, an example being the StringTokenizer class. Usually the name ends in -er or -or. Good for more complex jobs than something like a method that can handle along and that may need to be done multiple times. Representative ClassesSort of an unofficial category I just added. Used to represent something, for instance the Player or Client classes in RSPS. Or the String class. These are the most common type of class, and is a wide category as they typically take concepts from the other two. InheritanceInheritance is the foundation of object oriented design. We will start of with simple superclass - subclass relations. When you extend a class, you inherit that classes methods and fields. You then have the ability to override them, add methods and fields specific to the subclass, or call the superclasses methods or access its fields. Up til this point, we haven't mentioned the keywords this or super. This is an implicit parameter added by the compiler, it is a reference to the object that owns the method that is being called. As for inheritance, when you extend a class, the subclass is also now type of the superclass, which is why in error checking we can catch just the Exception class as all the specific exceptions are subclasses of Exception, thus making them all type of Exception also. Parent classes should be more general than subclasses, here is a simple example: - Code:
-
class Vehicle {
}
class Truck extends Vehicle {
}
class Car extends Vehicle {
}
Now, we could create a method that takes a parameter of type Vehicle, and be able to use objects of all three of these classes and have it work. This concept is called polymorphism, as the compiler doesn't know what specific class the object originated from until compile time. - Code:
-
public void drive(Vehicle v) {
}
drive(new Car()); drive(new Truck()); drive(new Vehicle());
Remember, you can always go from more specific to less specific, but never the other way around, meaning the following example would not compile: - Code:
-
public void drive(Car c) {
}
drive(new Car()); drive(new Truck()); drive(new Vehicle());
Even know Car is of type Vehicle, we can't go from less specific to more specific. We will fill in some shit code just to use for out example: - Code:
-
class Vehicle { public int tires; public Vehicle(int tires) { this.tires = tires; }
public void drive() { }
public void park() { } }
class Car extends Vehicle { public Car() { this(4); }
public Car(int i) { super(i); } public int getTires() { return super.tires; } }
class Truck extends Vehicle { public Truck() { this(4); }
public Truck(int i) { super(i); } public int getTires() { return super.tires; } }
First of all, lets look at the constructor of Vehicle. Notice the use of the implicit parameter this. Both the parameter and the instance field have the same name, but in Java, the local variable always wins over the global. If we had done tires = tires it would have set the parameters value to itself, in other words not doing really anything, but used the implicit parameter this to refer to the objects global variable. Look at the constructor of both Car and Truck, you can use the word super to refer to the superclass's constructor, so we can explicitly class the superclass's constructor using the word super like a method call. Calls to super's constructor must always be the first line in the subclasses constructor though. Notice the other constructors for Car and Truck, they use the implicit parameter this to call the other constructor of this object, meaning that it essentially calls the other constructor of the same object. Now take a look at the getTires method for both Car and Truck, they use the word super again, but this time in a similar way that we originally used the implicit parameter. Since the constructor called supers constructor, which in turn set tires value, we can use super.tires to refer to that value. Using this concept, we can require subclasses to implement their own version of methods using a concept known as abstraction. When you declare an abstract method, you must make the class itself abstract. You cannot "new" an abstract class. Abstract methods do not have a body, they are simply the declarations, and as a result the subclasses must implement the code for the method themselves, which is why you cannot new an abstract class as all of the code doesn't exist for that class. - Code:
-
abstract class Vehicle { public abstract void drive(); public abstract void park(); }
class Car extends Vehicle { public void drive() { }
public void park() { } }
class Truck extends Vehicle { public void drive() { }
public void park() { } }
This is also a powerful concept as it allows you to create "requirements" for the subclasses, if the subclasses do not have the methods that where declared abstract in the parent then you will receive a compile error. In java, a class can only extend a single class. In languages like C++ there is a feature of multiple inheritance, so the designers of Java made up for this lack by adding interfaces. Think of an interface as a purely abstract class (sort of). The difference being that in an abstract class, you can still have regular fields and methods, but in an interface, everything is abstract and public. You don't need to declare methods in an interface abstract as they already are by default, and just like an abstract class, you cannot new an interface. You can realize (technical term for implement) as many interfaces as you wish. Rather than using the word extends, you use the word implements. - Code:
-
interface Vehicle { void drive(); void park(); }
class Car implements Vehicle { public void drive() { }
public void park() { } }
class Truck implements Vehicle { public void drive() { }
public void park() { } }
And just like abstract classes, interfaces can be treated as a type so you can use it for method parameters etc. Efficiency- Spoiler:
Up to this point, coming from a RSPS background, the majority of you probably don't care to much about, or know to much about, efficiency. A key rule; speed does not equal efficiency, and neither does less code. While in a majority of cases, those two things are the ultimate goal, efficiency in itself is typically measured using big-O. Big-O is a algorithm used to measure efficiency of code to complete a specific task. There are multiple types of big-O algorithms: ConstantO(1) Logarithmic
O(log N) Linear
O(N) Exponential
O(2^N) Quadratic
O(N^2) CubicO(N^3) These all have their meanings. N representing the number of operations in relation to the size of data. Meaning constant, being O(1), will always be the same number of processes and speed no matter what data size, and example being opening a file. No matter how large a file is, creating a stream to read/write to that file will always take the same amount of time. Linear is O(N), meaning that the number of processes and time taken is directly proportional to the amount of data, O(N) is usually seen as a single loop. Quadratic, being O(N^2), is usually seen as a nested loop (A loop inside of a loop). This general notation is used for efficiency. Learn it, embrace it, and use it. big-O is fairly simple to understand once you think about it, the more common algorithms being O(1), O(N), O(N^2), and O(N^3). Example of O(N): - Code:
-
private void printData() { int data[] = {2, 3, 4, 5, 7, 2, 3}; for (int i = 0; i < data.length; i++) System.out.println(data[i]); }
Example of O(N^2): - Code:
-
private void printData() { int data[] = {2, 3, 4, 5, 7, 2, 3}; for(int i = 0; i < data.length; i++) for(int k = i; k < data.length; k++) System.out.println(data[k]); }
Links- Spoiler:
Tutorials[You must be registered and logged in to see this link.]IDE's[You must be registered and logged in to see this link.]Text Editors[You must be registered and logged in to see this link.] Use the javadocs! They will answer a lot of your questions: [You must be registered and logged in to see this link.]This will also be very useful on specific topics:[You must be registered and logged in to see this link.]This video is good for learning about memory, and pointers:[You must be registered and logged in to see this link.]^ The entire series of all 30 videos is quiet good as well Credits Chris 90% - Supplied the information Me 10% - Made the thread. | |
|