什么是 “static” 关键字?Java 中是否可以覆盖 ( override ) 一个 private 或者是 static 的方法?

在Java的世界里,关键字"static"与覆盖概念,往往是很多初级开发者容易混淆的非常重要的知识点。理解清楚它们,既是提升你编程技巧的关键,也是你面试成功的一部分。

"static"是Java中提供的关键字之一,一般用来修饰成员变量和成员方法,它可以用来创建类级别的变量和方法。所谓的类级别,就是说这些变量和方法是与类相关的,而非与类的实例相关的。换句话说,不管我们实例化多少个对象,只要是static修饰的成员变量和成员方法,它们在内存中始终有且仅有一份。

让我们构造一个例子。在游戏中,假设有一个角色对象,这个对象有姓名,生命值等非静态成员。但还有一个成员,就是底伤害(damage),这个值是每个角色都通用的,那么这时候,我们就可以将它设置为静态成员。静态成员在内存中只有一份,因此无论游戏中有多少个角色,他们的底伤害值都将相同。

对于覆盖(override)来说,这在Java面向对象编程中是很核心的一部分。覆盖是子类对父类的可见方法提供一个具体的实现。但是,Java对private和static的方法不支持覆盖。

对于private方法,因为它仅在声明这个方法的类中可见,所以它对子类是隐藏的。子类无法访问和覆盖这个方法。即使子类创建一个同名的方法,这只是在子类中定义了一个新的方法,它并不影响父类的private方法。这就解答了我们为什么不能覆盖private方法的问题。

与此同时,static方法也是不能被覆盖的。因为static方法是类级别的方法,和实例无关,它的调用并不需要对象实例,直接通过类名就可以调用。另一个原因是,在运行期间,静态方法的调用不使用动态绑定而是使用静态绑定。这就意味着调用的静态方法,在编译期间就已经确定了,而不是在运行时动态决定的。所以,如果子类定义了一个和父类签名完全一样的静态方法,它并没有覆盖父类的静态方法,而只是隐藏了父类的静态方法。

为了进一步理解"覆盖"与"隐藏"的区别,让我们通过一个实例来看看它们的实际工作方式。假设我们有一个父类"Parent"和一个子类"Child",两个类中都定义了一个静态方法"foo",并且子类的"foo"隐藏了父类的"foo"。然后我们声明一个"Parent"类的引用,引用指向一个"Child"的实例。当我们通过这个引用调用"foo"方法时,你可能会想,应该调用的是"Child"类的"foo"方法,但实际上,调用的却是"Parent"的"foo"方法。这就是隐藏的实质,也就是静态方法不支持覆盖,子类的同名静态方法并不会替代父类的方法。

Java中的"static"关键字是创建类级别变量和方法的工具,而覆盖(override)则是子类对父类方法的特殊实现。然而,你不能覆盖被标记为static或private的方法,因为一种是和类直接相关的,一种是对子类隐藏的。理解和区分这些概念,将使你更好地理解Java,从而在未来的编程和面试中都能从容应对。

Java,可以使用 `synchronized` 关键字让对象在任何时刻只能由一个线程访问。`synchronized` 关键字可以用于方法或代码块,用于实现线程的同步。 当一个方法或代码块被 `synchronized` 关键字修饰时,它被称为同步方法或同步代码块。在同步方法或同步代码块,只有一个线程可以访问该方法或代码块,其他线程必须等待该线程执行完毕后才能访问。 例如,下面的代码演示了如何使用 `synchronized` 关键字实现线程的同步: ```java class Counter { private int count = 0; public synchronized void increment() { count++; } public synchronized int getCount() { return count; } } class MyThread extends Thread { private Counter counter; public MyThread(Counter counter) { this.counter = counter; } @Override public void run() { for (int i = 0; i < 1000; i++) { counter.increment(); } } } public class Main { public static void main(String[] args) throws InterruptedException { Counter counter = new Counter(); MyThread thread1 = new MyThread(counter); MyThread thread2 = new MyThread(counter); thread1.start(); thread2.start(); thread1.join(); thread2.join(); System.out.println(counter.getCount()); // 输出 2000 } } ``` 在这个例子,首先定义了一个 `Counter` 类,包含了一个计数器 `count`,以及一个使用 `synchronized` 关键字修饰的 `increment()` 方法一个使用 `synchronized` 关键字修饰的 `getCount()` 方法。`increment()` 方法用于将计数器加 1,`getCount()` 方法用于获取计数器的值。 然后定义了一个 `MyThread` 类,继承自 `Thread` 类,用于执行计数器的增加操作。在 `Main` 类,创建了两个 `MyThread` 对象,传入同一个 `Counter` 对象,启动这两个线程并等待它们执行完毕后打印计数器的值。 运行这个程序可以看到,无论有多少个线程同时访问计数器,都能够保证计数器的值是正确的,这是因为 `increment()` 和 `getCount()` 方法都被 `synchronized` 关键字修饰,保证了同一时刻只有一个线程可以访问这些方法
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值