【Java】对象与类
文章目录
- 【Java】对象与类
- 1、学习背景
- 2、定义&使用
- 2.1 创建类
- 2.2 创建对象
- 3、static关键字
- 3.1 修饰变量
- 3.2 修饰方法
- 3.3 修饰代码块
- 3.4 修饰内部类
- 4、this关键字
- 5、封装特性
- 5.1 访问修饰符
- 5.2 包的概念
- 6、构造方法
- 7、代码块
- 7.1 普通代码块
- 7.2 成员代码块
- 7.3 静态代码块
- 7.4 同步代码块
1、学习背景
在学习C++后继续学习Java,Java给我的第一感觉和C++很像,它们都是面对对象编程(OOP)语言,在很多知识上都有相似之处,这也给学习带来了方便
就从类与对象入手,先分析Java与C++在这上边的区别
-
语法差异:
- 类定义: 在C++中,类的定义可以分为头文件(.h)和实现文件(.cpp)。在Java中,一个类通常定义在一个独立的源文件中。
- 构造函数: 在C++中,构造函数和析构函数的命名与类名相同,没有返回类型。在Java中,构造函数的命名与类名相同,但没有析构函数的概念。
- 继承: 在C++中,使用
public
、protected
和private
关键字来控制继承中的成员访问权限。在Java中,使用public
、protected
、private
和默认(包内可见)四种访问修饰符。
-
内存管理:
- 内存分配: C++中使用
new
和delete
来手动管理对象的内存分配和释放。Java中的对象内存管理由垃圾回收器自动处理。 - 析构函数和垃圾回收: C++的析构函数用于对象被销毁时执行清理操作。Java中没有析构函数,垃圾回收器负责释放不再使用的对象。
- 内存分配: C++中使用
-
引用类型:
- 在C++中,可以使用引用类型来创建别名,但在Java中,没有直接的引用类型,只有对象引用。
-
继承与多态:
- C++中支持多重继承,一个类可以从多个父类派生。Java中只支持单一继承,但可以通过接口实现类似多重继承的效果。
- C++中使用虚函数和关键字
virtual
来实现运行时多态性。Java中所有非静态方法默认都是虚方法,可以被子类重写。
-
访问控制:
- C++中通过
public
、protected
和private
来控制类成员的访问权限。Java也使用类似的修饰符,包括public
、protected
、private
和默认(包内可见)。
- C++中通过
尽管存在一些差异,但类与对象的概念在C++和Java中基本上是类似的。这两种语言都支持封装、继承、多态等面向对象的基本概念,但具体实现和一些细节方面会有一些差异。
2、定义&使用
2.1 创建类
在Java中,创建一个类需要使用特定的语法结构。下面是创建类的基本语法:
// 类的定义
public class ClassName {
// 成员变量(属性)
dataType variableName1;
dataType variableName2;
// ...
// 构造方法
public ClassName() {
// 构造方法的代码
}
// 成员方法(函数)
returnType methodName1(parameterType parameterName1, parameterType parameterName2, ...) {
// 方法的代码
}
returnType methodName2(parameterType parameterName1, parameterType parameterName2, ...) {
// 方法的代码
}
// ...
}
在上面的代码中,需要做以下几件事情:
-
类名(ClassName): 用于标识类的名称,通常采用驼峰命名法(首字母小写)。
-
成员变量(属性): 在类中定义成员变量,用于存储对象的数据。需要指定数据类型和变量名。
-
构造方法: 构造方法用于创建对象时初始化成员变量,没有返回类型,并且与类名相同。可以有多个构造方法,以支持不同的初始化方式。
-
成员方法(函数): 在类中定义成员方法,用于执行各种操作。需要指定返回类型、方法名、参数列表和方法体。
请注意以下几点:
- 成员变量和方法可以有不同的访问修饰符,如
public
、private
、protected
或默认(包内可见)。 - 构造方法在创建对象时被调用,用于初始化对象的属性。
- 成员方法可以执行各种操作,使用返回类型指定方法的返回值类型。
以下是一个简单的例子:
public class Person {
// 成员变量
private String name;
private int age;
// 构造方法
public Person(String n, int a) {
name = n;
age = a;
}
// 成员方法
public void introduce() {
System.out.println("My name is " + name + " and I am " + age + " years old.");
}
// 主方法(用于测试)
public static void main(String[] args) {
Person person1 = new Person("Alice", 25);
person1.introduce();
Person person2 = new Person("Bob", 30);
person2.introduce();
}
}
在这个例子中,Person
类有两个成员变量 name
和 age
,一个构造方法用于初始化这些变量,以及一个 introduce
方法用于介绍人物信息。在 main
方法中,我们创建了两个 Person
对象并调用了他们的 introduce
方法。
2.2 创建对象
创建了一个类并定义了其属性和方法后,可以使用 new
关键字来实例化(创建)该类的对象。
在上面的例子中,我们已经创建了 Person
类,现在让我们继续使用该类来创建对象并调用方法。
public class Person
{
// ...(之前的类定义)
// 主方法(用于测试)
public static void main(String[] args)
{
Person person1 = new Person("alice",25);
Person person2 = new Person("dick",13);
person1.introduce();
person2.introduce();
}
}
在上面的代码中,我们在 main
方法中创建了两个 Person
对象 person1
和 person2
。然后,我们分别调用了这两个对象的 introduce
方法,以显示它们的信息。
这就是在Java中创建对象的基本过程。通过实例化类,可以创建具有类定义属性和方法的实际对象,并通过调用对象的方法来执行特定的操作。在实际开发中,类和对象的概念是面向对象编程中非常重要的基础。
与C++不同点出现了,Java多数情况下将main
方法放在类中
main
方法在Java中的特殊作用是作为程序的入口点。当运行一个Java程序时,JVM会从main
方法开始执行。这种设计方式使得执行流程具有明确的起点,确保了程序的可预测性和可控性。将
main
方法放在类内部有几个重要的原因:
统一入口: 将
main
方法置于类内部使得每个Java类都可以作为一个独立的程序执行。这样,你可以在一个项目中创建多个独立的Java程序,每个程序都有自己的入口点。访问权限: Java中的访问修饰符(如
public
、private
等)也适用于方法。将main
方法放在类内部意味着你可以根据需要选择设置其访问权限,以便控制它是否可以被其他类访问。类的上下文:
main
方法可以直接访问类中定义的成员变量和方法。这对于在程序启动时进行一些初始化操作或测试非常有用。简化执行: Java应用程序的执行流程始终从
main
方法开始,这种统一入口的设计使得程序的执行过程变得清晰可见,易于理解和调试。基于以上原因,Java程序的
main
方法通常被放置在类的内部作为程序的入口。当在命令行或集成开发环境(IDE)中运行Java程序时,JVM会查找并执行具有正确签名(public static void main(String[] args)
)的main
方法。
3、static关键字
3.1 修饰变量
在Java中,使用 static
关键字来修饰变量会使这个变量成为一个静态变量(也叫类变量)。静态变量属于类而不是对象,它在类加载时被初始化,并且在整个类的生命周期内都只有一份副本,被所有实例共享。以下是关于使用 static
修饰变量的一些重要点:
-
静态变量声明: 静态变量通常在类的顶层声明,即在类的成员变量部分,但在方法和构造方法之外。它们必须使用
static
关键字修饰。 -
初始化: 静态变量在类加载时会被初始化,只会执行一次。你可以在声明时直接赋值,也可以在静态代码块中进行初始化。
-
共享性: 所有类的实例共享同一个静态变量,它不属于特定的实例,而是属于整个类。
-
通过类名访问: 静态变量可以使用类名直接访问,不需要创建对象。例如,
ClassName.staticVariable
。 -
在静态方法中使用: 静态变量可以在静态方法中直接使用,因为静态方法属于类而不是对象。
-
线程共享: 由于静态变量属于类,多个线程共享同一个静态变量。需要注意线程安全问题。
下面是一个使用静态变量的例子:
public class Example {
// 静态变量
static int count = 0;
// 构造方法
public Example() {
count++;
}
// 静态方法使用静态变量
public static void printCount() {
System.out.println("Count: " + count);
}
public static void main(String[] args) {
Example obj1 = new Example();
Example obj2 = new Example();
Example.printCount(); // 输出:Count: 2
}
}
在这个例子中,count
是一个静态变量,用于计算 Example
类的实例数量。通过每次创建对象时递增 count
,最终可以得到对象的总数。静态方法 printCount
可以直接访问静态变量 count
,因为它们都属于类本身。
3.2 修饰方法
方法与函数
方法(Method)和函数(Function)在编程中基本上是相同的概念,尤其在讨论通用性时。它们都表示一段可重复使用的代码块,用于执行特定的操作或完成特定的任务。然而,它们的使用和命名可能因编程语言而异。
在面向对象编程(OOP)中,尤其是在Java等一些编程语言中,我们更常用术语“方法”来表示属于类或对象的行为。方法是对象的行为,它们通常用于在对象上执行操作、访问数据或执行一些功能。
在一些非面向对象的编程语言(如C语言),我们更常用术语“函数”来表示可重用的代码块,它们可以独立于对象存在。这些函数可以接受参数,执行一些操作,并返回一个值。在这种情况下,函数不一定与对象相关联,它们可以是独立的代码模块。
虽然在一些语言中术语可能会有所不同,但在大多数情况下,方法和函数都指代相似的概念:可重用的代码块,用于执行特定的操作或完成任务。无论使用哪个术语,它们都是编程中非常重要的概念,用于模块化和组织代码。
在Java中,使用 static
关键字修饰方法会使这个方法成为一个静态方法。静态方法不属于特定的实例,而是属于整个类,因此它可以在不创建类的实例的情况下直接通过类名调用。以下是关于使用 static
修饰方法的一些重要点:
-
静态方法声明: 静态方法必须在类的顶层声明,即在类的成员方法部分,但在构造方法之外。它们必须使用
static
关键字修饰。 -
调用方式: 静态方法可以通过类名直接调用,不需要创建对象。例如,
ClassName.staticMethod()
。 -
不能访问实例变量: 静态方法不能直接访问实例变量和非静态方法,因为它们不属于具体的实例。只能访问静态变量和静态方法。
-
不能被重写: 静态方法不能被子类重写(覆盖),因为它们属于类而不是实例,不具有多态性。
-
可以访问静态成员: 静态方法可以访问类中的其他静态成员,包括静态变量和其他静态方法。
下面是一个使用静态方法的例子:
public class Example {
// 静态变量
static int count = 0;
// 静态方法
public static void incrementCount() {
count++;
}
// 非静态方法
public void printCount() {
System.out.println("Count: " + count);
}
public static void main(String[] args) {
Example.incrementCount();
Example.incrementCount();
Example.printCount(); // 错误,静态方法无法访问实例方法
}
}
在这个例子中,incrementCount
是一个静态方法,用于递增静态变量 count
。由于它是静态的,可以通过类名直接调用。printCount
是一个非静态方法,无法在静态方法中直接访问,所以在 main
方法中调用它会导致错误。
静态的main方法
在Java中,
main
方法必须是静态的,否则程序无法运行。这是因为在Java程序启动时,首先会加载类并调用静态的main
方法作为程序的入口点。要使Java程序能够作为可执行程序运行,
main
方法必须满足以下条件:
main
方法必须是public
访问修饰符,这样JVM才能访问它。main
方法的返回类型必须是void
,即不返回任何值。main
方法的名称必须是main
。main
方法的参数必须是一个String
数组(String[] args
),用于接收命令行参数。另外,由于程序启动时没有创建对象,所以
main
方法必须是静态的,以便可以通过类名直接调用,而不需要创建类的实例。以下是一个标准的
main
方法的示例:public class MainExample { public static void main(String[] args) { System.out.println("Hello, Java!"); } }
在这个例子中,
main
方法是一个静态方法,因此可以通过类名MainExample
直接调用。运行这个程序时,JVM会从main
方法开始执行,输出 “Hello, Java!”。
3.3 修饰代码块
在Java中,static
关键字不仅可以用于修饰静态变量和静态方法,还可以用于修饰静态代码块。静态代码块是一个特殊的代码块,在类加载时执行,用于进行一些类级别的初始化操作。以下是关于静态代码块的一些重要点:
-
静态代码块声明: 静态代码块是在类的成员部分,但在方法和构造方法之外声明的。它使用
static
关键字修饰。 -
执行时机: 静态代码块在类加载时执行,只会执行一次,且在类的构造方法之前执行。
-
初始化操作: 静态代码块通常用于执行一些类级别的初始化操作,如初始化静态变量、加载资源等。
-
无法访问实例成员: 静态代码块不能直接访问实例变量和实例方法,因为它在类加载时执行,没有创建对象。
-
可以访问静态成员: 静态代码块可以访问类中的其他静态成员,包括静态变量和静态方法。
下面是一个使用静态代码块的例子:
public class StaticBlockExample {
// 静态变量
static int count;
// 静态代码块
static {
count = 10;
System.out.println("Static block initialized.");
}
public static void main(String[] args) {
System.out.println("Count: " + count); // 输出:Count: 10
}
}
在这个例子中,静态代码块用于初始化静态变量 count
,并输出一条初始化信息。当类加载时,静态代码块会执行一次,然后在 main
方法中输出 count
的值。需要注意的是,静态代码块在类加载时自动执行,不需要显式调用。
静态代码块意义
静态代码块在Java中具有重要的意义,它主要用于在类加载时进行一些初始化操作,实现了以下几个方面的功能:
-
静态成员初始化: 静态代码块是在类加载时执行的,因此可以用于初始化静态变量。这对于需要进行一些计算、设置默认值或者加载静态资源的静态变量是非常有用的。
-
资源加载: 如果一个类需要加载一些外部资源,如配置文件、数据库连接等,静态代码块可以用来加载这些资源。这样可以确保在类第一次被使用时就完成资源的加载。
-
单次初始化: 静态代码块在类加载时只会执行一次,这意味着其中的初始化操作也只会执行一次。这对于需要确保某些初始化操作只执行一次的情况非常有用。
-
提高效率: 如果某些计算或操作在每个实例中都是相同的,将其放在静态代码块中可以减少重复计算,从而提高效率。
-
复杂初始化: 静态代码块可以用于执行一些较为复杂的初始化操作,这些操作可能无法在变量声明时完成。
下面是一个使用静态代码块初始化数据库连接的示例:
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class DatabaseConnection {
static Connection connection;
static {
try {
// 初始化数据库连接
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb", "username", "password");
System.out.println("Database connection initialized.");
} catch (SQLException e) {
System.err.println("Database connection failed: " + e.getMessage());
}
}
public static Connection getConnection() {
return connection;
}
public static void main(String[] args) {
Connection conn = DatabaseConnection.getConnection();
// 使用数据库连接执行操作
}
}
在这个例子中,静态代码块被用来初始化数据库连接,确保在类加载时连接已经建立,从而可以在其他地方使用这个连接。这种方式能够避免在每次需要连接数据库时都进行重复的初始化操作。
3.4 修饰内部类
在Java中,static
关键字可以用于修饰内部类,创建静态内部类。静态内部类与非静态内部类之间有一些重要的区别,主要体现在内部类的访问方式、生命周期和作用域等方面。
以下是关于使用 static
修饰内部类的一些重要点:
-
访问方式: 静态内部类可以直接通过外部类访问,无需创建外部类的实例。非静态内部类需要通过外部类的实例来访问。
-
生命周期: 静态内部类的生命周期与外部类无关,它的创建不依赖于外部类的实例。非静态内部类的生命周期与外部类实例的生命周期紧密关联。
-
作用域: 静态内部类可以在外部类的静态方法中直接使用,因为它们不依赖于外部类的实例。非静态内部类不能直接在外部类的静态方法中使用,因为它们需要通过外部类的实例来访问。
-
对外部类成员的访问: 静态内部类可以访问外部类的静态成员,但不能直接访问外部类的非静态成员。非静态内部类可以访问外部类的所有成员,包括静态和非静态。
以下是一个使用静态内部类的示例:
public class OuterClass {
private static int outerStaticField = 10;
private int outerInstanceField = 20;
// 静态内部类
public static class StaticInnerClass {
public void printOuterFields() {
System.out.println("Outer static field: " + outerStaticField);
// 无法访问 outerInstanceField
}
}
public static void main(String[] args) {
StaticInnerClass inner = new StaticInnerClass();
inner.printOuterFields();
}
}
在这个例子中,StaticInnerClass
是一个静态内部类。在 main
方法中,我们创建了 StaticInnerClass
的实例,并通过它访问了外部类的静态成员 outerStaticField
。需要注意的是,静态内部类不能直接访问外部类的非静态成员 outerInstanceField
。
总之,静态内部类是与外部类分开的独立实体,无需外部类实例即可访问,因此在某些场景下可以提供更好的灵活性和封装性。
4、this关键字
在Java中,this
关键字用于引用当前对象,即正在执行代码的实例对象。它在访问类的成员变量、方法和构造方法时非常有用,以便明确指示正在操作的对象。以下是 this
关键字的几个主要作用:
-
引用当前对象: 在类的方法中,使用
this
关键字引用当前对象,以便访问对象的成员变量和方法。 -
区分参数和成员变量: 如果方法的参数名与对象的成员变量名相同,使用
this
关键字可以明确指示要访问的是成员变量而不是参数。 -
在构造方法中调用其他构造方法: 当一个类有多个构造方法时,可以使用
this
关键字调用其他构造方法,以避免代码重复。 -
返回当前对象: 在方法中返回当前对象本身,以支持链式调用的编程风格。
以下是这些用法的示例:
public class Person {
private String name;
public Person(String name) {
this.name = name; // 使用 this 关键字区分参数和成员变量
}
public void setName(String name) {
this.name = name; // 使用 this 关键字引用当前对象的成员变量
}
public void printName() {
System.out.println("Name: " + this.name);
}
public Person anotherPerson() {
return this; // 返回当前对象本身
}
public static void main(String[] args) {
Person person1 = new Person("Alice");
Person person2 = new Person("Bob");
person1.printName();
person2.printName();
person1.anotherPerson().printName(); // 链式调用
}
}
在这个例子中,this
关键字用于引用当前对象,以区分参数和成员变量、调用方法以及返回当前对象本身。这有助于确保代码的清晰性和准确性。
5、封装特性
封装(Encapsulation)是面向对象编程(OOP)中的一种重要特性,它指的是将数据(成员变量)和操作数据的方法(成员方法)封装在一个类中,并通过访问修饰符来控制对这些数据和方法的访问。封装旨在实现数据隐藏、保护数据完整性,以及提供更好的抽象和封装性。
5.1 访问修饰符
Java的封装特性通过访问修饰符实现,主要有三种访问修饰符:private
、protected
和 public
。以下是封装特性的一些重要优点和原则:
-
数据隐藏: 封装允许将数据隐藏在类的内部,只暴露必要的接口供外部使用。这样可以避免直接访问数据,减少了错误和数据损坏的可能性。
-
数据完整性: 通过封装,可以限制对数据的访问方式,确保只有经过验证的方法可以修改数据。这有助于保持数据的正确性和一致性。
-
抽象: 封装使得类的外部使用者只需关心类的公共接口,而不需要了解内部实现细节。这促进了抽象和模块化编程。
-
灵活性: 封装允许在不影响外部代码的情况下修改类的内部实现。只要类的接口保持不变,类的实现可以进行改进。
-
维护性: 封装提高了代码的可维护性,因为修改类的内部实现不会影响使用该类的其他代码。
以下是一个使用封装的示例:
public class BankAccount {
private String accountNumber;
private double balance;
public BankAccount(String accountNumber, double initialBalance) {
this.accountNumber = accountNumber;
this.balance = initialBalance;
}
public double getBalance() {
return balance;
}
public void deposit(double amount) {
if (amount > 0) {
balance += amount;
}
}
public void withdraw(double amount) {
if (amount > 0 && amount <= balance) {
balance -= amount;
}
}
public static void main(String[] args) {
BankAccount account = new BankAccount("123456", 1000.0);
System.out.println("Initial balance: " + account.getBalance());
account.deposit(500.0);
System.out.println("After deposit: " + account.getBalance());
account.withdraw(200.0);
System.out.println("After withdrawal: " + account.getBalance());
}
}
在这个例子中,BankAccount
类将成员变量 accountNumber
和 balance
封装在内部,并通过 public
方法来提供访问。外部代码只能通过这些方法来修改和访问数据,而无法直接访问内部数据。这样可以确保数据的安全性和正确性。
5.2 包的概念
在Java中,“包”(Package)是一种用于组织和管理类和接口的方式。包是一种逻辑上的组织结构,可以将相关的类和接口组织在一起,从而实现更好的模块化、代码复用和命名空间管理。每个类都必须属于一个包,除非它位于默认包中。
以下是关于Java包的一些重要概念:
-
命名空间管理: 包在Java中用于创建命名空间,防止类名冲突。如果不使用包,可能会在不同的库或项目中出现相同的类名,导致命名冲突。
-
包的层次结构: 包可以嵌套形成层次结构。例如,
java.util
包是java
包的子包,java.util.ArrayList
类位于java.util
包中。 -
包的声明: 在类文件的开头,使用
package
关键字声明类所属的包。例如,package com.example;
声明了类属于com.example
包。 -
包的导入: 使用
import
关键字可以引入其他包中的类,以便在当前类中使用。例如,import java.util.ArrayList;
导入了java.util
包中的ArrayList
类。 -
默认包: 如果不在类文件中使用
package
声明,则类被认为属于默认包。默认包是没有命名空间的,因此不建议在实际项目中使用。 -
约定: 包名通常使用小写字母,采用逆域名(反转的域名)的命名方式,以确保唯一性和清晰性。例如,
com.example.myproject
。
以下是一个简单的包使用示例:
假设有两个类:Person
和 Address
。可以将它们组织到一个叫做 com.example.myproject
的包中。
在 Person.java
文件中:
package com.example.myproject;
public class Person {
// 类的定义
}
在 Address.java
文件中:
package com.example.myproject;
public class Address {
// 类的定义
}
这样,Person
和 Address
类就位于同一个包中,可以通过导入包的方式在其他类中使用它们。
和C/C++中的头文件有些相似。
6、构造方法
在Java中,构造方法(Constructor)是一种特殊类型的方法,用于创建和初始化对象。构造方法在对象被创建时自动调用,用于设置对象的初始状态,分配内存和执行必要的初始化操作。以下是构造方法的一些重要特点:
-
方法名与类名相同: 构造方法的方法名必须与类名完全相同,包括大小写。它没有返回类型,也不需要声明返回值。
-
没有返回值: 构造方法不需要返回值类型,因为它们的主要目的是初始化对象,而不是返回值。
-
自动调用: 构造方法在使用
new
关键字创建对象时自动调用。每次创建对象时,都会调用构造方法一次。 -
重载: 类可以有多个构造方法,以满足不同的初始化需求。这称为构造方法的重载。
-
默认构造方法: 如果类没有显式定义构造方法,编译器会自动提供一个无参数的默认构造方法。如果类定义了任何构造方法,编译器将不再提供默认构造方法。
以下是一个示例,展示如何定义和使用构造方法:
public class Person {
private String name;
private int age;
// 构造方法1:带参数的构造方法
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// 构造方法2:无参数的构造方法
public Person() {
this.name = "Unknown";
this.age = 0;
}
// 成员方法
public void introduce() {
System.out.println("Name: " + name);
System.out.println("Age: " + age);
}
public static void main(String[] args) {
Person person1 = new Person("Alice", 25);
Person person2 = new Person();
person1.introduce();
person2.introduce();
}
}
在这个例子中,Person
类定义了两个构造方法:一个带参数的构造方法用于设置对象的初始状态,另一个无参数的构造方法用于提供默认值。通过 new
关键字创建对象时,会根据提供的参数选择合适的构造方法进行初始化。
7、代码块
7.1 普通代码块
在Java中,普通代码块(也称为局部代码块)是一种用于限定变量作用域和执行特定代码的代码块。普通代码块不同于方法和构造方法,它位于方法内部,可以在其中定义临时变量,这些变量只在代码块内部可见。
以下是关于普通代码块的一些重要特点:
-
作用域限定: 在普通代码块中定义的变量只在代码块内部可见。这有助于避免命名冲突和混淆。
-
临时变量: 普通代码块通常用于定义临时变量,这些变量在代码块执行完毕后会被销毁,不会影响到方法外部的代码。
-
变量生命周期: 普通代码块内部定义的变量的生命周期仅限于代码块的执行期间。一旦代码块执行完毕,变量将被销毁。
-
代码块嵌套: 可以在方法中嵌套多个普通代码块。内部代码块中的变量对外部代码块不可见,但外部代码块中的变量对内部代码块可见。
以下是一个使用普通代码块的示例:
public class LocalBlockExample {
public static void main(String[] args) {
int x = 10; // 外部变量
System.out.println("Outside x: " + x);
{
int y = 20; // 内部代码块变量
System.out.println("Inside y: " + y);
System.out.println("Inside x: " + x); // 可以访问外部变量
}
// y 在这里不可见
{
int z = 30; // 另一个内部代码块变量
System.out.println("Inside z: " + z);
}
// z 在这里不可见
}
}
在这个例子中,我们在 main
方法中使用了两个普通代码块。内部代码块中的变量只在代码块内部可见,不会影响到外部的变量。这有助于限定变量的作用域,提高代码的可读性和维护性。
7.2 成员代码块
成员代码块(Instance Initializer Block)是在Java类中的一个代码块,用于在创建对象实例时执行初始化操作。它类似于构造方法,但与构造方法不同的是,成员代码块在每次创建对象时都会执行,而不是在特定的构造方法中执行。
以下是关于成员代码块的一些重要特点:
-
执行顺序: 成员代码块在每次创建对象时都会在构造方法之前执行。如果一个类有多个构造方法,它们都会共享同一个成员代码块。
-
共享初始化逻辑: 成员代码块允许在多个构造方法之间共享相同的初始化逻辑,从而避免代码重复。
-
可用性: 成员代码块可以在类的任何地方定义,不受方法的限制。它会在构造方法前执行,无论构造方法如何调用。
以下是一个使用成员代码块的示例:
public class InstanceInitializerExample {
private int x;
private int y;
// 成员代码块
{
x = 10;
y = 20;
System.out.println("Instance initializer block executed.");
}
// 构造方法1
public InstanceInitializerExample() {
System.out.println("Constructor 1 called.");
}
// 构造方法2
public InstanceInitializerExample(int x, int y) {
this.x = x;
this.y = y;
System.out.println("Constructor 2 called.");
}
public void printValues() {
System.out.println("x: " + x + ", y: " + y);
}
public static void main(String[] args) {
InstanceInitializerExample obj1 = new InstanceInitializerExample();
obj1.printValues();
InstanceInitializerExample obj2 = new InstanceInitializerExample(30, 40);
obj2.printValues();
}
}
在这个例子中,InstanceInitializerExample
类使用成员代码块来初始化 x
和 y
成员变量。每次创建对象时,成员代码块都会在构造方法之前执行。这有助于确保对象的初始化逻辑在不同的构造方法中得到共享。
7.3 静态代码块
上边 3.3 static修饰代码块处已经讲过,就不再赘述
7.4 同步代码块
// 构造方法2
public InstanceInitializerExample(int x, int y) {
this.x = x;
this.y = y;
System.out.println("Constructor 2 called.");
}
public void printValues() {
System.out.println("x: " + x + ", y: " + y);
}
public static void main(String[] args) {
InstanceInitializerExample obj1 = new InstanceInitializerExample();
obj1.printValues();
InstanceInitializerExample obj2 = new InstanceInitializerExample(30, 40);
obj2.printValues();
}
}
在这个例子中,`InstanceInitializerExample` 类使用成员代码块来初始化 `x` 和 `y` 成员变量。每次创建对象时,成员代码块都会在构造方法之前执行。这有助于确保对象的初始化逻辑在不同的构造方法中得到共享。
### 7.3 静态代码块
上边 3.3 static修饰代码块处已经讲过,就不再赘述
### 7.4 同步代码块
在以后Java多线程的学习中会介绍