在这里插入图片描述

◆ 博主名称: 小此方-CSDN博客
大家好,欢迎来到小此方的博客。
⭐️Linux系列个人专栏: 【主题曲】Linux
⭐️此方的GitHub: github_此方
⭐️ Re系列专栏:我们思考 (Rethink) · 我们重建 (Rebuild) · 我们记录 (Record)


概要&序論

  Hello,大家好我是此方,如果你是一个熟练的 C++ 程序员,现在因为项目需要或者个人兴趣想要快速上手 Java,那么这篇文章就是为你量身定做的。本文将从 C++ 程序员的视角 出发,对比两门语言的异同,帮助你用三天时间建立起 Java 面向对象编程的核心知识框架。废话不多说,我们直接开始吧。

一、从 C++ 到 Java:先打破你的惯性思维

1.1 Java 不是"简化版 C++"

  很多 C++ 程序员第一次接触 Java 时会觉得"这不就是少了指针的 C++ 吗?" —— 这个想法只对了一半。Java 确实从 C++ 身上借鉴了大量语法,但它的设计哲学有本质不同:

C++ 的哲学:信任程序员,给你所有工具(指针、多继承、运算符重载、手动内存管理),你自己负责不出错。
Java 的哲学:限制危险操作,用虚拟机 + 垃圾回收 + 强类型约束来兜底,让程序"不容易写坏"。

  理解这个差异,你就明白了为什么 Java 没有指针、没有析构函数、没有操作符重载、没有多继承 —— 不是做不了,是故意不给

1.2 环境差异速览

维度 C++ Java
编译方式 源码 → 平台相关的机器码 源码 → 字节码(.class) → JVM 解释/即时编译
内存管理 new / delete 手动管理 new 分配,GC 自动回收
跨平台 一份源码,各平台分别编译 一份字节码,到处运行(Write Once, Run Anywhere)
指针 原生支持,可做指针运算 没有指针语法,对象引用本质上是指针但被包装了
多继承 支持 不支持,用接口(interface)弥补

1.3 你的第一个 Java 程序

  来看一段代码,C++ 程序员一眼就能看懂 90%:

// HelloWorld.java —— 文件名必须和 public 类名一致!
public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello, Java!");
    }
}

  三个你需要注意的点

  1. 文件名 = public 类名:如果文件里有 public class HelloWorld,文件名必须是 HelloWorld.java,否则编译报错。
  2. main 函数签名是固定的public static void main(String[] args) —— 必须是 public、static、void,参数是 String 数组。
  3. 没有头文件:Java 用 import 代替 #include,不需要写头文件,也不存在"声明与实现分离"这回事。

二、数据类型:C++ 程序员最容易踩的坑

2.1 基本类型与包装类

  Java 的基本类型和 C++ 很像,但有一个重要区别:Java 中每种基本类型都有一个对应的"包装类"

基本类型 大小 包装类 默认值
byte 1 字节 Byte 0
short 2 字节 Short 0
int 4 字节 Integer 0
long 8 字节 Long 0L
float 4 字节 Float 0.0f
double 8 字节 Double 0.0d
char 2 字节(Unicode) Character `’

  核心差异

  • C++ 的 char 是 1 字节(ASCII),Java 的 char2 字节(Unicode),所以 Java 的 char 可以直接存中文。
  • Java 没有 unsigned,所有数值类型都是有符号的。
  • boolean 不能和整数互转—— C++ 里 if(1) 合法,Java 里 if(1) 直接编译报错。

2.2 自动装箱与拆箱

  基本类型和包装类之间可以自动转换,这叫做自动装箱(Autoboxing)和拆箱(Unboxing)

Integer a = 10;       // 自动装箱:int → Integer
int b = a;            // 自动拆箱:Integer → int

// 但要注意这个坑!
Integer x = 128;
Integer y = 128;
System.out.println(x == y);  // false!== 比较的是引用地址,不是值

// 正确做法
System.out.println(x.equals(y));  // true

  提醒 C++ 程序员:Java 里 == 对引用类型比较的是引用地址(类似 C++ 比较指针),比较对象内容要用 .equals()

三、类与对象:似曾相识却又大不相同

3.1 类定义的语法基本一致

  如果你熟悉 C++ 的 class,Java 的 class 写法基本不需要重新学习。来看一个对比:

C++ 版本:

class Student {
private:
    std::string name;
    int age;
public:
    Student(std::string name, int age) {
        this->name = name;
        this->age = age;
    }
    void study() {
        std::cout << name << " is studying" << std::endl;
    }
};

Java 版本:

public class Student {
    private String name;  // String 是类,首字母大写
    private int age;

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void study() {
        System.out.println(name + " is studying");
    }
}

  结构几乎一模一样,差别在于 Java 中每个成员都要加访问修饰符(private / public / protected),不像 C++ 可以用 public: 块来统一声明。

3.2 构造函数与 this

  构造函数写法和 C++ 基本一致,但 Java 多了一个实用特性:构造函数之间可以用 this() 互相调用

public class Student {
    private String name;
    private int age;

    // 无参构造
    public Student() {
        this("unknown", 0);  // 调用另一个构造器,必须是第一行
    }

    // 全参构造
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

  C++ 没有这种语法,你只能靠默认参数或者提取公共初始化函数来复用构造函数逻辑。

3.3 访问修饰符

修饰符 C++ 含义 Java 含义
private 只能本类访问 只能本类访问
默认(不写) class 内默认 private 包级可见(同包内可访问)
protected 本类 + 派生类可访问 本类 + 同包 + 子类可访问
public 完全公开 完全公开

  特别注意:Java 的 protected 比 C++ 更"大方"——它不仅对子类开放,还对本包所有类开放。另外 Java 多了一个包级访问权限(默认),这在 C++ 中没有对应概念。

四、继承与多态:最大的分歧点

4.1 Java 是单继承 + 接口

  这是 C++ 转 Java 时最大的思维转变。Java 不允许多继承,一个类只能有一个父类:

// C++ 可以(Java 不行)
// class Child : public Father, public Mother { };

// Java 的做法:extends 一个父类 + implements 多个接口
public class Child extends Father implements MotherBehavior, Skill {
    // 只能 extends 一个类
    // 可以 implements 多个接口
}

  为什么不支持多继承?Java 的设计者认为 C++ 的多继承带来了菱形继承虚基类等复杂问题。Java 用接口 (interface) 来弥补这个缺口。

4.2 方法重写与 super

  在 Java 中,所有非 final 方法默认就是虚函数,不需要像 C++ 那样写 virtual 关键字:

// 父类
public class Animal {
    public void speak() {
        System.out.println("Animal speaks");
    }
}

// 子类
public class Dog extends Animal {
    @Override   // 注解,标记为重写(非必须但强烈推荐)
    public void speak() {
        super.speak();  // 先调用父类方法
        System.out.println("Dog barks");
    }
}

  关键点

  • @Override 注解:不写也能运行,但写了可以让编译器帮你检查是否真的重写了父类方法(避免拼写错误)。
  • super 关键字:相当于 C++ 的 父类名::,用来调用父类的构造器或方法。
  • Java 中所有方法默认就是 virtual,如果你想禁止重写,才需要加 final

4.3 抽象类与接口

  这是 Java 多态体系的核心,也是 C++ 程序员最需要适应的地方:

抽象类 (abstract class):和 C++ 的抽象类基本一致,用 abstract 关键字标记。

public abstract class Shape {
    protected String color;

    public Shape(String color) {
        this.color = color;
    }

    // 抽象方法——没有方法体,子类必须实现
    public abstract double getArea();

    // 普通方法——有方法体,子类可以选择重写
    public void printColor() {
        System.out.println("Color: " + color);
    }
}

接口 (interface):Java 的核心武器,C++ 中没有直接对应物。可以把接口看作纯抽象类的终极形态

public interface Flyable {
    void fly();                    // 默认是 public abstract
    default void land() {          // Java 8+:可以有默认实现
        System.out.println("Landing...");
    }
    static void description() {    // Java 8+:可以有静态方法
        System.out.println("Things that can fly");
    }
}

// 一个类可以实现多个接口
public class Bird extends Animal implements Flyable, Singable {
    @Override
    public void fly() {
        System.out.println("Bird is flying");
    }
}

  接口 vs 抽象类的选择口诀

  • "是什么"用抽象类:Dog is an Animal → 继承
  • "能做什么"用接口:Bird can Fly → 接口实现

五、内存管理与垃圾回收

5.1 new 还在,delete 没了

  C++ 程序员的肌肉记忆里一定有 newdelete 的配对。到了 Java,new 依然在,但**delete 彻底消失了**:

// Java 中创建对象
Student s = new Student("此方", 20);
// 没有 delete s; —— JVM 的 GC 会帮你回收
// 你只需要把引用置为 null(或者等变量离开作用域),GC 会在合适的时候回收
s = null;  // 暗示 GC:这个对象我不要了

  Java 没有析构函数。如果你需要在对象被回收时释放资源(比如关闭文件、断开连接),你需要使用 finalize() 方法(已废弃,不推荐)或 try-with-resources

5.2 引用 vs 指针

操作 C++ Java
创建对象 Student* p = new Student(); Student s = new Student();
访问成员 p->study(); s.study();
释放对象 delete p; 不写,GC 自动处理
没有对象 nullptr / NULL null
空指针调用 未定义行为 NullPointerException 异常

  Java 的引用本质上是一个被包装好的指针 —— 你能用它访问对象,但不能做指针运算,不能获取地址,也不能手动释放。这种设计牺牲了一些灵活性,换来了更高的安全性。

六、常用关键字速查

6.1 static

  Java 的 static 和 C++ 基本一致:静态成员属于类而不属于对象。但 Java 多了一个静态代码块

public class Config {
    public static String appName;

    // 静态初始化块 —— 类加载时执行一次
    static {
        appName = "MyApplication";
        System.out.println("Class loaded, appName = " + appName);
    }
}

6.2 final

  final 在 Java 中是一个非常忙碌的关键字,能修饰三种东西:

修饰对象 含义 C++ 等价物
final 变量 常量,不可修改 const
final 方法 不能被子类重写 非 virtual 方法
final 不能被继承 C++11 final
public final class String {  // String 类不能被继承
    // ...
}

public class Parent {
    public final void doSomething() {  // 子类不能重写这个方法
        // ...
    }
}

6.3 this 与 super

public class Child extends Parent {
    private int value;

    public Child(int value) {
        super();         // 调用父类无参构造器(必须是第一行)
        this.value = value;  // this 指代当前对象
    }

    public void setValue(int value) {
        this.value = value;  // this.value = 成员变量,value = 参数
    }
}

七、包、import 与命名空间

7.1 包就是命名空间

  Java 用 package 来组织代码,作用等同于 C++ 的 namespace

package com.csdn.xiaocifang.blog;    // 声明这个类在哪个包里,必须放在文件第一行

import java.util.ArrayList;          // 类似 using std::vector;
import java.util.*;                  // 类似 using namespace std;

7.2 Java 的类库组织

  Java 的标准类库用包来组织,常用的有:

用途
java.lang 核心类(String、Math、System),自动导入,不用写 import
java.util 集合类(ArrayList、HashMap)、工具类(Scanner、Date)
java.io 输入输出流
java.net 网络编程

  对比 C++:Java 导入的是而不是头文件,import 只是一个语法糖,让编译器知道你在说哪个类,并不会把代码"粘贴"到文件里。

八、异常处理:比 C++ 更严格

8.1 受检异常(Checked Exception)

  这是 Java 独有的概念。Java 的异常分两类:

  • 受检异常(Checked):编译器强制你必须处理(try-catch 或在方法签名上 throws 出去),否则编译不通过。
  • 非受检异常(Unchecked):RuntimeException 及其子类,编译器不管,运行时报错。
// 受检异常:必须处理
public void readFile(String path) throws IOException {
    FileReader reader = new FileReader(path);  // 可能抛出 IOException
    // ...
}

// 调用时要么 try-catch,要么继续 throws
try {
    readFile("/tmp/test.txt");
} catch (IOException e) {
    e.printStackTrace();
}

  C++ 程序员可能会觉得这很烦,但 Java 的哲学是:“如果一个异常是可预见的,你就必须在编译期处理它”。

8.2 try-with-resources

  这是 Java 7 引入的语法,等价于 C++ 的 RAII,用来确保资源被自动关闭:

// 传统写法(麻烦)
BufferedReader br = null;
try {
    br = new BufferedReader(new FileReader("test.txt"));
    String line = br.readLine();
} catch (IOException e) {
    e.printStackTrace();
} finally {
    if (br != null) {
        try { br.close(); } catch (IOException e) { }
    }
}

// try-with-resources(优雅)
try (BufferedReader br = new BufferedReader(new FileReader("test.txt"))) {
    String line = br.readLine();
} catch (IOException e) {
    e.printStackTrace();
}
// br 自动关闭,不用写 finally

九、集合框架:告别 STL,拥抱 Collection

9.1 常用集合对照表

数据结构 C++ STL Java
动态数组 std::vector<T> ArrayList<T>
双向链表 std::list<T> LinkedList<T>
哈希表 std::unordered_map<K,V> HashMap<K,V>
红黑树 std::map<K,V> TreeMap<K,V>
哈希集合 std::unordered_set<T> HashSet<T>
优先级队列 std::priority_queue<T> PriorityQueue<T>

9.2 泛型:和 C++ 模板形似神不似

  Java 的泛型用的是类型擦除(Type Erasure),和 C++ 模板的"代码膨胀"机制完全不同:

// Java 泛型
ArrayList<String> list = new ArrayList<>();
list.add("Hello");
String s = list.get(0);  // 不需要强制类型转换

// Java 泛型不支持基本类型!
// ArrayList<int>   // 编译错误!
ArrayList<Integer> list2 = new ArrayList<>();  // 必须用包装类
  • C++ 的 vector<int> 是合法的,Java 的 ArrayList<int> 是编译错误 —— 必须写成 ArrayList<Integer>
  • C++ 模板是编译期代码生成,Java 泛型是编译期类型检查 + 运行期擦除。这意味着 Java 的泛型在运行时看不到具体的类型信息。

好的本期内容就到这里,如果对你有帮助,还不要忘记点赞三联支持。我是此方,我们下期再见。bye! Linux、C++、算法持续连载中,欢迎关注WeChat Official Account 【此方的技术栈】。
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐