JavaSE4️⃣OOP - 包 & 嵌套类
1、包
1.1、package
1.1.1、概念
包(package)
提供多层命名空间,方便管理组织 Java 文件。
-
作用:
- 防止不同 Java 文件之间产生命名冲突。
- 访问控制。
- 将功能相似或相关的文件组织在一起,方便使用。
-
语法:使用
package
语句,放在源文件的首行。-
适用于所有类型(类、接口、枚举、注释)。
-
单个源文件只能包含一个 package 语句。
package 包层级1[.包层级2.包层级3...];
-
1.1.2、命名
要求
-
全部为小写字母。
-
可以是多层次结构,使用
.
分割。package indi.jaywee;
规范:为避免冲突,需要确定唯一的包名。
-
不以
java
开头,避免冲突。 -
通常由倒置域名开头,根据功能命名子包。
package com.baidu; package com.baidu.utils;
1.1.3、注意
-
默认包:若没有定义包,则会存放在默认包中。
-
层次关系:
-
包没有父子关系,不同的包层次之间独立。
-
要求包层次与文件层次一致(源文件、字节码文件)。
src ├─ indi │ └─ secret │ └─ Person.java └─ com └─ baidu └─ Person.java bin ├─ indi │ └─ secret │ └─ Person.class └─ com └─ baidu └─ Person.class
-
1.2、类名
1.2.1、分类
- 简单类名:定义的
class
名。 - 全限类名:
包名.简单类名
。- JVM 使用全限类名。
- 类加载器+全限类名才能唯一确定一个类。
1.2.2、类的查找过程
编译器针对类名的检索步骤。
- 全限类名:直接根据类名查找。
- 简单类名:按以下顺序
- 查找当前 package 中是否存在。
- 查找 import 导入的包中是否包含。
- 查找 java.lang 中是否包含。
- 均不存在,则编译报错。
1.3、import
1.3.1、跨包使用类
一个类通常会使用其它类。
当使用其它包的类的时候,无法直接使用简单类名。
-
全限类名:
包名.类名 变量名 = new 包名.类名(参数);
-
导包 + 简单类名:使用
import
导入指定包层次下的类。-
位置:package 之后,类定义之前。
-
通配符
*
:表示包下的所有类,但不包括子包下的类。package 当前包名; import 包名.类名; // 指定的类 import 包名.*; // 包下的所有类 class 当前类名{}
-
单个源文件只能包含一个
package
语句,但可包含多个
import
语句。
1.3.2、特殊导入
-
默认导入:Java 为每个源文件默认导入以下类。
- 当前包的其它类。
java.lang.*
。
-
静态导入(Java 5):导入某个类的静态结构,引用静态结构无需指定类名。
import static java.lang.System.*;
1.3.3、失效情况
场景:同一个源文件导入了多个同名的类,无法使用简单类名。
此时必须使用全限类名,否则编译不通过。
示例:
-
程序中导入两个包,其中都有
Date
类。import java.util.*; import java.sql.*;
-
指定要使用的类。
Date d = new Date(); // 编译不通过 java.util.Date d1 = new java.uti.Date(); //ok java.sql.Date d2 = new java.sql.Date(); //ok
1.4、系统类库
常见的 Java 内置类库,位于
java.
下。
包 | 含义 | 举例 |
---|---|---|
java.lang | 核心类库 | 基本数据类型、基本数学函数、 字符串处理、异常处理、线程类 |
java.lang.reflect | 反射对象 | |
java.io | 标准输入输出 | 基本输入/输出流、文件输入/输出、过滤输入/输出流 |
java.util | 工具类 | 日期时间、集合框架 |
java.util.zip | 文件压缩 | |
java.awt | GUI 相关 | 低级绘图操作、图形界面组件、布局管理,用户界面交互控制、事件响应 |
java.net | 网络功能 | Socket、ServerSocket |
2、嵌套类
Nested Class(嵌套类)
在类中定义(使用)的类。
内部类 (Inner Class) |
匿名类 (Anonymous Class) |
静态内部类 (Static Inner Class) |
|
---|---|---|---|
含义 | 定义在另一个类的内部 | 无需明确定义,可直接实例化 | 使用 static 修饰的内部类 |
实例化方式 | 通过外部类实例 new |
直接 new ,实现抽象方法 |
直接 new |
依附于外部类实例 | ✔ | ✔ | ❌ |
可引用外部类实例 | ✔ | ✔ | ❌ |
可访问外部类 private 结构 | ✔ | ✔ | 仅静态结构 |
2.1、内部类
2.1.1、定义
Inner Class:定义在另一个类(外部类)的内部。
-
定义:
class 外部类 { class 内部类 { } }
-
实例化:通过外部类实例的
new
。// 先外后内 外部类 outer = new 外部类(参数); outer.new 内部类(参数); // 一步到位 new 外部类(参数).new 内部类(参数);
2.1.2、特点
实例不能单独存在,依附于外部类的实例。
体现为两个方面:
- 实例引用:即可引用当前实例,也可引用外部类实例。
- 内部类实例:
this.
- 外部类实例:
外部类名.this.
- 内部类实例:
- 私有结构访问:可访问外部类的
private
结构。
字节码文件:
- 外部类:编译为
外部类名.class
- 内部类:编译为
外部类名$内部类名.class
2.1.2、示例
-
定义外部类和内部类:
public class Outer { private String name; public Outer(String name) { this.name = name; } // 内部类 class Inner { private int age; Inner(int age) { this.age = age; } void show() { System.out.println(Outer.this.name + this.age); } } }
-
实例化:先实例化 Outer,再通过 outer 实例化 Innter。
public static void main(String[] args) { // 实例化外部类 Outer outer = new Outer("Nested"); // 实例化内部类 Outer.Inner inner = outer.new Inner(); inner.hello(); }
2.2、匿名类
2.2.1、定义
Anonymous Class:无需明确定义一个 class,可直接实例化。
定义(实例化):通过 new
直接创建。
new 类型() {
// 实现抽象方法
}
2.2.2、特点
类似 Inner Class(依附于外部类的实例)
- 实例引用:可引用当前实例,也可以引用外部类实例。
- 私有结构访问:可访问外部类的 private 结构。
字节码文件:
- 外部类:编译为
外部类名.class
- 匿名类:编译为
外部类名$1.class
(若有多个匿名类,数字依次增加)
2.2.2、示例
Runnable 是一个接口,定义匿名类并使用。
-
定义匿名类,实现接口的抽象方法。
-
通过
new
实例化该匿名类。 -
转型为
Runnable
类型。class Outer { private String name; public void showName() { Runnable r = new Runnable() { @Override public void run() { System.out.println(Outer.this.name); } }; new Thread(r).start(); } }
2.3、静态内部类
2.3.1、定义
Static Inner Class:使用
static
修饰。
-
定义:
class 外部类 { static class 匿名内部类 { } }
-
实例化:通过
new
直接创建。new 外部类.内部类(参数);
2.1.2、特点
具有独立性,与外部类实例无关。
因此跟 Inner Class 的特点不同。
- 实例引用:可引用当前实例,无法引用外部类的实例。
- 私有结构访问:仅可访问外部类的私有静态结构。
2.3.2、示例
-
声明类:
public class StaticOuter { private static String name; // 静态内部类 static class StaticInner { private int age; StaticInner(int age) { this.age = age; } void show() { // 访问外部类的静态属性 System.out.println(name + this.age); } } }
-
使用:
new StaticOuter.StaticInner(18);