面向对象编程基础
面向对象编程(Object-oriented programming,简称OOP)是一种编程范式,它将数据和操作数据的方法封装在一起,以便用于重复使用和维护。
在面向对象编程中,一个对象是一个实体,具有属性和方法。属性是对象的状态,而方法是对象的行为。对象与对象之间可以通过消息进行通信,以实现协作。
类和对象
在OOP中,类是创建对象的蓝图,它定义了对象的属性和方法。对象是类的实例。可以使用以下语法创建类:
public class ClassName {
//类成员变量
//类方法
}
例如:
public class Person {
String name;
int age;
void sayHello() {
System.out.println("Hello, my name is " + name);
}
}
上面的代码创建了一个名为Person的类,该类具有name和age属性以及sayHello方法。可以使用以下代码创建Person对象:
Person person = new Person();
person.name = "Alice";
person.age = 25;
person.sayHello();
在上述代码中,我们首先使用new运算符创建一个名为person的Person对象。然后,我们设置了对象的name和age属性,并使用sayHello方法打印欢迎消息。
封装和继承
封装是OOP的一个重要概念,它指的是将数据和方法封装在一个对象中,并限制外部访问该对象的方式。在Java中,可以使用访问修饰符来实现封装,其中public、private、protected和默认访问修饰符(不带任何修饰符)是四种访问修饰符。
使用封装的好处在于:
- 隐藏对象实现的细节,使对象更易于使用和维护。
- 防止外部代码意外修改对象的状态。
- 允许对象内部进行改变,而不影响对象外部的代码。
下面是一个Person类:
public class Person {
private String name;
private int age;
// 构造函数
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// getter和setter方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
在上述代码中,name和age属性被声明为私有属性,这意味着只有在Person类中才能访问它们。为了允许外部代码访问这些属性,我们提供了getter和setter方法。
通过这样的方式,我们可以控制对Person对象的修改,并在必要时执行任何必要的检查。例如,我们可以在setAge()方法中添加以下代码,以防止年龄为负数:
public void setAge(int age) {
if (age < 0) {
throw new IllegalArgumentException("Age cannot be negative");
}
this.age = age;
}
这里,我们使用IllegalArgumentException异常来表示输入的年龄无效。(后面还会详细介绍异常)
继承是OOP中的另一个重要概念,它允许一个类继承(扩展)另一个类的属性和方法。在Java中,使用extends关键字来实现继承关系。例如:
public class Student extends Person {
int grade;
void study() {
System.out.println("I'm studying!");
}
}
在上面的代码中,我们创建了一个名为Student的类,它继承了Person类,并且具有一个额外的grade属性和一个study方法。这样,Student对象就同时具有Person和Student类的属性和方法。
多态
多态是OOP中的一个重要概念,它允许不同的对象对同一消息作出不同的响应。在Java中,可以通过重写(Override)方法来实现多态。例如:
public class Animal {
void makeSound() {
System.out.println("The animal makes a sound");
}
}
public class Cat extends Animal {
void makeSound() {
System.out.println("The cat says meow");
}
}
public class Dog extends Animal {
void makeSound() {
System.out.println("The dog says woof");
}
}
在上述代码中,我们创建了Animal、Cat和Dog三个类。它们都具有makeSound方法,但是每个类重写了该方法以实现不同的行为。可以使用以下代码创建Animal、Cat和Dog对象并调用makeSound方法:
Animal animal = new Animal();
Cat cat = new Cat();
Dog dog = new Dog();
animal.makeSound();
cat.makeSound();
dog.makeSound();
在上述代码中,我们创建三个对象并调用它们的makeSound方法。由于这些对象具有不同的类型,因此它们对该方法作出不同的响应。
例子
为了更好地理解面向对象编程的概念,让我们看一下一个简单的示例:汽车。
在面向对象编程中,汽车可以作为一个类来实现,其属性可以包括品牌、型号、颜色、排量等。汽车还可以具有方法,例如启动、加速、停止等。可以使用以下语法创建汽车类:
public class Car {
String brand;
String model;
String color;
double displacement;
void start() {
System.out.println("The " + brand + " " + model + " starts");
}
void accelerate() {
System.out.println("The " + brand + " " + model + " accelerates");
}
void stop() {
System.out.println("The " + brand + " " + model + " stops");
}
}
上述代码创建了一个名为Car的类,它具有品牌、型号、颜色和排量等属性以及start、accelerate和stop方法。
可以使用以下代码创建Car对象并调用它的方法:
Car car = new Car();
car.brand = "Toyota";
car.model = "Corolla";
car.color = "blue";
car.displacement = 1.6;
car.start();
car.accelerate();
car.stop();
在上述代码中,我们首先使用new运算符创建一个名为car的Car对象。然后,我们设置对象的brand、model、color和displacement属性,并使用start、accelerate和stop方法模拟汽车的行驶过程。
抽象
抽象是OOP中的另一个重要概念,它指的是将通用概念和功能提取出来形成一个抽象类或接口。抽象类是一种不能被实例化的类,它只是一种模板或蓝图,用于派生出具体类。接口是一种特殊的抽象类,它定义了实现类必须遵循的方法。
使用抽象的好处在于:
- 简化代码,提高代码的可重用性和可维护性。
- 允许多态(Polymorphism)的实现,这是OOP的一个重要特性。
例如,我们可以考虑以下的Shape抽象类:
abstract class Shape {
public abstract double getArea();
}
在上述代码中,Shape抽象类定义了一个名为getArea()的抽象方法。由于Shape类是一个抽象类,所以不能直接创建Shape类的对象。相反,我们可以创建派生自Shape类的具体类,例如Circle和Rectangle类:
class Circle extends Shape {
private double radius;
public Circle(double radius) {
this.radius = radius;
}
public double getArea() {
return Math.PI * radius * radius;
}
}
class Rectangle extends Shape {
private double width;
private double height;
public Rectangle(double width, double height) {
this.width = width;
this.height = height;
}
public double getArea() {
return width * height;
}
}
在上述代码中,Circle和Rectangle类都派生自Shape抽象类,并实现了getArea()方法。这些类可以创建对象,并调用它们的方法,如下所示:
Circle circle = new Circle(5.0);
System.out.println("Area of circle: " + circle.getArea());
Rectangle rectangle = new Rectangle(3.0, 4.0);
System.out.println("Area of rectangle: " + rectangle.getArea());
由于Circle和Rectangle都是Shape的子类,因此我们可以将它们保存在Shape类型的变量中,如下所示:
Shape shape = new Circle(5.0);
System.out.println("Area of shape: " + shape.getArea());
shape = new Rectangle(3.0, 4.0);
System.out.println("Area of shape: " + shape.getArea());
这里,我们使用Shape类型的变量保存Circle和Rectangle对象,并调用它们的getArea()方法。由于getArea()方法在Shape抽象类中被声明为抽象方法,并且在具体的Circle和Rectangle类中被实现,因此程序可以正确地计算圆和矩形的面积。
接口
Java中的接口是一种特殊的抽象类,它定义了一组方法的集合,但是没有实现方法的具体内容。接口可以看作是一种“合同”,实现接口的类必须遵循接口中定义的规范。可以使用以下语法定义接口:
public interface InterfaceName {
// 接口方法
}
例如,我们可以考虑以下的Playable接口:
public interface Playable {
void play();
}
在上述代码中,Playable接口定义了一个名为play()的方法。由于该接口没有提供方法实现,因此任何实现该接口的类都必须提供自己的play()方法的实现。
现在,假设我们有一个名为Music的类,它实现了Playable接口:
public class Music implements Playable {
private String title;
public Music(String title) {
this.title = title;
}
public void play() {
System.out.println("Now playing: " + title);
}
}
在上述代码中,Music类实现了Playable接口并提供了自己的play()实现。现在,我们可以创建Music类的对象,并调用它的play()方法:
Music music = new Music("Stairway to Heaven");
music.play();
在上述代码中,我们创建一个名为music的Music对象,并调用它的play()方法。由于Music类实现了Playable接口,所以可以将Music对象视为Playable类型来处理。
内部类
Java中的内部类是定义在另一个类内部的类。内部类可以访问外部类的私有成员,这使得内部类在封装和隐藏实现方面非常有用。内部类可以分为静态内部类和非静态内部类两种。
可以使用以下语法定义一个静态内部类:
class OuterClass {
static class InnerClass {
// 内部类成员
}
}
例如,我们可以考虑以下的Outer类和Inner内部类:
public class Outer {
private int outerData;
public Outer(int outerData) {
this.outerData = outerData;
}
static class Inner {
void display(Outer outer) {
System.out.println("outerData: " + outer.outerData);
}
}
}
在上述代码中,Outer类包含一个名为outerData的私有成员,以及一个名为Inner的静态内部类。Inner类包含一个名为display()的方法,它可以访问Outer类的outerData成员。
现在,我们可以创建Outer和Inner类的对象,并调用Inner类的display()方法:
Outer outer = new Outer(42);
Outer.Inner inner = new Outer.Inner();
inner.display(outer);
在上述代码中,我们创建了一个名为outer的Outer对象,以及一个名为inner的Inner对象。然后,我们调用inner对象的display()方法,并将outer对象作为参数传递给它。由于Inner类是静态内部类,因此我们可以使用Outer.Inner语法来创建它的对象。
除了静态内部类之外,Java还支持非静态内部类,局部内部类和匿名内部类等。这些内部类在不同的场景中非常有用,例如,在事件处理程序中使用匿名内部类来处理事件。
常用类
Java中提供了许多常用的类,这些类涵盖了许多不同的功能,包括日期和时间、字符串操作、数字处理、文件和IO操作等。下面是一些常用的类以及它们的用途:
- String:用于表示字符串。
- StringBuilder和StringBuffer:用于构建字符串。
- Math:用于执行数学运算。
- Date和LocalDate:用于处理日期和时间。
- Random:用于生成随机数。
- File和Path:用于处理文件和目录。
- Scanner:用于从输入流读取数据。
- System:提供了与系统相关的信息和操作。
接下来,我们将讨论一些常用类的使用方法以及如何在面向对象的程序设计中使用它们。
例子
假设我们正在开发一个银行应用程序,并需要存储客户的信息。为此,我们可以创建一个名为BankAccount的类,该类包含以下属性:
- name:客户姓名
- id:客户身份证号
- balance:账户余额
我们可以使用以下代码来定义BankAccount类:
public class BankAccount {
private String name;
private String id;
private double balance;
public BankAccount(String name, String id, double balance) {
this.name = name;
this.id = id;
this.balance = balance;
}
// getter和setter方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public double getBalance() {
return balance;
}
public void setBalance(double balance) {
this.balance = balance;
}
}
现在,我们需要编写一个程序来生成客户账号的随机ID。为此,我们可以使用Random类来生成一个随机数,并将它转换为字符串。以下是代码示例:
import java.util.Random;
public class Main {
public static void main(String[] args) {
// 生成随机ID
Random random = new Random();
int idNum = random.nextInt(10000);
String id = String.format("%04d", idNum);
// 创建银行账户
BankAccount account = new BankAccount("John Smith", id, 1000);
System.out.println("New account created: " + account.getId());
}
}
在上述代码中,我们使用Random类生成一个随机数,并使用String.format()方法将其转换为一个四位数的字符串。然后,我们使用生成的ID创建BankAccount对象,并将其输出到控制台。
通过这个简单的例子,我们可以看到如何使用常用类(例如Random和String)来构建一个面向对象的程序。由于这些类提供了许多常用的功能,因此在开发Java应用程序时经常会使用它们。
评论区