C++程序员转型Java ,3天打破惯性思维的硬核速成指南

文章目录
概要&序論
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!");
}
}
三个你需要注意的点:
- 文件名 = public 类名:如果文件里有
public class HelloWorld,文件名必须是HelloWorld.java,否则编译报错。 - main 函数签名是固定的:
public static void main(String[] args)—— 必须是 public、static、void,参数是 String 数组。 - 没有头文件: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 的char是 2 字节(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++ 程序员的肌肉记忆里一定有 new 和 delete 的配对。到了 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 的泛型在运行时看不到具体的类型信息。
更多推荐
所有评论(0)