java面向对象学习--包

为什么要有包这个东西

加入我写了一个Array,但是java的jdk也有一个Array,那这个时候该怎么办呢,名字冲突了啊,包就是来解决这个问题的,每个类其实都是属于某一个包的
Java定义了一种名字空间,称之为包:package。一个类总是属于某个包,类名(比如Person)只是一个简写,真正的完整类名是包名.类名。
在Java虚拟机执行的时候,JVM只看完整类名,因此,只要包名不同,类就不同。
包是没有什么继承关系的,比如java.util和java.util.zip是不同的包,两者没有任何继承关系。
包可以是多层结构,用.隔开。例如:java.util。

小军的Arrays类存放在包mr.jun下面,因此,完整类名是mr.jun.Arrays;

JDK的Arrays类存放在包java.util下面,因此,完整类名是java.util.Arrays。

package mr.jun; // 申明包名mr.jun
public class Arrays {

}

可以理解为,包就是一个文件夹

我们还需要按照包结构把上面的Java文件组织起来。假设以package_sample作为根目录,src作为源码目录,那么所有文件结构就是:

package_sample
└─ src
    ├─ hong
    │  └─ Person.java
    │  ming
    │  └─ Person.java
    └─ mr
       └─ jun
          └─ Arrays.java
hong就是一个包,ming也是一个包

包的作用域

先看代码

package hello;

public class Person {
    // 包作用域:
    void hello() {
        System.out.println("Hello!");
    }
}
package hello;

public class Main {
    public static void main(String[] args) {
        Person p = new Person();
        p.hello(); // 可以调用,因为Main和Person在同一个包
    }
}

之所以可以调用可以这样理解:就是hello这个方法没有任何的权限修饰,所以只要是在同一个包里的就可以使用,Person类和Main类都是在hello这个包里的,所以都可以访问hello方法
那什么时候不可以用呢

package hello; // 它住在 hello 这个包(文件夹)里

public class Person {
    void hello() { // 注意:这里前面什么都没写!既没有 public,也没有 private
        System.out.println("Hello!");
    }
}
package world; // 这个类住在 world 包里,是“外人”

import hello.Person;

public class Other {
    void test() {
        Person p = new Person();
        // p.hello(); // ❌ 这里会报错!
    }
}

这里就会报错,这个Other类不是在hello类里的,包就是不同的包,不会存在什么继承的关系什么的

import

在一个class中,我们总会引用其他的class。例如,小明的ming.Person类,如果要引用小军的mr.jun.Arrays类,他有三种写法:
1.
// Person.java
package ming;

public class Person {
    public void run() {
        // 写完整类名: mr.jun.Arrays
        mr.jun.Arrays arrays = new mr.jun.Arrays();
    }
}
2.
// Person.java
package ming;

// 导入完整类名:
import mr.jun.Arrays;

public class Person {
    public void run() {
        // 写简单类名: Arrays
        Arrays arrays = new Arrays();
    }
}
//关于这第二种写法,有个地方要注意下,就是你把这个mr.jun.Arrays的包给improt进来之后,你还是没法访问mr.jun.Arrays中的没有任何访问权限的方法的,不是一个包就是无法访问不同包的
//无访问权限的方法的
3.
在写import的时候,可以使用*,表示把这个包下面的所有class都导入进来(但不包括子包的class):
//包之间没有什么继承结构,为什么还会有子包这种说法呢,其实是这样的,可以把包理解为文件夹
/*子包”这个词,其实是在描述硬盘上的文件夹结构。
package com.hong; 对应文件夹 com/hong/
package com.hong.math; 对应文件夹 com/hong/math/
因为 math 文件夹在 hong 文件夹里面,所以我们习惯称 com.hong.math 是 com.hong 的子包。
这只是物理上的嵌套而已*/
// Person.java
package ming;

// 导入mr.jun包的所有class:
import mr.jun.*;

public class Person {
    public void run() {
        Arrays arrays = new Arrays();
    }
}
//但是一般不建议这样使用,没法清楚的知道你使用的这个工具是哪一个包的

import static

可以导入一个类的静态字段和静态方法

package main;

// 导入System类的所有静态字段和静态方法:
import static java.lang.System.*;

public class Main {
    public static void main(String[] args) {
        // 相当于调用System.out.println(…)
        out.println("Hello, world!");
    }
}

在java编译中关于包的知识

Java编译器最终编译出的.class文件只使用完整类名,因此,在代码中,当编译器遇到一个class名称时:

如果是完整类名,就直接根据完整类名查找这个class;
如果是简单类名,按下面的顺序依次查找:
查找当前package是否存在这个class;
查找import的包是否包含这个class;
查找java.lang包是否包含这个class。

// Main.java
package test;

import java.text.Format;

public class Main {
    public static void main(String[] args) {
        java.util.List list; // ok,使用完整类名 -> java.util.List
        Format format = null; // ok,使用import的类 -> java.text.Format
        String s = "hi"; // ok,使用java.lang包的String -> java.lang.String
        System.out.println(s); // ok,使用java.lang包的System -> java.lang.System
        MessageFormat mf = null; // 编译错误:无法找到MessageFormat: MessageFormat cannot be resolved to a type
    }
}

因此,编写class的时候,编译器会自动帮我们做两个import动作:
1.默认自动import当前package的其他class;
2.默认自动import java.lang.*。
值得注意的是:自动导入的是java.lang包,但类似java.lang.reflect这些包仍需要手动导入。

第一个怎么理解呢

就是假设你的pakage hello 下面写了这两个

Person.java
Main.java

那么你在写main函数的时候

package hello;

public class Main {
    public static void main(String[] args) {
        Person p = new Person(); // 瞧!我没写 import hello.Person,但依然能用
    }
}

第二个怎么理解

就是常用的类比如

String(字符串)
System(比如 System.out.println)
Integer / Double(基本类型的包装类)
Math(数学运算)
Object(所有类的祖先)

如果没有这个设定的话

import java.lang.String;
import java.lang.System;
import java.lang.Object;
import java.lang.Math;
// ... 烦都烦死了

值得注意的是如果有两个class名称相同,例如,mr.jun.Arrays和java.util.Arrays,那么只能import其中一个,另一个必须写完整类名

一些开发中的小知识

要注意也不要和JDK常用类重名:
java.util.List
java.text.Format
java.math.BigInteger
要注意不要和java.lang包的类重名,即自己的类不要使用这些名字:
String
System
Runtime
为了避免名字冲突,我们需要确定唯一的包名。推荐的做法是使用倒置的域名来确保唯一性。

小疑惑:

在此之前自己写的小程序都是没有package的,那这些文件是在什么包里呢,就是在默认包里

src
 ├─ Hello.java      <-- 没有写 package,属于默认包
 ├─ Test.java       <-- 没有写 package,属于默认包
 └─ com
     └─ hong
         └─ Person.java <-- 属于 com.hong 包

好处是:同一个包互相认识: 如果 Hello.java 和 Test.java 都没有写 package,它们都在“默认包”里,所以它们互相之间可以直接调用,不需要 import。
坏处是:无法被“有名有姓”的包引用(致命伤):
这是一个非常重要的规则:如果一个类在“默认包”里,那么住在“有名有姓”的包(比如 com.hong)里的类,是无法通过 import 来调用它的。
例子: Person.java (在 com.hong 包里) 永远无法使用 Hello.java (在默认包里),因为默认包没有名字,没法写 import ???.Hello;。

posted @ 2026-04-19 15:16  Time_q  阅读(6)  评论(0)    收藏  举报