Class and Objects with a Civil Engineering Application
In object-oriented programming (OOP), classes and objects are fundamental concepts used to model real-world entities. A class acts as a blueprint for creating objects, which are instances of the class. This approach can be particularly useful in civil engineering, where complex systems and structures can be modelled, manipulated, and analysed through software. By leveraging OOP principles, engineers can create more modular, scalable, and maintainable code for simulations, structural analysis, design, and more.
Learning Objectives
- Understand the concept of classes and objects within the context of civil engineering applications.
- Learn how to design and implement classes to model civil engineering concepts such as forces, structures, and materials.
- Gain proficiency in defining methods within classes to perform operations like vector addition, structural analysis, and material property calculations.
- Develop an understanding of how to instantiate objects from classes and use them in engineering simulations and analyses.
Outline
- Introduction to Classes and Objects in Engineering Context
- Definition and significance
- Real-world applications in civil engineering
- Designing Classes for Civil Engineering Applications
- Modelling forces, vectors, and structures
- Incorporating engineering properties and behaviours
- Implementing Methods for Engineering Calculations
- Vector operations for force analysis
- Calculations for structural integrity and material properties
- Instantiating Objects and Conducting Simulations
- Creating instances for simulation scenarios
- Analysing and interpreting simulation results
Introducing Interface
An interface in Java is a reference type, similar to a class, that can contain only constants, method signatures, default methods, static methods, and nested types. Interfaces cannot contain instance fields or constructor methods. The main purpose of an interface is to specify a set of methods that a particular class must implement. Interfaces are used to achieve abstraction and multiple inheritance in Java.
Characteristics of an Interface:
- Abstraction: An interface provides a way to specify what a class must do without specifying how it does it.
- Contract: By implementing an interface, a class signs a contract to provide implementations for the methods declared by the interface.
- Multiple Inheritance: Unlike classes, Java allows a class to implement multiple interfaces. This enables a form of multiple inheritances since a class can inherit the contract from multiple interfaces.
Implementing an Interface and Collection:
A class implements an interface using the implements
keyword. All methods declared in the interface must be implemented in the class, unless the class is abstract.
public interface Animal {
void eat();
void sleep();
}
public class Dog implements Animal {
public void eat() {
// Implementation
}
public void sleep() {
// Implementation
}
}
The Java Collections Framework (JCF)
The Java Collections Framework (JCF) is a set of classes and interfaces that implement commonly reusable collection data structures. Introduced in Java 2 (JDK 1.2), the framework has become a core component of the Java programming language, providing an efficient way to manage groups of objects.
Key Features of the Java Collections Framework:
- Rich Set of Interfaces and Classes: The JCF provides a wide range of interfaces (like
List
,Set
,Queue
, andMap
) and classes (such asArrayList
,LinkedList
,HashSet
,TreeSet
,PriorityQueue
,HashMap
, andTreeMap
). These are designed for various data management tasks, such as storing elements in lists, unique element sets, key-value pairs in maps, and more. - Unified Architecture: The framework offers a unified architecture for storing and manipulating collections, allowing collections to be manipulated independently of the details of their representation. This unified architecture also makes it easier to pass collections around in your code and integrate different parts of your application or system.
- Generics Support: With the introduction of generics in Java 5, the Collections Framework has been enhanced to support type-safe collections. This means that you can specify the type of objects that a collection can contain, providing compile-time type checking and reducing the risk of runtime errors.
- Performance: Many implementations of the Collections Framework are highly optimized for performance. For example,
ArrayList
provides constant-time positional access andHashMap
offers constant-time performance for basic operations, assuming the hash function disperses elements properly among the buckets. - Algorithms: The Collections Framework also includes a set of algorithms that are common to many collection manipulations, such as sorting, searching, shuffling, and reversing. These algorithms are polymorphic: they operate on collections and are designed to be as fast as possible.
Core Interfaces of the Java Collections Framework:
Collection
Interface: The root interface of the framework. It represents a group of objects known as its elements.List
Interface: An ordered collection that can contain duplicate elements. It allows positional access and insertion of elements.Set
Interface: A collection that cannot contain duplicate elements. It models the mathematical set abstraction.Queue
Interface: A collection used for holding elements prior to processing. It typically orders elements in FIFO (first-in-first-out) manner but can be ordered differently depending on the implementation.Map
Interface: An object that maps keys to values. A map cannot contain duplicate keys, and each key can map to at most one value.
The Java Collections Framework provides a comprehensive set of tools for handling data in Java applications. Its rich set of interfaces and classes simplifies the development process by offering reusable data structures and algorithms, making it an essential part of Java programming.
Vector (Collection):
When referring to the Vector
in the context of Java collections (not the mathematical vector concept), it is a collection class that implements the List
interface. Vector
is part of the Java Collections Framework and represents a dynamic array that can grow or shrink in size.
Characteristics of Vector:
- Synchronization: Unlike
ArrayList
, every method inVector
is synchronized, making it thread-safe. This means that multiple threads can access and modify aVector
without causing data inconsistency issues, at the cost of performance. - Legacy:
Vector
was part of the original version of Java (Java 1.0). Although it has been retrofitted to be part of the Collections Framework, it's generally considered legacy code. TheArrayList
class is usually preferred for new development because it is unsynchronized and therefore faster in environments where synchronization is not a concern. - Resizable Array: Like
ArrayList
,Vector
internally uses an array to store its elements, and the array grows as needed to accommodate new elements.
When to Use Vector:
Vector
is used in scenarios where thread-safe operations on a list are required without having to manually synchronise the code. However, due to its performance overhead from synchronization, it is often bypassed in favor of ArrayList
or the concurrent collection classes from the java.util.concurrent
package (like CopyOnWriteArrayList
) when thread safety is a concern.
In modern Java applications, direct use of Vector
is rare, and it is primarily seen in legacy systems or specific situations requiring built-in synchronisation.
A problem that implements these concepts
To implement the approach for calculating the dot product of scaling vectors as inspired by R.C. Hibbeler's methodology, focusing on precision and rounding during calculations, you would typically use Java. Here, I'll outline a structure that includes a class for reading vectors from a file, a class for vector operations, and the main application class App.java
. We'll ensure all internal calculations are rounded to four decimal places and presented with three decimal places.
File Structure and Content
Assume you have a file named vectors.txt
with the following content, representing two vectors:
3,4 5,12
This represents two vectors, (3,4)
and (5,12)
, stored in a CSV (comma-separated values) format.
Given the vectors in \(\hat{i}\) and \(\hat{j}\) notation:
\(\vec{A} = 3\hat{i} + 4\hat{j}\)
\(\vec{B} = 5\hat{i} + 12\hat{j}\)
The dot product of \(\vec{A}\) and \(\vec{B}\) is calculated as follows:
\(\vec{A} \cdot \vec{B} = (3\hat{i} + 4\hat{j}) \cdot (5\hat{i} + 12\hat{j}) = 3 \times 5 + 4 \times 12\)
VectorFileReader.java
This class is responsible for reading vectors from a file and storing them in a suitable data structure, such as an array or a list of Vector
objects.
import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class VectorFileReader {
public List<Vector> readVectorsFromFile(String filePath) {
List<Vector> vectors = new ArrayList<>();
try (Scanner scanner = new Scanner(new File(filePath))) {
while (scanner.hasNextLine()) {
String[] parts = scanner.nextLine().split(",");
double x = Double.parseDouble(parts[0]);
double y = Double.parseDouble(parts[1]);
vectors.add(new Vector(x, y));
}
} catch (FileNotFoundException e) {
System.err.println("File not found: " + filePath);
}
return vectors;
}
}
Vector.java
This class models a mathematical vector and includes methods for vector operations, including the dot product. Calculations are rounded as specified.
import java.math.BigDecimal;
import java.math.RoundingMode;
public class Vector {
private double x;
private double y;
public Vector(double x, double y) {
this.x = x;
this.y = y;
}
public double dotProduct(Vector other) {
double product = round(this.x * other.x + this.y * other.y, 4);
return product;
}
// Utility method for rounding
private double round(double value, int places) {
if (places < 0) throw new IllegalArgumentException();
BigDecimal bd = new BigDecimal(Double.toString(value));
bd = bd.setScale(places, RoundingMode.HALF_UP);
return bd.doubleValue();
}
// Presentation method to round to 3 places for display
public String toRoundedString() {
return "(" + round(this.x, 3) + ", " + round(this.y, 3) + ")";
}
}
App.java
The entry point of the application, App.java
, uses VectorFileReader
to read vectors from a file, performs the dot product operation, and prints the result with the required precision.
import java.util.List;
public class App {
public static void main(String[] args) {
VectorFileReader reader = new VectorFileReader();
List<Vector> vectors = reader.readVectorsFromFile("vectors.txt");
if (vectors.size() >= 2) {
Vector v1 = vectors.get(0);
Vector v2 = vectors.get(1);
double dotProduct = v1.dotProduct(v2);
System.out.println("Vector 1: " + v1.toRoundedString());
System.out.println("Vector 2: " + v2.toRoundedString());
System.out.printf("Dot Product: %.3f\n", dotProduct);
} else {
System.err.println("Insufficient vectors in file.");
}
}
}
This setup emphasises precision in mathematical calculations, adhering to the rounding requirements for both internal calculations and presentation. The VectorFileReader
class separates the concern of file reading from the vector operations, adhering to good software design principles.
Class Diagram
The class diagram shows the relationships and interactions between classes within the application, focusing on attributes and methods relevant to calculating the dot product.
classDiagram class Vector2D { -double x -double y +Vector2D(double x, double y) +dotProduct(Vector2D other) double +toString() String } class Vector2DFileReader { +readVectorsFromFile(String filePath) List } class App { +void main(String[] args) } Vector2DFileReader --> Vector2D : reads and creates App --> Vector2DFileReader : uses App --> Vector2D : calculates dot product
In this diagram:
Vector2D
represents a 2D vector with methods for calculating the dot product and converting the vector to a string.Vector2DFileReader
is responsible for reading vector data from a file and creatingVector2D
objects.App
is the main class that usesVector2DFileReader
to read vectors and then performs the dot product calculation on the vectors.
Flow Chart
The flow chart describes the sequential steps involved in the dot product calculation process, from start to finish.
flowchart TD A[Start] --> B{Read vectors from file} B --> C[Create Vector2D objects] C --> D{Are there at least two vectors?} D -->|Yes| E[Calculate dot product of first two vectors] D -->|No| F[Error: Not enough vectors] E --> G[Display result] F --> H[End] G --> H[End]
This flowchart outlines the application's process:
- Start the application.
- Read vectors from a file, where each line represents a vector in 2D space.
- Create
Vector2D
objects for each vector read from the file. - Check if there are at least two vectors available for the dot product calculation.
- If yes, calculate the dot product of the first two vectors.
- If not, display an error message indicating the lack of sufficient vectors.
- Display the result of the dot product calculation.
- End the application.
These diagrams effectively convey the structure and process flow of the application designed to calculate the dot product of 2D vectors, emphasising the separation of concerns and the steps involved in the calculation.
Following is a Java application that performs the cross product of vectors. The cross product is a binary operation on two vectors in three-dimensional space, resulting in a vector perpendicular to both of the original vectors. It's widely used in physics, engineering, and computer graphics for calculations involving torque, rotational force, and the orientation of 3D models.
The application will consist of several classes to handle different aspects of the operation, such as reading vectors from a file, performing the cross product calculation, and the main application class for execution. We'll ensure all calculations are rounded to four decimal places internally and presented with three decimal places.
In the context of explaining how a List<Vector>
data structure works, each Vector
represents a 2D vector with x
and y
components. Given the vectors in the vectors.txt
file they are (3,4)
and (5,12)
, the List data structure contains a List of objects of class Vectors, which has two properties x
and y
. The same can be visualised that a List is a container with individual cells which are in a sequence where data can be stored. Now, because we used List<Vector>
, each list will contain an x
and y
pair, which can be visualised as a box within each box of the list where a list element is stored.
Here's a more detailed visualisation using Mermaid, reflecting the specific vectors mentioned:
flowchart TB list[List] --> vector1[(Vector: 3,4)] list --> vector2[(Vector: 5,12)] subgraph vector1 x1[x: 3] y1[y: 4] end subgraph vector2 x2[x: 5] y2[y: 12] end class vector1,vector2 vector; %% Styling classDef list fill:#f9f,stroke:#333,stroke-width:4px; classDef vector fill:#bbf,stroke:#333,stroke-width:2px;
This diagram now includes:
- A
List
namedlist
, representing the container that holds vectors read from the file. - Two vectors,
vector1
andvector2
, which correspond to the vectors(3,4)
and(5,12)
found in thevectors.txt
file. These vectors are represented as subgraphs to illustrate their internal structure:vector1
containsx: 3
andy: 4
vector2
containsx: 5
andy: 12
Frequently Asked Questions on the above Construct
In the Java code in the class file VectorFileReader.java
, <Vector>
does not refer to a built-in data type but rather to the Vector
class defined in the Vector.java
code snippet. The use of <Vector>
in List<Vector>
leverages Java's generics system to specify that the List
will contain elements of type Vector
, as defined by the Vector.java
class you outlined. Here's a breakdown to clarify common questions:
- Is
<Vector>
a Built-in Data Type?- No,
<Vector>
in the context ofList<Vector>
refers to the custom classVector
that you've defined in your code. Java has a built-inVector
class (java.util.Vector
), which is a part of the Collections framework and represents a growable array of objects. However, when you use<Vector>
inList<Vector>
, Java looks for a class namedVector
in the current package or among the imported classes. Since you've defined aVector
class that represents a mathematical vector and haven't importedjava.util.Vector
, Java uses your customVector
class.
- No,
- Why is
<>
Used AroundVector
?- The
<>
symbols are used to specify the type of elements theList
will contain, a feature known as generics. Generics were introduced in Java 5 to provide compile-time type checking and eliminate the risk ofClassCastException
that was common when using collections before generics. By specifying<Vector>
within<>
, you tell the compiler that theList
namedvectors
will only holdVector
objects, allowing for safer, more readable code. It helps ensure that you only add instances ofVector
to theList
and avoid errors related to handling incorrect types.
- The
- Purpose of Generics (
<>
)- Generics improve the type safety of your code, making it easier to read and maintain. They enable classes, interfaces, and methods to operate on types specified by the programmer, reducing runtime errors by catching incorrect types at compile time. For example, without generics, you would need to cast objects retrieved from a collection, which could lead to runtime errors if the object was not of the expected type.
<Vector>
specifies that the List
data structure will contain elements of your custom Vector
class. The <>
syntax is part of Java's generics system, providing compile-time type safety for collections and other generic types.
Problem Statement
Calculate the cross product of two three-dimensional vectors to determine the vector perpendicular to the plane containing the original vectors. This can be applied to calculate the torque of a force applied at a point or to find the normal vector of a plane in 3D space.
File Structure
Assume the vectors are stored in a file named vectors3D.txt
with the following format:
3,4,5
2,8,7
This file represents two vectors:
\(\vec{A} = 3\hat{i} + 4\hat{j} + 5\hat{k}\)
and
\(\vec{B} = 2\hat{i} + 8\hat{j} + 7\hat{k}\)
Vector3DFileReader.java
import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;
import java.util.ArrayList;
import java.util.List;
public class Vector3DFileReader {
public List<Vector3D> readVectorsFromFile(String filePath) {
List<Vector3D> vectors = new ArrayList<>();
try (Scanner scanner = new Scanner(new File(filePath))) {
while (scanner.hasNextLine()) {
String[] parts = scanner.nextLine().split(",");
double x = Double.parseDouble(parts[0]);
double y = Double.parseDouble(parts[1]);
double z = Double.parseDouble(parts[2]);
vectors.add(new Vector3D(x, y, z));
}
} catch (FileNotFoundException e) {
System.err.println("File not found: " + filePath);
}
return vectors;
}
}
Vector3D.java
import java.math.BigDecimal;
import java.math.RoundingMode;
public class Vector3D {
private double x, y, z;
public Vector3D(double x, double y, double z) {
this.x = x;
this.y = y;
this.z = z;
}
public Vector3D crossProduct(Vector3D other) {
double newX = round(this.y * other.z - this.z * other.y, 4);
double newY = round(this.z * other.x - this.x * other.z, 4);
double newZ = round(this.x * other.y - this.y * other.x, 4);
return new Vector3D(newX, newY, newZ);
}
private double round(double value, int places) {
if (places < 0) throw new IllegalArgumentException();
BigDecimal bd = new BigDecimal(Double.toString(value));
bd = bd.setScale(places, RoundingMode.HALF_UP);
return bd.doubleValue();
}
@Override
public String toString() {
return String.format("(%.3f, %.3f, %.3f)", round(x, 3), round(y, 3), round(z, 3));
}
}
App.java
import java.util.List;
public class App {
public static void main(String[] args) {
Vector3DFileReader reader = new Vector3DFileReader();
List<Vector3D> vectors = reader.readVectorsFromFile("vectors3D.txt");
if (vectors.size() >= 2) {
Vector3D v1 = vectors.get(0);
Vector3D v2 = vectors.get(1);
Vector3D crossProduct = v1.crossProduct(v2);
System.out.println("Vector 1: " + v1);
System.out.println("Vector 2: " + v2);
System.out.println("Cross Product: " + crossProduct);
} else {
System.err.println("Insufficient vectors in file.");
}
}
}
This application structure provides a clear separation of concerns, with different classes handling file reading, vector operations, and application logic. The rounding rules ensure precision in calculations and presentation, adhering to the high standards of engineering applications.
The following diagrams illustrate the design and workflow of the Java application for calculating the cross product of vectors, including class structure and process flow.
Class Diagram
The class diagram represents the relationship between the classes in the application, highlighting the attributes and methods of each class.
classDiagram class Vector3D { -double x -double y -double z +Vector3D(double x, double y, double z) +crossProduct(Vector3D other) Vector3D +toString() String } class Vector3DFileReader { +readVectorsFromFile(String filePath) List } class App { +void main(String[] args) } Vector3DFileReader --> Vector3D : reads and stores App --> Vector3DFileReader : uses App --> Vector3D : operates on
Flow Chart
The flow chart illustrates the process flow of the application, from reading vector data from a file to calculating the cross product.
flowchart TD A[Start] --> B{Read vectors from file} B --> C[Create Vector3D objects] C --> D{Are there at least two vectors?} D -->|Yes| E[Calculate cross product of first two vectors] D -->|No| F[Error: Not enough vectors] E --> G[Display result] F --> H[End] G --> H[End]
These diagrams convey the structured approach to solving the problem of calculating the cross product of vectors, using a well-defined class structure and a clear process flow.
A little more about the @Override
The @Override
annotation in Java is a marker annotation that is used to indicate that a method is intended to override a method declared in a superclass or implement an abstract method defined in an interface. Its primary purpose is to increase the readability and maintainability of the code by making the developer's intention clear. Additionally, it provides a compile-time safety check, ensuring that the method does indeed override or implement a method from a superclass or interface.
The @Override
and When to Use @Override
:
In this example, the @Override
annotation before the speak
method in the Dog
class signals that speak
overrides the method from its superclass, Animal
. If the speak
method in Animal
were to be renamed, removed, or have its parameters changed, the compiler would generate an error for the @Override
annotated speak
method in Dog
, alerting the developer to the issue.
- Overriding Superclass Methods: Always use
@Override
when you override a concrete method of a superclass to ensure you have correctly overridden the method. - Implementing Interface Methods: Use
@Override
when implementing methods from an interface. Although not strictly required (since interfaces define abstract methods), it's a good practice for clarity and consistency. - Abstract Methods in Abstract Classes: When a subclass implements an abstract method from an abstract class, using
@Override
is also considered good practice for the reasons mentioned above.
The @Override
annotation is a powerful feature in Java that aids in ensuring code correctness, readability, and maintainability. It serves as an explicit declaration of the programmer's intent to override or implement a method, providing compile-time verification of this intention. Adopting its use in appropriate contexts is considered a best practice in Java programming.
Key Points about @Override
:
- Compile-time Check: When a method is annotated with
@Override
, the compiler checks if there is a method in the superclass or interface that matches the annotated method. If no such method exists, the compiler issues an error. This check helps catch common errors, such as misspelling the method name or mismatching the parameters. - Enhances Readability: The
@Override
annotation makes it clear to anyone reading the code that the method is overriding a method from its superclass or implementing an abstract method from an interface. This clarity is particularly beneficial in complex class hierarchies. - Avoids Runtime Errors: By catching mismatches at compile time, the
@Override
annotation helps prevent certain types of runtime errors that could occur if an intended override method does not correctly override a superclass method.
Usage Example:
Consider a simple class hierarchy where a class Animal
has a method speak()
, and a subclass Dog
overrides this method.
class Animal {
void speak() {
System.out.println("This animal speaks");
}
}
class Dog extends Animal {
@Override
void speak() {
System.out.println("Bark");
}
}
Some Important Aspects of the Code
In the Vector3DFileReader.java
class, an ArrayList
is used for a few important reasons:
- Dynamic Array:
ArrayList
is a resizable array implementation of theList
interface in Java. Unlike regular arrays in Java, which have a fixed size,ArrayList
s can dynamically adjust their capacity when elements are added or removed. This makesArrayList
a convenient choice for situations where the number of elements is not known in advance, such as when reading data from a file. - Ease of Use:
ArrayList
provides methods for common operations such as adding, removing, and searching for elements, which simplifies code and improves readability. For instance, inVector3DFileReader.java
, elements (vectors in this case) can be easily added to theArrayList
without worrying about the underlying array's capacity. - API Compatibility:
ArrayList
is part of the Java Collections Framework, which means it integrates well with other collection types in Java. It's often a preferred choice when working with APIs that expect instances ofList
or its superinterfaces, such asCollection
orIterable
.
As mentioned earlier, the <>
syntax is related to Java Generics. Java Generics allows to specify the type of elements stored in a collection, providing stronger type checks at compile time and eliminating the need for casting when retrieving elements. For example, in Vector3DFileReader.java
, the use of List<Vector3D>
indicates that the ArrayList
will store elements of type Vector3D
. This ensures type safety, as only instances of Vector3D
(or its subclasses) can be added to the ArrayList
, and when retrieving an element, it is automatically typed as Vector3D
without needing to cast it.
Created : 26 Feb 24, 9:23 AM
Modified : 14 Mar 24, 7:42 PM