Share via

Data Types (C# vs. Java)

This topic discusses some of the primary similarities and differences in how data is represented, allocated, and garbage-collected in Java and in C#.

Compound data types

The concept of a class as a compound data type with fields, methods, and events is similar in Java and C#. (Class inheritance is discussed separately in the topic entitled Inheritance and Derived Classes (C# vs Java).) C# introduces the concept of a struct as a stack-allocated compound data type that does not support inheritance. In most other respects, structs are very similar to classes. Structs provide a lightweight way of grouping together related fields and methods for use in tight loops and other scenarios where performance is critical.

C# enables you to create a destructor method that is called before instances of a class are garbage-collected. In Java, a finalize method can be used to contain code that cleans up resources before the object is garbage-collected. In C#, this function is performed by the class destructor. The destructor resembles a constructor with no arguments and a preceding tilde character (~). Destructors in C# are optional and are rarely needed.

Built-In Data Types

C# provides all the data types that are available in Java, and adds support for unsigned numerals and a new 128-bit high-precision floating-point type.

For each primitive data type in Java, the core class library provides a wrapper class that represents it as a Java object. For example, the Int32 class wraps the int data type, and the Double class wraps the double data type.

On the other hand, all primitive data types in C# are objects in the System namespace. For each data type, a short name, or alias, is provided. For instance, int is the short name for System.Int32 and double is the short form of System.Double.

The list of C# data types and their aliases is provided in the following table. As you can see, the first eight of these correspond to the primitive types available in Java. Note, however, that Java's boolean is called bool in C#.

Short Name

.NET Class



Range (bits)



Unsigned integer


0 to 255



Signed integer


-128 to 127



Signed integer


-2,147,483,648 to 2,147,483,647



Unsigned integer


0 to 4294967295



Signed integer


-32,768 to 32,767



Unsigned integer


0 to 65535



Signed integer


-9223372036854775808 to 9223372036854775807



Unsigned integer


0 to 18446744073709551615



Single-precision floating point type


-3.402823e38 to 3.402823e38



Double-precision floating point type


-1.79769313486232e308 to 1.79769313486232e308



A single Unicode character


Unicode symbols used in text



Logical Boolean type


True or false



Base type of all other types



A sequence of characters



Precise fractional or integral type that can represent decimal numbers with 29 significant digits


±1.0 × 10e−28 to ±7.9 × 10e28

Because C# represents all primitive data types as objects, it is possible to call an object method on a primitive data type. For example:

static void Main()
    int i = 10;
    object o = i;

This is achieved with the help of automatic boxing and unboxing. For more information, see Boxing and Unboxing (C# Programming Guide).


Both Java and C# provide the ability to declare a variable whose value is specified at compile time and cannot be changed at runtime. Java uses the final field modifier to declare such a variable, while C# uses the const keyword. In addition to const, C# provides the readonly keyword to declare variables that can be assigned a value once at runtime--either in the declaration statement or else in the constructor. After initialization, the value of a readonly variable cannot change. One scenario in which readonly variables are useful is when modules that have been compiled separately need to share data such as a version number. If module A is updated and recompiled with a new version number, module B can be initialized with that new constant value without having to be recompiled.


Enumerations, or enums, are used to group named constants similar to how they are used in C and C++; they are available in Java beginning in Version 1.5. In C#, enums are value types, and enum constants must be integral numeric values. The ToString method can be used to print out string representations of the named constants. The following example defines a simple Color enumeration in C#.

public enum Color
    Green,   //defaults to 0
    Orange,  //defaults to 1
    Red,     //defaults to 2
    Blue     //defaults to 3

Integral values can also be assigned to enumerations as shown in the following enum declaration:

public enum Color2
    Green = 10,
    Orange = 20,
    Red = 30,
    Blue = 40

The following code example calls the GetNames method of the Enum type to display the available constants for an enumeration. It then assigns a value to an enumeration and displays the value.

class TestEnums
    static void Main()
        System.Console.WriteLine("Possible color choices: ");

        //Enum.GetNames returns a string array of named constants for the enum. 
        foreach(string s in System.Enum.GetNames(typeof(Color)))

        Color favorite = Color.Blue;

        System.Console.WriteLine("Favorite Color is {0}", favorite);
        System.Console.WriteLine("Favorite Color value is {0}", (int) favorite);

Beginning in C# 3.0, enumerations can be extended with user-defined extension methods. For more information, see How to: Create a New Method for an Enumeration (C# Programming Guide).


Possible color choices:





Favorite Color is Blue

Favorite Color value is 3


String types in both Java and C# exhibit similar behavior with slight differences. Both string types are immutable, meaning that the values of the strings cannot be changed once the strings have been created. In both instances, methods that appear to modify the actual content of a string actually create a new string to return, leaving the original string unchanged. The process of comparing string values is different in C# and Java. To compare string values in Java, developers need to call the equals method on a string type as the == operator compares reference types by default. In C#, developers can use the == or != operators to compare string values directly. Even though a string is a reference type in C#, the == and != operator will, by default, compare the string values rather then references.

Just like in Java, C# developers should not use the string type for concatenating strings to avoid the overhead of creating new string classes every time the string is concatenated. Instead, developers can use the StringBuilder class, which is functionally equivalent to the Java StringBuffer class.

String Literals

C# provides the ability to avoid the usage of escape sequences like "\t" for tab or "\" for backslash characters within string constants. To do this, simply declare the verbatim string using the @ symbol to precede the assignment of the string value. The following examples show how to use escape characters and how to assign string literals:

static void Main()
    //Using escaped characters: 
    string path1 = "\\\\FileShare\\Directory\\file.txt";

    //Using String Literals: 
    string path2 = @"\\FileShare\Directory\file.txt";

Converting and Casting

Both Java and C# follow similar rules for the automatic conversion and casting of data types.

Like Java, C# supports both implicit and explicit type conversions. In the case of widening conversions, the conversions are implicit. For example, the following conversion from int to long is implicit, as in Java:

int int1 = 5;
long long1 = int1;  //implicit conversion

The following is a list of implicit conversions between .NET Framework data types:

Source Type

Target Type


short, ushort, int, uint, long, ulong, float, double, or decimal


short, int, long, float, double, or decimal


long, float, double, or decimal


long, ulong, float, double, or decimal


int, long, float, double, or decimal


int, uint, long, ulong, float, double, or decimal


float, double, or decimal


float, double, or decimal




ushort, int, uint, long, ulong, float, double, or decimal

You cast expressions that you want to explicitly convert using the same syntax as Java:

long long2 = 5483;
int int2 = (int)long2;  //explicit conversion

The following table lists explicit conversions.

Source Type

Target Type


sbyte or char


byte, ushort, uint, ulong, or char


sbyte, byte, short, ushort, uint, ulong, or char


sbyte, byte, short, ushort, int, or char


sbyte, byte, ushort, uint, ulong, or char


sbyte, byte, short, or char


sbyte, byte, short, ushort, int, uint, ulong, or char


sbyte, byte, short, ushort, int, uint, long, or char


sbyte, byte, short, ushort, int, uint, long, ulong, char, ordecimal


sbyte, byte, short, ushort, int, uint, long, ulong, char, float, or decimal


sbyte, byte, or short


sbyte, byte, short, ushort, int, uint, long, ulong, char, float, or double

Value and Reference Types

C# supports two kinds of variable types:

  • Value types

    These are the built-in primitive data types, such as char, int, and float, as well as user-defined types declared with struct.

  • Reference types

    Classes and other complex data types that are constructed from the primitive types. Variables of such types do not contain an instance of the type, but merely a reference to an instance.

If you create two value-type variables, i and j, as follows, then i and j are completely independent of each other:

int i = 10;
int j = 20;

They are given separate memory locations:

Separate memory addresses for value types

If you change the value of one of these variables, the other will naturally not be affected. For instance, if you have an expression such as the following, then there is still no connection between the variables:

int k = i;

That is, if you change the value of i, k will remain at the value that i had at the time of the assignment.

i = 30;

System.Console.WriteLine(i.ToString());  // 30
System.Console.WriteLine(k.ToString());  // 10

Reference types, however, act differently. For instance, you could declare two variables as follows:

Employee ee1 = new Employee();
Employee ee2 = ee1;

Now, because classes are reference types in C#, ee1 is known as a reference to Employee. The first of the previous two lines creates an instance of Employee in memory, and sets ee1 to reference it. Thus, when you set ee2 to equal ee1, it contains a duplicate of the reference to the class in memory. If you now change properties on ee2, properties on ee1 reflect these changes, because both point to the same object in memory, as shown in the following:

Memory locations for reference types

Boxing and Unboxing

The process of converting a value type to a reference type is called boxing. The inverse process, converting a reference type to a value type, is called unboxing. This is illustrated in the following code:

int i = 123;      // a value type 
object o = i;     // boxing 
int j = (int)o;  // unboxing

Java requires you to perform such conversions manually. Primitive data types can be converted into objects of wrapper classes by constructing such objects, or boxing. Similarly, the values of primitive data types can be extracted from the objects of wrapper classes by calling an appropriate method on such objects, or unboxing. For more information about boxing and unboxing, see Boxing and Unboxing (C# Programming Guide).

See Also


C# Programming Guide


Types (C# Programming Guide)

Other Resources

Visual C#

The C# Programming Language for Java Developers