Java 包(package)
还未完成,继续修改
一、包的作用
-
把功能相似或相关的类或接口组织在同一个包中,方便类的查找和使用。
-
如同文件夹一样,包也采用了树形目录的存储方式。同一个包中的类名字是不同的,不同的包中的类的名字是可以相同的,当同时调用两个不同包中相同类名的类时,应该加上包名加以区别。因此,包可以避免名字冲突
-
包也限定了访问权限,拥有包访问权限的类才能访问某个包中的类。
package
C/C++ 的 #include会把所包含的内容在编译时添加到程序文件中,而java的import则不同。
- 作用
- package名称就像是我们的姓,而class名称就像是我们的名字 。package和package的附属关系用”.”来连接,这就像是复姓。比如说 java.lang.String就是复姓 java.lang,名字為 String 的类别;java.io.InputStream 则是复姓 java.io,名字為 InputStream的类别。
- Java 会使用 package 这种机制的原因也非常明显,就像我们取姓名一样 ,光是一间学校的同一届同学中,就有可能会出现不少同名的同学,如果不取姓的话,那学校在处理学生资料,或是同学彼此之间的称呼,就会发生很大的困扰。相同的,全世界的 Java 类数量,恐怕比日本人还多,如果类别不使用package名称,那在用到相同名称的不同类时, 就会产生极大的困扰。所以package这种方式让极大降低了类之间的命名冲突。
- Java 的package名称我们可以自己取,不像人的姓没有太大的选择 ( 所以出现很多同名同姓的情况 ),如果依照 Sun 的规范来取套件名称,那理论上不同人所取的套件名称不会相同 ( 需要的话请参阅 “命名惯例” 的相关文章 ),也就不会发生名称冲突的情况。
- 可是现在问题来了,因為很多package的名称非常的长,在编程时,要使用一个类要将多个包名.类名完全写出,会让代码变得冗长,减低了简洁度。例如
-
- java.io.InputStream is = java.lang.System.in;
- java.io.InputStreamReader isr= new java.io.InputStreamReader(is);
- java.io.BufferedReader br = new java.io.BufferedReader(isr);
注:显得非常麻烦,于是Sun公司就引入了import。
import介绍
- 引用包。
- import就是在java文件开头的地方,先说明会用到那些类别。接着我们就能在代码中只用类名指定某个类,也就是只称呼名字,不称呼他的姓。
- import java.lang.System;
- import java.io.InputStream;
- import java.io.InputStreamReader;
- import java.io.BufferedReader;
- import就是在java文件开头的地方,先说明会用到那些类别。接着我们就能在代码中只用类名指定某个类,也就是只称呼名字,不称呼他的姓。
- 使用包:
- InputStream = System.in;
- InputStreamReader isr = new InputStreamReader(is);
- BufferedReader br = new BufferedReader(isr);
- 简化引用包
- 一个java文件就像一个大房间,我们在门口写着在房间里面的class的姓和名字,所以在房间里面提到某个class就直接用他的名字就可以。例如:
- System 就是指 java.lang.System,而 InputStream 就是指 java.io.InputStream。
- 但是如果一个java文件里面有多个同个“姓”,即包名相同的类(例如上面的InputStream,InputStreamReader,BufferedReader都是java.io中的类),我们一一写出显得比较繁杂,所以Sun就让我们可以使用
- import java.lang.;
- import java.io.;
- 这里引用了两个包,java.lang包和java.io包。使用类时,编译器会帮我们选择对应的包。
- 可以在简化吗
- 那我们可不可以再懒一点直接写成下面声明呢?
- import java.*;
- 历史告诉我们,这样是不行的。因為那些类别是姓 java.io 而不是姓 java。就像姓『诸葛』的人应该不会喜欢你称他為『诸』 先生吧。这样写的话只会将java包下的类声明,而不不会声明子包的任何类。
- 这里注意,java.lang包里面的类实在是太常太常太常用到了,几乎没有类不用它的, 所以不管你有没有写 import java.lang,编译器都会自动帮你补上,也就是说编译器只要看到没有姓的类别,它就会自动去lang包里面查找。所以我们就不用特别去 import java.lang了。
import
- 一开始说 import 跟 #include 不同,是因为import 的功能到此為止,它不像#include 一样,会将其他java文件的内容载入进来。import 只是让编译器编译这个java文件时把没有姓的类别加上姓,并不会把别的文件程序写进来。你开心的话可以不使用import,只要在用到类别的时候,用它的全部姓名来称呼它就行了(就像例子一开始那样),这样跟使用import功能完全一样。
- import的两种导入声明
- 单类型导入(single-type-import)
- (例:import java.util.ArrayList; )
- 按需类型导入(type-import-on-demand)
- (例:import java.util.*;)
- java以这样两种方式导入包中的任何一个public的类和接口(只有public类和接口才能被导入)
- 单类型导入(single-type-import)
- 上面说到导入声明仅导入声明目录下面的类而不导入子包,这也是为什么称它们为类型导入声明的原因。
- 导入的类或接口的简名(simple name)具有编译单元作用域。这表示该类型简名可以在导入语句所在的编译单元的任何地方使用.这并不意味着你可以使用该类型所有成员的简名,而只能使用类型自身的简名。
- 例如: java.lang包中的public类都是自动导入的,包括Math和System类.但是,你不能使用它们的成员的简名PI()和gc(),而必须使用Math.PI()和System.gc().你不需要键入的是java.lang.Math.PI()和java.lang.System.gc()。
- 当然,如果你导入java.lang.Math.*就可以用成员的简名了
- 程序员有时会导入当前包或java.lang包,这是不需要的,因为当前包的成员本身就在作用域内,而java.lang包是自动导入的。java编译器会忽略这些冗余导入声明(redundant import declarations)。即使像这样
- import java.util.ArrayList; import java.util.*;
- static import静态导入
- 在Java程序中,是不允许定义独立的函数和常量的。即什么属性或者方法的使用必须依附于什么东西,例如使用类或接口作为挂靠单位才行(在类里可以挂靠各种成员,而接口里则只能挂靠常量)。
- 如果想要直接在程序里面不写出其他类或接口的成员的挂靠单元,有一种变通的做法 :
- 将所有的常量都定义到一个接口里面,然后让需要这些常量的类实现这个接口(这样的接口有一个专门的名称,叫(“Constant Interface”)。这个方法可以工作。但是,因为这样一来,就可以从“一个类实现了哪个接口”推断出“这个类需要使用哪些常量”,有“会暴露实现细节”的问题。
- 于是J2SE 1.5里引入了“Static Import”机制,借助这一机制,可以用略掉所在的类或接口名的方式,来使用静态成员。static import和import其中一个不一致的地方就是static import导入的是静态成员,而import导入的是类或接口类型。
- 如下是一个有静态变量和静态方法的类
- package com.assignment.test;
- public class staticFieldsClass {
- static int staticNoPublicField = 0;
- public static int staticField = 1;
- public static void staticFunction(){}
- }
- 平时我们使用这些静态成员是用类名.静态成员的形式使用,即staticFieldsClass.staticField或者staticFieldsClass.staticFunction()。
- 现在用static import的方式:
- //精准导入
- //直接导入具体的静态变量、常量、方法方法,注意导入方法直接写方法名不需要括号。
- import static com.assignment.test.StaticFieldsClass.staticField;
- import static com.assignment.test.StaticFieldsClass.staticFunction;
- //或者使用如下形式:
- //按需导入不必逐一指出静态成员名称的导入方式
-
//import static com.assignment.test.StaticFieldsClass.*; public class StaticTest { public static void main(String[] args) { //这里直接写静态成员而不需要通过类名调用 System.out.println(staticField); staticFunction(); } } }
- 这里有几个问题需要弄清楚:
- Static Import无权改变无法使用本来就不能使用的静态成员的约束,上面例子的StaticTest和staticFieldsClass不是在同一个包下,所以StaticTest只能访问到staticFieldsClass中public的变量。使用了Static Import也同样如此。
- 导入的静态成员和本地的静态成员名字相同起了冲突,这种情况下的处理规则,是“本地优先。
- 不同的类(接口)可以包括名称相同的静态成员。例如在进行Static Import的时候,出现了“两个导入语句导入同名的静态成员”的情况。在这种时候,J2SE 1.5会这样来加以处理:
- 如果两个语句都是精确导入的形式,或者都是按需导入的形式,那么会造成编译错误。
- 如果一个语句采用精确导入的形式,一个采用按需导入的形式,那么采用精确导入的形式的一个有效。
- 大家都这么聪明上面的几个特性我就不写例子了。
- static import这么叼那它有什么负面影响吗?
- 答案是肯定的,去掉静态成员前面的类型名,固然有助于在频繁调用时显得简洁,但是同时也失去了关于“这个东西在哪里定义”的提示信息,理解或维护代码就呵呵了。
- 但是如果导入的来源很著名(比如java.lang.Math),这个问题就不那么严重了。
- 按需导入机制
- 使用按需导入声明是否会降低Java代码的执行效率?
- 绝对不会!
- 一、import的按需导入
- import java.util.*;
- public class NeedImportTest {
- public static void main(String[] args) {
- ArrayList tList = new ArrayList();
- }
- }
- 编译之后的class文件 :
- //import java.util.*被替换成import java.util.ArrayList
- //即按需导入编译过程会替换成单类型导入。
- import java.util.ArrayList;
- public class NeedImportTest {
- public static void main(String[] args) {
- new ArrayList();
- }
- }
- 二、static import的按需导入
- import static com.assignment.test.StaticFieldsClass.*;
- public class StaticNeedImportTest {
- public static void main(String[] args) {
- System.out.println(staticField);
- staticFunction();
- }
- }
- 上面StaticNeedImportTest 类编译之后 :
- //可以看出 :
- //1、static import的精准导入以及按需导入编译之后都会变成import的单类型导入
- import com.assignment.test.StaticFieldsClass;
- public class StaticNeedImportTest {
- public static void main(String[] args) {
- //2、编译之后“打回原形”,使用原来的方法调用静态成员
- System.out.println(StaticFieldsClass.staticField);
- StaticFieldsClass.staticFunction();
- }
- }
- 附加
- 这是否意味着你总是可以使用按需导入声明?
- 是,也不是!
- 在类似Demo的非正式开发中使用按需导入声明显得很有用。
- 然而,有这四个理由让你可以放弃这种声明:
- 编译速度:在一个很大的项目中,它们会极大的影响编译速度.但在小型项目中使用在编译时间上可以忽略不计。
- 命名冲突:解决避免命名冲突问题的答案就是使用全名。而按需导入恰恰就是使用导入声明初衷的否定。
- 说明问题:毕竟高级语言的代码是给人看的,按需导入看不出使用到的具体类型。
- 无名包问题:如果在编译单元的顶部没有包声明,Java编译器首选会从无名包中搜索一个类型,然后才是按需类型声明。如果有命名冲突就会产生问题。
- Sun的工程师一般不使用按需类型导入声明.这你可以在他们的代码中找到:
- 在java.util.Properties类中的导入声明:
-
import java.io.IOException; import java.io.PrintStream; import java.io.PrintWriter; import java.io.InputStream; import java.io.OutputStream; import java.io.Reader; import java.io.Writer; import java.io.OutputStreamWriter; import java.io.BufferedWriter; import java.security.AccessController; import java.security.PrivilegedAction;
- 可以看到他们用单类型导入详细的列出了需要的java.io包中的具体类型
二、创建包
- 创建包的时候,你需要为这个包取一个合适的名字。之后,如果其他的一个源文件包含了这个包提供的类、接口、枚举或者注释类型的时候,都必须将这个包的声明放在这个源文件的开头。
- 使用路径做报名。
- 路径:net/java/util/Something.java
- package net.java.util;
- 包声明应该在源文件的第一行,每个源文件只能有一个包声明,这个文件中的每个类型都应用于它。
- 如果一个源文件中没有使用包声明,那么其中的类,函数,枚举,注释等将被放在一个无名的包(unnamed package)中。
本文来自博客园,作者:zwjvzwj,转载请注明原文链接:https://www.cnblogs.com/zwjvzwj/p/12883260.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效