Java面向对象

一、概念

Java面向对象编程是一种编程范式,它将程序中的数据和对数据的操作封装在对象中,通过对象之间的消息传递来实现程序的功能。比如,单独的个人就是一个对象,你会编程,其他人不能直接访问你编程的能力,需要和你进行交流提供你的编程能力。

1.1 类 - Class

类是对象的模版(原型、蓝图),它定义了对象的属性(成员变量)和行为(方法)。

1.1.1 类的表现形式

普通类、抽象类、接口类、枚举类,内部类(成员内部类、静态内部类、方法内部类、匿名内部类)。
示例:

// 普通类
class OrdinaryClass {
    void display() {
        System.out.println("This is an ordinary class.");
    }
}

// 接口类
interface MyInterface {
    void myMethod();
}

// 抽象类
abstract class AbstractClass {
    abstract void abstractMethod();

    void concreteMethod() {
        System.out.println("This is a concrete method in abstract class.");
    }
}

// 枚举类
enum MyEnum {
    VALUE1, VALUE2, VALUE3
}

// 外部类
public class OuterClass {

    // 成员内部类
    class MemberInnerClass {
        void display() {
            System.out.println("This is a member inner class.");
        }
    }

    // 静态内部类
    static class StaticInnerClass {
        void display() {
            System.out.println("This is a static inner class.");
        }
    }

    // 方法内部类
    void outerMethod() {
        class MethodInnerClass {
            void display() {
                System.out.println("This is a method inner class.");
            }
        }

        MethodInnerClass methodInner = new MethodInnerClass();
        methodInner.display();
    }

    // 匿名内部类(在接口或抽象类的实例化中使用)
    MyInterface anonymousInnerClass = new MyInterface() {
        @Override
        public void myMethod() {
            System.out.println("This is an anonymous inner class.");
        }
    };

    public static void main(String[] args) {
        // 创建对象并调用方法
        OrdinaryClass ordinary = new OrdinaryClass();
        ordinary.display();

        // 实现接口并调用方法
        MyInterface interfaceImpl = new MyInterface() {
            @Override
            public void myMethod() {
                System.out.println("This is an interface implementation.");
            }
        };
        interfaceImpl.myMethod();

        // 继承抽象类并调用方法
        AbstractClass abstractClassImpl = new AbstractClass() {
            @Override
            void abstractMethod() {
                System.out.println("This is an abstract method implementation.");
            }
        };
        abstractClassImpl.abstractMethod();
        abstractClassImpl.concreteMethod();

        // 使用枚举类型
        MyEnum myEnum = MyEnum.VALUE1;
        System.out.println("Selected enum value: " + myEnum);

        // 创建外部类对象
        OuterClass outerObject = new OuterClass();

        // 创建成员内部类对象
        OuterClass.MemberInnerClass memberInnerObject = outerObject.new MemberInnerClass();
        memberInnerObject.display();

        // 创建静态内部类对象
        OuterClass.StaticInnerClass staticInnerObject = new OuterClass.StaticInnerClass();
        staticInnerObject.display();

        // 调用外部类的方法内部类
        outerObject.outerMethod();

        // 调用匿名内部类的方法
        outerObject.anonymousInnerClass.myMethod();
    }
}

1.2 对象 - Object

对象是类的实例,具有类定义的属性和行为。

包 - package

在Java中,package 是用来组织和管理类文件的一种机制,它为类提供了命名空间,有助于解决命名冲突问题。

二、面向对象四大特征

面向对象的四大特征,也称为免息那个对象程序设计的核心原则是 封装、继承、多态、抽象。

2.1 封装

封装是将类的实现细节隐藏起来,只对外提供必要的接口。通过访问修饰符(public、private、protected等),可以控制成员变量和方法的访问权限。封装增加了代码的安全性和可维护性。
示例:

public class Person {
    private String name; // 私有成员变量

    public String getName() {
        return name;
    }

    public void setName(String newName) {
        name = newName;
    }
}

2.2 继承

继承是一种机制,允许一个类(子类)继承另一个类(父类)的属性和方法。子类可以重用父类的代码,并可以在不修改父类的情况下添加新的功能。增加代码的复用性。

判断继承关系的简单规则is-a
“is-a” 规则的另一中表述法是置换法则。它表明程序中出现超类对象的任何地方都可以用子类对象置换。

“is-a” 规则,例如,每个经理是一个员工,主管是一个员工;
示例:

Employee e; // 雇员
e = new Employee(); // 普通雇员是一个雇员
e = new Manager();  // 主管雇员是一个雇员
// 父类
public class Animal {
    void eat() {
        System.out.println("Animal is eating");
    }
}

// 子类
public class Dog extends Animal {
    void bark() {
        System.out.println("Dog is barking");
    }
}

注意:

  1. 子类拥有父类对象所有的属性和方法(包括私有属性和私有方法),但是子类无法访问父类中的私有属性和私有方法,只是拥有;
  2. 子类可以拥有自己的属性和方法,即子类可以对父类进行扩展;
  3. 子类可以用到自己的方式重新实现父类的方法(重写);
  4. 构造方法无法被继承;

2.3 多态

多态是继承情况下对象的一种表现形式。

多态允许同一个方法在不同的对象上表现出不同的行为。Java中的多态有两种形式:编译时多态(方法重载)和运行时多态(方法重写)。

is-a 置换法则中,Employee 可以引用一个 Employee 对象,也可以引用一个 Employee 类的子类对象 Manager,这种员工的表现形式(普通员工、主管)称为多态。简单的说就是一个对象具有多种形态,具体表现就是父类的引用指向子类的实例。
示例:

// 方法重载(编译时多态)
public class Calculator {
    int add(int a, int b) {
        return a + b;
    }

    double add(double a, double b) {
        return a + b;
    }
}

// 方法重写(运行时多态)
public class Animal {
    void makeSound() {
        System.out.println("Animal makes a sound");
    }
}
public class Dog extends Animal {
    void makeSound() {
        System.out.println("Dog barks");
    }
}

小结:

  1. 多态条件:继承、重写、父类指向子类对象;
  2. 对象类型和引用类型之间具有继承/实现关系;
  3. 引用类型变量发出的方法调用到底是哪个类中的方法,必须在程序运行期间才能确定;
  4. 多态不能调用只在子类存在但在父类不存在的方法;
  5. 如果子类重写了父类的方法,真正执行的是子类覆盖的方法,如果子类没有覆盖父类的方法,执行的是父类的方法;

2.3.1 重载

定义: 在同一个类中,可以定义多个方法,方法名相同但参数列表不同的情况称为方法重载。
条件: 方法名相同,但参数列表必须不同(包括参数的类型、个数、顺序);
返回值:与返回值类型无关;
示例:

public class Example {
    public int add(int a, int b) {
        return a + b;
    }
    
    public double add(double a, double b) {
        return a + b;
    }
    
    public String concatenate(String a, String b) {
        return a + b;
    }
}

2.3.2 重写

定义:子类可以提供对父类中的方法的新实现,称为方法重写;
条件:子类的方法名、参数列表和返回类型必须与父类的方法相同,访问修饰符不低于父类;(public、protected、default),抛出异常范围必须小于等于父类抛出的异常;
关键字:使用 @Override注解可以帮助编译器检查是否满足重写的条件;
示例

class Animal {
    public void makeSound() {
        System.out.println("Animal makes a sound");
    }
}

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

小结:

  1. 只有继承才可能发生重写;
  2. 重写发生在运行期,是子类对父类方法进行重新编写和实现;
  3. 重写方法返回值、方法名、参数列表必须相同,否则构不成重写;
  4. 访问修饰符范围高于或等于父类访问修饰符范围(父类是public,子类就不能是protected);
  5. 抛出异常范围大于或等于父类抛出异常的范围;
  6. final 修饰的类不能被继承(因为final修饰的类是不可被修改的),所以其方法不能被重写;
  7. 父类方法访问修饰符为 private、final、static 时,子类不能重写该方法,但static方法可以被在此声明(因为static方法属于类);
  8. 构造方法无法被重写;

2.4 抽象

抽象类和接口是用于实现抽象的概念。抽象类可以包含抽象方法和具体方法,而接口只能包含抽象方法。

// 抽象类
public abstract class Shape {
    abstract void draw(); // 抽象方法
    void resize() {
        System.out.println("Resizing shape");
    }
}

// 接口
public interface Drawable {
    void draw(); // 抽象方法
    void resize();
}

三、面向对象设计之七大原则

3.1 单一职责原则(Single Responsibility Principle - SRP)

一个类应该只有一个引起变化的原因,也就是说一个类只负责一个功能领域中的相应职责。这有助于提高类的内聚性、减少类的复杂性。

通俗地讲,就是一个类只能负责一个职责,就像富士康流水线上的工人一样,每个人负责一键事情,修改一个类不能影响到别的功能,也就是说只有一个导致该类被修改的原因。

3.2 开闭原则(Open/Closed Principle - OCP)

一个软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。这意味着一个类的行为可以通过扩展来进行更改,而无需修改它的源代码。

:对扩展开放,支持方便的扩展;
:对修改关闭,严格限制对已有内容的修改;

任何对老接口的修改都会带来一定的风险。一个软件产品只要在生命周期内,都会发生变化,既然变化是不可避免的,我们就应该在设计时尽量适应这些变化,以提高项目的稳定性和灵活性,真正实现**“拥抱变化”**。开闭原则告诉我们尽量通过扩展软件实体的行为来实现变化,而不是通过修改现有代码来完成变化,它是为软件实体的未来事件而定制的对现有开发设计进行约束的一个原则。

3.3 里氏替换原则(Liskov Substitution Principle - LSP)

所有引用积累的地方必须能透明地使用其子类对象。

里氏替换原则强调的是设计和实现要依赖于抽象而非具体;子类只能去扩展基类,而不是隐藏或者覆盖基类,包含四层含义:
1. 子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法;
2. 子类中可以增加自己特有的方法;
3. 当子类的方法重写父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松;
4. 当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格;
5. 里氏替换原则是开闭原则的体现,约束继承泛滥;

3.4 接口隔离原则(Interface Segregation Principle - ISP)

一个类不应该被迫实现它不使用的类,类之间的依赖关系应该建立在最小的接口上。

一个类对另一个类的依赖应该建立在最小的接口上,通俗地讲就是需要什么就提供什么,不需要的就不提供;

接口汇总的方法应该尽量少,不要使接口过于臃肿,不要有很多不相关的逻辑方法;

接口隔离原则可提高代码高内聚、低耦合、高可读性、易于维护。

3.5 依赖倒置原则(Dependency Inversion Principle - DIP)

高层次模块应该依赖于低层次模块,两者都应该依赖于抽象。抽象不应该依赖具体实现,具体实现应该依赖于抽象。

  • 依赖倒置原则的核心就是要我们面向接口编程,理解了面向接口编程,也就理解了依赖倒置原则;
  • 低层模块尽量都要有抽象类或接口,或者两者都有;
  • 变量的声明类型尽量使抽象类或接口;
  • 使用继承时遵循里氏替换原则;
  • 设计和实现要依赖于抽象而非具体实现。一方面抽象化更符合人的思维习惯;另一方面,根据里氏替换原则,可以很容易将原来的抽象替换为扩展后的具体实现,这样就可以很好的支持开闭原则;
  • 减少类之间的耦合性;

3.6 迪米特法则/最少知道原则/最少知识原则(Law of Demeter - LoD)

一个对象应该对其他对象保持最少的了解。一个类应该对自己需要耦合或调用的类保持最少了解,也被称为最少知识原则。

3.7 组合/聚合复用原则(Composition/Aggregation Reuse Principle - CARP)

尽量采用组合和聚合,而不是使用继承来达到代码复用的目的,这样更灵活,降低了类之间的耦合性。

  • 组合/聚合的优点:类之间的耦合比较低,一个类的变化对其它类造成的影响比较少;
  • 组合/聚合的缺点:类的数量增多实现起来比较麻烦;
  • 继承的优点:由于很多方法父类已经实现,子类的视线会相对比较简单;
  • 继承的缺点:将父类暴漏给了子类,一定程度上破坏了封装性,父类的改变对子类影响比较大;

四、面向接口编程

面向接口编程是一种面向对象编程的思想,强调使用接口来定义对象之间的契约关系,而不是依赖具体的实现类。这样的编程方式有助于提高代码的灵活性、可维护性、可扩展性。

  1. 接口定义契约
  • 接口定义了对象应该具有的行为和方法,而不关心具体实现。通过定义接口,可以形成对外的契约,规范了对象之间的交互方式。
  1. 解耦实现与使用
  • 面向接口编程有助于解耦代码中的实现和使用部分。客户端代码只需要依赖接口,而不需要关心具体的实现类,从而降低代码的耦合度。
  1. 多态性
  • 通过面向接口编程,可以利用多态性。一个对象可以被看做是其实现的接口类型,从而可以在运行时动态选择具体的实现类,提高代码的灵活性。
  1. 易于替换和扩展
  • 由于客户端代码只依赖于接口,当需要替换或者添加新的实现时,只需要实现相应的接口即可,而无需修改客户端代码。这降低了维护和扩展的难度。
  1. 适应变化
  • 面向接口的设计更容易适应需求变化。当系统需要变更时,只需要替换实现了相同接口的新类,而不需要修改已有代码。
  1. 提高可测试性
  • 通过面向接口编程,可以更容易进行单元测试。可以使用接口的模拟对象或者桩对象,来测试代码的某个模块,而不需要真正的实现。
  1. 规范化设计
  • 面向接口编程促使开发者更注重设计和规范,因为接口定义了对象的行为和功能,有助于形成清晰的设计结构。

在Java中,接口是实现面向接口编程的主要手段。通过定义接口,类可以实现这些接口,从而表达其拥有某些特定的行为。这种编程方式是Java中利用多态性和接口的核心思想之一。

4.1 面向接口编程示例

下面是一个简单的Java示例,演示了面向接口编程的概念。我们将创建一个简单的图形(Shape)的例子,包括接口、抽象类、普通类以及一些内部类的概念:

// 接口定义图形的行为
interface Shape {
    double getArea();
    double getPerimeter();
}

// 抽象类作为接口的实现,也可以包含一些通用的行为
abstract class AbstractShape implements Shape {
    // 可以提供一些通用的实现
}

// 普通类实现具体的图形,如矩形
class Rectangle extends AbstractShape {
    private double width;
    private double height;

    public Rectangle(double width, double height) {
        this.width = width;
        this.height = height;
    }

    @Override
    public double getArea() {
        return width * height;
    }

    @Override
    public double getPerimeter() {
        return 2 * (width + height);
    }
}

// 枚举类表示图形的类型
enum ShapeType {
    RECTANGLE,
    // 可以添加其他图形类型
}

// 工厂类用于创建图形
class ShapeFactory {
    public static Shape createShape(ShapeType type, double... params) {
        switch (type) {
            case RECTANGLE:
                return new Rectangle(params[0], params[1]);
            // 可以添加其他图形类型的创建逻辑
            default:
                throw new IllegalArgumentException("Unsupported shape type");
        }
    }
}

public class Main {
    public static void main(String[] args) {
        // 使用工厂类创建图形
        Shape rectangle = ShapeFactory.createShape(ShapeType.RECTANGLE, 5.0, 3.0);

        // 打印图形的面积和周长
        System.out.println("Rectangle Area: " + rectangle.getArea());
        System.out.println("Rectangle Perimeter: " + rectangle.getPerimeter());
    }
}

这个示例中,我们定义了一个Shape接口,包括getArea和getPerimeter方法,表示图形的面积和周长。然后通过抽象类AbstractShape实现了这个接口,作为具体图形类的基类。Rectangle类实现了具体的矩形图形,而ShapeFactory工厂类用于创建不同类型的图形。

这个例子演示了面向接口编程的概念,通过接口和抽象类的结合,可以更好地组织代码,提高代码的灵活性和可维护性。

  • 9
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值