这一章讲的是,包,类的引入之类的,开发都离不开引入,所以挺重要的(修正一下,非常重要);
因为编程语言中,这些涉及到 目录结构,开发时候不懂它的意思,就根本理解不了为什么要这样做,博主未正规学习时候去搭建 Java 项目时候,看得一头雾水。
1、包(package):
A、Java 中允许使用 package 将类组织起来。
B、使用包,可以方便组织自己的代码,同时方便清晰管理自己的代码和别人提供的代码。
C、* 使用包的主要原因是为了确保类名的唯一性,例如:A包 和 B包 下面都存在一个叫 Employee 的类,这样就不会产生冲突了。
D、实际上为了确保 包名 的唯一性,Sun 公司建议将公司的因特网域名以 逆序的形式作为包名,
例如:
· Java 的核心技术 的作者之一的注册域名为 horstmann.com, 逆序形式就是 com.horstmann。
· 上面说的,还可以进行进一步划分成子包,如:com.horstmann.corejava。
E、标准的 Java 类库,分布在多个包中。
F、标准的 Java 包具有一个层次结构;如同硬盘的目录嵌套一样,也可以使用嵌套层次组织包。
G、所有标准的 Java 包都处于 Java 和 Java小包层次中。
H、从编译器的角度来看,嵌套包直接没有任何关系,例如:java.util 包 和 java.util.jar 包 毫无关系;每一个都拥有独立的类集合。
2、类的导入:
Ps: 一个类可以使用所属包中的所有类,以及其他包中的公有类(public class)
A、访问另一个包中的公共类有下面两种方法:
1. 在每个类名之前添加完整的包名,如:java.time.LocalDate today = new java.time.LocalDate.now();
2. 使用 import 方法,使用了 import 方法后,就不需要把包名也带上了。
Ps: import 导入特定的类,或者 整个包中所有的类:
· 特定的类 ---- import java.time.LocalDate;
· 包中所有的类 ---- import java.util.*;(这种写法是比较简单的,但是不明确导入的类有哪些)
B、上面导入包中所有类的写法,* 号写法,只能导入一个包,而不能通过 java.*.* 来进行导入所有 java 前缀的包。
C、要是使用 * 号来导入两个包,刚刚包中有重复名字的类,这个时候会报错,例如:
/// java.util 和 java.sql 都有 名字为 Date 的类
import java.util.*;
import java.sql.*;
/// 上面的导入,会导致编译器不知道你要的是那个包下的 Date 类
/// 我们在下面增加一条 特定指向导入的类来解决错误。
import java.util.Date;
/// 解决错误后,但是我们实际上,两个包中的 Date 类都要用到。
/// 解决方法是 第一种使用 包中公共类的写法,写全包名。
java.util.Date deadline = new java.util.Date();
java.sql.Date today = new java.sql.Date(...);
D、在包中定位类时编译器(compiler)的工作,也就是说,把 .java 文件编译成 .class 文件的时候,编译器会完整的带上包名,存放在字节码中,相当于 语法糖 的作用。
3、静态导入:
A、import 除了能导入类外,还有导入 静态方法 和 静态域的功能。
/// 在源文件顶部添加一条命令。 /// 这样的写法会添加类中的 所有 静态方法 和 静态域。 /// 这里都是针对 public 类的。 import static java.lang.Math.*; class StaticImport { public static void main(String[] args) { /// pow 是 静态方法 double a = pow(10, 2); System.out.println("pow = " + a); /// PI 是 静态域 System.out.println(PI); } }
4、将类放入包中:
A、要想把类放入包中,就必须将 包 的名字放在源代码的开头
package 包名;
public class Employee
{
...
}
B、如果源文件没有放置 package 语句,这个源文件中的 类就被放置在一个 默认包(default package)中。
C、默认包是没有名字的。
D、* 将包中的文件放到与完整的 包名 匹配的子目录中:
例如:com.horstmann.corejava 包中的所有 源文件(即 .java 后缀的文件)应该放在 com/horstmann/corejava 目录下。
E、编译器会将类文件(.class)也放在相同的目录结构中:
也就是说,我们把 Employee 类,放在 com.horstmann.corejava 中个包中,源文件 Employee.java 是在 com/horstmann/corejava 目录下;
编译器 编译后 生成的 Employee.class 文件也会在 com/horstmann/corejava 目录下。
F、编译生成:
/* 目录结构: |— TestImportMain.java |— importclass/ |— ImportClass.java |— NotImportClass.java */ TestImportMain.java 文件,下面是它的全部内容 ---- /// 这里 使用了了 ImportClass; import importclass.ImportClass; class TestImportMain { public static void main(String[] args) { System.out.println(ImportClass.name); } } ----
ImportClass.java 文件,下面是它的全部内容
NotImoportClass.java 文件 也是一样的内容,只是 名字不同,name 的值不同。
----
package importclass;
public class ImportClass
/// 我们只需要执行 在根目录下 执行 javac TestImportClass.java 去编译,就能生成下面的目录结构:
/// 因为 NotImportClass 没有在 TestImportClass 中使用,所以编译不会去编译它。
/*
目录结构:
|— TestImportMain.java
|— TestImportMain.class
|— importclass/
|— ImportClass.java
|— ImportClass.class
|— NotImportClass.java
*/
G、编译器在编译源文件的时候,不检查目录结构,意思是有下面的情况:
我回退过头没了,心态炸了,不写了。有人说不明白 这句话 再说吧。
H、第 F 点说的入口文件编译,是在 default package 包下面的, 要是我们的入口文件不在默认包中,如下:
/*
.(基目录)/// 也就是 终端打开目录
|— com/
|— horstmann/
| |— corejava/
| |— Employee.java
|- myapp/
|- App.java /// 入口函数 Main 所在的 main
*/
/// 编译时候要从 基目录编译 和运行类,即包含 com 目录:
/// 编译:javac com/myapp/App.java
/// 运行:java com.myapp.App
Ps:
1、编译器(就是上面命令行中的 javac)对文件(带文件分隔符 和 扩展名 .java 的文件)进行操作。
2. java 解析器(就是上面命令行中的 java) 加载类(带 . 分隔符)
5、包的作用域:
A、被 public 修饰的类,可以被 任意类(不在同一个包里面的类也是可以的) 使用。
B、被 private 修饰的类,只能被定义它的类中 使用。
C、如果 一个 类 它没有被任何其他类包含着,只用使用 public/公共的、abstract/抽象的、final/最终的 这三个关键字是被允许去修饰它的。
否则会报:Illegal modifier for the class PrivateClass; only public, abstract & final are permitted
D、如果一个包中的类,没有被 public 修饰,那么它只能被 同一个包中的类的所有方法访问。
E、类 被 public 修饰后,构造器也要被 public 修饰,否则 类的构造器 同样只能在同一个包中(这个同一个包中,只要源文件上面的 package 包名; 都是相同的,就是同一个包,哪怕分成多个源文件,并不影响)使用。
F、同一个包中,分成多个源文件,不同源文件中都可以重复 类名,
Ps:
· 编译器会先找到同一个源文件中的 类,如果同一个源文件中 没有该类,往包的其他源文件中 查找,同一个包中的 不需要 import。
· 因为上面这点,编写一个包的时候,注意 类名 的重复。
G、注意:一个包中,类的 域(实例域 或者 静态域)没有被 public 或者 private 关键字修饰的话,类的域 也是可以被同一个包中其他所有的方法 获取到,所以 变量 这里不适宜 省略掉 private 或者 public 修饰词。
PS:
我们看了上面那个多 包 的解说,可以看到:
· 默认情况下,包不是一个封闭的实体,也就是说,任何人都可以往包里添加更多的类 或 修改包的代码。
· 默认情况下,1.2的JDK开始 类加载器 明确地禁止 加载用户自定义的 包名以 "java." 开始的类。
· 上面保护的方法,无法保护 用户自定义的类,解决这个问题,通过 包密封(package sealing)机制来解决各种包混杂在一起的问题。
· 将一个包密封起来,就不能再向这个包添加类了。制作包含密封包的 JAR 文件(也就是 .jar 文件了,终于知道这个后缀是什么鬼了),后面再说。
· JAR 文件使用 ZIP 格式组织文件和子目录;可以使用所有 ZIP 实用程序查看内部 的 rt.jar 以及其他的JAR 文件
6、类路径( class path)[这个鬼东西,就是window 安装 Java 时候,好多教程都有教到的设置 window 环境变量中的 CLASSPATH ]:
A、类的路径 必须 与 包名 匹配;类路径是包含类文件的路径的集合。
B、window 中使用(;)分隔,Linux 中使用(:)分隔.
C、Java SE 6 开始,JAR 的文件目录中可以用 * 通配符,这个通配符,值匹配 .jar文件(注意 Unix 系统不允许这样做,但 Linux 也是允许的)
· window: c:classdir;.;c:archives*
· Linux: /home/user/classdir:.:/home/user/archives/'*'
D、javac 编译器总是在当前目录中查找文件,但 Java 虚拟机仅在类路径中有 ( . ) 目录的时候才查看当前目录。
E、如果没有设置类路径,默认的类路径包含了 . 目录。
F、设置了类路径,但忘记写上 . 目录,会导致编译没问题,但是运行不了。
G、设置类路径:
# 最好采用 -classpath(或 -cp)选项指定类路径
# Linux 或者 Mac
$ java -classpath /home/user/classdir....
# window
$ java -classpath c:classdir...
# 整个命令要写在一行,太长的话,可以写在 shell 脚本 或者 批处理中然后执行。
# 上面的是首选,但是,我们同样可以设置 环境变量 CLASSPATH.
# window 设置环境变量就不用说了(点击设置哪些)
# 命令行设置
# Linux 依赖的是 export
$ export CLASSPATH=/home/user/classdir....
# window shell 如下:
$ set CLASSPATH=c:classdir...
# 上面命令行设置,知道 shell 退出之前,都有效,shell 推出后就失效了。
# 适合临时设置的,要是一直用到,把它写在环境变量配置中
文件分隔符:
参考:https://www.cnblogs.com/hellowhy/p/6526114.html
我直接复制粘贴过来了,写的挺好的,如果认为侵权删除,请联系我。
一、linux系统和windows系统中的文件路径:
Linux系统:
Windows系统:
可以看到Linux系统中,路径中的文件名分隔符是"/",而Windows中是""
二、linux系统和windows系统中的path路径:
Linux系统:
.:%JAVA_HOME%lib:%JAVA_HOME%lib ools.jar:%JAVA_HOME%jrelib t.jar:
Windows系统:
.;%JAVA_HOME%lib;%JAVA_HOME%lib ools.jar;%JAVA_HOME%jrelib t.jar;
同样,可以看到Linux系统中,path间的分隔符是":"(冒号),而Windows中是";"(分号)
因为分隔符的不同,我们在编程时就不能硬性制定文件路径或path之间的分隔符,因为这会导致跨平台时出现找不到文件或路径的错误,
在java中是这样解决的,jdk中有对应的方法,可以根据当前系统类型动态地获取文件或path的分隔符,下面是使用方法及源码中的相关描述:
一、获取文件路径中的文件名分隔符:
File.separator;
下面开始追踪源码:
第一步:
系统相关的默认名称分隔符。为了方便它被表示为一个字符串,该字符串只包含一个字符,即separatorChar
第二步:
系统相关的默认名称分隔符,这个字段被初始化为包含系统属性file.separator值的第一个字符,在UNIX系统中是”/”,在Windows系统中是””
第三步:
FileSystem对象表示当前平台的本地文件系统
第四步:
返回本地文件系统的名称分隔符
二、获取path中的分隔符:
File.pathSeparator
第一步:
系统相关的路径分隔符,为了方便被表示为一个字符串,这个字符串是一个单独的字符,即pathSeparatorChar
第二步:
系统相关的路径分隔符。这个字段被初始化为系统属性path.separator值的第一个字符,这个字符被用来分隔以列表形式给定的文件序列的文件名称,
在UNIX系统中是冒号(:),在Windows系统中是分号(;)
第三步:
FileSystem对象表示当前平台的本地文件系统
第四步:
返回本地文件系统的路径分隔符
所以在java编程中,遇到文件和path等操作时,为了跨平台时不引起因分隔符导致的错误,就要调用这两个方法来进行文件路径或path的拼接。