ZetCode

Java 类型转换

上次修改时间:2025 年 5 月 7 日

Java 中的类型转换(type casting)指的是将一个数据类型的值更改为另一个数据类型。 当您想以不同的格式使用数据、将其分配给另一种类型的变量或将其传递给期望不同类型的方法时,这是必需的。 Java 支持多种用于原始类型和对象类型的转换方法。

理解类型转换可以帮助您避免 Java 程序中的运行时错误和数据丢失。

原始类型转换

Java 的原始类型(byteshortintlongfloatdoublecharboolean)可以相互转换。 主要有两种类型:隐式转换(扩大)和显式转换(缩小)。

隐式转换(扩大或自动类型转换)

当较小的类型转换为较大的类型时,会发生扩大转换。 Java 会自动执行此操作,因为它是安全的,并且不会丢失数据。

典型的扩大转换顺序是

byte -> short -> int -> long -> float -> double

此外,char 可以扩大到 intlongfloatdouble

WideningConversionDemo.java
package com.zetcode;

public class WideningConversionDemo {

    public static void main(String[] args) {

        int myInt = 100;
        long myLong = myInt;      // int to long (implicit)
        float myFloat = myLong;   // long to float (implicit)
        double myDouble = myFloat; // float to double (implicit)

        System.out.println("Original int: " + myInt);
        System.out.println("Widened to long: " + myLong);
        System.out.println("Widened to float: " + myFloat);
        System.out.println("Widened to double: " + myDouble);

        char myChar = 'A';
        int charToInt = myChar;   // char to int (implicit)
        System.out.println("Original char: " + myChar);
        System.out.println("Widened char to int: " + charToInt); // Prints 65
    }
}

在此示例中,值会自动提升为更大的类型,而无需显式强制转换。

显式转换(缩小或手动类型转换)

当较大的类型转换为较小的类型时,会发生缩小转换。 这必须使用强制转换运算符 (targetType) 显式完成,因为它可能导致数据或精度损失。

典型的缩小转换顺序与扩大相反

double -> float -> long -> int -> short -> char -> byte
NarrowingConversionDemo.java
package com.zetcode;

public class NarrowingConversionDemo {

    public static void main(String[] args) {

        double myDouble = 123.456;
        float myFloat = (float) myDouble;   // double to float (explicit)
        long myLong = (long) myFloat;     // float to long (explicit, fractional part lost)
        int myInt = (int) myLong;         // long to int (explicit)
        short myShort = (short) myInt;    // int to short (explicit, potential overflow)
        byte myByte = (byte) myShort;     // short to byte (explicit, potential overflow)
        char myChar = (char) myInt;       // int to char (explicit, takes lower 16 bits)


        System.out.println("Original double: " + myDouble);
        System.out.println("Narrowed to float: " + myFloat);
        System.out.println("Narrowed to long: " + myLong);
        System.out.println("Narrowed to int: " + myInt);
        System.out.println("Narrowed to short: " + myShort);
        System.out.println("Narrowed to byte: " + myByte);
        System.out.println("Narrowed int to char: " + myChar);

        // Example of data loss due to overflow
        int largeInt = 300;
        byte smallByte = (byte) largeInt; // 300 is 100101100 in binary. Byte takes last 8 bits.
                                          // 300 % 256 = 44.
        System.out.println("Original large int: " + largeInt);
        System.out.println("Narrowed large int to byte: " + smallByte); // Output: 44

        double preciseDouble = 99.99;
        int truncatedInt = (int) preciseDouble;
        System.out.println("Original precise double: " + preciseDouble);
        System.out.println("Narrowed to int (truncation): " + truncatedInt); // Output: 99
    }
}

缩小转换时,如果该值超出目标类型的范围,则可能会丢失高位。 对于浮点数到整数的转换,小数部分将被简单地删除。

表达式中的类型提升

Java 在表达式中将较小的类型提升为较大的类型,以防止计算过程中出现数据丢失。 规则是

ExpressionPromotionDemo.java
package com.zetcode;

public class ExpressionPromotionDemo {

    public static void main(String[] args) {

        byte b1 = 10;
        byte b2 = 20;
        // byte resultByte = b1 + b2; // Error: b1 + b2 results in an int
        int resultInt = b1 + b2;    // Correct: byte + byte -> int
        byte resultByteCasted = (byte) (b1 + b2); // Explicit cast needed for byte result

        System.out.println("b1 + b2 (as int): " + resultInt);
        System.out.println("b1 + b2 (casted to byte): " + resultByteCasted);

        short s = 5;
        float f = 10.5f;
        // short shortResult = s + f; // Error: s + f results in a float
        float floatResult = s + f;  // Correct: short + float -> float
        System.out.println("s + f (as float): " + floatResult);

        int i = 100;
        double d = 20.75;
        // int intResultSum = i + d; // Error: i + d results in double
        double doubleResult = i + d; // Correct: int + double -> double
        System.out.println("i + d (as double): " + doubleResult);
    }
}

对象类型转换(Casting)

对于对象类型(类和接口),强制转换允许您将对象视为其继承树中的另一种类型。

向上转型(隐式)

向上转型意味着将子类对象视为其超类。 这始终是安全且自动的,因为子类是其超类的一种。

ObjectCastingDemo.java
package com.zetcode.casting;

class Animal {
    void makeSound() {
        System.out.println("Generic animal sound");
    }
}

class Dog extends Animal {
    @Override
    void makeSound() {
        System.out.println("Woof woof");
    }

    void fetch() {
        System.out.println("Dog is fetching.");
    }
}

class Cat extends Animal {
    @Override
    void makeSound() {
        System.out.println("Meow");
    }

    void scratch() {
        System.out.println("Cat is scratching.");
    }
}

public class ObjectCastingDemo {

    public static void main(String[] args) {

        // Upcasting
        Dog myDog = new Dog();
        Animal myAnimalFromDog = myDog; // Implicit upcasting (Dog to Animal)

        Cat myCat = new Cat();
        Animal myAnimalFromCat = myCat; // Implicit upcasting (Cat to Animal)

        System.out.print("myAnimalFromDog (originally Dog) says: ");
        myAnimalFromDog.makeSound(); // Calls Dog's overridden makeSound()

        System.out.print("myAnimalFromCat (originally Cat) says: ");
        myAnimalFromCat.makeSound(); // Calls Cat's overridden makeSound()

        // myAnimalFromDog.fetch(); // Compile error: Animal type doesn't have fetch() method
                                 // The reference type determines accessible methods.

        // Using an array of superclass type
        Animal[] animals = new Animal[2];
        animals[0] = new Dog(); // Upcasting
        animals[1] = new Cat(); // Upcasting

        for (Animal animal : animals) {
            animal.makeSound(); // Polymorphism in action
        }
    }
}

向上转型时,您只能使用超类中的方法和字段(或子类覆盖的方法和字段)。 子类特定的方法无法通过超类引用访问。

向下转型(显式)

向下转型意味着将超类对象视为子类。 这是有风险的,必须是显式的,通常在使用 instanceof 检查后进行,以避免 ClassCastException

ObjectDowncastingDemo.java
package com.zetcode.casting; // Assuming Animal, Dog, Cat are in the same package or imported

public class ObjectDowncastingDemo {

    public static void main(String[] args) {
        Animal myAnimal = new Dog(); // Upcasted to Animal, actual object is Dog
        // Animal anotherAnimal = new Animal(); // Actual object is Animal

        // Attempting to downcast myAnimal to Dog
        if (myAnimal instanceof Dog) {
            Dog myDog = (Dog) myAnimal; // Explicit downcasting
            System.out.print("Downcasted Dog object says: ");
            myDog.makeSound(); // Calls Dog's makeSound()
            myDog.fetch();     // Now fetch() is accessible
        } else {
            System.out.println("Cannot downcast myAnimal to Dog.");
        }

        Animal generalAnimal = new Animal();
         // Attempting to downcast generalAnimal to Dog (will fail if not checked)
        if (generalAnimal instanceof Dog) {
            Dog specificDog = (Dog) generalAnimal; // This line won't be reached
            specificDog.fetch();
        } else {
            System.out.println("generalAnimal is not an instance of Dog. Cannot downcast.");
        }

        // Example leading to ClassCastException
        Animal possiblyCat = new Cat();
        // Dog notADog = (Dog) possiblyCat; // This would throw ClassCastException if executed
                                        // because a Cat is not a Dog.

        try {
            Animal anAnimal = new Cat(); // Actual object is Cat
            Dog aDog = (Dog) anAnimal;   // Attempting to cast Cat to Dog
            aDog.fetch();
        } catch (ClassCastException e) {
            System.err.println("Error during downcasting: " + e.getMessage());
        }
    }
}

instanceof 运算符检查对象是否为特定类型或其子类。 始终在向下转型之前使用 instanceof 以避免错误。

涉及字符串的转换

字符串是 Java 中的对象。 在字符串和原始类型或其他对象之间进行转换是很常见的。

原始类型转换为字符串

字符串转换为原始类型

这通常通过包装类的 parseXxx 方法完成(例如,Integer.parseInt(String))。 如果字符串无效,则会抛出 NumberFormatException

对象转换为字符串

StringConversionDemo.java
package com.zetcode;

import java.util.Date;

public class StringConversionDemo {

    public static void main(String[] args) {

        // Primitive to String
        int num = 123;
        String strNum1 = String.valueOf(num);
        String strNum2 = Integer.toString(num);
        String strNum3 = "" + num;
        System.out.println("Primitive to String: " + strNum1 + ", " + strNum2 + ", " + strNum3);

        double val = 45.67;
        String strVal = String.valueOf(val);
        System.out.println("Double to String: " + strVal);

        // String to Primitive
        String strInt = "100";
        String strDouble = "200.50";
        String strInvalid = "abc";

        try {
            int parsedInt = Integer.parseInt(strInt);
            double parsedDouble = Double.parseDouble(strDouble);
            System.out.println("String to int: " + parsedInt);
            System.out.println("String to double: " + parsedDouble);

            // int invalidParse = Integer.parseInt(strInvalid); // This would throw NumberFormatException
        } catch (NumberFormatException e) {
            System.err.println("Error parsing string: " + e.getMessage());
        }

        // Boolean parsing
        String trueStr = "true";
        String falseStr = "FalSe"; // Case-insensitive for "true"
        boolean bTrue = Boolean.parseBoolean(trueStr); // true
        boolean bFalse = Boolean.parseBoolean(falseStr); // false (only "true" ignoring case is true)
        System.out.println("String 'true' to boolean: " + bTrue);
        System.out.println("String 'FalSe' to boolean: " + bFalse);


        // Object to String
        Date today = new Date();
        String dateStr = today.toString(); // Uses Date's overridden toString()
        System.out.println("Date object to String: " + dateStr);

        Object nullObj = null;
        String nullStr = String.valueOf(nullObj); // Safely handles null, returns "null"
        System.out.println("String.valueOf(nullObj): " + nullStr);
        // String problematicNullStr = nullObj.toString(); // This would throw NullPointerException!
    }
}

自动装箱和拆箱

自动装箱是指 Java 自动将原始类型转换为其包装类(例如,int 转换为 Integer)。 拆箱是相反的。 这使得代码更简单、更易读。

此功能使您可以编写 Integer myIntegerObject = 100; 而不是 Integer myIntegerObject = new Integer(100);(构造函数已弃用;请改用 valueOf)。

AutoboxingUnboxingDemo.java
package com.zetcode;

import java.util.ArrayList;
import java.util.List;

public class AutoboxingUnboxingDemo {

    public static void main(String[] args) {

        // Autoboxing: primitive int to Integer object
        Integer integerObject = 100; // Compiler converts to Integer.valueOf(100)
        System.out.println("Autoboxed Integer object: " + integerObject);

        // Unboxing: Integer object to primitive int
        int primitiveInt = integerObject; // Compiler converts to integerObject.intValue()
        System.out.println("Unboxed primitive int: " + primitiveInt);

        // Example in collections
        List<Integer> numberList = new ArrayList<>();
        numberList.add(10); // Autoboxing: 10 (int) becomes Integer.valueOf(10)
        numberList.add(20);

        int firstElement = numberList.get(0); // Unboxing: Integer object from list to int
        System.out.println("First element from list (unboxed): " + firstElement);

        // Pitfall: NullPointerException during unboxing
        Integer nullInteger = null;
        try {
            // int problematicInt = nullInteger; // This would throw NullPointerException
                                               // as it tries to call nullInteger.intValue()
            if (nullInteger != null) { // Always check for null before unboxing
                 int safeInt = nullInteger;
                 System.out.println("Safely unboxed: " + safeInt);
            } else {
                System.out.println("Cannot unbox a null Integer object.");
            }
        } catch (NullPointerException e) {
            System.err.println("Error during unboxing: " + e.getMessage());
        }
    }
}

虽然方便,但在对性能至关重要的代码中,自动装箱/拆箱会创建不必要的对象。 拆箱 null 包装器对象会导致 NullPointerException

关键转换和潜在问题摘要

转换类型 描述 机制 潜在问题
原始类型扩大 较小到更大的数字类型(例如,int 到 long) 隐式(自动) 无(安全)
原始类型缩小 较大到较小的数字类型(例如,double 到 int) 显式强制转换 (type)value 数据丢失、精度丢失、溢出
对象向上转型 子类到超类/接口 隐式(自动) 无(安全,但会通过超类引用失去对子类特定成员的访问权限)
对象向下转型 超类/接口到子类 显式强制转换 (SubclassType)object 如果对象不是目标类型的实例,则会抛出 ClassCastException
原始类型转换为字符串 例如,int 到 String String.valueOfWrapper.toString"" + primitive 通常安全
字符串转换为原始类型 例如,String 到 int Wrapper.parseXxx 如果字符串格式无效,则会抛出 NumberFormatException
对象转换为字符串 任何对象到 String obj.toStringString.valueOf(obj) 如果在 null 上调用 obj.toString,则会抛出 NullPointerExceptionString.valueOf 可以安全地处理 null)
自动装箱 原始类型到包装器(例如,int 到 Integer) 隐式(自动) 如果不小心,循环中会产生性能开销
拆箱 包装器到原始类型(例如,Integer 到 int) 隐式(自动) 如果包装器对象为 null,则会抛出 NullPointerException

类型转换的最佳实践

来源

官方 Java 原始数据类型和转换

Java 的类型转换系统非常灵活,允许数据在原始类型和对象类型之间移动。 隐式转换是无缝的,但显式转换需要小心,以避免数据丢失或运行时错误。 了解规则和陷阱可以帮助您编写更安全、更可靠的 Java 代码。

作者

我的名字是 Jan Bodnar。 我是一名拥有多年经验的程序员。 自 2007 年以来,我已经撰写了 1,400 多篇文章和 8 本电子书。 凭借超过八年的教学经验,我喜欢分享知识并帮助他人学习编程。

列出所有Java教程