Practice 02
Developer environments¶
The IDE (Integrated Development Environment) is designed to make programming easier. It can help us a lot, including knowing the syntax highlight known from earlier, support for debugging, or, for example, the ability to start compiling and running with the click of a button.
There are several development environments such as IntelliJ IDEA, Eclipse or Visual Studio Code. In this material, IntelliJ IDEA and Eclipse will be described in more detail, but other development environments have most of the features described, altough they may be accessed in other ways.
Using IntelliJ IDEA¶
When you start IDEA, the Welcome window appears. Here you can initially customize some settings (e.g. light/dark theme), and later you can see your previously opened projects.
If you've used the environment before, it will open the most recent project by default, which you can close with File→Close project.
When creating a project, you can choose from many templates, but you can also create a simple Java project. If you do not select anything, the latter will be created by default.
Next, we need to click Next a few times, and then we can specify the project name and the location where to save the project. It is important that our path also contains the project folder, for example C:\suli\prog1\elso-project
or /home/valaki/suli/prog1/elso-project
.
After creating the project, you will be able to see the project structure on the left. From here the src folder is the most important, this is where the Java files are placed.
To create a new file, right click on the src folder, then new -> Java class. Enter the name of the class here. In the window on the right you can type the source code.
If you want to run your program, you first have to find your executable class (which has a main function), then in the left menu right click on the class and select run. A Run menu item will then appear in the bottom bar, where you can see the result of the run. If you are requesting data from the user, you can also enter it here, these will be highlighted in green in the environment.
Once we have run our program, the system will have created a configuration for it, you can see this in the top right corner. If you click on it, you can edit it with the "Edit configuration" option. Here you can also enter arguments to the program (by typing in the program arguments field). You can run the latest configuration by clicking on the green arrow in the top right corner or by pressing Shift + F10.
The system checks our program code continuously as we write the program, and will tell us if there is an error. It usually reports 3 types of errors:
- Compilation error. If there is one, our program cannot be compiled. The environment will indicate this in red.
- Warning. If this is the case, our program can be compiled and run, but it is most likely to be malfunctioning, and fixing it should be considered. There is also a "weak warning" which usually warns of code quality problems. This could be that a block of code is repeated in several places instead of being outsourced to a function; or that in an if structure, the if and else branches do the same thing.
- Typo: If you type something wrong, it will also warn you. It knows English.
It also indicates errors by coloring the code, and in the scrollbar on the right. It can also suggest corrections for these in most cases. To do this, move the cursor to the part of the code with the error and press Alt+Enter. Here you can select the fix you like (if any).
In the top right corner of the screen, you will also see a summary of how many of each error are in your current class.
Debugging¶
Consider a very simple little Java program in which we print out the command line arguments to the program. Or, let's mess it up a bit, so we can start debugging!
public class Cat {
public static void main(String[] args) {
int i=0;
while (i<=args.length) {
System.out.println(i + ". argument: " + args[i]);
i++;
}
}
}
Run the program with the following command line arguments: first, second, third
The output of running the program will be as follows:
Breakpoint¶
The easiest way to observe how your program is running is to put a breakpoint on each line and then step through the execution to try to find out what might be the error that is causing your program to not work properly.
To do this, simply place it at the beginning of the line by clicking on the blank space next to the line number, then run the debugger by clicking on the bug icon in the top menu bar (Shift + F9).
In the Debug tab at the bottom you can see the current state of the program running. On the left, we can see where the running is currently at: we are now in the main function of the Cat class, on line 4 of the file. If we are in a particular function called from another function, we can see the call location here. On the right side you can see the current values of our variables, or you can modify them (right click). Or you can even add extra expressions by clicking on the + icon. For example, if you type "2*i" here, it will continuously display twice the value of the variable i.
In the program, you can use the icons above to move around, which mean:
- Step over (F8): runs the current line, then jumps to the next line. If there is a function call in the current line, it will also run it and if there is a breakpoint, it will stop there.
- Step into (F7): if the current line is a function call, it jumps to its first line
- Force step info (Alt+Shift+F7): step into the current function even if you would otherwise skip it (usually a built-in function)
- Step out (Shift+F8): runs through the current function, jumps to the call location
- Drop frame: jumps out of the current function, to the calling location, but the function will be called again in the next step
- Step to cursor (Alt + F9): runs the program until it reaches the line where the cursor is currently positioned.
Conditional breakpoint¶
Breakpoints can have a condition, which will only stop execution at the breakpoint if the condition is true. Such a condition could be, for example, "a > 10". To add a condition to a breakpoint, right-click on it and enter the condition there.
Exception breakpoint¶
You may want to stop your program when an exception is thrown somewhere in it. To do this, right-click on the breakpoint and select "more". Here, click on the + icon and select "Java exception breakpoints", then select which exception you want to stop on. Then if you run your program and get this type of exception, your program will stop running at that point. We can check the values of the variables and easily figure out what caused the error.
Using Eclipse¶
When you start Eclipse, the workspace selection window opens, where you can choose the directory you want to work in. Within that workspace, we can easily and quickly access our projects and the files within them. Of course, it is still possible to open files outside of the workspace, or to select another workspace later.
After startup you get a partitioned interface. We will now look at the most important parts of these sections.
The Package Explorer displays the contents of the selected workspace.
This means that you can see a list of your projects and the files used in them.
Each project will have an src
folder, which is generated when the project is created.
This is where we write the actual program code. Here we can store files in packages.
In practice, packages are also simple directories, so don't be scared of them.
Next to the src
folder you will see a file called JRE System Library
.
This contains the running environment, so don't be scared of that either.
At the bottom is a bar with access to several functions. The most important of these for us at the moment is the console, which is the default output. So if you type text in the way you've seen before, you'll see it here.
The large area in the middle is the workspace, where we can do the actual programming. To do this, of course, we need to open a file to work in.
We usually create a new program in a new project. Two projects with the same name cannot be in the same workspace.
If we have a new project, we need to create files to work with for the actual programming.
In this case we can do this by creating classes inside the src
folder.
If no package is specified, then the class is placed in the default package, which can be considered to be simply in the src
folder.
Only one class with the same name can exist in a package.
If we check the box to generate the main function, we immediately generate an executable class. From this, you can create a Hello World program by adding just one line. Eclipse also provides additional help, using Ctrl + Space to automatically complete any commands or names you start.
You can run it by pressing the small green circle on the top toolbar or by pressing Ctrl + F11.
If we make a mistake, it is not possible to run the program. You can see this earlier, because Eclipse indicates misspelled names with a red underline, as well as other errors: e.g. missing semicolon. If you have a longer code, you can find the errors by clicking on the red marks on the side of the page to jump to the part of the code with the error.
If you make a non-syntactic error, but perform some other action that Eclipse considers most likely to be incorrect or meaningless, it will indicate this with a yellow underline. We can still run the program, but it is worth checking for such cases. These can be: redundant variables (that we create but don't use for anything), dead code snippets (code snippets that will obviously never run, see examples).
Example of dead code.
public static int add(int a, int b) {
return a+b;
System.out.println("This is a dead code fragment.");
}
In the picture, Eclipse is complaining because we didn't do anything with the declared variable, so it considers it redundant.
Debugging¶
Consider a very simple little JAVA program in which we print out the command line arguments to the program. Or, let's mess it up a bit, so we can start debugging!
public class HelloWorld {
public static void main(String[] args) {
int i=0;
while (i<=args.length) {
System.out.println(i + ". argument: " + args[i]);
i++;
}
}
}
Run the program with the following command line arguments: first, second, third
The output of running the program will be as follows:
Breakpoint¶
The easiest way to observe how your program is running is to put a breakpoint on each line and then step through the execution to try to find out what might be the error that is causing your program to not work properly.
To do this, simply click at the beginning of the line where you want to place the breakpoint.
Then run our debugger!
You can step through the program's instructions using either the keys or the corresponding icons: F5, F6, F7, F8
Key | Action |
---|---|
F5 | Run the current line, then jump to the next instruction |
F6 | Skip method, i.e. execute the method without the debugger |
F7 | End execution of current method and return to calling method |
F8 | Execute the program to the next breakpoint or watchpoint |
Step through the lines in your program, and observe the values of the variables used in the program as you go.
If you want, you can even change the value of some variables here, getting closer to the malfunction!
Conditional breakpoint¶
Of course, changing the variables in places is not always a safe solution, but for example, in the case of a loop that repeats many times, you can tear your hair out by constantly executing the loop kernel. In this case, it helps to change the breakpoint properties and only stop the debugger at a certain location under certain conditions. When setting the breakpoint, set it to stop at the last execution of your loop.
Rerunning the debugger will only stop when the loop condition matches our condition.
Exception breakpoint¶
Since we have seen in this example that the error is tied to throwing an exception, we can also add a breakpoint to our program that matches the exception type. This is the best solution in this case, because it gets us closest to the root of the problem.
From the list, select the exception that matches your error message:
Run our debugger again, but only with this newly specified breakpoint!
The debugger stops at the moment the error occurs. If we pay attention, the variables will clearly show us how we have overindexed our array and what we need to fix.
Javadoc comments¶
You can generate HTML-based documentation from comments written in the source code.
An example of the generated HTML page.
There are many different generators, the most well known is javadoc, which is included in the Java JDK and can be found here, but there are also many alternatives: Doc-O-Matic, Doc++, Doxygen
It's simple to use, instead of traditional commenting, you write comments between /**
and */
, and then you can use various special references in them, which will appear in the final documentation as special references.
Some examples are:
@author
- author@version
- version@param
- parameter@throws
- exceptions that may be thrown by the function/class@return
- return value@see
- reference to other classes
All the specific references and their descriptions are available at this link.
/**
* Hello World program.
* @author First Student
* @author Second Student
* @version 1.0
*/
public class HelloWorld {
/**
* The main function
*
* @param args command line parameters.
* @author First Student
* @version 1.0
*/
public static void main(String[] args) {
System.out.println("Hello World!");
}
/**
* Addition
*
* @param a is the first number.
* @param b is the second number.
* @author Second Student
* @version 0.8
* @return is the sum of the numbers.
*/
public static int add(int a, int b) {
return a+b;
}
}
From the generated source code, we can build the documentation using the built-in javadoc
command, in the current example javadoc HelloWorld.java
, and then open the index.html
file from the generated files.
Custom functions¶
In addition to the main function, you can also write your own functions to create more transparent code.
For now, as a simplification, we'll put public static
in front of each of our functions.
This is followed by the return type of the function (if it does not return, then void
), the name of the function, and then its parameters, with types.
public class StudentProgram {
public static void main(String[] args) {
String name = "Kiss Pista";
int score = 85;
kiirJegy(score);
boolean passed = passed(score);
if (passed) {
greeting(name);
}
}
public static void printGrade(int score) {
if (score >= 85) System.out.println("Execllent");
else if (score >= 70) System.out.println("Good");
else if (score >= 55) System.out.println("Satisfactory");
else if (score >= 40) System.out.println("Pass");
else System.out.println("Fail");
}
public static boolean passed(int score) {
return score >= 40;
}
public static void greeting(String nev) {
System.out.println("Congratulations " + name + ", you passed!");
}
}
Command line parameters¶
Pass command line parameters from the command line when running with java
:
java ProgramName param1 param2 param3...
Setting command line parameters in different development environments:
- IntelliJ IDEA: First you need to create a configuration, which is most easily done by right-clicking on the main function line and Run 'filename.main()'. Then somewhere in the top right corner you will see the green triangle (Play) icon next to the configuration (it's a drop down selector). Click on it to Edit configurations, and then in the Program arguments section you can enter the command line parameters.
- Eclipseben: In the drop-down menu Run Configurations next to the green triangle (Play) icon indicating compile and run, and then here under the Arguments tab, you can enter command line parameters.
To enter an array, the length
(length, i.e. number of elements) property of the array can be used.
(Of course, you can retrieve the element count of any array, not just the array of command-line parameters, using the length
property.)
The code snippet below prints the number of command line parameters (of course, it must be placed inside the main function):
If you want to print each element, you can use a well-known for
loop.
The indexing of the arrays is done in a similar way as before.
// traversing parameters
for (int i=0; i < args.length; i++) {
System.out.println("" + (i + 1) + ". parameter: " + args[i]);
}
Sum of parameters¶
As we have seen, the main function gets a String
array, this cannot be overwritten/modified in any way.
If you want to treat the parameters as numbers, you must first convert each element of the array to a number, using the Integer.parseInt()
function.
Integer.parseInt()
is a very easy to use function that converts a number stored in text form into an actual number.
Of course, this function cannot work miracles either, and should only be used if you know that the incoming text is really just a number.
In light of this, the sum of the command line parameters can be implemented quickly.
// sum of parameters
int sum = 0;
for (int i = 0; i < args.length; i++) {
sum += Integer.parseInt(args[i]);
}
System.out.println("Sum of parameters: " + sum);
Data types (repetition)¶
Simple (primitive) datatypes: boolean, char, byte, short, int, long, float, double, discussed earlier.. For more information about primitive data types, see this. An interesting article about floating point numbers, number notation.
Note
However, if for some reason you still need an unsigned integer, for Java 8 (or later) you can use some of the methods of the Integer
class created for this purpose, such as compareUnsigned, divideUnsigned.
The usefulness of the following will be understood later. Basically, for every primitive type in Java, there is a wrapper class that can be used to "wrap" a primitive type data into an object. The wrapper classes are (in the same order as the primitive types): Boolean, Character, Integer, Long, Float, Double A summary table of the built-in types:
Type name | Value | Default value | Size | Value range |
---|---|---|---|---|
boolean | represents 1 bit | false | no precise definition | true/false |
char | 16-bit unicode character | \u0000 | 2 byte | 0 - 65535 |
byte | 8-bit integer | 0 | 1 byte | -128 - 127 |
short | 16 bit integer | 0 | 2 byte | -32768 - 32767 |
int | 32 bit integer | 0 | 4 byte | -2147483648 - 2147483647 |
long | 64 bit integer | 0L | 8 byte | -9223372036854775808 -,9223372036854775807 |
float | 32-bit floating point (IEEE 754) | 0.0f | 4 byte | 1.40129846432481707e-45 - 3.40282346638528860e38 (positive or negative), +/- infinity, +/- 0, NaN |
double | 64-bit floating point (IEEE 754) | 0.0d | 8 byte | 4.94065645841246544e-324d - 1.79769313486231570e+308d (positive or negative), +/- infinite, +/- 0, NaN |
public class PrimitiveTypes {
public static void main(String[] args) {
boolean bo = true; // logical type
char c1 = 'a'; // character type
char c2 = '\u0054'; // stores two-byte unicode characters!
byte b1 = 127; // 8-bit whole type
byte b2 = -128; // all whole types are signed!
short s = 1024; // 16-bit integer
int i = 0x7fffffffffff; // 32-bit integer
long l = 0x7fffffffffffffffffffffffffffL; // 64-bit whole
float f = 123.123f; // 32-bit floating point type
double d = 5.0; // 64-bit floating point
// print to console
System.out.println(bo);
System.out.println(c1);
System.out.println(c2);
System.out.println(b1);
System.out.println(b2);
System.out.println(s);
System.out.println(i);
System.out.println(l);
System.out.println(f);
System.out.println(d);
}
}
However, floating point numbers should be handled with care. A perfect example of this is the Cookie program.
The story is this: we are visiting the United States, but unfortunately we are about to leave, so we only have time to buy cookies at the airport bakery.
However, the airport baker takes advantage of our haste: he lures us away from the competition with a $0.1 cookie, but charges us $0.1 more for each subsequent cookie than the previous one cost.
How many cookies can we eat with our remaining $1? Write a program for it, save it as Cookie.java
.
public class Cookie {
public static void main(String[] args) {
double ourmoney = 1.00;
int boughtCookies = 0;
for (double price = 0.1; ourmoney >= price; price += 0.1) {
ourmoney -= price;
++boughtCookies;
}
System.out.println("Cookies bought: " + boughtCookies);
System.out.println("Money left: " + ourmoney);
}
}
Hm... something is not right. Let's calculate it by hand, using a simple spreadsheet:
Money | Price of next cookie | Number of cookies eaten |
---|---|---|
1.0 $ | 0.1 $ | 0 |
0.9 $ | 0.2 $ | 1 |
0.7 $ | 0.3 $ | 2 |
0.4 $ | 0.4 $ | 3 |
0.0 $ | irrelevant | 4 |
The above example shows that it is never a good idea to choose a float
or double
type for storing currency, for example.
More on this problem at this and this.
Comparison for non-primitives¶
In the example below we will use the String type as an example, but this will also be true for all non-primitive types. The type String is always capitalized, since it is not really a primitive type, but in many cases we can use it as if it were.
To compare values, we technically use the ==
and !=
operators.
However, this is really only a reference comparison, not a comparison of the content of the texts.
Let's look at a simple example.
public class Main {
public static void main(String[] args) {
String favoriteFruit = args[0];
if (favoriteFruit == "pear") {
System.out.println("My favorite is pear.");
}
}
}
Even if we run our program with java Main pear
, the comparison will not be correct, as the development environment warns us (the equals sign is yellow).
For real comparisons, the text.equals()
method can be used, which actually checks the content of the text.
public class Main {
public static void main(String[] args) {
String favoriteFruit = args[0];
if (favoriteFruit.equals("pear")) {
System.out.println("My favorite is pear.");
}
}
}
Of course, "pear".equals(text)
also works, and in the long run it is preferable to use this kind of comparison.
Operators¶
+, -, *, /, %
are operators and their usage is similar to Python.
The only exception is division, which, unlike in Python, is not floating-point division but integer division (this corresponds to Python's //
˙operator).
The + sign for strings indicates concatenation. Its use is very intuitive. For example, "dog" + "go" -> "doggo"
>>
- arithmetic shift: preserves the sign bit.
>>>>
- logical shift: shifts the sign bit as well. (In other words, it does not preserve the negativity of the number)
You can read more about operators at Summary of Operators, Java Operators Tutorial.
Lazy evaluation¶
The logical &&
(and) and ||
(or) operators are evaluated by short circuit evaluation.
This means that a logical expression is evaluated exactly until its value is clearly decided.
In line 1, since the left side of &&
(and) is false, it is unnecessary to look at the right side, since the value of and is only true if both the left and right sides are true.
The ||
(or) in line 2 has a false value on the left side, so it is necessary to look at the right side, since an or is true if either the left or the right side is true.
On the other hand, for &&
in line 3, the right side of and must be looked at, since the value of an and is true only if both sides are true.
In line 4, on the other hand, only the left-hand side of ||
makes it clear that the value of the or expression will be true, so it is unnecessary to look at the right-hand side of the or operator.
These are of course equally true for multiple subexpressions. It may not seem important at first, but in real life we can often optimize our code by writing our logical expressions in the right order (for example, if we have an and expression, it is useful to put on the left side the things that evaluate faster, and on the right side the code that may take more time to evaluate; concrete example: on the left side of and it is more appropriate to check a user input, and on the right side of the expression you can put code that looks something up on the internet or reads data from a database). Not to mention that you can prevent runtime errors by writing your logical expression inside the if correctly.
However, care must be taken, as the &&
and &
operators are not the same!
The first is the logical and, the second is the bitwise and.
Of course, for boolean
values, the two operators work the same way, except for fast evaluation!
For binary operators, both sides of the binary operator are always evaluated!
public static boolean longerThan(String input, int length) {
return input != null && input.length() > length;
}
public static boolean longerThan2(String input, int length) {
return input != null & input.length() > length;
}
In the longerThan
function, a logical and is executed, i.e. if the input is null
, the left side of and is evaluated to false and the right side is not executed.
In contrast, the function longerThan2
performs a binary operation, i.e. it tests both sides, so if the input is null
, the left side of and evaluates to false, and then tries to evaluate the right side expression as well, but since the input
variable is null, we will get a runtime error.
If-else structure¶
In Java, we cannot use the 4 < x < 9
structure for comparison, the desired operation is achieved by using two separate if-conditions: 4 < x && x < 9
.
Besides the traditional if
structure, there is a shorter version, the ternary if-else structure.
Three-operand operator¶
The operator is a shortened form of the if-else structure.
Syntax: condition ? value_ha_true : value_ha_false
.
It is the only operator in Java that uses three operands.
It is often used for simple value statements, where you want to specify a value depending on a condition.
For example, int max = (a > b) ? a : b;
means the same as:
Other¶
Logical expressions can be concatenated using the &&
(and) and ||
(or) operators. Bracketing can be critical in such cases.
if ((x >= 3 && x <= 7) || x == 13 || (x < 0 && x != -9)) {
System.out.println("The condition is satisfied.");
}
In Java, each logical expression evaluates to a boolean
value (i.e., either true or false).
Of course, if we have only one boolean
value/variable, it is itself a true value, so it is unnecessary to print the == true
part.
boolean positive = true;
if (positive) {
System.out.println("The number is positive.");
}
if (!positive) {
System.out.println("The number is not positive.");
}
if (positive == true) {
// The == true part is unnecessary, it means the same as the code in line 2
}
The use of the ?:
operator may be justified, for example, within a statement.
It simplifies the previous code considerably, for example:
boolean positive = true;
System.out.println("The number is" + ((positive) ? "" : "not ") + "positive.");
However, unlike Python, there is no implicit conversion between int
and boolean
here; Java requires the use of comparison operators in this case:
int x = 1;
if (x) { //Java does not work like that!
System.out.println("All is well!");
}
//The correct condition is:
if (x > 0) {
System.out.println("All is well!");
}
Note on some reserved keywords
You can tag parts of the program, but you cannot use the goto
command.
It is a reserved keyword in Java, but it has no implementation.
The reason is that the goto
command existed and worked in the original, first JVM (according to James Gosling, who created the JVM).
However, it was taken out shortly afterwards because it was not really used in Java, and the language's creators found that most people also used this statement incorrectly in C.
So they took the functionality out of the goto
statement, but kept it as a reserved keyword, because there might have been code that included it, and they didn't rule out the possibility that it might be needed in the future, so goto
is still a reserved keyword, but it has no underlying functionality.
Similarly, const
in C is a reserved keyword, but it also has no function.
Memory areas¶
The following is a brief introduction to stacks and heaps, but these topics were covered in much more detail in the lecture. For more information on this topic, see relevant parts of the JVM specification.
Stack¶
Stacks are used to store primitive type data. This memory space is very fast accessed, usually storing local variables, short-lived data. The lifetime of the variables created in a stack is limited to the block that created it, i.e., for example, if we have a local variable in a function, that variable will live and be stored in memory until the function is finished running. After that, the occupied memory space is automatically freed. The disadvantage compared to heap memory is that the stack is much smaller. In Java, only primitive data types (number, byte, character or logical value) can be stored in the stack.
In practice, if the value of a variable is stored in a stack (e.g. int x = 42;
), then whenever the variable is referenced, the variable name is effectively the same as the value, stored by value.
Heap¶
Objects, arrays (called reference types) are stored in heap memory. Unlike a stack, this is a slower, dynamic memory area, but in return it has a much larger area available. The data created here is stored in the long term and exists globally, not just locally inside a function.
When we create an object on the heap, the variable is effectively just a reference, a pointer to the memory space inside the heap where the object is actually stored, the variable itself only stores this reference (the stack). *This may be familiar, the concept of pointer is similar. So a variable of type reference does not directly store a value, it only stores a reference, and the value stored on the heap is accessed indirectly, through that reference.
Practical example: A very illustrative example, from Bruce Eckel's Thinking in Java: Imagine that you have an object of type
Television
on the heap, which has a reference stored in it, and its name should beremotecontrol
. Just like in real life, we sit in our comfortable armchair and watch TV, but when we want to make any changes to ourTelevision
object (turn the volume up or down, change the channel, turn it on or off), we do so using theremotecontrol
object. And if we want to do anything on theTelevision
object while walking around the room or lying down, we take the remote control with us, not theTelevision
itself.
Arrays¶
We have already discussed specific arrays (e.g.: array of command line parameters). What was discussed then is of course also true for arrays in general. A short summary: arrays are of the same type but are designed to store more than one value. In the case of Python, we could use the list data structure, which is a dynamic data structure designed to store an arbitrary number of elements of any type and number of items. In the case of Java, however, the number of elements is fixed, i.e.it is a data type with a given number of elements that must be the same type. Indexing starts with 0, as you may be familiar from Basics of Programming.
Comment
Although we index from 0 in Java and Python, this is not necessarily the case in all languages. Some languages start indexing from one. You can find a summary list of these languages [here][language_index].
Java (because it is a managed language, i.e. the code does not run directly on the hardware, but on the JVM as discussed earlier) does full index bounds checking, except when under- or over-indexing occurs when accessing array elements.
Arrays in Java can be created in several ways.
Statically, when the size of the array is known at compile time, or dynamically, when the size of the array is not known in advance.
You can use arrays for objects created from both primitive types and classes.
Arrays can be created dynamically with the new
operator, and their size can be queried with the familiar length
property.
One-dimensional arrays¶
Arrays with a single extent (dimension) are also called vectors, as this term may be familiar from mathematics. An array can be declared as follows:
There is no difference between the two declarations, but in Java, the lower version is recommended (IDEs may yellow the upper one). You can define it at runtime with the new
operator mentioned above:
A concrete example:
The elements of this array are: x[0], x[1], x[2], x[3], x[4].
The easiest way to fill the array is to use a for
loop.
Java allows arrays to be filled with constant values during definition. In this case, the array is created by the compiler.
The size of the given array is always a non-negative integer, whose value cannot be greater than the maximum possible index. This depends on the JVM, and since indexing is done with int
values, the theoretical maximum element number is Integer.MAX_VALUE
(this is a defined constant), but in practice it is a number 5-8 values smaller than this. Let's find out what this number is for our JVM: to do this, let's create an array and try to compile and run the resulting file:
Multidimensional arrays¶
Analogously to one-dimensional arrays:
An example of creating a two-dimensional array:
A concrete example of creating and populating a two-dimensional array. The simplest way to fill an array is to use a for
loop.
int[][] array;
array = new int[10][9];
for (int i=0; i < array.length; i++){
for (int j = 0; j < array[i].length; j++) {
array[i][j] = (i+1)*(j+1)*9;
}
}
Java allows you to fill arrays with constant values at definition time. In this case, the array is created by the compiler
A slightly larger example is the example showing grading.
public class GradesExample {
public static void main(String[] args) {
// The grades of three students in four subjects (math, hungarian, english, history)
int[][] grades = {
{5, 4, 3, 5}, // grades of Peter Kis
{4, 5, 5, 4}, // grades of Emma Nagy
{3, 4, 5, 4} // grades of Balázs Szabó
};
String[] students = {"Peter Kis", "Emma Nagy", "Balázs Szabó"};
String[] subjects = {"Maths", "Hungarian", "English", "History"};
// Write out the grades per student
for (int i = 0; i < classlists.length; i++) {
System.out.println("\n" + students[i] + " grades:");
for (int j = 0; j < grades[i].length; j++) {
System.out.println(subjects[j] + ": " + grades[i][j]);
}
// Average calculation
double avarage = 0;
for (int grade : grades[i]) {
avarage += grade;
}
avarage /= grade[i].length;
System.out.printf("Average: %.2f%n", avarage);
}
}
}
School grades is an example of a two-dimensional array, where one dimension represents students and the other represents subjects.
Each grade can be accessed uniquely using the corresponding student and subject index, for example, grades[1][2]
represents the grade of the second student in the third subject, which in this case is Emma Nagy's English grade.
Copying arrays¶
Since arrays are of reference type, in the case of a simple value assignment, only the reference (i.e. the address of the array) is copied to another variable, not the actual array in memory.
Actual array copying can also be done manually using a simple for loop, but there is a pre-built method in the JDK for just this purpose: actual copying is possible with the System.arraycopy()
method. Its header is (javadoc):
public static void arraycopy(Object source, int sourceInitialElement, Object destination, int initialPosition, int length)
This is much faster than traditional manual copying, because it does not copy each element of the array element by element, the implementation uses Java Native Interface (JNI).
A concrete example of copying arrays:
int array1[] = { 30, 31, 1, 2, 3, 4, 5, 6, 7 };
int array2[] = { 29, 0, 0, 32, 33 };
System.arraycopy(array1, 0, array2, 1, 2);
for (int i=0; i<array2.length; i++){
System.out.print(array2[i] + " ");
}
System.out.println();
Its output is 29 30 31 32 33
.
You can also use the Arrays.copyOf
and Arrays.copyOfRange
functions, which may be a bit more convenient for copying arrays, but you need to import the Arrays
class into your code to use them.
The importing will be discussed later.
A complete example of array copying:
import java.util.Arrays;
public class ArrayCopying {
public static void main(String[] args) {
int[] array1 = {30, 31, 1, 2, 3, 4, 5, 6, 7};
int[] array2 = {29, 0, 0, 32, 33};
System.arraycopy(array1, 0, array2, 1, 2);
for (int i = 0; i < array2.length; i++) {
System.out.print(array2[i] + " ");
}
System.out.println();
// Copy an array using Arrays.copyOf (full array)
int[] array3 = Arrays.copyOf(array1, 3);
for (int elem : array3) {
System.out.print(elem + " ");
}
System.out.println();
// Copy an array using Arrays.copyOfRange (part of the original array)
int[] array4 = Arrays.copyOfRange(array1, 2, 6);
for (int elem : array4) {
System.out.print(elem + " ");
}
System.out.println();
}
}
Tasks¶
- Print the command line parameters to the console and the number of parameters.
- Print the command line parameters to the console in reverse order.
- Print an executable Java class that print the command line arguments in reverse order, and all backwards!
-
Write the command line arguments so that the first n command line arguments are written in line n: Wirst line only the first one, second line the first two separated by a space, third line the first three, and so on.
java Main one two dog kitty icecream
one one two one two dog one two dog kitty one two dog kitty icecream
-
Print the largest, smallest and average of the values of the command line parameters. We can assume that only numbers are given as command line parameters.
-
Raffle draw. Pick one of the names given on the command line at random. If no name is given, indicate correct usage. You can request a random number between 0-1 using the built-in function
Math.random()
. -
Based on the command line parameters, decide whether the input numbers are an arithmetic sequence, a geometric sequence, or neither. You can assume that they are all integers and have at least 3 parameters. The sum formulas are:
- arithmetic: an = a1 + (n - 1) * d
- geometric: an = a1 * q n - 1
-
Calculate the time between two times (hours minutes hours minutes) on the command line and print it out on the console (in hours minutes). Make sure that the data are correct when writing the program
- number of input parameters
- the hours must fall within the interval 0-23
- minutes must fall within the interval 0-59
-
Create an array of 7*10
int
's, fill them up according to the following scheme:array[x][y] = x*y;
(e.g. array[5][8] = 40;) -
Create an array of characters
't' 'e' 'l' 'e' 'p' 'h' 'o' 'n' 'e'
! Copy the characters'p' 'h' 'o' 'n' 'e'
into a new array! -
Simulate the seating plan of a cinema with a room 8x10. The program gets the seats to be reserved, then makes the reservation, prints the amount. If you want to book a seat already reserved, the program should not allow it.
-
Simple torpedo game, where the program hides 3 ships on a 5x5 field (either randomly or burned in) and the player gives coordinates. Make the program handle winning and losing.
-
comment the code with javadoc comments and generate the documentation! And look at the generated files!
Tasks (with automatic evaluation)¶
The texts and skeleton of the exercises are available here: HaziFeladatUres.java. The functions' trunks need to be created! Testing of the solution is automatic, by compiling and running the file you can do it on any computer, even without internet!
- Create a static function called
lazytime
. The function is given two parameters: whether it is a weekday and whether it is a holiday. You can be lazy in the morning if it is a weekend or if you are on vacation. - Create a static function called
lastDigitEquality
. The function expects two numbers. Return true if the last digits of the numbers are the same. - Create a static function named
lastDigitEqualityN
. The function expects three numbers (a, b, n). Return true if the last n digits of the first two numbers are equal (the last parameter 3). - Create a static function called
month
that expects one number. The function returns the given month in text (without accents) if the given number is between 1 and 12. In all other cases, it returns with the text `unknown'! - Create a static function called
primes
that waits for a number. The function returns the number of primes up to the given number (including the number given as a parameter).