PHP 与 Java 关键差异速查
面向 PHP 开发者的 Java 概念对照速查手册。建议先读这篇,建立基本认知后再阅读其他文档。
1. 语言层面差异
强类型 vs 弱类型
php
// PHP:弱类型,变量类型自动转换
$count = "5"; // string
$count += 3; // int(8),PHP 自动转换类型java
// Java:强类型,每个变量、参数、返回值都必须声明类型
String count = "5"; // 编译通过
// count += 3; // ❌ 编译错误:String 不能 + int
int number = Integer.parseInt(count) + 3; // 必须先转换对开发的影响:
- Java 中类型错误在编译时就暴露,PHP 在运行时才发现
- Java 函数签名包含类型信息,本身就是文档
- 更多的键盘输入(类型声明),换来更安全的重构
JVM 编译执行 vs PHP 解释执行
| 特性 | PHP | Java |
|---|---|---|
| 执行方式 | 解释执行(OPcache 缓存 opcode) | 先编译成字节码,JVM 运行时解释/编译 |
| 修改代码 | 保存文件后下次请求即生效 | 需要重新编译(mvn compile),DevTools 可自动触发 restart |
| 启动速度 | ms 级(PHP-FPM 常驻) | 秒级(Spring Boot 需启动 JVM + Tomcat + 加载所有 Bean) |
| 运行性能 | 每次请求都要走框架初始化 | 一次启动持续处理请求,同类任务性能通常优于 PHP |
静态类型检查
PHP 开发者最需要适应的一点:Java 编译器会拦截很多看似"正常"的代码。
php
function divide($a, $b) {
return $a / $b;
}
echo divide(10, "0"); // PHP 只会给出 Division by zero 警告java
// Java 必须在调用前就处理好类型问题
public double divide(int a, int b) {
return a / b;
}
// 调用:divide(10, 0); // 编译通过,运行抛 ArithmeticException2. 语法层面对比
命名空间
php
// PHP:使用 \ 作为分隔符
namespace App\Http\Controllers;
use App\Models\User;java
// Java:使用 . 作为分隔符,且必须与目录结构一致
package com.example.myapp.controller;
import com.example.myapp.model.User;| 特性 | PHP | Java |
|---|---|---|
| 分隔符 | \ | . |
| 关键字 | namespace | package |
| 导入 | use | import |
| 与目录关系 | 松散关联(PSR-4 自动加载) | 强制对应(编译器要求) |
| 根命名空间 | 通常 App\ | 通常是 com.公司名.项目名 |
类与对象
php
// PHP
class UserService {
public function __construct(
private UserRepository $repository // PHP 8+ 构造器属性提升
) {}
public function find(int $id): ?User {
return $this->repository->find($id);
}
}java
// Java
public class UserService {
private final UserRepository repository; // final 对应 readonly
// 构造器注入(对应 PHP 的构造器属性提升)
public UserService(UserRepository repository) {
this.repository = repository;
}
public User find(int id) { // 返回值不能为 null(除非显式 Optional)
return repository.findById(id)
.orElseThrow(() -> new RuntimeException("User not found"));
}
}接口
php
// PHP:接口可以有常量,PHP 8+ 可以有默认方法
interface Logger {
public function log(string $message): void;
public function error(string $message): void; // PHP 8.1 可以定义
}java
// Java:接口更强大,可以有默认方法和静态方法
public interface Logger {
void log(String message); // 默认就是 public abstract
default void error(String message) { // 对应 PHP 的 trait 或接口默认方法
log("[ERROR] " + message);
}
static Logger getDefault() { // 工厂方法
return new ConsoleLogger();
}
}泛型(PHP 没有直接对应)
php
// PHP:没有编译期泛型,只有 PHPDoc 注解
/**
* @return Collection<User>
*/
function getUsers(): Collection {
return User::all();
}java
// Java:真正的编译期泛型
public List<User> getUsers() {
return userRepository.findAll();
// 返回 List<User> 保证了每个元素都是 User 类型
}
public <T> T findById(Long id, Class<T> type) {
// 泛型方法:调用时确定返回类型
return (T) repository.findById(id).orElse(null);
}注解(Annotation)vs PHP Attribute
PHP 8.0 引入了 Attribute,语法上与 Java 注解极其相似:
php
// PHP 8+ Attribute(从 Laravel 角度看,类似 Route 注解)
#[Route('/api/users', methods: ['GET'])]
public function index() { ... }java
// Java Annotation(注意:没有 # 号,用 @ 开头)
@GetMapping("/api/users")
public List<User> index() { ... }关键差别:
- Java 注解在运行时可通过反射读取(Spring Boot 大量依赖这一机制)
- PHP Attribute 同样在运行时读取,但 Spring Boot 的注解体系更庞大、更底层
- Java 中几乎所有"魔法"(路由、依赖注入、事务、缓存)都通过注解实现
3. 生态与工具对比
Maven / Gradle vs Composer
| 特性 | Composer | Maven | Gradle |
|---|---|---|---|
| 配置文件 | composer.json | pom.xml | build.gradle |
| 依赖存储 | vendor/ | ~/.m2/repository/ | ~/.gradle/caches/ |
| 命令示例 | composer install | mvn install | gradle build |
| 镜像源 | composer config repos | settings.xml mirrors | build.gradle repositories |
| 速度 | 快 | 较慢(但稳定) | 快(支持增量编译) |
包管理仓库
xml
<!-- Maven 配置阿里云镜像(类比 composer 配置中文镜像) -->
<!-- 在 ~/.m2/settings.xml 中添加 -->
<mirrors>
<mirror>
<id>aliyun</id>
<mirrorOf>central</mirrorOf>
<url>https://maven.aliyun.com/repository/central</url>
</mirror>
</mirrors>IDE 工具链
| 工具 | PHP 版 | Java 版 |
|---|---|---|
| IDE | PhpStorm | IntelliJ IDEA(同一家公司,快捷键通用) |
| 调试器 | Xdebug | 内嵌在 IDE 中,无需额外配置 |
| 代码补全 | 良好 | 极佳(类型系统更完善,补全更精确) |
| 重构能力 | 基本 | 强(重命名、提取、移动都能全项目安全执行) |
4. 框架层面差异
MVC 模式
| 概念 | Laravel | Spring Boot |
|---|---|---|
| 路由 | routes/web.php 集中定义 | 注解在控制器方法上(@GetMapping) |
| 控制器 | 继承 Controller 基类 | 无需继承,@RestController 注解即可 |
| 模型 | Eloquent Model(Active Record) | Entity + Repository(Data Mapper) |
| 模板引擎 | Blade(PHP 语法扩展) | Thymeleaf(HTML 友好) |
| DI 容器 | app()->bind() / app()->make() | @Component + @Autowired |
请求响应对比
| 操作 | Laravel | Spring Boot |
|---|---|---|
| 获取参数 | $request->input('name') | @RequestParam String name 或 @RequestBody |
| 返回 JSON | response()->json($data) | return userService.getUsers()(自动序列化为 JSON) |
| 异常处理 | App\Exceptions\Handler | @ControllerAdvice + @ExceptionHandler |
| 中间件 | php artisan make:middleware | HandlerInterceptor 或 Filter |
依赖注入对比
| 特性 | Laravel | Spring Boot |
|---|---|---|
| 注册服务 | $this->app->bind() | @Component / @Service(扫描注解自动注册) |
| 注入方式 | __construct 自动注入 | @Autowired 或构造器注入 |
| 单例 | $this->app->singleton() | @Service 默认就是单例 |
| 门面 | \DB::table() | 无对应(通常注入 Bean 使用) |
5. 常见"水土不服"场景
场景:修改代码后不生效
php
// PHP:修改 → 保存 → F5 刷新 → 生效java
// Java:修改 → 保存 → IDE 自动编译(或手动 mvn compile)
// → Spring Boot DevTools 检测到变化自动重启(2-5 秒)
// → F5 刷新场景:null 安全
php
// PHP:几乎不用担心 null
$user = User::find($id);
return $user->name; // 如果 $user 为 null,返回 null,不会抛异常java
// Java:null 是最大的问题来源
User user = userRepository.findById(id).orElse(null);
// user.getName(); // ❌ 如果 user 是 null,抛出 NullPointerException
// 正确做法:用 Optional 避免 NPE
User user = userRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("User not found"));场景:数组操作
php
// PHP:数组即一切
$users = User::all()->toArray();
$names = array_column($users, 'name');java
// Java:数组、List、Set、Map 是不同的类型
List<User> users = userService.getAllUsers();
List<String> names = users.stream()
.map(User::getName) // 类比 array_map
.collect(Collectors.toList()); // 转回 List
// Java 8+ 的 Stream API 类似 PHP 的 array_map / array_filter / array_reduce6. 快速决策指南
| 如果你在 Php/Laravel 中这么做 | 在 Java/Spring Boot 中应该 |
|---|---|
$user = User::find($id) | userRepository.findById(id) |
$request->input('name') | @RequestParam String name |
return response()->json(...) | return ResponseEntity.ok(...) |
Route::middleware('auth')->group(...) | @PreAuthorize 或 WebConfig.addInterceptors() |
config('app.name') | @Value("${app.name}") |
Log::info('message') | log.info("message")(通过 LoggerFactory) |
Cache::remember('key', 3600, fn() => ...) | @Cacheable("key") |
php artisan make:model User | 手动创建 User.java |
composer require vendor/pkg | 在 pom.xml 中添加 <dependency> |
dd($variable) | System.out.println() 或 log.debug()(不推荐,应使用断点调试) |
💡 阅读建议: 后续文档中遇到不理解的概念,随时回到此页查阅对照关系。