final关键字、权限修饰符、代码块
final关键字,权限修饰符,代码块
-
final
关键字final
关键字用来修饰类、方法和变量。被final
修饰的类不能再被继承,被final
修饰的方法不能再被重写,被final
修饰的变量不能再被赋值。final
变量必须在声明时或构造函数中进行初始化赋值,且不能再被更改。final
变量用于常量的定义,比如数学常数π。 -
权限修饰符(access modifiers)
权限修饰符用于限制类、接口、变量或方法的访问范围。Java中有四种权限修饰符:
public、protected、default
和private
。它们的访问范围从宽到窄分别是:public > protected > default > private
。其中,public
表示公开的,可以被任何类调用;protected
表示受保护的,只能被本包和子类调用;default
表示默认的,只能被同包的类调用;private
表示私有的,只能被当前类中的方法调用。 -
代码块(code block)
代码块是一段被大括号包围的代码,可以用来初始化代码或限制变量的生命周期。Java中有两种常见的代码块:静态代码块和实例代码块。静态代码块在类加载时执行,且只执行一次,用于初始化静态变量或执行一些全局操作;实例代码块在每次创建对象时执行,用于初始化对象或执行一些需要在构造函数之前执行的操作。
1. final
关键字
当我们在Java中使用关键字final修饰类、方法或变量时,它表达的含义有所不同。
1.1 final
关键字修饰的含义
-
final类:
使用final关键字修饰的类称为final类。final类不能被继承。这种设计用于确保类的完整性和稳定性,防止其他类对其进行扩展或修改。例如,String类就是一个final类,这意味着我们不能创建它的子类。
例如:
final class MyClass { // 类的定义 }
-
final方法:
使用final关键字修饰的方法称为final方法。final方法不能被子类重写。这主要用于确保父类的某个方法在继承关系中不被改变,以保证方法的行为不被修改。
例如:
class Parent { final void printMessage() { System.out.println("This is a final method."); } } class Child extends Parent { // 试图重写final方法,编译错误 // void printMessage() { ... } }
-
final变量:
使用final关键字修饰的变量称为final变量。final变量被赋值后,其值不能再次修改,因此它成为了一个常量。final变量通常用于声明不希望被修改的常量。
例如,定义一个圆周率的常量:
final double PI = 3.14159;
final变量必须在声明时或构造函数中进行初始化。一旦被赋值后,在后续的代码中无法再修改其值。
-
final修饰引用变量:
当我们使用final关键字修饰引用变量时,表示该变量的引用地址不能再改变,但是对象本身可以修改。例如:
final StringBuilder sb = new StringBuilder("Hello"); sb.append(" World"); // 合法,修改对象的内容 sb = new StringBuilder("Hi"); // 非法,修改引用地址
注意,在这种情况下,我们可以修改被引用的对象的内容,但不能再将sb引用指向其他对象。
1.2 final
关键字修饰的特点
当final关键字用于修饰字段(成员变量)时,它具有以下特点:
-
不可变性(Immunity):被final修饰的字段值在初始化后就不能再修改。
这意味着一旦final字段赋值完毕,它的值将不可更改。
-
必须进行初始化:final字段必须在声明时或构造方法中进行初始化赋值。
如果没有在这两个地方之一对final字段进行赋值,编译器将会报错。
-
编译时常量:如果final字段使用了编译时常量(如整数、字符串等),它的值会在编译时确定,而不是在运行时确定。
这样可以在编译时进行优化并提高性能。
1.3 注意事项
在使用final关键字修饰变量或方法时,需要注意以下几点:
-
初始化:final变量在声明时或构造方法中进行初始化,且只能初始化一次。如果没有在这两个地方之一对final变量进行赋值,编译器将会报错。确保在使用final变量之前已经对其进行了正确的初始化。
-
不可修改:final变量的值在初始化后就不能再修改。尝试修改final变量的值将导致编译错误。因此,在使用final变量时要确保其值不会发生变化。
-
常量命名:由于final变量通常用于表示常量,所以命名时应该使用大写字母和下划线来区分单词,以增加可读性。例如:FINAL_VALUE。
-
继承与覆盖:final关键字可以用于禁止类的继承、方法的覆盖以及变量的修改。在设计类或方法时,需要考虑是否需要禁止继承或覆盖,以及是否需要限制变量的修改。
-
性能优化:使用final关键字可以提供一些性能优化,尤其是在使用final静态变量时,编译器可以进行常量折叠和内联优化,减少代码的执行开销。
-
安全性和可维护性:final关键字可以增强代码的安全性和可维护性。通过将变量声明为final,可以防止意外的修改和副作用,并使代码更加健壮和可预测。
2. 权限修饰符
权限修饰符是Java中用来限制类、接口、变量、方法、构造函数等成员对其他类或对象的访问权限的关键字。在Java中,有四种访问权限修饰符:public、protected、default和private。
public | protected | 默认 | private | |
---|---|---|---|---|
同一类中 | √ | √ | √ | √ |
同一包中的类 | √ | √ | √ | |
不同包的子类 | √ | √ | ||
不同包中的无关类 | √ |
不加权限修饰符,就是默认权限
public具有最大权限,private则是最小权限。
2.1 public
权限修饰符
public
:公共的权限修饰符,具有最大的访问权限。
被public修饰的类、变量、方法或构造函数可以在任何其他类中被访问、调用和继承。它们的可见性是全局的。
例如:
public class MyClass {
public int myVariable; // 公共的成员变量
public void myMethod() { // 公共的方法
// 方法体
}
}
在其他类中可以这样访问公共成员:
MyClass obj = new MyClass();
obj.myVariable = 10; // 访问公共成员变量
obj.myMethod(); // 调用公共方法
2.2 protected
权限修饰符
protected
:受保护的权限修饰符,具有中等访问权限。
被protected修饰的成员(变量、方法或构造函数)可以在同一包内的其他类中访问,以及在不同包中的子类中访问。
示例:
public class MyClass {
protected int myVariable; // 受保护的成员变量
protected void myMethod() { // 受保护的方法
// 方法体
}
}
在子类中可以这样访问受保护的成员:
public class MySubClass extends MyClass {
int result = myVariable; // 访问受保护的成员变量
myMethod(); // 调用受保护的方法
}
2.3 default
权限修饰符
default
:默认的权限修饰符,没有显式地指定任何修饰符时,默认使用该修饰符。
被默认修饰的成员(变量、方法或构造函数)可以在同一包内访问,但在不同包中无法访问。
示例:
class MyClass {
int myVariable; // 默认访问权限的成员变量
void myMethod() { // 默认访问权限的方法
// 方法体
}
}
在同一包内的其他类可以这样访问默认成员:
MyClass obj = new MyClass();
obj.myVariable = 10; // 访问默认成员变量
obj.myMethod(); // 调用默认方法
2.4 private
权限修饰符
private
:私有的权限修饰符,具有最小的访问权限。
被private修饰的成员(变量、方法或构造函数)只能在声明它们的类内部被访问或调用,不能在其他类中被直接访问。私有成员可以通过公共的getter和setter方法间接地访问和修改。
示例:
public class MyClass {
private int myVariable; // 私有的成员变量
private void myMethod() { // 私有的方法
// 方法体
}
}
在同一类内部可以这样访问私有成员:
public class AnotherClass {
public void someMethod() {
MyClass obj = new MyClass();
obj.myVariable = 10; // 访问私有成员变量
obj.myMethod(); // 调用私有方法
}
}
2.5 注意事项
在使用权限修饰符时,需要注意以下事项:
-
访问权限从小到大是:private、默认、protected、public。
因此,方法或成员变量越是向内部封装,使用的访问修饰符的次序就应该越高,即private、默认、protected和public。
-
对于类来说,只有public和默认两种访问级别。
因此,没有定义中间级别的修饰符也不需要在类的定义中写上default关键字。
-
protected
访问修饰符可以让同一包中的所有类和其他包中的子类访问当前类的受保护成员,但是同一包中的其他类也无法访问。 -
方法重写时,子类中的访问修饰符不能具有更小的访问权限。
-
类中的成员变量一般定义为private,用public的取值和赋值方法(即getter和setter方法)进行访问和修改。而不是直接访问和修改成员变量。
-
在同一类中,private成员可以被其他成员访问和修改,因为它们在同一个作用域中。
但是,同一类中的方法也不能直接访问或修改另一个类的private成员。
3. 代码块
代码块是用来定义一段特定的代码片段,用于在特定的时机执行特定的逻辑。在Java中,有三种常见的代码块:静态代码块、实例代码块和局部代码块。
3.1 静态代码块
静态代码块(Static Block):用关键字static
定义的代码块,用来初始化静态成员变量或执行一次性的静态操作。
静态代码块在类加载时执行,即在类被首次加载时,会按照代码块的顺序执行。
示例:
public class MyClass {
static {
// 静态代码块
// 初始化静态成员变量或执行其他静态操作
}
}
3.2 实例代码块
实例代码块(Instance Block):没有任何修饰符或关键字定义的代码块,直接写在类中,用来初始化对象成员变量或执行对象操作。
实例代码块在创建对象时执行,在构造函数之前执行。
示例:
public class MyClass {
{
// 实例代码块
// 初始化实例成员变量或执行其他实例操作
}
}
3.3 局部代码块
局部代码块(Local Block):用花括号{}
包围的代码块,用来定义一个局部的、临时的代码块,主要用于限制变量的作用域。
局部代码块在执行到达时执行,并在代码块结束后销毁。
示例:
public class MyClass {
public void myMethod() {
// 某些代码
{
// 局部代码块
// 在这里定义的变量只在代码块内可见
}
// 某些代码
}
}
3.4 执行顺序
当一个类被加载到内存中时,会按照如下顺序执行相关的代码块:
-
静态代码块(Static Block):静态代码块在类首次加载时执行,仅执行一次,用于初始化静态成员变量或执行其他静态操作。
它的执行顺序是按照代码块的顺序来执行。
-
实例代码块(Instance Block):实例代码块在创建对象时执行,在构造函数之前执行,每次创建对象都会执行一次。
它的执行顺序是在调用构造函数之前按照代码块的顺序执行。
-
构造函数(Constructor):构造函数在创建对象时执行,用于完成对象的初始化。
它在实例代码块执行后执行,并且可以有多个重载的构造函数。
下面是一个示例,演示了代码块的执行顺序:
public class MyClass {
static {
// 静态代码块
System.out.println("静态代码块");
}
{
// 实例代码块
System.out.println("实例代码块");
}
public MyClass() {
// 无参构造函数
System.out.println("无参构造函数");
}
public MyClass(int num) {
// 带参构造函数
System.out.println("带参构造函数");
}
public void myMethod() {
// 普通方法
System.out.println("普通方法");
}
public static void main(String[] args) {
// 创建对象
MyClass obj1 = new MyClass();
System.out.println("--------------------");
MyClass obj2 = new MyClass(10);
}
}
输出结果:
静态代码块
实例代码块
无参构造函数
--------------------
实例代码块
带参构造函数
从输出结果可以看出,静态代码块在类加载时执行,实例代码块在对象创建时执行,构造函数在实例代码块执行后执行。
这三种代码块按照顺序组成了对象创建和初始化的过程。
3.5 注意事项
在使用代码块时,需要注意以下几个事项:
-
代码块的执行顺序
静态代码块在类加载时执行,实例代码块在创建对象时执行,构造函数在实例代码块执行后执行。每种类型的代码块都有其特定的执行时机和顺序。
-
代码块的作用域
代码块中定义的变量只在该代码块内可见,无法在外部访问。局部代码块的作用域在代码块内部,实例代码块和静态代码块可访问类的成员变量。
-
代码块与构造函数的关系
实例代码块是在构造函数执行之前执行的,因此可以在实例代码块中进行初始化操作。而构造函数则用于完成对象的初始化。
-
静态代码块只执行一次
静态代码块在类首次加载时执行,并且只执行一次。即使创建多个对象,静态代码块也只会执行一次。
-
代码块可以用来初始化实例变量和静态变量
实例代码块可以用于初始化实例变量,静态代码块可以用于初始化静态变量。通过在代码块中赋值,可以在创建对象或访问静态变量之前进行初始化操作。
-
代码块不能单独被调用
代码块不能像方法一样单独被调用,它们只能在创建对象或类加载时由系统自动调用。