实例变量、静态变量、实例方法和静态方法之间的关系
在Java中,实例变量、静态变量、实例方法和静态方法是类的成员
实例变量(Instance Variables):
重载与覆盖
- 实例变量是属于类的实例(对象)的变量,每个对象都有一份。
- 实例变量在对象创建时分配内存,随着对象的销毁而释放。
- 使用关键字
this
可以在实例方法中访问实例变量。public class MyClass { // 实例变量 int instanceVariable; // 实例方法 void instanceMethod() { // 访问实例变量 this.instanceVariable = 10; } }
静态变量(Static Variables):
- 静态变量是属于类的,而不是属于类的实例。
- 静态变量在类加载时被初始化,只有一份,所有对象共享。
- 使用类名直接访问静态变量。
public class MyClass { // 静态变量 static int staticVariable; // 静态方法 static void staticMethod() { // 访问静态变量 MyClass.staticVariable = 20; } }
实例方法(Instance Methods):
- 实例方法是属于类的实例的方法,可以访问和操作实例变量。
- 实例方法必须通过对象调用。
public class MyClass { // 实例变量 int instanceVariable; // 实例方法 void instanceMethod() { // 访问实例变量 this.instanceVariable = 10; } }
静态方法(Static Methods):
- 静态方法是属于类的方法,不能访问实例变量,只能访问静态变量。
- 静态方法可以通过类名直接调用,无需创建对象。
public class MyClass { // 静态变量 static int staticVariable; // 静态方法 static void staticMethod() { // 访问静态变量 MyClass.staticVariable = 20; } }
关系和总结:
- 实例变量和实例方法属于对象,每个对象有一份。
- 静态变量和静态方法属于类,只有一份,所有对象共享。
- 实例方法可以访问和操作实例变量,静态方法只能访问静态变量。
- 静态方法可以通过类名直接调用,而实例方法需要通过对象调用。
重载(Overloading):
重载指的是在同一个类中,可以有多个方法具有相同的名字,但参数列表不同(参数的类型、个数或顺序不同)。重载方法具有相同的名字,但是编译器会根据调用时传入的参数类型和个数选择合适的方法。
public class Example {
// 重载方法
void printNumber(int x) {
System.out.println("Printing an integer: " + x);
}
// 同名方法,参数类型不同
void printNumber(double x) {
System.out.println("Printing a double: " + x);
}
}
覆盖(Overriding):
覆盖指的是在子类中重新实现(重新定义)从父类继承而来的方法,使得子类的方法具有相同的签名(方法名、返回类型和参数列表)。覆盖实际上是多态性的一种体现,子类提供了对父类方法的新实现。
public class ParentClass {
// 父类方法
void displayMessage() {
System.out.println("Hello from ParentClass");
}
}
public class ChildClass extends ParentClass {
// 子类覆盖(重写)了父类方法
@Override
void displayMessage() {
System.out.println("Hello from ChildClass");
}
}
对象的创建与空间分配
声明对象: 在程序中声明一个对象时,会使用类的构造器或工厂方法来创建一个新的对象。
MyClass myObject; // 声明一个对象引用
使用 new
关键字创建对象: 使用 new
关键字调用类的构造方法来创建对象。这个过程包括为对象分配内存空间,并执行构造方法对对象进行初始化。
myObject = new MyClass(); // 创建一个 MyClass 类的对象
在这个过程中,内存管理系统会根据对象的大小分配足够的内存空间,构造方法则会初始化对象的各个成员变量。
初始化对象: 构造方法负责初始化对象的状态,可以通过构造方法的参数传递初始值,或者在构造方法中对成员变量进行赋值。
public class MyClass {
// 成员变量
int x;
// 构造方法
public MyClass(int initialX) {
// 初始化对象
x = initialX;
}
}
在对象创建完成后,可以使用构造方法中传递的初始值对对象的状态进行定制。
对象引用与实例之间的关系: 对象引用是指在程序中使用的对象的名称,而实例是分配了内存空间并初始化后的对象。通过对象引用,程序可以访问对象的方法和成员变量。
MyClass myObject = new MyClass(42); // 创建对象并将对象引用赋给 myObject
int value = myObject.x; // 通过对象引用访问对象的成员变量
总体而言,对象的创建与空间分配是在程序运行时进行的,其中包括为对象分配内存空间和执行构造方法初始化对象的过程。这使得面向对象编程能够更灵活地进行对象的创建和使用。
类,抽象类与接口,及其继承特点
类(Class):
-
定义: 类是Java中一种基本的编程结构,用于封装数据和行为。它是对象的蓝图,定义了对象的属性和方法。
-
特点:
- 可以包含实例变量(成员变量)和实例方法。
- 可以被实例化为对象。
public class MyClass { // 成员变量 private int x; // 实例方法 public void setX(int value) { this.x = value; } public int getX() { return this.x; } }
抽象类(Abstract Class):
-
定义: 抽象类是不能被实例化的类,用于定义一些通用的属性和方法,但其中的一些方法可以是抽象的,需要在子类中进行实现。
-
特点:
- 可以包含实例变量、实例方法,也可以包含抽象方法。
- 不能被直接实例化,需要通过子类继承并实现抽象方法。
public abstract class AbstractClass { // 成员变量 protected int x; // 抽象方法 public abstract void setX(int value); public abstract int getX(); }
接口(Interface):
-
定义: 接口是一种抽象类型,定义了一组抽象方法,但没有实例变量。类通过实现接口来获得某些行为。
-
特点:
- 可以包含抽象方法,常量(默认为
public static final
),默认方法(default
关键字)和静态方法(static
关键字)。 - 类通过
implements
关键字实现接口,并提供接口中定义的方法实现。public interface MyInterface { // 常量 int CONSTANT_VALUE = 42; // 抽象方法 void setX(int value); int getX(); }
继承特点:
- 类继承:
- 类可以继承另一个类。
- 子类继承父类的属性和方法。
- 子类可以重写(覆盖)父类的方法。
public class SubClass extends MyClass { // 子类可以访问父类的成员变量和方法 // 子类可以重写父类的方法 }
抽象类继承:
- 抽象类可以继承另一个抽象类或具体类。
- 子类需要实现抽象类中的抽象方法。
public abstract class SubAbstractClass extends AbstractClass { // 子类需要实现抽象类中的抽象方法 }
接口实现:
- 类可以实现多个接口。
- 类需要提供接口中定义的方法实现。
-
public class ImplementingClass implements MyInterface { // 类需要提供接口中定义的方法实现 }
- 可以包含抽象方法,常量(默认为
抽象类和接口的区别
-
方法的实现:
- 抽象类可以包含抽象方法(即没有实现的方法)和具体方法(有实现的方法)。
- 接口只能包含抽象方法,所有方法都没有实现。
-
继承:
- 一个类可以继承自一个抽象类,并且子类可以继承或实现抽象类中的方法。
- 一个类可以实现多个接口,但只能继承一个类(抽象或具体)。
-
构造方法:
- 抽象类可以有构造方法,而接口不能有构造方法。
-
访问修饰符:
- 抽象类中的方法可以有不同的访问修饰符,包括public、protected、default(包级别)等。
- 接口中的方法默认是public的,并且不允许使用其他修饰符。
-
变量:
- 抽象类可以包含实例变量、静态变量,也可以有普通方法。
- 接口中只能包含常量(public static final)和抽象方法。
-
多重继承:
- 一个类只能继承一个抽象类,但可以实现多个接口。
- 这意味着在 Java 中,通过接口可以实现类似多重继承的效果。
-
设计目的:
- 抽象类主要用于在相关类之间建立共同的基类,提供一些通用的方法,有时可能包含一些默认的实现。
- 接口用于定义一种契约,确保实现该接口的类都提供了特定的行为。
-
举个栗子
抽象类 就像是一张动物园规定的画图模板,上面画了一些轮廓和一些特征,比如说“有四条腿”、“有尾巴”等等。这个模板规定了动物的一些共同特征,但具体是画什么动物,比如是狮子还是大象,这个模板并没有具体说明。
abstract class AnimalTemplate {
// 共同特征
public void hasLegs() {
System.out.println("有四条腿");
}
public void hasTail() {
System.out.println("有尾巴");
}
// 具体的动物由子类去实现
public abstract void draw();
}
接口 则可以看作是一个动物园规定的加入条件,例如“要成为我们的动物,必须会游泳、会飞翔”。这里不关心你是狗、猫还是鸟,只要你符合这两个条件,就可以成为动物园的一员。
interface AnimalRequirements {
void canSwim();
void canFly();
}
现在,我们来创建一只动物,比如一只企鹅。这只企鹅既要符合动物园规定的基本特征(抽象类的模板),又要满足动物园的加入条件(接口的要求)。
class Penguin extends AnimalTemplate implements AnimalRequirements {
// 实现抽象类的具体细节
@Override
public void draw() {
System.out.println("画出一只企鹅");
}
// 实现接口的要求
@Override
public void canSwim() {
System.out.println("会游泳");
}
@Override
public void canFly() {
System.out.println("不会飞翔");
}
}
匿名内部类,lambda表达式
后面有生动的解释
1. 匿名内部类:
匿名内部类是一种没有名字的局部内部类,通常用于创建实现接口或继承类的对象。它的语法形式如下:
实现接口的匿名内部类:
interface MyInterface {
void myMethod();
}
public class Example {
public static void main(String[] args) {
// 匿名内部类实现接口
MyInterface myObject = new MyInterface() {
@Override
public void myMethod() {
System.out.println("Hello from anonymous class!");
}
};
// 调用接口方法
myObject.myMethod();
}
}
继承类的匿名内部类:
class ParentClass {
void parentMethod() {
System.out.println("Parent method");
}
}
public class Example {
public static void main(String[] args) {
// 匿名内部类继承类
ParentClass myObject = new ParentClass() {
@Override
void parentMethod() {
System.out.println("Hello from anonymous class!");
}
};
// 调用继承类的方法
myObject.parentMethod();
}
}
2. Lambda表达式:
Lambda表达式是Java 8引入的一种语法糖,用于创建匿名函数。主要用于函数式接口,即只有一个抽象方法的接口。Lambda表达式的语法形式如下:
Lambda表达式实现接口:
interface MyInterface {
void myMethod();
}
public class Example {
public static void main(String[] args) {
// Lambda表达式实现接口
MyInterface myObject = () -> System.out.println("Hello from lambda expression!");
// 调用接口方法
myObject.myMethod();
}
}
Lambda表达式实现Runnable接口:
public class Example {
public static void main(String[] args) {
// Lambda表达式实现Runnable接口
Runnable myRunnable = () -> System.out.println("Hello from lambda expression!");
// 使用Lambda表达式创建线程
Thread thread = new Thread(myRunnable);
thread.start();
}
}
Lambda表达式的特点:
- 简洁:Lambda表达式通常比匿名内部类更为简洁。
- 适用于函数式接口:主要用于实现函数式接口中的抽象方法。
- 函数参数类型的推断:可以省略参数类型,编译器会根据上下文进行类型推断。
- 闭包:可以访问外部变量。
总体来说,Lambda表达式和匿名内部类都提供了一种便捷的方式用于实现接口或创建匿名对象,具体选择取决于使用场景和个人偏好。Lambda表达式通常更简洁,适用于函数式接口,而匿名内部类则可以用于更广泛的场景。
举个栗子
匿名内部类:
想象一下,小明在画画时,他发现有一类画只需要画一次,不需要取名字,也不需要保存在画册里。他就直接在画纸上画了一张,然后就扔了。这就好比是我们在写Java代码的时候,有时候只需要用一次的类,就不用专门写一个类名,直接在代码里定义。
// 小明画了一张画
Painter myPainter = new Painter() {
@Override
public void draw() {
System.out.println("画了一张画!");
}
};
// 小明画画
myPainter.draw();
这里的Painter
就是一个虚构的画画接口,小明用匿名内部类直接实现了这个接口,然后画了一张画。
Lambda表达式:
再想象一下,小明觉得每次画画都要写好多字太麻烦,于是他用了一种新的方法,不再写字,只画图案。这就好比我们在写Java代码时,有了Lambda表达式,不再需要写一大堆代码,只需要关注实际的操作。
// 小明画画
Painter myPainter = () -> System.out.println("画了一张画!");
// 小明画画
myPainter.draw();
这里的Painter
还是画画的接口,而() -> System.out.println("画了一张画!")
就是小明用Lambda表达式画的图案,简洁而直观。
接口的定义,接口的实现、测试
接口的定义:
假设我们要定义一个接口表示可移动的事物:
// 定义可移动接口
public interface Movable {
void move(); // 抽象方法,表示移动的行为
}
接口的实现:
现在,我们来实现这个接口。假设有一辆小车和一只小猫,它们都可以移动:
// 小车类实现可移动接口
public class Car implements Movable {
@Override
public void move() {
System.out.println("The car is moving.");
}
}
// 小猫类实现可移动接口
public class Cat implements Movable {
@Override
public void move() {
System.out.println("The cat is moving.");
}
}
测试接口的实现:
现在我们创建一个测试类,测试这些可移动的事物:
public class TestMoveable {
public static void main(String[] args) {
// 创建小车对象
Movable car = new Car();
// 调用移动方法
car.move();
// 创建小猫对象
Movable cat = new Cat();
// 调用移动方法
cat.move();
}
}
解释:
-
接口的定义:
Movable
接口定义了一个抽象方法move()
,表示移动的行为。 -
接口的实现:
Car
类和Cat
类分别实现了Movable
接口,实现了move()
方法,提供了它们具体的移动方式。 -
测试接口的实现: 在
TestMoveable
类中,我们创建了一个小车对象和一个小猫对象,都可以被当作Movable
类型使用。通过调用它们的move()
方法,我们可以看到它们具体的移动行为。