• Home
  • LLMs
  • Docker
  • Kubernetes
  • Java
  • All
  • About
Java | Types
  1. Notes
  2. Primitive Types
    1. Notes
    2. Boolean type: boolean
    3. Character type: char (16 bits)
    4. Integer type: byte (1 byte/8 bits: -128 to 127)
    5. Integer type: short (2 bytes/16 bits: -32,768 to 32,767)
    6. Integer type: int (4 bytes/32 bits: -(231) to (231) - 1)
    7. Integer type: long (8 bytes/64 bits): -(263) to (263) - 1)
    8. Floating-point type: float (4 bytes/32 bits)
    9. Floating-point type: double (8 bytes/64 bits)
  3. Reference Types
    1. Notes
    2. Interface
    3. Class
    4. Array

  1. Notes

                  Data Types
          _______________⇓_______________
         ↓                               ↓
    Primitive Types                Reference Types
                _________________________⇓_________________________
               ↓                         ↓                         ↓
      Interface                     Class                     Array
    • Long integer numbers have a suffix l or L: 1l, 1L
    • Floating-point numbers (float) have a suffix f or F: 1.0f, 1.0F
    • Floating-point numbers (double) can optionally have a suffix d or D: 1.0, 1.0d, 1.0D
    • Hexadecimal numbers have a prefix 0x or 0X: 0x1, 0X1
    • Octal numbers have a prefix 0: 01
    • Binary numbers have a prefix 0b or 0B: 0b1, 0B1

    • Underscores can be added to numeric literals: 1_000_000

    • A character can be expressed as a hexadecimal value using the prefix \u: \u0000, \uFFFF

    • You can use the keyword "var" to declare a local variable if its type can be inferred by the compiler from the assigned value.
  2. Primitive Types

    1. Notes

      Primitive types:
      • Boolean type: boolean
      • Character type: char
      • Integer types: byte, short, int, long
      • Floating-point types: float, double

      The following are the possible conversions between numeric types without loss of information:
      • byte → short → int → long
      • char → int
      • int → long → float → double

      The following are the possible conversions between numeric types that may result in loss of information (precision):
      • int → float
      • long → float
      • long → double

      When a conversion between two numeric types can be done without loss of information, automatic conversion is possible:
      long value = intValue;
      Otherwise, you need to manually cast the value from one type to another:
      int value = (int) longValue;
      Note that the value might be truncated if it exceeds the numeric range supported by the target type:
      byte value = (byte) 128; // "value" will be equal to -128
      When applying a binary operator to operands of different types, the following rules apply:
      • If one operand is of type double, the other will be converted to double
      • If one operand is of type float, the other will be converted to float
      • If one operand is of type long, the other will be converted to long
      • Otherwise, both operands are converted to int (byte, short, and char operands are always promoted to int)
    2. Boolean type: boolean

      A variable of type boolean can hold the values true or false.
    3. Character type: char (16 bits)

      A variable of type char can hold characters.
      The value of the variable is represented by 16 bits.
      char c1 = 'a';
      A variable of type char can also hold positive integers.
      A variable of type char can hold values in the range: [0, 65535]
      A cast is required to assign an integer value to a variable of type char.
      char c2 = (char) 65535;
      A character can be expressed as a hexadecimal value using the prefix \u
      char c3 = '\u005B';
      Escape Characters:
      escape character | unicode value | name
      \"               | \u0022        | Double quote
      \'               | \u0027        | Single quote
      \\               | \u005c        | Backslash
      \t               | \u0009        | Tab
      \n               | \u000a        | Line feed
      \r               | \u000d        | Carriage return
      \b               | \u0008        | Backspace
      Unicode escape characters are interpreted before the code is parsed.
      • For example "\u0022 + \u0022" is equivalent to "" + ""
        System.out.println("\u0022 + \u0022");
        System.out.println("" + "");
      • \u0061 is the Unicode value of the character 'a'
        char c\u0061 = 'x';
        System.out.println(ca); // \u0061 in the identifier becomes the letter 'a'
      Some cases may cause the compiler to report a syntax error:
      • The following will be interpreted as if you typed System.out.println("" "");:
        System.out.println("\u0022 \u0022");
      • A line feed within a comment:
        // the line feed \u000a will cause multiple compiler errors
      • Invalid Unicode character prefix within a comment:
        // invalid unicode: missing the 4 hex digits after \u: \unicode
      Note that the type char can hold any 16-bit (basic) Unicode character. For supplementary Unicode characters, each character has a code value (code point) that is represented as a pair of 16-bit code units. When working with strings that may include supplementary Unicode characters, you should be aware that a char value might hold just one of the code units representing such a character. For example, the smiley symbol "🙂" is represented using 2 code units:
      String smiley = "🙂";
      System.out.println(smiley.length()); // code units count: 2
      System.out.println(smiley.codePointCount(0, smiley.length())); // code points count: 1
      System.out.println(String.format("%X", (int) smiley.charAt(0))); // first code unit D83D of the smiley character
      System.out.println(String.format("%X", (int) smiley.charAt(1))); // second code unit DE42 of the smiley character
      System.out.println(String.format("%X", smiley.codePointAt(0))); // code point 1F642 of the smiley character
      If you need to print all characters in a string, use the codePoints method of the String class:
      "mti🙂tek".codePoints().forEach(cp -> System.out.println(new String(Character.toChars(cp))));
      m
      t
      i
      🙂
      t
      e
      k
      See the Java API documentation for how to use the Character.toChars method:
      public static char[] toChars(int codePoint)
      //Converts the specified character (Unicode code point) to its UTF-16 representation stored in a char array.
      //If the specified code point is a BMP (Basic Multilingual Plane or Plane 0) value, the resulting char array has the same value as codePoint.
      //If the specified code point is a supplementary code point, the resulting char array has the corresponding surrogate pair.
      
      //Parameters:
      //codePoint - a Unicode code point
      
      //Returns:
      //a char array containing the UTF-16 representation of the code point.
    4. Integer type: byte (1 byte/8 bits: -128 to 127)

      A variable of type byte can hold both positive and negative values.
      The value of the variable is represented using 8 bits.
      A variable of type byte can hold values in the range [-128, 127].

      Java uses two's complement representation for all signed integer types (byte, short, int, long).
      In two's complement representation, all bits participate in the value calculation, including the most significant bit (MSB).
      The MSB has a negative weight, while all other bits have positive weights.
      When the most significant bit is 1, the number is negative; when it's 0, the number is positive.

      For a byte type with 8 bits, the bit weights are:
      Bit Position 7 (MSB) 6 5 4 3 2 1 0 (LSB)
      Weight -128 64 32 16 8 4 2 1

      Calculation formula: (-128 × bit7) + (64 × bit6) + (32 × bit5) + (16 × bit4) + (8 × bit3) + (4 × bit2) + (2 × bit1) + (1 × bit0)

      Examples:
      00000001
      = (-128×0) + (64×0) + (32×0) + (16×0) + (8×0) + (4×0) + (2×0) + (1×1)
      = 0 + 0 + 0 + 0 + 0 + 0 + 0 + 1
      = 1
      
      10000001
      = (-128×1) + (64×0) + (32×0) + (16×0) + (8×0) + (4×0) + (2×0) + (1×1)
      = -128 + 0 + 0 + 0 + 0 + 0 + 0 + 1
      = -127
      
      11111111
      = (-128×1) + (64×1) + (32×1) + (16×1) + (8×1) + (4×1) + (2×1) + (1×1)
      = -128 + 64 + 32 + 16 + 8 + 4 + 2 + 1
      = -1
      
      1000 0000
      = (-128×1) + (64×0) + (32×0) + (16×0) + (8×0) + (4×0) + (2×0) + (1×0)
      = -128 + 0 + 0 + 0 + 0 + 0 + 0 + 0
      = -128
      
    5. Integer type: short (2 bytes/16 bits: -32,768 to 32,767)

      A variable of type short can hold both positive and negative values.
      The value of the variable is represented using 16 bits.
      A variable of type short can hold values in the range [-32,768, 32,767].
      Same as byte, two's complement representation is used to represent short values.
    6. Integer type: int (4 bytes/32 bits: -(231) to (231) - 1)

      A variable of type int can hold both positive and negative values.
      The value of the variable is represented using 32 bits.
      A variable of type int can hold values in the range [-2,147,483,648 to 2,147,483,647].
      Same as byte, two's complement representation is used to represent int values.
    7. Integer type: long (8 bytes/64 bits): -(263) to (263) - 1

      A variable of type long can hold both positive and negative values.
      The value of the variable is represented using 64 bits.
      A variable of type long can hold values in the range [-9,223,372,036,854,775,808 to 9,223,372,036,854,775,807].
      Same as byte, two's complement representation is used to represent long values.

      Long integer numbers have a suffix l or L.
      Examples:
      long l1 = 1l;
      long l2 = 1L;
    8. Floating-point type: float (4 bytes/32 bits)

      A variable of type float can hold real numbers.
      The value of the variable is represented using 32 bits.

      Floating-point numbers (float) have a suffix f or F.
      Examples:
      // 1.0 is a double literal by default, which is why the explicit cast or f suffix is needed.
      float f1 = 1.0; // compiler error: "Type mismatch: cannot convert from double to float"
      float f2 = (float) 1.0; // OK
      float f3 = 1.0f; // OK
      float f4 = 1.0F; // OK
      Constant holding the positive infinity of type float:
      /**
       * A constant holding the positive infinity of type float.
       * It is equal to the value returned by Float.intBitsToFloat(0x7f800000).
       */
      float POSITIVE_INFINITY = 1.0f / 0.0f;
      Constant holding the negative infinity of type float:
      /**
       * A constant holding the negative infinity of type float.
       * It is equal to the value returned by Float.intBitsToFloat(0xff800000).
       */
      float NEGATIVE_INFINITY = -1.0f / 0.0f;
      Constant holding a Not-a-Number (NaN) value of type float:
      /**
       * A constant holding a Not-a-Number (NaN) value of type float.
       * It is equivalent to the value returned by Float.intBitsToFloat(0x7fc00000).
       */
      float NaN = 0.0f / 0.0f;
      Check if a variable is not a number:
      if (Float.isNaN(f3))
      The following is always false (use Float.isNaN instead):
      if (f3 == Float.NaN)
      Check if a variable is positive infinity:
      if (f3 == Float.POSITIVE_INFINITY)
      Check if a variable is negative infinity:
      if (f3 == Float.NEGATIVE_INFINITY)
      Check if a variable is a finite floating-point value:
      if (Float.isFinite(f3))
      Check if a variable is either positive infinity or negative infinity:
      if (Float.isInfinite(f3))
    9. Floating-point type: double (8 bytes/64 bits)

      A variable of type double can hold real numbers.
      The value of the variable is represented using 64 bits.

      Floating-point numbers (double) can optionally have a suffix d or D.
      Examples:
      double d1 = 1.0; // OK
      double d2 = 1.0d; // OK
      double d3 = 1.0D; // OK
      Constant holding the positive infinity of type double:
      /**
       * A constant holding the positive infinity of type double.
       * It is equal to the value returned by Double.longBitsToDouble(0x7ff0000000000000L).
       */
      double POSITIVE_INFINITY = 1.0 / 0.0;
      Constant holding the negative infinity of type double:
      /**
       * A constant holding the negative infinity of type double.
       * It is equal to the value returned by Double.longBitsToDouble(0xfff0000000000000L).
       */
      double NEGATIVE_INFINITY = -1.0 / 0.0;
      Constant holding a Not-a-Number (NaN) value of type double:
      /**
       * A constant holding a Not-a-Number (NaN) value of type double.
       * It is equivalent to the value returned by Double.longBitsToDouble(0x7ff8000000000000L).
       */
      double NaN = 0.0d / 0.0;
      Check if a variable is not a number:
      if (Double.isNaN(d1))
      The following is always false (use Double.isNaN instead):
      if (d1 == Double.NaN)
      Check if a variable is positive infinity:
      if (d1 == Double.POSITIVE_INFINITY)
      Check if a variable is negative infinity:
      if (d1 == Double.NEGATIVE_INFINITY)
      Check if a variable is a finite floating-point value:
      if (Double.isFinite(d1))
      Check if a variable is either positive infinity or negative infinity:
      if (Double.isInfinite(d1))
  3. Reference Types

    1. Notes

      When we talk about references, it’s important to distinguish between the objects created in memory and the variables that reference those objects:
      • An object is an instance of a specific class whose type and identity remain constant once created, though its state may be mutable depending on the class design (e.g., StringBuilder).
        Note that the object's reference/memory location can change due to JVM operations such as Garbage Collection, JIT Compilation, and Memory Management.

      • A variable, on the other hand, can reference any object in memory and can also reference different objects during its lifetime, as long as the variable is not declared with the final keyword.

        The only rules applying to variables concern the type used during their declaration:

        • The type of the variable cannot be changed once the variable is declared.
          public class MyClass {
              void foo() {
                  Integer var1; // The variable var1 will always be of type Integer!!!
              }
          }
        • If a variable is declared with the final keyword, then once it is initialized, its value cannot be changed (in other words, the variable cannot reference another object).
          public class MyClass {
              void foo() {
                  Integer var1 = Integer.valueOf(0);
                  final Integer var2 = Integer.valueOf(0);
          
                  var1 = Integer.valueOf(10); // OK
                  var2 = Integer.valueOf(20); // Compiler error: The final local variable var2 cannot be assigned. It must be blank and not using a compound assignment
              }
          }
        • The type of the variable determines which objects it can reference.
          This includes:
          - objects of the same type as the variable,
          - and also objects whose type is a subtype of the variable's type.
          public class MyClass {
              void foo() {
                  Integer var1 = Integer.valueOf(0); // OK: referenced object is the same type (Integer) as variable var1
                  Object var2 = Integer.valueOf(0); // OK: referenced object type (Integer) is a subtype of variable var2’s type (Object)
          
                  String var3 = var1; // Compiler error: Type mismatch: cannot convert from Integer to String
                  String var4 = var2; // Compiler error: Type mismatch: cannot convert from Object to String
              }
          }
        • The type of the variable determines which methods of the referenced object can be invoked.
          This restriction is especially important when the referenced object's type is a subtype of the variable's type, because potentially the object's type can define new methods that are not declared by the variable's type.
          public class MyClass {
              void foo() {
                  Integer var1 = Integer.valueOf(0);
                  Object var2 = Integer.valueOf(0);
          
                  var1.intValue();
                  var2.intValue(); // Compiler error: The method intValue() is undefined for the type Object
              }
          }
    2. Interface

      • Interface declaration rules:
        public abstract interface MyInterfaceType {}
        • The interface declaration can only use the public and abstract modifiers.

        • The interface declaration can omit the public access modifier (in this case it will have default access).

        • The interface declaration can include or omit the abstract modifier; an interface can contain method declarations as well as methods with the default modifier.

        • The interface declaration cannot use the access modifiers protected or private.

        • The interface declaration cannot use the modifiers static or final.

      • Interface variable declaration rules:
        public abstract interface MyInterfaceType {
            public final static Integer ADD_OPERATION_ID = 0;
            Integer MULTIPLY_OPERATION_ID = 1;
        }
        • Variable declarations can only use the modifiers public, final, and static.

        • Variable declarations can omit the modifiers public, final, and static; the compiler adds them by default.
          Variables declared in interfaces are actually constants and cannot be modified in classes that implement these interfaces.

        • Variable declarations cannot use the access modifiers protected or private.

      • Interface method declaration rules:
        public abstract interface MyInterfaceType {
            public abstract Integer addOperation(Integer value1, Integer value2);
            Integer multiplyOperation(Integer value1, Integer value2);
            default Integer identity(Integer i) { return i;}
        }
        • Method declarations can only use the modifiers public and abstract.

        • Method declarations can omit the modifiers public and abstract; the compiler adds them by default.

        • Method declarations cannot use the access modifiers protected or private.

        • Interface methods cannot use the modifiers final (because of the abstract keyword), native, or strictfp.

        • Interface methods cannot contain code (because of the abstract keyword) unless they are marked as default.

        • Static methods in interfaces must use the static modifier and must provide an implementation.

        • Static methods in interfaces can only use the public access modifier (which is implicit if omitted).

        • Private methods in interfaces can be used by default and static methods.
          Private methods must provide an implementation and can be either static or non-static.

      • Rules for extending interfaces:
        • An interface can extend (extends) only other interfaces (it cannot extend classes).
        package com.mtitek.inheritance;
        
        interface MyInterface1 {
            public void doAction1();
        }
        
        interface MyInterface2 {
            public void doAction2();
        }
        
        interface MyInterface3 extends MyInterface1, MyInterface2 {
            public void doAction3();
        }

      • Rules for implementing interface methods:
        • A class can implement (implements) multiple interfaces.

        • A class that implements an interface must implement all the methods of that interface unless the class is:
          - abstract;
          - or one of its superclasses has already implemented the methods of that interface.
        package com.mtitek.inheritance;
        
        interface MyInterface1 {
            public void doAction1();
        }
        
        interface MyInterface2 {
            public void doAction2();
        }
        
        interface MyInterface3 {
            public void doAction3();
        }
        /* The class "SuperClass1" is abstract, so it is not required to implement any of the methods from the interfaces "MyInterface1" and "MyInterface2". */
        abstract class SuperClass1 implements MyInterface1, MyInterface2 {
            @Override
            public void doAction1() {
            }
        }
        /* The class "SuperClass2" is not required to implement the method "doAction1" because it is already implemented by the class "SuperClass1". */
        class SuperClass2 extends SuperClass1 implements MyInterface3 {
            @Override
            public void doAction2() {
            }
        
            @Override
            public void doAction3() {
            }
        }
        /* The class "SubClass1" is not required to implement any methods because they are already implemented by its superclasses. */
        class SubClass1 extends SuperClass2 implements MyInterface2 {
        }

      • Interface default method declaration rules:
        • Default methods must use the default modifier and must provide an implementation.

        • Default methods can only use the public access modifier (which is implicit if omitted).

        • Default methods cannot use the modifiers abstract, static, final, native, strictfp, or synchronized.

        • Default methods cannot use the access modifiers protected or private.
        interface MyInterface4 {
            // Abstract method
            public abstract void doAction1();
        
            // Default method
            default void doAction2() {
                doAction3(); // OK: can call private methods
                doAction4(); // OK: can call static methods
            }
        
            // Static method
            static void doAction3() {
                System.out.println("This is a static method");
            }
        
            // Private method
            private void doAction4() {
                System.out.println("This is a private method");
            }
        }
        public class MyClass4 {
            public static void main(String[] args) {
                MyInterface4 myInterface4 = new MyInterface4() {
                    @Override
                    public void doAction1() {
                        System.out.println("This is the implementation of the abstract method");
                    }
        
                };
        
                myInterface4.doAction1();
                myInterface4.doAction2();
                MyInterface4.doAction3();
            }
        }

      • Rules for implementing interfaces with default methods:
        • A class that implements an interface with default methods is not required to override the default methods.
          The class will inherit the default implementation.

        • A class can override default methods if needed, treating them like any other inherited method.

        • When a class implements multiple interfaces with conflicting default methods (same signature), the class must override the method to resolve the conflict.

        • A class can call the default implementation of a specific interface using the syntax: InterfaceName.super.methodName()

        • If a class inherits a method from a superclass and also inherits a default method with the same signature from an interface, the class method takes precedence over the default method.
        interface MyInterface5 {
            default void doAction1() {
                System.out.println("Default from MyInterface5");
            }
        }
        
        interface MyInterface6 {
            default void doAction1() {
                System.out.println("Default from MyInterface6");
            }
        }
        
        // Case 1: implements interface with default method
        class MyClass5 implements MyInterface5 {
            public void doAction2() {
                doAction1(); // uses default implementation
            }
        }
        
        // Case 2: implements interface + overrides default method
        class MyClass6 implements MyInterface5 {
            @Override
            public void doAction1() {
                System.out.println("Overridden in MyClass6");
                MyInterface5.super.doAction1(); // Can call default implementation
            }
        
            public void doAction3() {
                doAction1(); // uses overridden method
            }
        }
        
        // Case 3: Diamond problem - must resolve conflict
        class MyClass7 implements MyInterface5, MyInterface6 {
            @Override
            public void doAction1() {
                System.out.println("Overridden in MyClass7");
                MyInterface5.super.doAction1(); // Must choose implementation to resolve conflict
            }
        
            public void doAction4() {
                doAction1(); // uses overridden method
            }
        }
        public class MyClass8 {
            public static void main(String[] args) {
                new MyClass5().doAction1(); // call MyInterface5
                new MyClass5().doAction2(); // call MyClass5
        
                new MyClass6().doAction1(); // call MyClass6
                new MyClass6().doAction3(); // call MyClass6
        
                new MyClass7().doAction1(); // call MyClass7
                new MyClass7().doAction4(); // call MyClass7
            }
        }
    3. Class

      • A class can extend (extends) only one class.

      • A class can implement (implements) multiple interfaces.

      • A class can declare multiple constructors (with different signatures) which can be invoked when instantiating the class.
        The constructor invoked depends on the arguments used in the class instantiation (new).
        A constructor can also invoke another constructor of the same class using the keyword this.

      • The compiler adds a no-argument constructor by default only if no constructors have been defined in the class.

      • Constructor declarations cannot specify a return type and cannot use the modifiers final, static, or abstract.
      class MyClassType implements MyInterfaceType {
          MyClassType() {
              // constructor code
          }
      
          @Override
          public Integer addOperation(Integer value1, Integer value2) {
              return value1 + value2;
          }
      
          @Override
          public Integer multiplyOperation(Integer value1, Integer value2) {
              return value1 * value2;
          }
      }
    4. Array

      • An array is an Object type structure that can hold a fixed number of elements of the same type (primitive or reference).

      • All elements in the array must be of the same type or subtypes of the main type defined in the array declaration.

      • The maximum number of elements an array can hold is fixed when the array is created and cannot be changed afterward.

      • Array elements are accessed by their indices;
        the first element has index 0, the second index 1, and so on.

      Declare an array that can hold integers:
      int myIntsArray[] = new int[10];
      myIntsArray[0] = 3;
      System.out.println(myIntsArray.length); // 10
      System.out.println(myIntsArray[0]); // 3
      Declare an array that can hold objects:
      Object[] myObjectsArray = new Object[5];
      myObjectsArray[0] = Integer.valueOf(7);
      System.out.println(myObjectsArray.length); // 5
      System.out.println(myObjectsArray[0]); // 7
      Declare a String array and initialize it with initial values:
      String[] myStringsArray = {"value1", "value2"};
      // explicit syntax:
      String[] myStringsArray = new String[] {"value1", "value2"};
      Declare an Object array and initialize it with initial values:
      Object[] myComplexObjectsArray = { myStringsArray, myObjectsArray, myIntsArray };
      // explicit syntax:
      Object[] myComplexObjectsArray = new Object[] { myStringsArray, myObjectsArray, myIntsArray };
      Declare a 2-dimensional array and initialize it with initial values:
      String[][] myStringsArray2D = {{"value11", "value12"}, {"value21", "value22"}, {"value31", "value32"}, {"value41", "value42"}};
      // explicit syntax:
      String[][] myStringsArray2D = new String[][] {{"value11", "value12"}, {"value21", "value22"}, {"value31", "value32"}, {"value41", "value42"}};
      
      System.out.println(myStringsArray2D.length); // 4
      System.out.println(myStringsArray2D[0].length); // 2
      Declare an empty array:
      String[] emptyArray1 = new String[0];
      String[] emptyArray2 = {};
      String[] emptyArray3 = new String[] {};
      System.out.println(emptyArray1.length); // 0
      System.out.println(emptyArray2.length); // 0
      System.out.println(emptyArray3.length); // 0
      To iterate over an array, use a "for-each" loop: for (type element : array) statement

      To copy an array, use the "copyOf" method of the "Arrays" class: Arrays.copyOf(array, array.length)

      To sort an array, use the "sort" method of the "Arrays" class: Arrays.sort(array)
© 2025  mtitek