Java Data Types
Choose types that match the information, not just the example value. Learn how ranges, arithmetic, precision, and references affect a program through a practical parcel-report scenario.
Before you start: complete Java Variables and Constants. Each code example is a complete Main.java program in the existing Java 21 runner. Read the explanation, predict the output, and then run or edit it. The reference-sharing section is optional on your first pass.
What you will be able to do
- Distinguish primitive values from object references.
- Choose types for counts, measurements, text identifiers, and yes/no facts.
- Read integer ranges and recognize overflow in a calculation.
- Use L and f suffixes for suitable numeric literals.
- Explain floating-point approximation and char's UTF-16 limitation.
- Create a typed parcel report and test realistic boundaries.
Start with meaning, not the way the value looks
A data type tells Java what kind of value an expression or variable has. It affects what can be stored, which operations are valid, and how calculations behave. Last lesson you named values. This lesson asks a different question: does each name have a representation that fits its meaning and its possible values?
Imagine a parcel report. A parcel code looks numeric, but you do not add two parcel codes together. Keeping 00742 as String preserves the leading zeroes and treats it as an identifier. The number of items is an int because it is a count. The measured weight can be a double, the simple zone code a char, and insured a boolean.
Do not choose a type just because today's example fits. Ask whether the value is for calculation or identification, whether fractions are meaningful, how large it can become, and whether missing information is allowed. A valid Java value is not automatically valid business data: int permits a negative item count, even though this parcel should not.
Main.java · Match the type to the information
Predict the result, edit Main.java, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Parcel: 00742
Items: 3
Weight (kg): 1.25
Zone: B
Insured: true
Two families: primitive values and references
Java has eight primitive types: byte, short, int, long, float, double, char, and boolean. An int variable holds an integer value; a boolean variable holds true or false. These primitive types are built into the language. Their names are lowercase, and they are not interchangeable just because two values might print similarly.
Class types such as String and StringBuilder are reference types. A variable of one of these types holds a reference to an object, or null. The reference is not the text itself and is not a raw memory address that you manipulate here. You can think of it as the value that lets the program reach an object. Copying it does not automatically make another object.
String is common, but it is not a ninth primitive type. A String literal uses double quotes, while a char literal uses single quotes. You do not need to memorize object internals yet: first distinguish the primitive value from the reference to an object, then use the examples near the end to test what copying each kind means.
A practical guide to the eight primitive types
Use this reference to choose a starting point, not as a rule to always choose the smallest type. int is usually clearer for a simple counter than byte. long is useful when a whole-number total can exceed int. byte and short matter when another format or API requires them, but they do not automatically make every calculation smaller or faster.
The bit widths below describe the primitive numeric representations. They do not tell you the total memory cost of an object, a wrapper, or a whole application. boolean has two logical values, but Java does not promise a single universal storage size for every boolean variable. Avoid claiming that all booleans take exactly one bit or one byte.
For a signed integer type, the negative range extends one value further than the positive range. For floating-point types, distinguish range from precision: being able to represent a very large magnitude does not mean being able to preserve every digit within it. We will experiment with both integer bounds and approximate fractions below.
byte8-bit signed integer: -128 to 127.
A compact integer representation when a file format or protocol calls for it. Usually not the first choice for an ordinary counter.
short16-bit signed integer: -32,768 to 32,767.
A bounded integer representation for specific data formats. Ordinary small-number arithmetic often still produces int.
int32-bit signed integer: -2,147,483,648 to 2,147,483,647.
A practical default for ordinary whole-number counts that fit its range. It can hold negative values even if your business rule forbids them.
long64-bit signed integer: -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807.
Larger whole-number counts or totals. Use uppercase L when an integer literal needs to be long.
float32-bit binary floating point, typically about 6–7 significant decimal digits of precision.
Approximate measurements when an API or storage format specifically needs float. A decimal float literal usually needs f.
double64-bit binary floating point, typically about 15–16 significant decimal digits of precision.
A usual choice for approximate decimal measurements. More precision than float does not make every decimal fraction exact.
charOne unsigned 16-bit UTF-16 code unit: 0 to 65,535.
A single code unit, written with single quotes such as 'B'. It cannot by itself represent every visible character or emoji.
booleanExactly two values: true and false. Java does not specify a universal storage size for a boolean variable.
A yes/no fact. Neither 0, 1, nor the text "true" is a boolean value.
Whole numbers: choose a range and write the literal correctly
byte, short, int, and long represent whole numbers within fixed ranges. A small constant such as 3 can initialize byte if it fits. A value such as 128 cannot initialize byte without a narrowing conversion, because byte ends at 127. Do not add a cast merely to hide a range error; first decide whether a wider type better represents the data.
An ordinary decimal integer literal without a suffix is treated as int and must fit the applicable literal rules. The positive literal 3_000_000_000 does not fit int, so use 3_000_000_000L for this long count. Uppercase L is easier to distinguish from the digit 1 than lowercase l. The variable's long declaration does not itself turn an oversized unsuffixed decimal literal into a long literal.
Underscores between digits make long numeric literals easier to read; they do not change the value or appear when it is printed. Notice 50_000 printing as 50000. Use quoted text for an identifier with leading zeroes. An integer literal starting with 0 can be interpreted as octal, so writing an unquoted parcel code is not a safe way to preserve its digits.
Main.java · Small storage types and a large count
Predict the result, edit Main.java, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Retries: 3
Shelf: 1200
Parcels: 50000
Visits: 3000000000
An integer operation can overflow without an exception
The value 2,000,000,000 fits int, but twice that value does not. In the example, both operands of dailyCount * 2 are int, so Java performs int multiplication. The result wraps within the 32-bit integer representation and becomes negative. Ordinary integer multiplication does not automatically throw an exception when it overflows.
Assigning that result to a long afterward does not recover the lost information. The second calculation still multiplies two int operands first, then converts the already incorrect result to long. The destination is wider, but it receives the wrong result. This is why you must reason about the expression's types, not only the declared variable on the left.
Using 2L makes an operand long before multiplication, so that operation uses long arithmetic and the result 4,000,000,000 fits. This is not unlimited arithmetic: long has its own bounds. A real system must choose suitable ranges and validation or checked arithmetic. For this exercise, change dailyCount to 1,000,000,000 and predict why all three results now agree.
Main.java · A long destination cannot repair int arithmetic
Predict the result, edit Main.java, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
int result: -294967296
long after int calculation: -294967296
long calculation: 4000000000
Check the smaller-count prediction
With dailyCount set to 1,000,000,000, all three outputs are 2000000000 because that result fits int. With the original count, only the expression containing 2L gives 4000000000. Testing only small values can hide the range problem.
Decimals: more precision is not the same as exact decimal arithmetic
A decimal literal such as 1.25 is normally double. To initialize a float directly with a floating-point literal, use a suffix such as 1.25f. double offers more precision and a larger magnitude range than float, but both use binary floating-point representation. They are useful for approximate measurements, not a promise that every decimal fraction is exact.
The raw sum of 0.1 and 0.2 illustrates the difference: it prints as 0.30000000000000004. The inputs and result are represented by nearby binary floating-point values. This does not mean every decimal is inaccurate: 1.25 is exactly representable in binary. It means you must understand the representation rather than assume every printed decimal behaves like exact schoolbook decimal arithmetic.
printf with two decimal places displays 0.30, but that formatting does not change the value stored in combined. For an exact small money exercise, whole cents can be appropriate; real decimal business calculations may need BigDecimal and explicit rounding policies. Those are separate design decisions. Today, keep measurements approximate and do not mistake a neat display for corrected arithmetic.
Main.java · Decimal literals and approximate arithmetic
Predict the result, edit Main.java, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Float reading: 1.25
Double weight: 1.25
Raw sum: 0.30000000000000004
Displayed sum: 0.30
A double destination does not make every calculation decimal
The division example repeats the overflow lesson in a simpler form. When both operands are integers, 5 / 2 performs integer division and produces 2. Storing that result in a double afterward produces 2.0, not 2.5. The fractional part was discarded before the assignment, so a wider destination cannot restore it.
With 2.0 as an operand, the division uses double arithmetic and produces 2.5. Read the right-hand expression first, work out its result, and only then consider how that result is assigned. This habit prevents many beginner errors in averages, percentages, and unit conversions.
You do not need to learn every conversion rule here. The next lesson is dedicated to casting and numeric conversion, including when information can be lost. For now, change both numerators from 5 to 6, predict the two outputs, then change them to 7. Explain why matching results for one input did not prove the original integer expression was always suitable.
Main.java · The expression's types decide the division
Predict the result, edit Main.java, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Integer expression in double: 2.0
Decimal expression: 2.5
Check the division predictions
For 6, both lines show 3.0. For 7, the integer expression stored in double shows 3.0, while division by 2.0 shows 3.5. A passing even-number case did not test what happens to a remainder.
char, String, and boolean answer different questions
Use boolean for a two-state fact such as insured. The literal true is a boolean; "true" is text, and 1 is an integer. Java does not treat a nonzero integer as automatically true. Later, comparisons will produce boolean results, but for this report we use an explicit true or false so the representation stays clear.
A char holds one UTF-16 code unit. A simple zone such as 'B' fits, but a visible character is not always one code unit: many emoji need two, and some visible symbols use multiple code points. Prefer String for names, messages, and identifiers. The example's rocket is written with two Unicode escapes inside a String; you do not need to memorize those escape values.
String.length() reports UTF-16 code units, not necessarily the number of user-perceived characters. The rocket String therefore has length 2. The empty String has length 0 and is still a real String object. A one-letter String such as "B" and a char such as 'B' may look alike when printed, but remain different types with different operations.
Main.java · A code unit is not the same as a text value
Predict the result, edit Main.java, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Zone: B
Parcel: 00742
Empty note length: 0
Rocket UTF-16 units: 2
References: sharing an object and representing absence
This optional example introduces StringBuilder, a mutable text object, only to make reference copying visible. new StringBuilder creates one object containing Box. Assigning label to sameLabel copies the reference, so both variables reach that same object. append changes the object's contents, and printing through either reference now shows Box A.
Contrast that with copying an int in the previous lesson: independent primitive values do not share mutable state. Also distinguish StringBuilder from String. String objects are immutable, so their text cannot be modified in place; assigning a different String to a variable changes the variable's reference, not the old String's contents. We will study classes and methods properly later.
null means a reference does not point to an object. It is not an empty String, the word "null", zero, or false, and it cannot be assigned to a primitive such as int. Printing the reference here displays null safely, but calling missingNote.length() would throw NullPointerException because no String object is available. A variable must still be initialized before a read; null is an explicitly assigned value, not a default you can assume for a local.
Main.java · Copying a reference does not clone an object
Predict the result, edit Main.java, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Original reference: Box A
Copied reference: Box A
Missing note: null
Empty note: []
Repair three literal/type mismatches
This report should print three billion visits, a 1.25 kg reading, and zone B. Run the starter and fix the three literal/type mismatches. Preserve the intended values; do not shrink the count just to make compilation succeed.
Main.java · Repair the parcel readings
Predict the result, edit Main.java, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Reveal the explained repair
The large decimal integer needs L on its literal, not just long on its variable. The float initializer needs f, because an unsuffixed floating-point literal is double. The char needs single quotes around one code unit rather than a String literal. If the reading does not specifically need float, using double is also a valid design; this exercise deliberately practices float syntax.
Main.java · Corrected parcel readings
Predict the result, edit Main.java, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Visits: 3000000000
Weight (kg): 1.25
Zone: B
Independent lab: choose types for a parcel report
Create a parcel report for a small warehouse. Choose a representation for each fact, explain your choices, then run the report. This is fixed local data, not a real shipment or payment. The larger total is deliberately chosen to reveal int overflow if you calculate it with the wrong operand types.
- Keep parcelCode as the text 00742, preserving both leading zeroes.
- Use int itemCount starting at 3, double weightKg of 1.25, char zone B, and boolean insured true.
- Use long unitValueCents of 1_500_000_000L. These invented large values test arithmetic range; do not model them as float or double.
- Calculate totalValueCents from itemCount times unitValueCents so the multiplication uses long arithmetic.
- Print the six labelled lines in the expected output using the declared variables and calculated result.
- Explain which values are primitive and which are references. Run the boundary experiments before comparing with the solution.
Write down why each type fits, then use this output contract to check your report:
Parcel: 00742
Items: 3
Weight (kg): 1.25
Zone: B
Insured: true
Total value (cents): 4500000000
Main.java · Build your parcel report
Predict the result, edit Main.java, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Show hints without revealing the solution
The code is text even though it contains digits. Use single quotes for the zone and no quotes for true. Make the unit value long before multiplication by giving its literal an L suffix. Print totalValueCents after calculating it; do not hard-code 4500000000 into the output.
Reveal the explained lab solution
Main.java · Parcel report solution
Predict the result, edit Main.java, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
parcelCode is a reference to a String; the remaining declared values use primitive types. The code's leading zeroes are text data. Multiplying an int count by the long unit value promotes the count for long multiplication, so 4,500,000,000 fits. A long total variable alone would not be enough if both multiplied operands were int. These types do not enforce nonnegative weights, positive counts, or valid zone codes; a real report needs validation too.
Test changes and boundaries
Reset between experiments. Predict the affected lines before you run each change.
- Set itemCount to 2: total value should become 3000000000, still beyond the positive int range.
- Set itemCount to 0: total value should be 0. Decide separately whether an empty parcel is valid; Java's type system does not make that decision.
- Change insured to false: only the insurance line should change.
- Change the parcel code to 00009: all five digits must remain in the output.
- Set itemCount to -1: total becomes -1500000000. This compiles but violates the intended parcel model; explain why type checking alone is insufficient.
Common mistakes and what to inspect
First identify whether compilation failed, a running program threw an exception, or a successful calculation gave an unsuitable result. A type mismatch, a null method call, and numeric overflow need different fixes.
integer number too large
Check the numeric literal itself. A large positive decimal count may need L even when the receiving variable is long. If the intended value exceeds long too, a different representation is required; adding more L characters is not a fix.
possible lossy conversion from double to float
A decimal literal without f is normally double. For an intentionally float value, write an f-suffixed literal; otherwise consider declaring double. Do not assume an implicit narrowing conversion will be safe because the example's value looks small.
String cannot be converted to char or boolean
Check the quotes and intended type. Use 'B' for a simple char, "B" for String, and true without quotes for boolean. Names and identifiers containing several characters belong in String, not char.
A negative total appears from positive inputs
Inspect the intermediate arithmetic types and bounds. int multiplication can overflow before a result reaches a long variable. Make the operation sufficiently wide before it executes, then check that even that wider range fits your expected data.
A fraction disappears or extra decimal digits appear
Integer division discards a fractional part; binary floating point can approximate a decimal value. These are different causes. Read the operands, inspect the raw result, and choose a representation deliberately before trying to tidy the output format.
NullPointerException
A reference used for a method call may be null. An empty String has a length; null has no String object on which to call length. Reproduce the missing-value case and establish an appropriate absence-handling rule instead of replacing every missing value with arbitrary text.
Frequently asked questions
Should I always use long instead of int?
No. Choose a type that fits the possible values and the APIs involved. int is a clear ordinary counter type; long supports larger totals but still has bounds. Also inspect intermediate arithmetic, because a wider destination alone does not widen an earlier int operation.
Is double exact because it has more bits than float?
No. It provides more precision, but many decimal fractions still require approximation in binary. Formatting to two decimal places changes the display, not the stored value. Exact decimal requirements need an appropriate representation and rounding policy.
Does Java have unsigned int?
The ordinary byte, short, int, and long types are signed. char is an unsigned 16-bit code unit, not a general recommendation for unsigned business counts. Specialized unsigned operations exist in library APIs, but they are beyond this beginner lesson.
Can a primitive variable be null?
No. Primitive types hold their primitive values, not null. Reference types can hold null. Wrapper classes such as Integer are reference types and will be studied later; they are not the same as primitive int.
Is String.length() the number of visible characters?
Not in general. It counts UTF-16 code units. A rocket emoji uses two such units, and other visible text can combine several code points. Choose text-processing APIs according to the actual meaning you need.
Choose the meaning, then check the representation
Primitive types hold primitive values; reference types reach objects or hold null. Integer types have bounds, float and double approximate many fractions, and char is one UTF-16 code unit rather than a promise to hold any visible character.
Before moving on, explain why a parcel identifier is String, why the large total needs long arithmetic, and why a formatted decimal is not necessarily exact. Next is Java Type Casting and Numeric Conversion, where you will examine deliberate conversions and information loss.
Can you choose and explain the type?
Answer all six questions and read the reasoning. Results are temporary on this page and do not save course progress.
References and related learning
The parcel scenario, examples, explanations, and exercises are original SovranCode material. These official references support the language and API rules.
- dev.java: primitive types
- Java 21 specification: types, values, and variables
- Java 21 specification: integer and floating-point literals
- Java 21 String API: UTF-16 representation and length
Review variables and constants · Review output and formatting · Java course roadmap.
Last updated: