static特点
Java中的static关键字允许在没有创建类的实例的情况下进行调用。以下是static关键字的主要用途和特点:
- 静态变量(类变量):使用static关键字声明的变量称为静态变量或类变量。这些变量属于类本身,而不是类的任何特定实例。因此,它们可以在没有创建类的实例的情况下被访问和修改。
- 静态方法:同样,用static修饰的方法称为静态方法。这些方法不依赖于类的实例,可以直接通过类名调用,而不需要创建类的实例。静态方法通常用于执行与类的实例无关的操作,比如工具方法或者对静态变量的操作。
- 静态代码块:静态代码块是在类加载时由Java虚拟机(JVM)自动执行的代码块。它通常用于初始化静态变量或者执行只需要一次的设置操作。
- 类加载时机:由于静态成员在类加载时就被初始化,因此它们的加载时机比实例成员早。这意味着在创建对象之前,静态成员已经被初始化并可以使用。
- 无需实例化:静态成员和方法的一个关键优点是它们可以在没有创建类的实例的情况下使用。这在某些情况下非常有用,比如当你想要在创建任何对象之前运行某些代码或者当你需要一个不依赖于任何特定实例的功能时。
- 全局访问性:静态成员虽然可以通过类名直接访问,但它们并不是真正意义上的全局变量。它们的作用域限制在定义它们的类中,但是它们可以被类的所有实例共享。
public class MyClass {// 静态变量public static int staticVar = 10;// 静态方法public static void staticMethod() {System.out.println("这是一个静态方法");}public static void main(String[] args) {// 访问静态变量和静态方法System.out.println("静态变量的值为:" + MyClass.staticVar);MyClass.staticMethod();}
}
静态方法静态变量的访问
public class MyClass {// 静态变量public static int staticVar = 10;// 静态方法public static void staticMethod() {System.out.println("这是一个静态方法");}public static void main(String[] args) {// 访问静态变量System.out.println("静态变量的值为:" + MyClass.staticVar);// 调用静态方法MyClass.staticMethod();}
}
在上面的示例中,我们定义了一个名为MyClass
的类,其中包含一个静态变量staticVar
和一个静态方法staticMethod
。在main
方法中,我们通过类名MyClass
直接访问了静态变量和静态方法,而不需要创建类的实例。
请注意,静态方法和静态变量属于类本身,而不是类的实例。这意味着它们可以在没有创建类的实例的情况下被访问和修改。
继承
在Java中,继承是面向对象编程的一个核心概念,它允许一个类(子类)继承另一个类(父类)的属性和方法。以下是Java继承的关键特点:
继承机制:
- 使用
extends
关键字实现类的继承。 - 子类继承父类后,会自动拥有父类的所有成员变量和方法,除了私有的(private)成员。
方法重写:
- 子类可以重写(Override)从父类继承来的方法,以提供特定于子类的行为。
单继承限制:
- Java只支持单继承,即一个子类只能有一个直接的父类。但是,可以通过接口实现多重继承的效果,因为一个类可以实现多个接口。
构造方法:
- 构造方法不是继承的一部分,子类不能继承父类的构造方法,但可以在子类的构造方法中调用父类的构造方法。
提高代码复用性:
- 继承可以减少代码冗余,提高程序的可维护性和开发效率。
is-a关系:
- 继承应该用于实现"是一个"(is-a)关系,即子类应该是父类的一个更具体的版本。
super关键字:
- 在子类中,可以使用
super
关键字来引用父类的成员变量和方法。
耦合性问题:
- 继承可能会增加类之间的耦合度,这可能导致代码之间的联系过于紧密,影响代码的独立性。
格式及案例
Java继承的格式如下:
class 子类 extends 父类 {// 子类的代码
}
下面是一个Java继承的案例:
class Animal {void eat() {System.out.println("Animal is eating");}
}class Dog extends Animal {void bark() {System.out.println("Dog is barking");}
}public class Main {public static void main(String[] args) {Dog dog = new Dog();dog.eat(); // 调用父类的方法dog.bark(); // 调用子类的方法}
}
在这个案例中,Dog
类继承了Animal
类。Dog
类可以访问Animal
类中的eat()
方法,并且还可以定义自己的方法bark()
。在Main
类的main
方法中,我们创建了一个Dog
对象,并分别调用了eat()
和bark()
方法。
子类不能继承父类的内容
在Java中,子类继承父类时,有一些特定的成员是无法直接继承的,以下是这些情况:
私有成员(Private Members):
- 私有成员变量和方法在父类中是不能被子类继承的。它们只能在声明它们的类内部访问。
构造方法(Constructors):
- 构造方法不是继承的一部分,因此子类不会继承父类的构造方法。如果子类需要调用父类的特定构造方法,它必须通过
super
关键字显式地调用。
静态初始化块(Static Blocks):
- 静态初始化块是在类加载到内存时执行的代码块,它们不是对象的状态的一部分,因此不会被继承。
final成员(Final Members):
- 被
final
修饰的成员变量和方法不能被子类修改。虽然子类可以继承final
变量的值和final
方法的实现,但是子类不能改变这些成员的值(对于变量)或者重写这些方法(对于方法)。
私有内部类(Private Inner Classes):
- 私有内部类只能在其封闭类中访问,因此它们不会被继承。
默认方法(Default Methods)的实现:
- 在接口中,默认方法是可以被实现类修改的,但是在继承时,子类不会自动继承默认方法的实现,除非子类选择显式地提供自己的实现。
父类的初始化和清理操作:
- 子类不会继承父类的初始化和清理操作,例如在父类中定义的
init()
和cleanup()
方法。
匿名类和局部类:
- 匿名类和局部类通常用于特定的上下文,它们不会被继承。
父类的实例创建:
- 子类不会继承父类的实例创建逻辑,如果子类需要创建父类的对象,它必须自己创建。
继承的成员变量、成员方法、构造方法的特点
在Java中,继承后的成员变量和成员方法具有以下特点:
成员变量的访问:
- 隐藏原则:如果子类中定义了与父类相同名称的成员变量,那么子类中的变量会隐藏父类中的同名变量。
- 访问权限:子类可以直接访问父类的非私有成员变量,但不能直接访问父类的私有成员变量。如果需要访问,可以通过父类提供的公共方法进行访问。
- 就近原则:在查找成员变量时,Java遵循“就近原则”,即先查找本类的成员变量,如果没有找到,则继续向上查找父类的成员变量,直到Object类。
成员方法的访问:
- 方法重写(Override):如果子类中定义了与父类相同签名的方法,那么子类的方法会覆盖父类中的同名方法。这是多态的一种体现,允许子类提供特定于自己的行为实现。
- 调用父类方法:子类可以通过
super
关键字调用父类中的方法,即使子类中存在同名方法。这在子类需要使用父类的方法逻辑时非常有用。 - 方法调用顺序:当通过对象调用方法时,Java虚拟机(JVM)会从该对象的实际类型开始查找方法,如果找到了就调用,找不到则向上查找父类的方法,直到Object类。
构造方法的特点:
- 必须调用基类的构造方法:子类的构造过程中需要调用其基类的构造方法。这是因为子类继承了父类的属性和方法,需要确保父类的部分得到适当的初始化。
- 隐式调用:如果子类的构造方法中没有显示地调用基类构造方法,Java编译器会默认插入一个对基类无参数构造方法的调用。这意味着,如果父类中没有无参数的构造方法,而子类又没有显式地调用其他构造方法,将会导致编译错误。
- 使用super关键字:子类可以通过
super(argument_list)
的形式显式地调用父类中的某个特定的构造方法。这样做可以让子类在初始化时执行父类中的特定行为。 - 构造方法的访问规则:子类构造方法中调用父类构造方法是通过
super
关键字实现的,这个调用必须是子类构造方法的第一条语句。这样的设计保证了父类部分总是在子类部分之前被初始化。 - 单继承的限制:Java语言支持单继承,即一个类的直接父类只能有一个。但是,一个父类可以有多个子类,这样形成了一个树状的继承结构。
- 多级继承:尽管Java只支持单继承,但可以通过多级继承来实现复杂的类层次结构。在这种结构中,一个子类可以继承另一个子类,形成一个继承链。
super关键字
在Java中,super
关键字是一个指向父类对象的引用。它主要用于以下几个方面:
-
调用父类构造方法: 使用
super()
可以在子类的构造方法中调用父类的构造方法。这通常用于初始化从父类继承来的成员变量。class Parent {public Parent() {System.out.println("Parent constructor");} }class Child extends Parent {public Child() {super(); // 调用父类的构造方法System.out.println("Child constructor");} }
-
访问父类成员方法: 使用
super
可以访问父类的成员方法,即使子类重写了这些方法。class Parent {void display() {System.out.println("Parent's display method");} }class Child extends Parent {void display() {super.display(); // 调用父类的display方法System.out.println("Child's display method");} }
-
访问父类成员变量: 使用
super
可以访问父类的成员变量,但如果变量在子类中被隐藏(即子类中有同名的变量),则必须使用super
来引用父类的变量。class Parent {int x = 10; }class Child extends Parent {int x = 20;void printX() {System.out.println("Parent's x: " + super.x); // 访问父类的xSystem.out.println("Child's x: " + x); // 访问子类的x} }
-
限定超类中的方法调用: 当子类覆盖了父类的方法,并且需要在同一个方法内部调用父类的方法时,
super
可以用来限定调用的是父类的方法。class Parent {void someMethod() {System.out.println("Parent's implementation");} }class Child extends Parent {void someMethod() {super.someMethod(); // 调用父类的someMethod方法// ... 其他代码} }
-
清除歧义: 当子类和父类有相同的方法或属性时,
super
可以用来清除歧义,明确指出我们想要访问的是父类的成员。
方法重写
Java方法重写是指子类中定义了一个与父类中同名的方法,并且具有相同的参数列表和返回类型。这样,当子类对象调用该方法时,会执行子类中的实现,而不是父类中的实现。这种特性称为方法重写(Method Overriding)。
方法重写的条件:
- 方法名相同。
- 参数列表相同。
- 返回类型相同或是子类型。
- 访问修饰符不能更严格。
- 抛出的异常不能比父类方法抛出的异常更多。
示例代码:
class Animal {public void eat() {System.out.println("Animal is eating");}
}class Dog extends Animal {@Overridepublic void eat() {System.out.println("Dog is eating");}
}public class Main {public static void main(String[] args) {Animal animal = new Animal();animal.eat(); // 输出 "Animal is eating"Dog dog = new Dog();dog.eat(); // 输出 "Dog is eating"}
}
this和super的区别
在Java中,this
和super
是两个特殊的关键字,它们在很多方面都有所不同。具体分析如下:
代表的对象不同:
this
关键字代表当前对象,即调用当前对象的实例方法或引用当前对象的实例变量时使用。super
关键字代表父类对象,主要用于访问父类的成员(包括成员变量和方法)以及在子类的构造方法中调用父类的构造方法。
使用场景不同:
this
可以用于普通的实例方法中,也可用于构造方法内部,用来调用同一类的另一个构造方法或者引用当前对象的实例变量。super
只能在子类的方法或构造方法中使用,用于调用父类的构造方法、成员变量或方法。
调用规则不同:
this
可以用于任何实例方法或构造方法中,没有限制。super
必须作为子类构造方法的第一条语句出现,用于调用父类的构造方法。
功能作用不同:
this
可以用于解决实例变量和局部变量之间的命名冲突,也可以用于调用同一个类中的其他构造方法。super
主要用于确保父类部分得到正确的初始化,特别是在继承层次较深时,它可以确保所有父类按顺序被正确构造。