Personal experience: transition from low-level C development to Java programming



The article reflects the personal experience of the author - an avid microcontroller programmer who, after many years of experience in microcontroller development in C (and a little in C ++), had the opportunity to participate in a large Java project developing software for set-top boxes running Android. In the course of this project, it was possible to collect notes on interesting differences between Java and C / C ++ languages, to evaluate different approaches to writing programs. The article does not claim to be a reference book, it does not consider the effectiveness and performance of Java programs. It is rather a collection of personal observations. Unless otherwise indicated, this is a Java SE 7 version.

Syntax Differences and Controls


In short, the differences are minimal, the syntax is very similar. Code blocks are also formed by a pair of curly braces {}. The rules for creating identifiers are the same as for C / C ++. The list of keywords is almost the same as in C / C ++. The built-in data types are similar to those in C / C ++. Arrays are all also declared using square brackets.

The if-else, while, do-while, for, and switch constructs are also almost identical. It is noteworthy that in Java there were tags familiar to C programmers (those used with the goto keyword and the use of which are strongly discouraged). However, the possibility of switching to a label using goto was excluded from Java. Labels should only be used to exit nested loops:

outer: for (int i = 0; i < 5; i++) { inner: for (int j = 0; j < 5; j++) { if (i == 2) break inner; if (i == 3) continue outer; } } 

To improve the readability of programs in Java, an interesting opportunity has been added to separate the digits of long numbers with an underscore character:

 int value1 = 1_500_000; long value2 = 0xAA_BB_CC_DD; 

Externally, the Java program is not much different from the program on the familiar C. The main visual difference is that Java does not allow functions, variables, definitions of new types (structures), constants, etc., “freely” located in the source file. Java is an object-oriented language, so all program entities must belong to a class. Another significant difference is the lack of a preprocessor. These two differences are discussed in more detail below.

Object Approach in the C Language


When we write large programs in C, in fact, we have to work with objects. The role of an object here is performed by a structure that describes a certain essence of the “real world”:

 //   – «» struct Data { int field; char *str; /* ... */ }; 

Also in C there are methods for processing "objects" structures - functions. However, the functions are not merged with the data. Yes, they are usually placed in one file, but every time you need to pass a pointer to the object being processed to the “typical” function:

 int process(struct Data *ptr, int arg1, const char *arg2) { /* ... */ return result_code; } 

You can use the “object” only after allocating memory to store it:

 Data *data = malloc(sizeof(Data)); 

In a C program, a function is usually defined that is responsible for the initial initialization of the “object” before its first use:

 void init(struct Data *data) { data->field = 1541; data->str = NULL; } 

Then the life cycle of an “object” in C is usually:

 /*    "" */ struct Data *data = malloc(sizeof(Data)); /*  "" */ init(data); /*   "" */ process(data, 0, "string"); /*  ,  ""     . */ free(data); 

Now we list the possible run-time errors that can be made by the programmer in the life cycle of the “object”:

  1. Forget to allocate memory for the "object"
  2. Specify invalid amount of allocated memory.
  3. Forget to initialize the "object"
  4. Forget to free up memory after using the object

To identify such errors can be extremely difficult, since they are not determined by the compiler and manifest themselves during the program operation. Moreover, their effect can be very diverse and affect other variables and "objects" of the program.

Java object approach


Facing OOP - object-oriented programming, you probably heard about one of the OOP whales - encapsulation. In Java, unlike C, data and methods for their processing are combined together and represent "true" objects. In terms of OOP, this is called encapsulation. A class is a description of an object, the closest analogue of a class in C is the definition of a new type using a typedef struct. In Java terms, those functions that belong to a class are called methods.

 //   class Entity { public int field; //   public String str; //   //  public int process(int arg1, String arg2) { /* ... */ return resultCode; } //  public Entity() { field = 1541; str = "value"; } } 

The ideology of the Java language is based on the statement “everything is an object”. Therefore, it is not surprising that Java prohibits the creation of both methods (functions) and data fields (variables) separately from the class. Even the familiar main () method, from which program execution begins, must belong to one of the classes.

A class description in Java is an analogue of a structure declaration in C. Describing a class does not create anything in memory. The object of this class appears at the moment of its creation by the operator new. Creating an object in Java is an analogue of memory allocation in the C language, but, unlike the latter, a special method is automatically called during creation — the object constructor. The constructor assumes the role of the object initialization - an analogue of the init () function, considered earlier. The name of the constructor must necessarily coincide with the name of the class. A constructor cannot return a value.

The life cycle of an object in a Java program is as follows:

 //   (   ,  ) Entity entity = new Entity(); //    entity.process(123, "argument"); 

Note that the number of possible errors in the Java program is much less than in the C program. Yes, you can still forget to create an object before first use (which, however, will result in an easily debugged NullPointerException), but for the rest of the inherent errors C programs, the situation is changing drastically:

  1. There is no sizeof () operator in Java. The Java compiler itself calculates the amount of memory to store the object. Therefore, it is not possible to specify an invalid size of the allocated area.
  2. The object is initialized at the time of creation. It is impossible to forget about initialization.
  3. The memory occupied by the object does not need to be freed; this work is performed by the garbage collector. It is impossible to forget to remove an object after use - there is less chance of a memory leak effect.

So, everything in Java is an object of a particular class. An exception are primitives that have been added to the language to improve performance and memory consumption. More information about primitives - below.

Memory and Garbage Collector


In Java, the familiar C / C ++ concepts of the heap and stack are stored to the programmer. When an object is created by the operator new, the memory for storing the object is borrowed from the heap. However, an object reference (the reference is an analogue of the pointer), if the created object is not part of another object, is placed on the stack. The heap stores the “bodies” of objects, and the stack contains local variables: references to objects and primitive types. If the heap exists during the execution of the program and is available for all program threads, then the stack refers to the method and exists only during its execution and is also unavailable to other program threads.

In Java, there is no need, and even more so — you cannot manually free the memory occupied by an object. This work is performed by the garbage collector in automatic mode. The runtime monitors whether it is possible to reach each object in the heap from the current program location by following the links from object to object. If not, then such an object is recognized as "garbage" and becomes a candidate for removal.

It is important to note that the deletion itself does not occur at the moment when the object “is no longer needed” - the decision on deletion is made by the garbage collector, and the deletion may be postponed as long as necessary, until the end of the program.

Of course, the work of the garbage collector requires processor overhead. But in return, it saves the programmer from the big headache associated with the need to free up memory after the end of the use of "objects". In fact, we “take” the memory when we need it and use it without thinking that it should be liberated after ourselves.

Speaking of local variables, remember the Java approach to initializing them. If in C / C ++ an uninitialized local variable contains a random value, then the Java compiler simply will not allow it to remain uninitialized:

 int i; //  . System.out.println("" + i); //  ! 

Links - Replacing Signs


There are no pointers in Java, respectively, a Java programmer does not have the opportunity to make one of the many errors that occur when working with pointers. When you create an object, you get a link to this object:

 //  entity –  . Entity entity = new Entity(); 

In the C language, the programmer had a choice: how to transfer, say, a structure to a function. It could be passed by value:

 //    . int func(Data data);    –   : //    . void process(Data *data); 

Passing by value guaranteed that the function did not change the data in the structure, but was inefficient in terms of speed — a copy of the structure was created at the time the function was called. Passing through the pointer is much more efficient: in fact, the address in the memory where the structure is located was passed to the function.

In Java, left only one way to transfer the object to the method - by reference. Passing by reference in Java - an analogue of passing through a pointer to C:

However, unlike the C language pointer, a Java reference cannot be incremented / decremented. "Run" on the elements of the array with a link to it in Java will not work. All that can be done with a link is to assign it a different value.

Certainly, the absence of pointers as such reduces the number of possible errors, however, an analogue of the null pointer remains in the language - a null link, denoted by the keyword null.

Zero link is a Java programmer’s headache, since forces to use an object reference before using it to check for null, or to handle NullPointerException exceptions. If this is not done, the program will crash.

So, all objects in Java are passed through links. Primitive data types (int, long, char ...) are passed by value (in more detail about primitives - below).

Java reference features


Access to any object in the program is carried out through a link - it definitely has a positive effect on performance, but it may surprise the newcomer:

 //  ,   entity1   . Entity entity1 = new Entity(); entity1.field = 123; //   entity2,     entity1. //    !   ! Entity entity2 = entity1; //   entity1  entity2         . entity2.field = 777; //  entity1.field  777. System.out.println(entity1.field); 

The arguments of the methods and the return values ​​are all passed through the link. In addition to the advantages, there is a disadvantage compared to C / C ++ languages, where we can explicitly prohibit functions from changing the value passed through a pointer using a const qualifier:

 void func(const struct Data* data) { //  ! //    ,    ! data->field = 0; } 

That is, the C language allows you to track this error at compile time. Java also has the const keyword, but it is reserved for future versions and is not currently used at all. Its role is to some extent called the final keyword. However, it does not allow protecting the object passed to the method from changes:

 public class Main { void func(final Entity data) { //    . //    final,    . data.field = 0; } } 

The thing is, the final keyword in this case applies to the link, and not to the object that the link points to. If we apply final to the primitive, the compiler behaves as expected:

 void func(final int value) { //    . value = 0; } 

Java links are very similar to C ++.

Java primitives


Each Java object in addition to the data fields contains supporting information. If we want to operate, for example, with individual bytes and each byte is represented by an object, then in the case of an array of bytes, the memory overhead can many times exceed the usable volume.
In order for Java to remain sufficiently effective and in the cases described above, support for primitive types has been added to the language.
PrimitiveViewDigit bitPossible analog in C
byteIntegereightchar
shortsixteenshort
charsixteenwchar_t
int32int (long)
long64long
floatFloating point numbers32float
double64double
booleanLogical-int (C89) / bool (C99)

All primitives have their counterparts in the language C. However, the C standard does not define the exact size of integer types; instead, it fixes the range of values ​​that this type can store. Often, the programmer wants to provide the same bit depth for different machines, which leads to the appearance of types like uint32_t in the program, although all library functions just need arguments of type int.
This fact can not be attributed to the advantages of the language.

Integer primitives in Java, unlike C, have a fixed bit depth. Thus, you can not worry about the actual capacity of the machine on which the Java program is running, as well as the order of bytes ("network" or "Intel"). This fact helps to realize the principle “once written - performed everywhere.”

In addition, in Java all integer primitives are signed (there is no unsigned keyword in the language). This eliminates the difficulty in sharing signed and unsigned variables in the same expression that are inherent in C.

Finally, the byte order in multibyte primitives in Java is fixed (low byte by lower address, Little-endian, reverse order).

The disadvantages of implementing operations with primitives in Java include the fact that here, like in a C / C ++ program, an overflow of the bit grid may occur, and no exceptions are raised:

 int i1 = 2_147_483_640; int i2 = 2_147_483_640; int r = (i1 + i2); // r = -16 

So, data in Java is represented by two kinds of entities: objects and primitives. Primitives violate the concept of “everything is an object”, but in some situations it is too effective to not use them.

Inheritance


Inheritance is another PLO whale that you have probably heard about. If you answer briefly the question “why inheritance is needed at all”, the answer will be “code reuse”.

Suppose you are programming in C, and you have a well-written and debugged "class" - the structure and functions for its processing. Further, there is a need to create a similar "class", but with extended functionality, and the base "class" still remains needed. In the case of the C language, you have a single way to solve this problem - composition. It is about creating a new extended “class” structure, which should contain a pointer to the base “class” structure:

 struct Base { int field1; char *field2; }; void baseMethod(struct Base *obj, int arg); struct Extended { struct Base *base; int auxField; }; void extendedMethod(struct Extended *obj, int arg) { baseMethod(obj->base, 123); /* ... */ } 

Java as an object-oriented language allows you to extend the functionality of existing classes using the inheritance mechanism:

 //   class Base { protected int baseField; private int hidden; public void baseMethod() { } } //   -   . class Extended extends Base { public void extendedMethod() { //    public  protected     . baseField = 123; baseMethod(); // !   private  ! hidden = 123; } } 

It should be noted that Java in no way prohibits the use of composition as a way of extending the functionality of already written classes. Moreover, in many situations, composition is preferable to inheritance.

Thanks to inheritance, classes in Java are lined up in a hierarchical structure, each class necessarily has one and only one “parent” and can have as many “children” as you like. Unlike C ++, a class in Java cannot inherit from more than one parent (this is how the diamond-shaped inheritance problem is solved).

During inheritance, the derived class gets all public and protected fields and methods of its base class, as well as the base class of its base class, and so on up the inheritance hierarchy.

At the top of the inheritance hierarchy is the common ancestor of all Java classes — the Object class, the only one who does not have a parent.

Dynamic type identification


One of the key points of the Java language is support for dynamic type identification (RTTI). In simple terms, RTTI allows you to substitute a derived class object where a reference to the base class is required:

 //     Base link; //         link = new Extended(); 

Having a link at runtime, you can determine the true type of the object to which this link refers - using the instanceof operator:

 if (link instanceof Base) { // false } else if (link instanceof Extended) { // true } 

Redefinition of methods


By redefining a method or function is meant replacing his or her body at the stage of program execution. C programmers know the ability of a language to change the behavior of a function during program execution. This is about using function pointers. For example, you can include a function pointer in the structure and assigning various functions to the pointer to change the data processing algorithm of this structure:

 struct Object { //   . void (*process)(struct Object *); int data; }; void divideByTwo(struct Object *obj) { obj->data = obj->data / 2; } void square(struct Object *obj) { obj->data = obj->data * obj->data; } struct Object obj; obj.data = 123; obj.process = divideByTwo; obj.process(&obj); // 123 / 2 = 61 obj.process = square; obj.process(&obj); // 61 * 61 = 3721 

In Java, as in other OOP languages, overriding methods is inextricably linked with inheritance. The derived class gains access to the public and protected methods of the base class. Besides the fact that he can call them, you can change the behavior of one of the methods of the base class without changing its signature. To do this, it is enough to define a method in the derived class with the exact same signature:

 //   -   . class Extended extends Base { //  . public void method() { /* ... */ } //     ! // E      . //     . public void method(int i) { /* ... */ } } 

It is very important that the signature (method name, return value, arguments) match exactly. If the name of the method matches, and the arguments are different, then the method is overloaded (overloading).

Polymorphism


Like encapsulation and inheritance, the third whale of OOP — polymorphism — also has a counterpart in procedural language C.

Suppose we have several "classes" of structures with which it is required to perform a single-type action, and the function that performs this action must be universal - must "know how" to work with any "class" as an argument. A possible solution is:

  /*   */ enum Ids { ID_A, ID_B }; struct ClassA { int id; /* ... */ } void aInit(ClassA obj) { obj->id = ID_A; } struct ClassB { int id; /* ... */ } void bInit(ClassB obj) { obj->id = ID_B; } /* klass -   ClassA, ClassB, ... */ void commonFunc(void *klass) { /*   */ int id = (int *)klass; switch (id) { case ID_A: ClassA *obj = (ClassA *) klass; /* ... */ break; case ID_B: ClassB *obj = (ClassB *) klass; /* ... */ break; } /* ... */ } 

, – commonFunc() «» «». – «»- , «» . «void *». , , «int *». , .

Now let's look at how polymorphism looks in Java (just like in any other OOP language). Suppose we have many classes that must be processed in the same way by some method. Unlike the solution for the C language presented above, this polymorphic method MUST be included in all classes of this set, and all its versions MUST have the same signature.

 class A { public void method() {/* ... */} } class B { public void method() {/* ... */} } class C { public void method() {/* ... */} } 

Next, you need to force the compiler to call exactly the version of the method that belongs to the corresponding class.

 void executor(_set_of_class_ klass) { klass.method(); } 

executor(), , «» (A, B C). - «» , _set_of_class_ . – , :

 abstract class Base { abstract public void method(); } class A extends Base { public void method() {/* ... */} } class B extends Base { public void method() {/* ... */} } class C extends Base { public void method() {/* ... */} }   executor()   : void executor(Base klass) { klass.method(); } 

, Base ( ):

 executor(new A()); executor(new B()); executor(new C()); 

, , , .

abstract ( , ). , . , . , , . abstract.

Java


Java *.java. *.h, . Java . , .

(package). :
  1. .
  2. .
  3. , , :

 package com.company.pkg; 

To ensure the uniqueness of package names within the globe, it is proposed to use the “inverted” domain name of the company. However, this is not a requirement and any names can be used in the local project.

It is also recommended to set the names of packages in lower case. So they can be easily distinguished from the class names.

Implementation hiding


– . ( ), . , « » , «» , .

C , , , static. , , . C : *.c *.h.

Java static, «» . «» 3 : private, protected, public.

, private, . protected . public , , . , , .

private .


One of the annoying features of the standard C library is the presence of a whole zoo of functions that perform essentially the same thing, but differ in the type of argument, for example: fabs (), fabsf (), fabsl () are functions for obtaining an absolute value for double, float and long double types respectively.

Java (as well as C ++) supports the method overloading mechanism - there can be several methods inside a class with a completely identical name, but differing in the type and number of arguments. According to the number of arguments and their type, the compiler will choose the necessary version of the method - it is very convenient and improves the readability of the program.

In Java, unlike C ++, operators cannot be overloaded. The exceptions are the “+” and “+ =” operators, which are initially overloaded for String strings.

Java characters and strings


C - , :

 char *str; //  ASCII  wchar_t *strw; //   ""  

. «», . , ( ) .

C- - , , , - . , C , .

Java char ( «» Character, «» – ) Unicode. UTF-16, 2 , .

Unicode:

 char ch1 = '\u20BD'; 

Unicode 216 char, int. 2 16 , - , 216, .

Java String 16- char. String , . , , «» . , Java, , .

, Java ( ++), String – «+» «+=».

 String str1 = "Hello, " + "World!"; String str2 = "Hello, "; str2 += "World!"; 

, Java – , . , , :

 String str = "Hello, World!"; str.toUpperCase(); System.out.println(str); //   "Hello, World!" 

T . , :

 String str = "Hello, World!"; String str2 = str.toUpperCase(); System.out.println(str2); //   "HELLO, WORLD!" 

Thus, each change of a string in reality turns around the creation of a new object (in fact, in cases of string merging, the compiler can optimize the code and use the StringBuilder class, which will be discussed later).

It happens that the program must often change the same line. In such cases, in order to optimize program performance and memory consumption, it is possible to prevent the creation of new line objects. For these purposes, you should use the StringBuilder class:

 String sourceString = "Hello, World!"; StringBuilder builder = new StringBuilder(sourceString); builder.setCharAt(4, '0'); builder.setCharAt(8, '0'); builder.append("!!"); String changedString = builder.toString(); System.out.println(changedString); //   "Hell0, W0rld!!!" 

Separately, it should be said about comparing strings A typical mistake for a novice Java programmer is to compare strings with the "==" operator:

 //    "Yes" // ! if (usersInput == "Yes") { //    } 

, , . , Java, , «==» , . true 2 . – , , equals():

 if (usersInput.equals("Yes")) { //    } 

, «==» :

 String someString = "abc", anotherString = "abc"; //   "true": System.out.println(someString == anotherString); 

This is due to the fact that someString and anotherString references actually point to the same object in memory. The compiler puts the same string literals in the string pool — the so-called internment occurs. Then every time the same string literal appears in the program, a reference to the string from the pool is used. Interning strings is just as possible due to the immutability property of strings.

Although the comparison of the contents of strings is allowed only by the equals () method, in Java it is possible to correctly use strings in switch-case constructions (starting with the Java 7 version):

 String str = new String(); // ... switch (str) { case "string_value_1": // ... break; case "string_value_2": // ... break; } 

Curiously, any Java object can be converted to a string. The corresponding toString () method is defined in the base class for all classes of Object.

Error handling approach


C, . - int. , 0. – . . , , , , :

 int function(struct Data **result, const char *arg) { int errorCode; /* ... */ return errorCode; } 

, , C .

. , , . . , :

 struct Data* function(const char *arg); int getLastError(); 

, C , «» , , , , .

Java , , – (, C++). , «» , , .

try-catch: try «» , catch – .

 //       try (FileReader reader = new FileReader("path\\to\\file.txt")) { //    -   . while (reader.read() != -1){ // ... } } catch (IOException ex) { //     } 

There are situations when it is not possible to correctly handle an error in the place of its origin. In such cases, an indication is placed in the method signature that the method may raise this type of exception:

 public void func() throws Exception { // ... } 

Now the call to this method must necessarily be framed in a try-catch block, or the calling method must also be flagged so that it can raise this exception.

Lack of preprocessor


No matter how convenient the familiar C / C ++ programmer preprocessor, in Java, it is missing. Java developers probably decided that it is used only to ensure portability of programs, and since Java is executed everywhere (almost), the preprocessor is not needed in it either.

You can compensate for the lack of a preprocessor using a static flag field and check its value in the program, where necessary.

If we are talking about the organization of testing, then it is possible to use annotations in conjunction with reflection (reflection).

The array is also an object.


When working with arrays on C, going out of the array beyond the bounds of the array is a very insidious error. The compiler will not inform about it in any way, and at run time the program will not be stopped with the appropriate message:

 int array[5]; array[6] = 666; 

, , array , . .

Java . ArrayIndexOutOfBoundsException. try-catch, , , . .

Java- , Java . Java , . :

 int[] array = new int[10]; int arraySize = array.length; // 10 

, C Java «» . :

 int[][] array = new int[10][]; for (int i = 0; i < array.length; i++) { array[i] = new int[i + 1]; } 

As in C, the elements of the array are located in memory one after the other, so access to the array is considered the most efficient. If you need to perform operations of inserting / deleting elements, or creating more complex data structures, then you need to use collections, such as Set (set), List (List), map (Map).

In the absence of pointers and the inability to increment links, access to the elements of an array is possible using indexes.

Collections


Often, the functionality of arrays is not enough - then you need to use dynamic data structures. Since the standard C library does not contain a ready-made implementation of dynamic data structures, it is necessary to use the implementation in source codes or in the form of libraries.

Unlike C, the standard Java library contains a rich set of implementations of dynamic data structures or collections, to put it in Java terms. All collections are divided into 3 large classes: lists, sets and maps.

– – / . , . « – », – 2 .

, , . , ArrayList LinkedList, ArrayList , LinkedList – / .

Only full-fledged Java objects can be stored in collections (in fact, references to objects), so you cannot create a collection of primitives directly (int, char, byte, etc.). In this case, the appropriate “wrapper” classes should be used:
PrimitiveWrap Class
byteByte
shortShort
charCharacter
intInteger
longLong
floatFloat
doubleDouble
booleanBoolean

, Java, «». , , Integer, int. , int, Integer. Java / .

, Java , Java . Enumeration, Vector, Stack, Dictionary, Hashtable, Properties.


. , , , ArrayList, -, :

 List<Integer> list = new ArrayList<Integer>(); 

, -:

 List<Integer> list = new ArrayList<Integer>(); //  ! list.add("First"); 

, - , , ,
 ArrayList<Integer> 
 ArrayList<String>. 
:

 public boolean containsInteger(List list) { //  ! if (list instanceof List<Integer>) { return true; } return false; } 

: :

 public boolean containsInteger(List list) { if (!list.isEmpty() && list.get(0) instanceof Integer) { return true; } return false; } 

, .

Java C++. Java «» .


:

 for (int i = 0; i < SIZE; i++) { /* ... */ } 

, SIZE «<=» «<».

Java «» for ( foreach):

 List<Integer> list = new ArrayList<>(); // ... for (Integer i : list) { // ... } 

, , «» for.


Since all objects are inherited from the root Object, in Java there is an interesting opportunity to create lists with different actual types of elements:

 List list = new ArrayList<>(); list.add(new String("First")); list.add(new Integer(2)); list.add(new Double(3.0));         instanceof: for (Object o : list) { if (o instanceof String) { // ... } else if (o instanceof Integer) { // ... } else if (o instanceof Double) { // ... } } 

Transfers


Comparing C / C ++ and Java, it is impossible not to notice how much more functional in Java are the enumerations. Here the enumeration is a full-fledged class, and the elements of the enumeration are objects of this class. This allows one element of the enumeration to assign several fields of any type to correspond:

 enum Colors { //     -   . RED ((byte)0xFF, (byte)0x00, (byte)0x00), GREEN ((byte)0x00, (byte)0xFF, (byte)0x00), BLUE ((byte)0x00, (byte)0x00, (byte)0xFF), WHITE ((byte)0xFF, (byte)0xFF, (byte)0xFF), BLACK ((byte)0x00, (byte)0x00, (byte)0x00); //  . private byte r, g, b; //  . private Colors(byte r, byte g, byte b) { this.r = r; this.g = g; this.b = b; } //  . public double getLuma() { return 0.2126 * r + 0.7152 * g + 0.0722 * b; } } 

As a full-fledged class, an enumeration can have methods, and with the help of a private constructor, you can set the field values ​​of individual enumeration elements.

There is a regular opportunity to get a string representation of the enumeration element, a sequence number, as well as an array of all elements:

 Colors color = Colors.BLACK; String str = color.toString(); // "BLACK" int i = color.ordinal(); // 4 Colors[] array = Colors.values(); // [RED, GREEN, BLUE, WHITE, BLACK] 

And vice versa - by a string representation, you can get an enumeration element, as well as call its methods:

 Colors red = Colors.valueOf("RED"); // Colors.RED Double redLuma = red.getLuma(); // 0.2126 * 255 

Naturally, enums can be used in switch-case constructs.

findings


Of course, the C and Java languages ​​are designed to solve completely different problems. But, if you still compare the software development process in these two languages, then, according to the subjective impressions of the author, the Java language is significantly superior to C in terms of convenience and speed of writing programs. The development environment (IDE) plays a significant role in providing convenience. The author worked with IDE IntelliJ IDEA. When programming in Java, you don’t have to constantly “be afraid” of making a mistake - often the development environment will tell you what needs to be fixed, and sometimes it will do it for you. If a runtime error occurs, the error type is always indicated in the log and the place of its occurrence in the source code - the fight against such errors becomes a trivial matter. C-programmer does not need to make inhuman efforts to go to Java, and all this is due to the fact that the syntax of the language has changed slightly.

, JNI ( C/C++- Java-). JNI , , Bluetooth- , Android .

Source: https://habr.com/ru/post/412775/


All Articles