认识类与对象(下)
目录
一.包
1.导入包中的类
2.自定义包
二.static关键字
1.static修饰成员变量
2.static修饰方法
三.代码块
1.局部代码块
2.构造代码块
2.静态代码块
一.包
包就是一组类的组合,用于组织和管理类和接口等代码文件,帮助开发者更好地组织代码,还防止了类名冲突,并提高了代码的可读性和可维护性。
1.导入包中的类
导入包中的类就是java提供的现成的类提供给我们使用,需要我们使用import关键字。导入特定类或整个包。
比如说:
- java.lang:包含基础类,如
String
、Math
、System
等,默认自动导入。- java.util:包含实用工具类和集合框架,如
ArrayList
、HashMap
、Arrays
。- java.io:包含输入/输出类,如
File
、InputStream
、OutputStream
。- java.nio:提供非阻塞I/O操作的类。
- java.net:支持网络编程的类,如
Socket
、URL
。- java.awt和javax.swing:用于图形用户界面(GUI)编程。
- 等等
举个例子:
输出结果为:
代码解释:
import java.util.Arrays; :这行代码导入了
java.util
包中的Arrays
类,使我们可以直接使用Arrays
类中的各种静态方法,比如sort
和toString
,来操作数组。Arrays.sort(arr); :
Arrays.sort(arr);
是调用Arrays
类中的静态方法sort
来对数组arr
进行排序,该方法使用的是快速排序算法(在大多数情况下)对数组进行排序。System.out.println(arr); :输出的是数组对象的内存地址或哈希码,而不是数组内容。这是因为
println
直接打印数组引用(对象),而不是它的值。输出类似于[I@1a2b3c4d
,其中[I
表示这是一个整型数组。System.out.println(Arrays.toString(arr)); :
Arrays.toString(arr);
将数组arr
转换为可读的字符串格式。Arrays.toString
方法将数组的内容以[元素1, 元素2, ...]
的形式转换成字符串。
在Java中,如果要使用其他包中的类或方法,通常需要在类名前添加它们所在的包名作为前缀。
具体情况:
1.显式导入类:通过import
语句导入一个类后,可以直接使用该类的名称,而不必加上包名前缀。
import java.util.Arrays; // 导入Arrays类
...
Arrays.sort(arr); // 直接使用Arrays类
2.完整路径(不导入类时):如果没有使用import
语句,那么每次调用这个类的方法时都需要写上完整的包路径。
// 没有import java.util.Arrays;
java.util.Arrays.sort(arr); // 需要写完整的路径
3.导入整个包(使用*
通配符):使用import java.util.*;
可以导入java.util
包下的所有类,之后可以直接使用包中任何类的名称(如Arrays
、ArrayList
等)。
import java.util.*; // 导入java.util包中的所有类
...
Arrays.sort(arr); // 直接使用Arrays类
PS:不过我还是推荐显式导入包,不推荐导入整个包。有些包他们之间有着相同的方法名但是功能是不同的,当我们通过导入整个包来导入他们,这样就有可能导致冲突,因为编译器都不知道你到底是想用哪一个包的方法,执行程序的时候就可能会达不到想要的效果或者编译错误。
当然也有特殊情况不需要在类名前添加它们所在的包名作为前缀。就是使用import static导入包中静态的方法和字段。
如:
2.自定义包
自定义包是由开发者自行定义的包,用于将相关的类和接口按功能进行分组。例如,一个电商应用可以分为用户管理、订单处理和产品管理等不同的功能模块,每个模块可以作为一个独立的包。
示例:
com.myecommerce.user // 用户相关功能的包
com.myecommerce.order // 订单处理功能的包
com.myecommerce.product // 产品管理功能的包
如何创建自定义包?
当然我们在创建包的时候得知道一些规则:
1.在类的开头使用
package
关键字声明包名。2.包名通常采用小写字母和公司域名的反写形式
3.包名要和代码路径相匹配. 例如创建 com.bit.demo1 的包, 那么会存在一个对应的路径 com/bit/demo1 来存储 代码.
4.如果一个类没有 package 语句, 则该类被放到一个默认包中(默认包没有名字,位于项目的根目录下(即不在任何包声明之内)。在这种情况下,类的文件顶部没有
package
语句。).
创建步骤:
(图片中的Person类和Test不用理会)
假设我们要创建一个名为com.example.utils
的包,并在其中定义一个名为Helper
的类
package com.example.utils;
:这行代码声明了Helper
类属于com.example.utils
包。同时我们也在该类中写了一个public类型修饰的方法printHello。
ok,创建完之后如果大家的包是平放的,如图所示:
教一下大家如何分级:
效果如图:
创建完之后我们来测试一下,我们在example上面创建一个类(右击example,点new,点Java class,取名为Test):
然后在该类中导入自定义包中的Helper类,并且调用Helper中的方法printHello
输出结果为:
如果我们把类Helper中的printHello的访问修饰符从public修改为protected或者private,在Test类中就会出现报错,尽管你已经导入Helper类:
这里再次给出访问限制符供大家参考:
No | 范围 | private | default | protected | public |
1 | 同一包中的同一类 | √ | √ | √ | √ |
2 | 同一包中的不同类 | √ | √ | √ | |
3 | 不同包中的子类 | √ | √ | ||
4 | 不同包中的非子类 | √ |
PS(重点):
在 Java 中,位于
com.example.utils
包中的类与位于com.example
包中的类属于不同的包。这是因为 Java 的包结构是层级化的,每个包都有其独立的命名空间。
包结构的具体说明
命名空间:
com.example
是一个包,包含在该包中的所有类都可以通过com.example
包名访问。com.example.utils
是一个子包,它属于com.example
包,所有在该包中的类可以通过com.example.utils
包名访问。
不同包的类:
位于 com.example.utils
中的类,比如 Utility,
位于 com.example
中的类,比如 Example。
包之间的关系
虽然 com.example.utils
是 com.example
的子包,但它们是两个不同的包。类 Utility
在 com.example.utils
中,而类 Example
在 com.example
中。
二.static关键字
1.static修饰成员变量
在类中用
static
关键字修饰的变量。这种变量属于类本身,而不是某个特定的实例。
特点:
共享性:所有该类的实例共享同一个静态变量。无论创建多少个对象,静态变量的存储只有一份。
生命周期:静态变量在类加载时被初始化,并在 JVM 关闭时被销毁。它的生命周期与类的生命周期相同。
访问方式:可以通过类名直接访问静态变量,无需创建类的实例。当然也可以通过对象来访问,但是更推荐使用类名来访问。例如:
ClassName.staticVariable
。初始化:静态变量可以在声明时初始化,也可以通过静态块进行初始化。静态块会在类加载时执行,仅执行一次。
举个例子:
public class Counter {
static int count = 0; // 静态变量
public Counter() {
count++; // 每次创建实例时增加计数
}
public static void displayCount() {
System.out.println("Count: " + count);
}
}
public class Test {
public static void main(String[] args) {
Counter c1 = new Counter(); // 创建第一个实例
Counter c2 = new Counter(); // 创建第二个实例
Counter c3 = new Counter(); // 创建第三个实例
Counter.displayCount(); // 输出: Count: 3
}
}
可以看出来,count是共享的,每实例化一个对象出来,count都会++。
访问静态变量:
通过实例访问:虽然可以通过实例访问静态变量,但不推荐这样做,因为这可能会导致混淆。示例如下:
Counter c1 = new Counter();
System.out.println(c1.count); // 可以访问,但不推荐
通过类名访问:这是推荐的方式,更清晰明了:
System.out.println(Counter.count); // 正确且推荐的方式
静态变量的初始化:
1.默认初始化(不初始化)
public class Example {
static int num;
static boolean flag;
static String str;
public static void main(String[] args) {
System.out.println(num);
System.out.println(flag);
System.out.println(str);
;
}
}
结果为:
2.显示初始化
public class Example {
static int age=10;
static String name="zhangsan";
public static void main(String[] args) {
System.out.println(age);
System.out.println(name);
}
}
结果为;
3.静态块初始化(后面讲)
public class Example {
static int staticVar = 10; // 显示初始化
static {
staticVar = 20; // 在静态块中重新初始化
System.out.println("Static block executed, staticVar: " + staticVar);
}
public static void main(String[] args) {
}
}
2.static修饰方法
在类中用
static
关键字修饰的方法。静态方法可以在没有创建类的实例的情况下被调用。
特点:
不依赖于实例:静态方法不依赖于类的实例,因此可以直接通过类名调用,而无需实例化对象,例如
ClassName.methodName()
。也可以通过实例对象调用,但不推荐这种方式,因为它可能会导致混淆。共享性:静态方法在内存中只有一份,所有实例共享这一份。
访问限制:静态方法只能直接访问其他静态变量和静态方法,不能直接访问实例变量和实例方法。这是因为静态方法在调用时不依赖于特定对象的状态。
生命周期:静态方法在类加载时加载,并在类卸载时销毁。其生命周期与类的生命周期相同。
举个例子:
class MathUtils {
// 静态方法,计算两个数的最大值
public static int max(int a, int b) {
return (a > b) ? a : b;
}
}
public class Main {
public static void main(String[] args) {
// 静态方法通过类名调用
int result = MathUtils.max(10, 20);
System.out.println("Max: " + result); // 输出:Max: 20
}
}
可以看出来,静态方法可以直接通过类名直接访问。
静态方法的限制(具体一点的例子):
class Example1 {
private int nonStaticVar = 10; // 非静态成员变量
private static int staticVar = 20; // 静态成员变量
// 非静态方法
public void nonStaticMethod() {
System.out.println("This is a non-static method.");
}
// 静态方法
public static void staticMethod() {
// System.out.println(nonStaticVar); // 错误,不能在静态方法中访问非静态变量
// nonStaticMethod(); // 错误,不能在静态方法中调用非静态方法
System.out.println(staticVar); // 可以访问静态变量
}
public static void main(String[] args) {
// 直接调用静态方法
staticMethod();
// 要访问非静态成员,必须先创建对象
Example1 example = new Example1();
System.out.println(example.nonStaticVar); // 访问非静态变量
example.nonStaticMethod(); // 调用非静态方法
}
}
三.代码块
1.局部代码块
局部代码块的语法是使用花括号
{}
包裹的代码,它在方法内部出现。
特点:
- 作用范围:局部代码块的作用范围仅限于方法内部。当方法执行结束或代码块外的作用域结束时,局部代码块中的变量也会被销毁。
- 生命周期:局部代码块的生命周期仅在方法调用过程中。方法每次调用时局部代码块都会重新执行。
- 独立性:局部代码块通常用于缩小变量的作用域或进行流程控制,不涉及类级别的初始化或对象创建的初始化。
举个例子:
public class Test {
public void exampleMethod() {
System.out.println("Method start");
// 局部代码块
{
int localVariable = 5;
System.out.println("方法内部 localVariable = " + localVariable);
}
// 这里无法访问 localVariable,因为它的作用范围仅限于局部代码块内部
// System.out.println(localVariable); // 编译错误
}
public static void main(String[] args) {
Test test = new Test();
test.exampleMethod();
}
}
输出为:
2.构造代码块
构造代码块(也称为实例代码块)是使用花括号
{}
包裹的一段代码,它直接写在类中,而不是在方法或构造方法中。
特点:
- 复用性:如果所有构造方法中需要执行相同的初始化代码,可以将这些代码放入构造代码块中,避免在每个构造方法中重复写相同的代码。
- 访问实例变量:构造代码块可以访问实例变量,因为它在对象创建时执行。
普通代码块的用途:
实例变量的初始化:可以在构造代码块中对实例变量进行一些复杂的初始化操作。
通用代码的复用:如果多个构造方法中需要执行相同的初始化代码,可以将这些代码写在构造代码块中,避免在每个构造方法中重复写相同的代码。
执行时机:
- 构造代码块在每次创建对象(实例化)时都会执行。
- 它的执行优先级比构造方法高,但在构造方法之前执行。
- 每次创建一个新的对象时,构造代码块都会被执行一次,即使构造方法不同。
举个例子:
class Example {
private int x;
// 构造代码块,用于初始化实例变量 x
{
x = 10;
System.out.println("构造代码块输出 x = " + x);
}
// 第一个构造方法
public Example() {
System.out.println("构造方法输出");
}
// 第二个构造方法
public Example(int x) {
this.x = x;
System.out.println("构造方法2输出 x = " + this.x);
}
}
public class Main {
public static void main(String[] args) {
Example example1 = new Example(); // 输出: 构造方法输出
Example example2 = new Example(20); // 输出: 构造方法2输出 x = 20
}
}
输出结果为:
代码解释:
- 当创建
Example
类的实例时,普通代码块会首先执行,将x
初始化为 10。- 然后,构造方法才会被调用。
- 如果传入了参数,则构造方法会覆盖普通代码块对
x
的初始化。
2.静态代码块
静态代码块(Static Block)是用
static {}
包裹的一段代码。静态代码块在类加载时执行,只执行一次,通常用于静态变量的初始化或执行与类相关的操作。'
特点:
- 与类关联:静态代码块与类关联,在 JVM 加载类时执行,而不是在创建实例时执行。
- 只执行一次:静态代码块在类加载时执行一次,并且整个程序运行期间不会再次执行。
- 用于静态变量的初始化:可以在静态代码块中初始化静态变量,或者执行需要在类加载时完成的操作。
静态代码块的用途:
- 静态变量初始化:静态代码块常用于静态变量的初始化,特别是当初始化需要执行复杂逻辑时。
- 执行与类相关的静态操作:在静态代码块中可以执行与类相关的静态操作,比如数据库连接、加载配置文件等。
执行时机:
- 类加载时执行:静态代码块在类加载到内存时执行,仅执行一次。
- 优先于对象实例化:静态代码块的执行优先级比构造方法和实例代码块更高,因此在任何对象实例化之前执行。
举个例子:
public class Test {
static int staticVariable;
// 静态代码块,用于初始化静态变量
static {
staticVariable = 10;
System.out.println("静态代码块执行 staticVariable = " + staticVariable);
}
// 构造方法
public Test() {
System.out.println("构造方法执行");
}
public static void main(String[] args) {
System.out.println("主函数执行");
Test example1 = new Test(); // 创建第一个对象
Test example2 = new Test(); // 创建第二个对象
}
}
输出结果为:
代码解释:
- 静态代码块在类加载时执行一次,初始化
staticVariable
。- 之后,
main
方法开始执行,两个对象的构造方法分别被调用。- 无论创建多少个
Example
对象,静态代码块都只执行一次。