Home Previous Next

CSC110AB::Lecture Note::Week 09
Assignments | Handouts | Resources | Email Thurman {Twitter::@compufoo Facebook::CSzero}
GDT::Bits:: Time  |  Weather  |  Populations  |  Special Dates

Overview Assignment(s): [All assignments have been assigned.] Code: [String & StringBuffer] MoreStringExamples.java | DemoStringBuffer.java | ReverseString.java
[arrays] A.java | Arrays.java | PasswordGenerator.java | Phone.java

class String

A string is a sequence of zero or more characters. String objects, once created, cannot be modified (i.e. they are immutable).

Frequently used String methods are:

It is important to remember that when you are testing two String objects for equality, then you must use the equals() method.

   String s1 = new String("hello");
   String s2 = new String();
   String s3 = new String(s1);
   String s4 = "bye";
   s4 = s1;

   if (s1 == s2)   // evaluates to false in this example
      // tests if  s1  and  s2  reference the same String object

   if (s1.equals(s2))   // evaluates to false in this example
      // tests if the content of the object referenced by  s1
      // equals the content of the object referenced by  s2

   s1.equals(s3)   // evaluates to true in this example
   s1 == s3        // evaluates to false in this example
   s1.equals(s4)   // evaluates to true in this example
   s4.equals(s1)   // evaluates to true in this example
   s1 == s4        // evaluates to true in this example

Docs.Oracle.com::String API

Additional String Related Notes

Here are some more notes concerning some of the String instance methods.

The substring() method is used to get a sub-string of an instance of a String object.
   public String substring(begin_i, end_i);

   "Hello".substring(0, 2)

      returns a String object with  He  as its content.
startsWith() and endsWith()
The startsWith() and endsWith() return true or false if the this object starts or ends with a particular String value.
   public boolean startsWith(String prefix);
   public boolean endsWith(String suffix);

   "WebAccount.java".startsWith("Web")   returns  true
   "WebAccount.java".startsWith("web")   returns  false
   "WebAccount.java".endsWith("Web")     returns  false
   "WebAccount.java".endsWith(".java")   returns  true
The compareTo() method is used to test String objects for equality. You use it over the equals() method when you need to determine if one string is less than or greater than another string (e.g. you are sorting strings). If compareTo() returns a value less than 0, then this string is lexicographically less than the string received as a parameter; otherwise, a 0 return from compareTo() implies the strings are equal; else, a positive return value indicates that this string is lexicographically greater than the parameter string.
   public int compareTo(String that);

   "hello".compareTo("bye")     returns positive value
   "hello".compareTo("jello")   returns negative value
   "hello".compareTo("hello")   returns zero
[review Stringer.java]

{TopOfPage} {Oracle.com::API Specification | Tutorial} {GDT::Java Resources} {Eclipse IDE} {Udacity} {udemy} {CodingGround (online IDE)

class StringBuffer

The String class is used to create immutable String objects. In some cases, when you are dynamically building a string, it is more efficient to use a StringBuffer object. The StringBuffer class implements a buffer for characters that allows updates and allows the buffer to grow and shrink as needed to accommodate the updates.

Oracle.com::API::class StringBuffer | class String

[ DemoStringBuffer.java]

{TopOfPage} {Oracle.com::API Specification | Tutorial} {GDT::Java Resources} {Eclipse IDE} {Udacity} {udemy} {CodingGround (online IDE)


An array is a aggregate (collection, set, group, list) of individual data values with two characteristics: it is ordered and homogeneous.

The following are some array-related terms:

An array is homogeneous because each element must be of the same type. Example: an array of int values, an array of float values, an array of char values, and array of Object values (handles, references, pointers).

An array is ordered because it has a 1st element, a 2nd element, a 3rd element, and so on. Array elements are probably stored in contiguous memory locations [this is definitely true in C and C++; in Java it is left up to the JVM implementors].

Here is a list of items you should understand when using arrays:

Defining an Array of Primitive Types

The following creates an array of long values:

   long[] ssNbrs;      // an array of social security numbers

      // ssNbrs is an object variable that is going to point to
      // an array of  long  values; at this point and time, it 
      // doesn't point to anything (i.e. it is equal to  null)


   int n = fooA();    // get the array length somehow


   ssNbrs = new long[n];

      // array length is decided at run-time (i.e. we don't know
      // the array length at compile-time; the information is
      // obtained from a user, or from a database file, etc.)
      // two things occur when an array is defined:  memory is
      // allocated from the heap and the memory is initialized
      // to the appropriate default values  [0 for integral and
      // floating-point types,  false  for  boolean  arrays, and
      // null  for arrays of object references]


   for (int i = 0; i < ssNbrs.length; i++)
      ssNbrs[i] = -1L;  // initialize each element to -1

      // use the  <  relational operator is important; a common
      // programming defect is to use the  <=  operator -- what
      // would happen if we did?


   ssNbrs = new long[2 * n];

      // after an array has been created, its length if fixed; if you
      // need more elements, then you need to create a new array;
      // now  ssNbrs  references (i.e. points to) an entirely
      // different array of long values; the original array still
      // exists, but the reference to it has been lost and it are
      // not able to copy the original values into the new array

   long[] orig = ssNbrs;
   ssNbrs = new long[n * 3];

   for (int i = 0; i < orig.length; i++) ssNbrs[i] = orig[i];

      // the handle to the original array is stored in the array variable
      // named  orig.  The new array is allocated and the contents of
      // the original arrray is copied to the new array

   System.arraycopy(orig, 0, ssNbrs, 0, orig.length);

      // arraycopy(Object src, int srcOffset, 
      //           Object destdst, int destOffset, int count);
      // the  arraycopy()  method copies a region of one array, src,
      // beginning at element  srcOffset, to another array, dst,
      // beginning at element  destOffset;  count  elements are copied.
      // note:  the arrays must already be allocated

   Tip:  In the control-EXPR of the loop, I'm better off using
         ssNbrs.length  rather than the variable  'n'  because 
         since 'n' is a variable, it could be modified such that 
         the EXPR  n == ssNbrs.length  is not true.
Defining an Array of Objects

The following code snippet an array of object variables.

   Integer[] iArray;

      // iArray  is a reference (handle, pointer) to an array of 
      // Integer references -- at this point and time, it doesn't
      // "point" to any array


   iArray = new Integer[10];

      // array length is known compile-time; an array to store 
      // 10 Integer references is created; each array element is 
      // initialized to   null   (recall,  null  is a Java keyword)


   for (int i = 0; i < iArray.length; i++)
      iArray[i] = new Integer(i);

         // an  Integer  object is created and a reference to it is
         // stored in  iArray; when the Integer object is created,
         // it is initialized to the value of 'i'...

Arrays are always stored on the heap. The reference to the array will be stored either on the stack or in the data segment depending on where it is defined.

The following are some comments on the efficiency of arrays:

Arrays.java | A.java

{TopOfPage} {Oracle.com::API Specification | Tutorial} {GDT::Java Resources} {Eclipse IDE} {Udacity} {udemy} {CodingGround (online IDE)

Introduction to Methods

Lowest level ideas become expressions (EXPRs), expressions are grouped into statements, and statements are grouped together into blocks and methods.

A method is a set of statements that have been collected together and given a name.

Methods break large computing tasks into smaller ones, and enable people to build on what others have done instead of starting over from scratch. Methods support reuse.

Methods hide details of operation from parts of the program that don't need to know about them. Methods support information hiding.

Methods usually consist of zero or more statements, and a collection of local data. Methods support encapsulation.

Programs generally consist of many small methods rather than a few big ones. Methods support modularity.

Repetitive expressions should be grouped into a method. Methods eliminate duplicate code.

Additional benefits derived from using methods:

Methods that perform generic tasks can be added to a class library. Library methods can be used in multiple programs. Library methods are static methods.

Java doesn't allow for global methods; i.e. every method must be defined in a class.

Static methods are class methods; whereas non-static methods are instance methods.

Instance methods typically contain code that understands and manipulate an object's state.

Static methods can be invoked in one of two ways:

Instance methods can only be invoked using an object variable.

Every instance method has access to an object variable named this. The this object variable points-to (or is a handle-to, or is a reference-to) the recipient object.

A recipient object is the object for which a method has been called.

Statically defined methods do not have access to the this object variable.

Calling an instance method is equivalent to sending a message to an object. The values of the arguments, if any, make up the content of the message. If no arguments are used, then you have an empty message.

Method arguments are passed by-value. All argument EXPRs are evaluated prior to the method being called.

[review Underline.java]

{TopOfPage} {Oracle.com::API Specification | Tutorial} {GDT::Java Resources} {Eclipse IDE} {Udacity} {udemy} {CodingGround (online IDE)

Method Terminology

The act of executing a set of statements associated with a method is known as calling the method. If method A calls method B, then method A is referred to as the calling or caller method and method B is the called method.

Arguments are a list of EXPRs that are evaluated and whose values are passed to the method. (Note: Java is a "pass-by-value" languages.) A method does not need to take arguments. If method A calls method B, then arguments allows method A to communicate (pass) information to method B.

Arguments, if any, that are passed to a method B, are parameters within method B. Parameters are treated as initialized local variables.

When a method is done, it returns to the caller by executing a return statement.

Methods can "return a value" back to the caller; thus, if method A calls method B, then the return value allows method B to communicate with method A.

{TopOfPage} {Oracle.com::API Specification | Tutorial} {GDT::Java Resources} {Eclipse IDE} {Udacity} {udemy} {CodingGround (online IDE)

Calling a Method

A method is called by specifying the method's name, followed by a left paren, a comma separated list of arguments (if any) and a right paren. Comma when used in this context is not the sequence operator.


   int rv = x(200, 210);
      //call the method  x  passing it two values: 200 and 210
      //capture the return value from  x  in the variable rv

      //call the method  beep()  implemented in the System class
      //no arguments are passed, nor does the method return any value

A method call is a sequence point: every EXPR that comprises an argument is evaluated prior to the method being called. The order in which the arguments are evaluated is left-to-right.

Java is a "call-by-value" language: the arguments are evaluated and their respective values are passed to the method.

{TopOfPage} {Oracle.com::API Specification | Tutorial} {GDT::Java Resources} {Eclipse IDE} {Udacity} {udemy} {CodingGround (online IDE)

Method Definitions

The method definition contains the statements that comprise a method. The definition of a method is also referred to as its implementation.

A method declaration consists of two parts: a header and a body.

Method statements are enclosed in {}'s and are referred to as the method body.

The body of a method consists of zero more variable declarations/definitions and zero or more statements.

At most one access specifier modifier can be used when a method is defined (public, or private or protected). If an access is not used, then it defaults to package access.

Class methods are defined using the static modifier. Instance methods are defined without using static.


   public static int x(int n1, int n2) {
      //this is the method body...

   public static void y(float f, char c, int i) {
      //this method doesn't return a value; it takes three
      //   parameters (a float, a char and an int)
      if (i) return;

When a method is defined, each parameter, if any, must be specified with a type and a name. Parameter type-name pairs are separated by comma's. [parameter type list]

Parameters can be thought of as initialized local variables (i.e. space is allocated for them from the stack and they are initialized to the value of the arguments that are passed).

Variables declared inside of a method body are visible only with that block of code. These variables are referred to as a local variables.

A method definition can contain multiple return statements; however, many programmers are of the opinion that a method should have only one exit point.

A stub method is a method that contains no statements.

Every method definition should begin a method comment block that desribes what the method does.

A method signature consists of the method name and the parameter type list enclosed in parentheses.

An instance method can access and/or modify an object's state. A static method is invoked on behalf of an entire class, not on a specific object instantiated from that class.

A static method can only access static fields and static methods of a class.

Method Modifiers
   access specifiers (public, private, protected)
   abstract (method has no body)
   static (class method)
   final (method cannot be overridden)
   synchronized (used with threads)
   native (written in some other language)
   strictfp (strict floating-point)

{TopOfPage} {Oracle.com::API Specification | Tutorial} {GDT::Java Resources} {Eclipse IDE} {Udacity} {udemy} {CodingGround (online IDE)

Method Comment Blocks

Every method should begin with a method comment block. The method comment block contains - at a minimum - the name of the method, a list of arguments that it receives, a description of its return value (if any), and a brief description of what the method does. In addition, any side-effects (or outputs) performed by the method should be documented.

The following is an example function comment block.

    * name:  isVowel
    * parameters:  char -- a character
    * returns:  true if parameter value is vowel; false otherwise
    * description:  This method tests to see if a character is a
    *               the vowel (a, e, i, o, u).  The character can
    *               be either upper- or lower-case.

Cay Horstmann quote.

"The description of a method comment block does not document the implementation but the idea."

{TopOfPage} {Oracle.com::API Specification | Tutorial} {GDT::Java Resources} {Eclipse IDE} {Udacity} {udemy} {CodingGround (online IDE)

Method Return Value

A method communicates back to the caller by returning a value.

The type of value returned by a method (i.e. the return-type) is specified when the method is defined.

A method does not have to return a value. In these cases, the return-type void is used.

Other points.

The syntax of the return statement is as follows.

   return;       //used when the return-type of the method is void

   return EXPR;
      EXPR is evaluated.  The result of the evaluation must
      match the return-type of the method.  If it doesn't,
      then explicit type-cast is needed.  For example,
      if the return-type of the method is  int  , then

         return (int)3.14;  

      will cause the 3.14 double to be converted to an int; therefore,
      the calling method will be returned the value 3.

   Optionally, the return EXPR can be enclosed in parens.

      return (EXPR);  
      return (3.14);           or   return 3.14;
      return (i + 3 * j);      or   return i + 3 * j;
      return (i > 4);          or   return i > 4;

If a method returns a value, then it is the caller's responsibility to examine the return value.

If a methods's return-type is void, then return statements, if any, cannot have any expressions.

{TopOfPage} {Oracle.com::API Specification | Tutorial} {GDT::Java Resources} {Eclipse IDE} {Udacity} {udemy} {CodingGround (online IDE)

Method Overloading

Method overloading is the ability to give different methods the same name.

Stroustrup says.

Most often, it is a good idea to give different methods different names, but when some methods conceptually perform the same task on objects of different types, it can be more convenient to give them the same name.

Use descriptive overloaded method names to describe similar operations, not different behaviors.

      int max(int, int);        //find max of two ints
      float max(float, float);  //find max of two floats

   Not so Good:
      void draw(Image);        //draw an image
      void draw(Card);         //draw a card 

The following is an example of overloaded methods having the name print.

   void print(int), print(double), print(long), print(char), 
        print(int, int), print(double, double);

   char c;
   short s;
   int i;
   float f;

   print(c);            // invoke print(char)
   print(i);            // invoke print(int)
   print(s);            // invoke print(int); s promoted to int
   print(f);            // invoke print(double); f promoted to double
   print('A');          // invoke print(char); 'A' is a char
   print(200);          // invoke print(int); 200 is an int
   print(200L);         // invoke print(long); 200L is a long
   print(99.9);         // invoke print(double); 99.9 is a double
   print(i, i);         // invoke print(int,int)
   print(i, 'a');       // invoke print(int,int); 'a' promoted to int
   print(s, 'A');       // invoke print(int,int); s - 'A' promoted to int
   print(200L, i);      // invoke print(int,int)
   print(i, 3.14);      // invoke print(double,double)

When print() is called, the compiler must figure out which of the methods with the name print is to be invoked. This is done by comparing the types of the actual arguments with the types of the parameters of all methods called print. The method with the best match is called; if none exist, then a compile-time error.


The signature of a method consists of the name of a method and its ordered set of parameter data types.

Criteria used to determine a match (partial).

  1. exact match of method call arguments with an overloaded method signature
  2. trivial conversions (exact match after applying promotions to argument data types -- char ==> int, short ==> int, float ==> double)
  3. exact match after programmer defined conversions (e.g. typecast)

{TopOfPage} {Oracle.com::API Specification | Tutorial} {GDT::Java Resources} {Eclipse IDE} {Udacity} {udemy} {CodingGround (online IDE)

Lifetime and Visibility (Scope)

Lifetime is the period, during execution of a program, in which a variable or method exists.

Visibility is the portions of the program in which a variable or method can be referenced by name (also referred to as scope (scope units: class, method, block).

Local variables are declared and/or defined within a block (either a method or a compound statement).

Local variables do not retain their values across method calls. Local variables cannot be defined static.

Global variables are declared and/or defined outside of any method. These are either static class variables or they are instance variables.

{TopOfPage} {Oracle.com::API Specification | Tutorial} {GDT::Java Resources} {Eclipse IDE} {Udacity} {udemy} {CodingGround (online IDE)

Introduction to Classes

A class is a blueprint or template that describes an object.

Class Syntax
   classDeclaration {
Syntax of a class declaration:
   [ modifiers ] class ClassName [ extends SuperClassName ]
                                 [ implements InterfaceNames ]

   Some example modifiers are:  public, abstract, final 
   If no modifier is specified, then it defaults to "package."

The class body contains static data members, instance data members, static methods and instance methods.

By convention, class names begin with an uppercase letter.

About Constructor Methods

A constructor is a special method that is used to help guarantee that objects are initialized to a "valid" or "sane" state when they are created (instantiated).

{TopOfPage} {Oracle.com::API Specification | Tutorial} {GDT::Java Resources} {Eclipse IDE} {Udacity} {udemy} {CodingGround (online IDE)


Objects are instantiated (created) using the new operator.

   Date today = new Date();
   String greeting = new String("Hello, World");
   Integer age = new Integer(42);
   Vector guesses = new Vector();

In a nutshell, the new operator determines the sizeof of the object and allocates memory for the object from the heap. If the memory allocation works, then it wants to initialize the object. By default, the memory allocated for an object is set to all-bits-zero. Integral and floating points instance variables are set to 0, boolean variables are set to false, and object variables are set to null.

In many cases, programmers want to control the initialization of objects when they are instantiated. This is accomplished by implementing a constructor method or methods.

If a class contains a constructor, then the new operator automatically "calls" it after memory has been allocated.

The constructor is responsible for initializing the newly created object. They help ensure that objects are initialized to valid states prior to being used.

A constructor is a special method having the same name as its class. Programmer's never call constructors directly. Constructor methods do not return value; consequently, a return-type is not specified when a constructor is declared.

   public class Foo {
      private int i;

      public Foo(int value) {    //no return-type specified
         this.i = value;         //or we could write:  i = value;

      public static void main(String[] argv) {
         Foo f = new Foo(200);

A class can contain more than one constructor and the one that is invoked depends on the type and number of arguments passed. Multiple constructors are possible because Java supports method overloading.

   public class Foo {
      private int i;

      public Foo() {
         i = -1;

      public Foo(int value) {
         this.i = value;         //or  i = value

      public static void main(String[] argv) {
         Foo b = new Foo();
         Foo a = new Foo(200);

A constructor that does not receive any parameters is called the default constructor.

A "family" of constructors allow objects to be initialized using various "levels" of initialization.

When you have a "family" of constructors, then you should try to implement them using only one of them. This can be accomplished using the this keyword as if you were calling a method named this.

   public class Foo {
      private int i;

      public Foo() {
         this(-1);               //invoke the Foo(int) constructor

      public Foo(int value) {
         this.i = value;         //or  i = value

      public static void main(String[] argv) {
         Foo b = new Foo();
         Foo a = new Foo(200);

If a constructor encounters an un-recoverable error condition, then it typically "throws" an exception.

   public class AgeTracker {
      private int age;
      public AgeTracker(int age) {
         if (age < 0) 
            throw new IllegalArgumentException("Negative ages not valid.");
         this.age = age;

Constructors cannot be used to re-initialize objects because programmers cannot call constructors directly. If re-initialization is necessary, then the initialization steps should be made into a stand-alone method that client's can call. Note: constructors should call this stand-alone method also in order to avoid duplicate code.

Most constructors are defined to be public, but they can be private. Making a constructor private prohibits objects to be created from your class.

{TopOfPage} {Oracle.com::API Specification | Tutorial} {GDT::Java Resources} {Eclipse IDE} {Udacity} {udemy} {CodingGround (online IDE)

class Object

The Object class is the superclass of all classes in Java; it is the root of the Java class hierarchy.

Every Java application derives from (or extends) the Object class (i.e. every object is an Object).

The Object class is defined in the java.lang package.

   public class Foo extends java.lang.Object ...


   public class Foo extends Object ...

Every method defined in the Object class is available in all of its subclasses.

In many cases, the behavior that is inherited from the Object does not work for objects created from other classes; consequently, these classes override many of the methods defind in the Object class.

The following Object methods are almost always overridden:

   boolean equals(Object obj);

      // compares this object with  obj  for equality; by default, 
      // two objects are equal if they point to the same object;

   String toString();

      // returns the String representation of this object; 
      // overriding this method allows the following:

         Foo f = new Foo();
         String s = f.toString();

To successfully override a method, its signature must match identically to the signature of the method being overridden.


{TopOfPage} {Oracle.com::API Specification | Tutorial} {GDT::Java Resources} {Eclipse IDE} {Udacity} {udemy} {CodingGround (online IDE)

Wrapper Classes

The Java class library comes with a set of Number classes that provide an object wrapper for their values.

   Boolean f = new Boolean(true);

   Byte b = new Byte(11);
   Character c = new Character('a');
   Short s = new Short(210);
   Integer i = new Integer(50000);
   Long l = new Long(19990909);

   Float f = new Float(3.14);
   Double d = new Double(39293.29393);

Wrapper objects are immutable (i.e. they cannot be modified once they are created).

Wrapper objects cannot be used in arithmetic operations. If you must do arithmetic, then you must use access methods to obtain their numeric values.

   Integer i = new Integer(100);
   int j = 52 * i.intValue();

Typically, two wrapper objects are compared for equality using an overridden equals() -- not the equality operator.

   Integer i1 = new Integer(210);
   Integer i2 = new Integer("210");

   if (i1 == i2)
      System.out.println("this will never print");

   if (i1.equals(i2))
      System.out.println("the object values are both 210");

The Character class provides an object wrapper for char values (Unicode characters). This class does not derive from Number class.

Why are wrapper classes needed? To handle cases where methods are used that operate on objects rather than primitive data types.

The integral wrapper classes contain static methods that can be used to convert String objects containing numbers into numbers. Examples.

   int i = Integer.parseInt("123");
   long l = Long.parseLong("123");
   byte b = Byte.parseByte("99");

   String n = new String("123");
   int j = Integer.parseInt(n);

Each wrapper class (except Boolean) contains manifest constants (i.e. static final defined class variables) named MAX_VALUE and MIN_VALUE that contain the maximum and minimum values that can be stored in variables of the primitive data types.

   int maxIntValue = Integer.MAX_VALUE;
   byte minByteValue = Byte.MIN_VALUE;
   float maxFloatValue = Float.MAX_VALUE;

{TopOfPage} {Oracle.com::API Specification | Tutorial} {GDT::Java Resources} {Eclipse IDE} {Udacity} {udemy} {CodingGround (online IDE)

class Vector

class Vector is data structure found in the java.util package that is used to store a variable number of object variables (pointers).

A vector object is useful when you have a collection of objects, but don't how many objects need to be stored.

Some of the more commonly used methods:
   int size();
      //determines the number of elements in the vector

   boolean isEmpty();
      //returns true if the vector has no elements; false otherwise

   void addElement(Object obj);  
      //adds the  obj  to the end of the vector

   boolean contains(Object obj);
      //searches the vector for  obj

   Object elementAt(int index);
      //0-based index of the element to retrieve [0 <= index < size()]

   boolean removeElement(Object obj);
      //removes the 1st occurrence of  obj  from the vector
      //returns true of obj found; false otherwise
      //if obj removed, all remaining elements are shifted down

   void removeElementAt(int index);
      //0-based index of the element to remove
In many cases, when you get an element from a vector, you will typecast it to the appropriate type.
   Date d = new Date();
   Vector v = new Vector();

   v.insertElementAt(d, 0);
      //insert Date object as the 1st element

   Object o = v.firstElement();
      //get the 1st element from the vector

   d = (Date)o;
      //convert the object pointer returned from  firstElement
      //to its appropriate type...

Oracle.com::class Vector<E>


Via Oracle.com: "Vector is synchronized. If a thread-safe implementation is not needed, it is recommended to use ArrayList in place of Vector."

class ArrayList is "roughly equivalent to Vector, except that it is unsynchronized." [via Oracle.com]

Oracle.com::class ArrayList<E>



{TopOfPage} {Oracle.com::API Specification | Tutorial} {GDT::Java Resources} {Eclipse IDE} {Udacity} {udemy} {CodingGround (online IDE)

class StringBuffer

The String class is used to create immutable String objects. In some cases, when you are dynamically building a string, it is more efficient to use a StringBuffer object. The StringBuffer class implements a buffer for characters that allows updates and allows the buffer to grow and shrink as needed to accommodate the updates.

Oracle.com::API::class StringBuffer | class String

[ DemoStringBuffer.java]

{TopOfPage} {Oracle.com::API Specification | Tutorial} {GDT::Java Resources} {Eclipse IDE} {Udacity} {udemy} {CodingGround (online IDE)

Home Previous Next