引言
Java面向对象是一种编程方法论,它将软件系统看作是一组对象的集合,每个对象都有自己的状态和行为,并且能够相互之间进行通信和协作。
java面向对象知识的思维导图
1、面向对象思想
面向对象(Object-Oriented)是一种编程思想和方法论,它的核心理念是将问题拆解成由对象组成的模块,并通过对象之间的交互来解决问题。
面向对象的思想有以下几个重要的特点:
-
封装(Encapsulation):将数据和方法封装在一个对象中,并对外提供接口,隐藏内部实现细节。对象通过使用接口来与其他对象进行通信,而不需要了解对象内部的实现细节。
-
继承(Inheritance):通过继承,一个类可以直接获取另一个类的属性和方法,并且可以在此基础上进行扩展和修改。继承可以减少代码的重复,提高代码的复用性,同时也能够定义出层次结构,表达对象之间的关系。
-
多态(Polymorphism):多态允许使用同一个接口来表示不同的对象,同一个方法可以根据不同的对象执行不同的行为。多态性使得代码更加灵活和可扩展,能够应对对象类型的变化和需求的变化。
通过封装、继承和多态这些特性,面向对象编程能够实现代码的模块化、重用和扩展,提高代码的可读性、可维护性和可靠性。它能够更好地对现实世界中的问题进行建模,并将问题解决方案转化为代码的形式。
2、封装
在Java中,封装可以通过类来实现。一个类可以包含属性(成员变量)和方法(成员函数),属性用于存储对象的状态信息,而方法用于定义对象的行为。通过访问修饰符(如private、public、protected)来控制属性和方法的访问权限,从而实现封装。
下面是一个简单的Java代码示例,展示了封装的实现:
public class Person {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
if (age >= 0 && age <= 120) {
this.age = age;
} else {
throw new IllegalArgumentException("Invalid age");
}
}
}
在这个示例中,Person类有两个私有属性name和age,它们被封装在类的内部。通过公有的getter和setter方法来访问和修改这些属性。在setter方法中,我们还加入了对age的合法性检查,确保age在0到120之间。
注意事项:
-
封装的目的是隐藏内部实现细节,所以应该尽量将属性的访问权限设置为private,只允许通过公有的方法来访问和修改属性。
-
公有的getter方法用于获取属性的值,公有的setter方法用于修改属性的值。在setter方法中可以加入对属性值的合法性检查,确保对象的状态的一致性和正确性。
-
当需要在类的外部操作属性时,应该通过方法调用的方式进行,而不是直接访问属性。这样可以保证对象的封装性,避免直接暴露内部实现,提高代码的安全性和可维护性。
-
在设计类的时候,应该遵循单一职责原则,即一个类应该只有一个主要责任。这样可以提高代码的可读性和可理解性,减少耦合,提高代码的重用性和扩展性。
-
封装并不意味着完全隐藏对象的内部实现,而是通过接口来控制对内部实现的访问。合理的封装能够提高代码的抽象程度,减少对实现的依赖,从而提高代码的灵活性和可扩展性。
3、成员变量和局部变量的区别
成员变量和局部变量是在Java中用于存储数据的两种不同类型的变量。
区别如下:
1.作用范围:
- 成员变量(也称为实例变量)是定义在类中、方法外部的变量。它的作用范围是整个类的内部,可以在类的任何方法中访问和使用。
- 局部变量是定义在方法、构造函数或代码块内部的变量。它的作用范围仅限于所属的方法、构造函数或代码块内部,不能在其他方法中访问。
2.生命周期:
- 成员变量的生命周期与对象一致。当对象创建时,成员变量会被分配内存空间,直到对象被销毁,成员变量才会被释放。
- 局部变量的生命周期仅限于所属的方法、构造函数或代码块执行期间。当执行离开所属的方法、构造函数或代码块时,局部变量的内存空间会被释放。
3.默认值:
- 成员变量会根据类型有默认的初始值,如整型默认为0,布尔型默认为false,引用类型默认为null。
- 局部变量没有默认值,必须先进行初始化后才能使用。
下面是一个示例代码,展示了成员变量和局部变量的对比:
public class VariableExample {
// 成员变量
private int memberVariable;
public void method() {
// 局部变量
int localVariable = 10;
System.out.println("成员变量的值:" + memberVariable); // 输出默认值0
System.out.println("局部变量的值:" + localVariable); // 输出初始化的值10
}
public void anotherMethod() {
// System.out.println(localVariable); // 错误,局部变量只能在所属的方法内部访问
System.out.println("成员变量的值:" + memberVariable); // 输出默认值0
}
}
在这个示例中,VariableExample类有一个成员变量memberVariable和一个方法method。在method方法中,定义了一个局部变量localVariable并对其进行了初始化。在anotherMethod方法中,尝试访问localVariable会导致编译错误,因为局部变量只能在所属的方法内部访问。
4、继承
继承是面向对象编程中的一种重要概念,它允许一个类获取另一个类的属性和方法。
继承的代码展示如下:
// 父类
public class Animal {
private String name;
public Animal(String name) {
this.name = name;
}
public void eat() {
System.out.println(name + " is eating.");
}
}
// 子类
public class Cat extends Animal {
public Cat(String name) {
super(name);
}
public void meow() {
System.out.println("Meow!");
}
}
上面的示例中,Animal类是父类,Cat类是子类。Cat类通过关键字extends
继承了Animal类。子类可以继承父类的成员变量和方法。
注意事项:
- 子类继承父类后,可以使用父类的公共方法和成员变量,也可以覆盖(重写)父类的方法或者给父类的方法添加额外的功能。
- 子类只能继承一个父类,Java不支持多继承。
- 继承是一种"是一个"的关系,即子类是父类的特殊化或者具体化。
- 可以使用
super
关键字在子类中访问父类的构造方法、成员变量和方法。 - 子类可以通过继承父类,实现代码的重用,减少重复代码的编写。
继承是面向对象编程中非常重要和常用的概念,合理使用继承可以帮助我们构建更清晰、可扩展和可重用的代码。
5、多态
多态是面向对象编程中的一个重要概念,它允许在同一方法调用中使用不同类型的对象,实现更灵活的代码结构。
多态的代码示例如下:
// 父类
public class Animal {
public void makeSound() {
System.out.println("Animal is making sound.");
}
}
// 子类1
public class Cat extends Animal {
@Override
public void makeSound() {
System.out.println("Meow!");
}
}
// 子类2
public class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("Woof!");
}
}
上面的示例中,Animal类是父类,Cat和Dog类是其子类。每个子类都重写了父类的makeSound()
方法,实现了不同的声音。
多态的使用示例:
Animal animal1 = new Cat();
Animal animal2 = new Dog();
animal1.makeSound(); // 输出 Meow!
animal2.makeSound(); // 输出 Woof!
上面的示例中,animal1和animal2是Animal类型的引用,但可以指向不同类型的对象,即Cat和Dog对象。在运行时,实际调用的是各个子类中重写的方法。
需要注意的事项:
- 多态依赖于继承和方法重写。子类必须重写父类的方法,并且具有相同的方法签名(方法名、参数列表和返回类型)。
- 多态使得代码更灵活和可扩展,可以通过增加子类来扩展功能,而不需要修改现有的代码。
- 在使用多态时,要注意引用变量的类型和实际对象的类型。引用变量的类型决定可以调用哪些成员方法,而实际对象的类型决定调用的是哪个方法的实现。
- 在多态中,父类引用变量可以指向子类对象,但无法访问子类特有的成员和方法。如果需要调用子类特有的方法,需要将引用变量强制转换为子类类型。
多态是面向对象编程中一个非常强大的特性,通过合理运用多态可以实现代码的灵活性、可扩展性和可重用性。
6、抽象类
抽象类是一种不能被实例化的类,它用于定义具有共同特征和行为的一组对象的通用结构和行为。抽象类可以包含抽象方法、普通方法和成员变量。
抽象类的代码示例如下:
// 抽象类
public abstract class Animal {
protected String name;
public Animal(String name) {
this.name = name;
}
// 抽象方法
public abstract void makeSound();
// 普通方法
public void eat() {
System.out.println(name + " is eating.");
}
}
// 子类
public class Cat extends Animal {
public Cat(String name) {
super(name);
}
@Override
public void makeSound() {
System.out.println(name + " says Meow!");
}
}
在上面的示例中,Animal类是一个抽象类,它包含一个抽象方法makeSound()
和一个普通方法eat()
。抽象方法没有具体的实现,需要在子类中实现。子类Cat重写了makeSound()
方法。
使用抽象类的注意事项:
- 抽象类不能被实例化,只能用来作为其他类的父类。
- 抽象类可以包含抽象方法和普通方法,普通方法可以有具体的实现。
- 如果一个类继承了抽象类,它必须实现抽象类中的所有抽象方法,除非它本身也是一个抽象类。
- 抽象方法在子类中必须重写,并提供具体的实现。
- 抽象类可以拥有构造方法,但不能直接使用
new
关键字实例化抽象类。 - 可以通过向上转型的方式,将子类的对象赋值给抽象类的引用变量。
- 抽象类可以包含成员变量,子类可以继承和使用这些成员变量。
- 抽象类的存在主要是为了提供一种规范和约束,用于定义一组相关的对象的行为和特征。
抽象类在面向对象编程中具有重要的作用,它提供了一种抽象化的方式来描述和组织对象的共性和特性。通过合理使用抽象类,可以提高代码的可维护性和可扩展性。
7、接口
接口是一种约定,用于定义了一组方法(包括抽象方法和默认方法)的集合。接口提供了一种规范,用于指定类应该具有哪些方法,并且允许类实现多个接口。接口实现了类之间的松耦合关系,使得代码更加灵活可扩展。
接口的代码示例如下:
// 接口
public interface Vehicle {
void start();
void stop();
}
// 实现接口的类
public class Car implements Vehicle {
@Override
public void start() {
System.out.println("Car is starting.");
}
@Override
public void stop() {
System.out.println("Car is stopping.");
}
}
在上面的示例中,Vehicle是一个接口,它定义了start()
和stop()
两个方法。类Car实现了Vehicle接口,并且需要提供这两个方法的具体实现。
使用接口的注意事项:
- 接口中的方法默认为public abstract,因此不需要显式写出修饰符。
- 一个类可以实现多个接口,使用逗号分隔。
- 实现接口的类必须实现接口中定义的所有方法,否则需要将类声明为抽象类。
- 接口不能包含成员变量,但可以包含常量(使用
static final
修饰)。 - 接口可以继承另一个接口,使用关键字
extends
。 - 接口不能被实例化,但可以根据接口创建引用变量。
- 一个类可以同时实现多个接口,从而实现多继承的效果。
- 接口的存在主要是为了实现多态和解耦,使得程序更加灵活可扩展。
使用接口可以提高代码的可重用性和可扩展性,通过接口可以实现更加灵活的对象组合和替换。同时,接口还可以用于定义回调函数、定义常量等场景。在编写接口时,需要注意定义合理的方法和常量,遵循接口命名的约定,并且在使用接口时要保证接口的一致性和适用性。
8、包和导包
包(Package)是Java中用于组织类和接口的一种机制。它是一种命名空间,用于将相关的类和接口组织在一起,以便更好地管理和使用。
在Java中,所有的类和接口都必须放在包中。一个包可以包含多个类和接口,也可以嵌套其他包。
在上面的示例中,com.example.myapp
是一个包,Car
是该包中的一个类。通过导入包的方式,可以使用包中的类,从而实例化对象并调用方法。
使用包和导包的注意事项:
- 包名应该是唯一的,避免与其他包冲突。
- 包名应该使用小写字母,多个单词之间可以使用"."进行分隔。
- 包名应该具有一定的语义,可以体现出所属的领域或功能。
- 导包时可以使用通配符"*",例如
import com.example.myapp.model.*;
,但应尽量避免导入不必要的类,以提高代码的可读性和性能。 - 如果多个类具有相同的简单类名,需要使用完整的类名(包括包名)来引用。
- 在IDE中创建类时,可以自动导入所需的包。
- 包结构应该与文件系统中的目录结构相对应,以便更好地管理和组织代码。
- 包名的命名规范是根据反转的域名来命名,例如
com.example.myapp
,这样可以避免包名冲突。
使用包和导包可以帮助我们更好地组织和管理代码,提高代码的可读性和可维护性。在编写代码时,需要注意给包取一个合适的名称,并且在导入包时选择合适的方式,避免冲突和导入不必要的类。
9、权限修饰符
权限修饰符是用来控制类、属性、方法和构造函数的访问权限的关键字。Java中有四种权限修饰符,它们分别是:
修饰符 | 同一类 | 同一包 | 子类 | 其他包 |
---|---|---|---|---|
public | ✅ | ✅ | ✅ | ✅ |
protected | ✅ | ✅ | ✅ | ❌ |
default (无) | ✅ | ✅ | ❌ | ❌ |
private | ✅ | ❌ | ❌ | ❌ |
public
:公共访问级别,被它修饰的类、属性、方法和构造函数可以被任何其他类访问。protected
:受保护访问级别,被它修饰的类、属性、方法和构造函数可以被同一包中的类以及其他包中的子类访问。- default (无修饰符):默认访问级别,被它修饰的类、属性、方法和构造函数可以被同一包中的类访问,但无法被其他包中的类访问。
private
:私有访问级别,被它修饰的类、属性、方法和构造函数只能在定义它们的类内部访问。
注意:在同一个类内部,无论是否使用修饰符都可以访问该类的所有成员。
以上是四种常用的权限修饰符,它们提供了不同层次的访问控制,可以根据需要选择合适的修饰符来控制代码的访问权限。
10、内部类
内部类是定义在其他类内部的类。它们具有更高的封装性和灵活性,可以访问外部类的成员,包括私有成员。内部类分为四种类型:
1.成员内部类(Member Inner Class):定义在类的内部,与成员变量和成员方法处于同一级别。
public class OuterClass {
private int x;
public class InnerClass {
public void display() {
System.out.println("Value of x is " + x);
}
}
public static void main(String[] args) {
OuterClass outer = new OuterClass();
OuterClass.InnerClass inner = outer.new InnerClass();
inner.display();
}
}
2.静态内部类(Static Inner Class):定义在类的内部,使用static
修饰。
public class OuterClass {
private static int x;
public static class InnerClass {
public void display() {
System.out.println("Value of x is " + x);
}
}
public static void main(String[] args) {
OuterClass.InnerClass inner = new OuterClass.InnerClass();
inner.display();
}
}
3.方法内部类(Method Local Inner Class):定义在方法内部的类,作用范围仅限于该方法内部。
public class OuterClass {
public void display() {
class InnerClass {
public void show() {
System.out.println("Inner class method");
}
}
InnerClass inner = new InnerClass();
inner.show();
}
public static void main(String[] args) {
OuterClass outer = new OuterClass();
outer.display();
}
}
4.匿名内部类(Anonymous Inner Class):没有类名的内部类,主要用于创建接口对象或抽象类的对象。
public interface Greeting {
void sayHello();
}
public class OuterClass {
public void displayGreeting() {
Greeting greeting = new Greeting() {
@Override
public void sayHello() {
System.out.println("Hello from anonymous inner class");
}
};
greeting.sayHello();
}
public static void main(String[] args) {
OuterClass outer = new OuterClass();
outer.displayGreeting();
}
}
注意事项:
- 内部类可以直接访问外部类的成员,包括私有成员。
- 内部类可以使用外部类的引用来访问外部类的成员。
- 静态内部类只能访问外部类中的静态成员。
- 方法内部类和匿名内部类不能使用访问控制修饰符(如
private
、public
)和static
修饰符。