一、什么是面向对象,什么是面向过程
面向过程
面向过程是一种以过程为中心的编程思想,它将一个复杂的问题分解为一系列的步骤,每个步骤用一个函数(或过程)来实现,然后按照一定的顺序依次调用这些函数,最终完成整个任务。程序的执行流程就像一条流水线,一步一步地按照预先设计好的顺序执行。
- 强调步骤:关注的是解决问题的具体步骤和过程,将问题拆分成多个子任务,每个子任务对应一个函数。
- 数据和操作分离:数据和对数据的操作是分开存储和处理的,数据通常作为函数的参数传递给函数进行处理。
- 代码结构:代码的组织方式是以函数为中心,函数之间通过参数和返回值进行交互。
面向对象
面向对象是一种以对象为中心的编程思想,它将数据和对数据的操作封装在一起,形成一个对象。对象之间通过消息传递来进行交互,从而完成整个任务。面向对象编程的核心概念包括类、对象、继承、多态等。
- 强调对象:关注的是问题中涉及的对象,将对象的属性(数据)和行为(操作)封装在一起,形成一个独立的实体。
- 数据和操作封装:对象将数据和对数据的操作封装在内部,外部只能通过对象提供的接口来访问和操作数据,提高了数据的安全性和可维护性。
- 继承和多态:继承允许一个类继承另一个类的属性和方法,从而实现代码的复用;多态允许不同的对象对同一消息做出不同的响应,提高了代码的灵活性和可扩展性。
- 面向过程:适用于简单的、功能单一的任务,例如脚本编写、数据处理等。
- 面向对象:适用于复杂的、大型的软件系统开发,例如企业级应用、游戏开发等。
二、什么是类,如何定义类。有了类后,如何能产生对象。
三、对象是引用数据类型
和数组一样,对象在栈上也是存放引用在堆上开辟的“地址”
四、如何访问对象的成员
1. 访问成员变量
访问对象的成员变量可以使用点号(.
)运算符。通过对象名和点号,再加上成员变量的名称,就可以访问和修改该成员变量的值。
public class Main {public static void main(String[] args) {// 创建 Person 对象Person person = new Person();// 访问成员变量并赋值person.name = "Alice";person.age = 25;// 访问成员变量并输出System.out.println("Name: " + person.name);System.out.println("Age: " + person.age);}
}
2. 访问成员方法
访问对象的成员方法同样使用点号(.
)运算符。通过对象名和点号,再加上成员方法的名称和参数列表(如果有参数),就可以调用该成员方法。
public class Main {public static void main(String[] args) {// 创建 Person 对象Person person = new Person();// 访问成员变量并赋值person.name = "Alice";person.age = 25;// 调用成员方法person.introduce();}
}
- 访问权限:如果成员变量或成员方法被声明为
private
,则不能直接在类外部访问,需要通过公共的访问器方法(如getter
和setter
方法)来访问。
class Person {private String name;private int age;// getter 方法public String getName() {return name;}// setter 方法public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public void introduce() {System.out.println("My name is " + name + ", and I'm " + age + " years old.");}
}public class Main {public static void main(String[] args) {Person person = new Person();person.setName("Bob");person.setAge(30);System.out.println("Name: " + person.getName());System.out.println("Age: " + person.getAge());person.introduce();}
}
- 静态成员:静态成员(使用
static
关键字声明的成员变量和成员方法)可以通过类名直接访问,也可以通过对象访问,但建议使用类名访问。
class MathUtils {public static int add(int a, int b) {return a + b;}
}public class Main {public static void main(String[] args) {// 通过类名访问静态方法int result = MathUtils.add(2, 3);System.out.println("Result: " + result);}
}
五、成员变量 VS 局部变量
成员变量
成员变量是定义在类中,但在方法、构造方法或代码块之外的变量。根据是否被 static
修饰,成员变量又可分为实例变量和类变量(静态变量)。
public class MemberVariableExample {// 实例变量String name;// 类变量(静态变量)static int count;public MemberVariableExample(String name) {this.name = name;count++;}public void printInfo() {System.out.println("Name: " + name + ", Count: " + count);}public static void main(String[] args) {MemberVariableExample obj1 = new MemberVariableExample("Alice");MemberVariableExample obj2 = new MemberVariableExample("Bob");obj1.printInfo();obj2.printInfo();}
}
特点
- 定义位置:直接定义在类的内部,方法、构造方法或代码块之外。
- 作用域:整个类都可以访问成员变量。实例变量通过对象访问,类变量可以通过类名或对象访问。
- 生命周期:
- 实例变量:随着对象的创建而创建,随着对象的销毁而销毁。
- 类变量:随着类的加载而创建,随着类的卸载而销毁。
- 初始值:成员变量有默认初始值,数值类型默认值为 0(如
int
、double
等),布尔类型默认值为false
,引用类型默认值为null
。 - 访问修饰符:可以使用
public
、private
、protected
等访问修饰符来控制成员变量的访问权限。
局部变量
局部变量是定义在方法、构造方法、代码块或方法参数中的变量。
public class LocalVariableExample {public void calculateSum(int a, int b) {// 局部变量int sum = a + b;System.out.println("Sum: " + sum);}public static void main(String[] args) {LocalVariableExample example = new LocalVariableExample();example.calculateSum(3, 5);}
}
特点
- 定义位置:定义在方法、构造方法、代码块内部或作为方法的参数。
- 作用域:仅限于定义它的方法、构造方法、代码块内部,出了这个范围就无法访问。
- 生命周期:从变量声明的位置开始,到定义它的方法、构造方法、代码块执行结束时销毁。
- 初始值:局部变量没有默认初始值,在使用之前必须显式赋值,否则会编译错误。
- 访问修饰符:局部变量不能使用访问修饰符,因为它的作用域是有限的,不需要额外的访问控制。
六、this
在 Java 中,this
是一个引用变量,它指向当前对象,也就是调用当前方法的对象。
1. 区分成员变量和局部变量
当成员变量和局部变量(例如方法参数)重名时,可以使用 this
关键字来明确引用成员变量。
class Person {private String name;private int age;// 构造方法public Person(String name, int age) {// 使用 this 引用成员变量this.name = name;this.age = age;}public String getName() {return this.name;}public int getAge() {return this.age;}
}public class Main {public static void main(String[] args) {Person person = new Person("Alice", 25);System.out.println("Name: " + person.getName());System.out.println("Age: " + person.getAge());}
}
2. 在构造方法中调用其他构造方法
在一个类中,你可以使用 this()
语句在一个构造方法中调用另一个构造方法,这样可以避免代码重复。
class Rectangle {private int width;private int height;// 无参构造方法public Rectangle() {// 调用带参数的构造方法this(1, 1);}// 带参数的构造方法public Rectangle(int width, int height) {this.width = width;this.height = height;}public int getArea() {return this.width * this.height;}
}public class Main {public static void main(String[] args) {Rectangle rect1 = new Rectangle();System.out.println("Area of rect1: " + rect1.getArea());Rectangle rect2 = new Rectangle(5, 3);System.out.println("Area of rect2: " + rect2.getArea());}
}
代码解释
- 在无参构造方法
Rectangle()
中,使用this(1, 1)
调用了带参数的构造方法Rectangle(int width, int height)
,并传入初始值。 this()
语句必须是构造方法中的第一条语句。
3. 返回当前对象
在方法中,可以使用 this
关键字返回当前对象,这在实现链式调用时非常有用。
class Calculator {private int result;public Calculator add(int num) {this.result += num;// 返回当前对象return this;}public Calculator subtract(int num) {this.result -= num;// 返回当前对象return this;}public int getResult() {return this.result;}
}public class Main {public static void main(String[] args) {Calculator calculator = new Calculator();int finalResult = calculator.add(5).subtract(2).getResult();System.out.println("Final result: " + finalResult);}
}
代码解释
add
方法和subtract
方法都返回this
,即当前对象。这样就可以在调用这些方法时进行链式调用,如calculator.add(5).subtract(2).getResult()
。
4. 在内部类中引用外部类的对象
当在内部类中需要引用外部类的对象时,可以使用 外部类名.this
的形式。
class Outer {private int value = 10;class Inner {public void printOuterValue() {// 引用外部类的对象System.out.println("Outer value: " + Outer.this.value);}}public void testInner() {Inner inner = new Inner();inner.printOuterValue();}
}public class Main {public static void main(String[] args) {Outer outer = new Outer();outer.testInner();}
}
七、静态方法和动态方法
- 静态方法:使用
static
关键字修饰的方法,属于类本身,而不属于类的某个具体实例。在类加载时,静态方法就会被分配内存空间,并且在整个程序运行期间只有一份副本。静态方法可以直接通过类名调用,也能通过对象实例调用(不过不推荐)。 - 非静态方法:没有使用
static
关键字修饰的方法,属于类的实例,每个对象都有自己独立的一份非静态方法。非静态方法必须通过对象实例来调用。
调用方式
静态方法
静态方法可以直接通过类名调用,也可以通过对象实例调用,但通常推荐使用类名调用,以明确表示该方法是静态方法。示例如下:
class MathUtils {// 静态方法public static int add(int a, int b) {return a + b;}
}public class Main {public static void main(String[] args) {// 通过类名调用静态方法int result1 = MathUtils.add(3, 5); System.out.println("result1: " + result1);}
}
非静态方法
非静态方法必须通过对象实例来调用。
class Person {// 非静态方法public void sayHello() {System.out.println("Hello!");}
}public class Main {public static void main(String[] args) {Person person = new Person();// 通过对象实例调用非静态方法person.sayHello(); }
}
使用场景
静态方法的使用场景
- 工具类方法:当一个方法不依赖于对象的状态,只完成一些通用的功能时,通常将其定义为静态方法。例如,Java 中的
Math
类提供了很多静态方法,如Math.sqrt()
用于计算平方根,Math.random()
用于生成随机数等。
class StringUtils {// 静态工具方法:判断字符串是否为空public static boolean isEmpty(String str) {return str == null || str.length() == 0;}
}
- 单例模式中的静态方法:在单例模式中,通常会提供一个静态方法来获取单例对象。
class Singleton {private static Singleton instance;private Singleton() {}// 静态方法:获取单例对象public static Singleton getInstance() {if (instance == null) {instance = new Singleton();}return instance;}
}
非静态方法的使用场景
- 依赖对象状态的操作:当一个方法需要访问对象的成员变量或调用对象的其他非静态方法时,应该将其定义为非静态方法。
class Account {private double balance;public Account(double balance) {this.balance = balance;}// 非静态方法:存款操作,依赖对象的 balance 成员变量public void deposit(double amount) {this.balance += amount;}// 非静态方法:获取账户余额public double getBalance() {return this.balance;}
}
- 需要多态性的场景:非静态方法可以被重写,实现多态性。在面向对象编程中,多态性是一个重要的特性,可以提高代码的灵活性和可扩展性。
class Animal {public void makeSound() {System.out.println("Animal makes a sound");}
}class Dog extends Animal {@Overridepublic void makeSound() {System.out.println("Dog barks");}
}public class Main {public static void main(String[] args) {Animal animal = new Dog();// 调用重写后的非静态方法,体现多态性animal.makeSound(); }
}
要实现创建多个 Dog
实例,并且在调用 animalMakesouds
方法时输出不同的文案,你可以为 Dog
类添加一个构造方法,用于接收不同的文案信息,并在 animalMakesouds
方法中输出这些信息。以下是修改后的代码:
// 定义 Animal 类
class Animal {public void animalMakesouds() {System.out.println("我是一只 animal。");}
}// 定义 Dog 类,继承自 Animal 类
class Dog extends Animal {private String message;// 构造方法,用于接收不同的文案信息public Dog(String message) {this.message = message;}@Overridepublic void animalMakesouds() {// 输出接收到的文案信息System.out.println(message);}
}public class Main {public static void main(String[] args) {// 创建第一个 Dog 实例,传入不同的文案信息Dog dog1 = new Dog("我是 dog1,汪汪汪!");// 调用 animalMakesouds 方法,输出相应的文案dog1.animalMakesouds();// 创建第二个 Dog 实例,传入不同的文案信息Dog dog2 = new Dog("我是 dog2,嗷嗷嗷!");// 调用 animalMakesouds 方法,输出相应的文案dog2.animalMakesouds();}
}
注意事项
- 静态方法不能直接访问非静态成员:静态方法属于类,在类加载时就已经存在,而此时可能还没有创建对象实例,所以静态方法不能直接访问对象的非静态成员变量和非静态方法。如果需要访问,必须先创建对象实例,然后通过对象实例来访问。
- 非静态方法可以访问静态成员:非静态方法属于对象实例,在对象创建后可以访问类的静态成员变量和静态方法。
八、构造方法
构造方法就是用来给实例化对象初始化用的
- 生成代码:第一张和第二张图展示的是在代码编辑区右键选择 “Generate...”(快捷键
Alt+Insert
),可以弹出代码生成菜单,能快速创建构造函数(Constructor)、Getter 和 Setter 方法、equals()
和hashCode()
方法等,方便开发者快速生成常用代码结构。 - 安装插件:第三张图是在 IDEA 的设置(Settings)中找到插件(Plugins)选项,在插件市场(Marketplace)搜索 “jclasslib Bytecode Viewer” 并安装。这是一个用于查看 Java 类文件字节码的插件,安装后可以帮助开发者分析字节码层面的信息,了解代码的底层执行逻辑。
九、封装
- setter 方法:也叫设置器或修改器方法,用于设置私有成员变量的值。一般以 “set” 开头,后面紧跟变量名,且变量名首字母大写,返回值类型通常为 void,需要一个与对应私有成员变量类型相同的参数。例如,对于私有成员变量
name
,其 setter 方法通常命名为setName(String name)
。 - getter 方法:也叫访问器或获取器方法,用于获取私有成员变量的值。一般以 “get” 开头(对于布尔类型的变量,常用 “is” 开头 ,如
isEnabled
),后面紧跟变量名,且变量名首字母大写,返回值类型与对应的私有成员变量类型相同,无参数。例如,对于私有成员变量name
,其 getter 方法通常命名为getName()
。
public class Person {private String name;private int age;// setter方法,设置name的值public void setName(String name) {this.name = name;}// getter方法,获取name的值public String getName() {return name;}// setter方法,设置age的值,并进行简单的数据验证public void setAge(int age) {if (age >= 0 && age <= 150) {this.age = age;} else {System.out.println("年龄不合法");}}// getter方法,获取age的值public int getAge() {return age;}
}
public class Main {public static void main(String[] args) {Person person = new Person();person.setName("Alice");person.setAge(25);System.out.println("姓名:" + person.getName() + ",年龄:" + person.getAge());}
}
包 ?是什么?
包是什么
在 Java 中,包(Package)是组织和管理类、接口、枚举和注解等代码实体的一种机制,类似于计算机中的文件夹,用于将相关的代码文件组合在一起。
- 命名空间:包为相关的类和接口提供了一个命名空间。比如
java.util
包中包含了集合框架、日期时间等相关类,java.awt
包中包含了图形界面相关类。不同包中的类即使名字相同也不会冲突,因为它们的完全限定名(包名 + 类名)是不同的 。 - 目录结构对应:包的层次结构与 Java 开发系统的文件系统结构相同。包名中的点(
.
)会对应文件系统中的目录分隔符。例如package com.example.demo
,那么在文件系统中,相关的类文件就应该存放在com/example/demo
这样的目录结构下。 - 声明方式:在 Java 源文件的开头使用
package
关键字声明该文件中的类所属的包。例如package mypackage;
,并且一个源文件中最多只能有一条package
语句,且该语句要放在文件的第一条可执行语句位置(前面只能有注释或空行)。
为什么要创建包
- 组织代码:随着项目规模增大,类的数量可能成百上千。通过创建包,将功能相似或相关的类放在同一个包中,能让代码结构更加清晰,便于开发者管理和维护。例如,一个电商项目中,可将用户相关的类放在
com.ecommerce.user
包中,订单相关类放在com.ecommerce.order
包中 。 - 避免命名冲突:不同包中的类可以同名。在大型项目或者多个团队协作开发时,很难避免类名重复,而包机制可以通过包名 + 类名的方式来唯一标识一个类,从而解决命名冲突问题。
- 提供访问控制:包可以配合访问修饰符(如
private
、默认、protected
、public
)来限定类、方法和变量的访问范围。处于同一个包中的类可以访问默认权限的成员;不同包中的子类在一定条件下可以访问父类protected
权限的成员,这样能更好地隐藏类的实现细节,增强代码的安全性。 - 模块化:每个包可以看作一个独立的模块,具有特定的功能和职责,降低了代码之间的耦合度,提高了代码的可重用性和可扩展性。开发者可以更方便地复用某个包中的代码,或者对单个包进行功能扩展和修改,而不影响其他包。
- 静态变量:被
static
关键字修饰的变量。它们在类加载时被分配内存并初始化,且在整个程序运行期间都存在。如public static int count = 0;
中的count
变量,就存储在方法区中。
静态的成员变量和静态的方法是放到方法区的,动态的方法是存放在堆上的
静态的可以通过类名访问,动态的依赖于对象,由于静态是直接通过调用类名进行访问的,所以静态方法内部不可以访问动态成员和其方法
- 静态代码块:在类加载时执行,且只执行一次。静态代码块主要用于对类进行一些初始化操作,比如初始化静态变量等。由于类只加载一次,所以静态代码块也只会执行一次。
- 实例代码块:在创建对象时执行,每次创建对象都会执行实例代码块。实例代码块会在构造方法之前执行,主要用于对对象进行一些通用的初始化操作。
- 构造方法:在创建对象时执行,用于初始化对象的属性。构造方法在实例代码块之后执行。