You are on page 1of 75

IBU CEN 221 | Object-Oriented

International Burch University


Programming with Java

Adnan Miljković
Lectures Schedule #1
Week 1 - User input, Printing on screen & Conditional statements

Week 2 - Loops & Basics of methods

Week 3 - Methods, ArrayList data structure, Streams and connecting to the database - Quiz 1

Week 4 - Basics of objects, Encapsulation and Inheritance

Week 5 - More on objects, Abstraction and Polymorphism - Quiz 2

Week 6 - Tables, Sorting & Searching, HashMap data structure, Generics

Week 7 - Lambda functions, Records, Optionals , final keyword, & Preparation for the Midterm Exam -
Quiz 3
Lectures Schedule #2
Week 8 & 9 - MIDTERM

Week 10 - Regular expressions, File Manipulation, Iterators, Reflection & Custom Annotations

Week 11 - Exceptions and Unit Testing

Week 12 - Design Patterns (Quiz 4)

Week 13 - Network Programming & Threads

Week 15 - Graphical user interface (GUI) - (Quiz 5)


Regular Expressions

A regular expression is a compact form to define a string.

Regular expressions are often used to check the validity of strings.


Regular Expressions

Exercise

- student numbers start with the string "01" which is followed by seven numerical
digits from 0 to 9
Regular Expressions

Example

System.out.print("Give student number: ");


String num = reader.nextLine();

if (num.matches("01[0-9]{7}")) {
System.out.println("The form is valid.");
} else {
System.out.println("The form is not valid.");
}
Vertical Bar: Logical or

The vertical bar means that the parts of the regular expression are optional.

For instance, the expression 00|111|0000 defines the strings 00, 111 and 0000.

The method matches returns true if the string matches one of the alternatives defined.
Vertical Bar: Logical or

Example

String string = "00";

if(string.matches("00|111|0000")) {
System.out.println("The string was matched by some of the alternatives");
} else {
System.out.println("The string not was matched by any of the alternatives");
}
Vertical Bar: Logical or

The regular expression 00|111|0000 requires the exactly same form of the string:
its functionality is not like "contains".

String string = "1111";

if(string.matches("00|111|0000")) {
System.out.println("The string was matched by some of the alternatives");
} else {
System.out.println("The string not was matched by any of the alternatives");
}
Round Brackets: a Delimited Part of the String

With the help of round brackets it is possible to define what part of the regular
expression is affected by the symbols.

If we want to allow for the alternatives 00000 and 00001, we can define it with the
help of a vertical bar: 00000|00001.

Thanks to round brackets we can delimit the choice to only a part of the string.

The expression 0000(0|1) defines the strings 00000 and 00001.


Round Brackets: a Delimited Part of the String

Example
System.out.print("Write a form of the verb to look: ");
String word = reader.nextLine();

if (word.matches("look(|s|ed|ing|er)")) {
System.out.println("Well done!");
} else {
System.out.println("Check again the form.");
}
Repetitions

The symbol * stands for a repetition from 0 to n times, for instance


String string = "trolololololo";

if(string.matches("trolo(lo)*")) {
System.out.println("The form is right.");
} else {
System.out.println("The form is wrong.");
}
Repetitions

The symbol + stands for a repetition from 1 to n times, for instance


String string = "trolololololo";

if(string.matches("tro(lo)+")) {
System.out.println("The form is right.");
} else {
System.out.println("The form is wrong.");
}
Repetitions

The symbol ? stands for a repetition of 0 or 1 time, for instance


String string = "You have accidentally the whole name";

if(characterString.matches("You have accidentally (deleted )?the whole name")) {


System.out.println("The form is right.");
} else {
System.out.println("The form is wrong.");
}
Repetitions

The symbol {a} stands for a repetition of a times, for instance


String string = "1010";

if(string.matches("(10){2}")) {
System.out.println("The form is right.");
} else {
System.out.println("The form is wrong.");
}
Repetitions

The symbol {a,b} stands for a repetition from a to b times, for instance
String string = "1";

if(string.matches("1{2,4}")) {
System.out.println("The form is right.");
} else {
System.out.println("The form is wrong.");
}
Repetitions

The symbol {a,} stands for a repetition from a to n times, for instance
String string = "11111";

if(string.matches("1{2,}")) {
System.out.println("The form is right.");
} else {
System.out.println("The form is wrong.");
}
Repetitions

You can also use various different repetition symbols within one regular
expression.

For instance, the regular expression 5{3}(1|0)*5{3} defines strings which start
and end with three fives.

In between there can be an indefinite number of 1 and 0.


Square Brackets: Character Groups

With square brackets we can quickly define groups of characters.

The characters are written inside the brackets, and we can define an interval with the
help of a hyphen (-).

For instance, [145] means the same as (1|4|5), whereas [2-36-9] means the
same as (2|3|6|7|8|9). Accordingly, [a-c]* defines a regular expression with a
string made only of characters a, b and c.
File manipulation

- In computing, a computer file is a resource for recording data on a computer


storage device, primarily identified by its filename.
- Just as words can be written on paper, so can data be written to a computer
file. Files can be shared with and transferred between computers and mobile
devices via removable media, networks, or the Internet.
- Being able to write data to the files and reading from them is important as
many computer programs involves some kind of file manipulation
File writing

- Let us start by writing to the file


- We will be using the BufferedWriter in order to accomplish this task
- The BufferedWriter maintains an internal buffer of 8192 characters.
- During the write operation, the characters are written to the internal buffer
instead of the disk. Once the buffer is filled or the writer is closed, the whole
characters in the buffer are written to the disk.
- Hence, the number of communication to the disk is reduced. This is why
writing characters is faster using BufferedWriter.
File writing

- In Java, BufferedWriter is a class that provides buffering for Writer instances. It


makes the performance of writing text to a file more efficient by buffering
characters and writing them in larger chunks, rather than one character at a time.
This can be especially beneficial when dealing with large amounts of data.
- Using BufferedWriter can significantly improve the performance of writing
operations, especially when dealing with large files, by minimizing the number
of physical disk writes. It is often used in combination with other classes from
the java.io package for file manipulation in Java.
File writing

- We will pass as a parameter to the BufferedWriter the FileWriter in which we


will specify the path of the file we are to create
- In Java, FileWriter is a class that is part of the java.io package and is used for
writing characters to a file. It's a subclass of the Writer class and is typically used
to write character data to a file in a character stream.
- Let us perform a simple, one line write, to our `output.txt` file
- NOTE: The reading and writing to the files can throw the IOException that has
to be managed (by surrounding with try-catch block or by adding the throws
declaration in the method signature)
File writing
public static void simpleWrite(String text) throws IOException {
BufferedWriter bufferedWriter = new BufferedWriter(
new FileWriter("output.txt")
);
bufferedWriter.write("Our first line \n");
bufferedWriter.close();
}

- One more important thing is that we HAVE TO CLOSE a file every time we
finish with processing it. If you leave it opened, nothing will be written to the file
(it will be created as empty file) and you have a memory leak.
- The `\n` is character used to specify that it should be a new row in the file
Simple file writing
public static void simpleWrite(String text) throws IOException {
BufferedWriter bufferedWriter = new BufferedWriter(
new FileWriter("output.txt")
);
bufferedWriter.write("Our first line \n");
bufferedWriter.close();
}

- One more important thing is that we HAVE TO CLOSE a file every time we
finish with processing it. If you leave it opened, nothing will be written to the file
(it will be created as empty file) and you have a memory leak.
Array file writing

- What if we want to write an array of values to the file


- The process is similar to the previous example, just we will iterate through all of
the array elements and invoke the write method
- Again, we have to close the file in order for process to be successful
Array file writing cont.
public static void arrayFileWrite(String[] data) throws IOException {
BufferedWriter bufferedWriter = new BufferedWriter(
new FileWriter("output.txt")
);
String[] names = {"Becir", "Adnan", "Amila"};

for (String name : names)


bufferedWriter.write("\n" + name);

bufferedWriter.close();
}
Reading values from the file

- What if we want to read from a file


- We have to use the BufferedReader class and pass as a parameter the FileReader
object with the path as a parameter
- Again, do not forget to close the file
Reading values from the file cont.
public static void simpleRead() throws IOException {
BufferedReader bufferedReader = new BufferedReader(
new FileReader("output.txt")
);
System.out.println(bufferedReader.readLine());
bufferedReader.close();
}
Reading all values from the file
- If we want to read all lines from the file we have to iterate through it

public static void readWholeFile() throws IOException {


BufferedReader bufferedReader = new BufferedReader(
new FileReader("output.txt")
);
String tempLine;
while ((tempLine = bufferedReader.readLine()) != null){
System.out.println(tempLine);
}

bufferedReader.close();
}
Reading all values from the file cont.
- We can also utilize streams to transform a file lines into ArrayList

public static void readWholeFileList() throws IOException{


BufferedReader bufferedReader = new BufferedReader(
new FileReader("output.txt")
);
List<String> lines = bufferedReader.lines().collect(Collectors.toList());

for (String line : lines)


System.out.println(line);

bufferedReader.close();
}
Files

- We have saw different methods for file manipulation and yet, there are so many
different ways and different utility libraries for file manipulation in Java
- Any of the approaches you might find is good as long as you are are able to
manipulate with files in the fast way
- Always pay attention to the process of closing files not to encounter the memory
leak
Java iterators

- In Java, an iterator is an interface provided by the java.util package that


facilitates the traversal of elements in a collection.
- The main purpose of an iterator is to provide a uniform way to access the
elements of different types of collections without exposing the internal details of
the collection.
- The Iterator interface is part of the Java Collections Framework and is used by
many collection classes such as ArrayList, LinkedList, HashSet, etc. You can
obtain an iterator from a collection by calling the iterator() method on the
collection as shown in the following example
Java iterators
class IteratorExample {
public static void main(String[] args) {
// Create an ArrayList of strings
ArrayList<String> fruits = new ArrayList<>();
fruits.add("Apple");
fruits.add("Banana");
fruits.add("Orange");

// Obtain an iterator for the ArrayList


Iterator<String> iterator = fruits.iterator();

// Iterate over the elements using the iterator


while (iterator.hasNext()) {
String fruit = iterator.next();
System.out.println(fruit);
}
}
}
Java iterators

- The Iterator interface includes three main methods:


- boolean hasNext(): Returns true if there are more elements in the collection,
false otherwise.
- E next(): Returns the next element in the iteration. This method should be
called only if hasNext() returns true. Otherwise, it may throw a
NoSuchElementException.
- void remove(): Removes the last element returned by next() from the
underlying collection. This method is optional and may not be supported by
all iterators. If the iterator does not support removal, it throws an
UnsupportedOperationException.
Java iterators

- It's important to note that starting from Java 5, the enhanced for-loop (for-each
loop) provides a more convenient syntax for iterating over collections, and it
internally uses iterators.
- The enhanced for-loop can be used with any class that implements the Iterable
interface, including collections that provide iterators.
- Creating custom iterators in Java is useful in situations where you have a custom
data structure or collection and you want to define your own way of iterating
over its elements. There are many reasons for implementing the custom iterators
like: Filtering or Transformation, Custom Data Structures, Specific Traversal
Logic, Encapsulation and Abstraction of data structures
Java iterators

- In order to implement the Iterator on your custom object one has to implement
the Iterator interface and provide implementation for the next() and hasNext()
methods
- Here's a simple example: Suppose you have a custom collection that holds only
positive integers, and you want to create an iterator that skips over even numbers
during iteration. You can achieve this with a custom iterator.
Java iterators
class OddNumbersIterator implements @Override
Iterator<Integer> { public Integer next() {
private int[] elements; if (!hasNext()) {
throw new NoSuchElementException();
private int currentIndex = 0;
}
return elements[currentIndex++];
public OddNumbersIterator(int[] elements) { }
this.elements = elements; }
}

@Override
public boolean hasNext() {
while (currentIndex < elements.length &&
elements[currentIndex] % 2 == 0) {
currentIndex++; // Skip even numbers
}
return currentIndex < elements.length;
}
Java iterators

- Now we can test our program to see if it works correctly

public static void main(String[] args) {


int[] numbers = {1,2,3,4,5,6,7,8,9};
OddNumbersIterator iterator = new OddNumbersIterator(numbers);
while (iterator.hasNext())
System.out.println(iterator.next());
}
- Many other examples of the custom iterators are available on the github page of
the course
Reflection

- Reflection API gives you ability to develop a program that can “look” at
itself and change itself while it is running
- Use it at your own risk!
- Highly powerful and allows you to break all of the rules of your program
- Change element fo your Java class while it is running
- myClassObject.getClass() -> is a window to the wide variety of reflection
functions
Reflection example

- Let us create a small class called Cat with two attributes (name and age)
public class Cat {
private final String name;
private int age;

public Cat(String name, int age) {


this.name = name;
this.age = age;
}
}
Reflection example

- Let us create a small class called Cat with two attributes (name and age)
public class Cat {
private final String name;
private int age;

public Cat(String name, int age) {


this.name = name;
this.age = age;
}
}
Reflection example

- We will create getters and setters for the attributes (note that final attribute
cannot have setter)
public String getName() {
return name;
}

public int getAge() {


return age;
}

public void setAge(int age) {


this.age = age;
}
Reflection example

- Let us create couple of more methods that will be needed for the
demonstration purposes
public static void thisIsPublicStaticMethod() {
System.out.println("I'm public and static");
}
public void meow() {
System.out.println("meow");
}
public void saySomething(String something) {
System.out.println("I said something ".concat(something));
}
private void heyThisIsPrivate() {
System.out.println("How did you call this?");
}
Reflection example
- Let us use some reflection now to get all fields inside of the Cat class

Cat myCat = new Cat("Cicko", 12);

Field[] catFields = myCat.getClass().getDeclaredFields();

for (Field f : catFields) {


System.out.println(f.getName());
}
Changing final and private variable
- By using the reflection can write code that can give us the structure of our Java class
- Even a private or a final attributes can be accessed and manipulated
- In our previous example, if you run following code it will give you the compile time error

myCat.name = "Cicko Micko";


Changing final and private variable
- With following code, we can change the value of the private final variable

for (Field field : catFields) {


if (field.getName().equals("name")) {
field.setAccessible(true);
// You have to handle IllegalAccessException the exception that could be
thrown
field.set(myCat, "Micko Cicko");
}
}
Using reflection on methods
Method[] catMethods = myCat.getClass().getDeclaredMethods();

for (Method method : catMethods) {


System.out.println(method.getName());
if(method.getName().equals("meow")){
method.invoke(myCat); // Exception to be handled
} else if (method.getName().equals("saySomething")) {
method.invoke(myCat, "AW AW AW"); // Params are passed in order they are in the method signature
} else if (method.getName().equals("heyThisIsPrivate")) {
method.setAccessible(true);
method.invoke(myCat); // This will not work if we do not set accessibility
} else if (method.getName().equals("thisIsPublicStaticMethod")) {
method.invoke(null); // Static methods does not need the object
}
}
Using reflection on methods
Method[] catMethods = myCat.getClass().getDeclaredMethods();

Method[] allMethods = myCat.getClass().getMethods();

- The method getDeclaredMethods includes all methods declared by the class itself, whereas
getMethods returns only public methods, but also those inherited from a base class (here
from java.lang.Object).
Why do we need it at all?

- Heavily used in Java, especially in different frameworks (Spring uses it to


see the classes you defined and to create objects from those classes)
- Dependencies & Loads/Injects them dynamically based on Annotation
Information.
- We would use it for testing purposes. Ex. There is a class with private
attributes, we may want to change that private attribute in order to test how
the program behaves.
- HOWEVER, BE CAREFUL WHEN USING IT. IT IS EASY TO BREAK
EVERYTHING
Annotations

- An annotation is a construct associated with Java source code elements such


as classes, methods, and variables.
- Annotations provide information to a program at compile time or at runtime
based on which the program can take further action.
- An annotation processor processes these annotations at compile time or
runtime to provide functionality such as code generation, error checking,
etc.
Annotations cont.

- An annotation is preceded by the @ symbol. Some common examples of


annotations are @Override and @SuppressWarnings.
- These are built-in annotations provided by Java through the java.lang
package. We can further extend the core functionality to provide our custom
annotations.
- An annotation by itself does not perform any action. It simply provides
information that can be used at compile time or runtime to perform further
processing.
Annotations example
public class Parent {
public String getName() {...}
}
public class Child extends Parent {
@Override
public String getname() {...}
}
Annotations cont.
- We use the @Override annotation to mark a method that exists in a parent class, but that we
want to override in a child class.
- The above program throws an error during compile time because the getname() method in
Child class is annotated with @Override even though it doesn’t override a method from
Parent class (because there is no getname() method in Parent class)
- By adding the @Override annotation in ChildClass, the compiler can enforce the rule that
the overriding method in the child class should have the same case-sensitive name as that in
the parent class, and so the program would throw an error at compile time, thereby catching
an error which could have gone undetected even at runtime.
Annotations cont.
- We use the @Override annotation to mark a method that exists in a parent class, but that we
want to override in a child class.
- The above program throws an error during compile time because the getname() method in
Child class is annotated with @Override even though it doesn’t override a method from
Parent class (because there is no getname() method in Parent class)
- By adding the @Override annotation in ChildClass, the compiler can enforce the rule that
the overriding method in the child class should have the same case-sensitive name as that in
the parent class, and so the program would throw an error at compile time, thereby catching
an error which could have gone undetected even at runtime.
Annotations cont.
- We use the @Override annotation to mark a method that exists in a parent class, but that we
want to override in a child class.
- The above program throws an error during compile time because the getname() method in
Child class is annotated with @Override even though it doesn’t override a method from
Parent class (because there is no getname() method in Parent class)
- By adding the @Override annotation in ChildClass, the compiler can enforce the rule that
the overriding method in the child class should have the same case-sensitive name as that in
the parent class, and so the program would throw an error at compile time, thereby catching
an error which could have gone undetected even at runtime.
Annotations cont.
- Annotations are supplemental information that you put into your Java code
- They do not directly affect the annotated code, but they can be processed by something else
such as compiler or at the runtime by the code you write by yourself (reflection)
- One more example of the build in annotations is the @SuppressWarnings annotation
- Let us say that we have created a variable that we never use in our code, the compiler will
give us a warning that the variable was never used
- If we want to remove that warning, we would put the @SuppressWarnings("unused")
annotation
- We see that annotations can also accept the parameters as they were a methods
Annotations cont.
- Annotations are supplemental information that you put into your Java code
- They do not directly affect the annotated code, but they can be processed by something else
such as compiler or at the runtime by the code you write by yourself (reflection)
- One more example of the build in annotations is the @SuppressWarnings annotation
- Let us say that we have created a variable that we never use in our code, the compiler will
give us a warning that the variable was never used
- If we want to remove that warning, we would put the @SuppressWarnings("unused")
annotation
- We see that annotations can also accept the parameters as they were a methods
Annotations cont.
- We can use the annotations on any level such as classes, methods, and variables
- We are always putting the annotations in the separate line, just above the resource that we
are annotating (Note: they can be in same line, be we always want to put them in new line
because of the code readability)
Creating Annotations
- Let us say that we want to create a new class-level annotation called @VeryImportant so
anybody who is processing this annotation knows that this is an important class
- Creating the annotation is similar to creating a class or an interface, the syntax is as follows
public @interface VeryImportant { }

- In order to specify how you plan to use the annotation we have to use the annotation as well
Creating Annotations
- First annotation is the @Target() allows us to specify which kind of Java element this
annotation is valid to be used on. If you omit this annotation, you annotation is valid on any
Java element. It can take following values:
- ANNOTATION_TYPE − An annotation type declaration
- CONSTRUCTOR − A constructor declaration
- FIELD − A field declaration
- LOCAL_VARIABLE − A local variable declaration
- METHOD − A method declaration
- PACKAGE − A package declaration
- PARAMETER − A parameter declaration
- TYPE − A class, interface, enum, or record declaration
- TYPE_PARAMETER − A type parameter declaration
- TYPE_USE − A use of a type
Creating Annotations
- Since our annotation will be used on the class level we will say that it is allowed to be used
on the class level by using the following syntax
@Target(ElementType.TYPE)

- If we try to use our annotation on some other Java element, it will give us the compile time
error.
- If we want the annotation to be used on multiple Java elements, let us say the class and
method level we would use the following syntax
@Target({ElementType.TYPE, ElementType.METHOD})
Creating Annotations
- Second annotation that we will specify for our annotation is called @Retention()
- It indicates how long the annotated type should be kept in the program's lifecycle and it can
take following values
- RUNTIME - it will be available to other code while program is running (Reflection)
- SOURCE - Java will get rid of this annotation before it compiles the code (Used for the annotations that
are needed before the code has been compiled ex. @SuppressWarnings)
- CLASS - Java will keep it during the compilation of the program, but once it stars running it will get rid of
it
- We want our annotation to be available during the runtime so we will use the following
syntax
@Retention(RetentionPolicy.RUNTIME)
Testing annotation
- Now we can test if our annotation is configured correctly by using a reflection
- We have created a new record called Lion that has only one property: name: String that is
annotated by using the @VeryImportant annotation
- In order to get the annotation and see is it present on our object we have used the following
code
Lion lion = new Lion("Lavcina");

System.out.println(lion.getClass().isAnnotationPresent(VeryImportant.class));

- The code returned us a true meaning that the annotation is present above the class Lion
Method level annotations
- Next let us create an annotation called @RunImmediately that will run the method
immediately if the annotation is present above that method
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RunImmediately {}
Method level annotations cont.
- Now we will create two methods in the Lion class, one that will have the annotation
@RunImmediately and the other that will not have that annotation
record Lion(String name) {
@RunImmediately
public void saySomething() {
System.out.println("Lion method saySomething");
}
public void saySomethingButDontRun() {
System.out.println("Lion method saySomethingButDontRun");
}
}
Method level annotations cont.
- Now, let us create a logic that will automatically run the method with the previously created
annotation
for (Method method : lion.getClass().getDeclaredMethods()) {
if(method.isAnnotationPresent(RunImmediately.class)){
System.out.println("Method name is " + method.getName());
method.invoke(lion);
}
}
- If we run this code, we will see that the method called saySomething will be runned by
the program written
Annotations with parameters
- What if we want to create and annotation that will run the method automatically n number
of times where the n is a parameter that we pass to our annotation
- Let us create the annotation called @RunImmediatelyNTimes that will as a parameter
accept the integer that will specified how many times this method will be invoked
automatically

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RunImmediatelyNTimes {
int times();
}
Annotations with parameters cont.
- In our annotation we have declared the method (only methods are allowed) called times
- We can also give a default value to our methods (if we don’t specify the default value you
will be enforced to pass the parameter to the annotation).
- Important thing to note is that the parameters to our interface can only be one of the
following:
- Primitive data type
- String
- Class type
- Array of any of the above mentioned types
Annotations with parameters cont.
- We can also give a default value to our methods (if we don’t specify the default value you
will be enforced to pass the parameter to the annotation).
- Let us say that the default value for our @RunImmediatelyNTimes is 1 we will add
following code:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RunImmediatelyNTimes {
int times() default 1;
}
Annotations with parameters cont.
- We want to develop a program that will check if the annotation is present above the method
and if it is we want to run it specified number of times

for (Method method : lion.getClass().getDeclaredMethods()) {


if (method.isAnnotationPresent(RunImmediatelyNTimes.class)) {
RunImmediatelyNTimes annotation =
method.getAnnotation(RunImmediatelyNTimes.class);
for (int i = 0; i < annotation.times(); i++){
method.invoke(lion);
}
}
}
Field level annotations
- Now, we want to create a field level annotation but only on the String type fields in our
class
- We will create a new annotation called @ImportantString
- Before that we will create a POJO class called Fox with two attributes: name: String, legs:
int, and our name attribute will be annotated with @ImportantString annotation
class Fox{
@ImportantString
private String name;
private int numOfLegs;

public Fox(String name, int numOfLegs) {


this.name = name;
this.numOfLegs = numOfLegs;
}
}
Field level annotations cont.
- Let us now create the annotation as well

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ImportantString {}

- So let us say that in our program we want to check whether the field in class Fox is
annotated with @ImportantString annotation and if it is we want to print to the console the
value of that attribute
- Note that we have to change the accessibility of the attribute before accessing it as it has
been declared as a private attribute
Testing field level annotations
Fox fox = new Fox("Lija", 4);
for (Field field : fox.getClass().getDeclaredFields()){
if(field.isAnnotationPresent(ImportantString.class)){
field.setAccessible(true);
Object value = field.get(fox);
if(value instanceof String myFoxName) // String myFoxName = (String) value
System.out.println(
"The field name is " +
field.getName() +
" and its value in uppercase is " + myFoxName.toUpperCase());
}
}
System.out.println("Thanks, bye!");

You might also like