Java面向对象编程核心概念详解
面向对象编程基础
Java是一种支持面向对象编程(OOP)的语言,其核心理念是将现实世界中的事物抽象为程序中的对象。每个对象都包含数据(属性)和行为(方法)两部分。
面向对象的本质在于:以类的形式组织代码结构,以对象的形式封装数据信息。
面向对象的三大特性
封装
封装是将对象的属性和行为捆绑在一起,对外只暴露必要的接口,隐藏内部实现细节。这种机制有效保护了对象的内部状态,防止外部代码随意修改。
封装的优势:
- 提高数据安全性
- 隐藏实现细节
- 增强代码可维护性
class Employee {
private String employeeName; // 私有属性,外部无法直接访问
// 公开方法,提供受控的访问方式
public void setName(String name) {
this.employeeName = name;
}
public String getName() {
return employeeName;
}
}
// employeeName 变量被声明为 private,外部无法直接访问
// 必须通过 setName() 和 getName() 方法进行间接操作
继承
继承允许子类获取父类的属性和方法,实现代码的复用和扩展。通过继承,可以建立类之间的层次关系。
继承的优势:
- 减少代码重复
- 提高代码可维护性
- 增强代码扩展性
// 父类
class Vehicle {
void move() {
System.out.println("交通工具在移动");
}
}
// 子类继承 Vehicle
class Car extends Vehicle {
void honk() {
System.out.println("汽车按喇叭");
}
}
public class Test {
public static void main(String[] args) {
Car myCar = new Car();
myCar.move(); // 继承自父类的方法
myCar.honk(); // 子类特有的方法
}
}
// Car 类继承 Vehicle 类,因此可以使用 move() 方法
多态
多态是指同一个方法在不同对象上可以表现出不同的行为。它允许使用父类类型的引用指向子类对象,提高代码的灵活性和可扩展性。
多态的优势:
- 提高代码灵活性
- 增强代码可维护性
- 实现接口与实现的分离
class Shape {
void draw() {
System.out.println("绘制图形");
}
}
class Circle extends Shape {
void draw() {
System.out.println("绘制圆形");
}
}
class Square extends Shape {
void draw() {
System.out.println("绘制正方形");
}
}
public class Test {
public static void main(String[] args) {
Shape myShape = new Circle(); // 父类引用指向子类对象
myShape.draw(); // 调用 Circle 类的 draw 方法
}
}
// Shape 是父类,Circle 和 Square 继承它,并重写了 draw() 方法
方法调用机制
Java中的方法调用分为两种情况:静态方法和实例方法。
静态方法调用
静态方法(使用 static 关键字修饰)属于类本身,不需要创建对象即可调用。
class Calculator {
// 静态方法
static int multiply(int a, int b) {
return a * b;
}
}
public class Test {
public static void main(String[] args) {
int result = Calculator.multiply(5, 3); // 直接通过类名调用
System.out.println("结果: " + result);
}
}
// 静态方法中不能使用 this 关键字
实例方法调用
实例方法属于对象,必须先创建对象才能调用。
class Student {
// 实例方法
void introduce() {
System.out.println("大家好,我是学生!");
}
}
public class Test {
public static void main(String[] args) {
Student s = new Student(); // 先创建对象
s.introduce(); // 通过对象调用方法
}
}
参数传递机制
Java中的参数传递都是值传递,但根据参数类型不同,表现出不同的行为。
基本类型参数传递
当参数是基本类型时,传递的是值的副本,方法内修改不会影响原始变量。
class Test {
static void modifyValue(int num) {
num = 10; // 修改的是 num 的副本
}
public static void main(String[] args) {
int a = 5;
modifyValue(a);
System.out.println(a); // 输出:5(a 没有被修改)
}
}
对象引用参数传递
当参数是对象时,传递的是对象引用的副本,方法内通过引用修改对象内容会影响原始对象。
class Book {
String title;
}
class Test {
static void changeBookTitle(Book b) {
b.title = "Java编程思想"; // 修改对象属性
}
public static void main(String[] args) {
Book myBook = new Book();
myBook.title = "Java核心技术";
changeBookTitle(myBook);
System.out.println(myBook.title); // 输出:Java编程思想
}
}
构造方法详解
构造方法是Java中一种特殊的方法,用于在创建对象时初始化对象状态。构造方法名称必须与类名相同,且没有返回值类型。
构造方法的特点
- 名称必须与类名相同
- 没有返回值类型(不能写void)
- 在创建对象时自动调用
- 支持方法重载
- 如果不定义,系统会提供默认无参构造方法
构造方法分类
无参构造方法
class Person {
String name;
// 无参构造方法
public Person() {
name = "默认姓名";
}
}
有参构造方法
class Person {
String name;
int age;
// 有参构造方法
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
this关键字在构造方法中的应用
区分成员变量和参数
class Product {
String name;
public Product(String name) {
this.name = name; // 使用 this 区分成员变量和参数
}
}
调用其他构造方法
class User {
String username;
String email;
// 调用其他构造方法
public User() {
this("默认用户", "default@example.com"); // 调用有参构造
}
public User(String username, String email) {
this.username = username;
this.email = email;
}
}
构造代码块
构造代码块是类中{}包裹的代码,每次创建对象都会执行,且在构造方法之前执行。
class Computer {
// 构造代码块
{
System.out.println("构造代码块执行!");
}
public Computer() {
System.out.println("构造方法执行!");
}
}
public class Main {
public static void main(String[] args) {
new Computer();
new Computer();
}
}
// 输出:
// 构造代码块执行!
// 构造方法执行!
// 构造代码块执行!
// 构造方法执行!
封装深入理解
封装是面向对象编程的核心概念之一,通过隐藏对象内部细节,仅暴露必要的接口,提高代码的安全性和可维护性。
封装的核心原则:属性私有化,方法公开化(private属性,public方法)。
封装的实现方式
class BankAccount {
// 1. 将属性私有化
private String accountNumber;
private double balance;
// 2. 提供getter和setter方法
public String getAccountNumber() {
return accountNumber;
}
public void setAccountNumber(String accountNumber) {
this.accountNumber = accountNumber;
}
public double getBalance() {
return balance;
}
public void setBalance(double balance) {
if (balance >= 0) { // 添加数据验证
this.balance = balance;
} else {
System.out.println("余额不能为负数!");
}
}
// 3. 提供公开的业务方法
public void deposit(double amount) {
if (amount > 0) {
balance += amount;
System.out.println("存款成功,当前余额: " + balance);
}
}
public void withdraw(double amount) {
if (amount > 0 && amount <= balance) {
balance -= amount;
System.out.println("取款成功,当前余额: " + balance);
} else {
System.out.println("取款金额无效或余额不足!");
}
}
}
public class Main {
public static void main(String[] args) {
BankAccount account = new BankAccount();
account.setAccountNumber("123456789");
account.setBalance(1000.0);
account.deposit(500.0);
account.withdraw(200.0);
// 尝试直接修改余额(不允许)
// account.balance = -1000; // 编译错误,balance是私有的
}
}
继承机制详解
继承是面向对象编程的重要特性,允许子类获取父类的属性和方法,建立类之间的层次关系。
继承的基本语法
// 父类(基类)
class Animal {
String name;
void eat() {
System.out.println(name + " 在吃东西");
}
}
// 子类(派生类)
class Bird extends Animal {
void fly() {
System.out.println(name + " 在飞翔");
}
}
// 测试继承
public class Test {
public static void main(String[] args) {
Bird bird = new Bird();
bird.name = "鹦鹉"; // 继承了name属性
bird.eat(); // 继承了eat()方法
bird.fly(); // 调用子类自己的方法
}
}
继承的特点
- 子类继承父类的public和protected成员
- Java只支持单继承(一个类只能继承一个父类)
- 子类可以扩展父类的方法,也可以重写父类的方法
- 构造方法不会被继承,但子类会调用父类的构造方法
Object类
Object类是Java中所有类的根类,所有类都直接或间接继承自Object类。它位于java.lang包中,无需导入即可使用。
super关键字
super关键字用于子类访问父类的内容,主要有以下用途:
调用父类的构造方法
class Parent {
Parent() {
System.out.println("父类无参构造方法");
}
Parent(String msg) {
System.out.println("父类有参构造方法:" + msg);
}
}
class Child extends Parent {
Child() {
super("来自Child的问候"); // 调用父类有参构造
System.out.println("子类构造方法");
}
}
public class Main {
public static void main(String[] args) {
Child c = new Child();
}
}
// 输出:
// 父类有参构造方法:来自Child的问候
// 子类构造方法
访问父类的成员变量
class Parent {
int value = 10;
}
class Child extends Parent {
int value = 20;
void display() {
System.out.println("子类的value: " + value); // 访问子类变量
System.out.println("父类的value: " + super.value); // 访问父类变量
}
}
public class Main {
public static void main(String[] args) {
Child c = new Child();
c.display();
}
}
// 输出:
// 子类的value: 20
// 父类的value: 10
调用父类的方法
class Parent {
void show() {
System.out.println("父类的方法");
}
}
class Child extends Parent {
@Override
void show() {
super.show(); // 先调用父类的方法
System.out.println("子类的方法");
}
}
public class Main {
public static void main(String[] args) {
Child c = new Child();
c.show();
}
}
// 输出:
// 父类的方法
// 子类的方法
多态深入理解
多态是面向对象编程的重要特性,允许使用父类类型的引用指向子类对象,并根据实际对象的类型调用相应的方法。
方法重写
方法重写(Override)是指子类对父类继承的方法进行重新定义,以提供更适合子类的实现。
方法重写的规则
- 方法名相同,参数列表相同
- 返回值类型:JDK 1.5后可以是父类返回类型的子类
- 访问权限不能比父类更严格
- static方法不能被重写(只能隐藏)
- final方法不能被重写
- 必须在继承关系中进行
class Vehicle {
void start() {
System.out.println("交通工具启动");
}
}
class Motorcycle extends Vehicle {
@Override
void start() {
System.out.println("摩托车启动:转动钥匙,踩踏板");
}
}
class Car extends Vehicle {
@Override
void start() {
System.out.println("汽车启动:插入钥匙,转动点火开关");
}
}
public class Test {
public static void main(String[] args) {
Vehicle motorcycle = new Motorcycle();
Vehicle car = new Car();
motorcycle.start(); // 输出:摩托车启动:转动钥匙,踩踏板
car.start(); // 输出:汽车启动:插入钥匙,转动点火开关
}
}
instanceof运算符
instanceof是Java中的运算符,用于判断对象是否是指定类或其子类的实例。
class Fruit {}
class Apple extends Fruit {}
class Banana extends Fruit {}
public class Test {
public static void main(String[] args) {
Fruit fruit = new Fruit();
Apple apple = new Apple();
Fruit appleFruit = new Apple(); // 向上转型
System.out.println(fruit instanceof Fruit); // true
System.out.println(apple instanceof Apple); // true
System.out.println(apple instanceof Fruit); // true
System.out.println(appleFruit instanceof Apple); // true
System.out.println(fruit instanceof Apple); // false
}
}
向下转型
向下转型是将父类引用转换为子类类型,但需要使用instanceof进行检查以确保类型安全。
class Device {
void powerOn() {
System.out.println("设备开机");
}
}
class Smartphone extends Device {
void makeCall() {
System.out.println("拨打电话");
}
}
public class Test {
public static void main(String[] args) {
Device device = new Smartphone(); // 向上转型
device.powerOn(); // 只能调用Device类的方法
// 向下转型
if (device instanceof Smartphone) {
Smartphone phone = (Smartphone) device; // 向下转型
phone.makeCall(); // 现在可以调用Smartphone特有的方法
}
}
}
static关键字详解
static关键字用于修饰类的成员,使其属于类而不是实例。
静态变量
静态变量(类变量)被所有实例共享,只有一个副本。
class Counter {
static int count = 0; // 静态变量
public Counter() {
count++; // 每次创建对象时增加count
}
public void getCount() {
System.out.println("当前count值: " + count);
}
}
public class Test {
public static void main(String[] args) {
Counter c1 = new Counter();
Counter c2 = new Counter();
Counter c3 = new Counter();
c1.getCount(); // 输出:当前count值: 3
c2.getCount(); // 输出:当前count值: 3
c3.getCount(); // 输出:当前count值: 3
}
}
静态方法
静态方法属于类,不依赖于实例,可以通过类名直接调用。静态方法不能访问非静态成员。
class MathUtils {
static int square(int x) {
return x * x;
}
int instanceVar = 10; // 实例变量
static void demo() {
// System.out.println(instanceVar); // 编译错误:不能访问实例变量
System.out.println("这是一个静态方法");
}
}
public class Test {
public static void main(String[] args) {
int result = MathUtils.square(5); // 通过类名调用
System.out.println("5的平方是: " + result);
MathUtils.demo(); // 通过类名调用静态方法
}
}
静态代码块
静态代码块在类加载时执行,且只执行一次,通常用于初始化静态变量。
class DatabaseConnection {
static String connectionUrl;
// 静态代码块
static {
System.out.println("静态代码块执行,初始化数据库连接");
connectionUrl = "jdbc:mysql://localhost:3306/mydb";
}
public static void connect() {
System.out.println("连接到: " + connectionUrl);
}
}
public class Test {
public static void main(String[] args) {
DatabaseConnection.connect();
DatabaseConnection.connect();
}
}
// 输出:
// 静态代码块执行,初始化数据库连接
// 连接到: jdbc:mysql://localhost:3306/mydb
// 连接到: jdbc:mysql://localhost:3306/mydb
静态内部类
静态内部类不依赖于外部类的实例,可以直接创建,只能访问外部类的静态成员。
class OuterClass {
private static String staticMsg = "这是静态消息";
private String instanceMsg = "这是实例消息"; // 外部类实例变量
// 静态内部类
static static InnerClass {
void displayStaticMsg() {
System.out.println(staticMsg); // 可以访问外部类的静态成员
// System.out.println(instanceMsg); // 编译错误:不能访问非静态成员
}
}
}
public class Test {
public static void main(String[] args) {
OuterClass.InnerClass inner = new OuterClass.InnerClass();
inner.displayStaticMsg();
}
}
抽象类详解
抽象类是不能直接实例化的类,通常用于作为其他类的父类,提供一些通用行为和抽象方法。
抽象类的特点
- 使用abstract关键字声明
- 不能直接实例化,只能通过子类实例化
- 可以包含普通方法和抽象方法(没有方法体)
- 子类必须实现抽象方法,或者自身也是抽象类
- 抽象方法只能写在抽象类中
抽象类的示例
// 抽象类
abstract class Shape {
// 抽象方法(没有方法体,子类必须实现)
public abstract double calculateArea();
// 普通方法(子类可以直接使用)
public void display() {
System.out.println("这是一个图形");
}
}
// 具体子类
class Circle extends Shape {
private double radius;
public Circle(double radius) {
this.radius = radius;
}
@Override
public double calculateArea() {
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;
}
@Override
public double calculateArea() {
return width * height;
}
}
public class Test {
public static void main(String[] args) {
Shape circle = new Circle(5.0);
Shape rectangle = new Rectangle(4.0, 6.0);
System.out.println("圆的面积: " + circle.calculateArea());
System.out.println("矩形的面积: " + rectangle.calculateArea());
circle.display();
rectangle.display();
}
}
接口详解
接口是Java中用于定义行为规范的机制,它规定了一组方法,但不提供具体实现。实现接口的类必须提供接口中定义的所有方法。
接口的特点
- 接口中的方法默认是public abstract
- 接口中的变量默认是public static final(常量)
- 类使用implements关键字实现接口
- 一个类可以实现多个接口
- Java 8后,接口可以包含default方法和static方法
接口示例
interface Payment {
// 接口方法默认是 public abstract
void processPayment(double amount);
// Java 8+ 可以有默认方法
default void validatePayment() {
System.out.println("验证支付信息");
}
// Java 8+ 可以有静态方法
static void logTransaction(String id) {
System.out.println("记录交易: " + id);
}
}
class CreditCardPayment implements Payment {
@Override
public void processPayment(double amount) {
System.out.println("使用信用卡支付: " + amount);
}
}
class PayPalPayment implements Payment {
@Override
public void processPayment(double amount) {
System.out.println("使用PayPal支付: " + amount);
}
}
public class Test {
public static void main(String[] args) {
Payment creditCard = new CreditCardPayment();
Payment payPal = new PayPalPayment();
creditCard.processPayment(100.0);
creditCard.validatePayment();
payPal.processPayment(200.0);
payPal.validatePayment();
Payment.logTransaction("TX12345"); // 调用接口的静态方法
}
}
内部类详解
内部类是在一个类的内部定义的类,它可以访问外部类的成员,包括private成员。
内部类的类型
成员内部类
class Outer {
private String message = "来自外部类的消息";
class Inner {
void displayMessage() {
System.out.println(message); // 可以访问外部类的私有成员
}
}
}
public class Test {
public static void main(String[] args) {
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner(); // 通过外部类对象创建内部类对象
inner.displayMessage(); // 输出:来自外部类的消息
}
}
静态内部类
class Outer {
private static String staticMessage = "静态消息";
private String instanceMessage = "实例消息"; // 外部类实例变量
static class StaticInner {
void displayStaticMessage() {
System.out.println(staticMessage); // 可以访问外部类的静态成员
// System.out.println(instanceMessage); // 编译错误:不能访问非静态成员
}
}
}
public class Test {
public static void main(String[] args) {
Outer.StaticInner inner = new Outer.StaticInner(); // 直接创建,不需要外部类实例
inner.displayStaticMessage(); // 输出:静态消息
}
}
局部内部类
class Outer {
void display() {
// 局部内部类,定义在方法内部
class LocalInner {
void show() {
System.out.println("局部内部类");
}
}
LocalInner inner = new LocalInner();
inner.show(); // 输出:局部内部类
}
}
public class Test {
public static void main(String[] args) {
Outer outer = new Outer();
outer.display();
}
}
匿名内部类
// 匿名内部类实现接口
interface Greeting {
void sayHello();
}
public class Test {
public static void main(String[] args) {
Greeting englishGreeting = new Greeting() {
@Override
public void sayHello() {
System.out.println("Hello!");
}
};
Greeting chineseGreeting = new Greeting() {
@Override
public void sayHello() {
System.out.println("你好!");
}
};
englishGreeting.sayHello(); // 输出:Hello!
chineseGreeting.sayHello(); // 输出:你好!
}
}
总结
面向对象编程是Java的核心特性,通过封装、继承和多态三大特性,可以构建结构清晰、可维护性高的程序。理解这些概念对于掌握Java编程至关重要。
在实际开发中,合理运用面向对象的思想可以有效地组织代码,提高代码的可重用性和可扩展性。抽象类和接口的使用可以帮助我们更好地设计系统的架构,而内部类则提供了更灵活的代码组织方式。
