课时101:包的定义
在实际的项目开发过程中,肯定要一直存在包的概念,利用包可以实现类的包装,在以后的实际开发之中,所有的类都必须放在包里面。
-
包的定义与使用
对于项目而言,尤其是现代的项目是不可能一个人开发完成的,往往在一个项目的开发团队之中会有2-3位的开发者进行项目业务的实现,于是在这样的情况下就不得不去面对一个问题:有可能产生类的重名定义。
在操作系统之中已经明确严格的定义了一个要求:同一个目录之中不允许存放有相同的程序类文件,但是在实际的开发之中很难保证类的不重复,所以为了可以进行类的方便管理,那么往往可以将程序文件放在不同的目录下,不同的目录之中是可以提供有相同文件的,二者目录就称为包。 包=目录。
范例:定义包
package cn.mldn.demo; //定义包,其中.表示分割子目录(子包)
public class Hello{
public static void main(String args[]){
System.out.println("Hello World !");
}
}
一旦程序开发之中出现有包,此时程序编译后的结果就必须将*.class文件保存在指定的目录之中,但是如果手工建立则非常的麻烦,那么此时最好的做法是可以进行打包处理:javac -d.Hello.java
- “-d”:表示要生成目录,而目录的结构就是package定义的结构;
- “.”:表示在当前所在的目录中生成程序类文件;
在程序执行的时候一定要带着包执行程序类:java cn.mldn.demo.Hello,也就是说从此之后完整的类名称是“包.类”名称。
课时102:包的导入
利用包的定义实际上就可以将不同的功能的类保存在不同的包之中,但是这些类彼此之间也会存在有互相调用的关系,那么在这个时候就需要使用import语句来导入其他包中的程序类。
范例:定义一个程序类“cn.mldn.util.Message”,这个类负责进行一个消息数据的获取
package cn.mldn.util;
public class Message{
public String getContent(){
return "www.mldn.cn";
}
}
范例:定义一个测试类使用Message类“cn.mldn.test.TestMessage”。
package cn.mldn.test;
import cn.mldn.util.Message; //导入其它包的类
public class TestMessage{
public static void main(String args[]){
Message msg = new Message(); //实例化对象
System.out.println(msg.getContent());
}
}
此时按照使用顺序来讲肯定要先编译Message.java,而后再编译TestMessage.java,但是你思考一个问题,如果此时你写了一个程序代码,里面有100个类,彼此之间互相引用严格,此时你怎么区分?那么这时候最好的做法不是区分顺序,而是直接交给Java自己完成,编译的命令:javac -h . *.java;
注意:关于public class 与 class定义的区别?
- public class :类名称必须与文件名称保持一致,一个*.java文件里面只允许有一个public class,同时如果一个类需要被其他的包所使用,那么这个类一定要你定义为public class文件,但是这些类只能被本包所访问,外包无法访问。
- class:类名称可以与文件名称不一致,并且在一个*.java文件里面可以提供有多个class定义,编译后将形成不同的*.class
- 在实际的开发之中往往在一个*.java源码文件里面只会提供有一个程序类,而这个程序类一般都使用public class定义,程序类中定义的包名称必须采用小写字母的形式定义,例如:cn.mldn.util;
但是这个时候会有一个新的问题产生了,有些时候可能会使用某一个包中的很多类,于是这样分开进行类的导入会比较麻烦,为了解决这样的问题,也可以使用通配符“*”来处理。
package cn.mldn.test;
import cn.mldn.util.*; //导入其它包的类
public class TestMessage{
public static void main(String args[]){
Message msg = new Message(); //实例化对象
System.out.println(msg.getContent());
}
}
即便此时使用了“包.*”的导入形式,那么也不表示进行全部的加载,它会根据自己的需要加载所需要的程序类,而不需要的程序类是不会被加载的,所以是使用“*”还是使用具体的类,其最终的性能是完全相同的。
但是如果在开发之中采用的是“包.*”的形式进行包的导入时,那么有一点会比较麻烦:有可能两个不同的包中存在有相同的类名称,例如,现在假设TestMessage类由于某种需要,要导入两个包:cn.mldn.util、orz.demo,但是这两个包里面都有Message类。
由于某种需要在TestMessage类里面导入了两个包:
package cn.mldn.test;
import cn.mldn.util.*; //导入其它包的类
import org.demo.*; //导入其它包的类
public class TestMessage{
public static void main(String args[]){
Message msg = new Message(); //实例化对象
System.out.println(msg.getContent());
}
}
cn.mldn.util.Message: | org.demo.Message: |
package cn.mldn.util; public class Message{ public String getContent(){ return "www.mldn.cn"; } } | package org.demo; class Message{ public String getInfo(){ return "人民万岁!"; } } |
程序编译: | javac -d.TestMessage.java |
org.demo 中的类 org.demo.Message和cn.mldn.util中的类cn.mldn.util.Message都匹配TestMessage.java:6: 错误:对Message的引用不明确 Message msg = new Message(); //实例化类对象 org.demo中的类org.demo.Message 和 cn.mldn.util中的类cn.mldn.util.Message都匹配2个错误 |
这个时候就会发现类名称相同的时候就会出现不明确的引用处理,所以此时最简单的处理形式就是直接写上类的完整名称
package cn.mldn.test;
import cn.mldn.util.*; //导入其它包的类
import org.demo.*; //导入其它包的类
public class TestMessage{
public static void main(String args[]){
cn.mldn.util.Message msg = new cn.mldn.util.Message(); //实例化对象
System.out.println(msg.getContent());
}
}
在日后的开发过程之中经常会见到大量的重名的类(包不重名),此时为了更好地解决问题,往往会使用类的完整名称。
课时103:静态导入
假如说现在有一个类,这个类中的全部方法都是static方法,那么按照原始的做法肯定要导入程序所在的“包.类”。而后才可以通过类名称调用这些静态方法;
范例:定义一个MyMath数学类
package cn.mldn.util;
public class MyMath {
public static int add(int... args) {
int sum = 0;
for (int temp:args) {
sum += temp;
}
return sum;
}
public static int sub(int x, int y) {
return x - y;
}
}
如果此时按照原始的方式进行处理,那么此时就需要导入包.类,而后通过类名称调用方法。
范例:原始方法使用
package cn.mldn.test;
import cn.mldn.util.MyMath;
public class TestMath {
public static void main(String args[]) {
System.out.println(MyMath.add(10, 20, 30));
System.out.println(MyMath.sub(30, 20));
}
}
从JDK1.5开始,对于类中全部由静态方法提供的特殊类,是可以采用静态导入处理形式的。
范例:静态导入处理
package cn.mldn.test;
import static cn.mldn.util.MyMath.*;
public class TestMath {
public static void main(String args[]) {
System.out.println(add(10, 20, 30));
System.out.println(sub(30, 20));
}
}
当使用静态导入处理之后就好比该方法是直接定义在主类中的,可以有主方法直接调用。
课时104:生成jar文件
-
Jar命令
当一个项目开发完成之后一定会存在大量的*.class文件,那么对于这些文件的管理,往往可以利用一种压缩结构的形式来进行处理,而这样的结构在Java之中就被称为jar文件,如果要想将程序打包为Jar文件,那么可以直接利用JDK中提供的jar命令完成。
在最原始的时候如果想要知道jar命令的时候直接输入jar即可,而在JDK1.9之后为了统一化,所以需要使用“ --help”查看相关的说明。
下面通过程序的具体演示来实现jar的使用与配置操作。
1、定义一个程序类,这个类的代码如下:
package cn.mldn.util;
public class Message {
public String getContent(){
return "www.mldn.cn";
}
}
2、对程序进行编译与打包处理:
- 对程序打包编译:javac -d .Message.java;
- 此时会形成cn的包,包里面又相应的子包与*.class文件,将其打包为mldn.jar:jar -cvf mldn.jar cn
- “-c”:创建一个新的jar文件;
- “-v”:得到一个详细输出;
- “-f”:设置要生成的jar文件名称,本处定义的是“mldn.jar”;
3、每一个*3.jar文件都是一个独立的程序路径,如果要想在java程序之中使用此路径,则必须通过CLASSPATH进行配置:
SET CLASSPATH=.;d:\mldndemo\mldn.jar
4、建立测试类,直接导入Message类并且调用方法:
package cn.mldn.test;
public class TestMessage{
public static void main(String args[]){
cn.mldn.util.Message msg = new cn.mldn.util.Message(); //实例化对象
System.out.println(msg.getContent());
}
}
随后就可以正常编译TestMessage类并且可以使用这个类:
- 编译程序类:javac -d .Message.java;
- 解释程序类:java cn.mldn.test.TestMessage。
如果此时程序编译通过之后,但是由于CLASSPATH发生了改变,类无法加载到了,则执行TestMessage类的时候将会出现如下的错误提示:Exception in thread "main" java.lang.NoClassDefFoundError: cn/mldn/util/Message
出现这种错误只有一种情况:*.jar包没有配置正确。
JDK1.9之后出现的模块化操作
- 在JDK1.9以前所有的历史版本之中实际上提供的是一个所有类的*.jar文件(rt.jar、tools.jar),在传统的开发之中只要启动了Java虚拟机,那么就需要加载这几十兆的类文件;
- 在JDK1.9之后提供了一个模块化的设计,将原本很大的一个要加载的一个*.jar文件变成了若干个模块文件,这样在启动的时候可以根据程序需要加载指定的模块(模块中有包),就可以实现启动速度变快的效果。
课时105:系统常用包
Java语言从发展至今一直提供有大量的支持类库,这些类库一般由两个方面组成:
- Java自身提供的(除了JDK提供的类库之外还会有一些标准);
- 由第三方厂商提供的Java的支持类库,可以完成各种你需要的功能,并且支持的厂商很多;
而在JDK之中也会提供有大量的类库,并且这些类库都是封装在不同的开发包之中
- java.lang:像String、Number、Object等类都在这个包里面,在这包在JDK1.1之后自动默认导入;
- java.lang.reflect:反射机制处理包,所有的设计从此开始;
- java.util:工具类的定义,包括数据结构的定义;
- java.io:进行输入与输出流操作的程序包;
- java.net:网络程序开发的程序包;
- java.sql:进行数据库编程的开发包;
- java.applet:Java的最原始的使用形式,直接嵌套在网页上执行的程序类;
|- 现在的程序已经以Application为主了(有主方法的程序);
- java.awt、javax.swing:Java的图形界面开发包(GUI),其中awt属于重量级的组件,而swing是轻量级的组件;
课时106:访问控制权限
在面向对象的开发过程之中有三大主要特点:封装、继承、多态。那么对于封装性而言主要的实现依靠的就是访问控制权限,而访问控制权限在程序之中一共定义有四种:private、default(不写)、protected、public,这四种权限的作用如下。
No. | 访问范围 | private | default | protected | public |
1 | 同一包中的同一类 | √ | √ | √ | √ |
2 | 同一包中的不同类 | √ | √ | √ | |
3 | 不同包的子类 | √ | √ | ||
4 | 不同包的所有类 | √ |
在整个访问控制权限的处理之中,只有protected(受保护)的权限是比较新的概念,下面对这一访问权限的使用进行说明,本次定义两个大类:
- cn.mldn.a.Message类:提供有protected访问权限;
- cn.mldn.b.NetMessage类:将直接访问protected属性;
范例:定义Message类
package cn.mldn.a;
public class Message {
protected String info = "www.mldn.cn";
}
范例:定义子类,与父类不在同一个包中
package cn.mldn.b;
import cn.mldn.a.Message;
public class NetMessage extends Message {
public void print() {
System.out.println(super.info);
}
}
范例:编写测试类,通过子类实现操作
package cn.mldn.test;
import cn.mldn.b.*;
import cn.mldn.b.NetMessage;
public class TestMessage {
public static void main(String args[]) {
new NetMessage().print();
}
}
此时的程序是通过子类访问父类中的protected属性。但是如果说此时你直接通过了Message访问info属性,那么就将出现错误的提示。
范例:在测试类中直接访问Message
package cn.mldn.test;
import cn.mldn.a.*;
public class TestMessage {
public static void main(String args[]) {
System.out.println(new Message().info);
}
}
java: info 在 cn.mldn.a.Message 中是 protected 访问控制
在程序之中的封装一共有三个对应的访问权限:private、default、protected,但是如果每次在使用的时候进行区分会比较麻烦,所以可以给出一个参考的选择方案(90%的设计问题):
- 只要是进行属性的定义,全部使用private;
- 只要是进行方法的定义,全部使用public。