抽象类

抽象方法:

抽象方法是在抽象类中声明但没有实现的方法。它只包含方法的签名,没有方法体。抽象方法必须在子类中被重写和实现。

特点

  1. 无实现:抽象方法只包含方法的声明,没有具体的实现。这意味着它们不能被直接调用。

  2. 强制重写:任何继承抽象类的子类必须实现所有的抽象方法,否则该子类也必须被声明为抽象类。

  3. 多态性:抽象方法支持多态性,允许不同的子类提供不同的实现,从而实现灵活的代码设计。

  4. 提高代码可维护性:通过定义抽象方法,可以清晰地指定子类需要实现的功能,增强代码的可读性和可维护性。

  5. 不能实例化:抽象类本身不能被实例化,因此抽象方法也不能被直接调用。

注意点

  1. 定义位置:抽象方法必须在抽象类中定义,不能在普通类中。

  2. 访问修饰符:抽象方法可以使用不同的访问修饰符(如 public、protected、private),但通常使用 public 或 protected,以便子类能够访问。

  3. 参数和返回类型:抽象方法可以有参数和返回类型,子类在实现时需要保持一致。

  4. 实现时必须完整:在子类中实现抽象方法时,必须提供完整的实现,不能留有未实现的部分。

  5. 遵循设计原则:使用抽象方法时,应遵循设计原则,如单一职责原则和开放-关闭原则,以确保代码的灵活性和可扩展性。

在Java中,使用关键字abstract来声明抽象方法。示例代码如下:

abstract class Animal {
    public abstract void sound();
}

class Dog extends Animal {
    public void sound() {
        System.out.println("汪汪汪");
    }
}

class Cat extends Animal {
    public void sound() {
        System.out.println("喵喵喵");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal dog = new Dog();
        dog.sound(); // 输出:汪汪汪

        Animal cat = new Cat();
        cat.sound(); // 输出:喵喵喵
    }
}

在上面的示例中,Animal是一个抽象类,其中声明了一个抽象方法sound()DogCatAnimal的子类,它们必须实现sound()方法。在Main类中,我们创建了DogCat的实例,并调用了它们的sound()方法。输出结果分别是"汪汪汪"和"喵喵喵"。

final关键词

特点:

  1. 不可继承:final修饰的类不能被继承,即为最终类。

  2. 不可重写:final修饰的方法不能被子类重写,即为最终方法。

  3. 不可修改:final修饰的变量表示常量,只能被赋值一次,赋值后数值不可改变。

示例代码:

final class FinalClass {
    final int constantValue = 10;

    final void finalMethod() {
        System.out.println("This is a final method.");
    }
}

class SubClass extends FinalClass {
    // 无法继承最终类FinalClass
}

class SubClass2 {
    final int constantValue = 20;

    // 无法重写最终方法finalMethod
    // final void finalMethod() {}
}

在示例代码中,FinalClass是一个final类,无法被其他类继承。它包含一个final变量constantValue和一个final方法finalMethodconstantValue是一个常量,只能被赋值一次。finalMethod是一个最终方法,不能被子类重写。

另外,SubClass类试图继承FinalClass,但由于FinalClass是final类,所以会导致编译错误。SubClass2类中也展示了一个常量constantValue和试图重写final方法的情况,但由于final关键词的限制,这些操作都会导致编译错误。

多态

多态是面向对象编程中的一个重要概念,它允许使用不同的对象来调用相同的方法,从而实现灵活的代码设计和扩展性。

父类引用指向子类对象是多态的一种表现形式。在面向对象编程中,父类可以作为子类的通用类型,通过父类引用来引用子类对象。这样做的好处是可以实现代码的灵活性和可扩展性。

具体来说,当父类引用指向子类对象时,可以通过父类引用调用子类中重写的方法。这是因为在编译时,编译器只会检查父类中定义的方法,而不会检查实际引用的对象的类型。然后,在运行时,程序会根据实际引用的对象的类型来确定调用哪个方法。

特点

  1. 多态性:多态性是指同一个方法可以在不同的对象上产生不同的行为。即使调用的是相同的方法,但实际执行的代码可能会根据对象的类型而有所不同。

  2. 动态绑定:多态性是通过动态绑定实现的。在运行时,系统会根据对象的实际类型来决定调用哪个方法。

  3. 代码重用:多态性可以提高代码的重用性。通过定义抽象类或接口,并在子类中实现具体的方法,可以实现不同类型的对象共享相同的代码。

示例代码

abstract class Animal {
    public abstract void sound();
}

class Dog extends Animal {
    public void sound() {
        System.out.println("汪汪汪");
    }
}

class Cat extends Animal {
    public void sound() {
        System.out.println("喵喵喵");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal dog = new Dog();
        dog.sound(); // 输出:汪汪汪

        Animal cat = new Cat();
        cat.sound(); // 输出:喵喵喵
    }
}

在上面的示例中,Animal是一个抽象类,其中声明了一个抽象方法sound()DogCatAnimal的子类,它们分别实现了sound()方法。在Main类中,我们创建了DogCat的实例,并将它们赋值给Animal类型的变量。然后,我们调用了这些变量的sound()方法。由于多态性的存在,实际执行的方法取决于对象的类型。因此,输出结果分别是"汪汪汪"和"喵喵喵"。

通过多态性,我们可以在不修改现有代码的情况下,轻松地添加新的子类,并在运行时根据需要调用相应的方法。这种灵活性和扩展性是多态性的重要优势。

多态的几种实现方法

多态是面向对象编程中的重要概念,它可以通过不同的方式来实现。以下是几种常见的多态实现方法:

1. 方法重写(Override)

方法重写是最常见的实现多态的方式之一。当子类重写(Override)父类的方法时,可以通过父类引用指向子类对象,从而实现多态性。在运行时,系统会根据实际对象的类型来调用相应的方法。

class Animal {
    public void sound() {
        System.out.println("动物发出声音");
    }
}

class Dog extends Animal {
    @Override
    public void sound() {
        System.out.println("汪汪汪");
    }
}

class Cat extends Animal {
    @Override
    public void sound() {
        System.out.println("喵喵喵");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal dog = new Dog();
        dog.sound(); // 输出:汪汪汪

        Animal cat = new Cat();
        cat.sound(); // 输出:喵喵喵
    }
}

在上面的示例中,Animal类有一个sound()方法,DogCat类分别重写了这个方法。在Main类中,通过父类引用指向子类对象,实现了多态性。

2. 接口实现(Interface)

另一种实现多态的方式是通过接口。接口定义了一组抽象方法,而实现接口的类需要提供这些方法的具体实现。通过接口,不同的类可以实现相同的接口并提供不同的实现,从而实现多态性。

interface Animal {
    void sound();
}

class Dog implements Animal {
    @Override
    public void sound() {
        System.out.println("汪汪汪");
    }
}

class Cat implements Animal {
    @Override
    public void sound() {
        System.out.println("喵喵喵");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal dog = new Dog();
        dog.sound(); // 输出:汪汪汪

        Animal cat = new Cat();
        cat.sound(); // 输出:喵喵喵
    }
}

在上面的示例中,Animal是一个接口,定义了一个sound()方法。DogCat类分别实现了Animal接口,并提供了各自的sound()方法实现。通过接口,实现了不同类之间的多态性。

3. 抽象类实现(Abstract Class)

抽象类也可以用来实现多态性。抽象类可以包含抽象方法,子类必须实现这些抽象方法。通过抽象类,可以定义通用的行为并要求子类提供具体实现,从而实现多态性。

abstract class Animal {
    public abstract void sound();
}

class Dog extends Animal {
    @Override
    public void sound() {
        System.out.println("汪汪汪");
    }
}

class Cat extends Animal {
    @Override
    public void sound() {
        System.out.println("喵喵喵");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal dog = new Dog();
        dog.sound(); // 输出:汪汪汪

        Animal cat = new Cat();
        cat.sound(); // 输出:喵喵喵
    }
}

在上面的示例中,Animal是一个抽象类,其中声明了一个抽象方法sound()DogCat类分别继承了Animal抽象类,并实现了sound()方法。通过抽象类,实现了多态性的效果。

4. 使用父类作为方法的形参

另一种实现多态的方式是将父类作为方法的形参。通过将父类作为方法的形参,可以接受不同子类的对象作为实参,从而实现多态性。

class Animal {
    public void sound() {
        System.out.println("动物发出声音");
    }
}

class Dog extends Animal {
    @Override
    public void sound() {
        System.out.println("汪汪汪");
    }
}

class Cat extends Animal {
    @Override
    public void sound() {
        System.out.println("喵喵喵");
    }
}

public class Main {
    public static void makeSound(Animal animal) {
        animal.sound();
    }

    public static void main(String[] args) {
        Animal dog = new Dog();
        Animal cat = new Cat();

        makeSound(dog); // 输出:汪汪汪
        makeSound(cat); // 输出:喵喵喵
    }
}

在上面的示例中,makeSound()方法接受一个Animal类型的参数。在main()方法中,我们创建了DogCat的实例,并将它们作为实参传递给makeSound()方法。由于多态性的存在,实际执行的方法取决于传递的对象的类型。因此,输出结果分别是"汪汪汪"和"喵喵喵"。

通过将父类作为方法的形参,可以实现对不同子类对象的统一处理,提高代码的灵活性和可扩展性。

5. 使用父类作为方法的返回值

还可以将父类作为方法的返回值,从而实现多态性。通过将父类作为方法的返回值,可以返回不同子类的对象,实现灵活的代码设计。

class Animal {
    public void sound() {
        System.out.println("动物发出声音");
    }
}

class Dog extends Animal {
    @Override
    public void sound() {
        System.out.println("汪汪汪");
    }
}

class Cat extends Animal {
    @Override
    public void sound() {
        System.out.println("喵喵喵");
    }
}

public class Main {
    public static Animal createAnimal(String type) {
        if (type.equals("dog")) {
            return new Dog();
        } else if (type.equals("cat")) {
            return new Cat();
        } else {
            return null;
        }
    }

    public static void main(String[] args) {
        Animal dog = createAnimal("dog");
        Animal cat = createAnimal("cat");

        dog.sound(); // 输出:汪汪汪
        cat.sound(); // 输出:喵喵喵
    }
}

在上面的示例中,createAnimal()方法根据传入的参数类型创建相应的子类对象,并将其作为父类Animal的对象返回。在main()方法中,我们调用createAnimal()方法创建了DogCat的实例,并将它们赋值给Animal类型的变量。然后,我们调用这些变量的sound()方法。由于多态性的存在,实际执行的方法取决于对象的类型。因此,输出结果分别是"汪汪汪"和"喵喵喵"。

通过将父类作为方法的返回值,可以实现根据不同的条件返回不同子类的对象,实现灵活的代码设计。

instanceof

instanceof 运算符

instanceof 是 Java 中的一个运算符,用于判断一个对象是否是某个类的实例或者实现了某个接口。它的语法格式为:对象 instanceof 类名对象 instanceof 接口名

特点

  1. 返回值instanceof 运算符返回一个布尔值,如果对象是指定类的实例或者实现了指定接口,则返回 true,否则返回 false

  2. 用途:通常用于在运行时检查对象的类型,以便进行相应的处理。

示例代码

class Animal {
    public void sound() {
        System.out.println("动物发出声音");
    }
}

class Dog extends Animal {
    public void sound() {
        System.out.println("汪汪汪");
    }
}

class Cat extends Animal {
    public void sound() {
        System.out.println("喵喵喵");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal dog = new Dog();
        Animal cat = new Cat();

        if (dog instanceof Dog) {
            System.out.println("dog 是 Dog 类的实例");
            Dog specificDog = (Dog) dog;
            specificDog.sound(); // 输出:汪汪汪
        }

        if (cat instanceof Cat) {
            System.out.println("cat 是 Cat 类的实例");
            Cat specificCat = (Cat) cat;
            specificCat.sound(); // 输出:喵喵喵
        }
    }
}

在上面的示例中,我们创建了 DogCat 的实例,并将它们赋值给 Animal 类型的变量 dogcat。然后,我们使用 instanceof 运算符来检查 dogcat 是否分别是 DogCat 类的实例。如果是,就进行相应的处理。

通过 instanceof 运算符,我们可以在运行时动态地判断对象的类型,从而实现更灵活的代码设计。

接口

普通类、抽象类和接口的区别

普通类

  • 定义:普通类是最常见的类,可以被实例化为对象,包含属性、方法和构造函数。

  • 特点:普通类可以包含成员变量、成员方法,可以被实例化为对象,也可以被继承。

  • 实例化:普通类可以直接实例化为对象,创建类的实例。

  • 方法:普通类中的方法可以有具体的实现,也可以包含抽象方法。

  • 继承:普通类可以被其他类继承,可以作为父类或子类存在。

抽象类

  • 定义:抽象类是不能被实例化的类,其中可以包含抽象方法和具体方法,用于作为其他类的父类。

  • 特点:抽象类不能被实例化,只能被继承,子类必须实现抽象方法。

  • 抽象方法:抽象类中可以包含抽象方法,子类必须实现这些抽象方法。

  • 继承:抽象类可以被其他类继承,子类需要实现抽象方法。

  • 多态性:抽象类可以通过多态性实现不同子类的对象调用相同的方法。

接口

  • 定义:接口是一种抽象类型,其中只包含常量和抽象方法的声明,不能包含具体的方法实现。

  • 特点:接口中的方法默认为抽象方法,不包含方法体,不能被实例化,只能被实现。

  • 实现:类可以实现一个或多个接口,实现接口的类必须实现接口中定义的所有方法。

  • 多继承:接口支持多继承,一个类可以实现多个接口。

  • 灵活性:接口提供了一种规范,实现类可以根据需要实现接口中的方法,实现了代码的灵活性和可扩展性。

总结:

  • 普通类是最基本的类,可以被实例化和继承。

  • 抽象类是一种不能被实例化的类,用于作为其他类的父类,可以包含抽象方法和具体方法。

  • 接口是一种抽象类型,定义了一组方法的规范,实现类必须实现接口中的所有方法。接口提供了一种多继承的机制,增强了代码的灵活性。

接口定义

接口是 Java 中一种抽象类型,它定义了一组抽象方法,但没有具体的实现。接口可以包含常量和抽象方法,但不能包含普通变量和方法的实现。类实现接口时,必须实现接口中定义的所有方法。

特点

  1. 抽象性:接口中的方法都是抽象的,没有方法体。

  2. 多继承:类可以实现多个接口,从而实现多继承的效果。

  3. 解耦性:接口可以帮助实现类之间的解耦,降低类之间的耦合度。

  4. 规范性:接口定义了一组规范,实现类必须按照规范提供具体实现。

  5. 扩展性:接口可以被多个类实现,从而提高代码的扩展性。

接口示例

// 定义一个接口
interface Animal {
    void sound(); // 抽象方法
}

// 实现接口的类
class Dog implements Animal {
    @Override
    public void sound() {
        System.out.println("汪汪汪");
    }
}

class Cat implements Animal {
    @Override
    public void sound() {
        System.out.println("喵喵喵");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal dog = new Dog();
        dog.sound(); // 输出:汪汪汪

        Animal cat = new Cat();
        cat.sound(); // 输出:喵喵喵
    }
}

在上面的示例中,定义了一个名为Animal的接口,其中包含一个抽象方法sound()DogCat类分别实现了Animal接口,并提供了sound()方法的具体实现。在Main类中,创建了DogCat的实例,并通过接口类型的引用调用了它们的sound()方法,实现了多态的效果。

通过接口,可以定义一组规范,让不同的类按照这些规范提供具体实现,从而实现代码的灵活性和扩展性。

约定

  1. 不能被实例化:接口和抽象类一样,不能被直接实例化(使用 new 关键字创建对象)。

  2. 继承的是类:类通过关键字 implements 实现接口,而不是继承。一个类可以实现多个接口。

  3. 实现的是接口:类通过实现接口来定义接口中声明的方法,实现接口的类必须提供接口中所有方法的具体实现。

  4. 有继承和实现的顺序:在类继承和实现接口的情况下,先继承类再实现接口。

  5. 类只能继承一个,实现可以实现多个:Java 中类只支持单继承,但可以实现多个接口。

  6. 接口之间可以互相继承,多继承:接口可以通过 extends 关键字继承其他接口,实现多继承的效果。

接口是Java中一种重要的机制,用于定义一组抽象方法,以实现类之间的共享行为和规范。通过接口,可以实现代码的解耦和灵活性,提高代码的可维护性和可扩展性。

以下是一个示例代码,展示了如何定义接口和实现接口:

// 定义一个接口
interface Animal {
    void eat();
    void sleep();
}

// 实现接口
class Cat implements Animal {
    @Override
    public void eat() {
        System.out.println("Cat is eating.");
    }

    @Override
    public void sleep() {
        System.out.println("Cat is sleeping.");
    }
}

class Dog implements Animal {
    @Override
    public void eat() {
        System.out.println("Dog is eating.");
    }

    @Override
    public void sleep() {
        System.out.println("Dog is sleeping.");
    }
}

public class Main {
    public static void main(String[] args) {
        Cat cat = new Cat();
        cat.eat();
        cat.sleep();

        Dog dog = new Dog();
        dog.eat();
        dog.sleep();
    }
}

在上面的示例中,我们定义了一个接口 Animal,它包含了两个抽象方法 eat()sleep()。然后我们创建了两个类 CatDog,它们分别实现了 Animal 接口,并提供了具体的方法实现。在 Main 类中,我们创建了一个 Cat 对象和一个 Dog 对象,并调用它们的方法。通过接口,我们可以将不同的类归为一类,实现了代码的解耦和灵活性。

java值传递

Java值传递

在Java中,所有的参数传递都是值传递。这意味着当你将一个变量作为参数传递给一个方法时,实际上传递的是变量的值,而不是变量本身。无论是基本数据类型还是对象引用,都是按值传递的方式传递的。

示例代码

public class ValuePassingExample {

    public static void main(String[] args) {
        int number = 10;
        System.out.println("Before calling changeNumber method: " + number);
        
        changeNumber(number);
        
        System.out.println("After calling changeNumber method: " + number);
    }
    
    public static void changeNumber(int num) {
        num = 20;
        System.out.println("Inside changeNumber method: " + num);
    }
}

在上面的示例代码中,我们定义了一个ValuePassingExample类,其中包含一个main方法和一个changeNumber方法。在main方法中,我们声明一个整型变量number并赋值为10,然后调用changeNumber方法并传递number作为参数。在changeNumber方法中,我们将参数num的值修改为20。在main方法中打印number的值,可以看到虽然在changeNumber方法中修改了num的值,但是number的值仍然是10,这证明了Java中的值传递特性。

这就是Java中值传递的工作原理。