Java Programming 7 | Abstract Data Types and Object-Oriented Programming

Series: Java Programming

Java Programming 7 | Abstract Data Types and Object-Oriented Programming

  1. Abstract Data Types

(1) The Definition of Data Types

A data type is a set of values and a set of operations on those values.

(2) The Definition of Primitive Data Types

The primitive data types in Java are those types whose values immediately map to machine representations. What’s more, the operations of these data types also immediately map to machine instructions. Basically, there are 8 primitive data types in Java as follows,

Resource Link

(3) The Definition of Abstract Data Type (ADT)

Although we have many in-built primitive data types in Java, we also want to write programs that process other types of data. For example, we may want colors, pictures, strings, vectors, matrices, etc. An abstract data type is a data type whose representation is hidden from the client.

(4) The Definition of Object-Oriented Programming (OOP)

OOP means basically two things. One is that you are able to create your own data types. The other is that you can use them in your programs by manipulating objects. So the main point is that the abstraction of an ADT is hidden from the client and we can use abstract data types without knowing implementation details.

(5) Example 1: String ADT

Guess what! We have already been using ADTs. In Java, we can find out that the string is not a primitive data type and it is actually a sequence of Unicode characters. Java’s String ADT allows us to write Java programs that manipulate strings when the exact representation of this ADT is hidden. The common APIs for the String ADT are,

  • create a string by some value,
String()
  • get the string length
String.length()
  • get the i-th character in the string
String.charAt(int i)
  • get the substring from the i-th character to the j-th character
String.substring(int i, int j)
  • check if a string contains a substring sub
String.contains(String sub)

(6) How to Use a Data Type?

First, we should know the name of this data type. In Java, the name of an ADT should be capitalized. Second, we should know how to construct new objects according to the API. And finally, we need to find out how to apply applications to this given object.

(7) How to Construct a New Data Type?

Basically, we have two steps to construct a new object,

  • use the data type name to specify the type of an object
  • use the keyword new to invoke a constructor

We have to use the keyword new because we have to allocate the memory on the heap before we assign the values of an instance.

However, because strings are originally an object, we can initialize a string without using the keyword new. For example, we can use

String a = new String("Hi");

Or,

String b = "Hi";

(8) How to Apply an Operation?

First, we have to use the object name to specify which object. Then, we need to use the dot operator to indicate that an operation is to be applied. Finally, a method name should be used to specify which operation we would like to call. For example,

 obj   method
+----+ +-----+
StdOut.println("Hi");
|
Dot Operator

2. Case Study: Color ADT

Before we talk about how to build our own ADT, let’s first see a case study about the Color ADT, which is an ADT allows us to write Java programs that manipulate color.

(1) APIs for Color ADT

Here are some APIs for the Color ADT,

  • construct a Color object
Color(int r, int g, int b);
  • get red intensity
int getRed()
  • get green intensity
int getGreen()
  • get blue intensity
int getBlue()
  • get a brighter version of this color
Color brighter()
  • get a darker version of this color
Color darker()
  • get a String representations of this color
String toString()
  • is this color the same as color c
boolean equals(Color c)

(2) Josef Albers’ Albers squares

Josef Albers was a German-born artist and headed Yale University’s department of design. He is considered one of the most influential teachers of the visual arts of the twentieth century. One of the most famous works of Josef Albers is the Albers squares, and they are made from 1933, when Nazis forced the school to close.

Now, let’s write a Java program to generate Albers squares. The position of the squares should be,

And the Java program should be as follows. Remember if we want to use the Color class, we have to import it by import java.awt.Color;

import java.awt.Color;
public class AlbersSquares
{
public static void main(String[] args)
{
int r1 = Integer.parseInt(args[0]);
int g1 = Integer.parseInt(args[1]);
int b1 = Integer.parseInt(args[2]);
Color c1 = new Color(r1, g1, b1);
int r2 = Integer.parseInt(args[3]);
int g2 = Integer.parseInt(args[4]);
int b2 = Integer.parseInt(args[5]);
Color c2 = new Color(r2, g2, b2);
StdDraw.setPenColor(c1);
StdDraw.filledSquare(.25, .5, .2);
StdDraw.setPenColor(c2);
StdDraw.filledSquare(.25, .45, .1);
StdDraw.setPenColor(c2);
StdDraw.filledSquare(.75, .5, .2);
StdDraw.setPenColor(c1);
StdDraw.filledSquare(.75, .45, .1);
}
}

To test this code, we can run,

$ java AlbersSquares 0 64 128 105 105 105
$ java AlbersSquares 251 112 34 177 153 71
$ java AlbersSquares 28 183 122 15 117 123

3. Object-Oriented Programming (OOP)

(1) Implementing a Data Type

To create a data type, you need to provide the code that,

  • defines the set of values or so-called instance variables
  • implements operations on those values (methods or static methods)
  • creates and initialize new objects (constructors)

(2) The Definition of Class

In Java, a data-type implementation is known as a class. A typical class structure is as follows,

For example, an example Charge class can be,

(3) The Definition of Java Constructors

A constructor in Java is a special method that is used to initialize objects. The constructor is called when an object of a class is created. The name of a constructor should be the same as the class name. Note that because the constructor returns no value, it is actually NOT a method.

public class CreateClass {
public CreateClass() {
StdOut.println("An object is created.");
}
public static void main(String[] args) {
CreateClass object = new CreateClass();
}
}

Similar to the principle that methods with different assignments will be treated as different methods, constructors with different assignments will also be reckoned as different constructors. For example,

public class CreateClass {
public CreateClass() {
StdOut.println("An object1 is created.");
}
public CreateClass(int a) {
StdOut.println("An object2 is created with integer " + a + ".");
}
public static void main(String[] args) {
CreateClass object1 = new CreateClass();
CreateClass object2 = new CreateClass(5);
}
}

And we can test this program by,

$ java CreateClass
An object1 is created.
An object2 is created with integer 5.

A common mistake for constructors is that someone may add a void before the constructor assignment because no value should be returned for a constructor. However, this is now correct because if we write void , the compiler will treat the current constructor as a normal static method. So, for a constructor, we have to use,

 public CreateClass()

Instead of,

public void CreateClass()

(4) The Definition of Attributes

Attributes are the variables owned by the classes. Let’s see the easiest case with an attribute ID of value 0. The program should be as follows,

public class MyAttributes {
int ID = 0;
public MyAttributes() {};
public static void main(String[] args) {
MyAttributes object = new MyAttributes();
StdOut.println(object.ID);
}
}

if we want to create a MyAttributes object and then assign its ID to 5, we can use the following way,

public class MyAttributes {
int ID = 0;
public MyAttributes() {};
public static void main(String[] args) {
MyAttributes object = new MyAttributes();
object.ID = 5;
StdOut.println(object.ID);
}
}

While in many cases, it is more convenient if we assign the value of the attributes when we create an object. For example, ID can be written to 5 if we write a new constructor with one parameter a ,

public class MyAttributes {
int ID = 0;
public MyAttributes() {};
public MyAttributes(int a) {
ID = a;
};
public static void main(String[] args) {
MyAttributes object = new MyAttributes(5);
StdOut.println(object.ID);
}
}

(5) final Modifier for Attributes

In addition, we may need some read-only attributes, and it may not be safe if everyone can override these attributes. For example, we would like to have a password attribute, and no matter what is ID number of an object is, it should have the same password 274133 . To solve this problem, the final modifier is used in front of the attribute’s data type for declaring this attribute is read-only. If we run the following code, we will generate error: cannot assign a value to a final variable ,

public class MyAttributes {
int ID = 0;
final int password = 274133;
public MyAttributes() {};
public MyAttributes(int a) {
ID = a;
};
public static void main(String[] args) {
MyAttributes object = new MyAttributes(5);
object.password = 123456;
}
}

If we delete the testing client, the present program can be compiled into a class. The program is,

public class MyAttributes {
int ID = 0;
final int password = 274133;
public MyAttributes() {};
public MyAttributes(int a) {
ID = a;
};
}

And we should compile it by,

$ javac MyAttributes.java

(6) Get The Password in a Dangerous Way

Now, let’s suppose we have a client program called GetPassword.java and it is used to retrieve the password from a MyAttributes object. The program should easily be,

public class GetPassword {
public static void main(String[] args) {
MyAttributes object = new MyAttributes();
System.out.println(object.password);
}
}

Well, we can find in this case that any client can easily get the password and MyAttributes class provides no protection of its data. This is quite dangerous and we don’t want anyone to get our password in such a dangerous way.

(7) Java Encapsulation: private Keyword

Encapsulation means that we should make sure that sensitive data is hidden from users. In order to protect the password, we can use the private modifier for the variable password by,

private final int password = 274133;

The private modifier means that the current variable can only be called by the methods in the same class. The modified member is only visible within the class, not from any other classes. To test this feature, then we need to recompile the program MyAttributes.java,

$ javac MyAttributes.java

If we use the same program GetPassword to get the password, an exception of Error: variable has private access in class.

(8) The Definition of Methods

We have already seen some APIs and now it’s time to create our own API for MyAttributes. Because someone can have an access to the password, we need a method GetPWD() in the class for getting the password.

public class MyAttributes {
int ID = 0;
private final int password = 274133;
public MyAttributes() {};
public MyAttributes(int a) {
ID = a;
};
public int getPWD() {
return password;
};
}

And when we have the following program, we can retrieve the password from the object because the API of MyAttributes allows this operation.

public class GetPassword {
public static void main(String[] args) {
MyAttributes object = new MyAttributes();
System.out.println(object.getPWD());
}
}

(9) Checking the Permission By ID

Whereas, we can make the program better if we check to see whether the current ID has permission to the password. Suppose ID 71035 has permission to the password, and we have a checking method isAdmin() for checking if the current ID has permission to the password. Then the program should be,

public class MyAttributes {
int ID = 0;
private final int password = 274133;
public MyAttributes() {};
public MyAttributes(int a) {
ID = a;
};
public boolean isAdmin() {
if (ID == 71035) return true;
return false;
}
public int getPWD() {
if (isAdmin()) return password;
return 0;
};
}

Then after we create an object by new , we can retrieve the password if we enter the ID = 71035,

public class GetPassword {
public static void main(String[] args) {
MyAttributes object = new MyAttributes(Integer.parseInt(args[0]));
System.out.println(object.isAdmin());
System.out.println(object.getPWD());
}
}

To test this code, we can use,

$ java GetPassword.java 71035
true
274133

(10) static Keyword

In the example above, the method isAdmin() can only be used when we already have an object created by,

MyAttributes object = new MyAttributes(Integer.parseInt(args[0]));

However, sometimes we would like to call a method without creating an object. The keyword static means the current method or value can also be called or used even if there is no instance of its class. For example, based on one ID, we want to check if it is an Admin account without creating an object of it. We have to construct a new method public static boolean isAdmin() . The program should be as follows,

public class MyAttributes {
int ID = 0;
private final int password = 274133;
public MyAttributes() {};
public MyAttributes(int a) {
ID = a;
};
public boolean isAdmin() {
if (ID == 71035) return true;
return false;
}
public static boolean isAdmin(int a) {
if (a == 71035) return true;
return false;
}
public int getPWD() {
if (isAdmin()) return password;
return 0;
};
}

In the client program, statement

System.out.println(MyAttributes.isAdmin(Integer.parseInt(args[0])));

And

MyAttributes object = new MyAttributes(Integer.parseInt(args[0]));
System.out.println(object.isAdmin());

basically mean the same thing. We can easily find out the first code segment checks the admin permission without any newly created objects.

(11) The Definition of Inheritance

Inheritance can be defined as the process where one class acquires the properties of another. The class that inherits from another class is called a subclass (or child class), and the class being inherited from is called a superclass (or parent class). In Java, we use keyword extends to declare a inheritance.

The main benefit of inheritance is that the inheritance improves code reusability. We can reuse attributes and methods of an existing class when we create a new class.

(12) Example: Animal Sound

Let’s now see a relatively simple program called Animal.java . The program only has one method called sound , and it is only used for output the String The animal make a sound. ,

public class Animal {
public void sound() {
StdOut.println("The animal makes a sound.");
}
public static void main(String[] args) {
Animal object = new Animal();
object.sound();
}
}

We can test this program by,

$ javac Animal.java
$ java Animal.java
The animal makes a sound.

Now we want another class called Cat , which, of course, is a kind of animal (means that it has some relationships to the Animal class). We have an attribute name equals Cat in this class. It should also contain the method sound and a new method called makeSound . When makeSound() is called, the program will first call the sound() method, and then it will print Cat: Meow . Even though we can write the sound method inside the Cat class again, we will choose to inherit the Animal class at this time. In order to do so, we have to use keyword extends . The program Cat.java should be,

public class Cat extends Animal {
final String name = "Cat";
public void makeSound() {
sound();
StdOut.println(name + ": Meow");
}
public static void main(String[] args) {
Cat kitty = new Cat();
kitty.makeSound();
}
}

The program can be tested by,

$ java Cat.java
The animal makes a sound.
Cat: Meow

(13) Java Polymorphism

Polymorphism means many forms in Java, which occurs when we have many classes that are related to each other by inheritance. Polymorphism uses the those methods to perform different tasks and it is actually to override the pre-existing methods in the subclass.

Let’s now see an example. The Admin.java has the ID and a private password that can not be overwrite. It also has a method getPWD that can be used to get the password.

public class Admin {
private int ID = 71035;
private final static int password = 274133;
public int getPWD() {
return password;
};
}

The Staff class inherit class Admin , but it can not inherit the private attributes ID and password in the Admin class. The default ID of an Staff object is 0, and we can assign this ID when we construct a new object of it. It also has a method called getPWD but it will check whether the current ID has the permission to the password. The present method getPWD will override the method with the same name in the superclass Admin , and this is called polymorphism. However, because we still need to call the method getPWD in the superclass to get the password, we have to use the super keyword. Therefore, the program should be,

public class Staff extends Admin {
int ID = 0;
public Staff(int a) {
ID = a;
};
public int getPWD() {
if (ID == 71035) {
StdOut.println("Permission Allowed");
return super.getPWD();
} else {
StdOut.println("Permission Denied");
return 0;
}
};
public static void main(String[] args) {
Staff object = new Staff(Integer.parseInt(args[0]));
StdOut.println(object.getPWD());
}
}

And we can test it by,

$ java Staff.java 71035
Permission Allowed
274133
$ java Staff.java 710
Permission Denied
0

4. Common Java Modifiers

(1) The Defintion of Modifiers

In the sections above, we have talked about many modifiers. For example,

  • public
  • static
  • private
  • final

Now let’s see some more detailed descriptions.

(2) The Defintion of Properties

A modifier can be used for properties means that it can be used for attributes and methods.

(3) The Defintion of Access Modifier

Access modifiers (or access specifiers) are keywords in Java that set the accessibility of classes, methods, constructors, and other members. Commonly, there are four access modifiers,

  • public: means the current class, attribute, method, or constructor is accessible for all classes.
  • private: means the current attribute, method, or constructor is only accessible within the declared class. What’s more, they can not be inherited in the subclass.
  • protected: means the current attribute, method, or constructor is only accessible within the declared class and its subclasses.
  • default (means no modifier): means the current class, attribute, method, or constructor is only accessible by classes in the same package.

(4) Non-Access Modifiers

There are also many non-access modifiers. For example,

  • final: For classes, it means the current class can not be inherited by other classes. For properties, it means the current attribute or method can not be overridden or modified.
  • static: means that the current properties can be called without an object of the current class.
  • abstract: For classes, it means that the current class can not be used to create object. To access an abstract class, it must be inherited from another class. This modifier can also be used to the methods in an abstract class, which means the method does not have a body.