← 返回首页
Java学习

4.Java对象方法、包装类与接口

2026-09-19·6 次浏览
4.Java对象方法、包装类与接口

Java 对象方法、包装类与接口

对象已经会创建和使用了。接下来看看怎样打印和比较对象,再认识包装类,最后用工厂换设备的例子理解接口。

1. Object 与 toString()

Object 是普通 Java 类的直接或间接父类。toString() 用来把对象表示成文字。

直接打印对象时,希望看到它的编号和名称,而不是一串不直观的默认文字。

在自己的类里重写 toString()。

java 复制代码
public class Product {
    private int id;
    private String name;

    public Product(int id, String name) {
        this.id = id;
        this.name = name;
    }

    // 打印对象时,使用这个方法返回的文字。
    @Override
    public String toString() {
        return "Product{id=" + id + ", name='" + name + "'}";
    }
}

接着调用:

java 复制代码
Product product = new Product(1, "手机");
System.out.println(product); // Product{id=1, name='手机'}

println 会使用对象的 toString()。默认格式中的 @ 后面是哈希码的十六进制形式,不是 Java 保证的内存地址。

2. ==、equals()、hashCode()

对对象来说,== 判断是不是同一个对象;equals() 判断是否按某种规则相等;hashCode() 提供一个供哈希集合使用的整数。

比如两次拿到的商品名称都是“苹果”。它们可以是两个不同的字符串对象,但文字内容相同。

先用已经实现好内容比较的 String 看清这三个方法,不急着自己写比较规则:

java 复制代码
// 特意创建两个对象,观察“同一个对象”和“内容相同”的区别。
String first = new String("苹果");
String second = new String("苹果");
System.out.println(first == second); // false,不是同一个对象
System.out.println(first.equals(second)); // true,内容相同
System.out.println(first.hashCode() == second.hashCode()); // true

这里特意用 new 创建两个对象,好让“同一个对象”和“内容相同”区别开来。

必须记住:相等的对象,哈希码必须相同;哈希码相同,对象不一定相等。

如果不重写,Object.equals() 默认也是判断是否同一个对象。字符串内容比较通常使用 equals()。

补充:自己的商品类怎样比较

前面的字符串已经有自己的比较规则。我们定义商品类时,也可以约定“编号相同就相等”。这时一起重写 equals() 和 hashCode():

java 复制代码
public final class Product {
    private final int id;

    public Product(int id) {
        this.id = id;
    }

    @Override
    public boolean equals(Object object) {
        // 先确认是商品对象,null 或其他类型都不能按商品编号比较。
        if (!(object instanceof Product)) {
            return false;
        }
        // 这个例子约定:编号相同,就认为是相等的商品。
        return id == ((Product) object).id;
    }

    @Override
    public int hashCode() {
        // 哈希码也按编号计算,保证相等商品的哈希码相同。
        return Integer.hashCode(id);
    }
}

接着调用:

java 复制代码
Product first = new Product(1);
Product second = new Product(1);

System.out.println(first == second); // false
System.out.println(first.equals(second)); // true
System.out.println(first.hashCode() == second.hashCode()); // true

3. 包装类、装箱和拆箱

包装类让基本类型的值以对象形式使用。基本类型转成包装对象叫装箱,反过来叫拆箱。

集合等 API 使用对象类型,例如 List<Integer>,不能写成 List<int>。

Java 可以自动完成常见的装箱、拆箱。

基本类型 包装类
byte Byte
short Short
int Integer
long Long
float Float
double Double
char Character
boolean Boolean
java 复制代码
Integer object = 10; // 装箱
int number = object; // 拆箱
System.out.println(number); // 10

Integer first = 200;
Integer second = 200;
// 比较包装对象的数值,不依赖 == 的缓存行为。
System.out.println(first.equals(second)); // true

包装对象比较数值时用 equals(),不要依赖 ==。部分值会被缓存,这容易让 == 的结果看起来时对时错。

包装对象可以是 null,但 null 不能拆成一个基本类型值。拆箱前要确认它不为空。

4. 字符串与数字转换

把文字形式的数字转成数值,或者把数值转成文字。

用户输入、配置文件中拿到的经常是字符串,计算前要先转换。

整数用 Integer.parseInt(),小数用 Double.parseDouble()。

java 复制代码
// 字符串不能直接参与年龄计算,先转成整数。
int age = Integer.parseInt("18");
double price = Double.parseDouble("19.9");
String text = String.valueOf(age);

System.out.println(age + 1); // 19
System.out.println(price);  // 19.9
System.out.println(text);   // 18

"abc" 不能转成整数,会抛出 NumberFormatException。这种情况可以用异常机制处理。

5. 接口与面向接口编程

接口规定“能够做什么”,实现类负责“具体怎样做”。

还是看工厂换设备的例子。原来工厂只生产产品 A,用的是 EquipmentA;后来接到产品 B 的订单,要换成 EquipmentB。如果工厂的字段写死为 EquipmentA,换设备就得修改工厂代码。

我们想保留工厂的生产流程,只更换设备。做法是:两种设备实现同一个接口,工厂通过这个接口调用设备。

第一步,定义设备接口。这里的 work() 表示“设备可以工作”:

java 复制代码
public interface Equipment {
    // 接口只规定设备能够工作,具体生产什么由设备类决定。
    void work();
}

第二步,让设备 A 和设备 B 分别实现它:

java 复制代码
public class EquipmentA implements Equipment {
    @Override
    public void work() {
        System.out.println("设备A运行,生产产品A");
    }
}
java 复制代码
public class EquipmentB implements Equipment {
    @Override
    public void work() {
        System.out.println("设备B运行,生产产品B");
    }
}

第三步,工厂的字段使用 Equipment,通过 setEquipment() 安装或更换设备:

java 复制代码
public class Factory {
    // 字段使用接口类型,既能放设备 A,也能放设备 B。
    private Equipment equipment;

    public void setEquipment(Equipment equipment) {
        this.equipment = equipment;
    }

    public void work() {
        System.out.println("开始生产.....");
        // 调用前先安装设备,实际执行当前设备自己的 work()。
        equipment.work();
    }
}

现在让同一个工厂先用设备 A,再换成设备 B:

java 复制代码
Factory factory = new Factory();
factory.setEquipment(new EquipmentA());
factory.work();
// 同一个工厂换设备,Factory.work() 的代码不用改。
factory.setEquipment(new EquipmentB());
factory.work();

输出:

text 复制代码
开始生产.....
设备A运行,生产产品A
开始生产.....
设备B运行,生产产品B

Factory.work() 一直是同一段代码,实际工作的设备却可以更换。这就是“面向接口编程”:工厂只要求设备会 work(),不把具体设备类型写死。

本例使用前要先装好设备,也就是先调用 setEquipment();否则字段还是 null,无法调用它的 work()。接口本身不能直接 new,要创建它的实现类。一个类可以实现多个接口,接口也可以继承其他接口。

补充:以后再看
  • equals() 要满足与自身相等、相互比较对称等规则;不要让结果随着无关操作随意改变。
  • 放入 HashSet 或作为 HashMap 的键后,不要修改参与相等、哈希计算的字段。
  • Objects.equals(a, b) 可以安全比较可能为空的对象。
  • 接口字段隐式是 public static final;普通抽象方法隐式是 public abstract。
  • Java 8 起接口可有默认方法、静态方法;Java 9 起可有私有辅助方法。
  • native 表示方法实现由本地代码提供,不需要把它用于普通 Java 学习示例。
  • 只有一个抽象方法的接口可以学习 Lambda 写法,但先理解上面的普通实现类。

Object API · Integer API · 接口规范

── 完 ──

评论

还没有评论,来说点什么
发表评论
0 / 1000