😀 准备好了吗?让我们一起步入这座Java神奇的城堡,解开每一个关键字的奥秘,成为真正的Java魔法师!
📝 Java常用关键字
访问权限修饰符
在Java 中,提供了四种访问权限控制:默认访问权限(包访问权限)、public、private、protected
default //默认,啥都不写。同一包内可见,不同包的子类不可见,未使用任何修饰符的都默认,使用对象:类,接口,变量,方法。
public //公共的,所有类可见,使用对象:类,接口,变量,方法。
protected //受保护的 对同一包内的类和所有子类(不同包子类)可见。使用对象:变量、方法。 注意:不能修饰类(外部类)。
private //私有的 在同一类内可见。使用对象:变量、方法。 注意:不能修饰类(外部类)
public > protected > default > private
| 访问范围 | private | default | protected | public |
|---|---|---|---|---|
| 同一个类 | 可以访问 | 可以访问 | 可以访问 | 可以访问 |
| 同一个包中的其他类 | 不可访问 | 可以访问 | 可以访问 | 可以访问 |
| 不同包中的子类 | 不可访问 | 不可访问 | 可以访问 | 可以访问 |
| 不同包中的非子类 | 不可访问 | 不可访问 | 不可访问 | 可以访问 |
默认访问修饰符
使用默认访问修饰符声明的变量和方法,对同一个包内的类是可见的。接口里的变量都隐式声明为 public static final,而接口里的方法默认情况下访问权限为 public。
特征:
-
也称为包级私有(package-private)
-
可以被同一个包内的任何类访问
-
不能被其他包的类访问
-
适用于只在包内使用的类、方法或变量
class test{
}
public 关键字
当一个类被声明为 public 时,它就具有了被其他包中的所有类访问的可能性,只要包中的其他类在程序中使用 import 语句引入该类,就可以访问和引用这个类。
特征:
-
最宽松的访问级别
-
可以被任何类访问
-
用于定义类的公共接口
public class Test{
public static void main(String[] arguments) {
// ...
}
}
private 关键字
private 修饰的类成员,只能被该类自身的方法访问和修改,而不能被任何其他类(包括该类的子类)访问和引用,具有最高的保护级别。
特征:
-
最严格的访问级别
-
只能在声明它的类内部访问
-
用于隐藏类的实现细节
-
常用于封装,确保类的内部状态只能通过类的方法来修改
public class Logger {
private String format;
public String getFormat() {
return this.format;
}
}
format 变量为私有变量,所以其他类不能直接得到该变量的值,
为了能够是其他类能够操作该变量,定义了public方法,getFormat(),来返回format 的值。
protected 关键字
用 protected 关键字修饰的类成员可以被三种类所访问:该类自身、与它在同一个包中的其他类以及在其他包中的该类的子类。
protected 可以修饰数据成员,构造方法,方法成员,不能修饰类(内部类除外)、接口及接口的成员变量和成员方法不能声明为 protected。
特征:
-
可以被同一个包内的任何类访问
-
可以被不同包中的子类访问
-
常用于允许子类访问父类的某些细节,同时对外部类隐藏这些细节
class AudioPlayer {
protected boolean openSpeaker(Speaker sp) {
// 实现细节
}
}
class StreamingAudioPlayer extends AudioPlayer {
protected boolean openSpeaker(Speaker sp) {
// 实现细节
}
}
子类重写父类的方法。
注意点:
-
类的访问控制符只能是默认访问权限或者public修饰,但是变量和方法则都可以修饰。
public class Test{} class Test{} -
父类中声明为 public 的方法在子类中也必须为 public。
-
父类中声明为 protected 的方法在子类中要么声明为 protected,要么声明为 public,不能声明为 private。
-
父类中声明为 private 的方法,不能够被子类继承。
-
对于一个 Java 源代码文件,如果存在 public 类的话,只能有一个 public 类,且此时源代码文件的名称必须和 public 类的名称完全相同。
如果还存在其他类,这些类在包外是不可见的。如果源代码文件没有 public 类,则源代码文件的名称可以随意命名。
简单记忆:不想让别人看的就 private,想让人看的就 public,想同一个班级/部门看的就默认,想让下一级看的就 protected
其他常用关键字
this 关键字
this 关键字在Java中用于引用当前对象,避免变量名冲突,调用类中的其他方法或构造器,以及实现方法链调用。
1、引用当前对象
在一个对象的方法或构造器中,this 指向调用该方法或构造器的对象。这样可以访问该对象的实例变量和其他方法。
如果构造方法中没有使用 this 关键字,name 指向的并不是实例变量而是参数本身。
使用了this 关键字后,
this.xxx指向的就是实例变量,而不再是参数本身了。当然如果参数名和实例变量名不相同的话,就不必使用
this关键字了 。
public class Dog {
private String name;
public Dog(String name) {
this.name = name; // 使用 this 区分参数 name 和实例变量 name
}
public void setName(String name) {
this.name = name; // 使用 this 区分参数 name 和实例变量 name
}
public String getName() {
return this.name; // 使用 this 指向当前对象的实例变量 name
}
}
2、调用当前对象的方法
在一个方法中,可以使用 this 关键字调用同一个类中的其他方法。如果没有使用的话,编译器会自动帮我们加上。
public class Dog {
private String name;
public Dog(String name) {
this.name = name;
}
public void bark() {
System.out.println("Woof!");
}
public void callBark() {
this.bark(); // 调用当前对象的 bark 方法
}
public void callBark2() {
bark(); // 编译器会自动加上this关键字
}
}
3、调用当前对象的构造器
在一个构造器中,可以使用 this 调用同一个类中的另一个构造器。这通常用于构造器的重载,以避免代码重复。
this() 必须放在构造方法的第一行,否则就报错了!public class Dog {
private String name;
private int age;
public Dog(String name) {
this(name, 0); // 调用带有两个参数的构造器
}
public Dog(String name, int age) {
this.name = name;
this.age = age;
}
}
4、作为参数在方法中传递
this 关键字可以作为参数在方法中传递,它指向的是当前类的对象。
public class Param {
void method1(Param p) {
System.out.println(p);
}
void method2() {
method1(this);
}
public static void main(String[] args) {
Param p = new Param();
System.out.println(p);
p.method2();
}
}
/**
Param@77459877
Param@77459877
**/
5、在构造函数中传递this
在某些情况下,可以在构造函数中将当前对象传递给其他类的构造函数或方法。
public class MyClass {
private String name;
public MyClass(String name) {
this.name = name;
Helper helper = new Helper(this);//this 指向的是new MyClass()这个对象
helper.printDetails();
}
public String getName() {
return name;
}
}
class Helper {
private MyClass myClass;
public Helper(MyClass myClass) {
this.myClass = myClass;
}
public void printDetails() {
System.out.println("Name: " + myClass.getName());
}
}
public class Main {
public static void main(String[] args) {
new MyClass("Alice"); // 输出: Name: Alice
}
}
6、返回当前对象
在某些设计模式中,特别是方法链(method chaining)中,this 可以用于返回当前对象,从而可以连续调用该对象的方法。
this关键字作为方法的返回值的时候,方法的返回类型为类的类型。
public class Dog {
private String name;
public Dog setName(String name) {
this.name = name;
return this; // 返回当前对象 dog
}
public Dog printName() {
System.out.println(this.name);
return this; // 返回当前对象
}
}
public class Main {
public static void main(String[] args) {
Dog dog = new Dog();
dog.setName("Buddy").printName(); // 连续调用方法,链式调用
}
}
super 关键字
super关键字主要用于引用父类的成员(变量、方法)和构造函数。它常用于实现继承和方法重写时访问父类的成员。
1. 访问父类的实例变量
当子类和父类中有同名的实例变量时,可以使用super关键字来访问父类的实例变量。
class Parent {
int x = 10;
}
class Child extends Parent {
int x = 20;
void display() {
System.out.println("Child x: " + x); // 子类的 x
System.out.println("Parent x: " + super.x); // 父类的 x
}
public static void main(String[] args) {
new Child().display();
}
}
/**
Child x: 20
Parent x: 10
**/
2. 调用父类的方法
当子类重写了父类的方法时,可以使用super关键字调用父类的版本。
class Parent {
void display() {
System.out.println("Parent display()");
}
}
class Child extends Parent {
void display() {
super.display(); // 调用父类的 display() 方法
System.out.println("Child display()");
}
public static void main(String[] args) {
new Child().display();
}
}
/**
Parent display()
Child display()
**/
3. 调用父类的构造函数
在子类的构造函数中,可以使用super关键字来调用父类的构造函数。必须在子类构造函数的第一行使用super。
class Parent {
Parent() {
System.out.println("Parent Constructor");
}
Parent(String message) {
System.out.println("Parent Constructor: " + message);
}
}
class Child extends Parent {
Child() {
super("Hello from Child"); // 调用父类的带参数构造函数
System.out.println("Child Constructor");
}
public static void main(String[] args) {
Child obj = new Child();
}
}
/**
Parent Constructor: Hello from Child
Child Constructor
**/
static 关键字
static关键字用于声明类成员(字段和方法)为类级别的,而不是实例级别的。这意味着static成员属于类本身,而不是类的实例(对象)。
1. static变量(静态变量)
静态变量,也称为类变量,是类所有实例共享的变量。它们在内存中只有一个副本,无论创建了多少个类的实例,都使用同一个静态变量。
存储位置:
static变量存储在方法区(Method Area),该区域是所有实例共享的区域。生命周期:
static变量在类加载时初始化,并在类卸载时销毁,生命周期贯穿整个应用程序的运行过程。
public class MyClass {
static int staticVar = 0; // 静态变量
public MyClass() {
staticVar++; // 每次创建实例时,staticVar递增1
}
public void display() {
System.out.println("staticVar: " + staticVar); // 打印当前的staticVar值
}
public static void main(String[] args) {
MyClass obj1 = new MyClass();
obj1.display(); // 输出: staticVar: 1
MyClass obj2 = new MyClass();
obj2.display(); // 输出: staticVar: 2
MyClass obj3 = new MyClass();
obj3.display(); // 输出: staticVar: 3
}
}
//所以输出是 1,2,3,因为同一个变量的地址没变。
如果类中 int staticVar = 0;为实例变量,那么输出就为1,1,1
static变量。2.static方法(静态方法)
-
静态方法是属于类的,而不是属于实例的。
-
它们可以在不创建类的实例的情况下调用。
-
静态方法只能访问静态变量和其他静态方法,不能访问实例变量或调用非静态方法。
java.lang.Math 类的几乎所有方法都是静态的,可以直接通过类名来调用,不需要创建类的对象。
public class MyClass {
static int staticVar = 0;
static void staticMethod() {
System.out.println("Static method called.");
System.out.println("staticVar: " + staticVar);
}
public static void main(String[] args) {
MyClass.staticMethod(); // 调用静态方法
}
}
main方法为静态方法
3.static块(静态代码块)
静态代码块用于初始化类的静态变量。它在类加载时执行一次,可以包含多个静态代码块,按它们在类中出现的顺序执行。
public class StaticBlock {
static {
System.out.println("静态代码块");
}
public static void main(String[] args) {
System.out.println("main 方法");
}
}
/**
静态代码块
main 方法
**/
4.static内部类(静态内部类)
静态内部类可以独立于外部类的实例存在。静态内部类不能访问外部类的实例变量和方法,但可以访问外部类的静态变量和方法。
public class OuterClass {
static int staticVar = 10;
static class InnerClass {
void display() {
System.out.println("Static variable from OuterClass: " + staticVar);
}
}
public static void main(String[] args) {
OuterClass.InnerClass inner = new OuterClass.InnerClass();
inner.display();
}
}
5.static导入(静态导入)
静态导入允许直接使用静态成员(方法或变量),而无需通过类名限定。在导入某些常用的静态成员时非常方便,如Math类的方法。
import static java.lang.Math.*;
public class MyClass {
public static void main(String[] args) {
double result = sqrt(16); // 直接使用 Math.sqrt() 方法
System.out.println("Square root of 16 is: " + result);
}
}
final 关键字
final关键字在Java中是一个非常重要的修饰符,可以用来修饰类、方法和变量。
1、final 变量
final 修饰的变量即成为常量,只能赋值一次。换句话说,被 final 修饰的变量无法重新赋值。
注意:final 修饰的变量不能被赋值这种说法是错误的,严格的说法是,final 修饰的变量不可被改变,一旦获得了初始值,该 final 变量的值就不能被重新赋值。
-
final 修饰的局部变量必须使用之前被赋值一次才能使用。
-
final 修饰的成员变量在声明时没有赋值的叫“空白 final 变量”。空白 final 变量必须在构造方法或静态代码块中初始化。、
final 和 static 一起修饰的成员变量叫做常量,常量名必须全部大写。如果一个程序中的变量使用 public static final 声明,则此变量将称为全局变量.
public class FinalDemo {
void doSomething() {
//局部变量
// 没有在声明的同时赋值
final int e;
// 只能赋值一次
e = 100;
System.out.print(e);
// 声明的同时赋值
final int f = 200;
}
// 实例常量
final int a = 5; // 直接赋值
final int b; // 空白final变量
// 静态常量
final static int c = 12;// 直接赋值
final static int d; // 空白final变量
// 静态代码块
static {
// 初始化静态变量
d = 32;
}
// 构造方法
FinalDemo() {
// 初始化实例变量
b = 3;
// 第二次赋值,会发生编译错误
// b = 4;
}
final 修饰基本类型变量和引用类型变量的区别
当使用 final 修饰基本类型变量时,不能对基本类型变量重新赋值,因此基本类型变量不能被改变。
但对于引用类型变量而言,它保存的仅仅是一个引用,final 只保证这个引用类型变量所引用的地址不会改变,即一直引用同一个对象,但这个对象完全可以发生改变。
import java.util.Arrays; class Person { private int age; public Person() { } // 有参数的构造器 public Person(int age) { this.age = age; } // 省略age的setter和getter方法 // age 的 setter 和 getter 方法 } public class FinalReferenceTest { public static void main(String[] args) { // final修饰数组变量,iArr是一个引用变量 final int[] iArr = { 5, 6, 12, 9 }; System.out.println(Arrays.toString(iArr)); // 对数组元素进行排序,合法 Arrays.sort(iArr); System.out.println(Arrays.toString(iArr)); // 对数组元素赋值,合法 iArr[2] = -8; System.out.println(Arrays.toString(iArr)); // 下面语句对iArr重新赋值,非法 // iArr = null; // final修饰Person变量,p是一个引用变量 final Person p = new Person(45); // 改变Person对象的age实例变量,合法 p.setAge(23); System.out.println(p.getAge()); // 下面语句对P重新赋值,非法 // p = null; } }
2、final方法
在声明类中,final 方法只被实现一次。
不能被重写:声明为final的方法不能在子类中被重写。这确保了方法的行为在继承层次结构中保持不变。
public class FinalMethodTest {
public final void test() {
}
}
class Sub extends FinalMethodTest {
// 下面方法定义将出现编译错误,不能重写final方法
public void test() {
}
}
但是,final修饰的方法可以被重载。
public class FinalOverload {
// final 修饰的方法只是不能被重写,完全可以被重载
public final void test(){}
public final void test(String arg){}
}
注意点:
对于一个 private 方法,因为它仅在当前类中可见,其子类无法访问该方法,所以子类无法重写该方法——如果子类中定义一个与父类 private 方法有相同方法名、相同形参列表、相同返回值类型的方法,也不是方法重写,只是重新定义了一个新方法。因此,即使使用 final 修饰一个 private 访问权限的方法,依然可以在其子类中定义与该方法具有相同方法名、相同形参列表、相同返回值类型的方法。
public class PrivateFinalMethodTest { private final void test() { } } class Sub extends PrivateFinalMethodTest { // 下面的方法定义不会出现问题 public void test() { } } 这样的写法相当于子类重新定义了一个新方法,并不是重写父类的方法。
3、final类
**final 修饰的类不可被继承。**当某个类被final修饰时,意味着此类无后代,不需要进行修改或扩展。
final class SuperClass {
}
class SubClass extends SuperClass { //编译错误
}
4.、final参数
不可修改:final参数在方法内部不能被修改。这可以防止方法中的参数被意外改变。
public class MyClass {
void display(final int param) {
System.out.println("Parameter: " + param);
// 以下代码会导致编译错误,因为 final 参数不能被修改
// param = 10;
}
public static void main(String[] args) {
MyClass obj = new MyClass();
obj.display(5);
}
}
instanceof 关键字
instanceof 关键字用于测试一个对象是否是一个特定类的实例,或者是这个类的子类的实例。它返回一个布尔值,表示测试结果。instanceof 通常用于类型检查,以确保安全地进行类型转换和操作。
(object) instanceof (type)
//基础使用
class Animal {}
class Dog extends Animal {}
public class Main {
public static void main(String[] args) {
Animal myAnimal = new Animal();
Dog myDog = new Dog();
System.out.println(myAnimal instanceof Animal); // 输出: true
System.out.println(myDog instanceof Dog); // 输出: true
System.out.println(myDog instanceof Animal); // 输出: true
System.out.println(myAnimal instanceof Dog); // 输出: false
}
}
检查接口实现
instanceof也可以用于检查对象是否实现了某个接口。
interface Pet {}
class Dog implements Pet {}
class Cat {}
public class Main {
public static void main(String[] args) {
Dog myDog = new Dog();
Cat myCat = new Cat();
System.out.println(myDog instanceof Pet); // 输出: true
System.out.println(myCat instanceof Pet); // 输出: false
}
}
null 值检查
instanceof对null值进行检查时,总是返回false。只有对象才会有 null 值,但是又因为所有的对象都可以为 null,所以也不好确定 null 到底属于哪一个类。
public class Main {
public static void main(String[] args) {
Animal myAnimal = null;
System.out.println(myAnimal instanceof Animal); // 输出: false
}
}
使用示例:
// 先判断类型
if (obj instanceof String) {
// 然后强制转换
String s = (String) obj;
// 然后才能使用
}
//或者这种
if (obj instanceof String s) {
// 如果类型匹配 直接使用 s
}
//示例
class Animal {}
class Dog extends Animal {
void bark() {
System.out.println("Woof!");
}
}
public class Main {
public static void main(String[] args) {
Animal myAnimal = new Dog(); // 多态
if (myAnimal instanceof Dog) {
Dog myDog = (Dog) myAnimal; // 安全转换
myDog.bark(); // 输出: Woof!
} else {
System.out.println("The object is not an instance of Dog.");
}
}
}