Java面向对象与权限修饰符、final关键字、代码块及内部类¶
约 2052 个字 368 行代码 预计阅读时间 11 分钟
权限修饰符¶
在Java中,一共有4种访问修饰符,分别是public
、protected
、default
和private
,下面是访问权限表:
public | protected | default | private | |
---|---|---|---|---|
同类 | 可以 | 可以 | 可以 | 可以 |
同包不同类 | 可以 | 可以 | 可以 | 不可以 |
不同包父子类 | 可以 | 可以 | 不可以 | 不可以 |
不同包且非父子类 | 可以 | 不可以 | 不可以 | 不可以 |
从上表可以看出来,public
具有最大访问权限,private
具有最小访问权限
在实际开发中,一般建议按照下面的方式使用权限修饰符(除非有特殊要求):
- 成员变量(属性):
private
修饰(封装思想) - 成员方法:
public
修饰(便于调用) - 构造方法:
public
修饰(便于创建对象)
需要注意的是,如果父类成员是被protected
修饰,并且子类和父类并不是同包的,那么只有子类对象可以直接访问,其他类对象的类必须与父类同包才可以让该类对象直接访问,例如:
Java | |
---|---|
1 2 3 4 5 |
|
Java | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
final
关键字¶
在Java中,final
关键字一般表示「最后一个」,具体到指定成员时表现出不同的效果:
final
修饰类:被final
修饰的类无法被继承,理解为「最后一个类」,例如public final class A{}
final
修饰成员方法:被final
修饰的方法无法被重写,理解为「最后一个方法」,例如public final void method(){}
final
修饰局部变量:被final
修饰的局部变量无法被二次赋值(只要是第一次赋值都允许),理解为「最后一个局部变量赋值」,例如final int a = 1;
final
修饰对象引用:被final
修饰的对象引用一旦指向了一个对象,将无法指向其他对象,理解为「最后一次对象引用」,例如final A a = new A();
final
修饰成员变量:被final
修饰的成员变量必须给定初始值,并且无法通过赋值行为再次为成员变量赋值,例如final String name = "zhangsan";
注意:
- 因为
final
修饰类后对应类后该类不可以被继承,修饰成员方法后该方法不可以被重写,所以final
不可以与abstract
一起使用,因为final
关键字要求不被继承或重写,但是abstract
要求继承且重写对应方法 final
修饰局部变量时表示不能二次赋值,但不代表变量未初始化时不可以赋值,例如下面的代码:
Java | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 |
|
代码块¶
在Java中,代码块分为静态代码块和非静态代码块(也称实例代码块)
非静态代码块¶
不使用static
修饰的代码块,写法如下:
Java | |
---|---|
1 2 3 |
|
非静态代码块在对象中时,会优先于构造函数执行,并且有多少个对象就执行几次,例如下面的代码:
Java | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 |
|
Java | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
需要注意,如果成员变量赋予了初始值并且该成员变量声明在非静态代码块之后,那么此时成员变量的值就是初始值而不是非静态代码块中赋予的值,否则就是非静态代码块中赋予的值:
Java | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
在上面代码中,num
值为10而非20,除非int num = 10;
在非静态代码块之前
静态代码块¶
使用static
修饰的代码块即为静态代码块,写法如下:
Java | |
---|---|
1 2 3 |
|
静态代码块与非静态代码块一样,在类中时,创建对象会优先于构造方法和非静态代码块执行,但是静态代码块只会在第一次创建对象时执行,例如下面的代码:
Java | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 |
|
Java | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 |
|
代码块与继承¶
前面提到,代码块分为静态代码块和非静态代码块,而静态代码块优先于非静态代码块并且只执行一次,非静态代码块优先于构造方法执行,那么如果现在父类和子类都有静态代码块、非静态代码块和构造方法,具体的执行顺序应该为:
- 父类静态代码块
- 子类静态代码块
- 父类非静态代码块
- 父类构造方法
- 子类非静态代码块
- 子类构造方法
例如下面的代码:
Java | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
Java | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
Java | |
---|---|
1 2 3 4 5 |
|
输出结果:
Text Only | |
---|---|
1 2 3 4 5 6 |
|
内部类¶
在Java中,内部类分为以下四种:
- 非静态成员内部类(也称实例内部类)
- 静态成员内部类
- 局部成员内部类
- 匿名成员内部类
非静态成员内部类¶
当成员内部类没有被static
修饰时,称为非静态成员内部类,写法如下:
Java | |
---|---|
1 2 3 |
|
非静态成员内部类有以下的特点:
- 可以被
final
或abstract
修饰 - 内部类成员与普通类基本一致
- 可以访问外部类的成员
- 外部类如果想访问内部类非私有成员需要通过创建内部类对象才可以访问非私有成员
- 当内部类中的方法存在局部变量、内部类成员变量与外部类成员变量同名时,优先访问局部变量,此时需要访问内部类成员变量,需要使用
this.成员变量
,如果需要访问外部类的成员变量,需要使用外部类.this.外部类成员变量
Note
注意,在JDK16之前,内部类成员(包括成员变量和成员方法)不可以是静态的,如果想要修改当前模块的JDK可以按照下面的方式进行:
File
->Project Structure
->Modules
->选择需要改变JDK的模块->在Source
页面改变Language Level
为17
->在Dependencies
页面改变Module SDK
(SDK即JDK)为17
->OK
需要修改回JDK8重复上面的步骤,选择JDK8即可
改变项目JDK版本只需要在选择Module
的位置选择Project
即可
当需要创建非静态成员内部类对象时,按照下面的方法创建:
Java | |
---|---|
1 |
|
例如下面的代码:
Java | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
Java | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
静态成员内部类¶
静态成员内部类和非静态成员内部类基本一致,不同的是:静态内部类不可以访问外部类中的非静态成员,创建静态成员内部类方式如下:
Java | |
---|---|
1 2 3 |
|
创建静态成员内部类对象方式如下:
Java | |
---|---|
1 |
|
例如下面的代码:
Java | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
Java | |
---|---|
1 2 3 4 5 6 7 8 9 |
|
局部内部类¶
定义与基本使用¶
在Java中,局部内部类可以理解为局部变量,定义在方法中或者代码块中,定义方式如下:
Java | |
---|---|
1 2 3 4 5 |
|
Note
注意:局部内部类创建对象只能在内部类所在的外部类创建
例如下面的代码:
Java | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
Java | |
---|---|
1 2 3 4 5 6 7 8 9 |
|
接口类型/抽象类型作为方法参数传递和返回¶
Note
下面的代码以接口类型为例,抽象类型基本一致
-
接口/抽象类型作为方法参数传递时,传递的是对应实现类的对象
Java 1 2 3 4
// 接口 public interface Test_interface { void show(); }
Java 1 2 3 4 5 6 7
// 实现类 public class Test_interfaceImpl implements Test_interface{ @Override public void show() { System.out.println("Test_interface接口实现类"); } }
Java 1 2 3 4 5 6 7 8 9 10 11
// 测试 public class Test { public static void main(String[] args) { Test_interfaceImpl testInterface = new Test_interfaceImpl(); method(testInterface); // 传递的是实现类的对象 } public static void method(Test_interface ti){ ti.show(); // 多态,向上转型 } }
-
接口/抽象类型作为方法参数返回时,返回的是对应实现类的对象
Java 1 2 3 4
// 接口 public interface Test_interface { void show(); }
Java 1 2 3 4 5 6 7
// 实现类 public class Test_interfaceImpl implements Test_interface{ @Override public void show() { System.out.println("Test_interface接口实现类"); } }
Java 1 2 3 4 5 6 7 8 9 10
// 测试 public class Test { public static void main(String[] args) { Test_interface testInterface = getTestInterface(); } public static Test_interface getTestInterface(){ return new Test_interfaceImpl(); } }
局部类实现如下:
Java | |
---|---|
1 2 3 4 |
|
Java | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
匿名内部类¶
定义匿名内部类¶
匿名内部类,即没有类名的内部类,可以替换前面的接口/抽象实现类,此时创建出的内部类即为对应接口/抽象类的实现类,创建方式如下:
Java | |
---|---|
1 2 3 |
|
常见的使用方式有:
Java | |
---|---|
1 2 3 4 5 6 7 8 9 10 |
|
匿名内部类基本使用¶
Java | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
|
匿名内部类修改接口/抽象类型作为方法参数传递¶
Java | |
---|---|
1 2 3 4 |
|
Java | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
匿名内部类修改接口/抽象类型作为返回值返回¶
Java | |
---|---|
1 2 3 4 |
|
Java | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|