Java学习笔记(1)

       在自己写代码中,一定要体现出封装性,将抽象类的属性设置为私有,通过set和get方法实现。这是个好的编程习惯。

        问题1:在VSCode中有以下报错:Vehicle.java is a non-project file, only syntax errors are reportedJava(16)。

        说明:是 Visual Studio Code(VSCode)中 Java 插件给出的一个提示,意思是:你当前打开的 Vehicle.java 文件不属于一个项目结构,所以 IDE 只能提供基本的语法检查,不能进行完整的编译、调试和智能提示等操作。


🔧 原因说明

VSCode 本身只是一个编辑器,它不像 IntelliJ IDEA 或 Eclipse 那样默认就是面向项目的开发环境。当你只是单独打开了 .java 文件,而没有配置 Java 项目结构(如使用 Maven、Gradle 或简单目录结构 + tasks.json 等),就会出现这个提示。


✅ 解决方法 / 设置建议

你可以通过以下几种方式解决这个问题:


✅ 方法一:创建一个简单的 Java 项目结构(推荐)
  1. 在 VSCode 中打开一个文件夹作为你的项目根目录。
  2. 创建如下目录结构:
my-java-project/
├── src/
│   ├── Vehicle.java
│   ├── Car.java
│   ├── ElectricCar.java
│   ├── Truck.java
│   └── TestVehicle.java
└── bin/  (用于存放编译后的 .class 文件)
  1. 在 VSCode 中打开这个文件夹,而不是单个 .java 文件。

这样 VSCode 就会识别为 Java 项目,并启用完整的语言支持功能。


✅ 方法二:使用 Java 扩展的功能创建新类或项目

如果你安装了 Java 插件Language Support for Java(TM),可以使用命令面板(Ctrl+Shift+P)选择:

  • Java: Create Java Project
  • 或者 Java: Create Class

这会自动帮你建立项目结构并添加必要的配置。


✅ 方法三:手动编译运行(适合简单测试)

如果你只是想快速测试代码,也可以不创建完整项目,但需要手动使用命令行编译和运行:

# 编译所有 Java 文件
javac -d bin src/*.java

# 运行测试类
java -cp bin TestVehicle

注意:TestVehicle 是主类名,确保其中包含 public static void main(String[] args) 方法。


📌 总结

方式是否推荐说明
使用项目结构✅ 推荐更好的 IDE 支持,方便管理和扩展
使用插件创建项目✅ 推荐自动生成标准结构
单独打开 Java 文件❌ 不推荐功能受限,容易出警告
手动编译运行⚠️ 临时可用适合简单测试

问题2:

       说明: "Constructor to initialize the license plate" 指的是用于初始化车牌号(license plate)的构造函数。在面向对象编程中,构造函数是一种特殊的方法,它在创建类的对象时自动调用,用于初始化该对象的状态。具体来说,在你提到的上下文中,这意味着我们需要定义一个构造函数来设置Vehicle类或其子类实例中的licensePlate属性值。

public abstract class Vehicle {
    protected String licensePlate;

    public Vehicle(String licensePlate) {
        this.licensePlate = licensePlate;// Constructor to initialize the license plate
                                        //用于初始化车牌号(license plate)的构造函数
    }

        这里,public Vehicle(String licensePlate) 就是构造函数,它接受一个字符串参数licensePlate,并将其赋值给类的成员变量this.licensePlate。这样,在创建Vehicle类的任何子类对象时,可以通过传递车牌号给构造函数来初始化这个对象的车牌号属性。

        

问题3:The type Car must implement the inherited abstract method Vehicle.calculateFee(int)Java(67109264)

        说明:这个错误信息指出Car类没有实现从其父类Vehicle继承的抽象方法calculateFee(int hours)。即使你在Car类中定义了一个calculateFee方法,如果它的签名(包括返回类型、方法名和参数列表)不完全匹配于抽象方法的签名,编译器也会认为该抽象方法没有被实现。

class Car extends Vehicle {
    public Car(String licensePlate) {
        super(licensePlate);
    }

    // 确保方法签名与抽象方法完全一致
    @Override
    public double calculateFee(int hours) {
        return Math.ceil(hours) * 5; // 每小时5元,不足1小时按1小时计
    }
}

注意以下几点:

  • 使用@Override注解来明确表示你正在重写一个父类的方法。虽然这不是必需的,但它有助于捕获签名不匹配的问题。
  • 确认返回类型是double,并且方法名和参数列表与抽象方法声明完全相同。

问题4:Exception in thread "main" java.lang.Error: Unresolved compilation problem: at TestVehicle.main(Vehicle.java:63)

        说明:是一个编译错误在运行时被抛出的提示,它说明你的 Java 代码 并没有成功通过编译,但你却尝试去运行它了。

❗ 常见原因

这种错误通常出现在以下几种情况中:

  1. 你在 IDE(如 Eclipse、VSCode)中运行了未正确编译的代码
  2. 代码中有语法错误或类名/方法名写错
  3. 主类没有正确配置
  4. 文件名和类名不一致
  5. Java 版本不兼容

✅ 解决步骤

✅ 第一步:确认所有 .java 文件都已正确编译

请确保你在终端或命令行中使用的是如下方式来编译和运行程序:

# 编译所有 Java 文件
javac *.java

# 运行测试类
java TestVehicle

如果 javac *.java 报错,那说明有编译错误,请先解决这些错误。


✅ 第二步:检查报错位置

你说错误出现在:

at TestVehicle.main(Vehicle.java:63)

这表示程序运行到了 Vehicle.java 的第 63 行,但它其实并不是 TestVehicle 类的源文件。这说明你可能搞混了文件名和类名!


⚠️ 最常见的问题:文件名与类名不一致

如果你把多个类写在同一个 .java 文件里(比如你把 TestVehicleVehicle 写在了 Vehicle.java 中),然后你运行的时候执行的是:

java TestVehicle

TestVehicle 并不在 Vehicle.java 中,这就可能导致运行时报错。


✅ 正确做法:每个类单独一个文件

请按照以下结构组织代码:

- Vehicle.java          ← 抽象类
- Car.java              ← Car类
- ElectricCar.java      ← ElectricCar类
- Truck.java            ← Truck类
- TestVehicle.java      ← 测试类(包含 main 方法)

每个文件只定义一个类,且文件名与类名完全一致。


✅ 示例:TestVehicle.java

确保 TestVehicle.java 内容类似这样:

public class TestVehicle {
    public static void main(String[] args) {
        Vehicle[] vehicles = new Vehicle[3];
        vehicles[0] = new Car("京A1234");
        vehicles[1] = new ElectricCar("沪B5678");
        vehicles[2] = new Truck("粤C9012");

        int hours = 3;

        for (Vehicle v : vehicles) {
            v.displayInfo();
            System.out.println("停放 " + hours + " 小时的费用为:" + v.calculateFee(hours));
            System.out.println("--------------------------");
        }
    }
}

✅ 总结排查清单

检查项是否完成
所有类是否分开放在不同 .java 文件中?
文件名和类名是否完全一致?
是否使用 javac *.java 成功编译?
主类是否有 public static void main(String[] args)
是否在错误的文件中运行了 java TestVehicle

问题5:super语句是什么

   说明:super 关键字在 Java 中用于引用直接父类的对象。它有几种不同的用途,但在你提到的例子中,super 被用来调用父类的构造函数。具体来说:

super() 语句的作用

        当你在子类中定义了一个构造函数时,Java 要求你在该构造函数的第一行要么显式地调用父类的构造函数(通过 super()),要

么隐式地调用无参构造函数(如果父类没有默认的无参构造函数,则必须显式调用)。这是为了确保在创建子类对象之前,父类的部分已经被正确初始化。

class Truck extends Vehicle {
    public Truck(String licensePlate) {
        super(licensePlate); // 调用父类(Vehicle)的构造函数,并传递参数
    }
}

        在这个例子中,Truck 类继承自 Vehicle 类。super(licensePlate); 这一行代码的作用是调用 Vehicle 类中接受一个 String 参数的构造函数,并将 licensePlate 参数传递给它。这样做的目的是初始化从 Vehicle 类继承来的 licensePlate 属性。

如果不使用 super()

如果没有使用 super() 来调用父类的构造函数,且父类没有无参构造函数,那么 Java 编译器会报错,因为无法自动选择合适的父类构造函数来初始化父类部分。

super 的其他用途

除了调用父类的构造函数外,super 还可以用来:

  • 访问父类中被子类成员覆盖的方法或变量。例如,如果你在子类中有一个与父类同名的变量或方法,你可以使用 super 来明确指定你要访问的是父类中的版本。

class Example {
    int value = 10;
    
    void showValue() {
        System.out.println("Parent value: " + value);
    }
}

class SubExample extends Example {
    int value = 20; // 隐藏了父类的value
    
    void showValue() {
        System.out.println("Child value: " + value);
        System.out.println("Parent value using super: " + super.value); // 使用super访问父类的value
    }
}

问题6:Math.ceil的作用

        说明:Math.ceil作用是 将停车时间 hours 向上取整,以实现“不足1小时按1小时计费”的规则。

向下取整是Math.floor。


📌 Math.ceil() 是什么?

  • Math.ceil() 是 Java 中 Math 类提供的一个静态方法。
  • 它的作用是:向上取整,即返回大于或等于给定数值的最小整数。

🔍 示例说明

原始小时数(hours)使用 Math.ceil(hours) 后的结果
1.01.0
1.12.0
2.53.0
33.0

💡 在你的场景中的作用

你的需求是:

每小时收费 5 元,不足 1 小时按 1 小时计算

所以:

  • 如果停了 2.1 小时 → 按 3 小时计费 → Math.ceil(2.1) = 3
  • 如果停了 2.9 小时 → 按 3 小时计费 → Math.ceil(2.9) = 3
  • 如果刚好停了 3 小时 → 按 3 小时计费 → Math.ceil(3.0) = 3

然后再乘以每小时费用 5 元:

Math.ceil(2.1) * 5 = 3 * 5 = 15 元
Math.ceil(2.9) * 5 = 3 * 5 = 15 元
Math.ceil(3.0) * 5 = 3 * 5 = 15 元

✅ 总结

表达式含义
Math.ceil(hours)把小数部分向上取整,确保“不足1小时也按1小时算”
* 5每小时 5 元
整体作用实现“每小时 5 元,不足 1 小时按 1 小时计”的计费逻辑

如果你希望结果返回的是 int 而不是 double,可以做类型转换:

return (int) Math.ceil(hours) * 5;

这样返回值就是整数,例如 15 而不是 15.0

        

问题7:如何在Light文件中提供一个判断当前状态,以确保不会出现状态冲突

        说明:为了确保在 Light 类中不会出现重复设置状态的情况(例如,在灯已经是“开”的状态下再次调用 turnOn() 方法),我们可以在 turnOn()turnOff() 方法中添加对当前状态的检查。这样可以避免不必要的状态设置操作,同时也让代码逻辑更加严谨。

public class Light extends Device implements Switchable {

    public Light(String deviceName) {
        super(deviceName); // 调用父类构造器完成封装初始化
    }

    @Override
    public void turnOn() {
        if ("关".equals(getStatus())) { // 检查当前状态是否为“关”
            setStatus("开");
            System.out.println(getDeviceName() + " 已经打开。");
        } else {
            System.out.println(getDeviceName() + " 已经是开着的。");
        }
    }

    @Override
    public void turnOff() {
        if ("开".equals(getStatus())) { // 检查当前状态是否为“开”
            setStatus("关");
            System.out.println(getDeviceName() + " 已经关闭。");
        } else {
            System.out.println(getDeviceName() + " 已经是关闭的。");
        }
    }

    // 为了方便获取设备名称,增加getDeviceName方法
    private String getDeviceName() {
        return ((Device)this).deviceName;
    }

    @Override
    public String getStatus() {
        return super.getStatus(); // 返回当前状态
    }
}

主要改动点:

  1. 状态检查:在 turnOn()turnOff() 方法中增加了对当前状态的判断。

    • 在 turnOn() 方法中,如果当前状态已经是“开”,则输出提示信息而不改变状态。
    • 在 turnOff() 方法中,如果当前状态已经是“关”,则输出提示信息而不改变状态。
  2. 辅助方法:增加了 getDeviceName() 方法用于获取设备名,因为 deviceName 是私有的,不能直接访问。

示例输出:

假设你有如下测试代码:

public class TestHome {
    public static void main(String[] args) {
        Light light = new Light("客厅灯");

        light.turnOn(); // 输出: 客厅灯 已经打开。
        light.turnOn(); // 输出: 客厅灯 已经是开着的。
        light.turnOff(); // 输出: 客厅灯 已经关闭。
        light.turnOff(); // 输出: 客厅灯 已经是关闭的。
    }
}

运行上述测试代码将会得到如下的输出结果:

客厅灯 已经打开。
客厅灯 已经是开着的。
客厅灯 已经关闭。
客厅灯 已经是关闭的。

这种实现方式不仅防止了无效的状态更改操作,还通过反馈信息让用户知道当前的操作是否真正改变了设备的状态。这样的设计提高了用户体验和程序的健壮性。

问题8:equal是什么语句,作用是什么

        说明:在 Java 中,equals() 是一个用于比较两个对象是否相等的方法。它属于 Object 类,因此所有 Java 类都继承了这个方法(因为所有的类都直接或间接地继承自 Object)。默认情况下,equals() 方法比较的是两个对象的引用是否相同(即它们是否指向内存中的同一个对象)。然而,对于许多类来说,重写 equals() 方法以基于对象的内容而不是引用进行比较是很有用的。

equals() 方法的作用

  • 默认行为:如果未重写 equals() 方法,则它会比较两个对象的引用地址。
  • 重写后的行为:通常你会重写 equals() 方法来根据对象的实际内容来进行比较。例如,在字符串比较中,"abc".equals("abc") 会返回 true,即使这两个字符串是不同的对象实例,因为它们的内容相同。

在你的代码上下文中的应用

在你的 Light 类中,使用 equals(getStatus()) 的目的是为了检查当前设备的状态是否为“开”或“关”,以便决定是否需要执行开关操作。具体来说:

@Override
public void turnOn() {
    if ("关".equals(getStatus())) { // 检查当前状态是否为“关”
        setStatus("开");
        System.out.println(getDeviceName() + " 已经打开。");
    } else {
        System.out.println(getDeviceName() + " 已经是开着的。");
    }
}

@Override
public void turnOff() {
    if ("开".equals(getStatus())) { // 检查当前状态是否为“开”
        setStatus("关");
        System.out.println(getDeviceName() + " 已经关闭。");
    } else {
        System.out.println(getDeviceName() + " 已经是关闭的。");
    }
}

这里 "关".equals(getStatus())"开".equals(getStatus()) 分别用于检查设备的当前状态是否为“关”或“开”。

为什么使用 "关".equals(getStatus()) 而不是 getStatus().equals("关")
  • 防止空指针异常:如果你直接使用 getStatus().equals("关"),而 getStatus() 返回 null,则会导致 NullPointerException。通过将常量字符串 "关" 放在前面作为调用者,可以避免这个问题,因为 "关" 肯定不会是 null

示例解释

假设你有以下代码片段:
 

Light light = new Light("客厅灯");

light.turnOn(); // 输出: 客厅灯 已经打开。
light.turnOn(); // 输出: 客厅灯 已经是开着的。
light.turnOff(); // 输出: 客厅灯 已经关闭。
light.turnOff(); // 输出: 客厅灯 已经是关闭的。

在这个例子中:

  1. 首次调用 turnOn() 时,由于初始状态是“关”,所以条件 "关".equals(getStatus()) 成立,状态被更改为“开”,并输出“客厅灯 已经打开。”。
  2. 第二次调用 turnOn() 时,状态已经是“开”,因此 "关".equals(getStatus()) 不成立,输出“客厅灯 已经是开着的。”。
  3. 调用 turnOff() 后,状态从“开”变为“关”,输出“客厅灯 已经关闭。”。
  4. 再次调用 turnOff() 时,状态已经是“关”,因此 "开".equals(getStatus()) 不成立,输出“客厅灯 已经是关闭的。”。

这样就确保了不会重复设置相同的设备状态,并且提供了明确的用户反馈。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值