在C/C++中,是通过.h文件和.cpp文件进行功能区分的,通常一个.cpp文件会对应一个.h文件,而如果不同模块之间需要进行交互操作,或者需要进行接口调用的话,就需要包含对应的.h文件,以表示当前的模块包含了对应模块,因此就可以调用对应模块的接口了。而JAVA中只存在一种.java文件,因此便通过包和包管理实现了这种功能。
在JAVA中,可以将大型的类进行独立,不同功能分别保存到不同的包中,然后将所有包中的程序文件进行编译执行,这样产生的代码便会更易于维护。
包
在JAVA中,源程序文件是.java文件,而.java文件经过编译后都保存在同一个目录中,因此在大型项目中,很可能会发生同名覆盖的问题,因为大型项目编译后很难保证不存在重名文件。而如果要想解决同名文件冲突的问题,就必须要设置在不同的目录。而所谓的包就是指不同的文件夹,这样就能够保证不存在重名文件。在JAVA中使用package关键字来定义包,同时该语句必须写在.java文件的首行。
package com.wood.demo;
public class Demo {
public static void main(String args[]) {
System.out.println("Hello world");
}
}
上面的代码会在当前路径下生成com/wood/demo/Demo.class文件。不过在生成文件之前需要经过打包编译,存在两种打包编译方法:
- 打包编译:javac -d path xxx.java
- -d表示生成目录,根据package的定义生成
- path表示保存的路径
- 在解释程序的时候在包外输入类的完整名称
- 指令为:java com.wood.demo.Demo;
在实际开发中,为了方便程序的管理,所有的类都一定要放在一个包中,而完整的类名永远都是“包.类”,同时没有包的类最好不要在开发中出现。
包的导入
使用包可以将一个完整的程序拆分为不同的文件进行保存。而既然存在包,就会存在包之间的互相访问,此时就需要进行包的导入。
JAVA中包的导入类似于C/C++中的#include,不过使用的是import,同时import语句必须写在package语句之后,而import语句应该编写在类的定义之前。
package com.wood.util;
public class Message {
public void fun() {
System.out.println("fun");
}
}
如果定义了上面的类,并构建了对应的包。那么就可以进行导入,并调用相应的方法:
package com.wood.demo;
import com.wood.util.Message;
public class Demo {
public static void main(String args[]) {
System.out.println("Hello world");
Message msg = new Message();
msg.fun();
}
}
结果为:
Hello world
fun
上面的代码中,导入了com.wood.util.Message类,然后便可以实例化Message类对象,调用fun方法了。
同时如果在当前目录存在多个.java文件的话,JAVA编译器考虑到大型项目中可能存在多个.java文件中的类相互引用,为了解决编译顺序的问题,提供了通配符*操作,即:
javac -d path *.java
这样就可以根据代码调用顺序自动进行程序编译。反之如果不使用通配符,就需要按照调用顺序进行手动编译。上边的代码中,也就是需要先编译Message.java,后编译Demo.java。
而如果将上面的Message类定义的public去掉,在编译完Message.java之后编译Demo.java时会出现报错,提示Message类没有使用public class声明,只能在一个包中访问,而外包无法进行访问。也就是说,public class和class声明的类区别在于:
- public class:文件名必须和类名保持一致,在一个.java文件中只能有一个public class类,如果一个类需要被不同的包访问,就一定要定义为public class
- class:文件名称可以和类名不一致,并且一个.java文件中可以存在多个class类,此时编译该文件会形成多个.class文件,而如果一个类使用class定义,就表示该类只能被本包访问
而既然编译时存在通配符*的操作,那么导包时便也存在通配符*的操作,此时表示可以导入该包中的所有类:
import com.wood.util.*;
此时表示可以导入com.wood.util包中的所有类。不过在具体使用时,类加载只是加载所需要的类,不使用的类不会被加载。
系统常见包
C/C++中提供了大量的内置接口,JAVA类似地也提供了大量的程序开发包。除了JAVA内置的,也存在许多第三方提供的开发包。常用的包有:
- java.lang:基本包,如String这样的类就保存在该包中,不过现在该包都是自动导入的
- java.lang.reflect:反射机制的包,是java.lang的子包
- java.util:工具包,一些常用的类库、日期操作等都在该包中
- java.text:文本的处理类库
- java.sql:数据库操作包,提供了各种数据库操作的类和接口
- java.net:网络编程相关的包
- java.io:输入、输出及文件处理操作
- java.awt:包含抽象窗口工具集的多个类,这些类可以用来构建和管理应用程序的GUI
- javax.swing:用于建立GUI,不过相对于java.awt包而言则是轻量级组件
- java.applet:小应用程序开发包
jar
C/C++中,提供给用户的文件既可以是源文件,也可以是编译打包后的库文件。与C/C++库文件对应的就是JAVA中的jar包。
JAVA中,如果项目中存在大量的.class文件,如果.class文件过多,则会显得结构过于臃肿。此时可以使用jar命令对.class文件进行压缩,实际开发中,交付给用户的也是java归档(JAVA Archive, jar)文件,也就是jar包。
JDK默认提供了生成jar包的工具(jar.exe),可以在命令行直接使用,相关参数为:
- -c:创建一个新文件
- -v:生成标准的压缩信息
- -f:由用户指定生成的.jar文件名称
比如之前的Message.java文件可以使用jar.exe进行打包:
jar -cvf self.jar com
上面的命令可以将生成的com目录(包的根目录)打包成一个压缩的jar包在本地目录。不过生成的jar包并不能直接使用,必须配置CLASSPATH才能够被其它程序类正常加载使用。
访问控制权限
C/C++中存在三种权限,分别是private/protected/public,而在JAVA中存在四种权限:
范围 | private | default | protected | public |
在同一包的同一类 | yes | yes | yes | yes |
同一包的不同类 | yes | yes | yes | |
不同包的子类 | yes | yes | ||
不同包的非子类 | yes |
上表可以理解为:
- private只能在一个类中访问
- default只能在一个包中访问
- protected可以在不同包的子类中访问
- public则都可以
对于protected权限来说,可以定义下边的代码:
package com.wood.demoA;
public class DemoA {
protected String info = "Hello";
}
以及:
package com.wood.demoB;
import com.wood.demoA.DemoA;
public class DemoB extends DemoA {
public void fun() {
System.out.println("DemoA info is " + super.info);
}
}
执行代码:
package com.wood.demo;
import com.wood.demoB.DemoB;
public class Demo {
public static void main(String args[]) {
new DemoB().fun();
}
}
结果为:
DemoA info is Hello
因为是protected修饰的info,因此才可以在包外的子类中访问。
不过对于访问权限,只需要了解两个原则:
- 属性声明主要使用private权限
- 方法声明主要使用public权限
命名规范
JAVA中的命名规范主要包括:
- 类名称:每一个单词的开头首字母大写
- 变量名称:第一个单词的首字母小写,之后每个单词的首字母大写
- 方法名称:第一个单词的首字母小写,之后每个单词的首字母大写
- 常量名称:每个字母都大写
- 包名称:每个字母都小写
单例设计模式
单例设计模式是一种基本的设计模式,用JAVA语言可以实现为:
class Singleton {
private static final Singleton instance = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return instance;
}
public void fun() {
System.out.println("Singleton");
}
}
public class Demo {
public static void main(String args[]) {
Singleton inst = Singleton.getInstance();
inst.fun();
}
}
结果为:
Singleton
这里解释下程序:
- instance前的private表示该属性不可直接在类外访问,需要通过getInstance方法获得该属性
- instance前的static则是由于getInstance方法为static,static方法只能调用static属性和static方法
- instance前的final则是为了防止重复实例化Singleton对象,避免重复申请空间
- Singleton构造方法前的private是为了避免在类外实例化Singleton对象
- getInstance方法前的static则是为了可以在类外获得Singleton实例,这也是获得单例对象的唯一方法
多例设计模式
多例设计模式则是相对于单例设计模式而言的,对于只能构建固定个数的类来说,不应构建超过该个数的实例对象,比如性别,颜色,性别等。为了防止无限制地构建实例化对象,可以使用多例设计模式。
class Sex {
private String title;
private static final Sex MALE = new Sex("Male");
private static final Sex FEMALE = new Sex("Female");
private Sex(String title) {
this.title = title;
}
public String toString() {
return this.title;
}
public static Sex getInstance(String str) {
switch (str) {
case "male":
return MALE;
case "female":
return FEMALE;
default:
return null;
}
}
}
public class Demo {
public static void main(String args[]) {
Sex inst = Sex.getInstance("male");
System.out.println(inst.toString());
}
}
结果为:
Male
上面的代码中则可以通过传入参数判断实例化的对象,而由于只存在两种Sex实例对象,因此便称为多例设计模式。